Cw 591 in app cake pay integration (#1376)

* init commit

* buy card UI

* buy card detail page

* card filter

* dropdown button

* user auth flow

* create order

* denomination option

* fix searching

* denom option fix UI

* simulate payment

* Update pr_test_build.yml

* Update pr_test_build.yml

* Implement order expiration handling [skip ci]

* refactor code [skip ci]

* remove ionia related code [skip ci]

* change auth flow

* add currency prefix

* grid view UI

* fix country filter issue

* fix underline color

* fix fetching card list [skip ci]

* list view

* update cake pay title

* Optimize API usage by fetching CakePay vendors

* handle no cards found case

* adjust the flow of purchases

* UI fixes

* fix btc payment data

* link extractor

* fix fetch next page issue

* UI fixes

* fix text size

* revert base page changes

* Revert "revert base page changes"

* UI fixes

* fix UI

* fix link style + localization

* update cake pay title

* update cake pay subtitle

* Update cake_pay_order.dart

* revert inject_app_details update
This commit is contained in:
Serhii 2024-06-06 04:51:22 +00:00 committed by GitHub
parent 287c2d8b60
commit 30dc8f9238
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
108 changed files with 3500 additions and 5267 deletions

View file

@ -151,6 +151,10 @@ jobs:
echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> lib/.secrets.g.dart
echo "const polygonScanApiKey = '${{ secrets.POLYGON_SCAN_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
echo "const ankrApiKey = '${{ secrets.ANKR_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart
echo "const testCakePayApiKey = '${{ secrets.TEST_CAKE_PAY_API_KEY }}';" >> lib/.secrets.g.dart
echo "const cakePayApiKey = '${{ secrets.CAKE_PAY_API_KEY }}';" >> lib/.secrets.g.dart
echo "const authorization = '${{ secrets.CAKE_PAY_AUTHORIZATION }}';" >> lib/.secrets.g.dart
echo "const CSRFToken = '${{ secrets.CSRF_TOKEN }}';" >> lib/.secrets.g.dart
echo "const quantexExchangeMarkup = '${{ secrets.QUANTEX_EXCHANGE_MARKUP }}';" >> lib/.secrets.g.dart
echo "const nano2ApiKey = '${{ secrets.NANO2_API_KEY }}';" >> cw_nano/lib/.secrets.g.dart
echo "const tronGridApiKey = '${{ secrets.TRON_GRID_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart

View file

@ -0,0 +1,245 @@
import 'dart:convert';
import 'package:cake_wallet/cake_pay/cake_pay_order.dart';
import 'package:cake_wallet/cake_pay/cake_pay_user_credentials.dart';
import 'package:cake_wallet/cake_pay/cake_pay_vendor.dart';
import 'package:http/http.dart' as http;
class CakePayApi {
static const testBaseUri = false;
static const baseTestCakePayUri = 'test.cakepay.com';
static const baseProdCakePayUri = 'buy.cakepay.com';
static const baseCakePayUri = testBaseUri ? baseTestCakePayUri : baseProdCakePayUri;
static const vendorsPath = '/api/vendors';
static const countriesPath = '/api/countries';
static const authPath = '/api/auth';
static final verifyEmailPath = '/api/verify';
static final logoutPath = '/api/logout';
static final createOrderPath = '/api/order';
static final simulatePaymentPath = '/api/simulate_payment';
/// AuthenticateUser
Future<String> authenticateUser({required String email, required String apiKey}) async {
try {
final uri = Uri.https(baseCakePayUri, authPath);
final headers = {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Api-Key $apiKey',
};
final response = await http.post(uri, headers: headers, body: json.encode({'email': email}));
if (response.statusCode != 200) {
throw Exception('Unexpected http status: ${response.statusCode}');
}
final bodyJson = json.decode(response.body) as Map<String, dynamic>;
if (bodyJson.containsKey('user') && bodyJson['user']['email'] != null) {
return bodyJson['user']['email'] as String;
}
throw Exception('Failed to authenticate user with error: $bodyJson');
} catch (e) {
throw Exception('Failed to authenticate user with error: $e');
}
}
/// Verify email
Future<CakePayUserCredentials> verifyEmail({
required String email,
required String code,
required String apiKey,
}) async {
final uri = Uri.https(baseCakePayUri, verifyEmailPath);
final headers = {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Api-Key $apiKey',
};
final query = <String, String>{'email': email, 'otp': code};
final response = await http.post(uri, headers: headers, body: json.encode(query));
if (response.statusCode != 200) {
throw Exception('Unexpected http status: ${response.statusCode}');
}
final bodyJson = json.decode(response.body) as Map<String, dynamic>;
if (bodyJson.containsKey('error')) {
throw Exception(bodyJson['error'] as String);
}
if (bodyJson.containsKey('token')) {
final token = bodyJson['token'] as String;
final userEmail = bodyJson['user']['email'] as String;
return CakePayUserCredentials(userEmail, token);
} else {
throw Exception('E-mail verification failed.');
}
}
/// createOrder
Future<CakePayOrder> createOrder({
required String apiKey,
required int cardId,
required String price,
required int quantity,
required String userEmail,
required String token,
}) async {
final uri = Uri.https(baseCakePayUri, createOrderPath);
final headers = {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Api-Key $apiKey',
};
final query = <String, dynamic>{
'card_id': cardId,
'price': price,
'quantity': quantity,
'user_email': userEmail,
'token': token,
'send_email': true
};
try {
final response = await http.post(uri, headers: headers, body: json.encode(query));
if (response.statusCode != 201) {
final responseBody = json.decode(response.body);
if (responseBody is List) {
throw '${responseBody[0]}';
} else {
throw Exception('Unexpected error: $responseBody');
}
}
final bodyJson = json.decode(response.body) as Map<String, dynamic>;
return CakePayOrder.fromMap(bodyJson);
} catch (e) {
throw Exception('${e}');
}
}
///Simulate Payment
Future<void> simulatePayment(
{required String CSRFToken, required String authorization, required String orderId}) async {
final uri = Uri.https(baseCakePayUri, simulatePaymentPath + '/$orderId');
final headers = {
'accept': 'application/json',
'authorization': authorization,
'X-CSRFToken': CSRFToken,
};
final response = await http.get(uri, headers: headers);
print('Response: ${response.statusCode}');
if (response.statusCode != 200) {
throw Exception('Unexpected http status: ${response.statusCode}');
}
final bodyJson = json.decode(response.body) as Map<String, dynamic>;
throw Exception('You just bot a gift card with id: ${bodyJson['order_id']}');
}
/// Logout
Future<void> logoutUser({required String email, required String apiKey}) async {
final uri = Uri.https(baseCakePayUri, logoutPath);
final headers = {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Api-Key $apiKey',
};
try {
final response = await http.post(uri, headers: headers, body: json.encode({'email': email}));
if (response.statusCode != 200) {
throw Exception('Unexpected http status: ${response.statusCode}');
}
} catch (e) {
print('Caught exception: $e');
}
}
/// Get Countries
Future<List<String>> getCountries(
{required String CSRFToken, required String authorization}) async {
final uri = Uri.https(baseCakePayUri, countriesPath);
final headers = {
'accept': 'application/json',
'authorization': authorization,
'X-CSRFToken': CSRFToken,
};
final response = await http.get(uri, headers: headers);
if (response.statusCode != 200) {
throw Exception('Unexpected http status: ${response.statusCode}');
}
final bodyJson = json.decode(response.body) as List;
return bodyJson.map<String>((country) => country['name'] as String).toList();
}
/// Get Vendors
Future<List<CakePayVendor>> getVendors({
required String CSRFToken,
required String authorization,
int? page,
String? country,
String? countryCode,
String? search,
List<String>? vendorIds,
bool? giftCards,
bool? prepaidCards,
bool? onDemand,
bool? custom,
}) async {
var queryParams = {
'page': page?.toString(),
'country': country,
'country_code': countryCode,
'search': search,
'vendor_ids': vendorIds?.join(','),
'gift_cards': giftCards?.toString(),
'prepaid_cards': prepaidCards?.toString(),
'on_demand': onDemand?.toString(),
'custom': custom?.toString(),
};
final uri = Uri.https(baseCakePayUri, vendorsPath, queryParams);
var headers = {
'accept': 'application/json; charset=UTF-8',
'authorization': authorization,
'X-CSRFToken': CSRFToken,
};
var response = await http.get(uri, headers: headers);
if (response.statusCode != 200) {
throw Exception(response.body);
}
final bodyJson = json.decode(response.body);
if (bodyJson is List<dynamic> && bodyJson.isEmpty) {
return [];
}
return (bodyJson['results'] as List)
.map((e) => CakePayVendor.fromJson(e as Map<String, dynamic>))
.toList();
}
}

View file

@ -0,0 +1,87 @@
import 'dart:convert';
import 'package:cake_wallet/entities/fiat_currency.dart';
class CakePayCard {
final int id;
final String name;
final String? description;
final String? termsAndConditions;
final String? howToUse;
final String? expiryAndValidity;
final String? cardImageUrl;
final String? country;
final FiatCurrency fiatCurrency;
final List<String> denominationsUsd;
final List<String> denominations;
final String? minValueUsd;
final String? maxValueUsd;
final String? minValue;
final String? maxValue;
CakePayCard({
required this.id,
required this.name,
this.description,
this.termsAndConditions,
this.howToUse,
this.expiryAndValidity,
this.cardImageUrl,
this.country,
required this.fiatCurrency,
required this.denominationsUsd,
required this.denominations,
this.minValueUsd,
this.maxValueUsd,
this.minValue,
this.maxValue,
});
factory CakePayCard.fromJson(Map<String, dynamic> json) {
final name = stripHtmlIfNeeded(json['name'] as String? ?? '');
final decodedName = fixEncoding(name);
final description = stripHtmlIfNeeded(json['description'] as String? ?? '');
final decodedDescription = fixEncoding(description);
final termsAndConditions = stripHtmlIfNeeded(json['terms_and_conditions'] as String? ?? '');
final decodedTermsAndConditions = fixEncoding(termsAndConditions);
final howToUse = stripHtmlIfNeeded(json['how_to_use'] as String? ?? '');
final decodedHowToUse = fixEncoding(howToUse);
final fiatCurrency = FiatCurrency.deserialize(raw: json['currency_code'] as String? ?? '');
final List<String> denominationsUsd =
(json['denominations_usd'] as List?)?.map((e) => e.toString()).toList() ?? [];
final List<String> denominations =
(json['denominations'] as List?)?.map((e) => e.toString()).toList() ?? [];
return CakePayCard(
id: json['id'] as int? ?? 0,
name: decodedName,
description: decodedDescription,
termsAndConditions: decodedTermsAndConditions,
howToUse: decodedHowToUse,
expiryAndValidity: json['expiry_and_validity'] as String?,
cardImageUrl: json['card_image_url'] as String?,
country: json['country'] as String?,
fiatCurrency: fiatCurrency,
denominationsUsd: denominationsUsd,
denominations: denominations,
minValueUsd: json['min_value_usd'] as String?,
maxValueUsd: json['max_value_usd'] as String?,
minValue: json['min_value'] as String?,
maxValue: json['max_value'] as String?,
);
}
static String stripHtmlIfNeeded(String text) {
return text.replaceAll(RegExp(r'<[^>]*>|&[^;]+;'), ' ');
}
static String fixEncoding(String text) {
final bytes = latin1.encode(text);
return utf8.decode(bytes, allowMalformed: true);
}
}

View file

@ -0,0 +1,131 @@
class CakePayOrder {
final String orderId;
final List<OrderCard> cards;
final String? externalId;
final double amountUsd;
final String status;
final String? vouchers;
final PaymentData paymentData;
CakePayOrder({
required this.orderId,
required this.cards,
required this.externalId,
required this.amountUsd,
required this.status,
required this.vouchers,
required this.paymentData,
});
factory CakePayOrder.fromMap(Map<String, dynamic> map) {
return CakePayOrder(
orderId: map['order_id'] as String,
cards: (map['cards'] as List<dynamic>)
.map((x) => OrderCard.fromMap(x as Map<String, dynamic>))
.toList(),
externalId: map['external_id'] as String?,
amountUsd: map['amount_usd'] as double,
status: map['status'] as String,
vouchers: map['vouchers'] as String?,
paymentData: PaymentData.fromMap(map['payment_data'] as Map<String, dynamic>));
}
}
class OrderCard {
final int cardId;
final int? externalId;
final String price;
final int quantity;
final String currencyCode;
OrderCard({
required this.cardId,
required this.externalId,
required this.price,
required this.quantity,
required this.currencyCode,
});
factory OrderCard.fromMap(Map<String, dynamic> map) {
return OrderCard(
cardId: map['card_id'] as int,
externalId: map['external_id'] as int?,
price: map['price'] as String,
quantity: map['quantity'] as int,
currencyCode: map['currency_code'] as String,
);
}
}
class PaymentData {
final CryptoPaymentData btc;
final CryptoPaymentData xmr;
final DateTime invoiceTime;
final DateTime expirationTime;
final int? commission;
PaymentData({
required this.btc,
required this.xmr,
required this.invoiceTime,
required this.expirationTime,
required this.commission,
});
factory PaymentData.fromMap(Map<String, dynamic> map) {
return PaymentData(
btc: CryptoPaymentData.fromMap(map['BTC'] as Map<String, dynamic>),
xmr: CryptoPaymentData.fromMap(map['XMR'] as Map<String, dynamic>),
invoiceTime: DateTime.fromMillisecondsSinceEpoch(map['invoice_time'] as int),
expirationTime: DateTime.fromMillisecondsSinceEpoch(map['expiration_time'] as int),
commission: map['commission'] as int?,
);
}
}
class CryptoPaymentData {
final String price;
final PaymentUrl? paymentUrls;
final String address;
CryptoPaymentData({
required this.price,
this.paymentUrls,
required this.address,
});
factory CryptoPaymentData.fromMap(Map<String, dynamic> map) {
return CryptoPaymentData(
price: map['price'] as String,
paymentUrls: PaymentUrl.fromMap(map['paymentUrls'] as Map<String, dynamic>?),
address: map['address'] as String,
);
}
}
class PaymentUrl {
final String? bip21;
final String? bip72;
final String? bip72b;
final String? bip73;
final String? bolt11;
PaymentUrl({
this.bip21,
this.bip72,
this.bip72b,
this.bip73,
this.bolt11,
});
factory PaymentUrl.fromMap(Map<String, dynamic>? map) {
return PaymentUrl(
bip21: map?['BIP21'] as String?,
bip72: map?['BIP72'] as String?,
bip72b: map?['BIP72b'] as String?,
bip73: map?['BIP73'] as String?,
bolt11: map?['BOLT11'] as String?,
);
}
}

View file

@ -0,0 +1,15 @@
class PaymentCredential {
final double amount;
final int quantity;
final double totalAmount;
final String? userName;
final String fiatCurrency;
PaymentCredential({
required this.amount,
required this.quantity,
required this.totalAmount,
required this.userName,
required this.fiatCurrency,
});
}

View file

@ -0,0 +1,107 @@
import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cake_wallet/cake_pay/cake_pay_api.dart';
import 'package:cake_wallet/cake_pay/cake_pay_order.dart';
import 'package:cake_wallet/cake_pay/cake_pay_vendor.dart';
import 'package:cake_wallet/core/secure_storage.dart';
class CakePayService {
CakePayService(this.secureStorage, this.cakePayApi);
static const cakePayEmailStorageKey = 'cake_pay_email';
static const cakePayUsernameStorageKey = 'cake_pay_username';
static const cakePayUserTokenKey = 'cake_pay_user_token';
static String get testCakePayApiKey => secrets.testCakePayApiKey;
static String get cakePayApiKey => secrets.cakePayApiKey;
static String get CSRFToken => secrets.CSRFToken;
static String get authorization => secrets.authorization;
final SecureStorage secureStorage;
final CakePayApi cakePayApi;
/// Get Available Countries
Future<List<String>> getCountries() async =>
await cakePayApi.getCountries(CSRFToken: CSRFToken, authorization: authorization);
/// Get Vendors
Future<List<CakePayVendor>> getVendors({
int? page,
String? country,
String? countryCode,
String? search,
List<String>? vendorIds,
bool? giftCards,
bool? prepaidCards,
bool? onDemand,
bool? custom,
}) async {
final result = await cakePayApi.getVendors(
CSRFToken: CSRFToken,
authorization: authorization,
page: page,
country: country,
countryCode: countryCode,
search: search,
vendorIds: vendorIds,
giftCards: giftCards,
prepaidCards: prepaidCards,
onDemand: onDemand,
custom: custom);
return result;
}
/// LogIn
Future<void> logIn(String email) async {
final userName = await cakePayApi.authenticateUser(email: email, apiKey: cakePayApiKey);
await secureStorage.write(key: cakePayEmailStorageKey, value: userName);
await secureStorage.write(key: cakePayUsernameStorageKey, value: userName);
}
/// Verify email
Future<void> verifyEmail(String code) async {
final email = (await secureStorage.read(key: cakePayEmailStorageKey))!;
final credentials =
await cakePayApi.verifyEmail(email: email, code: code, apiKey: cakePayApiKey);
await secureStorage.write(key: cakePayUserTokenKey, value: credentials.token);
await secureStorage.write(key: cakePayUsernameStorageKey, value: credentials.username);
}
Future<String?> getUserEmail() async {
return (await secureStorage.read(key: cakePayEmailStorageKey));
}
/// Check is user logged
Future<bool> isLogged() async {
final username = await secureStorage.read(key: cakePayUsernameStorageKey) ?? '';
final password = await secureStorage.read(key: cakePayUserTokenKey) ?? '';
return username.isNotEmpty && password.isNotEmpty;
}
/// Logout
Future<void> logout(String email) async {
await secureStorage.delete(key: cakePayUsernameStorageKey);
await secureStorage.delete(key: cakePayUserTokenKey);
await cakePayApi.logoutUser(email: email, apiKey: cakePayApiKey);
}
/// Purchase Gift Card
Future<CakePayOrder> createOrder(
{required int cardId, required String price, required int quantity}) async {
final userEmail = (await secureStorage.read(key: cakePayEmailStorageKey))!;
final token = (await secureStorage.read(key: cakePayUserTokenKey))!;
return await cakePayApi.createOrder(
apiKey: cakePayApiKey,
cardId: cardId,
price: price,
quantity: quantity,
token: token,
userEmail: userEmail);
}
///Simulate Purchase Gift Card
Future<void> simulatePayment({required String orderId}) async => await cakePayApi.simulatePayment(
CSRFToken: CSRFToken, authorization: authorization, orderId: orderId);
}

View file

@ -0,0 +1,67 @@
import 'cake_pay_card.dart';
abstract class CakePayUserVerificationState {}
class CakePayUserVerificationStateInitial extends CakePayUserVerificationState {}
class CakePayUserVerificationStateSuccess extends CakePayUserVerificationState {}
class CakePayUserVerificationStatePending extends CakePayUserVerificationState {}
class CakePayUserVerificationStateLoading extends CakePayUserVerificationState {}
class CakePayUserVerificationStateFailure extends CakePayUserVerificationState {
CakePayUserVerificationStateFailure({required this.error});
final String error;
}
abstract class CakePayOtpState {}
class CakePayOtpValidating extends CakePayOtpState {}
class CakePayOtpSuccess extends CakePayOtpState {}
class CakePayOtpSendDisabled extends CakePayOtpState {}
class CakePayOtpSendEnabled extends CakePayOtpState {}
class CakePayOtpFailure extends CakePayOtpState {
CakePayOtpFailure({required this.error});
final String error;
}
class CakePayCreateCardState {}
class CakePayCreateCardStateSuccess extends CakePayCreateCardState {}
class CakePayCreateCardStateLoading extends CakePayCreateCardState {}
class CakePayCreateCardStateFailure extends CakePayCreateCardState {
CakePayCreateCardStateFailure({required this.error});
final String error;
}
class CakePayCardsState {}
class CakePayCardsStateNoCards extends CakePayCardsState {}
class CakePayCardsStateFetching extends CakePayCardsState {}
class CakePayCardsStateFailure extends CakePayCardsState {}
class CakePayCardsStateSuccess extends CakePayCardsState {
CakePayCardsStateSuccess({required this.card});
final CakePayCard card;
}
abstract class CakePayVendorState {}
class InitialCakePayVendorLoadingState extends CakePayVendorState {}
class CakePayVendorLoadingState extends CakePayVendorState {}
class CakePayVendorLoadedState extends CakePayVendorState {}

View file

@ -0,0 +1,6 @@
class CakePayUserCredentials {
const CakePayUserCredentials(this.username, this.token);
final String username;
final String token;
}

View file

@ -0,0 +1,51 @@
import 'dart:convert';
import 'cake_pay_card.dart';
class CakePayVendor {
final int id;
final String name;
final bool unavailable;
final String? cakeWarnings;
final List<String> countries;
final CakePayCard? card;
CakePayVendor({
required this.id,
required this.name,
required this.unavailable,
this.cakeWarnings,
required this.countries,
this.card,
});
factory CakePayVendor.fromJson(Map<String, dynamic> json) {
final name = stripHtmlIfNeeded(json['name'] as String);
final decodedName = fixEncoding(name);
var cardsJson = json['cards'] as List?;
CakePayCard? firstCard;
if (cardsJson != null && cardsJson.isNotEmpty) {
firstCard = CakePayCard.fromJson(cardsJson.first as Map<String, dynamic>);
}
return CakePayVendor(
id: json['id'] as int,
name: decodedName,
unavailable: json['unavailable'] as bool? ?? false,
cakeWarnings: json['cake_warnings'] as String?,
countries: List<String>.from(json['countries'] as List? ?? []),
card: firstCard,
);
}
static String stripHtmlIfNeeded(String text) {
return text.replaceAll(RegExp(r'<[^>]*>|&[^;]+;'), ' ');
}
static String fixEncoding(String text) {
final bytes = latin1.encode(text);
return utf8.decode(bytes, allowMalformed: true);
}
}

View file

@ -2,7 +2,6 @@ import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cake_wallet/anonpay/anonpay_api.dart';
import 'package:cake_wallet/anonpay/anonpay_info_base.dart';
import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart';
import 'package:cake_wallet/anypay/anypay_api.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
@ -33,16 +32,10 @@ import 'package:cake_wallet/entities/qr_view_data.dart';
import 'package:cake_wallet/entities/template.dart';
import 'package:cake_wallet/entities/transaction_description.dart';
import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/cake_pay/cake_pay_card.dart';
import 'package:cake_wallet/exchange/exchange_template.dart';
import 'package:cake_wallet/exchange/trade.dart';
import 'package:cake_wallet/haven/haven.dart';
import 'package:cake_wallet/ionia/ionia_any_pay_payment_info.dart';
import 'package:cake_wallet/ionia/ionia_anypay.dart';
import 'package:cake_wallet/ionia/ionia_api.dart';
import 'package:cake_wallet/ionia/ionia_gift_card.dart';
import 'package:cake_wallet/ionia/ionia_merchant.dart';
import 'package:cake_wallet/ionia/ionia_service.dart';
import 'package:cake_wallet/ionia/ionia_tip.dart';
import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/nano/nano.dart';
import 'package:cake_wallet/polygon/polygon.dart';
@ -72,14 +65,6 @@ import 'package:cake_wallet/src/screens/exchange/exchange_template_page.dart';
import 'package:cake_wallet/src/screens/exchange_trade/exchange_confirm_page.dart';
import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_page.dart';
import 'package:cake_wallet/src/screens/faq/faq_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_cards_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_redeem_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_tip_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_gift_card_detail_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_more_options_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_payment_status_page.dart';
import 'package:cake_wallet/src/screens/ionia/ionia.dart';
import 'package:cake_wallet/src/screens/monero_accounts/monero_account_edit_or_create_page.dart';
import 'package:cake_wallet/src/screens/monero_accounts/monero_account_list_page.dart';
import 'package:cake_wallet/src/screens/nano/nano_change_rep_page.dart';
@ -124,16 +109,57 @@ import 'package:cake_wallet/src/screens/subaddress/address_edit_or_create_page.d
import 'package:cake_wallet/src/screens/support/support_page.dart';
import 'package:cake_wallet/src/screens/support_chat/support_chat_page.dart';
import 'package:cake_wallet/src/screens/support_other_links/support_other_links_page.dart';
import 'package:cake_wallet/src/screens/wallet/wallet_edit_page.dart';
import 'package:cake_wallet/src/screens/wallet_connect/wc_connections_listing_view.dart';
import 'package:cake_wallet/themes/theme_list.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart';
import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/view_model/dashboard/desktop_sidebar_view_model.dart';
import 'package:cake_wallet/view_model/anon_invoice_page_view_model.dart';
import 'package:cake_wallet/view_model/anonpay_details_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/home_settings_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart';
import 'package:cake_wallet/view_model/cake_pay/cake_pay_auth_view_model.dart';
import 'package:cake_wallet/view_model/cake_pay/cake_pay_buy_card_view_model.dart';
import 'package:cake_wallet/cake_pay/cake_pay_service.dart';
import 'package:cake_wallet/cake_pay/cake_pay_api.dart';
import 'package:cake_wallet/cake_pay/cake_pay_vendor.dart';
import 'package:cake_wallet/src/screens/cake_pay/auth/cake_pay_account_page.dart';
import 'package:cake_wallet/src/screens/cake_pay/cake_pay.dart';
import 'package:cake_wallet/view_model/cake_pay/cake_pay_account_view_model.dart';
import 'package:cake_wallet/view_model/cake_pay/cake_pay_cards_list_view_model.dart';
import 'package:cake_wallet/view_model/cake_pay/cake_pay_purchase_view_model.dart';
import 'package:cake_wallet/view_model/nano_account_list/nano_account_edit_or_create_view_model.dart';
import 'package:cake_wallet/view_model/nano_account_list/nano_account_list_view_model.dart';
import 'package:cake_wallet/view_model/node_list/pow_node_list_view_model.dart';
import 'package:cake_wallet/view_model/seed_type_view_model.dart';
import 'package:cake_wallet/view_model/set_up_2fa_viewmodel.dart';
import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart';
import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart';
import 'package:cake_wallet/view_model/settings/other_settings_view_model.dart';
import 'package:cake_wallet/view_model/settings/privacy_settings_view_model.dart';
import 'package:cake_wallet/view_model/settings/security_settings_view_model.dart';
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
import 'package:cake_wallet/view_model/settings/trocador_providers_view_model.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_edit_view_model.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
import 'package:cake_wallet/view_model/wallet_restore_choose_derivation_view_model.dart';
import 'package:cw_core/nano_account.dart';
import 'package:cw_core/unspent_coins_info.dart';
import 'package:cw_core/wallet_service.dart';
import 'package:cw_core/transaction_info.dart';
import 'package:cw_core/node.dart';
import 'package:cake_wallet/src/screens/trade_details/trade_details_page.dart';
import 'package:cake_wallet/src/screens/transaction_details/rbf_details_page.dart';
import 'package:cake_wallet/src/screens/transaction_details/transaction_details_page.dart';
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_details_page.dart';
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart';
import 'package:cake_wallet/src/screens/wallet/wallet_edit_page.dart';
import 'package:cake_wallet/src/screens/wallet_connect/wc_connections_listing_view.dart';
import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart';
import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart';
import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/store/authentication_store.dart';
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
@ -148,14 +174,7 @@ import 'package:cake_wallet/store/templates/exchange_template_store.dart';
import 'package:cake_wallet/store/templates/send_template_store.dart';
import 'package:cake_wallet/store/wallet_list_store.dart';
import 'package:cake_wallet/store/yat/yat_store.dart';
import 'package:cake_wallet/themes/theme_list.dart';
import 'package:cake_wallet/tron/tron.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
import 'package:cake_wallet/view_model/anon_invoice_page_view_model.dart';
import 'package:cake_wallet/view_model/anonpay_details_view_model.dart';
import 'package:cake_wallet/view_model/auth_view_model.dart';
import 'package:cake_wallet/view_model/backup_view_model.dart';
import 'package:cake_wallet/view_model/buy/buy_amount_view_model.dart';
@ -165,46 +184,22 @@ import 'package:cake_wallet/view_model/contact_list/contact_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/cake_features_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/desktop_sidebar_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/home_settings_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart';
import 'package:cake_wallet/view_model/edit_backup_password_view_model.dart';
import 'package:cake_wallet/view_model/exchange/exchange_trade_view_model.dart';
import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart';
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_account_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_auth_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_buy_card_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_custom_redeem_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_custom_tip_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_gift_card_details_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_gift_cards_list_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_payment_status_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_purchase_merch_view_model.dart';
import 'package:cake_wallet/view_model/link_view_model.dart';
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
import 'package:cake_wallet/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart';
import 'package:cake_wallet/view_model/monero_account_list/monero_account_list_view_model.dart';
import 'package:cake_wallet/view_model/nano_account_list/nano_account_edit_or_create_view_model.dart';
import 'package:cake_wallet/view_model/nano_account_list/nano_account_list_view_model.dart';
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart';
import 'package:cake_wallet/view_model/node_list/pow_node_list_view_model.dart';
import 'package:cake_wallet/view_model/order_details_view_model.dart';
import 'package:cake_wallet/view_model/rescan_view_model.dart';
import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart';
import 'package:cake_wallet/view_model/restore_from_backup_view_model.dart';
import 'package:cake_wallet/view_model/seed_type_view_model.dart';
import 'package:cake_wallet/view_model/send/send_template_view_model.dart';
import 'package:cake_wallet/view_model/send/send_view_model.dart';
import 'package:cake_wallet/view_model/set_up_2fa_viewmodel.dart';
import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart';
import 'package:cake_wallet/view_model/settings/other_settings_view_model.dart';
import 'package:cake_wallet/view_model/settings/privacy_settings_view_model.dart';
import 'package:cake_wallet/view_model/settings/security_settings_view_model.dart';
import 'package:cake_wallet/view_model/settings/silent_payments_settings_view_model.dart';
import 'package:cake_wallet/view_model/settings/trocador_providers_view_model.dart';
import 'package:cake_wallet/view_model/setup_pin_code_view_model.dart';
import 'package:cake_wallet/view_model/support_view_model.dart';
import 'package:cake_wallet/view_model/trade_details_view_model.dart';
@ -213,25 +208,16 @@ import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_details_view_
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_item.dart';
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_list_view_model.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
import 'package:cake_wallet/view_model/wallet_hardware_restore_view_model.dart';
import 'package:cake_wallet/view_model/wallet_keys_view_model.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_edit_view_model.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
import 'package:cake_wallet/view_model/wallet_new_vm.dart';
import 'package:cake_wallet/view_model/wallet_restore_choose_derivation_view_model.dart';
import 'package:cake_wallet/view_model/wallet_restore_view_model.dart';
import 'package:cake_wallet/view_model/wallet_seed_view_model.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/nano_account.dart';
import 'package:cw_core/node.dart';
import 'package:cw_core/receive_page_option.dart';
import 'package:cw_core/transaction_info.dart';
import 'package:cw_core/unspent_coins_info.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_service.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
@ -239,6 +225,7 @@ import 'package:get_it/get_it.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'cake_pay/cake_pay_payment_credantials.dart';
final getIt = GetIt.instance;
@ -993,6 +980,8 @@ Future<void> setup({
trades: _tradesSource,
settingsStore: getIt.get<SettingsStore>()));
getIt.registerFactory(() => CakeFeaturesViewModel(getIt.get<CakePayService>()));
getIt.registerFactory(() => BackupService(getIt.get<SecureStorage>(), _walletInfoSource,
getIt.get<KeyService>(), getIt.get<SharedPreferences>()));
@ -1088,113 +1077,60 @@ Future<void> setup({
getIt.registerFactoryParam<FullscreenQRPage, QrViewData, void>(
(QrViewData viewData, _) => FullscreenQRPage(qrViewData: viewData));
getIt.registerFactory(() => IoniaApi());
getIt.registerFactory(() => CakePayApi());
getIt.registerFactory(() => AnyPayApi());
getIt.registerFactory<IoniaService>(
() => IoniaService(getIt.get<SecureStorage>(), getIt.get<IoniaApi>()));
getIt.registerFactory<CakePayService>(
() => CakePayService(getIt.get<SecureStorage>(), getIt.get<CakePayApi>()));
getIt.registerFactory<IoniaAnyPay>(() => IoniaAnyPay(
getIt.get<IoniaService>(), getIt.get<AnyPayApi>(), getIt.get<AppStore>().wallet!));
getIt.registerFactory(() => CakePayCardsListViewModel(cakePayService: getIt.get<CakePayService>()));
getIt.registerFactory(() => IoniaGiftCardsListViewModel(ioniaService: getIt.get<IoniaService>()));
getIt.registerFactory(() => CakePayAuthViewModel(cakePayService: getIt.get<CakePayService>()));
getIt.registerFactory(() => CakeFeaturesViewModel(getIt.get<IoniaService>()));
getIt.registerFactory(() => IoniaAuthViewModel(ioniaService: getIt.get<IoniaService>()));
getIt.registerFactoryParam<IoniaMerchPurchaseViewModel, double, IoniaMerchant>(
(double amount, merchant) {
return IoniaMerchPurchaseViewModel(
ioniaAnyPayService: getIt.get<IoniaAnyPay>(),
amount: amount,
ioniaMerchant: merchant,
getIt.registerFactoryParam<CakePayPurchaseViewModel, PaymentCredential, CakePayCard>(
(PaymentCredential paymentCredential, CakePayCard card) {
return CakePayPurchaseViewModel(
cakePayService: getIt.get<CakePayService>(),
paymentCredential: paymentCredential,
card: card,
sendViewModel: getIt.get<SendViewModel>());
});
getIt.registerFactoryParam<IoniaBuyCardViewModel, IoniaMerchant, void>(
(IoniaMerchant merchant, _) {
return IoniaBuyCardViewModel(ioniaMerchant: merchant);
getIt.registerFactoryParam<CakePayBuyCardViewModel, CakePayVendor, void>(
(CakePayVendor vendor, _) {
return CakePayBuyCardViewModel(vendor: vendor);
});
getIt.registerFactory(() => IoniaAccountViewModel(ioniaService: getIt.get<IoniaService>()));
getIt.registerFactory(() => CakePayAccountViewModel(cakePayService: getIt.get<CakePayService>()));
getIt.registerFactory(() => IoniaCreateAccountPage(getIt.get<IoniaAuthViewModel>()));
getIt.registerFactory(() => CakePayWelcomePage(getIt.get<CakePayAuthViewModel>()));
getIt.registerFactory(() => IoniaLoginPage(getIt.get<IoniaAuthViewModel>()));
getIt.registerFactoryParam<IoniaVerifyIoniaOtp, List<dynamic>, void>((List<dynamic> args, _) {
getIt.registerFactoryParam<CakePayVerifyOtpPage, List<dynamic>, void>((List<dynamic> args, _) {
final email = args.first as String;
final isSignIn = args[1] as bool;
return IoniaVerifyIoniaOtp(getIt.get<IoniaAuthViewModel>(), email, isSignIn);
return CakePayVerifyOtpPage(getIt.get<CakePayAuthViewModel>(), email, isSignIn);
});
getIt.registerFactory(() => IoniaWelcomePage());
getIt.registerFactoryParam<CakePayBuyCardPage, List<dynamic>, void>((List<dynamic> args, _) {
final vendor = args.first as CakePayVendor;
getIt.registerFactoryParam<IoniaBuyGiftCardPage, List<dynamic>, void>((List<dynamic> args, _) {
final merchant = args.first as IoniaMerchant;
return IoniaBuyGiftCardPage(getIt.get<IoniaBuyCardViewModel>(param1: merchant));
return CakePayBuyCardPage(getIt.get<CakePayBuyCardViewModel>(param1: vendor),
getIt.get<CakePayService>());
});
getIt.registerFactoryParam<IoniaBuyGiftCardDetailPage, List<dynamic>, void>(
getIt.registerFactoryParam<CakePayBuyCardDetailPage, List<dynamic>, void>(
(List<dynamic> args, _) {
final amount = args.first as double;
final merchant = args.last as IoniaMerchant;
return IoniaBuyGiftCardDetailPage(
getIt.get<IoniaMerchPurchaseViewModel>(param1: amount, param2: merchant));
final paymentCredential = args.first as PaymentCredential;
final card = args[1] as CakePayCard;
return CakePayBuyCardDetailPage(
getIt.get<CakePayPurchaseViewModel>(param1: paymentCredential, param2: card));
});
getIt.registerFactoryParam<IoniaGiftCardDetailsViewModel, IoniaGiftCard, void>(
(IoniaGiftCard giftCard, _) {
return IoniaGiftCardDetailsViewModel(
ioniaService: getIt.get<IoniaService>(), giftCard: giftCard);
});
getIt.registerFactory(() => CakePayCardsPage(getIt.get<CakePayCardsListViewModel>()));
getIt.registerFactoryParam<IoniaCustomTipViewModel, List<dynamic>, void>((List<dynamic> args, _) {
final amount = args[0] as double;
final merchant = args[1] as IoniaMerchant;
final tip = args[2] as IoniaTip;
return IoniaCustomTipViewModel(amount: amount, tip: tip, ioniaMerchant: merchant);
});
getIt.registerFactoryParam<IoniaGiftCardDetailPage, IoniaGiftCard, void>(
(IoniaGiftCard giftCard, _) {
return IoniaGiftCardDetailPage(getIt.get<IoniaGiftCardDetailsViewModel>(param1: giftCard));
});
getIt.registerFactoryParam<IoniaMoreOptionsPage, List<dynamic>, void>((List<dynamic> args, _) {
final giftCard = args.first as IoniaGiftCard;
return IoniaMoreOptionsPage(giftCard);
});
getIt.registerFactoryParam<IoniaCustomRedeemViewModel, IoniaGiftCard, void>(
(IoniaGiftCard giftCard, _) =>
IoniaCustomRedeemViewModel(giftCard: giftCard, ioniaService: getIt.get<IoniaService>()));
getIt.registerFactoryParam<IoniaCustomRedeemPage, List<dynamic>, void>((List<dynamic> args, _) {
final giftCard = args.first as IoniaGiftCard;
return IoniaCustomRedeemPage(getIt.get<IoniaCustomRedeemViewModel>(param1: giftCard));
});
getIt.registerFactoryParam<IoniaCustomTipPage, List<dynamic>, void>((List<dynamic> args, _) {
return IoniaCustomTipPage(getIt.get<IoniaCustomTipViewModel>(param1: args));
});
getIt.registerFactory(() => IoniaManageCardsPage(getIt.get<IoniaGiftCardsListViewModel>()));
getIt.registerFactory(() => IoniaDebitCardPage(getIt.get<IoniaGiftCardsListViewModel>()));
getIt.registerFactory(() => IoniaActivateDebitCardPage(getIt.get<IoniaGiftCardsListViewModel>()));
getIt.registerFactory(() => IoniaAccountPage(getIt.get<IoniaAccountViewModel>()));
getIt.registerFactory(() => IoniaAccountCardsPage(getIt.get<IoniaAccountViewModel>()));
getIt.registerFactory(() => CakePayAccountPage(getIt.get<CakePayAccountViewModel>()));
getIt.registerFactoryParam<RBFDetailsPage, TransactionInfo, void>(
(TransactionInfo transactionInfo, _) => RBFDetailsPage(
@ -1225,18 +1161,6 @@ Future<void> setup({
(AnonpayInvoiceInfo anonpayInvoiceInfo, _) => AnonpayDetailsPage(
anonpayDetailsViewModel: getIt.get<AnonpayDetailsViewModel>(param1: anonpayInvoiceInfo)));
getIt.registerFactoryParam<IoniaPaymentStatusViewModel, IoniaAnyPayPaymentInfo,
AnyPayPaymentCommittedInfo>(
(IoniaAnyPayPaymentInfo paymentInfo, AnyPayPaymentCommittedInfo committedInfo) =>
IoniaPaymentStatusViewModel(getIt.get<IoniaService>(),
paymentInfo: paymentInfo, committedInfo: committedInfo));
getIt.registerFactoryParam<IoniaPaymentStatusPage, IoniaAnyPayPaymentInfo,
AnyPayPaymentCommittedInfo>(
(IoniaAnyPayPaymentInfo paymentInfo, AnyPayPaymentCommittedInfo committedInfo) =>
IoniaPaymentStatusPage(
getIt.get<IoniaPaymentStatusViewModel>(param1: paymentInfo, param2: committedInfo)));
getIt.registerFactoryParam<HomeSettingsPage, BalanceViewModel, void>((balanceViewModel, _) =>
HomeSettingsPage(getIt.get<HomeSettingsViewModel>(param1: balanceViewModel)));

View file

@ -1,9 +0,0 @@
import 'package:cake_wallet/anypay/any_pay_payment.dart';
import 'package:cake_wallet/ionia/ionia_order.dart';
class IoniaAnyPayPaymentInfo {
const IoniaAnyPayPaymentInfo(this.ioniaOrder, this.anyPayPayment);
final IoniaOrder ioniaOrder;
final AnyPayPayment anyPayPayment;
}

View file

@ -1,91 +0,0 @@
import 'package:cw_core/monero_amount_format.dart';
import 'package:cw_core/monero_transaction_priority.dart';
import 'package:cw_core/output_info.dart';
import 'package:cw_core/pending_transaction.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cake_wallet/anypay/any_pay_payment.dart';
import 'package:cake_wallet/anypay/any_pay_payment_instruction.dart';
import 'package:cake_wallet/ionia/ionia_service.dart';
import 'package:cake_wallet/anypay/anypay_api.dart';
import 'package:cake_wallet/anypay/any_pay_chain.dart';
import 'package:cake_wallet/anypay/any_pay_trasnaction.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart';
import 'package:cake_wallet/ionia/ionia_any_pay_payment_info.dart';
class IoniaAnyPay {
IoniaAnyPay(this.ioniaService, this.anyPayApi, this.wallet);
final IoniaService ioniaService;
final AnyPayApi anyPayApi;
final WalletBase wallet;
Future<IoniaAnyPayPaymentInfo> purchase({
required String merchId,
required double amount}) async {
final invoice = await ioniaService.purchaseGiftCard(
merchId: merchId,
amount: amount,
currency: wallet.currency.title.toUpperCase());
final anypayPayment = await anyPayApi.paymentRequest(invoice.uri);
return IoniaAnyPayPaymentInfo(invoice, anypayPayment);
}
Future<AnyPayPaymentCommittedInfo> commitInvoice(AnyPayPayment payment) async {
final transactionCredentials = payment.instructions
.where((instruction) => instruction.type == AnyPayPaymentInstruction.transactionType)
.map((AnyPayPaymentInstruction instruction) {
switch(payment.chain.toUpperCase()) {
case AnyPayChain.xmr:
return monero!.createMoneroTransactionCreationCredentialsRaw(
outputs: instruction.outputs.map((out) =>
OutputInfo(
isParsedAddress: false,
address: out.address,
cryptoAmount: moneroAmountToString(amount: out.amount),
formattedCryptoAmount: out.amount,
sendAll: false)).toList(),
priority: MoneroTransactionPriority.medium); // FIXME: HARDCODED PRIORITY
case AnyPayChain.btc:
return bitcoin!.createBitcoinTransactionCredentialsRaw(
instruction.outputs.map((out) =>
OutputInfo(
isParsedAddress: false,
address: out.address,
formattedCryptoAmount: out.amount,
sendAll: false)).toList(),
feeRate: instruction.requiredFeeRate);
case AnyPayChain.ltc:
return bitcoin!.createBitcoinTransactionCredentialsRaw(
instruction.outputs.map((out) =>
OutputInfo(
isParsedAddress: false,
address: out.address,
formattedCryptoAmount: out.amount,
sendAll: false)).toList(),
feeRate: instruction.requiredFeeRate);
default:
throw Exception('Incorrect transaction chain: ${payment.chain.toUpperCase()}');
}
});
final transactions = (await Future.wait(transactionCredentials
.map((Object credentials) async => await wallet.createTransaction(credentials))))
.map((PendingTransaction pendingTransaction) {
switch (payment.chain.toUpperCase()){
case AnyPayChain.xmr:
final ptx = monero!.pendingTransactionInfo(pendingTransaction);
return AnyPayTransaction(ptx['hex'] ?? '', id: ptx['id'] ?? '', key: ptx['key']);
default:
return AnyPayTransaction(pendingTransaction.hex, id: pendingTransaction.id, key: null);
}
})
.toList();
return await anyPayApi.payment(
payment.paymentUrl,
chain: payment.chain,
currency: payment.chain,
transactions: transactions);
}
}

View file

@ -1,440 +0,0 @@
import 'dart:convert';
import 'package:cake_wallet/ionia/ionia_merchant.dart';
import 'package:cake_wallet/ionia/ionia_order.dart';
import 'package:http/http.dart';
import 'package:cake_wallet/ionia/ionia_user_credentials.dart';
import 'package:cake_wallet/ionia/ionia_virtual_card.dart';
import 'package:cake_wallet/ionia/ionia_category.dart';
import 'package:cake_wallet/ionia/ionia_gift_card.dart';
class IoniaApi {
static const baseUri = 'api.ionia.io';
static const pathPrefix = 'cake';
static const requestedUUIDHeader = 'requestedUUID';
static final createUserUri = Uri.https(baseUri, '/$pathPrefix/CreateUser');
static final verifyEmailUri = Uri.https(baseUri, '/$pathPrefix/VerifyEmail');
static final signInUri = Uri.https(baseUri, '/$pathPrefix/SignIn');
static final createCardUri = Uri.https(baseUri, '/$pathPrefix/CreateCard');
static final getCardsUri = Uri.https(baseUri, '/$pathPrefix/GetCards');
static final getMerchantsUrl = Uri.https(baseUri, '/$pathPrefix/GetMerchants');
static final getMerchantsByFilterUrl = Uri.https(baseUri, '/$pathPrefix/GetMerchantsByFilter');
static final getPurchaseMerchantsUrl = Uri.https(baseUri, '/$pathPrefix/PurchaseGiftCard');
static final getCurrentUserGiftCardSummariesUrl = Uri.https(baseUri, '/$pathPrefix/GetCurrentUserGiftCardSummaries');
static final changeGiftCardUrl = Uri.https(baseUri, '/$pathPrefix/ChargeGiftCard');
static final getGiftCardUrl = Uri.https(baseUri, '/$pathPrefix/GetGiftCard');
static final getPaymentStatusUrl = Uri.https(baseUri, '/$pathPrefix/PaymentStatus');
// Create user
Future<String> createUser(String email, {required String clientId}) async {
final headers = <String, String>{'clientId': clientId};
final query = <String, String>{'emailAddress': email};
final uri = createUserUri.replace(queryParameters: query);
final response = await put(uri, headers: headers);
if (response.statusCode != 200) {
throw Exception('Unexpected http status: ${response.statusCode}');
}
final bodyJson = json.decode(response.body) as Map<String, dynamic>;
final data = bodyJson['Data'] as Map<String, dynamic>;
final isSuccessful = bodyJson['Successful'] as bool;
if (!isSuccessful) {
throw Exception(data['ErrorMessage'] as String);
}
return data['username'] as String;
}
// Verify email
Future<IoniaUserCredentials> verifyEmail({
required String email,
required String code,
required String clientId}) async {
final headers = <String, String>{
'clientId': clientId,
'EmailAddress': email};
final query = <String, String>{'verificationCode': code};
final uri = verifyEmailUri.replace(queryParameters: query);
final response = await put(uri, headers: headers);
if (response.statusCode != 200) {
throw Exception('Unexpected http status: ${response.statusCode}');
}
final bodyJson = json.decode(response.body) as Map<String, dynamic>;
final data = bodyJson['Data'] as Map<String, dynamic>;
final isSuccessful = bodyJson['Successful'] as bool;
if (!isSuccessful) {
throw Exception(bodyJson['ErrorMessage'] as String);
}
final password = data['password'] as String;
final username = data['username'] as String;
return IoniaUserCredentials(username, password);
}
// Sign In
Future<void> signIn(String email, {required String clientId}) async {
final headers = <String, String>{'clientId': clientId};
final query = <String, String>{'emailAddress': email};
final uri = signInUri.replace(queryParameters: query);
final response = await put(uri, headers: headers);
if (response.statusCode != 200) {
throw Exception('Unexpected http status: ${response.statusCode}');
}
final bodyJson = json.decode(response.body) as Map<String, dynamic>;
final data = bodyJson['Data'] as Map<String, dynamic>;
final isSuccessful = bodyJson['Successful'] as bool;
if (!isSuccessful) {
throw Exception(data['ErrorMessage'] as String);
}
}
// Get virtual card
Future<IoniaVirtualCard> getCards({
required String username,
required String password,
required String clientId}) async {
final headers = <String, String>{
'clientId': clientId,
'username': username,
'password': password};
final response = await post(getCardsUri, headers: headers);
if (response.statusCode != 200) {
throw Exception('Unexpected http status: ${response.statusCode}');
}
final bodyJson = json.decode(response.body) as Map<String, dynamic>;
final data = bodyJson['Data'] as Map<String, dynamic>;
final isSuccessful = bodyJson['Successful'] as bool;
if (!isSuccessful) {
throw Exception(data['message'] as String);
}
final virtualCard = data['VirtualCard'] as Map<String, dynamic>;
return IoniaVirtualCard.fromMap(virtualCard);
}
// Create virtual card
Future<IoniaVirtualCard> createCard({
required String username,
required String password,
required String clientId}) async {
final headers = <String, String>{
'clientId': clientId,
'username': username,
'password': password};
final response = await post(createCardUri, headers: headers);
if (response.statusCode != 200) {
throw Exception('Unexpected http status: ${response.statusCode}');
}
final bodyJson = json.decode(response.body) as Map<String, dynamic>;
final data = bodyJson['Data'] as Map<String, dynamic>;
final isSuccessful = bodyJson['Successful'] as bool? ?? false;
if (!isSuccessful) {
throw Exception(data['message'] as String);
}
return IoniaVirtualCard.fromMap(data);
}
// Get Merchants
Future<List<IoniaMerchant>> getMerchants({
required String username,
required String password,
required String clientId}) async {
final headers = <String, String>{
'clientId': clientId,
'username': username,
'password': password};
final response = await post(getMerchantsUrl, headers: headers);
if (response.statusCode != 200) {
return [];
}
final decodedBody = json.decode(response.body) as Map<String, dynamic>;
final isSuccessful = decodedBody['Successful'] as bool? ?? false;
if (!isSuccessful) {
return [];
}
final data = decodedBody['Data'] as List<dynamic>;
final merch = <IoniaMerchant>[];
for (final item in data) {
try {
final element = item as Map<String, dynamic>;
merch.add(IoniaMerchant.fromJsonMap(element));
} catch(_) {}
}
return merch;
}
// Get Merchants By Filter
Future<List<IoniaMerchant>> getMerchantsByFilter({
required String username,
required String password,
required String clientId,
String? search,
List<IoniaCategory>? categories,
int merchantFilterType = 0}) async {
// MerchantFilterType: {All = 0, Nearby = 1, Popular = 2, Online = 3, MyFaves = 4, Search = 5}
final headers = <String, String>{
'clientId': clientId,
'username': username,
'password': password,
'Content-Type': 'application/json'};
final body = <String, dynamic>{'MerchantFilterType': merchantFilterType};
if (search != null) {
body['SearchCriteria'] = search;
}
if (categories != null) {
body['Categories'] = categories
.map((e) => e.ids)
.expand((e) => e)
.toList();
}
final response = await post(getMerchantsByFilterUrl, headers: headers, body: json.encode(body));
if (response.statusCode != 200) {
return [];
}
final decodedBody = json.decode(response.body) as Map<String, dynamic>;
final isSuccessful = decodedBody['Successful'] as bool? ?? false;
if (!isSuccessful) {
return [];
}
final data = decodedBody['Data'] as List<dynamic>;
final merch = <IoniaMerchant>[];
for (final item in data) {
try {
final element = item['Merchant'] as Map<String, dynamic>;
merch.add(IoniaMerchant.fromJsonMap(element));
} catch(_) {}
}
return merch;
}
// Purchase Gift Card
Future<IoniaOrder> purchaseGiftCard({
required String requestedUUID,
required String merchId,
required double amount,
required String currency,
required String username,
required String password,
required String clientId}) async {
final headers = <String, String>{
'clientId': clientId,
'username': username,
'password': password,
requestedUUIDHeader: requestedUUID,
'Content-Type': 'application/json'};
final body = <String, dynamic>{
'Amount': amount,
'Currency': currency,
'MerchantId': merchId};
final response = await post(getPurchaseMerchantsUrl, headers: headers, body: json.encode(body));
if (response.statusCode != 200) {
throw Exception('Unexpected response');
}
final decodedBody = json.decode(response.body) as Map<String, dynamic>;
final isSuccessful = decodedBody['Successful'] as bool? ?? false;
if (!isSuccessful) {
throw Exception(decodedBody['ErrorMessage'] as String);
}
final data = decodedBody['Data'] as Map<String, dynamic>;
return IoniaOrder.fromMap(data);
}
// Get Current User Gift Card Summaries
Future<List<IoniaGiftCard>> getCurrentUserGiftCardSummaries({
required String username,
required String password,
required String clientId}) async {
final headers = <String, String>{
'clientId': clientId,
'username': username,
'password': password};
final response = await post(getCurrentUserGiftCardSummariesUrl, headers: headers);
if (response.statusCode != 200) {
return [];
}
final decodedBody = json.decode(response.body) as Map<String, dynamic>;
final isSuccessful = decodedBody['Successful'] as bool? ?? false;
if (!isSuccessful) {
return [];
}
final data = decodedBody['Data'] as List<dynamic>;
final cards = <IoniaGiftCard>[];
for (final item in data) {
try {
final element = item as Map<String, dynamic>;
cards.add(IoniaGiftCard.fromJsonMap(element));
} catch(_) {}
}
return cards;
}
// Charge Gift Card
Future<void> chargeGiftCard({
required String username,
required String password,
required String clientId,
required int giftCardId,
required double amount}) async {
final headers = <String, String>{
'clientId': clientId,
'username': username,
'password': password,
'Content-Type': 'application/json'};
final body = <String, dynamic>{
'Id': giftCardId,
'Amount': amount};
final response = await post(
changeGiftCardUrl,
headers: headers,
body: json.encode(body));
if (response.statusCode != 200) {
throw Exception('Failed to update Gift Card with ID ${giftCardId};Incorrect response status: ${response.statusCode};');
}
final decodedBody = json.decode(response.body) as Map<String, dynamic>;
final isSuccessful = decodedBody['Successful'] as bool? ?? false;
if (!isSuccessful) {
final data = decodedBody['Data'] as Map<String, dynamic>;
final msg = data['Message'] as String? ?? '';
if (msg.isNotEmpty) {
throw Exception(msg);
}
throw Exception('Failed to update Gift Card with ID ${giftCardId};');
}
}
// Get Gift Card
Future<IoniaGiftCard> getGiftCard({
required String username,
required String password,
required String clientId,
required int id}) async {
final headers = <String, String>{
'clientId': clientId,
'username': username,
'password': password,
'Content-Type': 'application/json'};
final body = <String, dynamic>{'Id': id};
final response = await post(
getGiftCardUrl,
headers: headers,
body: json.encode(body));
if (response.statusCode != 200) {
throw Exception('Failed to get Gift Card with ID ${id};Incorrect response status: ${response.statusCode};');
}
final decodedBody = json.decode(response.body) as Map<String, dynamic>;
final isSuccessful = decodedBody['Successful'] as bool? ?? false;
if (!isSuccessful) {
final msg = decodedBody['ErrorMessage'] as String ?? '';
if (msg.isNotEmpty) {
throw Exception(msg);
}
throw Exception('Failed to get Gift Card with ID ${id};');
}
final data = decodedBody['Data'] as Map<String, dynamic>;
return IoniaGiftCard.fromJsonMap(data);
}
// Payment Status
Future<int> getPaymentStatus({
required String username,
required String password,
required String clientId,
required String orderId,
required String paymentId}) async {
final headers = <String, String>{
'clientId': clientId,
'username': username,
'password': password,
'Content-Type': 'application/json'};
final body = <String, dynamic>{
'order_id': orderId,
'paymentId': paymentId};
final response = await post(
getPaymentStatusUrl,
headers: headers,
body: json.encode(body));
if (response.statusCode != 200) {
throw Exception('Failed to get Payment Status for order_id ${orderId} paymentId ${paymentId};Incorrect response status: ${response.statusCode};');
}
final decodedBody = json.decode(response.body) as Map<String, dynamic>;
final isSuccessful = decodedBody['Successful'] as bool? ?? false;
if (!isSuccessful) {
final msg = decodedBody['ErrorMessage'] as String ?? '';
if (msg.isNotEmpty) {
throw Exception(msg);
}
throw Exception('Failed to get Payment Status for order_id ${orderId} paymentId ${paymentId}');
}
final data = decodedBody['Data'] as Map<String, dynamic>;
return data['gift_card_id'] as int;
}
}

View file

@ -1,22 +0,0 @@
class IoniaCategory {
const IoniaCategory({
required this.index,
required this.title,
required this.ids,
required this.iconPath});
static const allCategories = <IoniaCategory>[all, apparel, onlineOnly, food, entertainment, delivery, travel];
static const all = IoniaCategory(index: 0, title: 'All', ids: [], iconPath: 'assets/images/category.png');
static const apparel = IoniaCategory(index: 1, title: 'Apparel', ids: [1], iconPath: 'assets/images/tshirt.png');
static const onlineOnly = IoniaCategory(index: 2, title: 'Online Only', ids: [13, 43], iconPath: 'assets/images/global.png');
static const food = IoniaCategory(index: 3, title: 'Food', ids: [4], iconPath: 'assets/images/food.png');
static const entertainment = IoniaCategory(index: 4, title: 'Entertainment', ids: [5], iconPath: 'assets/images/gaming.png');
static const delivery = IoniaCategory(index: 5, title: 'Delivery', ids: [114, 109], iconPath: 'assets/images/delivery.png');
static const travel = IoniaCategory(index: 6, title: 'Travel', ids: [12], iconPath: 'assets/images/airplane.png');
final int index;
final String title;
final List<int> ids;
final String iconPath;
}

View file

@ -1,68 +0,0 @@
import 'package:cake_wallet/ionia/ionia_virtual_card.dart';
import 'package:flutter/material.dart';
abstract class IoniaCreateAccountState {}
class IoniaInitialCreateState extends IoniaCreateAccountState {}
class IoniaCreateStateSuccess extends IoniaCreateAccountState {}
class IoniaCreateStateLoading extends IoniaCreateAccountState {}
class IoniaCreateStateFailure extends IoniaCreateAccountState {
IoniaCreateStateFailure({required this.error});
final String error;
}
abstract class IoniaOtpState {}
class IoniaOtpValidating extends IoniaOtpState {}
class IoniaOtpSuccess extends IoniaOtpState {}
class IoniaOtpSendDisabled extends IoniaOtpState {}
class IoniaOtpSendEnabled extends IoniaOtpState {}
class IoniaOtpFailure extends IoniaOtpState {
IoniaOtpFailure({required this.error});
final String error;
}
class IoniaCreateCardState {}
class IoniaCreateCardSuccess extends IoniaCreateCardState {}
class IoniaCreateCardLoading extends IoniaCreateCardState {}
class IoniaCreateCardFailure extends IoniaCreateCardState {
IoniaCreateCardFailure({required this.error});
final String error;
}
class IoniaFetchCardState {}
class IoniaNoCardState extends IoniaFetchCardState {}
class IoniaFetchingCard extends IoniaFetchCardState {}
class IoniaFetchCardFailure extends IoniaFetchCardState {}
class IoniaCardSuccess extends IoniaFetchCardState {
IoniaCardSuccess({required this.card});
final IoniaVirtualCard card;
}
abstract class IoniaMerchantState {}
class InitialIoniaMerchantLoadingState extends IoniaMerchantState {}
class IoniaLoadingMerchantState extends IoniaMerchantState {}
class IoniaLoadedMerchantState extends IoniaMerchantState {}

View file

@ -1,70 +0,0 @@
import 'dart:convert';
import 'package:cake_wallet/ionia/ionia_gift_card_instruction.dart';
import 'package:flutter/foundation.dart';
class IoniaGiftCard {
IoniaGiftCard({
required this.id,
required this.merchantId,
required this.legalName,
required this.systemName,
required this.barcodeUrl,
required this.cardNumber,
required this.cardPin,
required this.instructions,
required this.tip,
required this.purchaseAmount,
required this.actualAmount,
required this.totalTransactionAmount,
required this.totalDashTransactionAmount,
required this.remainingAmount,
required this.createdDateFormatted,
required this.lastTransactionDateFormatted,
required this.isActive,
required this.isEmpty,
required this.logoUrl});
factory IoniaGiftCard.fromJsonMap(Map<String, dynamic> element) {
return IoniaGiftCard(
id: element['Id'] as int,
merchantId: element['MerchantId'] as int,
legalName: element['LegalName'] as String,
systemName: element['SystemName'] as String,
barcodeUrl: element['BarcodeUrl'] as String,
cardNumber: element['CardNumber'] as String,
cardPin: element['CardPin'] as String,
tip: element['Tip'] as double,
purchaseAmount: element['PurchaseAmount'] as double,
actualAmount: element['ActualAmount'] as double,
totalTransactionAmount: element['TotalTransactionAmount'] as double,
totalDashTransactionAmount: (element['TotalDashTransactionAmount'] as double?) ?? 0.0,
remainingAmount: element['RemainingAmount'] as double,
isActive: element['IsActive'] as bool,
isEmpty: element['IsEmpty'] as bool,
logoUrl: element['LogoUrl'] as String,
createdDateFormatted: element['CreatedDate'] as String,
lastTransactionDateFormatted: element['LastTransactionDate'] as String,
instructions: IoniaGiftCardInstruction.parseListOfInstructions(element['PaymentInstructions'] as String));
}
final int id;
final int merchantId;
final String legalName;
final String systemName;
final String barcodeUrl;
final String cardNumber;
final String cardPin;
final List<IoniaGiftCardInstruction> instructions;
final double tip;
final double purchaseAmount;
final double actualAmount;
final double totalTransactionAmount;
final double totalDashTransactionAmount;
double remainingAmount;
final String createdDateFormatted;
final String lastTransactionDateFormatted;
final bool isActive;
final bool isEmpty;
final String logoUrl;
}

View file

@ -1,28 +0,0 @@
import 'dart:convert';
import 'package:intl/intl.dart' show toBeginningOfSentenceCase;
class IoniaGiftCardInstruction {
IoniaGiftCardInstruction(this.header, this.body);
factory IoniaGiftCardInstruction.fromJsonMap(Map<String, dynamic> element) {
return IoniaGiftCardInstruction(
toBeginningOfSentenceCase(element['title'] as String? ?? '') ?? '',
element['description'] as String);
}
static List<IoniaGiftCardInstruction> parseListOfInstructions(String instructionsJSON) {
List<IoniaGiftCardInstruction> instructions = <IoniaGiftCardInstruction>[];
if (instructionsJSON.isNotEmpty) {
final decodedInstructions = json.decode(instructionsJSON) as List<dynamic>;
instructions = decodedInstructions
.map((dynamic e) =>IoniaGiftCardInstruction.fromJsonMap(e as Map<String, dynamic>))
.toList();
}
return instructions;
}
final String header;
final String body;
}

View file

@ -1,101 +0,0 @@
import 'package:cake_wallet/ionia/ionia_gift_card_instruction.dart';
import 'package:cake_wallet/generated/i18n.dart';
class IoniaMerchant {
IoniaMerchant({
required this.id,
required this.legalName,
required this.systemName,
required this.description,
required this.website,
required this.termsAndConditions,
required this.logoUrl,
required this.cardImageUrl,
required this.cardholderAgreement,
required this.isActive,
required this.isOnline,
required this.isPhysical,
required this.isVariablePurchase,
required this.minimumCardPurchase,
required this.maximumCardPurchase,
required this.acceptsTips,
required this.createdDateFormatted,
required this.modifiedDateFormatted,
required this.usageInstructions,
required this.usageInstructionsBak,
required this.hasBarcode,
required this.instructions,
required this.savingsPercentage});
factory IoniaMerchant.fromJsonMap(Map<String, dynamic> element) {
return IoniaMerchant(
id: element["Id"] as int,
legalName: element["LegalName"] as String,
systemName: element["SystemName"] as String,
description: element["Description"] as String,
website: element["Website"] as String,
termsAndConditions: element["TermsAndConditions"] as String,
logoUrl: element["LogoUrl"] as String,
cardImageUrl: element["CardImageUrl"] as String,
cardholderAgreement: element["CardholderAgreement"] as String,
isActive: element["IsActive"] as bool?,
isOnline: element["IsOnline"] as bool,
isPhysical: element["IsPhysical"] as bool,
isVariablePurchase: element["IsVariablePurchase"] as bool,
minimumCardPurchase: element["MinimumCardPurchase"] as double,
maximumCardPurchase: element["MaximumCardPurchase"] as double,
acceptsTips: element["AcceptsTips"] as bool,
createdDateFormatted: element["CreatedDate"] as String?,
modifiedDateFormatted: element["ModifiedDate"] as String?,
usageInstructions: element["UsageInstructions"] as String?,
usageInstructionsBak: element["UsageInstructionsBak"] as String?,
hasBarcode: element["HasBarcode"] as bool,
instructions: IoniaGiftCardInstruction.parseListOfInstructions(element['PaymentInstructions'] as String),
savingsPercentage: element["SavingsPercentage"] as double);
}
final int id;
final String legalName;
final String systemName;
final String description;
final String website;
final String termsAndConditions;
final String logoUrl;
final String cardImageUrl;
final String cardholderAgreement;
final bool? isActive;
final bool isOnline;
final bool? isPhysical;
final bool isVariablePurchase;
final double minimumCardPurchase;
final double maximumCardPurchase;
final bool acceptsTips;
final String? createdDateFormatted;
final String? modifiedDateFormatted;
final String? usageInstructions;
final String? usageInstructionsBak;
final bool hasBarcode;
final List<IoniaGiftCardInstruction> instructions;
final double savingsPercentage;
double get discount => savingsPercentage;
String get avaibilityStatus {
var status = '';
if (isOnline) {
status += S.current.online;
}
if (isPhysical ?? false) {
if (status.isNotEmpty) {
status = '$status & ';
}
status = '${status}${S.current.in_store}';
}
return status;
}
}

View file

@ -1,23 +0,0 @@
import 'package:flutter/foundation.dart';
class IoniaOrder {
IoniaOrder({required this.id,
required this.uri,
required this.currency,
required this.amount,
required this.paymentId});
factory IoniaOrder.fromMap(Map<String, dynamic> obj) {
return IoniaOrder(
id: obj['order_id'] as String,
uri: obj['uri'] as String,
currency: obj['currency'] as String,
amount: obj['amount'] as double,
paymentId: obj['paymentId'] as String);
}
final String id;
final String uri;
final String currency;
final double amount;
final String paymentId;
}

View file

@ -1,171 +0,0 @@
import 'package:cake_wallet/core/secure_storage.dart';
import 'package:cake_wallet/ionia/ionia_merchant.dart';
import 'package:cake_wallet/ionia/ionia_order.dart';
import 'package:cake_wallet/ionia/ionia_virtual_card.dart';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cake_wallet/ionia/ionia_api.dart';
import 'package:cake_wallet/ionia/ionia_gift_card.dart';
import 'package:cake_wallet/ionia/ionia_category.dart';
class IoniaService {
IoniaService(this.secureStorage, this.ioniaApi);
static const ioniaEmailStorageKey = 'ionia_email';
static const ioniaUsernameStorageKey = 'ionia_username';
static const ioniaPasswordStorageKey = 'ionia_password';
static String get clientId => secrets.ioniaClientId;
final SecureStorage secureStorage;
final IoniaApi ioniaApi;
// Create user
Future<void> createUser(String email) async {
final username = await ioniaApi.createUser(email, clientId: clientId);
await secureStorage.write(key: ioniaEmailStorageKey, value: email);
await secureStorage.write(key: ioniaUsernameStorageKey, value: username);
}
// Verify email
Future<void> verifyEmail(String code) async {
final email = (await secureStorage.read(key: ioniaEmailStorageKey))!;
final credentials = await ioniaApi.verifyEmail(email: email, code: code, clientId: clientId);
await secureStorage.write(key: ioniaPasswordStorageKey, value: credentials.password);
await secureStorage.write(key: ioniaUsernameStorageKey, value: credentials.username);
}
// Sign In
Future<void> signIn(String email) async {
await ioniaApi.signIn(email, clientId: clientId);
await secureStorage.write(key: ioniaEmailStorageKey, value: email);
}
Future<String> getUserEmail() async {
return (await secureStorage.read(key: ioniaEmailStorageKey))!;
}
// Check is user logined
Future<bool> isLogined() async {
final username = await secureStorage.read(key: ioniaUsernameStorageKey) ?? '';
final password = await secureStorage.read(key: ioniaPasswordStorageKey) ?? '';
return username.isNotEmpty && password.isNotEmpty;
}
// Logout
Future<void> logout() async {
await secureStorage.delete(key: ioniaUsernameStorageKey);
await secureStorage.delete(key: ioniaPasswordStorageKey);
}
// Create virtual card
Future<IoniaVirtualCard> createCard() async {
final username = (await secureStorage.read(key: ioniaUsernameStorageKey))!;
final password = (await secureStorage.read(key: ioniaPasswordStorageKey))!;
return ioniaApi.createCard(username: username, password: password, clientId: clientId);
}
// Get virtual card
Future<IoniaVirtualCard> getCard() async {
final username = (await secureStorage.read(key: ioniaUsernameStorageKey))!;
final password = (await secureStorage.read(key: ioniaPasswordStorageKey))!;
return ioniaApi.getCards(username: username, password: password, clientId: clientId);
}
// Get Merchants
Future<List<IoniaMerchant>> getMerchants() async {
final username = (await secureStorage.read(key: ioniaUsernameStorageKey))!;
final password = (await secureStorage.read(key: ioniaPasswordStorageKey))!;
return ioniaApi.getMerchants(username: username, password: password, clientId: clientId);
}
// Get Merchants By Filter
Future<List<IoniaMerchant>> getMerchantsByFilter({
String? search,
List<IoniaCategory>? categories,
int merchantFilterType = 0}) async {
final username = (await secureStorage.read(key: ioniaUsernameStorageKey))!;
final password = (await secureStorage.read(key: ioniaPasswordStorageKey))!;
return ioniaApi.getMerchantsByFilter(
username: username,
password: password,
clientId: clientId,
search: search,
categories: categories,
merchantFilterType: merchantFilterType);
}
// Purchase Gift Card
Future<IoniaOrder> purchaseGiftCard({
required String merchId,
required double amount,
required String currency}) async {
final username = (await secureStorage.read(key: ioniaUsernameStorageKey))!;
final password = (await secureStorage.read(key: ioniaPasswordStorageKey))!;
final deviceId = '';
return ioniaApi.purchaseGiftCard(
requestedUUID: deviceId,
merchId: merchId,
amount: amount,
currency: currency,
username: username,
password: password,
clientId: clientId);
}
// Get Current User Gift Card Summaries
Future<List<IoniaGiftCard>> getCurrentUserGiftCardSummaries() async {
final username = (await secureStorage.read(key: ioniaUsernameStorageKey))!;
final password = (await secureStorage.read(key: ioniaPasswordStorageKey))!;
return ioniaApi.getCurrentUserGiftCardSummaries(username: username, password: password, clientId: clientId);
}
// Charge Gift Card
Future<void> chargeGiftCard({
required int giftCardId,
required double amount}) async {
final username = (await secureStorage.read(key: ioniaUsernameStorageKey))!;
final password = (await secureStorage.read(key: ioniaPasswordStorageKey))!;
await ioniaApi.chargeGiftCard(
username: username,
password: password,
clientId: clientId,
giftCardId: giftCardId,
amount: amount);
}
// Redeem
Future<void> redeem({required int giftCardId, required double amount}) async {
await chargeGiftCard(giftCardId: giftCardId, amount: amount);
}
// Get Gift Card
Future<IoniaGiftCard> getGiftCard({required int id}) async {
final username = (await secureStorage.read(key: ioniaUsernameStorageKey))!;
final password = (await secureStorage.read(key: ioniaPasswordStorageKey))!;
return ioniaApi.getGiftCard(username: username, password: password, clientId: clientId,id: id);
}
// Payment Status
Future<int> getPaymentStatus({
required String orderId,
required String paymentId}) async {
final username = (await secureStorage.read(key: ioniaUsernameStorageKey))!;
final password = (await secureStorage.read(key: ioniaPasswordStorageKey))!;
return ioniaApi.getPaymentStatus(username: username, password: password, clientId: clientId, orderId: orderId, paymentId: paymentId);
}
}

View file

@ -1,18 +0,0 @@
class IoniaTip {
const IoniaTip({
required this.originalAmount,
required this.percentage,
this.isCustom = false});
final double originalAmount;
final double percentage;
final bool isCustom;
double get additionalAmount => double.parse((originalAmount * percentage / 100).toStringAsFixed(2));
static const tipList = [
IoniaTip(originalAmount: 0, percentage: 0),
IoniaTip(originalAmount: 10, percentage: 10),
IoniaTip(originalAmount: 20, percentage: 20)
];
}

View file

@ -1,43 +0,0 @@
import 'package:flutter/foundation.dart';
import 'dart:convert';
class IoniaTokenData {
IoniaTokenData({required this.accessToken, required this.tokenType, required this.expiredAt});
factory IoniaTokenData.fromJson(String source) {
final decoded = json.decode(source) as Map<String, dynamic>;
final accessToken = decoded['access_token'] as String;
final expiresIn = decoded['expires_in'] as int;
final tokenType = decoded['token_type'] as String;
final expiredAtInMilliseconds = decoded['expired_at'] as int;
DateTime expiredAt;
if (expiredAtInMilliseconds != null) {
expiredAt = DateTime.fromMillisecondsSinceEpoch(expiredAtInMilliseconds);
} else {
expiredAt = DateTime.now().add(Duration(seconds: expiresIn));
}
return IoniaTokenData(
accessToken: accessToken,
tokenType: tokenType,
expiredAt: expiredAt);
}
final String accessToken;
final String tokenType;
final DateTime expiredAt;
bool get isExpired => DateTime.now().isAfter(expiredAt);
@override
String toString() => '$tokenType $accessToken';
String toJson() {
return json.encode(<String, dynamic>{
'access_token': accessToken,
'token_type': tokenType,
'expired_at': expiredAt.millisecondsSinceEpoch
});
}
}

View file

@ -1,6 +0,0 @@
class IoniaUserCredentials {
const IoniaUserCredentials(this.username, this.password);
final String username;
final String password;
}

View file

@ -1,41 +0,0 @@
class IoniaVirtualCard {
IoniaVirtualCard({
required this.token,
required this.createdAt,
required this.lastFour,
required this.state,
required this.pan,
required this.cvv,
required this.expirationMonth,
required this.expirationYear,
required this.fundsLimit,
required this.spendLimit});
factory IoniaVirtualCard.fromMap(Map<String, dynamic> source) {
final created = source['created'] as String;
final createdAt = DateTime.tryParse(created);
return IoniaVirtualCard(
token: source['token'] as String,
createdAt: createdAt,
lastFour: source['lastFour'] as String,
state: source['state'] as String,
pan: source['pan'] as String,
cvv: source['cvv'] as String,
expirationMonth: source['expirationMonth'] as String,
expirationYear: source['expirationYear'] as String,
fundsLimit: source['FundsLimit'] as double,
spendLimit: source['spend_limit'] as double);
}
final String token;
final String lastFour;
final String state;
final String pan;
final String cvv;
final String expirationMonth;
final String expirationYear;
final DateTime? createdAt;
final double fundsLimit;
final double spendLimit;
}

View file

@ -1,6 +1,5 @@
import 'package:cake_wallet/anonpay/anonpay_info_base.dart';
import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart';
import 'package:cake_wallet/buy/order.dart';
import 'package:cake_wallet/core/totp_request_details.dart';
import 'package:cake_wallet/core/wallet_connect/web3wallet_service.dart';
@ -10,7 +9,6 @@ import 'package:cake_wallet/entities/qr_view_data.dart';
import 'package:cake_wallet/entities/wallet_nft_response.dart';
import 'package:cake_wallet/exchange/trade.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/ionia/ionia_any_pay_payment_info.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/anonpay_details/anonpay_details_page.dart';
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
@ -36,14 +34,6 @@ import 'package:cake_wallet/src/screens/exchange/exchange_template_page.dart';
import 'package:cake_wallet/src/screens/exchange_trade/exchange_confirm_page.dart';
import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_page.dart';
import 'package:cake_wallet/src/screens/faq/faq_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_cards_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_redeem_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_tip_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_gift_card_detail_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_more_options_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_payment_status_page.dart';
import 'package:cake_wallet/src/screens/ionia/ionia.dart';
import 'package:cake_wallet/src/screens/monero_accounts/monero_account_edit_or_create_page.dart';
import 'package:cake_wallet/src/screens/nano/nano_change_rep_page.dart';
import 'package:cake_wallet/src/screens/nano_accounts/nano_account_edit_or_create_page.dart';
@ -76,9 +66,11 @@ import 'package:cake_wallet/src/screens/settings/manage_nodes_page.dart';
import 'package:cake_wallet/src/screens/settings/other_settings_page.dart';
import 'package:cake_wallet/src/screens/settings/privacy_page.dart';
import 'package:cake_wallet/src/screens/settings/security_backup_page.dart';
import 'package:cake_wallet/src/screens/cake_pay/auth/cake_pay_account_page.dart';
import 'package:cake_wallet/src/screens/settings/silent_payments_settings.dart';
import 'package:cake_wallet/src/screens/settings/tor_page.dart';
import 'package:cake_wallet/src/screens/settings/trocador_providers_page.dart';
import 'package:cake_wallet/src/screens/settings/tor_page.dart';
import 'package:cake_wallet/src/screens/setup_2fa/modify_2fa_page.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_enter_code_page.dart';
@ -120,7 +112,7 @@ import 'package:cw_core/wallet_type.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:cake_wallet/src/screens/cake_pay/cake_pay.dart';
import 'src/screens/dashboard/pages/nft_import_page.dart';
late RouteSettings currentRouteSettings;
@ -518,73 +510,30 @@ Route<dynamic> createRoute(RouteSettings settings) {
param1: settings.arguments as QrViewData,
));
case Routes.ioniaWelcomePage:
case Routes.cakePayCardsPage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<CakePayCardsPage>());
case Routes.cakePayBuyCardPage:
final args = settings.arguments as List;
return CupertinoPageRoute<void>(
fullscreenDialog: true,
builder: (_) => getIt.get<IoniaWelcomePage>(),
builder: (_) => getIt.get<CakePayBuyCardPage>(param1: args));
case Routes.cakePayBuyCardDetailPage:
final args = settings.arguments as List;
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<CakePayBuyCardDetailPage>(param1: args));
case Routes.cakePayWelcomePage:
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<CakePayWelcomePage>(),
);
case Routes.ioniaLoginPage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaLoginPage>());
case Routes.ioniaCreateAccountPage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaCreateAccountPage>());
case Routes.ioniaManageCardsPage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaManageCardsPage>());
case Routes.ioniaBuyGiftCardPage:
case Routes.cakePayVerifyOtpPage:
final args = settings.arguments as List;
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<IoniaBuyGiftCardPage>(param1: args));
return CupertinoPageRoute<void>(builder: (_) => getIt.get<CakePayVerifyOtpPage>(param1: args));
case Routes.ioniaBuyGiftCardDetailPage:
final args = settings.arguments as List;
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<IoniaBuyGiftCardDetailPage>(param1: args));
case Routes.ioniaVerifyIoniaOtpPage:
final args = settings.arguments as List;
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaVerifyIoniaOtp>(param1: args));
case Routes.ioniaDebitCardPage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaDebitCardPage>());
case Routes.ioniaActivateDebitCardPage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaActivateDebitCardPage>());
case Routes.ioniaAccountPage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaAccountPage>());
case Routes.ioniaAccountCardsPage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaAccountCardsPage>());
case Routes.ioniaCustomTipPage:
final args = settings.arguments as List;
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaCustomTipPage>(param1: args));
case Routes.ioniaGiftCardDetailPage:
final args = settings.arguments as List;
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<IoniaGiftCardDetailPage>(param1: args.first));
case Routes.ioniaCustomRedeemPage:
final args = settings.arguments as List;
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<IoniaCustomRedeemPage>(param1: args));
case Routes.ioniaMoreOptionsPage:
final args = settings.arguments as List;
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<IoniaMoreOptionsPage>(param1: args));
case Routes.ioniaPaymentStatusPage:
final args = settings.arguments as List;
final paymentInfo = args.first as IoniaAnyPayPaymentInfo;
final commitedInfo = args[1] as AnyPayPaymentCommittedInfo;
return CupertinoPageRoute<void>(
builder: (_) =>
getIt.get<IoniaPaymentStatusPage>(param1: paymentInfo, param2: commitedInfo));
case Routes.cakePayAccountPage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<CakePayAccountPage>());
case Routes.webViewPage:
final args = settings.arguments as List;

View file

@ -64,22 +64,13 @@ class Routes {
static const unspentCoinsDetails = '/unspent_coins_details';
static const addressPage = '/address_page';
static const fullscreenQR = '/fullscreen_qr';
static const ioniaWelcomePage = '/cake_pay_welcome_page';
static const ioniaCreateAccountPage = '/cake_pay_create_account_page';
static const ioniaLoginPage = '/cake_pay_login_page';
static const ioniaManageCardsPage = '/manage_cards_page';
static const ioniaBuyGiftCardPage = '/buy_gift_card_page';
static const ioniaBuyGiftCardDetailPage = '/buy_gift_card_detail_page';
static const ioniaVerifyIoniaOtpPage = '/cake_pay_verify_otp_page';
static const ioniaDebitCardPage = '/debit_card_page';
static const ioniaActivateDebitCardPage = '/activate_debit_card_page';
static const ioniaAccountPage = 'ionia_account_page';
static const ioniaAccountCardsPage = 'ionia_account_cards_page';
static const ioniaCustomTipPage = 'ionia_custom_tip_page';
static const ioniaGiftCardDetailPage = '/ionia_gift_card_detail_page';
static const ioniaPaymentStatusPage = '/ionia_payment_status_page';
static const ioniaMoreOptionsPage = '/ionia_more_options_page';
static const ioniaCustomRedeemPage = '/ionia_custom_redeem_page';
static const cakePayWelcomePage = '/cake_pay_welcome_page';
static const cakePayLoginPage = '/cake_pay_login_page';
static const cakePayCardsPage = '/cake_pay_cards_page';
static const cakePayBuyCardPage = '/cake_pay_buy_card_page';
static const cakePayBuyCardDetailPage = '/cake_pay_buy_card_detail_page';
static const cakePayVerifyOtpPage = '/cake_pay_verify_otp_page';
static const cakePayAccountPage = '/cake_pay_account_page';
static const webViewPage = '/web_view_page';
static const silentPaymentsSettings = '/silent_payments_settings';
static const connectionSync = '/connection_sync_page';

View file

@ -7,7 +7,7 @@ import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/src/widgets/nav_bar.dart';
import 'package:cake_wallet/generated/i18n.dart';
enum AppBarStyle { regular, withShadow, transparent }
enum AppBarStyle { regular, withShadow, transparent, completelyTransparent }
abstract class BasePage extends StatelessWidget {
BasePage() : _scaffoldKey = GlobalKey<ScaffoldState>();
@ -125,7 +125,7 @@ abstract class BasePage extends StatelessWidget {
Widget? floatingActionButton(BuildContext context) => null;
ObstructingPreferredSizeWidget appBar(BuildContext context) {
PreferredSizeWidget appBar(BuildContext context) {
final appBarColor = pageBackgroundColor(context);
switch (appBarStyle) {
@ -156,6 +156,16 @@ abstract class BasePage extends StatelessWidget {
border: null,
);
case AppBarStyle.completelyTransparent:
return AppBar(
leading: leading(context),
title: middle(context),
actions: <Widget>[if (trailing(context) != null) trailing(context)!],
backgroundColor: Colors.transparent,
elevation: 0,
centerTitle: true,
);
default:
// FIX-ME: NavBar no context
return NavBar(

View file

@ -0,0 +1,90 @@
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/cake_pay/widgets/cake_pay_tile.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/typography.dart';
import 'package:cake_wallet/view_model/cake_pay/cake_pay_account_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
class CakePayAccountPage extends BasePage {
CakePayAccountPage(this.cakePayAccountViewModel);
final CakePayAccountViewModel cakePayAccountViewModel;
@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: MaterialStateColor.resolveWith(
(states) => Colors.transparent),
),
onPressed: () => Navigator.pop(context),
child: backButton(context),
),
),
),
),
);
}
@override
Widget middle(BuildContext context) {
return Text(
S.current.account,
style: textMediumSemiBold(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
);
}
@override
Widget body(BuildContext context) {
return ScrollableWithBottomSection(
contentPadding: EdgeInsets.all(24),
content: Column(
children: [
SizedBox(height: 20),
Observer(
builder: (_) => Container(decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 1.0,
color:
Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor),
),
),
child: CakePayTile(title: S.of(context).email_address, subTitle: cakePayAccountViewModel.email)),
),
],
),
bottomSectionPadding: EdgeInsets.all(30),
bottomSection: Column(
children: [
PrimaryButton(
color: Theme.of(context).primaryColor,
textColor: Colors.white,
text: S.of(context).logout,
onPressed: () {
cakePayAccountViewModel.logout();
Navigator.pushNamedAndRemoveUntil(context, Routes.dashboard, (route) => false);
},
),
],
),
);
}
}

View file

@ -1,39 +1,38 @@
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
import 'package:cake_wallet/ionia/ionia_create_state.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/cake_pay/cake_pay_states.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/src/widgets/keyboard_done_button.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/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
import 'package:cake_wallet/typography.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/ionia/ionia_auth_view_model.dart';
import 'package:cake_wallet/view_model/cake_pay/cake_pay_auth_view_model.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:keyboard_actions/keyboard_actions.dart';
import 'package:mobx/mobx.dart';
class IoniaVerifyIoniaOtp extends BasePage {
IoniaVerifyIoniaOtp(this._authViewModel, this._email, this.isSignIn)
class CakePayVerifyOtpPage extends BasePage {
CakePayVerifyOtpPage(this._authViewModel, this._email, this.isSignIn)
: _codeController = TextEditingController(),
_codeFocus = FocusNode() {
_codeController.addListener(() {
final otp = _codeController.text;
_authViewModel.otp = otp;
if (otp.length > 3) {
_authViewModel.otpState = IoniaOtpSendEnabled();
if (otp.length > 5) {
_authViewModel.otpState = CakePayOtpSendEnabled();
} else {
_authViewModel.otpState = IoniaOtpSendDisabled();
_authViewModel.otpState = CakePayOtpSendDisabled();
}
});
}
final IoniaAuthViewModel _authViewModel;
final CakePayAuthViewModel _authViewModel;
final bool isSignIn;
final String _email;
@ -53,11 +52,11 @@ class IoniaVerifyIoniaOtp extends BasePage {
@override
Widget body(BuildContext context) {
reaction((_) => _authViewModel.otpState, (IoniaOtpState state) {
if (state is IoniaOtpFailure) {
reaction((_) => _authViewModel.otpState, (CakePayOtpState state) {
if (state is CakePayOtpFailure) {
_onOtpFailure(context, state.error);
}
if (state is IoniaOtpSuccess) {
if (state is CakePayOtpSuccess) {
_onOtpSuccessful(context);
}
});
@ -98,9 +97,7 @@ class IoniaVerifyIoniaOtp extends BasePage {
Text(S.of(context).didnt_get_code),
SizedBox(width: 20),
InkWell(
onTap: () => isSignIn
? _authViewModel.signIn(_email)
: _authViewModel.createUser(_email),
onTap: () => _authViewModel.logIn(_email),
child: Text(
S.of(context).resend_code,
style: textSmallSemiBold(color: Palette.blueCraiola),
@ -120,8 +117,8 @@ class IoniaVerifyIoniaOtp extends BasePage {
builder: (_) => LoadingPrimaryButton(
text: S.of(context).continue_text,
onPressed: _verify,
isDisabled: _authViewModel.otpState is IoniaOtpSendDisabled,
isLoading: _authViewModel.otpState is IoniaOtpValidating,
isDisabled: _authViewModel.otpState is CakePayOtpSendDisabled,
isLoading: _authViewModel.otpState is CakePayOtpValidating,
color: Theme.of(context).primaryColor,
textColor: Colors.white,
),
@ -149,8 +146,7 @@ class IoniaVerifyIoniaOtp extends BasePage {
}
void _onOtpSuccessful(BuildContext context) =>
Navigator.of(context)
.pushNamedAndRemoveUntil(Routes.ioniaManageCardsPage, (route) => route.isFirst);
Navigator.pop(context);
void _verify() async => await _authViewModel.verifyEmail(_codeController.text);
}

View file

@ -1,22 +1,22 @@
import 'package:cake_wallet/core/email_validator.dart';
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/ionia/ionia_create_state.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/cake_pay/cake_pay_states.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.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/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/typography.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/ionia/ionia_auth_view_model.dart';
import 'package:cake_wallet/view_model/cake_pay/cake_pay_auth_view_model.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
class IoniaLoginPage extends BasePage {
IoniaLoginPage(this._authViewModel)
class CakePayWelcomePage extends BasePage {
CakePayWelcomePage(this._authViewModel)
: _formKey = GlobalKey<FormState>(),
_emailController = TextEditingController() {
_emailController.text = _authViewModel.email;
@ -25,14 +25,14 @@ class IoniaLoginPage extends BasePage {
final GlobalKey<FormState> _formKey;
final IoniaAuthViewModel _authViewModel;
final CakePayAuthViewModel _authViewModel;
final TextEditingController _emailController;
@override
Widget middle(BuildContext context) {
return Text(
S.current.login,
S.current.welcome_to_cakepay,
style: textMediumSemiBold(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
@ -41,25 +41,40 @@ class IoniaLoginPage extends BasePage {
@override
Widget body(BuildContext context) {
reaction((_) => _authViewModel.signInState, (IoniaCreateAccountState state) {
if (state is IoniaCreateStateFailure) {
reaction((_) => _authViewModel.userVerificationState, (CakePayUserVerificationState state) {
if (state is CakePayUserVerificationStateFailure) {
_onLoginUserFailure(context, state.error);
}
if (state is IoniaCreateStateSuccess) {
if (state is CakePayUserVerificationStateSuccess) {
_onLoginSuccessful(context, _authViewModel);
}
});
return ScrollableWithBottomSection(
contentPadding: EdgeInsets.all(24),
content: Form(
key: _formKey,
child: BaseTextFormField(
hintText: S.of(context).email_address,
keyboardType: TextInputType.emailAddress,
validator: EmailValidator(),
controller: _emailController,
onSubmit: (text) => _login(),
),
content: Column(
children: [
SizedBox(height: 90),
Form(
key: _formKey,
child: BaseTextFormField(
hintText: S.of(context).email_address,
keyboardType: TextInputType.emailAddress,
validator: EmailValidator(),
controller: _emailController,
onSubmit: (text) => _login(),
),
),
SizedBox(height: 20),
Text(
S.of(context).about_cake_pay,
style: textLarge(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
),
SizedBox(height: 20),
Text(S.of(context).cake_pay_account_note,
style: textLarge(color: Theme.of(context).extension<CakeTextTheme>()!.titleColor)),
],
),
bottomSectionPadding: EdgeInsets.symmetric(vertical: 36, horizontal: 24),
bottomSection: Column(
@ -71,7 +86,8 @@ class IoniaLoginPage extends BasePage {
builder: (_) => LoadingPrimaryButton(
text: S.of(context).login,
onPressed: _login,
isLoading: _authViewModel.signInState is IoniaCreateStateLoading,
isLoading:
_authViewModel.userVerificationState is CakePayUserVerificationStateLoading,
color: Theme.of(context).primaryColor,
textColor: Colors.white,
),
@ -98,9 +114,10 @@ class IoniaLoginPage extends BasePage {
});
}
void _onLoginSuccessful(BuildContext context, IoniaAuthViewModel authViewModel) => Navigator.pushNamed(
void _onLoginSuccessful(BuildContext context, CakePayAuthViewModel authViewModel) =>
Navigator.pushReplacementNamed(
context,
Routes.ioniaVerifyIoniaOtpPage,
Routes.cakePayVerifyOtpPage,
arguments: [authViewModel.email, true],
);
@ -108,6 +125,6 @@ class IoniaLoginPage extends BasePage {
if (_formKey.currentState != null && !_formKey.currentState!.validate()) {
return;
}
await _authViewModel.signIn(_emailController.text);
await _authViewModel.logIn(_emailController.text);
}
}

View file

@ -0,0 +1,5 @@
export 'auth/cake_pay_welcome_page.dart';
export 'auth/cake_pay_verify_otp_page.dart';
export 'cards/cake_pay_confirm_purchase_card_page.dart';
export 'cards/cake_pay_cards_page.dart';
export 'cards/cake_pay_buy_card_page.dart';

View file

@ -0,0 +1,474 @@
import 'package:auto_size_text/auto_size_text.dart';
import 'package:cake_wallet/cake_pay/cake_pay_card.dart';
import 'package:cake_wallet/cake_pay/cake_pay_payment_credantials.dart';
import 'package:cake_wallet/cake_pay/cake_pay_service.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/cake_pay/widgets/image_placeholder.dart';
import 'package:cake_wallet/src/screens/cake_pay/widgets/link_extractor.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
import 'package:cake_wallet/src/widgets/number_text_fild_widget.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/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
import 'package:cake_wallet/typography.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/view_model/cake_pay/cake_pay_buy_card_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/dropdown_filter_item_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:keyboard_actions/keyboard_actions.dart';
class CakePayBuyCardPage extends BasePage {
CakePayBuyCardPage(
this.cakePayBuyCardViewModel,
this.cakePayService,
) : _amountFieldFocus = FocusNode(),
_amountController = TextEditingController(),
_quantityFieldFocus = FocusNode(),
_quantityController =
TextEditingController(text: cakePayBuyCardViewModel.quantity.toString()) {
_amountController.addListener(() {
cakePayBuyCardViewModel.onAmountChanged(_amountController.text);
});
}
final CakePayBuyCardViewModel cakePayBuyCardViewModel;
final CakePayService cakePayService;
@override
String get title => cakePayBuyCardViewModel.card.name;
@override
bool get extendBodyBehindAppBar => true;
@override
AppBarStyle get appBarStyle => AppBarStyle.completelyTransparent;
@override
Widget? middle(BuildContext context) {
return Text(
title,
textAlign: TextAlign.center,
maxLines: 2,
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
fontFamily: 'Lato',
color: titleColor(context)),
);
}
final TextEditingController _amountController;
final FocusNode _amountFieldFocus;
final TextEditingController _quantityController;
final FocusNode _quantityFieldFocus;
@override
Widget body(BuildContext context) {
final card = cakePayBuyCardViewModel.card;
final vendor = cakePayBuyCardViewModel.vendor;
return KeyboardActions(
disableScroll: true,
config: KeyboardActionsConfig(
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
keyboardBarColor: Theme.of(context).extension<KeyboardTheme>()!.keyboardBarColor,
nextFocus: false,
actions: [
KeyboardActionsItem(
focusNode: _amountFieldFocus,
toolbarButtons: [(_) => KeyboardDoneButton()],
),
]),
child: Container(
color: Theme.of(context).colorScheme.background,
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.zero,
content: Column(
children: [
ClipRRect(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(25.0), bottomRight: Radius.circular(25.0)),
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Theme.of(context).extension<SendPageTheme>()!.firstGradientColor,
Theme.of(context).extension<SendPageTheme>()!.secondGradientColor,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
height: responsiveLayoutUtil.screenHeight * 0.35,
width: double.infinity,
child: Column(
children: [
Expanded(flex: 4, child: const SizedBox()),
Expanded(
flex: 7,
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(10)),
child: Image.network(
card.cardImageUrl ?? '',
fit: BoxFit.cover,
loadingBuilder: (BuildContext context, Widget child,
ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) return child;
return Center(child: CircularProgressIndicator());
},
errorBuilder: (context, error, stackTrace) =>
CakePayCardImagePlaceholder(),
),
),
),
Expanded(child: const SizedBox()),
],
)),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Container(
height: responsiveLayoutUtil.screenHeight * 0.5,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 24),
Expanded(
child: Text(S.of(context).enter_amount,
style: TextStyle(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
fontSize: 24,
fontWeight: FontWeight.w600,
)),
),
card.denominations.isNotEmpty
? Expanded(
flex: 2,
child: _DenominationsAmountWidget(
fiatCurrency: card.fiatCurrency.title,
denominations: card.denominations,
amountFieldFocus: _amountFieldFocus,
amountController: _amountController,
quantityFieldFocus: _quantityFieldFocus,
quantityController: _quantityController,
onAmountChanged: cakePayBuyCardViewModel.onAmountChanged,
onQuantityChanged: cakePayBuyCardViewModel.onQuantityChanged,
cakePayBuyCardViewModel: cakePayBuyCardViewModel,
),
)
: Expanded(
flex: 2,
child: _EnterAmountWidget(
minValue: card.minValue ?? '-',
maxValue: card.maxValue ?? '-',
fiatCurrency: card.fiatCurrency.title,
amountFieldFocus: _amountFieldFocus,
amountController: _amountController,
onAmountChanged: cakePayBuyCardViewModel.onAmountChanged,
),
),
Expanded(
flex: 5,
child: Column(
children: [
if (vendor.cakeWarnings != null)
Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
borderRadius: BorderRadius.circular(10),
border: Border.all(color: Colors.white.withOpacity(0.20)),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
vendor.cakeWarnings!,
textAlign: TextAlign.center,
style: textSmallSemiBold(color: Colors.white),
),
),
),
),
Expanded(
child: SingleChildScrollView(
child: ClickableLinksText(
text: card.description ?? '',
textStyle: TextStyle(
color: Theme.of(context)
.extension<CakeTextTheme>()!
.secondaryTextColor,
fontSize: 18,
fontWeight: FontWeight.w400,
),
),
),
),
],
),
),
],
),
),
),
],
),
bottomSection: Column(
children: [
Observer(builder: (_) {
return Padding(
padding: EdgeInsets.only(bottom: 12),
child: PrimaryButton(
onPressed: () => navigateToCakePayBuyCardDetailPage(context, card),
text: S.of(context).buy_now,
isDisabled: !cakePayBuyCardViewModel.isEnablePurchase,
color: Theme.of(context).primaryColor,
textColor: Colors.white,
),
);
}),
],
),
),
),
);
}
Future<void> navigateToCakePayBuyCardDetailPage(BuildContext context, CakePayCard card) async {
final userName = await cakePayService.getUserEmail();
final paymentCredential = PaymentCredential(
amount: cakePayBuyCardViewModel.amount,
quantity: cakePayBuyCardViewModel.quantity,
totalAmount: cakePayBuyCardViewModel.totalAmount,
userName: userName,
fiatCurrency: card.fiatCurrency.title,
);
Navigator.pushNamed(
context,
Routes.cakePayBuyCardDetailPage,
arguments: [paymentCredential, card],
);
}
}
class _DenominationsAmountWidget extends StatelessWidget {
const _DenominationsAmountWidget({
required this.fiatCurrency,
required this.denominations,
required this.amountFieldFocus,
required this.amountController,
required this.quantityFieldFocus,
required this.quantityController,
required this.cakePayBuyCardViewModel,
required this.onAmountChanged,
required this.onQuantityChanged,
});
final String fiatCurrency;
final List<String> denominations;
final FocusNode amountFieldFocus;
final TextEditingController amountController;
final FocusNode quantityFieldFocus;
final TextEditingController quantityController;
final CakePayBuyCardViewModel cakePayBuyCardViewModel;
final Function(String) onAmountChanged;
final Function(int?) onQuantityChanged;
@override
Widget build(BuildContext context) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
flex: 12,
child: Column(
children: [
Expanded(
child: DropdownFilterList(
items: denominations,
itemPrefix: fiatCurrency,
selectedItem: denominations.first,
textStyle: textMediumSemiBold(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor),
onItemSelected: (value) {
amountController.text = value;
onAmountChanged(value);
},
caption: '',
),
),
const SizedBox(height: 4),
Expanded(
child: Container(
width: double.infinity,
decoration: BoxDecoration(
border: Border(
top: BorderSide(
width: 1.0,
color: Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor),
),
),
child: Text(S.of(context).choose_card_value + ':',
maxLines: 2,
style: textSmall(
color: Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor)),
),
),
],
),
),
Expanded(child: const SizedBox()),
Expanded(
flex: 8,
child: Column(
children: [
Expanded(
child: NumberTextField(
controller: quantityController,
focusNode: quantityFieldFocus,
min: 1,
max: 99,
onChanged: (value) => onQuantityChanged(value),
),
),
const SizedBox(height: 4),
Expanded(
child: Container(
width: double.infinity,
decoration: BoxDecoration(
border: Border(
top: BorderSide(
width: 1.0,
color: Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor),
),
),
child: Text(S.of(context).quantity + ':',
maxLines: 1,
style: textSmall(
color: Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor)),
),
),
],
),
),
Expanded(child: const SizedBox()),
Expanded(
flex: 12,
child: Column(
children: [
Expanded(
child: Container(
alignment: Alignment.bottomCenter,
child: Observer(
builder: (_) => AutoSizeText(
'$fiatCurrency ${cakePayBuyCardViewModel.totalAmount}',
maxLines: 1,
style: textMediumSemiBold(
color:
Theme.of(context).extension<CakeTextTheme>()!.titleColor)))),
),
const SizedBox(height: 4),
Expanded(
child: Container(
width: double.infinity,
decoration: BoxDecoration(
border: Border(
top: BorderSide(
width: 1.0,
color:
Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor),
),
),
child: Text(S.of(context).total + ':',
maxLines: 1,
style: textSmall(
color:
Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor)),
),
),
],
)),
],
);
}
}
class _EnterAmountWidget extends StatelessWidget {
const _EnterAmountWidget({
required this.minValue,
required this.maxValue,
required this.fiatCurrency,
required this.amountFieldFocus,
required this.amountController,
required this.onAmountChanged,
});
final String minValue;
final String maxValue;
final String fiatCurrency;
final FocusNode amountFieldFocus;
final TextEditingController amountController;
final Function(String) onAmountChanged;
@override
Widget build(BuildContext context) {
return Column(
children: [
Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 1.0,
color: Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor),
),
),
child: BaseTextFormField(
controller: amountController,
keyboardType: TextInputType.numberWithOptions(signed: false, decimal: true),
hintText: '0.00',
maxLines: null,
borderColor: Colors.transparent,
prefixIcon: Padding(
padding: const EdgeInsets.only(top: 12),
child: Text(
'$fiatCurrency: ',
style: textMediumSemiBold(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor),
),
),
textStyle:
textMediumSemiBold(color: Theme.of(context).extension<CakeTextTheme>()!.titleColor),
placeholderTextStyle: textMediumSemiBold(
color: Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor),
inputFormatters: [
FilteringTextInputFormatter.deny(RegExp('[\-|\ ]')),
FilteringTextInputFormatter.allow(
RegExp(r'^\d+(\.|\,)?\d{0,2}'),
),
],
),
),
SizedBox(height: 4),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(S.of(context).min_amount(minValue) + ' $fiatCurrency',
style: textSmall(
color: Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor)),
Text(S.of(context).max_amount(maxValue) + ' $fiatCurrency',
style: textSmall(
color: Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor)),
],
),
],
);
}
}

View file

@ -1,42 +1,40 @@
import 'package:cake_wallet/ionia/ionia_create_state.dart';
import 'package:cake_wallet/ionia/ionia_merchant.dart';
import 'package:cake_wallet/cake_pay/cake_pay_states.dart';
import 'package:cake_wallet/cake_pay/cake_pay_vendor.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/gradient_background.dart';
import 'package:cake_wallet/src/screens/ionia/widgets/card_item.dart';
import 'package:cake_wallet/src/screens/ionia/widgets/card_menu.dart';
import 'package:cake_wallet/src/screens/ionia/widgets/ionia_filter_modal.dart';
import 'package:cake_wallet/src/screens/cake_pay/widgets/card_item.dart';
import 'package:cake_wallet/src/screens/cake_pay/widgets/card_menu.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/filter_widget.dart';
import 'package:cake_wallet/src/widgets/cake_scrollbar.dart';
import 'package:cake_wallet/themes/extensions/exchange_page_theme.dart';
import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/utils/debounce.dart';
import 'package:cake_wallet/typography.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/ionia/ionia_gift_cards_list_view_model.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart';
import 'package:cake_wallet/src/widgets/gradient_background.dart';
import 'package:cake_wallet/themes/extensions/balance_page_theme.dart';
import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart';
import 'package:cake_wallet/themes/extensions/exchange_page_theme.dart';
import 'package:cake_wallet/themes/extensions/filter_theme.dart';
import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart';
import 'package:cake_wallet/typography.dart';
import 'package:cake_wallet/utils/debounce.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/cake_pay/cake_pay_cards_list_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
class IoniaManageCardsPage extends BasePage {
IoniaManageCardsPage(this._cardsListViewModel): searchFocusNode = FocusNode() {
class CakePayCardsPage extends BasePage {
CakePayCardsPage(this._cardsListViewModel) : searchFocusNode = FocusNode() {
_searchController.addListener(() {
if (_searchController.text != _cardsListViewModel.searchString) {
_searchDebounce.run(() {
_cardsListViewModel.searchMerchant(_searchController.text);
_cardsListViewModel.resetLoadingNextPageState();
_cardsListViewModel.getVendors(text: _searchController.text);
});
}
});
_cardsListViewModel.getMerchants();
}
final FocusNode searchFocusNode;
final IoniaGiftCardsListViewModel _cardsListViewModel;
final CakePayCardsListViewModel _cardsListViewModel;
final _searchDebounce = Debounce(Duration(milliseconds: 500));
final _searchController = TextEditingController();
@ -46,8 +44,7 @@ class IoniaManageCardsPage extends BasePage {
@override
Widget Function(BuildContext, Widget) get rootWrapper =>
(BuildContext context, Widget scaffold) =>
GradientBackground(scaffold: scaffold);
(BuildContext context, Widget scaffold) => GradientBackground(scaffold: scaffold);
@override
bool get resizeToAvoidBottomInset => false;
@ -58,7 +55,7 @@ class IoniaManageCardsPage extends BasePage {
@override
Widget middle(BuildContext context) {
return Text(
S.of(context).gift_cards,
'Cake Pay',
style: textMediumSemiBold(
color: Theme.of(context).extension<DashboardPageTheme>()!.textColor,
),
@ -68,9 +65,17 @@ class IoniaManageCardsPage extends BasePage {
@override
Widget trailing(BuildContext context) {
return _TrailingIcon(
asset: 'assets/images/profile.png',
onPressed: () => Navigator.pushNamed(context, Routes.ioniaAccountPage),
);
asset: 'assets/images/profile.png',
iconColor: pageIconColor(context) ?? Colors.white,
onPressed: () {
_cardsListViewModel.isCakePayUserAuthenticated().then((value) {
if (value) {
Navigator.pushNamed(context, Routes.cakePayAccountPage);
return;
}
Navigator.pushNamed(context, Routes.cakePayWelcomePage);
});
});
}
@override
@ -79,8 +84,12 @@ class IoniaManageCardsPage extends BasePage {
label: S.of(context).filter_by,
child: InkWell(
onTap: () async {
await showCategoryFilter(context);
_cardsListViewModel.getMerchants();
_cardsListViewModel.storeInitialFilterStates();
await showFilterWidget(context);
if (_cardsListViewModel.hasFiltersChanged) {
_cardsListViewModel.resetLoadingNextPageState();
_cardsListViewModel.getVendors();
}
},
child: Container(
width: 32,
@ -120,7 +129,7 @@ class IoniaManageCardsPage extends BasePage {
),
SizedBox(height: 8),
Expanded(
child: IoniaManageCardsPageBody(
child: CakePayCardsPageBody(
cardsListViewModel: _cardsListViewModel,
),
),
@ -129,36 +138,35 @@ class IoniaManageCardsPage extends BasePage {
);
}
Future <void> showCategoryFilter(BuildContext context) async {
Future<void> showFilterWidget(BuildContext context) async {
return showPopUp<void>(
context: context,
builder: (BuildContext context) {
return IoniaFilterModal(
ioniaGiftCardsListViewModel: _cardsListViewModel,
);
return FilterWidget(filterItems: _cardsListViewModel.createFilterItems);
},
);
}
}
class IoniaManageCardsPageBody extends StatefulWidget {
const IoniaManageCardsPageBody({
class CakePayCardsPageBody extends StatefulWidget {
const CakePayCardsPageBody({
Key? key,
required this.cardsListViewModel,
}) : super(key: key);
final IoniaGiftCardsListViewModel cardsListViewModel;
final CakePayCardsListViewModel cardsListViewModel;
@override
_IoniaManageCardsPageBodyState createState() => _IoniaManageCardsPageBodyState();
_CakePayCardsPageBodyState createState() => _CakePayCardsPageBodyState();
}
class _IoniaManageCardsPageBodyState extends State<IoniaManageCardsPageBody> {
class _CakePayCardsPageBodyState extends State<CakePayCardsPageBody> {
double get backgroundHeight => MediaQuery.of(context).size.height * 0.75;
double thumbHeight = 72;
bool get isAlwaysShowScrollThumb => merchantsList == null ? false : merchantsList.length > 3;
List<IoniaMerchant> get merchantsList => widget.cardsListViewModel.ioniaMerchants;
bool get isAlwaysShowScrollThumb => merchantsList.isEmpty ? false : merchantsList.length > 3;
List<CakePayVendor> get merchantsList => widget.cardsListViewModel.cakePayVendors;
final _scrollController = ScrollController();
@ -166,61 +174,93 @@ class _IoniaManageCardsPageBodyState extends State<IoniaManageCardsPageBody> {
void initState() {
_scrollController.addListener(() {
final scrollOffsetFromTop = _scrollController.hasClients
? (_scrollController.offset / _scrollController.position.maxScrollExtent * (backgroundHeight - thumbHeight))
? (_scrollController.offset /
_scrollController.position.maxScrollExtent *
(backgroundHeight - thumbHeight))
: 0.0;
widget.cardsListViewModel.setScrollOffsetFromTop(scrollOffsetFromTop);
double threshold = 200.0;
bool isNearBottom =
_scrollController.offset >= _scrollController.position.maxScrollExtent - threshold;
if (isNearBottom && !_scrollController.position.outOfRange) {
widget.cardsListViewModel.fetchNextPage();
}
});
super.initState();
}
@override
Widget build(BuildContext context) {
return Observer(
builder: (_) {
final merchantState = widget.cardsListViewModel.merchantState;
if (merchantState is IoniaLoadedMerchantState) {
return Observer(builder: (_) {
final vendorsState = widget.cardsListViewModel.vendorsState;
if (vendorsState is CakePayVendorLoadedState) {
bool isLoadingMore = widget.cardsListViewModel.isLoadingNextPage;
final vendors = widget.cardsListViewModel.cakePayVendors;
if (vendors.isEmpty) {
return Center(child: Text(S.of(context).no_cards_found));
}
return Stack(children: [
ListView.separated(
padding: EdgeInsets.only(left: 2, right: 22),
controller: _scrollController,
itemCount: merchantsList.length,
separatorBuilder: (_, __) => SizedBox(height: 4),
itemBuilder: (_, index) {
final merchant = merchantsList[index];
return CardItem(
logoUrl: merchant.logoUrl,
onTap: () {
Navigator.of(context).pushNamed(Routes.ioniaBuyGiftCardPage, arguments: [merchant]);
},
title: merchant.legalName,
subTitle: merchant.avaibilityStatus,
backgroundColor: Theme.of(context).extension<SyncIndicatorTheme>()!.syncedBackgroundColor,
titleColor: Theme.of(context).extension<DashboardPageTheme>()!.textColor,
subtitleColor: Theme.of(context).extension<BalancePageTheme>()!.labelTextColor,
discount: merchant.discount,
);
},
),
isAlwaysShowScrollThumb
? CakeScrollbar(
backgroundHeight: backgroundHeight,
thumbHeight: thumbHeight,
rightOffset: 1,
width: 3,
backgroundColor: Theme.of(context).extension<FilterTheme>()!.iconColor.withOpacity(0.05),
thumbColor: Theme.of(context).extension<FilterTheme>()!.iconColor.withOpacity(0.5),
fromTop: widget.cardsListViewModel.scrollOffsetFromTop,
)
: Offstage()
]);
}
return Center(
child: CircularProgressIndicator(
backgroundColor: Theme.of(context).extension<DashboardPageTheme>()!.textColor,
valueColor: AlwaysStoppedAnimation<Color>(Theme.of(context).extension<ExchangePageTheme>()!.firstGradientBottomPanelColor),
GridView.builder(
controller: _scrollController,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: responsiveLayoutUtil.shouldRenderTabletUI ? 2 : 1,
childAspectRatio: 5,
crossAxisSpacing: responsiveLayoutUtil.shouldRenderTabletUI ? 10 : 5,
mainAxisSpacing: responsiveLayoutUtil.shouldRenderTabletUI ? 10 : 5,
),
padding: EdgeInsets.only(left: 2, right: 22),
itemCount: vendors.length + (isLoadingMore ? 1 : 0),
itemBuilder: (_, index) {
if (index >= vendors.length) {
return _VendorLoadedIndicator();
}
final vendor = vendors[index];
return CardItem(
logoUrl: vendor.card?.cardImageUrl,
onTap: () {
Navigator.of(context).pushNamed(Routes.cakePayBuyCardPage, arguments: [vendor]);
},
title: vendor.name,
subTitle: vendor.card?.description ?? '',
backgroundColor:
Theme.of(context).extension<SyncIndicatorTheme>()!.syncedBackgroundColor,
titleColor: Theme.of(context).extension<DashboardPageTheme>()!.textColor,
subtitleColor: Theme.of(context).extension<BalancePageTheme>()!.labelTextColor,
discount: 0.0,
);
},
),
);
isAlwaysShowScrollThumb
? CakeScrollbar(
backgroundHeight: backgroundHeight,
thumbHeight: thumbHeight,
rightOffset: 1,
width: 3,
backgroundColor:
Theme.of(context).extension<FilterTheme>()!.iconColor.withOpacity(0.05),
thumbColor:
Theme.of(context).extension<FilterTheme>()!.iconColor.withOpacity(0.5),
fromTop: widget.cardsListViewModel.scrollOffsetFromTop,
)
: Offstage()
]);
}
return _VendorLoadedIndicator();
});
}
}
class _VendorLoadedIndicator extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: CircularProgressIndicator(
backgroundColor: Theme.of(context).extension<DashboardPageTheme>()!.textColor,
valueColor: AlwaysStoppedAnimation<Color>(
Theme.of(context).extension<ExchangePageTheme>()!.firstGradientBottomPanelColor),
),
);
}
}
@ -233,6 +273,7 @@ class _SearchWidget extends StatelessWidget {
}) : super(key: key);
final TextEditingController controller;
final FocusNode focusNode;
@override
Widget build(BuildContext context) {
final searchIcon = ExcludeSemantics(
@ -284,30 +325,25 @@ class _SearchWidget extends StatelessWidget {
}
class _TrailingIcon extends StatelessWidget {
const _TrailingIcon({required this.asset, this.onPressed});
const _TrailingIcon({required this.asset, this.onPressed, required this.iconColor});
final String asset;
final VoidCallback? onPressed;
final Color iconColor;
@override
Widget build(BuildContext context) {
return Semantics(
label: S.of(context).profile,
child: Material(
color: Colors.transparent,
child: IconButton(
padding: EdgeInsets.zero,
constraints: BoxConstraints(),
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
iconSize: 25,
onPressed: onPressed,
icon: Image.asset(
asset,
color: Theme.of(context).extension<DashboardPageTheme>()!.textColor,
label: S.of(context).profile,
child: Material(
color: Colors.transparent,
child: IconButton(
padding: EdgeInsets.zero,
constraints: BoxConstraints(),
highlightColor: Colors.transparent,
onPressed: onPressed,
icon: ImageIcon(AssetImage(asset), size: 25, color: iconColor),
),
),
),
);
));
}
}

View file

@ -0,0 +1,403 @@
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/cake_pay/cake_pay_card.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/cake_pay/widgets/cake_pay_alert_modal.dart';
import 'package:cake_wallet/src/screens/cake_pay/widgets/image_placeholder.dart';
import 'package:cake_wallet/src/screens/cake_pay/widgets/link_extractor.dart';
import 'package:cake_wallet/src/screens/cake_pay/widgets/text_icon_button.dart';
import 'package:cake_wallet/src/screens/send/widgets/confirm_sending_alert.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.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/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/themes/extensions/exchange_page_theme.dart';
import 'package:cake_wallet/themes/extensions/picker_theme.dart';
import 'package:cake_wallet/themes/extensions/receive_page_theme.dart';
import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
import 'package:cake_wallet/typography.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/cake_pay/cake_pay_purchase_view_model.dart';
import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
class CakePayBuyCardDetailPage extends BasePage {
CakePayBuyCardDetailPage(this.cakePayPurchaseViewModel);
final CakePayPurchaseViewModel cakePayPurchaseViewModel;
@override
String get title => cakePayPurchaseViewModel.card.name;
@override
Widget? middle(BuildContext context) {
return Text(
title,
textAlign: TextAlign.center,
maxLines: 2,
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
fontFamily: 'Lato',
color: titleColor(context)),
);
}
@override
Widget? trailing(BuildContext context) => null;
bool _effectsInstalled = false;
@override
Widget body(BuildContext context) {
_setEffects(context);
final card = cakePayPurchaseViewModel.card;
return ScrollableWithBottomSection(
contentPadding: EdgeInsets.zero,
content: Observer(builder: (_) {
return Column(
children: [
SizedBox(height: 36),
ClipRRect(
borderRadius:
BorderRadius.horizontal(left: Radius.circular(20), right: Radius.circular(20)),
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).extension<PickerTheme>()!.searchBackgroundFillColor,
borderRadius: BorderRadius.circular(20),
border: Border.all(color: Colors.white.withOpacity(0.20)),
),
child: Row(
children: [
Expanded(
child: Container(
child: ClipRRect(
borderRadius: BorderRadius.horizontal(
left: Radius.circular(20), right: Radius.circular(20)),
child: Image.network(
card.cardImageUrl ?? '',
fit: BoxFit.cover,
loadingBuilder: (BuildContext context, Widget child,
ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) return child;
return Center(child: CircularProgressIndicator());
},
errorBuilder: (context, error, stackTrace) =>
CakePayCardImagePlaceholder(),
),
)),
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Column(children: [
Row(
children: [
Text(
S.of(context).value + ':',
style: textLarge(
color:
Theme.of(context).extension<CakeTextTheme>()!.titleColor),
),
SizedBox(width: 8),
Text(
'${cakePayPurchaseViewModel.amount.toStringAsFixed(2)} ${cakePayPurchaseViewModel.fiatCurrency}',
style: textLarge(
color:
Theme.of(context).extension<CakeTextTheme>()!.titleColor),
),
],
),
SizedBox(height: 16),
Row(
children: [
Text(
S.of(context).quantity + ':',
style: textLarge(
color:
Theme.of(context).extension<CakeTextTheme>()!.titleColor),
),
SizedBox(width: 8),
Text(
'${cakePayPurchaseViewModel.quantity}',
style: textLarge(
color:
Theme.of(context).extension<CakeTextTheme>()!.titleColor),
),
],
),
SizedBox(height: 16),
Row(
children: [
Text(
S.of(context).total + ':',
style: textLarge(
color:
Theme.of(context).extension<CakeTextTheme>()!.titleColor),
),
SizedBox(width: 8),
Text(
'${cakePayPurchaseViewModel.totalAmount.toStringAsFixed(2)} ${cakePayPurchaseViewModel.fiatCurrency}',
style: textLarge(
color:
Theme.of(context).extension<CakeTextTheme>()!.titleColor),
),
],
),
]),
),
)
],
),
),
),
SizedBox(height: 20),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: TextIconButton(
label: S.of(context).how_to_use_card,
onTap: () => _showHowToUseCard(context, card),
),
),
SizedBox(height: 20),
if (card.expiryAndValidity != null && card.expiryAndValidity!.isNotEmpty)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(S.of(context).expiry_and_validity + ':',
style: textMediumSemiBold(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor)),
SizedBox(height: 10),
Container(
width: double.infinity,
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor,
width: 1,
),
),
),
child: Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Text(
card.expiryAndValidity ?? '',
style: textMedium(
color: Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor,
),
),
),
),
],
),
),
],
);
}),
bottomSection: Column(
children: [
Padding(
padding: EdgeInsets.only(bottom: 12),
child: Observer(builder: (_) {
return LoadingPrimaryButton(
isLoading: cakePayPurchaseViewModel.sendViewModel.state is IsExecutingState,
onPressed: () => purchaseCard(context),
text: S.of(context).purchase_gift_card,
color: Theme.of(context).primaryColor,
textColor: Colors.white,
);
}),
),
SizedBox(height: 8),
InkWell(
onTap: () => _showTermsAndCondition(context, card.termsAndConditions),
child: Text(S.of(context).settings_terms_and_conditions,
style: textMediumSemiBold(
color: Theme.of(context).primaryColor,
).copyWith(fontSize: 12)),
),
SizedBox(height: 16)
],
),
);
}
void _showTermsAndCondition(BuildContext context, String? termsAndConditions) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return CakePayAlertModal(
title: S.of(context).settings_terms_and_conditions,
content: Align(
alignment: Alignment.bottomLeft,
child: ClickableLinksText(
text: termsAndConditions ?? '',
textStyle: TextStyle(
color: Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor,
fontSize: 18,
fontWeight: FontWeight.w400,
),
),
),
actionTitle: S.of(context).agree,
showCloseButton: false,
heightFactor: 0.6,
);
});
}
Future<void> purchaseCard(BuildContext context) async {
bool isLogged = await cakePayPurchaseViewModel.cakePayService.isLogged();
if (!isLogged) {
Navigator.of(context).pushNamed(Routes.cakePayWelcomePage);
} else {
await cakePayPurchaseViewModel.createOrder();
}
}
void _showHowToUseCard(
BuildContext context,
CakePayCard card,
) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return CakePayAlertModal(
title: S.of(context).how_to_use_card,
content: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Padding(
padding: EdgeInsets.all(10),
child: Text(
card.name,
style: textLargeSemiBold(
color: Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor,
),
)),
ClickableLinksText(
text: card.howToUse ?? '',
textStyle: TextStyle(
color: Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor,
fontSize: 18,
fontWeight: FontWeight.w400,
),
linkStyle: TextStyle(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
fontSize: 18,
fontStyle: FontStyle.italic,
fontWeight: FontWeight.w400,
),
),
]),
actionTitle: S.current.got_it,
);
});
}
Future<void> _showConfirmSendingAlert(BuildContext context) async {
if (cakePayPurchaseViewModel.order == null) {
return;
}
ReactionDisposer? disposer;
disposer = reaction((_) => cakePayPurchaseViewModel.isOrderExpired, (bool isExpired) {
if (isExpired) {
if (Navigator.of(context).canPop()) {
Navigator.of(context).pop();
}
if (disposer != null) {
disposer();
}
}
});
final order = cakePayPurchaseViewModel.order;
final pendingTransaction = cakePayPurchaseViewModel.sendViewModel.pendingTransaction!;
await showPopUp<void>(
context: context,
builder: (_) {
return Observer(
builder: (_) => ConfirmSendingAlert(
alertTitle: S.of(context).confirm_sending,
paymentId: S.of(context).payment_id,
paymentIdValue: order?.orderId,
expirationTime: cakePayPurchaseViewModel.formattedRemainingTime,
onDispose: () => _handleDispose(disposer),
amount: S.of(context).send_amount,
amountValue: pendingTransaction.amountFormatted,
fiatAmountValue:
cakePayPurchaseViewModel.sendViewModel.pendingTransactionFiatAmountFormatted,
fee: S.of(context).send_fee,
feeValue: pendingTransaction.feeFormatted,
feeFiatAmount:
cakePayPurchaseViewModel.sendViewModel.pendingTransactionFeeFiatAmountFormatted,
feeRate: pendingTransaction.feeRate,
outputs: cakePayPurchaseViewModel.sendViewModel.outputs,
rightButtonText: S.of(context).send,
leftButtonText: S.of(context).cancel,
actionRightButton: () async {
Navigator.of(context).pop();
await cakePayPurchaseViewModel.sendViewModel.commitTransaction();
},
actionLeftButton: () => Navigator.of(context).pop()));
},
);
}
void _setEffects(BuildContext context) {
if (_effectsInstalled) {
return;
}
reaction((_) => cakePayPurchaseViewModel.sendViewModel.state, (ExecutionState state) {
if (state is FailureState) {
WidgetsBinding.instance.addPostFrameCallback((_) {
showStateAlert(context, S.of(context).error, state.error);
});
}
if (state is ExecutedSuccessfullyState) {
WidgetsBinding.instance.addPostFrameCallback((_) async {
await _showConfirmSendingAlert(context);
});
}
if (state is TransactionCommitted) {
WidgetsBinding.instance.addPostFrameCallback((_) {
cakePayPurchaseViewModel.sendViewModel.clearOutputs();
if (context.mounted) {
showStateAlert(context, S.of(context).sending, S.of(context).transaction_sent);
}
});
}
});
_effectsInstalled = true;
}
void showStateAlert(BuildContext context, String title, String content) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: title,
alertContent: content,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop());
});
}
void _handleDispose(ReactionDisposer? disposer) {
cakePayPurchaseViewModel.dispose();
if (disposer != null) {
disposer();
}
}
}

View file

@ -5,8 +5,8 @@ import 'package:cake_wallet/themes/extensions/cake_scrollbar_theme.dart';
import 'package:cake_wallet/typography.dart';
import 'package:flutter/material.dart';
class IoniaAlertModal extends StatelessWidget {
const IoniaAlertModal({
class CakePayAlertModal extends StatelessWidget {
const CakePayAlertModal({
Key? key,
required this.title,
required this.content,

View file

@ -3,8 +3,8 @@ import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/themes/extensions/transaction_trade_theme.dart';
class IoniaTile extends StatelessWidget {
const IoniaTile({
class CakePayTile extends StatelessWidget {
const CakePayTile({
Key? key,
required this.title,
required this.subTitle,

View file

@ -0,0 +1,104 @@
import 'package:flutter/material.dart';
import 'image_placeholder.dart';
class CardItem extends StatelessWidget {
CardItem({
required this.title,
required this.subTitle,
required this.backgroundColor,
required this.titleColor,
required this.subtitleColor,
this.hideBorder = false,
this.discount = 0.0,
this.isAmount = false,
this.discountBackground,
this.onTap,
this.logoUrl,
});
final VoidCallback? onTap;
final String title;
final String subTitle;
final String? logoUrl;
final double discount;
final bool isAmount;
final bool hideBorder;
final Color backgroundColor;
final Color titleColor;
final Color subtitleColor;
final AssetImage? discountBackground;
@override
Widget build(BuildContext context) {
return Theme(
data: ThemeData(
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
),
child: InkWell(
onTap: onTap,
child: Container(
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: BorderRadius.circular(10),
border: hideBorder
? Border.all(color: Colors.transparent)
: Border.all(color: Colors.white.withOpacity(0.20)),
),
child: Row(
children: [
if (logoUrl != null)
AspectRatio(
aspectRatio: 1.8,
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(10)),
child: Image.network(
logoUrl!,
fit: BoxFit.cover,
loadingBuilder:
(BuildContext context, Widget child, ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) return child;
return Center(child: CircularProgressIndicator());
},
errorBuilder: (context, error, stackTrace) => CakePayCardImagePlaceholder(),
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: titleColor,
fontSize: 18,
fontWeight: FontWeight.w700,
),
),
Text(
subTitle,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: titleColor,
fontSize: 10,
fontWeight: FontWeight.w700,
),
),
],
),
),
),
],
),
),
),
);
}
}

View file

@ -0,0 +1,29 @@
import 'package:flutter/material.dart';
class CakePayCardImagePlaceholder extends StatelessWidget {
const CakePayCardImagePlaceholder({this.text});
final String? text;
@override
Widget build(BuildContext context) {
return AspectRatio(
aspectRatio: 1.8,
child: Container(
child: Center(
child: Text(
text ?? 'Image not found!',
style: TextStyle(
color: Colors.black,
fontSize: 12,
fontWeight: FontWeight.w900,
),
),
),
decoration: BoxDecoration(
color: Colors.white,
),
),
);
}
}

View file

@ -0,0 +1,66 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
class ClickableLinksText extends StatelessWidget {
const ClickableLinksText({
required this.text,
required this.textStyle,
this.linkStyle,
});
final String text;
final TextStyle textStyle;
final TextStyle? linkStyle;
@override
Widget build(BuildContext context) {
List<InlineSpan> spans = [];
RegExp linkRegExp = RegExp(r'(https?://[^\s]+)');
Iterable<Match> matches = linkRegExp.allMatches(text);
int previousEnd = 0;
matches.forEach((match) {
if (match.start > previousEnd) {
spans.add(TextSpan(text: text.substring(previousEnd, match.start), style: textStyle));
}
String url = text.substring(match.start, match.end);
if (url.toLowerCase().endsWith('.md')) {
spans.add(
TextSpan(
text: url,
style: TextStyle(
color: Colors.blue,
fontSize: 18,
fontWeight: FontWeight.w400,
),
recognizer: TapGestureRecognizer()
..onTap = () async {
if (await canLaunchUrl(Uri.parse(url))) {
await launchUrl(Uri.parse(url), mode: LaunchMode.externalApplication);
}
},
),
);
} else {
spans.add(
TextSpan(
text: url,
style: linkStyle,
recognizer: TapGestureRecognizer()
..onTap = () {
launchUrl(Uri.parse(url));
},
),
);
}
previousEnd = match.end;
});
if (previousEnd < text.length) {
spans.add(TextSpan(text: text.substring(previousEnd), style: textStyle));
}
return RichText(text: TextSpan(children: spans));
}
}

View file

@ -1,17 +1,18 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/dashboard_card_widget.dart';
import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/view_model/dashboard/cake_features_view_model.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart';
import 'package:flutter_svg/flutter_svg.dart';
class CakeFeaturesPage extends StatelessWidget {
CakeFeaturesPage({
required this.dashboardViewModel,
required this.cakeFeaturesViewModel,
});
CakeFeaturesPage({required this.dashboardViewModel, required this.cakeFeaturesViewModel});
final DashboardViewModel dashboardViewModel;
final CakeFeaturesViewModel cakeFeaturesViewModel;
@ -45,20 +46,11 @@ class CakeFeaturesPage extends StatelessWidget {
child: ListView(
controller: _scrollController,
children: <Widget>[
// SizedBox(height: 20),
// DashBoardRoundedCardWidget(
// onTap: () => launchUrl(
// Uri.parse("https://cakelabs.com/news/cake-pay-mobile-to-shut-down/"),
// mode: LaunchMode.externalApplication,
// ),
// title: S.of(context).cake_pay_title,
// subTitle: S.of(context).cake_pay_subtitle,
// ),
SizedBox(height: 20),
DashBoardRoundedCardWidget(
onTap: () => _launchUrl("buy.cakepay.com"),
title: S.of(context).cake_pay_web_cards_title,
subTitle: S.of(context).cake_pay_web_cards_subtitle,
onTap: () => _navigatorToGiftCardsPage(context),
title: 'Cake Pay',
subTitle: S.of(context).cake_pay_subtitle,
svgPicture: SvgPicture.asset(
'assets/images/cards.svg',
height: 125,
@ -88,6 +80,28 @@ class CakeFeaturesPage extends StatelessWidget {
Uri.https(url),
mode: LaunchMode.externalApplication,
);
} catch (_) {}
} catch (e) {
print(e);
}
}
void _navigatorToGiftCardsPage(BuildContext context) {
final walletType = dashboardViewModel.type;
switch (walletType) {
case WalletType.haven:
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.of(context).error,
alertContent: S.of(context).gift_cards_unavailable,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop());
});
break;
default:
Navigator.pushNamed(context, Routes.cakePayCardsPage);
}
}
}

View file

@ -3,18 +3,21 @@ import 'package:cake_wallet/src/screens/dashboard/widgets/filter_tile.dart';
import 'package:cake_wallet/src/widgets/section_divider.dart';
import 'package:cake_wallet/src/widgets/standard_checkbox.dart';
import 'package:cake_wallet/themes/extensions/menu_theme.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/dropdown_filter_item.dart';
import 'package:cake_wallet/view_model/dashboard/dropdown_filter_item_widget.dart';
import 'package:cake_wallet/view_model/dashboard/filter_item.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/src/widgets/picker_wrapper_widget.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
//import 'package:date_range_picker/date_range_picker.dart' as date_rage_picker;
import 'package:cake_wallet/themes/extensions/transaction_trade_theme.dart';
class FilterWidget extends StatelessWidget {
FilterWidget({required this.dashboardViewModel});
FilterWidget({required this.filterItems});
final DashboardViewModel dashboardViewModel;
final Map<String, List<FilterItem>> filterItems;
@override
Widget build(BuildContext context) {
@ -27,75 +30,90 @@ class FilterWidget extends StatelessWidget {
borderRadius: BorderRadius.all(Radius.circular(24)),
child: Container(
color: Theme.of(context).extension<CakeMenuTheme>()!.backgroundColor,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.all(24.0),
child: Text(
S.of(context).filter_by,
style: TextStyle(
color: Theme.of(context).extension<TransactionTradeTheme>()!.detailsTitlesColor,
fontSize: 16,
fontFamily: 'Lato',
decoration: TextDecoration.none,
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Padding(
padding: EdgeInsets.all(24.0),
child: Text(
S.of(context).filter_by,
style: TextStyle(
color:
Theme.of(context).extension<TransactionTradeTheme>()!.detailsTitlesColor,
fontSize: 16,
fontFamily: 'Lato',
decoration: TextDecoration.none,
),
),
),
sectionDivider,
ListView.separated(
padding: EdgeInsets.zero,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: filterItems.length,
separatorBuilder: (context, _) => sectionDivider,
itemBuilder: (_, index1) {
final title = filterItems.keys.elementAt(index1);
final section = filterItems.values.elementAt(index1);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 20, left: 24, right: 24),
child: Text(
title,
style: TextStyle(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
fontSize: 16,
fontFamily: 'Lato',
fontWeight: FontWeight.bold,
decoration: TextDecoration.none),
),
),
),
),
sectionDivider,
ListView.separated(
padding: EdgeInsets.zero,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: dashboardViewModel.filterItems.length,
separatorBuilder: (context, _) => sectionDivider,
itemBuilder: (_, index1) {
final title = dashboardViewModel.filterItems.keys
.elementAt(index1);
final section = dashboardViewModel.filterItems.values
.elementAt(index1);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding:
EdgeInsets.only(top: 20, left: 24, right: 24),
child: Text(
title,
style: TextStyle(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
fontSize: 16,
fontFamily: 'Lato',
fontWeight: FontWeight.bold,
decoration: TextDecoration.none),
),
),
ListView.builder(
padding: EdgeInsets.symmetric(vertical: 8.0),
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: section.length,
itemBuilder: (_, index2) {
final item = section[index2];
final content = Observer(
builder: (_) => StandardCheckbox(
value: item.value(),
caption: item.caption,
gradientBackground: true,
borderColor:
Theme.of(context).dividerColor,
iconColor: Colors.white,
onChanged: (value) =>
item.onChanged(),
));
return FilterTile(child: content);
},
)
],
);
},
),
]),
ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 28.0),
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: section.length,
itemBuilder: (_, index2) {
final item = section[index2];
if (item is DropdownFilterItem) {
return Padding(
padding: EdgeInsets.fromLTRB(8, 0, 8, 16),
child: Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 1.0,
color: Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor),
),
),
child: DropdownFilterList(
items: item.items,
caption: item.caption,
selectedItem: item.selectedItem,
onItemSelected: item.onItemSelected,
),
),
);
}
final content = Observer(
builder: (_) => StandardCheckbox(
value: item.value(),
caption: item.caption,
gradientBackground: true,
borderColor: Theme.of(context).dividerColor,
iconColor: Colors.white,
onChanged: (value) => item.onChanged(),
));
return FilterTile(child: content);
},
)
],
);
},
),
]),
),
),
)

View file

@ -37,7 +37,7 @@ class HeaderRow extends StatelessWidget {
onTap: () {
showPopUp<void>(
context: context,
builder: (context) => FilterWidget(dashboardViewModel: dashboardViewModel),
builder: (context) => FilterWidget(filterItems: dashboardViewModel.filterItems),
);
},
child: Semantics(

View file

@ -1,6 +1,6 @@
import 'package:cake_wallet/src/widgets/alert_close_button.dart';
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/src/screens/ionia/widgets/rounded_checkbox.dart';
import 'package:cake_wallet/src/screens/cake_pay/widgets/rounded_checkbox.dart';
import 'package:cake_wallet/src/widgets/alert_background.dart';
import 'package:cake_wallet/typography.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';

View file

@ -1,159 +0,0 @@
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/core/email_validator.dart';
import 'package:cake_wallet/ionia/ionia_create_state.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.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/typography.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/ionia/ionia_auth_view_model.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
import 'package:url_launcher/url_launcher.dart';
class IoniaCreateAccountPage extends BasePage {
IoniaCreateAccountPage(this._authViewModel)
: _emailFocus = FocusNode(),
_emailController = TextEditingController(),
_formKey = GlobalKey<FormState>() {
_emailController.text = _authViewModel.email;
_emailController.addListener(() => _authViewModel.email = _emailController.text);
}
final IoniaAuthViewModel _authViewModel;
final GlobalKey<FormState> _formKey;
final FocusNode _emailFocus;
final TextEditingController _emailController;
static const privacyPolicyUrl = 'https://ionia.docsend.com/view/jhjvdn7qq7k3ukwt';
static const termsAndConditionsUrl = 'https://ionia.docsend.com/view/uceirymz2ijacq5g';
@override
Widget middle(BuildContext context) {
return Text(
S.current.sign_up,
style: textMediumSemiBold(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
);
}
@override
Widget body(BuildContext context) {
reaction((_) => _authViewModel.createUserState, (IoniaCreateAccountState state) {
if (state is IoniaCreateStateFailure) {
_onCreateUserFailure(context, state.error);
}
if (state is IoniaCreateStateSuccess) {
_onCreateSuccessful(context, _authViewModel);
}
});
return ScrollableWithBottomSection(
contentPadding: EdgeInsets.all(24),
content: Form(
key: _formKey,
child: BaseTextFormField(
hintText: S.of(context).email_address,
focusNode: _emailFocus,
validator: EmailValidator(),
keyboardType: TextInputType.emailAddress,
controller: _emailController,
onSubmit: (_) => _createAccount(),
),
),
bottomSectionPadding: EdgeInsets.symmetric(vertical: 36, horizontal: 24),
bottomSection: Column(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Observer(
builder: (_) => LoadingPrimaryButton(
text: S.of(context).create_account,
onPressed: _createAccount,
isLoading:
_authViewModel.createUserState is IoniaCreateStateLoading,
color: Theme.of(context).primaryColor,
textColor: Colors.white,
),
),
SizedBox(
height: 20,
),
RichText(
textAlign: TextAlign.center,
text: TextSpan(
text: S.of(context).agree_to,
style: TextStyle(
color: Color(0xff7A93BA),
fontSize: 12,
fontFamily: 'Lato',
),
children: [
TextSpan(
text: S.of(context).settings_terms_and_conditions,
style: TextStyle(
color: Theme.of(context).primaryColor,
fontWeight: FontWeight.w700,
),
recognizer: TapGestureRecognizer()
..onTap = () async {
if (await canLaunch(termsAndConditionsUrl)) await launch(termsAndConditionsUrl);
},
),
TextSpan(text: ' ${S.of(context).and} '),
TextSpan(
text: S.of(context).privacy_policy,
style: TextStyle(
color: Theme.of(context).primaryColor,
fontWeight: FontWeight.w700,
),
recognizer: TapGestureRecognizer()
..onTap = () async {
if (await canLaunch(privacyPolicyUrl)) await launch(privacyPolicyUrl);
}),
TextSpan(text: ' ${S.of(context).by_cake_pay}'),
],
),
),
],
),
],
),
);
}
void _onCreateUserFailure(BuildContext context, String error) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.current.create_account,
alertContent: error,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop());
});
}
void _onCreateSuccessful(BuildContext context, IoniaAuthViewModel authViewModel) => Navigator.pushNamed(
context,
Routes.ioniaVerifyIoniaOtpPage,
arguments: [authViewModel.email, false],
);
void _createAccount() async {
if (_formKey.currentState != null && !_formKey.currentState!.validate()) {
return;
}
await _authViewModel.createUser(_emailController.text);
}
}

View file

@ -1,95 +0,0 @@
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/typography.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
class IoniaWelcomePage extends BasePage {
IoniaWelcomePage();
@override
Widget middle(BuildContext context) {
return Text(
S.current.welcome_to_cakepay,
style: textMediumSemiBold(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
);
}
@override
Widget body(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
children: [
SizedBox(height: 90),
Text(
S.of(context).about_cake_pay,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w400,
fontFamily: 'Lato',
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
),
SizedBox(height: 20),
Text(
S.of(context).cake_pay_account_note,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w400,
fontFamily: 'Lato',
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
),
],
),
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
PrimaryButton(
text: S.of(context).create_account,
onPressed: () => Navigator.of(context).pushNamed(Routes.ioniaCreateAccountPage),
color: Theme.of(context).primaryColor,
textColor: Colors.white,
),
SizedBox(
height: 16,
),
Text(
S.of(context).already_have_account,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500,
fontFamily: 'Lato',
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
),
SizedBox(height: 8),
InkWell(
onTap: () => Navigator.of(context).pushNamed(Routes.ioniaLoginPage),
child: Text(
S.of(context).login,
style: TextStyle(
color: Palette.blueCraiola,
fontSize: 18,
letterSpacing: 1.5,
fontWeight: FontWeight.w900,
),
),
),
SizedBox(height: 20)
],
)
],
),
);
}
}

View file

@ -1,204 +0,0 @@
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/ionia/ionia_create_state.dart';
import 'package:cake_wallet/ionia/ionia_gift_card.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/ionia/widgets/card_item.dart';
import 'package:cake_wallet/themes/extensions/exchange_page_theme.dart';
import 'package:cake_wallet/themes/extensions/order_theme.dart';
import 'package:cake_wallet/typography.dart';
import 'package:cake_wallet/view_model/ionia/ionia_account_view_model.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart';
import 'package:cake_wallet/themes/extensions/transaction_trade_theme.dart';
class IoniaAccountCardsPage extends BasePage {
IoniaAccountCardsPage(this.ioniaAccountViewModel);
final IoniaAccountViewModel ioniaAccountViewModel;
@override
Widget middle(BuildContext context) {
return Text(
S.of(context).cards,
style: textLargeSemiBold(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
);
}
@override
Widget body(BuildContext context) {
return _IoniaCardTabs(ioniaAccountViewModel);
}
}
class _IoniaCardTabs extends StatefulWidget {
_IoniaCardTabs(this.ioniaAccountViewModel);
final IoniaAccountViewModel ioniaAccountViewModel;
@override
_IoniaCardTabsState createState() => _IoniaCardTabsState();
}
class _IoniaCardTabsState extends State<_IoniaCardTabs> with SingleTickerProviderStateMixin {
_IoniaCardTabsState();
TabController? _tabController;
@override
void initState() {
_tabController = TabController(length: 2, vsync: this);
super.initState();
}
@override
void dispose() {
super.dispose();
_tabController?.dispose();
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: 45,
width: 230,
padding: EdgeInsets.all(5),
decoration: BoxDecoration(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor
.withOpacity(0.1),
borderRadius: BorderRadius.circular(
25.0,
),
),
child: Theme(
data: ThemeData(primaryTextTheme: TextTheme(bodyLarge: TextStyle(backgroundColor: Colors.transparent))),
child: TabBar(
controller: _tabController,
indicator: BoxDecoration(
borderRadius: BorderRadius.circular(
25.0,
),
color: Theme.of(context).primaryColor,
),
labelColor: Theme.of(context).extension<OrderTheme>()!.iconColor,
unselectedLabelColor:
Theme.of(context).extension<CakeTextTheme>()!.titleColor,
tabs: [
Tab(
text: S.of(context).active,
),
Tab(
text: S.of(context).redeemed,
),
],
),
),
),
SizedBox(height: 16),
Expanded(
child: Observer(builder: (_) {
final viewModel = widget.ioniaAccountViewModel;
return TabBarView(
controller: _tabController,
children: [
_IoniaCardListView(
emptyText: S.of(context).gift_card_balance_note,
merchList: viewModel.activeMechs,
isLoading: viewModel.merchantState is IoniaLoadingMerchantState,
onTap: (giftCard) {
Navigator.pushNamed(
context,
Routes.ioniaGiftCardDetailPage,
arguments: [giftCard])
.then((_) => viewModel.updateUserGiftCards());
}),
_IoniaCardListView(
emptyText: S.of(context).gift_card_redeemed_note,
merchList: viewModel.redeemedMerchs,
isLoading: viewModel.merchantState is IoniaLoadingMerchantState,
onTap: (giftCard) {
Navigator.pushNamed(
context,
Routes.ioniaGiftCardDetailPage,
arguments: [giftCard])
.then((_) => viewModel.updateUserGiftCards());
}),
],
);
}),
),
],
),
);
}
}
class _IoniaCardListView extends StatelessWidget {
_IoniaCardListView({
Key? key,
required this.emptyText,
required this.merchList,
required this.onTap,
this.isLoading = false,
}) : super(key: key);
final String emptyText;
final List<IoniaGiftCard> merchList;
final void Function(IoniaGiftCard giftCard) onTap;
final bool isLoading;
@override
Widget build(BuildContext context) {
if(isLoading){
return Center(
child: CircularProgressIndicator(
backgroundColor: Theme.of(context).extension<DashboardPageTheme>()!.textColor,
valueColor: AlwaysStoppedAnimation<Color>(
Theme.of(context).extension<ExchangePageTheme>()!.firstGradientBottomPanelColor),
),
);
}
return merchList.isEmpty
? Center(
child: Text(
emptyText,
textAlign: TextAlign.center,
style: textSmall(
color: Theme.of(context).extension<TransactionTradeTheme>()!.detailsTitlesColor,
),
),
)
: ListView.builder(
itemCount: merchList.length,
itemBuilder: (context, index) {
final merchant = merchList[index];
return Padding(
padding: const EdgeInsets.only(bottom: 16),
child: CardItem(
onTap: () => onTap?.call(merchant),
title: merchant.legalName,
backgroundColor: Theme.of(context).extension<CakeTextTheme>()!.titleColor
.withOpacity(0.1),
discount: 0,
hideBorder: true,
discountBackground: AssetImage('assets/images/red_badge_discount.png'),
titleColor: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
subtitleColor: Theme.of(context).hintColor,
subTitle: '',
logoUrl: merchant.logoUrl,
),
);
},
);
}
}

View file

@ -1,178 +0,0 @@
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/ionia/widgets/ionia_tile.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/typography.dart';
import 'package:cake_wallet/view_model/ionia/ionia_account_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
class IoniaAccountPage extends BasePage {
IoniaAccountPage(this.ioniaAccountViewModel);
final IoniaAccountViewModel ioniaAccountViewModel;
@override
Widget middle(BuildContext context) {
return Text(
S.current.account,
style: textMediumSemiBold(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
);
}
@override
Widget body(BuildContext context) {
return ScrollableWithBottomSection(
contentPadding: EdgeInsets.all(24),
content: Column(
children: [
_GradiantContainer(
content: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Observer(
builder: (_) => RichText(
text: TextSpan(
text: '${ioniaAccountViewModel.countOfMerch}',
style: textLargeSemiBold(),
children: [
TextSpan(
text: ' ${S.of(context).active_cards}',
style: textSmall(color: Colors.white.withOpacity(0.7))),
],
),
)),
InkWell(
onTap: () {
Navigator.pushNamed(context, Routes.ioniaAccountCardsPage)
.then((_) => ioniaAccountViewModel.updateUserGiftCards());
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
S.of(context).view_all,
style: textSmallSemiBold(),
),
),
)
],
),
),
SizedBox(height: 8),
//Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// _GradiantContainer(
// padding: EdgeInsets.all(16),
// width: deviceWidth * 0.28,
// content: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text(
// S.of(context).total_saving,
// style: textSmall(),
// ),
// SizedBox(height: 8),
// Text(
// '\$100',
// style: textMediumSemiBold(),
// ),
// ],
// ),
// ),
// _GradiantContainer(
// padding: EdgeInsets.all(16),
// width: deviceWidth * 0.28,
// content: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text(
// S.of(context).last_30_days,
// style: textSmall(),
// ),
// SizedBox(height: 8),
// Text(
// '\$100',
// style: textMediumSemiBold(),
// ),
// ],
// ),
// ),
// _GradiantContainer(
// padding: EdgeInsets.all(16),
// width: deviceWidth * 0.28,
// content: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text(
// S.of(context).avg_savings,
// style: textSmall(),
// ),
// SizedBox(height: 8),
// Text(
// '10%',
// style: textMediumSemiBold(),
// ),
// ],
// ),
// ),
// ],
//),
SizedBox(height: 40),
Observer(
builder: (_) => IoniaTile(title: S.of(context).email_address, subTitle: ioniaAccountViewModel.email ?? ''),
),
Divider()
],
),
bottomSectionPadding: EdgeInsets.all(30),
bottomSection: Column(
children: [
PrimaryButton(
color: Theme.of(context).primaryColor,
textColor: Colors.white,
text: S.of(context).logout,
onPressed: () {
ioniaAccountViewModel.logout();
Navigator.pushNamedAndRemoveUntil(context, Routes.dashboard, (route) => false);
},
),
],
),
);
}
}
class _GradiantContainer extends StatelessWidget {
const _GradiantContainer({
Key? key,
required this.content,
}) : super(key: key);
final Widget content;
@override
Widget build(BuildContext context) {
return Container(
child: content,
padding: EdgeInsets.all(24),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
gradient: LinearGradient(
colors: [
Theme.of(context).extension<SendPageTheme>()!.secondGradientColor,
Theme.of(context).extension<SendPageTheme>()!.firstGradientColor,
],
begin: Alignment.topRight,
end: Alignment.bottomLeft,
),
),
);
}
}

View file

@ -1,115 +0,0 @@
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/ionia/ionia_create_state.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/ionia/widgets/text_icon_button.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.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/typography.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/ionia/ionia_gift_cards_list_view_model.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:mobx/mobx.dart';
class IoniaActivateDebitCardPage extends BasePage {
IoniaActivateDebitCardPage(this._cardsListViewModel);
final IoniaGiftCardsListViewModel _cardsListViewModel;
@override
Widget middle(BuildContext context) {
return Text(
S.current.debit_card,
style: textMediumSemiBold(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
);
}
@override
Widget body(BuildContext context) {
reaction((_) => _cardsListViewModel.createCardState, (IoniaCreateCardState state) {
if (state is IoniaCreateCardFailure) {
_onCreateCardFailure(context, state.error);
}
if (state is IoniaCreateCardSuccess) {
_onCreateCardSuccess(context);
}
});
return ScrollableWithBottomSection(
contentPadding: EdgeInsets.zero,
content: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
SizedBox(height: 16),
Text(S.of(context).debit_card_terms),
SizedBox(height: 24),
Text(S.of(context).please_reference_document),
SizedBox(height: 40),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Column(
children: [
TextIconButton(
label: S.current.cardholder_agreement,
onTap: () {},
),
SizedBox(
height: 24,
),
TextIconButton(
label: S.current.e_sign_consent,
onTap: () {},
),
],
),
),
],
),
),
bottomSection: LoadingPrimaryButton(
onPressed: () {
_cardsListViewModel.createCard();
},
isLoading: _cardsListViewModel.createCardState is IoniaCreateCardLoading,
text: S.of(context).agree_and_continue,
color: Theme.of(context).primaryColor,
textColor: Colors.white,
),
);
}
void _onCreateCardFailure(BuildContext context, String errorMessage) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.current.error,
alertContent: errorMessage,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop());
});
}
void _onCreateCardSuccess(BuildContext context) {
Navigator.pushNamed(
context,
Routes.ioniaDebitCardPage,
);
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.of(context).congratulations,
alertContent: S.of(context).you_now_have_debit_card,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop(),
);
},
);
}
}

View file

@ -1,478 +0,0 @@
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/themes/extensions/receive_page_theme.dart';
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/ionia/ionia_merchant.dart';
import 'package:cake_wallet/ionia/ionia_tip.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/ionia/widgets/ionia_alert_model.dart';
import 'package:cake_wallet/src/screens/ionia/widgets/text_icon_button.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/discount_badge.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/themes/extensions/exchange_page_theme.dart';
import 'package:cake_wallet/typography.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/ionia/ionia_purchase_merch_view_model.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/send/widgets/confirm_sending_alert.dart';
import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
import 'package:cake_wallet/themes/extensions/transaction_trade_theme.dart';
class IoniaBuyGiftCardDetailPage extends BasePage {
IoniaBuyGiftCardDetailPage(this.ioniaPurchaseViewModel);
final IoniaMerchPurchaseViewModel ioniaPurchaseViewModel;
@override
Widget middle(BuildContext context) {
return Text(
ioniaPurchaseViewModel.ioniaMerchant.legalName,
style: textMediumSemiBold(color: Theme.of(context).extension<CakeTextTheme>()!.titleColor),
);
}
@override
Widget? trailing(BuildContext context)
=> ioniaPurchaseViewModel.ioniaMerchant.discount > 0
? DiscountBadge(percentage: ioniaPurchaseViewModel.ioniaMerchant.discount)
: null;
@override
Widget body(BuildContext context) {
reaction((_) => ioniaPurchaseViewModel.invoiceCreationState, (ExecutionState state) {
if (state is FailureState) {
WidgetsBinding.instance.addPostFrameCallback((_) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.of(context).error,
alertContent: state.error,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop());
});
});
}
});
reaction((_) => ioniaPurchaseViewModel.invoiceCommittingState, (ExecutionState state) {
if (state is FailureState) {
WidgetsBinding.instance.addPostFrameCallback((_) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.of(context).error,
alertContent: state.error,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop());
});
});
}
if (state is ExecutedSuccessfullyState) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.of(context).pushReplacementNamed(
Routes.ioniaPaymentStatusPage,
arguments: [
ioniaPurchaseViewModel.paymentInfo,
ioniaPurchaseViewModel.committedInfo]);
});
}
});
return ScrollableWithBottomSection(
contentPadding: EdgeInsets.zero,
content: Observer(builder: (_) {
final tipAmount = ioniaPurchaseViewModel.tipAmount;
return Column(
children: [
SizedBox(height: 36),
Container(
padding: EdgeInsets.symmetric(vertical: 24),
margin: EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
gradient: LinearGradient(
colors: [
Theme.of(context).extension<SendPageTheme>()!.firstGradientColor,
Theme.of(context).extension<SendPageTheme>()!.secondGradientColor,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
child: Column(
children: [
Text(
S.of(context).gift_card_amount,
style: textSmall(),
),
SizedBox(height: 4),
Text(
'\$${ioniaPurchaseViewModel.giftCardAmount.toStringAsFixed(2)}',
style: textXLargeSemiBold(),
),
SizedBox(height: 24),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
S.of(context).bill_amount,
style: textSmall(),
),
SizedBox(height: 4),
Text(
'\$${ioniaPurchaseViewModel.billAmount.toStringAsFixed(2)}',
style: textLargeSemiBold(),
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
S.of(context).tip,
style: textSmall(),
),
SizedBox(height: 4),
Text(
'\$${tipAmount.toStringAsFixed(2)}',
style: textLargeSemiBold(),
),
],
),
],
),
),
],
),
),
if(ioniaPurchaseViewModel.ioniaMerchant.acceptsTips)
Padding(
padding: const EdgeInsets.fromLTRB(24.0, 24.0, 0, 24.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
S.of(context).tip,
style: TextStyle(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
fontWeight: FontWeight.w700,
fontSize: 14,
),
),
SizedBox(height: 4),
Observer(
builder: (_) => TipButtonGroup(
selectedTip: ioniaPurchaseViewModel.selectedTip!.percentage,
tipsList: ioniaPurchaseViewModel.tips,
onSelect: (value) => ioniaPurchaseViewModel.addTip(value),
amount: ioniaPurchaseViewModel.amount,
merchant: ioniaPurchaseViewModel.ioniaMerchant,
),
)
],
),
),
SizedBox(height: 20),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: TextIconButton(
label: S.of(context).how_to_use_card,
onTap: () => _showHowToUseCard(context, ioniaPurchaseViewModel.ioniaMerchant),
),
),
],
);
}),
bottomSection: Column(
children: [
Padding(
padding: EdgeInsets.only(bottom: 12),
child: Observer(builder: (_) {
return LoadingPrimaryButton(
isLoading: ioniaPurchaseViewModel.invoiceCreationState is IsExecutingState ||
ioniaPurchaseViewModel.invoiceCommittingState is IsExecutingState,
onPressed: () => purchaseCard(context),
text: S.of(context).purchase_gift_card,
color: Theme.of(context).primaryColor,
textColor: Colors.white,
);
}),
),
SizedBox(height: 8),
InkWell(
onTap: () => _showTermsAndCondition(context),
child: Text(S.of(context).settings_terms_and_conditions,
style: textMediumSemiBold(
color: Theme.of(context).extension<ExchangePageTheme>()!.firstGradientBottomPanelColor,
).copyWith(fontSize: 12)),
),
SizedBox(height: 16)
],
),
);
}
void _showTermsAndCondition(BuildContext context) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return IoniaAlertModal(
title: S.of(context).settings_terms_and_conditions,
content: Align(
alignment: Alignment.bottomLeft,
child: Text(
ioniaPurchaseViewModel.ioniaMerchant.termsAndConditions,
style: textMedium(
color: Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor,
),
),
),
actionTitle: S.of(context).agree,
showCloseButton: false,
heightFactor: 0.6,
);
});
}
Future<void> purchaseCard(BuildContext context) async {
await ioniaPurchaseViewModel.createInvoice();
if (ioniaPurchaseViewModel.invoiceCreationState is ExecutedSuccessfullyState) {
await _presentSuccessfulInvoiceCreationPopup(context);
}
}
void _showHowToUseCard(
BuildContext context,
IoniaMerchant merchant,
) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return IoniaAlertModal(
title: S.of(context).how_to_use_card,
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: merchant.instructions
.map((instruction) {
return [
Padding(
padding: EdgeInsets.all(10),
child: Text(
instruction.header,
style: textLargeSemiBold(
color: Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor,
),
)),
Text(
instruction.body,
style: textMedium(
color: Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor,
),
)
];
})
.expand((e) => e)
.toList()),
actionTitle: S.current.got_it,
);
});
}
Future<void> _presentSuccessfulInvoiceCreationPopup(BuildContext context) async {
if (ioniaPurchaseViewModel.invoice == null) {
return;
}
final amount = ioniaPurchaseViewModel.invoice!.totalAmount;
final addresses = ioniaPurchaseViewModel.invoice!.outAddresses;
ioniaPurchaseViewModel.sendViewModel.outputs.first.setCryptoAmount(amount);
ioniaPurchaseViewModel.sendViewModel.outputs.first.address = addresses.first;
await showPopUp<void>(
context: context,
builder: (_) {
return ConfirmSendingAlert(
alertTitle: S.of(context).confirm_sending,
paymentId: S.of(context).payment_id,
paymentIdValue: ioniaPurchaseViewModel.invoice!.paymentId,
amount: S.of(context).send_amount,
amountValue: '$amount ${ioniaPurchaseViewModel.invoice!.chain}',
fiatAmountValue:
'~ ${ioniaPurchaseViewModel.sendViewModel.outputs.first.fiatAmount} '
'${ioniaPurchaseViewModel.sendViewModel.fiat.title}',
fee: S.of(context).send_fee,
feeValue:
'${ioniaPurchaseViewModel.sendViewModel.outputs.first.estimatedFee} '
'${ioniaPurchaseViewModel.invoice!.chain}',
feeFiatAmount:
'${ioniaPurchaseViewModel.sendViewModel.outputs.first.estimatedFeeFiatAmount} '
'${ioniaPurchaseViewModel.sendViewModel.fiat.title}',
outputs: ioniaPurchaseViewModel.sendViewModel.outputs,
rightButtonText: S.of(context).ok,
leftButtonText: S.of(context).cancel,
alertLeftActionButtonTextColor: Colors.white,
alertRightActionButtonTextColor: Colors.white,
alertLeftActionButtonColor: Palette.brightOrange,
alertRightActionButtonColor: Theme.of(context).primaryColor,
actionRightButton: () async {
Navigator.of(context).pop();
await ioniaPurchaseViewModel.commitPaymentInvoice();
},
actionLeftButton: () => Navigator.of(context).pop());
},
);
}
}
class TipButtonGroup extends StatelessWidget {
const TipButtonGroup({
Key? key,
required this.selectedTip,
required this.onSelect,
required this.tipsList,
required this.amount,
required this.merchant,
}) : super(key: key);
final Function(IoniaTip) onSelect;
final double selectedTip;
final List<IoniaTip> tipsList;
final double amount;
final IoniaMerchant merchant;
bool _isSelected(double value) => selectedTip == value;
Set<double> get filter => tipsList.map((e) => e.percentage).toSet();
bool get _isCustomSelected => !filter.contains(selectedTip);
@override
Widget build(BuildContext context) {
return Container(
height: 50,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: tipsList.length,
itemBuilder: (BuildContext context, int index) {
final tip = tipsList[index];
return Padding(
padding: EdgeInsets.only(right: 5),
child: TipButton(
isSelected: tip.isCustom ? _isCustomSelected : _isSelected(tip.percentage),
onTap: () async {
IoniaTip ioniaTip = tip;
if(tip.isCustom){
final customTip = await Navigator.pushNamed(context, Routes.ioniaCustomTipPage, arguments: [amount, merchant, tip]) as IoniaTip?;
ioniaTip = customTip ?? tip;
}
onSelect(ioniaTip);
},
caption: tip.isCustom ? S.of(context).custom : '${tip.percentage.toStringAsFixed(0)}%',
subTitle: tip.isCustom ? null : '\$${tip.additionalAmount.toStringAsFixed(2)}',
));
}));
}
}
class TipButton extends StatelessWidget {
const TipButton({
required this.caption,
required this.onTap,
this.subTitle,
this.isSelected = false,
});
final String caption;
final String? subTitle;
final bool isSelected;
final void Function() onTap;
bool isDark(BuildContext context) => Theme.of(context).brightness == Brightness.dark;
Color captionTextColor(BuildContext context) {
if (isDark(context)) {
return Theme.of(context).extension<CakeTextTheme>()!.titleColor;
}
return isSelected
? Theme.of(context).dialogTheme.backgroundColor!
: Theme.of(context).extension<CakeTextTheme>()!.titleColor;
}
Color subTitleTextColor(BuildContext context) {
if (isDark(context)) {
return Theme.of(context).extension<CakeTextTheme>()!.titleColor;
}
return isSelected
? Theme.of(context).dialogTheme.backgroundColor!
: Theme.of(context).extension<TransactionTradeTheme>()!.detailsTitlesColor;
}
Color? backgroundColor(BuildContext context) {
if (isDark(context)) {
return isSelected
? null
: Theme.of(context).extension<CakeTextTheme>()!.titleColor.withOpacity(0.01);
}
return isSelected
? null
: Theme.of(context).extension<CakeTextTheme>()!.titleColor.withOpacity(0.1);
}
@override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
child: Container(
height: 49,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(caption,
style: textSmallSemiBold(
color: captionTextColor(context))),
if (subTitle != null) ...[
SizedBox(height: 4),
Text(
subTitle!,
style: textXxSmallSemiBold(
color: subTitleTextColor(context),
),
),
]
],
),
padding: EdgeInsets.symmetric(horizontal: 18, vertical: 8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: backgroundColor(context),
gradient: isSelected
? LinearGradient(
colors: [
Theme.of(context).extension<SendPageTheme>()!.firstGradientColor,
Theme.of(context).extension<SendPageTheme>()!.secondGradientColor,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
)
: null,
),
),
);
}
}

View file

@ -1,186 +0,0 @@
import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/ionia/widgets/card_item.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/src/widgets/keyboard_done_button.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/themes/theme_base.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/view_model/ionia/ionia_buy_card_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:keyboard_actions/keyboard_actions.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
class IoniaBuyGiftCardPage extends BasePage {
IoniaBuyGiftCardPage(
this.ioniaBuyCardViewModel,
) : _amountFieldFocus = FocusNode(),
_amountController = TextEditingController() {
_amountController.addListener(() {
ioniaBuyCardViewModel.onAmountChanged(_amountController.text);
});
}
final IoniaBuyCardViewModel ioniaBuyCardViewModel;
@override
String get title => S.current.enter_amount;
@override
bool get extendBodyBehindAppBar => true;
@override
AppBarStyle get appBarStyle => AppBarStyle.transparent;
Color get textColor => currentTheme.type == ThemeType.dark ? Colors.white : Color(0xff393939);
final TextEditingController _amountController;
final FocusNode _amountFieldFocus;
@override
Widget body(BuildContext context) {
final merchant = ioniaBuyCardViewModel.ioniaMerchant;
return KeyboardActions(
disableScroll: true,
config: KeyboardActionsConfig(
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
keyboardBarColor: Theme.of(context).extension<KeyboardTheme>()!.keyboardBarColor,
nextFocus: false,
actions: [
KeyboardActionsItem(
focusNode: _amountFieldFocus,
toolbarButtons: [(_) => KeyboardDoneButton()],
),
]),
child: Container(
color: Theme.of(context).colorScheme.background,
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.zero,
content: Column(
children: [
Container(
padding: EdgeInsets.symmetric(horizontal: 25),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(24),
bottomRight: Radius.circular(24),
),
gradient: LinearGradient(colors: [
Theme.of(context).extension<SendPageTheme>()!.firstGradientColor,
Theme.of(context).extension<SendPageTheme>()!.secondGradientColor,
], begin: Alignment.topLeft, end: Alignment.bottomRight),
),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(height: 150),
SizedBox(
width: 200,
child: BaseTextFormField(
controller: _amountController,
focusNode: _amountFieldFocus,
keyboardType: TextInputType.numberWithOptions(signed: false, decimal: true),
inputFormatters: [
FilteringTextInputFormatter.deny(RegExp('[\-|\ ]')),
FilteringTextInputFormatter.allow(
RegExp(r'^\d+(\.|\,)?\d{0,2}'),
),
],
hintText: '1000',
placeholderTextStyle: TextStyle(
color: Theme.of(context).extension<SendPageTheme>()!.textFieldBorderColor,
fontWeight: FontWeight.w600,
fontSize: 36,
),
prefixIcon: Text(
'USD: ',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
fontSize: 36,
),
),
textColor: Colors.white,
textStyle: TextStyle(
color: Colors.white,
fontSize: 36,
),
),
),
Divider(
color: Theme.of(context).extension<SendPageTheme>()!.textFieldBorderColor,
height: 1,
),
SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
S.of(context).min_amount(merchant.minimumCardPurchase.toStringAsFixed(2)),
style: TextStyle(
color: Theme.of(context).extension<SendPageTheme>()!.textFieldBorderColor,
),
),
Text(
S.of(context).max_amount(merchant.maximumCardPurchase.toStringAsFixed(2)),
style: TextStyle(
color: Theme.of(context).extension<SendPageTheme>()!.textFieldBorderColor,
),
),
],
),
SizedBox(height: 24),
],
),
),
Padding(
padding: const EdgeInsets.all(24.0),
child: CardItem(
title: merchant.legalName,
backgroundColor: Theme.of(context).extension<CakeTextTheme>()!.titleColor
.withOpacity(0.1),
discount: merchant.discount,
titleColor: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
subtitleColor: Theme.of(context).hintColor,
subTitle: merchant.avaibilityStatus,
logoUrl: merchant.logoUrl,
),
)
],
),
bottomSection: Column(
children: [
Observer(builder: (_) {
return Padding(
padding: EdgeInsets.only(bottom: 12),
child: PrimaryButton(
onPressed: () => Navigator.of(context).pushNamed(
Routes.ioniaBuyGiftCardDetailPage,
arguments: [
ioniaBuyCardViewModel.amount,
ioniaBuyCardViewModel.ioniaMerchant,
],
),
text: S.of(context).continue_text,
isDisabled: !ioniaBuyCardViewModel.isEnablePurchase,
color: Theme.of(context).primaryColor,
textColor: Colors.white,
),
);
}),
SizedBox(height: 30),
],
),
),
),
);
}
}

View file

@ -1,175 +0,0 @@
import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/ionia/widgets/card_item.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/src/widgets/keyboard_done_button.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/themes/theme_base.dart';
import 'package:cake_wallet/view_model/ionia/ionia_custom_redeem_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:keyboard_actions/keyboard_actions.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
class IoniaCustomRedeemPage extends BasePage {
IoniaCustomRedeemPage(
this.ioniaCustomRedeemViewModel,
) : _amountFieldFocus = FocusNode(),
_amountController = TextEditingController() {
_amountController.addListener(() {
ioniaCustomRedeemViewModel.updateAmount(_amountController.text);
});
}
final IoniaCustomRedeemViewModel ioniaCustomRedeemViewModel;
@override
String get title => S.current.custom_redeem_amount;
@override
bool get extendBodyBehindAppBar => true;
@override
AppBarStyle get appBarStyle => AppBarStyle.transparent;
Color get textColor => currentTheme.type == ThemeType.dark ? Colors.white : Color(0xff393939);
final TextEditingController _amountController;
final FocusNode _amountFieldFocus;
@override
Widget body(BuildContext context) {
final _width = MediaQuery.of(context).size.width;
final giftCard = ioniaCustomRedeemViewModel.giftCard;
return KeyboardActions(
disableScroll: true,
config: KeyboardActionsConfig(
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
keyboardBarColor: Theme.of(context).extension<KeyboardTheme>()!.keyboardBarColor,
nextFocus: false,
actions: [
KeyboardActionsItem(
focusNode: _amountFieldFocus,
toolbarButtons: [(_) => KeyboardDoneButton()],
),
]),
child: Container(
color: Theme.of(context).colorScheme.background,
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.zero,
content: Column(
children: [
Container(
padding: EdgeInsets.symmetric(horizontal: 25),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)),
gradient: LinearGradient(colors: [
Theme.of(context).extension<SendPageTheme>()!.firstGradientColor,
Theme.of(context).extension<SendPageTheme>()!.secondGradientColor,
], begin: Alignment.topLeft, end: Alignment.bottomRight),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SizedBox(height: 150),
BaseTextFormField(
controller: _amountController,
focusNode: _amountFieldFocus,
keyboardType: TextInputType.numberWithOptions(signed: false, decimal: true),
inputFormatters: [FilteringTextInputFormatter.deny(RegExp('[\-|\ ]'))],
hintText: '1000',
placeholderTextStyle: TextStyle(
color: Theme.of(context).extension<SendPageTheme>()!.textFieldBorderColor,
fontWeight: FontWeight.w500,
fontSize: 36,
),
borderColor: Theme.of(context).extension<SendPageTheme>()!.textFieldBorderColor,
textColor: Colors.white,
textStyle: TextStyle(
color: Colors.white,
fontSize: 36,
),
suffixIcon: SizedBox(
width: _width / 6,
),
prefixIcon: Padding(
padding: EdgeInsets.only(
top: 5.0,
left: _width / 4,
),
child: Text(
'USD: ',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w900,
fontSize: 36,
),
),
),
),
SizedBox(height: 8),
Observer(
builder: (_) => !ioniaCustomRedeemViewModel.disableRedeem
? Center(
child: Text(
'\$${giftCard.remainingAmount} - \$${ioniaCustomRedeemViewModel.amount} = \$${ioniaCustomRedeemViewModel.formattedRemaining} ${S.of(context).remaining}',
style: TextStyle(
color: Theme.of(context).extension<SendPageTheme>()!.textFieldBorderColor,
),
),
)
: SizedBox.shrink(),
),
SizedBox(height: 24),
],
),
),
Padding(
padding: const EdgeInsets.all(24.0),
child: CardItem(
title: giftCard.legalName,
backgroundColor: Theme.of(context).extension<CakeTextTheme>()!.titleColor
.withOpacity(0.1),
discount: giftCard.remainingAmount,
isAmount: true,
discountBackground: AssetImage('assets/images/red_badge_discount.png'),
titleColor: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
subtitleColor: Theme.of(context).hintColor,
subTitle: S.of(context).online,
logoUrl: giftCard.logoUrl,
),
),
],
),
bottomSection: Column(
children: [
Observer(
builder: (_) => Padding(
padding: EdgeInsets.only(bottom: 12),
child: LoadingPrimaryButton(
isLoading: ioniaCustomRedeemViewModel.redeemState is IsExecutingState,
isDisabled: ioniaCustomRedeemViewModel.disableRedeem,
text: S.of(context).add_custom_redemption,
color: Theme.of(context).primaryColor,
textColor: Colors.white,
onPressed: () => ioniaCustomRedeemViewModel.addCustomRedeem().then((value) {
Navigator.of(context).pop(ioniaCustomRedeemViewModel.remaining.toString());
}),
),
),
),
SizedBox(height: 30),
],
),
),
),
);
}
}

View file

@ -1,177 +0,0 @@
import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/ionia/ionia_merchant.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/ionia/widgets/card_item.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/src/widgets/keyboard_done_button.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/themes/theme_base.dart';
import 'package:cake_wallet/view_model/ionia/ionia_custom_tip_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:keyboard_actions/keyboard_actions.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
class IoniaCustomTipPage extends BasePage {
IoniaCustomTipPage(
this.customTipViewModel,
) : _amountFieldFocus = FocusNode(),
_amountController = TextEditingController() {
_amountController.addListener(() {
customTipViewModel.onTipChanged(_amountController.text);
});
}
final IoniaCustomTipViewModel customTipViewModel;
@override
String get title => S.current.enter_amount;
@override
bool get extendBodyBehindAppBar => true;
@override
AppBarStyle get appBarStyle => AppBarStyle.transparent;
Color get textColor => currentTheme.type == ThemeType.dark ? Colors.white : Color(0xff393939);
final TextEditingController _amountController;
final FocusNode _amountFieldFocus;
@override
Widget body(BuildContext context) {
final _width = MediaQuery.of(context).size.width;
final merchant = customTipViewModel.ioniaMerchant;
return KeyboardActions(
disableScroll: true,
config: KeyboardActionsConfig(
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
keyboardBarColor: Theme.of(context).extension<KeyboardTheme>()!.keyboardBarColor,
nextFocus: false,
actions: [
KeyboardActionsItem(
focusNode: _amountFieldFocus,
toolbarButtons: [(_) => KeyboardDoneButton()],
),
]),
child: Container(
color: Theme.of(context).colorScheme.background,
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.zero,
content: Column(
children: [
Container(
padding: EdgeInsets.symmetric(horizontal: 25),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)),
gradient: LinearGradient(colors: [
Theme.of(context).extension<SendPageTheme>()!.firstGradientColor,
Theme.of(context).extension<SendPageTheme>()!.secondGradientColor,
], begin: Alignment.topLeft, end: Alignment.bottomRight),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SizedBox(height: 150),
BaseTextFormField(
controller: _amountController,
focusNode: _amountFieldFocus,
keyboardType: TextInputType.numberWithOptions(signed: false, decimal: true),
inputFormatters: [FilteringTextInputFormatter.deny(RegExp('[\-|\ ]'))],
hintText: '1000',
placeholderTextStyle: TextStyle(
color: Theme.of(context).extension<SendPageTheme>()!.textFieldBorderColor,
fontWeight: FontWeight.w500,
fontSize: 36,
),
borderColor: Theme.of(context).extension<SendPageTheme>()!.textFieldBorderColor,
textColor: Colors.white,
textStyle: TextStyle(
color: Colors.white,
fontSize: 36,
),
suffixIcon: SizedBox(
width: _width / 6,
),
prefixIcon: Padding(
padding: EdgeInsets.only(
top: 5.0,
left: _width / 4,
),
child: Text(
'USD: ',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w900,
fontSize: 36,
),
),
),
),
SizedBox(height: 8),
Observer(builder: (_) {
if (customTipViewModel.percentage == 0.0) {
return SizedBox.shrink();
}
return RichText(
textAlign: TextAlign.center,
text: TextSpan(
text: '\$${_amountController.text}',
style: TextStyle(
color: Theme.of(context).extension<SendPageTheme>()!.textFieldBorderColor,
),
children: [
TextSpan(text: ' ${S.of(context).is_percentage} '),
TextSpan(text: '${customTipViewModel.percentage.toStringAsFixed(2)}%'),
TextSpan(text: ' ${S.of(context).percentageOf(customTipViewModel.amount.toStringAsFixed(2))} '),
],
),
);
}),
SizedBox(height: 24),
],
),
),
Padding(
padding: const EdgeInsets.all(24.0),
child: CardItem(
title: merchant.legalName,
backgroundColor: Theme.of(context).extension<CakeTextTheme>()!.titleColor
.withOpacity(0.1),
discount: 0.0,
titleColor: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
subtitleColor: Theme.of(context).hintColor,
subTitle: merchant.isOnline ? S.of(context).online : S.of(context).offline,
logoUrl: merchant.logoUrl,
),
)
],
),
bottomSection: Column(
children: [
Padding(
padding: EdgeInsets.only(bottom: 12),
child: PrimaryButton(
onPressed: () {
Navigator.of(context).pop(customTipViewModel.customTip);
},
text: S.of(context).add_tip,
color: Theme.of(context).primaryColor,
textColor: Colors.white,
),
),
SizedBox(height: 30),
],
),
),
),
);
}
}

View file

@ -1,393 +0,0 @@
import 'package:cake_wallet/ionia/ionia_create_state.dart';
import 'package:cake_wallet/themes/extensions/receive_page_theme.dart';
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/ionia/ionia_virtual_card.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/ionia/widgets/text_icon_button.dart';
import 'package:cake_wallet/src/widgets/alert_background.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/themes/extensions/cake_scrollbar_theme.dart';
import 'package:cake_wallet/typography.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/ionia/ionia_gift_cards_list_view_model.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
class IoniaDebitCardPage extends BasePage {
final IoniaGiftCardsListViewModel _cardsListViewModel;
IoniaDebitCardPage(this._cardsListViewModel);
@override
Widget middle(BuildContext context) {
return Text(
S.current.debit_card,
style: textMediumSemiBold(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
);
}
@override
Widget body(BuildContext context) {
return Observer(
builder: (_) {
final cardState = _cardsListViewModel.cardState;
if (cardState is IoniaFetchingCard) {
return Center(child: CircularProgressIndicator());
}
if (cardState is IoniaCardSuccess) {
return ScrollableWithBottomSection(
contentPadding: EdgeInsets.zero,
content: Padding(
padding: const EdgeInsets.all(16.0),
child: _IoniaDebitCard(
cardInfo: cardState.card,
),
),
bottomSection: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: Text(
S.of(context).billing_address_info,
style: textSmall(
color: Theme.of(context).extension<ReceivePageTheme>()!.iconsColor),
textAlign: TextAlign.center,
),
),
SizedBox(height: 24),
PrimaryButton(
text: S.of(context).order_physical_card,
onPressed: () {},
color: Color(0xffE9F2FC),
textColor: Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor,
),
SizedBox(height: 8),
PrimaryButton(
text: S.of(context).add_value,
onPressed: () {},
color: Theme.of(context).primaryColor,
textColor: Colors.white,
),
SizedBox(height: 16)
],
),
);
}
return ScrollableWithBottomSection(
contentPadding: EdgeInsets.zero,
content: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
_IoniaDebitCard(isCardSample: true),
SizedBox(height: 40),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Column(
children: [
TextIconButton(
label: S.current.how_to_use_card,
onTap: () => _showHowToUseCard(context),
),
SizedBox(
height: 24,
),
TextIconButton(
label: S.current.frequently_asked_questions,
onTap: () {},
),
],
),
),
SizedBox(height: 50),
Container(
padding: EdgeInsets.all(20),
margin: EdgeInsets.all(8),
width: double.infinity,
decoration: BoxDecoration(
color: Color.fromRGBO(233, 242, 252, 1),
borderRadius: BorderRadius.circular(20),
),
child: RichText(
text: TextSpan(
text: S.of(context).get_a,
style: textMedium(
color:
Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor),
children: [
TextSpan(
text: S.of(context).digital_and_physical_card,
style: textMediumBold(
color: Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor),
),
TextSpan(
text: S.of(context).get_card_note,
)
],
)),
),
],
),
),
bottomSectionPadding: EdgeInsets.symmetric(
horizontal: 16,
vertical: 32,
),
bottomSection: PrimaryButton(
text: S.of(context).activate,
onPressed: () => _showHowToUseCard(context, activate: true),
color: Theme.of(context).primaryColor,
textColor: Colors.white,
),
);
},
);
}
void _showHowToUseCard(BuildContext context, {bool activate = false}) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertBackground(
child: Material(
color: Colors.transparent,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(height: 10),
Container(
padding: EdgeInsets.only(top: 24, left: 24, right: 24),
margin: EdgeInsets.all(24),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background,
borderRadius: BorderRadius.circular(30),
),
child: Column(
children: [
Text(
S.of(context).how_to_use_card,
style: textLargeSemiBold(
color:
Theme.of(context).extension<CakeScrollbarTheme>()!.thumbColor,
),
),
SizedBox(height: 24),
Align(
alignment: Alignment.bottomLeft,
child: Text(
S.of(context).signup_for_card_accept_terms,
style: textSmallSemiBold(
color: Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor,
),
),
),
SizedBox(height: 24),
_TitleSubtitleTile(
title: S.of(context).add_fund_to_card('1000'),
subtitle: S.of(context).use_card_info_two,
),
SizedBox(height: 21),
_TitleSubtitleTile(
title: S.of(context).use_card_info_three,
subtitle: S.of(context).optionally_order_card,
),
SizedBox(height: 35),
PrimaryButton(
onPressed: () => activate
? Navigator.pushNamed(context, Routes.ioniaActivateDebitCardPage)
: Navigator.pop(context),
text: S.of(context).got_it,
color: Color.fromRGBO(233, 242, 252, 1),
textColor:
Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor,
),
SizedBox(height: 21),
],
),
),
InkWell(
onTap: () => Navigator.pop(context),
child: Container(
margin: EdgeInsets.only(bottom: 40),
child: CircleAvatar(
child: Icon(
Icons.close,
color: Colors.black,
),
backgroundColor: Colors.white,
),
),
)
],
),
),
);
});
}
}
class _IoniaDebitCard extends StatefulWidget {
const _IoniaDebitCard({
Key? key,
this.cardInfo,
this.isCardSample = false,
}) : super(key: key);
final bool isCardSample;
final IoniaVirtualCard? cardInfo;
@override
_IoniaDebitCardState createState() => _IoniaDebitCardState();
}
class _IoniaDebitCardState extends State<_IoniaDebitCard> {
bool _showDetails = false;
void _toggleVisibility() {
setState(() => _showDetails = !_showDetails);
}
String _formatPan(String pan) {
if (pan == null) return '';
return pan.replaceAllMapped(RegExp(r'.{4}'), (match) => '${match.group(0)} ');
}
String get _getLast4 => widget.isCardSample ? '0000' : widget.cardInfo!.pan.substring(widget.cardInfo!.pan.length - 5);
String get _getSpendLimit => widget.isCardSample ? '10000' : widget.cardInfo!.spendLimit.toStringAsFixed(2);
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 19),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24),
gradient: LinearGradient(
colors: [
Theme.of(context).extension<SendPageTheme>()!.firstGradientColor,
Theme.of(context).extension<SendPageTheme>()!.secondGradientColor,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
S.current.cakepay_prepaid_card,
style: textSmall(),
),
Image.asset(
'assets/images/mastercard.png',
width: 54,
),
],
),
Text(
widget.isCardSample ? S.of(context).upto(_getSpendLimit) : '\$$_getSpendLimit',
style: textXLargeSemiBold(),
),
SizedBox(height: 16),
Text(
_showDetails ? _formatPan(widget.cardInfo?.pan ?? '') : '**** **** **** $_getLast4',
style: textMediumSemiBold(),
),
SizedBox(height: 32),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (widget.isCardSample)
Text(
S.current.no_id_needed,
style: textMediumBold(),
)
else ...[
Column(
children: [
Text(
'CVV',
style: textXSmallSemiBold(),
),
SizedBox(height: 4),
Text(
_showDetails ? widget.cardInfo!.cvv : '***',
style: textMediumSemiBold(),
)
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
S.of(context).expires,
style: textXSmallSemiBold(),
),
SizedBox(height: 4),
Text(
'${widget.cardInfo?.expirationMonth ?? S.of(context).mm}/${widget.cardInfo?.expirationYear ?? S.of(context).yy}',
style: textMediumSemiBold(),
)
],
),
]
],
),
if (!widget.isCardSample) ...[
SizedBox(height: 8),
Center(
child: InkWell(
onTap: () => _toggleVisibility(),
child: Text(
_showDetails ? S.of(context).hide_details : S.of(context).show_details,
style: textSmall(),
),
),
),
],
],
),
);
}
}
class _TitleSubtitleTile extends StatelessWidget {
const _TitleSubtitleTile({
Key? key,
required this.title,
required this.subtitle,
}) : super(key: key);
final String title;
final String subtitle;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: textSmallSemiBold(
color: Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor),
),
SizedBox(height: 4),
Text(
subtitle,
style: textSmall(
color: Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor),
),
],
);
}
}

View file

@ -1,215 +0,0 @@
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/themes/extensions/receive_page_theme.dart';
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/ionia/ionia_gift_card.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/ionia/widgets/ionia_alert_model.dart';
import 'package:cake_wallet/src/screens/ionia/widgets/ionia_tile.dart';
import 'package:cake_wallet/src/screens/ionia/widgets/text_icon_button.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.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/typography.dart';
import 'package:cake_wallet/utils/show_bar.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/utils/route_aware.dart';
import 'package:cake_wallet/view_model/ionia/ionia_gift_card_details_view_model.dart';
import 'package:device_display_brightness/device_display_brightness.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
class IoniaGiftCardDetailPage extends BasePage {
IoniaGiftCardDetailPage(this.viewModel);
final IoniaGiftCardDetailsViewModel viewModel;
@override
Widget? leading(BuildContext context) {
if (ModalRoute.of(context)!.isFirst) {
return null;
}
final _backButton = Icon(
Icons.arrow_back_ios,
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
size: 16,
);
return Padding(
padding: const EdgeInsets.only(left: 10.0),
child: SizedBox(
height: 37,
width: 37,
child: ButtonTheme(
minWidth: double.minPositive,
child: TextButton(
// FIX-ME: Style
//highlightColor: Colors.transparent,
//splashColor: Colors.transparent,
//padding: EdgeInsets.all(0),
onPressed: ()=> onClose(context),
child: _backButton),
),
),
);
}
@override
Widget middle(BuildContext context) {
return Text(
viewModel.giftCard.legalName,
style: textMediumSemiBold(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor),
);
}
@override
Widget body(BuildContext context) {
reaction((_) => viewModel.redeemState, (ExecutionState state) {
if (state is FailureState) {
WidgetsBinding.instance.addPostFrameCallback((_) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.of(context).error,
alertContent: state.error,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop());
});
});
}
});
return ScrollableWithBottomSection(
contentPadding: EdgeInsets.all(24),
content: Column(
children: [
if (viewModel.giftCard.barcodeUrl != null && viewModel.giftCard.barcodeUrl.isNotEmpty)
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 24.0,
vertical: 24,
),
child: Image.network(viewModel.giftCard.barcodeUrl),
),
SizedBox(height: 24),
buildIoniaTile(
context,
title: S.of(context).gift_card_number,
subTitle: viewModel.giftCard.cardNumber,
),
if (viewModel.giftCard.cardPin.isNotEmpty) ...[
Divider(height: 30),
buildIoniaTile(
context,
title: S.of(context).pin_number,
subTitle: viewModel.giftCard.cardPin,
)
],
Divider(height: 30),
Observer(
builder: (_) => buildIoniaTile(
context,
title: S.of(context).amount,
subTitle: viewModel.remainingAmount.toStringAsFixed(2),
)),
Divider(height: 50),
TextIconButton(
label: S.of(context).how_to_use_card,
onTap: () => _showHowToUseCard(context, viewModel.giftCard),
),
],
),
bottomSection: Padding(
padding: EdgeInsets.only(bottom: 12),
child: Observer(
builder: (_) {
if (!viewModel.giftCard.isEmpty) {
return Column(
children: [
PrimaryButton(
onPressed: () async {
await Navigator.of(context).pushNamed(
Routes.ioniaMoreOptionsPage,
arguments: [viewModel.giftCard]) as String?;
viewModel.refeshCard();
},
text: S.of(context).more_options,
color: Theme.of(context).cardColor,
textColor: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
SizedBox(height: 12),
LoadingPrimaryButton(
isLoading: viewModel.redeemState is IsExecutingState,
onPressed: () => viewModel.redeem().then(
(_) {
Navigator.of(context).pushNamedAndRemoveUntil(
Routes.ioniaManageCardsPage, (route) => route.isFirst);
},
),
text: S.of(context).mark_as_redeemed,
color: Theme.of(context).primaryColor,
textColor: Colors.white,
),
],
);
}
return Container();
},
),
),
);
}
Widget buildIoniaTile(BuildContext context, {required String title, required String subTitle}) {
return IoniaTile(
title: title,
subTitle: subTitle,
onTap: () {
Clipboard.setData(ClipboardData(text: subTitle));
showBar<void>(context, S.of(context).transaction_details_copied(title));
});
}
void _showHowToUseCard(
BuildContext context,
IoniaGiftCard merchant,
) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return IoniaAlertModal(
title: S.of(context).how_to_use_card,
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: viewModel.giftCard.instructions
.map((instruction) {
return [
Padding(
padding: EdgeInsets.all(10),
child: Text(
instruction.header,
style: textLargeSemiBold(
color: Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor,
),
)),
Text(
instruction.body,
style: textMedium(
color: Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor,
),
)
];
})
.expand((e) => e)
.toList()),
actionTitle: S.of(context).got_it,
);
});
}
}

View file

@ -1,91 +0,0 @@
import 'package:cake_wallet/ionia/ionia_gift_card.dart';
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart';
import 'package:cake_wallet/typography.dart';
import 'package:flutter/material.dart';
class IoniaMoreOptionsPage extends BasePage {
IoniaMoreOptionsPage(this.giftCard);
final IoniaGiftCard giftCard;
@override
Widget middle(BuildContext context) {
return Text(
S.current.more_options,
style: textMediumSemiBold(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
);
}
@override
Widget body(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SizedBox(
height: 10,
),
Center(
child: Text(
S.of(context).choose_from_available_options,
style: textMedium(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
),
),
SizedBox(height: 40),
InkWell(
onTap: () async {
final amount = await Navigator.of(context)
.pushNamed(Routes.ioniaCustomRedeemPage, arguments: [giftCard]) as String?;
if (amount != null && amount.isNotEmpty) {
Navigator.pop(context);
}
},
child: _GradiantContainer(
content: Padding(
padding: const EdgeInsets.only(top: 24, left: 20, right: 24, bottom: 50),
child: Text(
S.of(context).custom_redeem_amount,
style: textXLargeSemiBold(),
),
),
),
)
],
),
);
}
}
class _GradiantContainer extends StatelessWidget {
const _GradiantContainer({Key? key, required this.content}) : super(key: key);
final Widget content;
@override
Widget build(BuildContext context) {
return Container(
child: content,
padding: EdgeInsets.all(24),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
gradient: LinearGradient(
colors: [
Theme.of(context).extension<DashboardPageTheme>()!.secondGradientBackgroundColor,
Theme.of(context).extension<DashboardPageTheme>()!.firstGradientBackgroundColor,
],
begin: Alignment.topRight,
end: Alignment.bottomLeft,
),
),
);
}
}

View file

@ -1,222 +0,0 @@
import 'package:cake_wallet/ionia/ionia_gift_card.dart';
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.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/typography.dart';
import 'package:cake_wallet/utils/show_bar.dart';
import 'package:cake_wallet/view_model/ionia/ionia_payment_status_view_model.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/themes/extensions/transaction_trade_theme.dart';
class IoniaPaymentStatusPage extends BasePage {
IoniaPaymentStatusPage(this.viewModel);
final IoniaPaymentStatusViewModel viewModel;
@override
Widget middle(BuildContext context) {
return Text(
S.of(context).generating_gift_card,
textAlign: TextAlign.center,
style: textMediumSemiBold(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor));
}
@override
Widget body(BuildContext context) {
return _IoniaPaymentStatusPageBody(viewModel);
}
}
class _IoniaPaymentStatusPageBody extends StatefulWidget {
_IoniaPaymentStatusPageBody(this.viewModel);
final IoniaPaymentStatusViewModel viewModel;
@override
_IoniaPaymentStatusPageBodyBodyState createState() => _IoniaPaymentStatusPageBodyBodyState();
}
class _IoniaPaymentStatusPageBodyBodyState extends State<_IoniaPaymentStatusPageBody> {
ReactionDisposer? _onGiftCardReaction;
@override
void initState() {
if (widget.viewModel.giftCard != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.of(context)
.pushReplacementNamed(Routes.ioniaGiftCardDetailPage, arguments: [widget.viewModel.giftCard]);
});
}
_onGiftCardReaction = reaction((_) => widget.viewModel.giftCard, (IoniaGiftCard? giftCard) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.of(context)
.pushReplacementNamed(Routes.ioniaGiftCardDetailPage, arguments: [giftCard]);
});
});
super.initState();
}
@override
void dispose() {
_onGiftCardReaction?.reaction.dispose();
widget.viewModel.timer?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ScrollableWithBottomSection(
contentPadding: EdgeInsets.all(24),
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Row(children: [
Padding(
padding: EdgeInsets.only(right: 10),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.green),
height: 10,
width: 10)),
Text(
S.of(context).awaiting_payment_confirmation,
style: textLargeSemiBold(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor))
]),
SizedBox(height: 40),
Row(children: [
SizedBox(width: 20),
Expanded(child:
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
...widget.viewModel
.committedInfo
.transactions
.map((transaction) => buildDescriptionTileWithCopy(context, S.of(context).transaction_details_transaction_id, transaction.id)),
if (widget.viewModel.paymentInfo.ioniaOrder.id != null)
...[Divider(height: 30),
buildDescriptionTileWithCopy(context, S.of(context).order_id, widget.viewModel.paymentInfo.ioniaOrder.id)],
if (widget.viewModel.paymentInfo.ioniaOrder.paymentId != null)
...[Divider(height: 30),
buildDescriptionTileWithCopy(context, S.of(context).payment_id, widget.viewModel.paymentInfo.ioniaOrder.paymentId)],
]))
]),
SizedBox(height: 40),
Observer(builder: (_) {
if (widget.viewModel.giftCard != null) {
return Container(
padding: EdgeInsets.only(top: 40),
child: Row(children: [
Padding(
padding: EdgeInsets.only(right: 10,),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.green),
height: 10,
width: 10)),
Text(
S.of(context).gift_card_is_generated,
style: textLargeSemiBold(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor))
]));
}
return Row(children: [
Padding(
padding: EdgeInsets.only(right: 10),
child: Observer(builder: (_) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: widget.viewModel.giftCard == null ? Colors.grey : Colors.green),
height: 10,
width: 10);
})),
Text(
S.of(context).generating_gift_card,
style: textLargeSemiBold(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor))]);
}),
],
),
bottomSection: Padding(
padding: EdgeInsets.only(bottom: 12),
child: Column(children: [
Container(
padding: EdgeInsets.only(left: 40, right: 40, bottom: 20),
child: Text(
widget.viewModel.payingByBitcoin ? S.of(context).bitcoin_payments_require_1_confirmation
: S.of(context).proceed_after_one_minute,
style: textMedium(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
).copyWith(fontWeight: FontWeight.w500),
textAlign: TextAlign.center,
)),
Observer(builder: (_) {
if (widget.viewModel.giftCard != null) {
return PrimaryButton(
onPressed: () => Navigator.of(context)
.pushReplacementNamed(
Routes.ioniaGiftCardDetailPage,
arguments: [widget.viewModel.giftCard]),
text: S.of(context).open_gift_card,
color: Theme.of(context).primaryColor,
textColor: Colors.white);
}
return PrimaryButton(
onPressed: () => Navigator.of(context).pushNamed(Routes.support),
text: S.of(context).contact_support,
color: Theme.of(context).cardColor,
textColor: Theme.of(context).extension<CakeTextTheme>()!.titleColor);
})
])
),
);
}
Widget buildDescriptionTile(BuildContext context, String title, String subtitle, VoidCallback onTap) {
return GestureDetector(
onTap: () => onTap(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: textXSmall(
color: Theme.of(context).extension<TransactionTradeTheme>()!.detailsTitlesColor,
),
),
SizedBox(height: 8),
Text(
subtitle,
style: textMedium(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
),
],
));
}
Widget buildDescriptionTileWithCopy(BuildContext context, String title, String subtitle) {
return buildDescriptionTile(context, title, subtitle, () {
Clipboard.setData(ClipboardData(text: subtitle));
showBar<void>(context,
S.of(context).transaction_details_copied(title));
});
}
}

View file

@ -1,9 +0,0 @@
export 'auth/ionia_welcome_page.dart';
export 'auth/ionia_create_account_page.dart';
export 'auth/ionia_login_page.dart';
export 'auth/ionia_verify_otp_page.dart';
export 'cards/ionia_activate_debit_card_page.dart';
export 'cards/ionia_buy_card_detail_page.dart';
export 'cards/ionia_manage_cards_page.dart';
export 'cards/ionia_debit_card_page.dart';
export 'cards/ionia_buy_gift_card.dart';

View file

@ -1,144 +0,0 @@
import 'package:cake_wallet/src/widgets/discount_badge.dart';
import 'package:flutter/material.dart';
class CardItem extends StatelessWidget {
CardItem({
required this.title,
required this.subTitle,
required this.backgroundColor,
required this.titleColor,
required this.subtitleColor,
this.hideBorder = false,
this.discount = 0.0,
this.isAmount = false,
this.discountBackground,
this.onTap,
this.logoUrl,
});
final VoidCallback? onTap;
final String title;
final String subTitle;
final String? logoUrl;
final double discount;
final bool isAmount;
final bool hideBorder;
final Color backgroundColor;
final Color titleColor;
final Color subtitleColor;
final AssetImage? discountBackground;
@override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
child: Stack(
children: [
Container(
padding: EdgeInsets.all(12),
width: double.infinity,
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: BorderRadius.circular(20),
border: hideBorder ? Border.symmetric(horizontal: BorderSide.none, vertical: BorderSide.none) : Border.all(
color: Colors.white.withOpacity(0.20),
),
),
child: Row(
children: [
if (logoUrl != null) ...[
ClipOval(
child: Image.network(
logoUrl!,
width: 40.0,
height: 40.0,
fit: BoxFit.cover,
loadingBuilder: (BuildContext _, Widget child, ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) {
return child;
} else {
return _PlaceholderContainer(text: 'Logo');
}
},
errorBuilder: (_, __, ___) => _PlaceholderContainer(text: '!'),
),
),
SizedBox(width: 5),
],
Column(
crossAxisAlignment: (subTitle?.isEmpty ?? false)
? CrossAxisAlignment.center
: CrossAxisAlignment.start,
children: [
SizedBox(
width: 200,
child: Text(
title,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: titleColor,
fontSize: 20,
fontWeight: FontWeight.w900,
),
),
),
if (subTitle?.isNotEmpty ?? false)
Padding(
padding: EdgeInsets.only(top: 5),
child: Text(
subTitle,
style: TextStyle(
color: subtitleColor,
fontWeight: FontWeight.w500,
fontFamily: 'Lato')),
)
],
),
],
),
),
if (discount != 0.0)
Align(
alignment: Alignment.topRight,
child: Padding(
padding: const EdgeInsets.only(top: 20.0),
child: DiscountBadge(
percentage: discount,
isAmount: isAmount,
discountBackground: discountBackground,
),
),
),
],
),
);
}
}
class _PlaceholderContainer extends StatelessWidget {
const _PlaceholderContainer({required this.text});
final String text;
@override
Widget build(BuildContext context) {
return Container(
height: 42,
width: 42,
child: Center(
child: Text(
text,
style: TextStyle(
color: Colors.black,
fontSize: 12,
fontWeight: FontWeight.w900,
),
),
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(100),
),
);
}
}

View file

@ -1,132 +0,0 @@
import 'package:cake_wallet/src/screens/ionia/widgets/rounded_checkbox.dart';
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/src/widgets/alert_background.dart';
import 'package:cake_wallet/typography.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/view_model/ionia/ionia_gift_cards_list_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/themes/extensions/menu_theme.dart';
class IoniaFilterModal extends StatelessWidget {
IoniaFilterModal({required this.ioniaGiftCardsListViewModel}){
ioniaGiftCardsListViewModel.resetIoniaCategories();
}
final IoniaGiftCardsListViewModel ioniaGiftCardsListViewModel;
@override
Widget build(BuildContext context) {
final searchIcon = Padding(
padding: EdgeInsets.all(10),
child: Image.asset(
'assets/images/mini_search_icon.png',
color: Theme.of(context).primaryColor,
),
);
return Scaffold(
resizeToAvoidBottomInset: false,
body: AlertBackground(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(height: 10),
Container(
padding: EdgeInsets.only(top: 24, bottom: 20),
margin: EdgeInsets.all(24),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background,
borderRadius: BorderRadius.circular(30),
),
child: Column(
children: [
SizedBox(
height: 40,
child: Padding(
padding: const EdgeInsets.only(left: 24, right: 24),
child: TextField(
onChanged: ioniaGiftCardsListViewModel.onSearchFilter,
style: textMedium(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
decoration: InputDecoration(
filled: true,
prefixIcon: searchIcon,
hintText: S.of(context).search_category,
contentPadding: EdgeInsets.only(bottom: 5),
fillColor: Theme.of(context).extension<CakeMenuTheme>()!.dividerColor.withOpacity(0.5),
border: OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular(8),
),
),
),
),
),
SizedBox(height: 10),
Divider(thickness: 2),
SizedBox(height: 24),
Observer(builder: (_) {
return ListView.builder(
padding: EdgeInsets.zero,
shrinkWrap: true,
itemCount: ioniaGiftCardsListViewModel.ioniaCategories.length,
itemBuilder: (_, index) {
final category = ioniaGiftCardsListViewModel.ioniaCategories[index];
return Padding(
padding: const EdgeInsets.only(left: 24, right: 24, bottom: 24),
child: InkWell(
onTap: () => ioniaGiftCardsListViewModel.setSelectedFilter(category),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: [
Image.asset(
category.iconPath,
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
SizedBox(width: 10),
Text(category.title,
style: textSmall(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
).copyWith(fontWeight: FontWeight.w500)),
],
),
Observer(builder: (_) {
final value = ioniaGiftCardsListViewModel.selectedIndices;
return RoundedCheckbox(
value: value.contains(category),
);
}),
],
),
),
);
},
);
}),
],
),
),
InkWell(
onTap: () => Navigator.pop(context),
child: Container(
margin: EdgeInsets.only(bottom: 40),
child: CircleAvatar(
child: Icon(
Icons.close,
color: Palette.darkBlueCraiola,
),
backgroundColor: Colors.white,
),
),
)
],
),
),
);
}
}

View file

@ -12,6 +12,7 @@ class ConfirmSendingAlert extends BaseAlertDialog {
{required this.alertTitle,
this.paymentId,
this.paymentIdValue,
this.expirationTime,
required this.amount,
required this.amountValue,
required this.fiatAmountValue,
@ -28,11 +29,13 @@ class ConfirmSendingAlert extends BaseAlertDialog {
this.alertLeftActionButtonTextColor,
this.alertRightActionButtonTextColor,
this.alertLeftActionButtonColor,
this.alertRightActionButtonColor});
this.alertRightActionButtonColor,
this.onDispose});
final String alertTitle;
final String? paymentId;
final String? paymentIdValue;
final String? expirationTime;
final String amount;
final String amountValue;
final String fiatAmountValue;
@ -50,6 +53,7 @@ class ConfirmSendingAlert extends BaseAlertDialog {
final Color? alertRightActionButtonTextColor;
final Color? alertLeftActionButtonColor;
final Color? alertRightActionButtonColor;
final Function? onDispose;
@override
String get titleText => alertTitle;
@ -88,6 +92,7 @@ class ConfirmSendingAlert extends BaseAlertDialog {
Widget content(BuildContext context) => ConfirmSendingAlertContent(
paymentId: paymentId,
paymentIdValue: paymentIdValue,
expirationTime: expirationTime,
amount: amount,
amountValue: amountValue,
fiatAmountValue: fiatAmountValue,
@ -95,13 +100,15 @@ class ConfirmSendingAlert extends BaseAlertDialog {
feeRate: feeRate,
feeValue: feeValue,
feeFiatAmount: feeFiatAmount,
outputs: outputs);
outputs: outputs,
onDispose: onDispose);
}
class ConfirmSendingAlertContent extends StatefulWidget {
ConfirmSendingAlertContent(
{this.paymentId,
this.paymentIdValue,
this.expirationTime,
required this.amount,
required this.amountValue,
required this.fiatAmountValue,
@ -109,10 +116,12 @@ class ConfirmSendingAlertContent extends StatefulWidget {
this.feeRate,
required this.feeValue,
required this.feeFiatAmount,
required this.outputs});
required this.outputs,
required this.onDispose}) {}
final String? paymentId;
final String? paymentIdValue;
final String? expirationTime;
final String amount;
final String amountValue;
final String fiatAmountValue;
@ -121,11 +130,13 @@ class ConfirmSendingAlertContent extends StatefulWidget {
final String feeValue;
final String feeFiatAmount;
final List<Output> outputs;
final Function? onDispose;
@override
ConfirmSendingAlertContentState createState() => ConfirmSendingAlertContentState(
paymentId: paymentId,
paymentIdValue: paymentIdValue,
expirationTime: expirationTime,
amount: amount,
amountValue: amountValue,
fiatAmountValue: fiatAmountValue,
@ -133,13 +144,15 @@ class ConfirmSendingAlertContent extends StatefulWidget {
feeRate: feeRate,
feeValue: feeValue,
feeFiatAmount: feeFiatAmount,
outputs: outputs);
outputs: outputs,
onDispose: onDispose);
}
class ConfirmSendingAlertContentState extends State<ConfirmSendingAlertContent> {
ConfirmSendingAlertContentState(
{this.paymentId,
this.paymentIdValue,
this.expirationTime,
required this.amount,
required this.amountValue,
required this.fiatAmountValue,
@ -147,7 +160,8 @@ class ConfirmSendingAlertContentState extends State<ConfirmSendingAlertContent>
this.feeRate,
required this.feeValue,
required this.feeFiatAmount,
required this.outputs})
required this.outputs,
this.onDispose})
: recipientTitle = '' {
recipientTitle = outputs.length > 1
? S.current.transaction_details_recipient_address
@ -156,6 +170,7 @@ class ConfirmSendingAlertContentState extends State<ConfirmSendingAlertContent>
final String? paymentId;
final String? paymentIdValue;
final String? expirationTime;
final String amount;
final String amountValue;
final String fiatAmountValue;
@ -164,6 +179,7 @@ class ConfirmSendingAlertContentState extends State<ConfirmSendingAlertContent>
final String feeValue;
final String feeFiatAmount;
final List<Output> outputs;
final Function? onDispose;
final double backgroundHeight = 160;
final double thumbHeight = 72;
@ -172,6 +188,12 @@ class ConfirmSendingAlertContentState extends State<ConfirmSendingAlertContent>
String recipientTitle;
bool showScrollbar = false;
@override
void dispose() {
if (onDispose != null) onDispose!();
super.dispose();
}
@override
Widget build(BuildContext context) {
controller.addListener(() {
@ -217,14 +239,18 @@ class ConfirmSendingAlertContentState extends State<ConfirmSendingAlertContent>
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
paymentIdValue!,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
fontFamily: 'Lato',
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
decoration: TextDecoration.none,
Container(
width: 160,
child: Text(
paymentIdValue!,
textAlign: TextAlign.right,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
fontFamily: 'Lato',
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
decoration: TextDecoration.none,
),
),
),
],
@ -232,6 +258,8 @@ class ConfirmSendingAlertContentState extends State<ConfirmSendingAlertContent>
],
),
),
if (widget.expirationTime != null)
ExpirationTimeWidget(expirationTime: widget.expirationTime!),
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -468,3 +496,46 @@ class ConfirmSendingAlertContentState extends State<ConfirmSendingAlertContent>
]);
}
}
class ExpirationTimeWidget extends StatelessWidget {
const ExpirationTimeWidget({
required this.expirationTime,
});
final String expirationTime;
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(bottom: 32),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
S.current.offer_expires_in,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
fontFamily: 'Lato',
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
decoration: TextDecoration.none,
),
),
Text(
expirationTime,
textAlign: TextAlign.right,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
fontFamily: 'Lato',
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
decoration: TextDecoration.none,
),
)
],
),
);
}
}

View file

@ -0,0 +1,145 @@
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/typography.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class NumberTextField extends StatefulWidget {
final TextEditingController? controller;
final FocusNode? focusNode;
final int min;
final int max;
final int step;
final double arrowsWidth;
final double arrowsHeight;
final EdgeInsets contentPadding;
final double borderWidth;
final ValueChanged<int?>? onChanged;
const NumberTextField({
Key? key,
this.controller,
this.focusNode,
this.min = 0,
this.max = 999,
this.step = 1,
this.arrowsWidth = 24,
this.arrowsHeight = kMinInteractiveDimension,
this.contentPadding = const EdgeInsets.symmetric(horizontal: 8),
this.borderWidth = 2,
this.onChanged,
}) : super(key: key);
@override
State<StatefulWidget> createState() => _NumberTextFieldState();
}
class _NumberTextFieldState extends State<NumberTextField> {
late TextEditingController _controller;
late FocusNode _focusNode;
bool _canGoUp = false;
bool _canGoDown = false;
@override
void initState() {
super.initState();
_controller = widget.controller ?? TextEditingController();
_focusNode = widget.focusNode ?? FocusNode();
_updateArrows(int.tryParse(_controller.text));
}
@override
void didUpdateWidget(covariant NumberTextField oldWidget) {
super.didUpdateWidget(oldWidget);
_controller = widget.controller ?? _controller;
_focusNode = widget.focusNode ?? _focusNode;
_updateArrows(int.tryParse(_controller.text));
}
@override
Widget build(BuildContext context) => TextField(
style: textMediumSemiBold(color: Theme.of(context).extension<CakeTextTheme>()!.titleColor),
enableInteractiveSelection: false,
textAlign: TextAlign.center,
textAlignVertical: TextAlignVertical.bottom,
controller: _controller,
focusNode: _focusNode,
textInputAction: TextInputAction.done,
keyboardType: TextInputType.number,
maxLength: widget.max.toString().length + (widget.min.isNegative ? 1 : 0),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.all(0),
fillColor: Colors.transparent,
counterText: '',
isDense: true,
filled: true,
suffixIconConstraints: BoxConstraints(
maxHeight: widget.arrowsHeight,
maxWidth: widget.arrowsWidth + widget.contentPadding.right),
prefixIconConstraints: BoxConstraints(
maxHeight: widget.arrowsHeight,
maxWidth: widget.arrowsWidth + widget.contentPadding.left),
prefixIcon: Material(
type: MaterialType.transparency,
child: InkWell(
child: Container(
width: widget.arrowsWidth,
alignment: Alignment.bottomCenter,
child: Icon(Icons.arrow_left_outlined, size: widget.arrowsWidth)),
onTap: _canGoDown ? () => _update(false) : null)),
suffixIcon: Material(
type: MaterialType.transparency,
child: InkWell(
child: Container(
width: widget.arrowsWidth,
alignment: Alignment.bottomCenter,
child: Icon(Icons.arrow_right_outlined, size: widget.arrowsWidth)),
onTap: _canGoUp ? () => _update(true) : null))),
maxLines: 1,
onChanged: (value) {
final intValue = int.tryParse(value);
widget.onChanged?.call(intValue);
_updateArrows(intValue);
},
inputFormatters: [_NumberTextInputFormatter(widget.min, widget.max)]);
void _update(bool up) {
var intValue = int.tryParse(_controller.text);
intValue == null ? intValue = widget.min : intValue += up ? widget.step : -widget.step;
intValue = intValue.clamp(widget.min, widget.max); // Ensure intValue is within range
_controller.text = intValue.toString();
// Manually call the onChanged callback after updating the controller's text
widget.onChanged?.call(intValue);
_updateArrows(intValue);
_focusNode.requestFocus();
}
void _updateArrows(int? value) {
final canGoUp = value == null || value < widget.max;
final canGoDown = value == null || value > widget.min;
if (_canGoUp != canGoUp || _canGoDown != canGoDown)
setState(() {
_canGoUp = canGoUp;
_canGoDown = canGoDown;
});
}
}
class _NumberTextInputFormatter extends TextInputFormatter {
final int min;
final int max;
_NumberTextInputFormatter(this.min, this.max);
@override
TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) {
if (const ['-', ''].contains(newValue.text)) return newValue;
final intValue = int.tryParse(newValue.text);
if (intValue == null) return oldValue;
if (intValue < min) return newValue.copyWith(text: min.toString());
if (intValue > max) return newValue.copyWith(text: max.toString());
return newValue.copyWith(text: intValue.toString());
}
}

View file

@ -0,0 +1,20 @@
import 'package:cake_wallet/cake_pay/cake_pay_service.dart';
import 'package:mobx/mobx.dart';
part 'cake_pay_account_view_model.g.dart';
class CakePayAccountViewModel = CakePayAccountViewModelBase with _$CakePayAccountViewModel;
abstract class CakePayAccountViewModelBase with Store {
CakePayAccountViewModelBase({required this.cakePayService}) : email = '' {
cakePayService.getUserEmail().then((email) => this.email = email ?? '');
}
final CakePayService cakePayService;
@observable
String email;
@action
Future<void> logout() async => cakePayService.logout(email);
}

View file

@ -0,0 +1,51 @@
import 'package:cake_wallet/cake_pay/cake_pay_states.dart';
import 'package:cake_wallet/cake_pay/cake_pay_service.dart';
import 'package:mobx/mobx.dart';
part 'cake_pay_auth_view_model.g.dart';
class CakePayAuthViewModel = CakePayAuthViewModelBase with _$CakePayAuthViewModel;
abstract class CakePayAuthViewModelBase with Store {
CakePayAuthViewModelBase({required this.cakePayService})
: userVerificationState = CakePayUserVerificationStateInitial(),
otpState = CakePayOtpSendDisabled(),
email = '',
otp = '';
final CakePayService cakePayService;
@observable
CakePayUserVerificationState userVerificationState;
@observable
CakePayOtpState otpState;
@observable
String email;
@observable
String otp;
@action
Future<void> verifyEmail(String code) async {
try {
otpState = CakePayOtpValidating();
await cakePayService.verifyEmail(code);
otpState = CakePayOtpSuccess();
} catch (_) {
otpState = CakePayOtpFailure(error: 'Invalid OTP. Try again');
}
}
@action
Future<void> logIn(String email) async {
try {
userVerificationState = CakePayUserVerificationStateLoading();
await cakePayService.logIn(email);
userVerificationState = CakePayUserVerificationStateSuccess();
} catch (e) {
userVerificationState = CakePayUserVerificationStateFailure(error: e.toString());
}
}
}

View file

@ -0,0 +1,48 @@
import 'package:cake_wallet/cake_pay/cake_pay_card.dart';
import 'package:cake_wallet/cake_pay/cake_pay_vendor.dart';
import 'package:mobx/mobx.dart';
part 'cake_pay_buy_card_view_model.g.dart';
class CakePayBuyCardViewModel = CakePayBuyCardViewModelBase with _$CakePayBuyCardViewModel;
abstract class CakePayBuyCardViewModelBase with Store {
CakePayBuyCardViewModelBase({required this.vendor})
: amount = vendor.card!.denominations.isNotEmpty
? double.parse(vendor.card!.denominations.first)
: 0,
quantity = 1,
min = double.parse(vendor.card!.minValue ?? '0'),
max = double.parse(vendor.card!.maxValue ?? '0'),
card = vendor.card!;
final CakePayVendor vendor;
final CakePayCard card;
final double min;
final double max;
bool get isDenominationSelected => card.denominations.isNotEmpty;
@observable
double amount;
@observable
int quantity;
@computed
bool get isEnablePurchase =>
(amount >= min && amount <= max) || (isDenominationSelected && quantity > 0);
@computed
double get totalAmount => amount * quantity;
@action
void onQuantityChanged(int? input) => quantity = input ?? 1;
@action
void onAmountChanged(String input) {
if (input.isEmpty) return;
amount = double.parse(input.replaceAll(',', '.'));
}
}

View file

@ -0,0 +1,221 @@
import 'package:cake_wallet/cake_pay/cake_pay_service.dart';
import 'package:cake_wallet/cake_pay/cake_pay_states.dart';
import 'package:cake_wallet/cake_pay/cake_pay_vendor.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/view_model/dashboard/dropdown_filter_item.dart';
import 'package:cake_wallet/view_model/dashboard/filter_item.dart';
import 'package:mobx/mobx.dart';
part 'cake_pay_cards_list_view_model.g.dart';
class CakePayCardsListViewModel = CakePayCardsListViewModelBase with _$CakePayCardsListViewModel;
abstract class CakePayCardsListViewModelBase with Store {
CakePayCardsListViewModelBase({
required this.cakePayService,
}) : cardState = CakePayCardsStateNoCards(),
cakePayVendors = [],
availableCountries = [],
page = 1,
selectedCountry = 'USA',
displayPrepaidCards = true,
displayGiftCards = true,
displayDenominationsCards = true,
displayCustomValueCards = true,
scrollOffsetFromTop = 0.0,
vendorsState = InitialCakePayVendorLoadingState(),
createCardState = CakePayCreateCardState(),
searchString = '',
CakePayVendorList = <CakePayVendor>[] {
initialization();
}
void initialization() async {
await getCountries();
selectedCountry = availableCountries.first;
getVendors();
}
final CakePayService cakePayService;
List<CakePayVendor> CakePayVendorList;
Map<String, List<FilterItem>> get createFilterItems => {
S.current.filter_by: [
FilterItem(
value: () => displayPrepaidCards,
caption: S.current.prepaid_cards,
onChanged: togglePrepaidCards),
FilterItem(
value: () => displayGiftCards,
caption: S.current.gift_cards,
onChanged: toggleGiftCards),
],
S.current.value_type: [
FilterItem(
value: () => displayDenominationsCards,
caption: S.current.denominations,
onChanged: toggleDenominationsCards),
FilterItem(
value: () => displayCustomValueCards,
caption: S.current.custom_value,
onChanged: toggleCustomValueCards),
],
S.current.countries: [
DropdownFilterItem(
items: availableCountries,
caption: '',
selectedItem: selectedCountry,
onItemSelected: (String value) => setSelectedCountry(value),
),
]
};
String searchString;
int page;
late String _initialSelectedCountry;
late bool _initialDisplayPrepaidCards;
late bool _initialDisplayGiftCards;
late bool _initialDisplayDenominationsCards;
late bool _initialDisplayCustomValueCards;
@observable
double scrollOffsetFromTop;
@observable
CakePayCreateCardState createCardState;
@observable
CakePayCardsState cardState;
@observable
CakePayVendorState vendorsState;
@observable
bool hasMoreDataToFetch = true;
@observable
bool isLoadingNextPage = false;
@observable
List<CakePayVendor> cakePayVendors;
@observable
List<String> availableCountries;
@observable
bool displayPrepaidCards;
@observable
bool displayGiftCards;
@observable
bool displayDenominationsCards;
@observable
bool displayCustomValueCards;
@observable
String selectedCountry;
bool get hasFiltersChanged =>
selectedCountry != _initialSelectedCountry ||
displayPrepaidCards != _initialDisplayPrepaidCards ||
displayGiftCards != _initialDisplayGiftCards ||
displayDenominationsCards != _initialDisplayDenominationsCards ||
displayCustomValueCards != _initialDisplayCustomValueCards;
Future<void> getCountries() async {
availableCountries = await cakePayService.getCountries();
}
@action
Future<void> getVendors({
String? text,
int? currentPage,
}) async {
vendorsState = CakePayVendorLoadingState();
searchString = text ?? '';
var newVendors = await cakePayService.getVendors(
country: selectedCountry,
page: currentPage ?? page,
search: searchString,
giftCards: displayGiftCards,
prepaidCards: displayPrepaidCards,
custom: displayCustomValueCards,
onDemand: displayDenominationsCards);
cakePayVendors = CakePayVendorList = newVendors;
vendorsState = CakePayVendorLoadedState();
}
@action
Future<void> fetchNextPage() async {
if (vendorsState is CakePayVendorLoadingState || !hasMoreDataToFetch || isLoadingNextPage)
return;
isLoadingNextPage = true;
page++;
try {
var newVendors = await cakePayService.getVendors(
country: selectedCountry,
page: page,
search: searchString,
giftCards: displayGiftCards,
prepaidCards: displayPrepaidCards,
custom: displayCustomValueCards,
onDemand: displayDenominationsCards);
cakePayVendors.addAll(newVendors);
} catch (error) {
if (error.toString().contains('detail":"Invalid page."')) {
hasMoreDataToFetch = false;
}
} finally {
isLoadingNextPage = false;
}
}
Future<bool> isCakePayUserAuthenticated() async {
return await cakePayService.isLogged();
}
void resetLoadingNextPageState() {
hasMoreDataToFetch = true;
page = 1;
}
void storeInitialFilterStates() {
_initialSelectedCountry = selectedCountry;
_initialDisplayPrepaidCards = displayPrepaidCards;
_initialDisplayGiftCards = displayGiftCards;
_initialDisplayDenominationsCards = displayDenominationsCards;
_initialDisplayCustomValueCards = displayCustomValueCards;
}
@action
void setSelectedCountry(String country) => selectedCountry = country;
@action
void togglePrepaidCards() => displayPrepaidCards = !displayPrepaidCards;
@action
void toggleGiftCards() => displayGiftCards = !displayGiftCards;
@action
void toggleDenominationsCards() => displayDenominationsCards = !displayDenominationsCards;
@action
void toggleCustomValueCards() => displayCustomValueCards = !displayCustomValueCards;
void setScrollOffsetFromTop(double scrollOffset) {
scrollOffsetFromTop = scrollOffset;
}
}

View file

@ -0,0 +1,162 @@
import 'dart:async';
import 'package:cake_wallet/cake_pay/cake_pay_card.dart';
import 'package:cake_wallet/cake_pay/cake_pay_order.dart';
import 'package:cake_wallet/cake_pay/cake_pay_payment_credantials.dart';
import 'package:cake_wallet/cake_pay/cake_pay_service.dart';
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/view_model/send/send_view_model.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:mobx/mobx.dart';
part 'cake_pay_purchase_view_model.g.dart';
class CakePayPurchaseViewModel = CakePayPurchaseViewModelBase with _$CakePayPurchaseViewModel;
abstract class CakePayPurchaseViewModelBase with Store {
CakePayPurchaseViewModelBase({
required this.cakePayService,
required this.paymentCredential,
required this.card,
required this.sendViewModel,
}) : walletType = sendViewModel.walletType;
final WalletType walletType;
final PaymentCredential paymentCredential;
final CakePayCard card;
final SendViewModel sendViewModel;
final CakePayService cakePayService;
CakePayOrder? order;
Timer? _timer;
DateTime? expirationTime;
Duration? remainingTime;
String? get userName => paymentCredential.userName;
double get amount => paymentCredential.amount;
int get quantity => paymentCredential.quantity;
double get totalAmount => paymentCredential.totalAmount;
String get fiatCurrency => paymentCredential.fiatCurrency;
CryptoPaymentData? get cryptoPaymentData {
if (order == null) return null;
if (WalletType.monero == walletType) {
return order!.paymentData.xmr;
}
if (WalletType.bitcoin == walletType) {
final paymentUrls = order!.paymentData.btc.paymentUrls!.bip21;
final uri = Uri.parse(paymentUrls!);
final address = uri.path;
final price = uri.queryParameters['amount'];
return CryptoPaymentData(
address: address,
price: price ?? '0',
);
}
return null;
}
@observable
bool isOrderExpired = false;
@observable
String formattedRemainingTime = '';
@action
Future<void> createOrder() async {
if (walletType != WalletType.bitcoin && walletType != WalletType.monero) {
sendViewModel.state = FailureState('Unsupported wallet type, please use Bitcoin or Monero.');
}
try {
order = await cakePayService.createOrder(
cardId: card.id,
price: paymentCredential.amount.toString(),
quantity: paymentCredential.quantity);
await confirmSending();
expirationTime = order!.paymentData.expirationTime;
updateRemainingTime();
_startExpirationTimer();
} catch (e) {
sendViewModel.state = FailureState(
sendViewModel.translateErrorMessage(e, walletType, sendViewModel.wallet.currency));
}
}
@action
Future<void> confirmSending() async {
final cryptoPaymentData = this.cryptoPaymentData;
try {
if (order == null || cryptoPaymentData == null) return;
sendViewModel.clearOutputs();
final output = sendViewModel.outputs.first;
output.address = cryptoPaymentData.address;
output.setCryptoAmount(cryptoPaymentData.price);
await sendViewModel.createTransaction();
} catch (e) {
throw e;
}
}
@action
void updateRemainingTime() {
if (expirationTime == null) {
formattedRemainingTime = '';
return;
}
remainingTime = expirationTime!.difference(DateTime.now());
isOrderExpired = remainingTime!.isNegative;
if (isOrderExpired) {
disposeExpirationTimer();
sendViewModel.state = FailureState('Order has expired.');
} else {
formattedRemainingTime = formatDuration(remainingTime!);
}
}
void _startExpirationTimer() {
_timer?.cancel();
_timer = Timer.periodic(Duration(seconds: 1), (_) {
updateRemainingTime();
});
}
String formatDuration(Duration duration) {
final hours = duration.inHours;
final minutes = duration.inMinutes.remainder(60);
final seconds = duration.inSeconds.remainder(60);
return '${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
}
void disposeExpirationTimer() {
_timer?.cancel();
remainingTime = null;
formattedRemainingTime = '';
expirationTime = null;
}
void dispose() {
disposeExpirationTimer();
}
}

View file

@ -1,4 +1,4 @@
import 'package:cake_wallet/ionia/ionia_service.dart';
import 'package:cake_wallet/cake_pay/cake_pay_service.dart';
import 'package:mobx/mobx.dart';
part 'cake_features_view_model.g.dart';
@ -6,11 +6,11 @@ part 'cake_features_view_model.g.dart';
class CakeFeaturesViewModel = CakeFeaturesViewModelBase with _$CakeFeaturesViewModel;
abstract class CakeFeaturesViewModelBase with Store {
final IoniaService _ioniaService;
final CakePayService _cakePayService;
CakeFeaturesViewModelBase(this._ioniaService);
CakeFeaturesViewModelBase(this._cakePayService);
Future<bool> isIoniaUserAuthenticated() async {
return await _ioniaService.isLogined();
return await _cakePayService.isLogged();
}
}

View file

@ -0,0 +1,19 @@
import 'package:cake_wallet/view_model/dashboard/filter_item.dart';
class DropdownFilterItem extends FilterItem {
DropdownFilterItem({
required this.items,
required this.caption,
required this.selectedItem,
required this.onItemSelected,
}) : super(
value: () => false,
caption: caption,
onChanged: (_) {},
);
final List<String> items;
final String caption;
final String selectedItem;
final Function(String) onItemSelected;
}

View file

@ -0,0 +1,68 @@
import 'package:auto_size_text/auto_size_text.dart';
import 'package:cake_wallet/themes/extensions/picker_theme.dart';
import 'package:flutter/material.dart';
class DropdownFilterList extends StatefulWidget {
DropdownFilterList({
Key? key,
required this.items,
this.itemPrefix,
this.textStyle,
required this.caption,
required this.selectedItem,
required this.onItemSelected,
}) : super(key: key);
final List<String> items;
final String? itemPrefix;
final TextStyle? textStyle;
final String caption;
final String selectedItem;
final Function(String) onItemSelected;
@override
_DropdownFilterListState createState() => _DropdownFilterListState();
}
class _DropdownFilterListState extends State<DropdownFilterList> {
String? selectedValue;
@override
void initState() {
super.initState();
selectedValue = widget.selectedItem;
}
@override
Widget build(BuildContext context) {
return DropdownButtonHideUnderline(
child: Container(
child: DropdownButton<String>(
isExpanded: true,
icon: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Icon(Icons.arrow_drop_down, color: Theme.of(context).extension<PickerTheme>()!.searchIconColor),
],
),
),
dropdownColor: Theme.of(context).extension<PickerTheme>()!.searchBackgroundFillColor,
borderRadius: BorderRadius.circular(10),
items: widget.items
.map((item) => DropdownMenuItem<String>(
alignment: Alignment.bottomCenter,
value: item,
child: AutoSizeText('${widget.itemPrefix ?? ''} $item', style: widget.textStyle),
))
.toList(),
value: selectedValue,
onChanged: (newValue) {
setState(() => selectedValue = newValue);
widget.onItemSelected(newValue!);
},
),
),
);
}
}

View file

@ -1,50 +0,0 @@
import 'package:cake_wallet/ionia/ionia_create_state.dart';
import 'package:cake_wallet/ionia/ionia_service.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/ionia/ionia_gift_card.dart';
part 'ionia_account_view_model.g.dart';
class IoniaAccountViewModel = IoniaAccountViewModelBase with _$IoniaAccountViewModel;
abstract class IoniaAccountViewModelBase with Store {
IoniaAccountViewModelBase({required this.ioniaService})
: email = '',
giftCards = [],
merchantState = InitialIoniaMerchantLoadingState() {
ioniaService.getUserEmail().then((email) => this.email = email);
updateUserGiftCards();
}
final IoniaService ioniaService;
@observable
String email;
@observable
List<IoniaGiftCard> giftCards;
@observable
IoniaMerchantState merchantState;
@computed
int get countOfMerch => giftCards.where((giftCard) => !giftCard.isEmpty).length;
@computed
List<IoniaGiftCard> get activeMechs => giftCards.where((giftCard) => !giftCard.isEmpty).toList();
@computed
List<IoniaGiftCard> get redeemedMerchs => giftCards.where((giftCard) => giftCard.isEmpty).toList();
@action
void logout() {
ioniaService.logout();
}
@action
Future<void> updateUserGiftCards() async {
merchantState = IoniaLoadingMerchantState();
giftCards = await ioniaService.getCurrentUserGiftCardSummaries();
merchantState = IoniaLoadedMerchantState();
}
}

View file

@ -1,69 +0,0 @@
import 'package:cake_wallet/ionia/ionia_create_state.dart';
import 'package:cake_wallet/ionia/ionia_service.dart';
import 'package:mobx/mobx.dart';
part 'ionia_auth_view_model.g.dart';
class IoniaAuthViewModel = IoniaAuthViewModelBase with _$IoniaAuthViewModel;
abstract class IoniaAuthViewModelBase with Store {
IoniaAuthViewModelBase({required this.ioniaService}):
createUserState = IoniaInitialCreateState(),
signInState = IoniaInitialCreateState(),
otpState = IoniaOtpSendDisabled(),
email = '',
otp = '';
final IoniaService ioniaService;
@observable
IoniaCreateAccountState createUserState;
@observable
IoniaCreateAccountState signInState;
@observable
IoniaOtpState otpState;
@observable
String email;
@observable
String otp;
@action
Future<void> verifyEmail(String code) async {
try {
otpState = IoniaOtpValidating();
await ioniaService.verifyEmail(code);
otpState = IoniaOtpSuccess();
} catch (_) {
otpState = IoniaOtpFailure(error: 'Invalid OTP. Try again');
}
}
@action
Future<void> createUser(String email) async {
try {
createUserState = IoniaCreateStateLoading();
await ioniaService.createUser(email);
createUserState = IoniaCreateStateSuccess();
} catch (e) {
createUserState = IoniaCreateStateFailure(error: e.toString());
}
}
@action
Future<void> signIn(String email) async {
try {
signInState = IoniaCreateStateLoading();
await ioniaService.signIn(email);
signInState = IoniaCreateStateSuccess();
} catch (e) {
signInState = IoniaCreateStateFailure(error: e.toString());
}
}
}

View file

@ -1,30 +0,0 @@
import 'package:cake_wallet/ionia/ionia_merchant.dart';
import 'package:mobx/mobx.dart';
part 'ionia_buy_card_view_model.g.dart';
class IoniaBuyCardViewModel = IoniaBuyCardViewModelBase with _$IoniaBuyCardViewModel;
abstract class IoniaBuyCardViewModelBase with Store {
IoniaBuyCardViewModelBase({required this.ioniaMerchant})
: isEnablePurchase = false,
amount = 0;
final IoniaMerchant ioniaMerchant;
@observable
double amount;
@observable
bool isEnablePurchase;
@action
void onAmountChanged(String input) {
if (input.isEmpty) return;
amount = double.parse(input.replaceAll(',', '.'));
final min = ioniaMerchant.minimumCardPurchase;
final max = ioniaMerchant.maximumCardPurchase;
isEnablePurchase = amount >= min && amount <= max;
}
}

View file

@ -1,51 +0,0 @@
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/ionia/ionia_gift_card.dart';
import 'package:cake_wallet/ionia/ionia_service.dart';
import 'package:mobx/mobx.dart';
part 'ionia_custom_redeem_view_model.g.dart';
class IoniaCustomRedeemViewModel = IoniaCustomRedeemViewModelBase with _$IoniaCustomRedeemViewModel;
abstract class IoniaCustomRedeemViewModelBase with Store {
IoniaCustomRedeemViewModelBase({
required this.giftCard,
required this.ioniaService,
}) : amount = 0,
redeemState = InitialExecutionState();
final IoniaGiftCard giftCard;
final IoniaService ioniaService;
@observable
ExecutionState redeemState;
@observable
double amount;
@computed
double get remaining =>
amount <= giftCard.remainingAmount ? giftCard.remainingAmount - amount : 0;
@computed
String get formattedRemaining => remaining.toStringAsFixed(2);
@computed
bool get disableRedeem => amount > giftCard.remainingAmount;
@action
void updateAmount(String text) {
amount = double.tryParse(text.replaceAll(',', '.')) ?? 0;
}
@action
Future<void> addCustomRedeem() async {
try {
redeemState = IsExecutingState();
await ioniaService.redeem(giftCardId: giftCard.id, amount: amount);
redeemState = ExecutedSuccessfullyState();
} catch (e) {
redeemState = FailureState(e.toString());
}
}
}

View file

@ -1,34 +0,0 @@
import 'package:cake_wallet/ionia/ionia_merchant.dart';
import 'package:cake_wallet/ionia/ionia_tip.dart';
import 'package:mobx/mobx.dart';
part 'ionia_custom_tip_view_model.g.dart';
class IoniaCustomTipViewModel = IoniaCustomTipViewModelBase with _$IoniaCustomTipViewModel;
abstract class IoniaCustomTipViewModelBase with Store {
IoniaCustomTipViewModelBase({
required this.amount,
required this.tip,
required this.ioniaMerchant})
: customTip = tip,
percentage = 0;
final IoniaMerchant ioniaMerchant;
final double amount;
final IoniaTip tip;
@observable
IoniaTip customTip;
@observable
double percentage;
@action
void onTipChanged(String value){
final _amount = value.isEmpty ? 0 : double.parse(value.replaceAll(',', '.'));
percentage = _amount/amount * 100;
customTip = IoniaTip(percentage: percentage, originalAmount: amount);
}
}

View file

@ -1,54 +0,0 @@
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/ionia/ionia_service.dart';
import 'package:cake_wallet/ionia/ionia_gift_card.dart';
import 'package:mobx/mobx.dart';
import 'package:device_display_brightness/device_display_brightness.dart';
part 'ionia_gift_card_details_view_model.g.dart';
class IoniaGiftCardDetailsViewModel = IoniaGiftCardDetailsViewModelBase
with _$IoniaGiftCardDetailsViewModel;
abstract class IoniaGiftCardDetailsViewModelBase with Store {
IoniaGiftCardDetailsViewModelBase({required this.ioniaService, required this.giftCard})
: redeemState = InitialExecutionState(),
remainingAmount = giftCard.remainingAmount,
brightness = 0;
final IoniaService ioniaService;
double brightness;
@observable
IoniaGiftCard giftCard;
@observable
double remainingAmount;
@observable
ExecutionState redeemState;
@action
Future<void> redeem() async {
giftCard.remainingAmount = remainingAmount;
try {
redeemState = IsExecutingState();
await ioniaService.redeem(giftCardId: giftCard.id, amount: giftCard.remainingAmount);
giftCard = await ioniaService.getGiftCard(id: giftCard.id);
redeemState = ExecutedSuccessfullyState();
} catch (e) {
redeemState = FailureState(e.toString());
}
}
@action
Future<void> refeshCard() async {
giftCard = await ioniaService.getGiftCard(id: giftCard.id);
remainingAmount = giftCard.remainingAmount;
}
void increaseBrightness() async {
brightness = await DeviceDisplayBrightness.getBrightness();
await DeviceDisplayBrightness.setBrightness(1.0);
}
}

View file

@ -1,139 +0,0 @@
import 'package:cake_wallet/ionia/ionia_category.dart';
import 'package:cake_wallet/ionia/ionia_service.dart';
import 'package:cake_wallet/ionia/ionia_create_state.dart';
import 'package:cake_wallet/ionia/ionia_merchant.dart';
import 'package:mobx/mobx.dart';
part 'ionia_gift_cards_list_view_model.g.dart';
class IoniaGiftCardsListViewModel = IoniaGiftCardsListViewModelBase with _$IoniaGiftCardsListViewModel;
abstract class IoniaGiftCardsListViewModelBase with Store {
IoniaGiftCardsListViewModelBase({
required this.ioniaService,
}) :
cardState = IoniaNoCardState(),
ioniaMerchants = [],
ioniaCategories = IoniaCategory.allCategories,
selectedIndices = ObservableList<IoniaCategory>.of([IoniaCategory.all]),
scrollOffsetFromTop = 0.0,
merchantState = InitialIoniaMerchantLoadingState(),
createCardState = IoniaCreateCardState(),
searchString = '',
ioniaMerchantList = <IoniaMerchant>[] {
}
final IoniaService ioniaService;
List<IoniaMerchant> ioniaMerchantList;
String searchString;
@observable
double scrollOffsetFromTop;
@observable
IoniaCreateCardState createCardState;
@observable
IoniaFetchCardState cardState;
@observable
IoniaMerchantState merchantState;
@observable
List<IoniaMerchant> ioniaMerchants;
@observable
List<IoniaCategory> ioniaCategories;
@observable
ObservableList<IoniaCategory> selectedIndices;
@action
Future<void> createCard() async {
try {
createCardState = IoniaCreateCardLoading();
await ioniaService.createCard();
createCardState = IoniaCreateCardSuccess();
} catch (e) {
createCardState = IoniaCreateCardFailure(error: e.toString());
}
}
@action
void searchMerchant(String text) {
if (text.isEmpty) {
ioniaMerchants = ioniaMerchantList;
return;
}
searchString = text;
ioniaService.getMerchantsByFilter(search: searchString).then((value) {
ioniaMerchants = value;
});
}
Future<void> _getCard() async {
cardState = IoniaFetchingCard();
try {
final card = await ioniaService.getCard();
cardState = IoniaCardSuccess(card: card);
} catch (_) {
cardState = IoniaFetchCardFailure();
}
}
void getMerchants() {
merchantState = IoniaLoadingMerchantState();
ioniaService.getMerchantsByFilter(categories: selectedIndices).then((value) {
value.sort((a, b) => a.legalName.toLowerCase().compareTo(b.legalName.toLowerCase()));
ioniaMerchants = ioniaMerchantList = value;
merchantState = IoniaLoadedMerchantState();
});
}
@action
void setSelectedFilter(IoniaCategory category) {
if (category == IoniaCategory.all) {
selectedIndices.clear();
selectedIndices.add(category);
return;
}
if (category != IoniaCategory.all) {
selectedIndices.remove(IoniaCategory.all);
}
if (selectedIndices.contains(category)) {
selectedIndices.remove(category);
if (selectedIndices.isEmpty) {
selectedIndices.add(IoniaCategory.all);
}
return;
}
selectedIndices.add(category);
}
@action
void onSearchFilter(String text) {
if (text.isEmpty) {
ioniaCategories = IoniaCategory.allCategories;
} else {
ioniaCategories = IoniaCategory.allCategories
.where((e) => e.title.toLowerCase().contains(text.toLowerCase()),)
.toList();
}
}
@action
void resetIoniaCategories() {
ioniaCategories = IoniaCategory.allCategories;
}
void setScrollOffsetFromTop(double scrollOffset) {
scrollOffsetFromTop = scrollOffset;
}
}

View file

@ -1,62 +0,0 @@
import 'dart:async';
import 'package:cake_wallet/anypay/any_pay_chain.dart';
import 'package:mobx/mobx.dart';
import 'package:flutter/foundation.dart';
import 'package:cake_wallet/ionia/ionia_service.dart';
import 'package:cake_wallet/ionia/ionia_gift_card.dart';
import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart';
import 'package:cake_wallet/ionia/ionia_any_pay_payment_info.dart';
part 'ionia_payment_status_view_model.g.dart';
class IoniaPaymentStatusViewModel = IoniaPaymentStatusViewModelBase with _$IoniaPaymentStatusViewModel;
abstract class IoniaPaymentStatusViewModelBase with Store {
IoniaPaymentStatusViewModelBase(
this.ioniaService, {
required this.paymentInfo,
required this.committedInfo})
: error = '' {
_timer = Timer.periodic(updateTime, (timer) async {
await updatePaymentStatus();
if (giftCard != null) {
timer?.cancel();
}
});
}
static const updateTime = Duration(seconds: 3);
final IoniaService ioniaService;
final IoniaAnyPayPaymentInfo paymentInfo;
final AnyPayPaymentCommittedInfo committedInfo;
@observable
IoniaGiftCard? giftCard;
@observable
String error;
Timer? get timer => _timer;
bool get payingByBitcoin => paymentInfo.anyPayPayment.chain == AnyPayChain.btc;
Timer? _timer;
@action
Future<void> updatePaymentStatus() async {
try {
final giftCardId = await ioniaService.getPaymentStatus(
orderId: paymentInfo.ioniaOrder.id,
paymentId: paymentInfo.ioniaOrder.paymentId);
if (giftCardId != null) {
giftCard = await ioniaService.getGiftCard(id: giftCardId);
}
} catch (e) {
error = e.toString();
}
}
}

View file

@ -1,104 +0,0 @@
import 'package:flutter/foundation.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/anypay/any_pay_payment.dart';
import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart';
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/ionia/ionia_anypay.dart';
import 'package:cake_wallet/ionia/ionia_merchant.dart';
import 'package:cake_wallet/ionia/ionia_tip.dart';
import 'package:cake_wallet/ionia/ionia_any_pay_payment_info.dart';
import 'package:cake_wallet/view_model/send/send_view_model.dart';
part 'ionia_purchase_merch_view_model.g.dart';
class IoniaMerchPurchaseViewModel = IoniaMerchPurchaseViewModelBase with _$IoniaMerchPurchaseViewModel;
abstract class IoniaMerchPurchaseViewModelBase with Store {
IoniaMerchPurchaseViewModelBase({
required this.ioniaAnyPayService,
required this.amount,
required this.ioniaMerchant,
required this.sendViewModel,
}) : tipAmount = 0.0,
percentage = 0.0,
invoiceCreationState = InitialExecutionState(),
invoiceCommittingState = InitialExecutionState(),
tips = <IoniaTip>[
IoniaTip(percentage: 0, originalAmount: amount),
IoniaTip(percentage: 15, originalAmount: amount),
IoniaTip(percentage: 18, originalAmount: amount),
IoniaTip(percentage: 20, originalAmount: amount),
IoniaTip(percentage: 0, originalAmount: amount, isCustom: true),
] {
selectedTip = tips.first;
}
final double amount;
List<IoniaTip> tips;
@observable
IoniaTip? selectedTip;
final IoniaMerchant ioniaMerchant;
final SendViewModel sendViewModel;
final IoniaAnyPay ioniaAnyPayService;
IoniaAnyPayPaymentInfo? paymentInfo;
AnyPayPayment? get invoice => paymentInfo?.anyPayPayment;
AnyPayPaymentCommittedInfo? committedInfo;
@observable
ExecutionState invoiceCreationState;
@observable
ExecutionState invoiceCommittingState;
@observable
double percentage;
@computed
double get giftCardAmount => double.parse((amount + tipAmount).toStringAsFixed(2));
@computed
double get billAmount => double.parse((giftCardAmount * (1 - (ioniaMerchant.discount / 100))).toStringAsFixed(2));
@observable
double tipAmount;
@action
void addTip(IoniaTip tip) {
tipAmount = tip.additionalAmount;
selectedTip = tip;
}
@action
Future<void> createInvoice() async {
try {
invoiceCreationState = IsExecutingState();
paymentInfo = await ioniaAnyPayService.purchase(merchId: ioniaMerchant.id.toString(), amount: giftCardAmount);
invoiceCreationState = ExecutedSuccessfullyState();
} catch (e) {
invoiceCreationState = FailureState(e.toString());
}
}
@action
Future<void> commitPaymentInvoice() async {
try {
if (invoice == null) {
throw Exception('Invoice is created. Invoince is null');
}
invoiceCommittingState = IsExecutingState();
committedInfo = await ioniaAnyPayService.commitInvoice(invoice!);
invoiceCommittingState = ExecutedSuccessfullyState(payload: committedInfo!);
} catch (e) {
invoiceCommittingState = FailureState(e.toString());
}
}
}

View file

@ -87,6 +87,7 @@
"buy": "اشتري",
"buy_alert_content": ".ﺎﻬﻴﻟﺇ ﻞﻳﺪﺒﺘﻟﺍ ﻭﺃ Monero ﻭﺃ Litecoin ﻭﺃ Ethereum ﻭﺃ Bitcoin ﺔﻈﻔﺤﻣ ءﺎﺸﻧﺇ ﻰﺟﺮﻳ .",
"buy_bitcoin": "شراء Bitcoin",
"buy_now": "اشتري الآن",
"buy_provider_unavailable": "مزود حاليا غير متوفر.",
"buy_with": "اشتر بواسطة",
"by_cake_pay": "عن طريق Cake Pay",
@ -94,8 +95,7 @@
"cake_dark_theme": "موضوع الكعكة الظلام",
"cake_pay_account_note": "قم بالتسجيل باستخدام عنوان بريد إلكتروني فقط لمشاهدة البطاقات وشرائها. حتى أن بعضها متوفر بسعر مخفض!",
"cake_pay_learn_more": "شراء واسترداد بطاقات الهدايا على الفور في التطبيق!\nاسحب من اليسار إلى اليمين لمعرفة المزيد.",
"cake_pay_subtitle": "شراء بطاقات هدايا مخفضة السعر (الولايات المتحدة فقط)",
"cake_pay_title": "بطاقات هدايا Cake Pay",
"cake_pay_subtitle": "شراء بطاقات مسبقة الدفع وبطاقات الهدايا في جميع أنحاء العالم",
"cake_pay_web_cards_subtitle": "اشتري بطاقات مدفوعة مسبقا وبطاقات هدايا في جميع أنحاء العالم",
"cake_pay_web_cards_title": "بطاقات Cake Pay Web",
"cake_wallet": "Cake Wallet",
@ -123,6 +123,7 @@
"change_wallet_alert_title": "تغيير المحفظة الحالية",
"choose_account": "اختر حساب",
"choose_address": "\n\nالرجاء اختيار عنوان:",
"choose_card_value": "اختر قيمة بطاقة",
"choose_derivation": "اختر اشتقاق المحفظة",
"choose_from_available_options": "اختر من بين الخيارات المتاحة:",
"choose_one": "اختر واحدة",
@ -166,6 +167,7 @@
"copy_address": "نسخ العنوان",
"copy_id": "نسخ معرف العملية",
"copyWalletConnectLink": "ﺎﻨﻫ ﻪﻘﺼﻟﺍﻭ dApp ﻦﻣ WalletConnect ﻂﺑﺍﺭ ﺦﺴﻧﺍ",
"countries": "بلدان",
"create_account": "إنشاء حساب",
"create_backup": "انشئ نسخة احتياطية",
"create_donation_link": "إنشاء رابط التبرع",
@ -178,6 +180,7 @@
"custom": "مخصصة",
"custom_drag": "مخصص (عقد وسحب)",
"custom_redeem_amount": "مبلغ الاسترداد مخصص",
"custom_value": "القيمة الجمركية",
"dark_theme": "داكن",
"debit_card": "بطاقة ائتمان",
"debit_card_terms": "يخضع تخزين واستخدام رقم بطاقة الدفع الخاصة بك (وبيانات الاعتماد المقابلة لرقم بطاقة الدفع الخاصة بك) في هذه المحفظة الرقمية لشروط وأحكام اتفاقية حامل البطاقة المعمول بها مع جهة إصدار بطاقة الدفع ، كما هو معمول به من وقت لآخر.",
@ -190,6 +193,7 @@
"delete_wallet": "حذف المحفظة",
"delete_wallet_confirm_message": "هل أنت متأكد أنك تريد حذف محفظة ${wallet_name}؟",
"deleteConnectionConfirmationPrompt": "ـﺑ ﻝﺎﺼﺗﻻﺍ ﻑﺬﺣ ﺪﻳﺮﺗ ﻚﻧﺃ ﺪﻛﺄﺘﻣ ﺖﻧﺃ ﻞﻫ",
"denominations": "الطوائف",
"descending": "النزول",
"description": "ﻒﺻﻭ",
"destination_tag": "علامة الوجهة:",
@ -277,6 +281,7 @@
"expired": "منتهي الصلاحية",
"expires": "تنتهي",
"expiresOn": "ﻲﻓ ﻪﺘﻴﺣﻼﺻ ﻲﻬﺘﻨﺗ",
"expiry_and_validity": "انتهاء الصلاحية والصلاحية",
"export_backup": "تصدير نسخة احتياطية",
"extra_id": "معرف إضافي:",
"extracted_address_content": "سوف ترسل الأموال إلى\n${recipient_name}",
@ -384,6 +389,7 @@
"new_template": "قالب جديد",
"new_wallet": "إنشاء محفظة جديدة",
"newConnection": "ﺪﻳﺪﺟ ﻝﺎﺼﺗﺍ",
"no_cards_found": "لم يتم العثور على بطاقات",
"no_id_needed": "لا حاجة لID!",
"no_id_required": "لا ID مطلوب. اشحن وانفق في أي مكان",
"no_relay_on_domain": ".ﻡﺍﺪﺨﺘﺳﻼﻟ ﻊﺑﺎﺘﺘﻟﺍ ﺭﺎﻴﺘﺧﺍ ءﺎﺟﺮﻟﺍ .ﺡﺎﺘﻣ ﺮﻴﻏ ﻞﻴﺣﺮﺘﻟﺍ ﻥﺃ ﻭﺃ ﻡﺪﺨﺘﺴﻤﻟﺍ ﻝﺎﺠﻤﻟ ﻞﻴﺣﺮﺗ ﺪ",
@ -452,6 +458,7 @@
"pre_seed_button_text": "انا أفهم. أرني سييد الخاص بي",
"pre_seed_description": "في الصفحة التالية ستشاهد سلسلة من الكلمات ${words}. هذه هي سييد الفريدة والخاصة بك وهي الطريقة الوحيدة لاسترداد محفظتك في حالة فقدها أو عطلها. تقع على عاتقك مسؤولية تدوينها وتخزينها في مكان آمن خارج تطبيق Cake Wallet.",
"pre_seed_title": "مهم",
"prepaid_cards": "البطاقات المدفوعة مسبقا",
"prevent_screenshots": "منع لقطات الشاشة وتسجيل الشاشة",
"privacy": "خصوصية",
"privacy_policy": "سياسة الخصوصية",
@ -467,6 +474,7 @@
"purple_dark_theme": "موضوع الظلام الأرجواني",
"qr_fullscreen": "انقر لفتح ال QR بملء الشاشة",
"qr_payment_amount": "يحتوي هذا ال QR على مبلغ الدفع. هل تريد تغير المبلغ فوق القيمة الحالية؟",
"quantity": "كمية",
"question_to_disable_2fa": "هل أنت متأكد أنك تريد تعطيل Cake 2FA؟ لن تكون هناك حاجة إلى رمز 2FA للوصول إلى المحفظة ووظائف معينة.",
"receivable_balance": "التوازن القادم",
"receive": "استلام",
@ -708,6 +716,7 @@
"tokenID": "ﻒﻳﺮﻌﺗ ﺔﻗﺎﻄﺑ",
"tor_connection": "ﺭﻮﺗ ﻝﺎﺼﺗﺍ",
"tor_only": "Tor فقط",
"total": "المجموع",
"total_saving": "إجمالي المدخرات",
"totp_2fa_failure": "شفرة خاطئة. يرجى تجربة رمز مختلف أو إنشاء مفتاح سري جديد. استخدم تطبيق 2FA متوافقًا يدعم الرموز المكونة من 8 أرقام و SHA512.",
"totp_2fa_success": "نجاح! تم تمكين Cake 2FA لهذه المحفظة. تذكر حفظ بذرة ذاكري في حالة فقد الوصول إلى المحفظة.",
@ -798,6 +807,8 @@
"use_ssl": "استخدم SSL",
"use_suggested": "استخدام المقترح",
"use_testnet": "استخدم testnet",
"value": "قيمة",
"value_type": "نوع القيمة",
"variable_pair_not_supported": "هذا الزوج المتغير غير مدعوم في التبادلات المحددة",
"verification": "تَحَقّق",
"verify_with_2fa": "تحقق مع Cake 2FA",

View file

@ -87,6 +87,7 @@
"buy": "Купуване",
"buy_alert_content": "В момента поддържаме само закупуването на Bitcoin, Ethereum, Litecoin и Monero. Моля, създайте или превключете към своя портфейл Bitcoin, Ethereum, Litecoin или Monero.",
"buy_bitcoin": "Купуване на Bitcoin",
"buy_now": "Купи сега",
"buy_provider_unavailable": "Понастоящем доставчик не е наличен.",
"buy_with": "Купуване чрез",
"by_cake_pay": "от Cake Pay",
@ -94,8 +95,7 @@
"cake_dark_theme": "Торта тъмна тема",
"cake_pay_account_note": "Регистрайте се само с един имейл, за да виждате и купувате карти. За някои има дори и отстъпка!",
"cake_pay_learn_more": "Купете и използвайте гифткарти директно в приложението!\nПлъзнете отляво надясно, за да научите още.",
"cake_pay_subtitle": "Купете гифткарти на намалени цени (само за САЩ)",
"cake_pay_title": "Cake Pay Gift Карти",
"cake_pay_subtitle": "Купете предплатени карти и карти за подаръци в световен мащаб",
"cake_pay_web_cards_subtitle": "Купете световно признати предплатени и гифт карти",
"cake_pay_web_cards_title": "Cake Pay Онлайн Карти",
"cake_wallet": "Cake Wallet",
@ -123,6 +123,7 @@
"change_wallet_alert_title": "Смяна на сегашния портфейл",
"choose_account": "Избиране на профил",
"choose_address": "\n\nМоля, изберете адреса:",
"choose_card_value": "Изберете стойност на картата",
"choose_derivation": "Изберете производно на портфейла",
"choose_from_available_options": "Изберете от следните опции:",
"choose_one": "Изберете едно",
@ -166,6 +167,7 @@
"copy_address": "Copy Address",
"copy_id": "Копиране на ID",
"copyWalletConnectLink": "Копирайте връзката WalletConnect от dApp и я поставете тук",
"countries": "Държави",
"create_account": "Създаване на профил",
"create_backup": "Създаване на резервно копие",
"create_donation_link": "Създайте връзка за дарение",
@ -178,6 +180,7 @@
"custom": "персонализирано",
"custom_drag": "Персонализиране (задръжте и плъзнете)",
"custom_redeem_amount": "Персонализирана сума за използване",
"custom_value": "Персонализирана стойност",
"dark_theme": "Тъмно",
"debit_card": "Дебитна карта",
"debit_card_terms": "Съхранението и използването на данните от вашата платежна карта в този дигитален портфейл подлежат на условията на съответното съгласие за картодържец от издателя на картата.",
@ -190,6 +193,7 @@
"delete_wallet": "Изтриване на портфейл",
"delete_wallet_confirm_message": "Сигурни ли сте, че искате да изтриете протфейла ${wallet_name}?",
"deleteConnectionConfirmationPrompt": "Сигурни ли сте, че искате да изтриете връзката към",
"denominations": "Деноминации",
"descending": "Низходящ",
"description": "Описание",
"destination_tag": "Destination tag:",
@ -277,6 +281,7 @@
"expired": "Изтекло",
"expires": "Изтича",
"expiresOn": "Изтича на",
"expiry_and_validity": "Изтичане и валидност",
"export_backup": "Експортиране на резервно копие",
"extra_id": "Допълнително ID:",
"extracted_address_content": "Ще изпратите средства на \n${recipient_name}",
@ -308,7 +313,7 @@
"gift_card_is_generated": "Gift Card бе създадена",
"gift_card_number": "Номер на Gift Card",
"gift_card_redeemed_note": "Използваните гифткарти ще се покажат тук",
"gift_cards": "Gift Карти",
"gift_cards": "Карти за подаръци",
"gift_cards_unavailable": "В момента гифткарти могат да бъдат закупени само с Monero, Bitcoin и Litecoin",
"got_it": "Готово",
"gross_balance": "Брутен баланс",
@ -384,6 +389,7 @@
"new_template": "Нов шаблон",
"new_wallet": "Нов портфейл",
"newConnection": "Нова връзка",
"no_cards_found": "Не са намерени карти",
"no_id_needed": "Без нужда от документ за самоличност!",
"no_id_required": "Без нужда от документ за самоличност. Използвайте навсякъде",
"no_relay_on_domain": "Няма реле за домейна на потребителя или релето не е налично. Моля, изберете реле, което да използвате.",
@ -452,6 +458,7 @@
"pre_seed_button_text": "Разбирам. Покажи seed",
"pre_seed_description": "На следващата страница ще видите поредица от ${words} думи. Това е вашият таен личен seed и е единственият начин да възстановите портфейла си. Отговорността за съхранението му на сигурно място извън приложението на Cake Wallet е изцяло ВАША.",
"pre_seed_title": "ВАЖНО",
"prepaid_cards": "Предплатени карти",
"prevent_screenshots": "Предотвратете екранни снимки и запис на екрана",
"privacy": "Поверителност",
"privacy_policy": "Политика за поверителността",
@ -467,6 +474,7 @@
"purple_dark_theme": "Лилава тъмна тема",
"qr_fullscreen": "Натиснете, за да отворите QR кода на цял екран",
"qr_payment_amount": "Този QR код съдържа сума за плащане. Искате ли да промените стойността?",
"quantity": "Количество",
"question_to_disable_2fa": "Сигурни ли сте, че искате да деактивирате Cake 2FA? Вече няма да е необходим 2FA код за достъп до портфейла и определени функции.",
"receivable_balance": "Баланс за вземания",
"receive": "Получи",
@ -708,6 +716,7 @@
"tokenID": "документ за самоличност",
"tor_connection": "Tor връзка",
"tor_only": "Само чрез Tor",
"total": "Обща сума",
"total_saving": "Общо спестявания",
"totp_2fa_failure": "Грешен код. Моля, опитайте с различен код или генерирайте нов таен ключ. Използвайте съвместимо 2FA приложение, което поддържа 8-цифрени кодове и SHA512.",
"totp_2fa_success": "Успех! Cake 2FA е активиран за този портфейл. Не забравяйте да запазите мнемоничното начало, в случай че загубите достъп до портфейла.",
@ -798,6 +807,8 @@
"use_ssl": "Използване на SSL",
"use_suggested": "Използване на предложеното",
"use_testnet": "Използвайте TestNet",
"value": "Стойност",
"value_type": "Тип стойност",
"variable_pair_not_supported": "Този variable pair не се поддържа от избраната борса",
"verification": "Потвърждаване",
"verify_with_2fa": "Проверете с Cake 2FA",

View file

@ -87,6 +87,7 @@
"buy": "Koupit",
"buy_alert_content": "V současné době podporujeme pouze nákup bitcoinů, etherea, litecoinů a monero. Vytvořte nebo přepněte na svou peněženku bitcoinů, etherea, litecoinů nebo monero.",
"buy_bitcoin": "Nakoupit Bitcoin",
"buy_now": "Kup nyní",
"buy_provider_unavailable": "Poskytovatel aktuálně nedostupný.",
"buy_with": "Nakoupit pomocí",
"by_cake_pay": "od Cake Pay",
@ -94,8 +95,7 @@
"cake_dark_theme": "Dort tmavé téma",
"cake_pay_account_note": "Přihlaste se svou e-mailovou adresou pro zobrazení a nákup karet. Některé jsou dostupné ve slevě!",
"cake_pay_learn_more": "Okamžitý nákup a uplatnění dárkových karet v aplikaci!\nPřejeďte prstem zleva doprava pro další informace.",
"cake_pay_subtitle": "Kupte si zlevněné dárkové karty (pouze USA)",
"cake_pay_title": "Cake Pay dárkové karty",
"cake_pay_subtitle": "Kupte si celosvětové předplacené karty a dárkové karty",
"cake_pay_web_cards_subtitle": "Kupte si celosvětové předplacené a dárkové karty",
"cake_pay_web_cards_title": "Cake Pay webové karty",
"cake_wallet": "Cake Wallet",
@ -123,6 +123,7 @@
"change_wallet_alert_title": "Přepnout peněženku",
"choose_account": "Zvolte částku",
"choose_address": "\n\nProsím vyberte adresu:",
"choose_card_value": "Vyberte hodnotu karty",
"choose_derivation": "Vyberte derivaci peněženky",
"choose_from_available_options": "Zvolte si z dostupných možností:",
"choose_one": "Zvolte si",
@ -166,6 +167,7 @@
"copy_address": "Zkopírovat adresu",
"copy_id": "Kopírovat ID",
"copyWalletConnectLink": "Zkopírujte odkaz WalletConnect z dApp a vložte jej sem",
"countries": "Země",
"create_account": "Vytvořit účet",
"create_backup": "Vytvořit zálohu",
"create_donation_link": "Vytvořit odkaz na darování",
@ -178,6 +180,7 @@
"custom": "vlastní",
"custom_drag": "Custom (Hold and Drag)",
"custom_redeem_amount": "Vlastní částka pro uplatnění",
"custom_value": "Vlastní hodnota",
"dark_theme": "Tmavý",
"debit_card": "Debetní karta",
"debit_card_terms": "Uložení a použití vašeho čísla platební karty (a přihlašovací údaje k vašemu číslu karty) v této digitální peněžence se řídí Obchodními podmínkami smlouvy příslušného držitele karty s vydavatelem karty (v jejich nejaktuálnější verzi).",
@ -190,6 +193,7 @@
"delete_wallet": "Smazat peněženku",
"delete_wallet_confirm_message": "Opravdu chcete smazat ${wallet_name} peněženku?",
"deleteConnectionConfirmationPrompt": "Jste si jisti, že chcete smazat připojení k?",
"denominations": "Označení",
"descending": "Klesající",
"description": "Popis",
"destination_tag": "Destination Tag:",
@ -277,6 +281,7 @@
"expired": "Vypršelo",
"expires": "Vyprší",
"expiresOn": "Vyprší dne",
"expiry_and_validity": "Vypršení a platnost",
"export_backup": "Exportovat zálohu",
"extra_id": "Extra ID:",
"extracted_address_content": "Prostředky budete posílat na\n${recipient_name}",
@ -384,6 +389,7 @@
"new_template": "Nová šablona",
"new_wallet": "Nová peněženka",
"newConnection": "Nové připojení",
"no_cards_found": "Žádné karty nenalezeny",
"no_id_needed": "Žádné ID není potřeba!",
"no_id_required": "Žádní ID není potřeba. Dobijte si a utrácejte kdekoliv",
"no_relay_on_domain": "Pro doménu uživatele neexistuje přenos nebo je přenos nedostupný. Vyberte relé, které chcete použít.",
@ -452,6 +458,7 @@
"pre_seed_button_text": "Rozumím. Ukaž mi můj seed.",
"pre_seed_description": "Na následující stránce uvidíte sérii ${words} slov. Je to váš tzv. seed a je to JEDINÁ možnost, jak můžete později obnovit svou peněženku v případě ztráty nebo poruchy. Je VAŠÍ zodpovědností zapsat si ho a uložit si ho na bezpečném místě mimo aplikaci Cake Wallet.",
"pre_seed_title": "DŮLEŽITÉ",
"prepaid_cards": "Předplacené karty",
"prevent_screenshots": "Zabránit vytváření snímků obrazovky a nahrávání obrazovky",
"privacy": "Soukromí",
"privacy_policy": "Zásady ochrany soukromí",
@ -467,6 +474,7 @@
"purple_dark_theme": "Fialové temné téma",
"qr_fullscreen": "Poklepáním otevřete QR kód na celé obrazovce",
"qr_payment_amount": "Tento QR kód obsahuje i částku. Chcete přepsat současnou hodnotu?",
"quantity": "Množství",
"question_to_disable_2fa": "Opravdu chcete deaktivovat Cake 2FA? Pro přístup k peněžence a některým funkcím již nebude potřeba kód 2FA.",
"receivable_balance": "Zůstatek pohledávek",
"receive": "Přijmout",
@ -708,6 +716,7 @@
"tokenID": "ID",
"tor_connection": "Připojení Tor",
"tor_only": "Pouze Tor",
"total": "Celkový",
"total_saving": "Celkem ušetřeno",
"totp_2fa_failure": "Nesprávný kód. Zkuste prosím jiný kód nebo vygenerujte nový tajný klíč. Použijte kompatibilní aplikaci 2FA, která podporuje 8místné kódy a SHA512.",
"totp_2fa_success": "Úspěch! Pro tuto peněženku povolen Cake 2FA. Nezapomeňte si uložit mnemotechnický klíč pro případ, že ztratíte přístup k peněžence.",
@ -798,6 +807,8 @@
"use_ssl": "Použít SSL",
"use_suggested": "Použít doporučený",
"use_testnet": "Použijte testNet",
"value": "Hodnota",
"value_type": "Typ hodnoty",
"variable_pair_not_supported": "Tento pár s tržním kurzem není ve zvolené směnárně podporován",
"verification": "Ověření",
"verify_with_2fa": "Ověřte pomocí Cake 2FA",

View file

@ -87,6 +87,7 @@
"buy": "Kaufen",
"buy_alert_content": "Derzeit unterstützen wir nur den Kauf von Bitcoin, Ethereum, Litecoin und Monero. Bitte erstellen Sie Ihr Bitcoin-, Ethereum-, Litecoin- oder Monero-Wallet oder wechseln Sie zu diesem.",
"buy_bitcoin": "Bitcoin kaufen",
"buy_now": "Kaufe jetzt",
"buy_provider_unavailable": "Anbieter derzeit nicht verfügbar.",
"buy_with": "Kaufen mit",
"by_cake_pay": "von Cake Pay",
@ -94,8 +95,7 @@
"cake_dark_theme": "Cake Dark Thema",
"cake_pay_account_note": "Melden Sie sich nur mit einer E-Mail-Adresse an, um Karten anzuzeigen und zu kaufen. Einige sind sogar mit Rabatt erhältlich!",
"cake_pay_learn_more": "Kaufen und lösen Sie Geschenkkarten sofort in der App ein!\nWischen Sie von links nach rechts, um mehr zu erfahren.",
"cake_pay_subtitle": "Kaufen Sie ermäßigte Geschenkkarten (nur USA)",
"cake_pay_title": "Cake Pay-Geschenkkarten",
"cake_pay_subtitle": "Kaufen Sie weltweite Prepaid -Karten und Geschenkkarten",
"cake_pay_web_cards_subtitle": "Kaufen Sie weltweit Prepaid-Karten und Geschenkkarten",
"cake_pay_web_cards_title": "Cake Pay-Webkarten",
"cake_wallet": "Cake Wallet",
@ -123,6 +123,7 @@
"change_wallet_alert_title": "Aktuelle Wallet ändern",
"choose_account": "Konto auswählen",
"choose_address": "\n\nBitte wählen Sie die Adresse:",
"choose_card_value": "Wählen Sie einen Kartenwert",
"choose_derivation": "Wählen Sie Wallet-Ableitung",
"choose_from_available_options": "Wähle aus verfügbaren Optionen:",
"choose_one": "Wähle ein",
@ -166,6 +167,7 @@
"copy_address": "Adresse kopieren",
"copy_id": "ID kopieren",
"copyWalletConnectLink": "Kopieren Sie den WalletConnect-Link von dApp und fügen Sie ihn hier ein",
"countries": "Länder",
"create_account": "Konto erstellen",
"create_backup": "Backup erstellen",
"create_donation_link": "Spendenlink erstellen",
@ -178,6 +180,7 @@
"custom": "benutzerdefiniert",
"custom_drag": "Custom (Hold and Drag)",
"custom_redeem_amount": "Benutzerdefinierter Einlösungsbetrag",
"custom_value": "Benutzerdefinierten Wert",
"dark_theme": "Dunkel",
"debit_card": "Debitkarte",
"debit_card_terms": "Die Speicherung und Nutzung Ihrer Zahlungskartennummer (und Ihrer Zahlungskartennummer entsprechenden Anmeldeinformationen) in dieser digitalen Geldbörse unterliegt den Allgemeinen Geschäftsbedingungen des geltenden Karteninhabervertrags mit dem Zahlungskartenaussteller, gültig ab von Zeit zu Zeit.",
@ -190,6 +193,7 @@
"delete_wallet": "Wallet löschen",
"delete_wallet_confirm_message": "Sind Sie sicher, dass Sie das ${wallet_name} Wallet löschen möchten?",
"deleteConnectionConfirmationPrompt": "Sind Sie sicher, dass Sie die Verbindung zu löschen möchten?",
"denominations": "Konfessionen",
"descending": "Absteigend",
"description": "Beschreibung",
"destination_tag": "Ziel-Tag:",
@ -277,6 +281,7 @@
"expired": "Abgelaufen",
"expires": "Läuft ab",
"expiresOn": "Läuft aus am",
"expiry_and_validity": "Ablauf und Gültigkeit",
"export_backup": "Sicherung exportieren",
"extra_id": "Extra ID:",
"extracted_address_content": "Sie senden Geld an\n${recipient_name}",
@ -384,6 +389,7 @@
"new_template": "neue Vorlage",
"new_wallet": "Neue Wallet",
"newConnection": "Neue Verbindung",
"no_cards_found": "Keine Karten gefunden",
"no_id_needed": "Keine ID erforderlich!",
"no_id_required": "Keine ID erforderlich. Upgraden und überall ausgeben",
"no_relay_on_domain": "Es gibt kein Relay für die Domäne des Benutzers oder das Relay ist nicht verfügbar. Bitte wählen Sie ein zu verwendendes Relais aus.",
@ -442,8 +448,8 @@
"placeholder_transactions": "Ihre Transaktionen werden hier angezeigt",
"please_fill_totp": "Bitte geben Sie den 8-stelligen Code ein, der auf Ihrem anderen Gerät vorhanden ist",
"please_make_selection": "Bitte treffen Sie unten eine Auswahl zum Erstellen oder Wiederherstellen Ihrer Wallet.",
"please_reference_document": "Bitte verweisen Sie auf die folgenden Dokumente, um weitere Informationen zu erhalten.",
"Please_reference_document": "Weitere Informationen finden Sie in den Dokumenten unten.",
"please_reference_document": "Bitte verweisen Sie auf die folgenden Dokumente, um weitere Informationen zu erhalten.",
"please_select": "Bitte auswählen:",
"please_select_backup_file": "Bitte wählen Sie die Sicherungsdatei und geben Sie das Sicherungskennwort ein.",
"please_try_to_connect_to_another_node": "Bitte versuchen Sie, sich mit einem anderen Knoten zu verbinden",
@ -453,6 +459,7 @@
"pre_seed_button_text": "Verstanden. Zeig mir meinen Seed",
"pre_seed_description": "Auf der nächsten Seite sehen Sie eine Reihe von ${words} Wörtern. Dies ist Ihr einzigartiger und privater Seed und der EINZIGE Weg, um Ihre Wallet im Falle eines Verlusts oder einer Fehlfunktion wiederherzustellen. Es liegt in IHRER Verantwortung, ihn aufzuschreiben und an einem sicheren Ort außerhalb der Cake Wallet-App aufzubewahren.",
"pre_seed_title": "WICHTIG",
"prepaid_cards": "Karten mit Guthaben",
"prevent_screenshots": "Verhindern Sie Screenshots und Bildschirmaufzeichnungen",
"privacy": "Datenschutz",
"privacy_policy": "Datenschutzrichtlinie",
@ -468,6 +475,7 @@
"purple_dark_theme": "Lila dunkle Thema",
"qr_fullscreen": "Tippen Sie hier, um den QR-Code im Vollbildmodus zu öffnen",
"qr_payment_amount": "This QR code contains a payment amount. Do you want to overwrite the current value?",
"quantity": "Menge",
"question_to_disable_2fa": "Sind Sie sicher, dass Sie Cake 2FA deaktivieren möchten? Für den Zugriff auf die Wallet und bestimmte Funktionen wird kein 2FA-Code mehr benötigt.",
"receivable_balance": "Forderungsbilanz",
"receive": "Empfangen",
@ -709,6 +717,7 @@
"tokenID": "AUSWEIS",
"tor_connection": "Tor-Verbindung",
"tor_only": "Nur Tor",
"total": "Gesamt",
"total_saving": "Gesamteinsparungen",
"totp_2fa_failure": "Falscher Code. Bitte versuchen Sie es mit einem anderen Code oder generieren Sie einen neuen geheimen Schlüssel. Verwenden Sie eine kompatible 2FA-App, die 8-stellige Codes und SHA512 unterstützt.",
"totp_2fa_success": "Erfolg! Cake 2FA für dieses Wallet aktiviert. Denken Sie daran, Ihren mnemonischen Seed zu speichern, falls Sie den Zugriff auf die Wallet verlieren.",
@ -800,6 +809,8 @@
"use_ssl": "SSL verwenden",
"use_suggested": "Vorgeschlagen verwenden",
"use_testnet": "TESTNET verwenden",
"value": "Wert",
"value_type": "Werttyp",
"variable_pair_not_supported": "Dieses Variablenpaar wird von den ausgewählten Börsen nicht unterstützt",
"verification": "Verifizierung",
"verify_with_2fa": "Verifizieren Sie mit Cake 2FA",

View file

@ -87,6 +87,7 @@
"buy": "Buy",
"buy_alert_content": "Currently we only support the purchase of Bitcoin, Ethereum, Litecoin, and Monero. Please create or switch to your Bitcoin, Ethereum, Litecoin, or Monero wallet.",
"buy_bitcoin": "Buy Bitcoin",
"buy_now": "Buy Now",
"buy_provider_unavailable": "Provider currently unavailable.",
"buy_with": "Buy with",
"by_cake_pay": "by Cake Pay",
@ -94,8 +95,7 @@
"cake_dark_theme": "Cake Dark Theme",
"cake_pay_account_note": "Sign up with just an email address to see and purchase cards. Some are even available at a discount!",
"cake_pay_learn_more": "Instantly purchase and redeem gift cards in the app!\nSwipe left to right to learn more.",
"cake_pay_subtitle": "Buy discounted gift cards (USA only)",
"cake_pay_title": "Cake Pay Gift Cards",
"cake_pay_subtitle": "Buy worldwide prepaid cards and gift cards",
"cake_pay_web_cards_subtitle": "Buy worldwide prepaid cards and gift cards",
"cake_pay_web_cards_title": "Cake Pay Web Cards",
"cake_wallet": "Cake Wallet",
@ -123,6 +123,7 @@
"change_wallet_alert_title": "Change current wallet",
"choose_account": "Choose account",
"choose_address": "\n\nPlease choose the address:",
"choose_card_value": "Choose a card value",
"choose_derivation": "Choose Wallet Derivation",
"choose_from_available_options": "Choose from the available options:",
"choose_one": "Choose one",
@ -166,6 +167,7 @@
"copy_address": "Copy Address",
"copy_id": "Copy ID",
"copyWalletConnectLink": "Copy the WalletConnect link from dApp and paste here",
"countries": "Countries",
"create_account": "Create Account",
"create_backup": "Create backup",
"create_donation_link": "Create donation link",
@ -178,6 +180,7 @@
"custom": "Custom",
"custom_drag": "Custom (Hold and Drag)",
"custom_redeem_amount": "Custom Redeem Amount",
"custom_value": "Custom Value",
"dark_theme": "Dark",
"debit_card": "Debit Card",
"debit_card_terms": "The storage and usage of your payment card number (and credentials corresponding to your payment card number) in this digital wallet are subject to the Terms and Conditions of the applicable cardholder agreement with the payment card issuer, as in effect from time to time.",
@ -190,6 +193,7 @@
"delete_wallet": "Delete wallet",
"delete_wallet_confirm_message": "Are you sure that you want to delete ${wallet_name} wallet?",
"deleteConnectionConfirmationPrompt": "Are you sure that you want to delete the connection to",
"denominations": "Denominations",
"descending": "Descending",
"description": "Description",
"destination_tag": "Destination tag:",
@ -277,6 +281,7 @@
"expired": "Expired",
"expires": "Expires",
"expiresOn": "Expires on",
"expiry_and_validity": "Expiry and Validity",
"export_backup": "Export backup",
"extra_id": "Extra ID:",
"extracted_address_content": "You will be sending funds to\n${recipient_name}",
@ -384,6 +389,7 @@
"new_template": "New Template",
"new_wallet": "New Wallet",
"newConnection": "New Connection",
"no_cards_found": "No cards found",
"no_id_needed": "No ID needed!",
"no_id_required": "No ID required. Top up and spend anywhere",
"no_relay_on_domain": "There isn't a relay for user's domain or the relay is unavailable. Please choose a relay to use.",
@ -452,6 +458,7 @@
"pre_seed_button_text": "I understand. Show me my seed",
"pre_seed_description": "On the next page you will see a series of ${words} words. This is your unique and private seed and it is the ONLY way to recover your wallet in case of loss or malfunction. It is YOUR responsibility to write it down and store it in a safe place outside of the Cake Wallet app.",
"pre_seed_title": "IMPORTANT",
"prepaid_cards": "Prepaid Cards",
"prevent_screenshots": "Prevent screenshots and screen recording",
"privacy": "Privacy",
"privacy_policy": "Privacy Policy",
@ -467,6 +474,7 @@
"purple_dark_theme": "Purple Dark Theme",
"qr_fullscreen": "Tap to open full screen QR code",
"qr_payment_amount": "This QR code contains a payment amount. Do you want to overwrite the current value?",
"quantity": "Quantity",
"question_to_disable_2fa": "Are you sure that you want to disable Cake 2FA? A 2FA code will no longer be needed to access the wallet and certain functions.",
"receivable_balance": "Receivable Balance",
"receive": "Receive",
@ -708,6 +716,7 @@
"tokenID": "ID",
"tor_connection": "Tor connection",
"tor_only": "Tor only",
"total": "Total",
"total_saving": "Total Savings",
"totp_2fa_failure": "Incorrect code. Please try a different code or generate a new secret key. Use a compatible 2FA app that supports 8-digit codes and SHA512.",
"totp_2fa_success": "Success! Cake 2FA enabled for this wallet. Remember to save your mnemonic seed in case you lose wallet access.",
@ -798,6 +807,8 @@
"use_ssl": "Use SSL",
"use_suggested": "Use Suggested",
"use_testnet": "Use Testnet",
"value": "Value",
"value_type": "Value Type",
"variable_pair_not_supported": "This variable pair is not supported with the selected exchanges",
"verification": "Verification",
"verify_with_2fa": "Verify with Cake 2FA",

View file

@ -87,6 +87,7 @@
"buy": "Comprar",
"buy_alert_content": "Actualmente solo admitimos la compra de Bitcoin, Ethereum, Litecoin y Monero. Cree o cambie a su billetera Bitcoin, Ethereum, Litecoin o Monero.",
"buy_bitcoin": "Comprar Bitcoin",
"buy_now": "Comprar ahora",
"buy_provider_unavailable": "Proveedor actualmente no disponible.",
"buy_with": "Compra con",
"by_cake_pay": "por Cake Pay",
@ -94,8 +95,7 @@
"cake_dark_theme": "Tema oscuro del pastel",
"cake_pay_account_note": "Regístrese con solo una dirección de correo electrónico para ver y comprar tarjetas. ¡Algunas incluso están disponibles con descuento!",
"cake_pay_learn_more": "¡Compre y canjee tarjetas de regalo al instante en la aplicación!\nDeslice el dedo de izquierda a derecha para obtener más información.",
"cake_pay_subtitle": "Compre tarjetas de regalo con descuento (solo EE. UU.)",
"cake_pay_title": "Tarjetas de regalo Cake Pay",
"cake_pay_subtitle": "Compre tarjetas prepagas y tarjetas de regalo en todo el mundo",
"cake_pay_web_cards_subtitle": "Compre tarjetas de prepago y tarjetas de regalo en todo el mundo",
"cake_pay_web_cards_title": "Tarjetas Web Cake Pay",
"cake_wallet": "Cake Wallet",
@ -123,6 +123,7 @@
"change_wallet_alert_title": "Cambiar billetera actual",
"choose_account": "Elegir cuenta",
"choose_address": "\n\nPor favor elija la dirección:",
"choose_card_value": "Elija un valor de tarjeta",
"choose_derivation": "Elija la derivación de la billetera",
"choose_from_available_options": "Elija entre las opciones disponibles:",
"choose_one": "Elige uno",
@ -166,6 +167,7 @@
"copy_address": "Copiar dirección ",
"copy_id": "Copiar ID",
"copyWalletConnectLink": "Copie el enlace de WalletConnect de dApp y péguelo aquí",
"countries": "Países",
"create_account": "Crear Cuenta",
"create_backup": "Crear copia de seguridad",
"create_donation_link": "Crear enlace de donación",
@ -178,6 +180,7 @@
"custom": "Costumbre",
"custom_drag": "Custom (mantenía y arrastre)",
"custom_redeem_amount": "Cantidad de canje personalizada",
"custom_value": "Valor personalizado",
"dark_theme": "Oscura",
"debit_card": "Tarjeta de Débito",
"debit_card_terms": "El almacenamiento y el uso de su número de tarjeta de pago (y las credenciales correspondientes a su número de tarjeta de pago) en esta billetera digital están sujetos a los Términos y condiciones del acuerdo del titular de la tarjeta aplicable con el emisor de la tarjeta de pago, en vigor desde tiempo al tiempo.",
@ -190,6 +193,7 @@
"delete_wallet": "Eliminar billetera",
"delete_wallet_confirm_message": "¿Está seguro de que desea eliminar la billetera ${wallet_name}?",
"deleteConnectionConfirmationPrompt": "¿Está seguro de que desea eliminar la conexión a",
"denominations": "Denominaciones",
"descending": "Descendente",
"description": "Descripción",
"destination_tag": "Etiqueta de destino:",
@ -277,6 +281,7 @@
"expired": "Muerto",
"expires": "Caduca",
"expiresOn": "Expira el",
"expiry_and_validity": "Vencimiento y validez",
"export_backup": "Exportar copia de seguridad",
"extra_id": "ID adicional:",
"extracted_address_content": "Enviará fondos a\n${recipient_name}",
@ -384,6 +389,7 @@
"new_template": "Nueva plantilla",
"new_wallet": "Nueva billetera",
"newConnection": "Nueva conexión",
"no_cards_found": "No se encuentran cartas",
"no_id_needed": "¡No se necesita identificación!",
"no_id_required": "No se requiere identificación. Recargue y gaste en cualquier lugar",
"no_relay_on_domain": "No hay una retransmisión para el dominio del usuario o la retransmisión no está disponible. Elija un relé para usar.",
@ -453,6 +459,7 @@
"pre_seed_button_text": "Entiendo. Muéstrame mi semilla",
"pre_seed_description": "En la página siguiente verá una serie de ${words} palabras. Esta es su semilla única y privada y es la ÚNICA forma de recuperar su billetera en caso de pérdida o mal funcionamiento. Es SU responsabilidad escribirlo y guardarlo en un lugar seguro fuera de la aplicación Cake Wallet.",
"pre_seed_title": "IMPORTANTE",
"prepaid_cards": "Tajetas prepagadas",
"prevent_screenshots": "Evitar capturas de pantalla y grabación de pantalla",
"privacy": "Privacidad",
"privacy_policy": "Política de privacidad",
@ -468,6 +475,7 @@
"purple_dark_theme": "Tema morado oscuro",
"qr_fullscreen": "Toque para abrir el código QR en pantalla completa",
"qr_payment_amount": "This QR code contains a payment amount. Do you want to overwrite the current value?",
"quantity": "Cantidad",
"question_to_disable_2fa": "¿Está seguro de que desea deshabilitar Cake 2FA? Ya no se necesitará un código 2FA para acceder a la billetera y a ciertas funciones.",
"receivable_balance": "Saldo de cuentas por cobrar",
"receive": "Recibir",
@ -709,6 +717,7 @@
"tokenID": "IDENTIFICACIÓN",
"tor_connection": "conexión tor",
"tor_only": "solo Tor",
"total": "Total",
"total_saving": "Ahorro Total",
"totp_2fa_failure": "Código incorrecto. Intente con un código diferente o genere una nueva clave secreta. Use una aplicación 2FA compatible que admita códigos de 8 dígitos y SHA512.",
"totp_2fa_success": "¡Éxito! Cake 2FA habilitado para esta billetera. Recuerde guardar su semilla mnemotécnica en caso de que pierda el acceso a la billetera.",
@ -799,6 +808,8 @@
"use_ssl": "Utilice SSL",
"use_suggested": "Usar sugerido",
"use_testnet": "Use TestNet",
"value": "Valor",
"value_type": "Tipo de valor",
"variable_pair_not_supported": "Este par de variables no es compatible con los intercambios seleccionados",
"verification": "Verificación",
"verify_with_2fa": "Verificar con Cake 2FA",

View file

@ -87,6 +87,7 @@
"buy": "Acheter",
"buy_alert_content": "Actuellement, nous ne prenons en charge que l'achat de Bitcoin, Ethereum, Litecoin et Monero. Veuillez créer ou basculer vers votre portefeuille Bitcoin, Ethereum, Litecoin ou Monero.",
"buy_bitcoin": "Acheter du Bitcoin",
"buy_now": "Acheter maintenant",
"buy_provider_unavailable": "Fournisseur actuellement indisponible.",
"buy_with": "Acheter avec",
"by_cake_pay": "par Cake Pay",
@ -94,8 +95,7 @@
"cake_dark_theme": "Thème sombre du gâteau",
"cake_pay_account_note": "Inscrivez-vous avec juste une adresse e-mail pour voir et acheter des cartes. Certaines sont même disponibles à prix réduit !",
"cake_pay_learn_more": "Achetez et utilisez instantanément des cartes-cadeaux dans l'application !\nBalayer de gauche à droite pour en savoir plus.",
"cake_pay_subtitle": "Achetez des cartes-cadeaux à prix réduit (États-Unis uniquement)",
"cake_pay_title": "Cartes cadeaux Cake Pay",
"cake_pay_subtitle": "Achetez des cartes et des cartes-cadeaux prépayées mondiales",
"cake_pay_web_cards_subtitle": "Achetez des cartes prépayées et des cartes-cadeaux dans le monde entier",
"cake_pay_web_cards_title": "Cartes Web Cake Pay",
"cake_wallet": "Cake Wallet",
@ -123,6 +123,7 @@
"change_wallet_alert_title": "Changer le portefeuille (wallet) actuel",
"choose_account": "Choisir le compte",
"choose_address": "\n\nMerci de choisir l'adresse :",
"choose_card_value": "Choisissez une valeur de carte",
"choose_derivation": "Choisissez le chemin de dérivation du portefeuille",
"choose_from_available_options": "Choisissez parmi les options disponibles :",
"choose_one": "Choisissez-en un",
@ -166,6 +167,7 @@
"copy_address": "Copier l'Adresse",
"copy_id": "Copier l'ID",
"copyWalletConnectLink": "Copiez le lien WalletConnect depuis l'application décentralisée (dApp) et collez-le ici",
"countries": "Des pays",
"create_account": "Créer un compte",
"create_backup": "Créer une sauvegarde",
"create_donation_link": "Créer un lien de don",
@ -178,6 +180,7 @@
"custom": "personnalisé",
"custom_drag": "Custom (maintenir et traîner)",
"custom_redeem_amount": "Montant d'échange personnalisé",
"custom_value": "Valeur personnalisée",
"dark_theme": "Sombre",
"debit_card": "Carte de débit",
"debit_card_terms": "Le stockage et l'utilisation de votre numéro de carte de paiement (et des informations d'identification correspondant à votre numéro de carte de paiement) dans ce portefeuille (wallet) numérique peuvent être soumis aux conditions générales de l'accord du titulaire de carte parfois en vigueur avec l'émetteur de la carte de paiement.",
@ -190,6 +193,7 @@
"delete_wallet": "Supprimer le portefeuille (wallet)",
"delete_wallet_confirm_message": "Êtes-vous sûr de vouloir supprimer le portefeuille (wallet) ${wallet_name}?",
"deleteConnectionConfirmationPrompt": "Êtes-vous sûr de vouloir supprimer la connexion à",
"denominations": "Dénominations",
"descending": "Descendant",
"description": "Description",
"destination_tag": "Tag de destination :",
@ -277,6 +281,7 @@
"expired": "Expirée",
"expires": "Expire",
"expiresOn": "Expire le",
"expiry_and_validity": "Expiration et validité",
"export_backup": "Exporter la sauvegarde",
"extra_id": "ID supplémentaire :",
"extracted_address_content": "Vous allez envoyer des fonds à\n${recipient_name}",
@ -308,7 +313,7 @@
"gift_card_is_generated": "La carte-cadeau est générée",
"gift_card_number": "Numéro de carte cadeau",
"gift_card_redeemed_note": "Les cartes-cadeaux que vous avez utilisées apparaîtront ici",
"gift_cards": "Cartes-Cadeaux",
"gift_cards": "Cartes cadeaux",
"gift_cards_unavailable": "Les cartes-cadeaux ne sont disponibles à l'achat que via Monero, Bitcoin et Litecoin pour le moment",
"got_it": "Compris",
"gross_balance": "Solde brut",
@ -384,6 +389,7 @@
"new_template": "Nouveau Modèle",
"new_wallet": "Nouveau Portefeuille (Wallet)",
"newConnection": "Nouvelle connexion",
"no_cards_found": "Pas de cartes trouvées",
"no_id_needed": "Aucune pièce d'identité nécessaire !",
"no_id_required": "Aucune pièce d'identité requise. Rechargez et dépensez n'importe où",
"no_relay_on_domain": "Il n'existe pas de relais pour le domaine de l'utilisateur ou le relais n'est pas disponible. Veuillez choisir un relais à utiliser.",
@ -452,6 +458,7 @@
"pre_seed_button_text": "J'ai compris. Montrez moi ma phrase secrète (seed)",
"pre_seed_description": "Sur la page suivante vous allez voir une série de ${words} mots. Ils constituent votre phrase secrète (seed) unique et privée et sont le SEUL moyen de restaurer votre portefeuille (wallet) en cas de perte ou de dysfonctionnement. Il est de VOTRE responsabilité d'écrire cette série de mots et de la stocker dans un lieu sûr en dehors de l'application Cake Wallet.",
"pre_seed_title": "IMPORTANT",
"prepaid_cards": "Cartes prépayées",
"prevent_screenshots": "Empêcher les captures d'écran et l'enregistrement d'écran",
"privacy": "Confidentialité",
"privacy_policy": "Politique de confidentialité",
@ -467,6 +474,7 @@
"purple_dark_theme": "THÈME PURPLE DARK",
"qr_fullscreen": "Appuyez pour ouvrir le QR code en mode plein écran",
"qr_payment_amount": "Ce QR code contient un montant de paiement. Voulez-vous remplacer la valeur actuelle ?",
"quantity": "Quantité",
"question_to_disable_2fa": "Êtes-vous sûr de vouloir désactiver Cake 2FA ? Un code 2FA ne sera plus nécessaire pour accéder au portefeuille (wallet) et à certaines fonctions.",
"receivable_balance": "Solde de créances",
"receive": "Recevoir",
@ -708,6 +716,7 @@
"tokenID": "IDENTIFIANT",
"tor_connection": "Connexion Tor",
"tor_only": "Tor uniquement",
"total": "Total",
"total_saving": "Économies totales",
"totp_2fa_failure": "Code incorrect. Veuillez essayer un code différent ou générer un nouveau secret TOTP. Utilisez une application 2FA compatible qui prend en charge les codes à 8 chiffres et SHA512.",
"totp_2fa_success": "Succès! Cake 2FA est activé pour ce portefeuille. N'oubliez pas de sauvegarder votre phrase secrète (seed) au cas où vous perdriez l'accès au portefeuille (wallet).",
@ -798,6 +807,8 @@
"use_ssl": "Utiliser SSL",
"use_suggested": "Suivre la suggestion",
"use_testnet": "Utiliser TestNet",
"value": "Valeur",
"value_type": "Type de valeur",
"variable_pair_not_supported": "Cette paire variable n'est pas prise en charge avec les échanges sélectionnés",
"verification": "Vérification",
"verify_with_2fa": "Vérifier avec Cake 2FA",

View file

@ -87,6 +87,7 @@
"buy": "Sayi",
"buy_alert_content": "A halin yanzu muna tallafawa kawai siyan Bitcoin, Ethereum, Litecoin, da Monero. Da fatan za a ƙirƙiri ko canza zuwa Bitcoin, Ethereum, Litecoin, ko Monero walat.",
"buy_bitcoin": "Sayi Bitcoin",
"buy_now": "Saya yanzu",
"buy_provider_unavailable": "Mai ba da kyauta a halin yanzu babu.",
"buy_with": "Saya da",
"by_cake_pay": "da Cake Pay",
@ -94,8 +95,7 @@
"cake_dark_theme": "Cake Dark Jigo",
"cake_pay_account_note": "Yi rajista tare da adireshin imel kawai don gani da siyan katunan. Wasu ma suna samuwa a rangwame!",
"cake_pay_learn_more": "Nan take siya ku kwaso katunan kyaututtuka a cikin app!\nTake hagu zuwa dama don ƙarin koyo.",
"cake_pay_subtitle": "Sayi katunan kyauta masu rahusa (Amurka kawai)",
"cake_pay_title": "Cake Pay Gift Cards",
"cake_pay_subtitle": "Sayi katunan shirye-shiryen duniya da katunan kyauta",
"cake_pay_web_cards_subtitle": "Sayi katunan da aka riga aka biya na duniya da katunan kyauta",
"cake_pay_web_cards_title": "Cake Pay Web Cards",
"cake_wallet": "Cake Wallet",
@ -123,6 +123,7 @@
"change_wallet_alert_title": "Canja walat yanzu",
"choose_account": "Zaɓi asusu",
"choose_address": "\n\n Da fatan za a zaɓi adireshin:",
"choose_card_value": "Zabi darajar katin",
"choose_derivation": "Zaɓi walatawa",
"choose_from_available_options": "Zaɓi daga zaɓuɓɓukan da ake da su:",
"choose_one": "Zaɓi ɗaya",
@ -166,6 +167,7 @@
"copy_address": "Kwafi Adireshin",
"copy_id": "Kwafi ID",
"copyWalletConnectLink": "Kwafi hanyar haɗin WalletConnect daga dApp kuma liƙa a nan",
"countries": "Kasashe",
"create_account": "Kirkira ajiya",
"create_backup": "Ƙirƙiri madadin",
"create_donation_link": "Sanya hanyar sadaka",
@ -178,6 +180,7 @@
"custom": "al'ada",
"custom_drag": "Al'ada (riƙe da ja)",
"custom_redeem_amount": "Adadin Fansa na Musamman",
"custom_value": "Darajar al'ada",
"dark_theme": "Duhu",
"debit_card": "Katin Zare kudi",
"debit_card_terms": "Adana da amfani da lambar katin kuɗin ku (da takaddun shaida masu dacewa da lambar katin kuɗin ku) a cikin wannan walat ɗin dijital suna ƙarƙashin Sharuɗɗa da Sharuɗɗa na yarjejeniya mai amfani da katin tare da mai fitar da katin biyan kuɗi, kamar yadda yake aiki daga lokaci zuwa lokaci.",
@ -190,6 +193,7 @@
"delete_wallet": "Share walat",
"delete_wallet_confirm_message": "Shin kun tabbata cewa kuna son share jakar ${wallet_name}?",
"deleteConnectionConfirmationPrompt": "Shin kun tabbata cewa kuna son share haɗin zuwa",
"denominations": "Denominations",
"descending": "Saukowa",
"description": "Bayani",
"destination_tag": "Tambarin makoma:",
@ -277,6 +281,7 @@
"expired": "Karewa",
"expires": "Ya ƙare",
"expiresOn": "Yana ƙarewa",
"expiry_and_validity": "Karewa da inganci",
"export_backup": "Ajiyayyen fitarwa",
"extra_id": "Karin ID:",
"extracted_address_content": "Za ku aika da kudade zuwa\n${recipient_name}",
@ -384,6 +389,7 @@
"new_template": "Sabon Samfura",
"new_wallet": "Sabuwar Wallet",
"newConnection": "Sabuwar Haɗi",
"no_cards_found": "Babu katunan da aka samo",
"no_id_needed": "Babu ID da ake buƙata!",
"no_id_required": "Babu ID da ake buƙata. Yi da kuma ciyar a ko'ina",
"no_relay_on_domain": "Babu gudun ba da sanda ga yankin mai amfani ko kuma ba a samu ba. Da fatan za a zaɓi gudun ba da sanda don amfani.",
@ -454,6 +460,7 @@
"pre_seed_button_text": "Ina fahimta. Nuna mini seed din nawa",
"pre_seed_description": "A kan shafin nan za ku ga wata ƙungiya na ${words} kalmomi. Wannan shine tsarin daban-daban ku kuma na sirri kuma shine hanya ɗaya kadai don mai da purse dinku a cikin yanayin rasa ko rashin aiki. Yana da damar da kuke a cikin tabbatar da kuyi rubuta shi kuma kuyi ajiye shi a wuri na aminci wanda ya wuce wurin app na Cake Wallet.",
"pre_seed_title": "MUHIMMANCI",
"prepaid_cards": "Katunan shirye-shirye",
"prevent_screenshots": "Fada lambobi da jarrabobi na kayan lambobi",
"privacy": "Keɓantawa",
"privacy_policy": "takardar kebantawa",
@ -469,6 +476,7 @@
"purple_dark_theme": "M duhu jigo",
"qr_fullscreen": "Matsa don buɗe lambar QR na cikakken allo",
"qr_payment_amount": "Wannan QR code yana da adadin kuɗi. Kuna so ku overwrite wannan adadi?",
"quantity": "Yawa",
"question_to_disable_2fa": "Ka tabbata cewa kana son kashe cake 2fa? Ba za a sake buƙatar lambar 2FA ba don samun damar yin walat da takamaiman ayyuka.",
"receivable_balance": "Daidaituwa da daidaituwa",
"receive": "Samu",
@ -710,6 +718,7 @@
"tokenID": "ID",
"tor_connection": "Tor haɗin gwiwa",
"tor_only": "Tor kawai",
"total": "Duka",
"total_saving": "Jimlar Adana",
"totp_2fa_failure": "Ba daidai ba. Da fatan za a gwada wata lamba ta daban ko samar da sabon maɓallin asirin. Yi amfani da aikace-aikacen da ya dace 2FA wanda ke tallafawa lambobin lambobi 8 da Sha512.",
"totp_2fa_success": "Nasara! Cake 2FA ya dogara da wannan waljin. Ka tuna domin adana zuriyar mnemmonic naka idan ka rasa damar walat.",
@ -800,6 +809,8 @@
"use_ssl": "Yi amfani da SSL",
"use_suggested": "Amfani da Shawarwari",
"use_testnet": "Amfani da gwaji",
"value": "Daraja",
"value_type": "Nau'in darajar",
"variable_pair_not_supported": "Ba a samun goyan bayan wannan m biyu tare da zaɓaɓɓun musayar",
"verification": "tabbatar",
"verify_with_2fa": "Tabbatar da Cake 2FA",

View file

@ -87,6 +87,7 @@
"buy": "खरीदें",
"buy_alert_content": "वर्तमान में हम केवल बिटकॉइन, एथेरियम, लाइटकॉइन और मोनेरो की खरीद का समर्थन करते हैं। कृपया अपना बिटकॉइन, एथेरियम, लाइटकॉइन, या मोनेरो वॉलेट बनाएं या उस पर स्विच करें।",
"buy_bitcoin": "बिटकॉइन खरीदें",
"buy_now": "अभी खरीदें",
"buy_provider_unavailable": "वर्तमान में प्रदाता अनुपलब्ध है।",
"buy_with": "के साथ खरीदें",
"by_cake_pay": "केकपे द्वारा",
@ -94,8 +95,7 @@
"cake_dark_theme": "केक डार्क थीम",
"cake_pay_account_note": "कार्ड देखने और खरीदने के लिए केवल एक ईमेल पते के साथ साइन अप करें। कुछ छूट पर भी उपलब्ध हैं!",
"cake_pay_learn_more": "ऐप में उपहार कार्ड तुरंत खरीदें और रिडीम करें!\nअधिक जानने के लिए बाएं से दाएं स्वाइप करें।",
"cake_pay_subtitle": "रियायती उपहार कार्ड खरीदें (केवल यूएसए)",
"cake_pay_title": "केक पे गिफ्ट कार्ड्स",
"cake_pay_subtitle": "दुनिया भर में प्रीपेड कार्ड और उपहार कार्ड खरीदें",
"cake_pay_web_cards_subtitle": "दुनिया भर में प्रीपेड कार्ड और गिफ्ट कार्ड खरीदें",
"cake_pay_web_cards_title": "केक भुगतान वेब कार्ड",
"cake_wallet": "Cake Wallet",
@ -123,6 +123,7 @@
"change_wallet_alert_title": "वर्तमान बटुआ बदलें",
"choose_account": "खाता चुनें",
"choose_address": "\n\nकृपया पता चुनें:",
"choose_card_value": "एक कार्ड मूल्य चुनें",
"choose_derivation": "वॉलेट व्युत्पत्ति चुनें",
"choose_from_available_options": "उपलब्ध विकल्पों में से चुनें:",
"choose_one": "एक का चयन",
@ -166,6 +167,7 @@
"copy_address": "पता कॉपी करें",
"copy_id": "प्रतिलिपि ID",
"copyWalletConnectLink": "dApp से वॉलेटकनेक्ट लिंक को कॉपी करें और यहां पेस्ट करें",
"countries": "देशों",
"create_account": "खाता बनाएं",
"create_backup": "बैकअप बनाएँ",
"create_donation_link": "दान लिंक बनाएं",
@ -178,6 +180,7 @@
"custom": "कस्टम",
"custom_drag": "कस्टम (पकड़ और खींचें)",
"custom_redeem_amount": "कस्टम रिडीम राशि",
"custom_value": "कस्टम मूल्य",
"dark_theme": "अंधेरा",
"debit_card": "डेबिट कार्ड",
"debit_card_terms": "इस डिजिटल वॉलेट में आपके भुगतान कार्ड नंबर (और आपके भुगतान कार्ड नंबर से संबंधित क्रेडेंशियल) का भंडारण और उपयोग भुगतान कार्ड जारीकर्ता के साथ लागू कार्डधारक समझौते के नियमों और शर्तों के अधीन है, जैसा कि प्रभावी है समय - समय पर।",
@ -190,6 +193,7 @@
"delete_wallet": "वॉलेट हटाएं",
"delete_wallet_confirm_message": "क्या आप वाकई ${wallet_name} वॉलेट हटाना चाहते हैं?",
"deleteConnectionConfirmationPrompt": "क्या आप वाकई कनेक्शन हटाना चाहते हैं?",
"denominations": "मूल्यवर्ग",
"descending": "अवरोही",
"description": "विवरण",
"destination_tag": "गंतव्य टैग:",
@ -277,6 +281,7 @@
"expired": "समय सीमा समाप्त",
"expires": "समाप्त हो जाता है",
"expiresOn": "पर समय सीमा समाप्त",
"expiry_and_validity": "समाप्ति और वैधता",
"export_backup": "निर्यात बैकअप",
"extra_id": "अतिरिक्त आईडी:",
"extracted_address_content": "आपको धनराशि भेजी जाएगी\n${recipient_name}",
@ -384,6 +389,7 @@
"new_template": "नया टेम्पलेट",
"new_wallet": "नया बटुआ",
"newConnection": "नया कनेक्शन",
"no_cards_found": "कोई कार्ड नहीं मिला",
"no_id_needed": "कोई आईडी नहीं चाहिए!",
"no_id_required": "कोई आईडी आवश्यक नहीं है। टॉप अप करें और कहीं भी खर्च करें",
"no_relay_on_domain": "उपयोगकर्ता के डोमेन के लिए कोई रिले नहीं है या रिले अनुपलब्ध है। कृपया उपयोग करने के लिए एक रिले चुनें।",
@ -453,6 +459,7 @@
"pre_seed_button_text": "मै समझता हुँ। मुझे अपना बीज दिखाओ",
"pre_seed_description": "अगले पेज पर आपको ${words} शब्दों की एक श्रृंखला दिखाई देगी। यह आपका अद्वितीय और निजी बीज है और नुकसान या खराबी के मामले में अपने बटुए को पुनर्प्राप्त करने का एकमात्र तरीका है। यह आपकी जिम्मेदारी है कि इसे नीचे लिखें और इसे Cake Wallet ऐप के बाहर सुरक्षित स्थान पर संग्रहीत करें।",
"pre_seed_title": "महत्वपूर्ण",
"prepaid_cards": "पूर्वदत्त कार्ड",
"prevent_screenshots": "स्क्रीनशॉट और स्क्रीन रिकॉर्डिंग रोकें",
"privacy": "गोपनीयता",
"privacy_policy": "गोपनीयता नीति",
@ -468,6 +475,7 @@
"purple_dark_theme": "पर्पल डार्क थीम",
"qr_fullscreen": "फ़ुल स्क्रीन क्यूआर कोड खोलने के लिए टैप करें",
"qr_payment_amount": "This QR code contains a payment amount. Do you want to overwrite the current value?",
"quantity": "मात्रा",
"question_to_disable_2fa": "क्या आप सुनिश्चित हैं कि आप Cake 2FA को अक्षम करना चाहते हैं? वॉलेट और कुछ कार्यों तक पहुँचने के लिए अब 2FA कोड की आवश्यकता नहीं होगी।",
"ready_have_account": "क्या आपके पास पहले से ही एक खाता है?",
"receivable_balance": "प्राप्य शेष",
@ -710,6 +718,7 @@
"tokenID": "पहचान",
"tor_connection": "टोर कनेक्शन",
"tor_only": "Tor केवल",
"total": "कुल",
"total_saving": "कुल बचत",
"totp_2fa_failure": "गलत कोड़। कृपया एक अलग कोड का प्रयास करें या एक नई गुप्त कुंजी उत्पन्न करें। 8-अंकीय कोड और SHA512 का समर्थन करने वाले संगत 2FA ऐप का उपयोग करें।",
"totp_2fa_success": "सफलता! इस वॉलेट के लिए Cake 2FA सक्षम है। यदि आप वॉलेट एक्सेस खो देते हैं तो अपने स्मरक बीज को सहेजना याद रखें।",
@ -800,6 +809,8 @@
"use_ssl": "उपयोग SSL",
"use_suggested": "सुझाए गए का प्रयोग करें",
"use_testnet": "टेस्टनेट का उपयोग करें",
"value": "कीमत",
"value_type": "मान प्रकार",
"variable_pair_not_supported": "यह परिवर्तनीय जोड़ी चयनित एक्सचेंजों के साथ समर्थित नहीं है",
"verification": "सत्यापन",
"verify_with_2fa": "केक 2FA के साथ सत्यापित करें",

View file

@ -87,6 +87,7 @@
"buy": "Kupi",
"buy_alert_content": "Trenutno podržavamo samo kupnju Bitcoina, Ethereuma, Litecoina i Monera. Izradite ili prijeđite na svoj Bitcoin, Ethereum, Litecoin ili Monero novčanik.",
"buy_bitcoin": "Kupite Bitcoin",
"buy_now": "Kupi sada",
"buy_provider_unavailable": "Davatelj trenutno nije dostupan.",
"buy_with": "Kupite s",
"by_cake_pay": "od Cake Paya",
@ -94,8 +95,7 @@
"cake_dark_theme": "TOKA DARKA TEMA",
"cake_pay_account_note": "Prijavite se samo s adresom e-pošte da biste vidjeli i kupili kartice. Neke su čak dostupne uz popust!",
"cake_pay_learn_more": "Azonnal vásárolhat és válthat be ajándékutalványokat az alkalmazásban!\nTovábbi információért csúsztassa balról jobbra az ujját.",
"cake_pay_subtitle": "Kupite darovne kartice s popustom (samo SAD)",
"cake_pay_title": "Cake Pay poklon kartice",
"cake_pay_subtitle": "Kupite svjetske unaprijed plaćene kartice i poklon kartice",
"cake_pay_web_cards_subtitle": "Kupujte prepaid kartice i poklon kartice diljem svijeta",
"cake_pay_web_cards_title": "Cake Pay Web kartice",
"cake_wallet": "Cake Wallet",
@ -123,6 +123,7 @@
"change_wallet_alert_title": "Izmijeni trenutni novčanik",
"choose_account": "Odaberi račun",
"choose_address": "\n\nOdaberite adresu:",
"choose_card_value": "Odaberite vrijednost kartice",
"choose_derivation": "Odaberite izvedbu novčanika",
"choose_from_available_options": "Odaberite neku od dostupnih opcija:",
"choose_one": "Izaberi jedan",
@ -166,6 +167,7 @@
"copy_address": "Kopiraj adresu",
"copy_id": "Kopirati ID",
"copyWalletConnectLink": "Kopirajte vezu WalletConnect iz dApp-a i zalijepite je ovdje",
"countries": "Zemalja",
"create_account": "Stvori račun",
"create_backup": "Stvori sigurnosnu kopiju",
"create_donation_link": "Izradi poveznicu za donaciju",
@ -178,6 +180,7 @@
"custom": "prilagođeno",
"custom_drag": "Prilagođeni (držite i povucite)",
"custom_redeem_amount": "Prilagođeni iznos otkupa",
"custom_value": "Prilagođena vrijednost",
"dark_theme": "Tamna",
"debit_card": "Debitna kartica",
"debit_card_terms": "Pohranjivanje i korištenje broja vaše platne kartice (i vjerodajnica koje odgovaraju broju vaše platne kartice) u ovom digitalnom novčaniku podliježu Uvjetima i odredbama važećeg ugovora vlasnika kartice s izdavateljem platne kartice, koji su na snazi od S vremena na vrijeme.",
@ -190,6 +193,7 @@
"delete_wallet": "Izbriši novčanik",
"delete_wallet_confirm_message": "Jeste li sigurni da želite izbrisati ${wallet_name} novčanik?",
"deleteConnectionConfirmationPrompt": "Jeste li sigurni da želite izbrisati vezu s",
"denominations": "Denominacije",
"descending": "Silazni",
"description": "Opis",
"destination_tag": "Odredišna oznaka:",
@ -277,6 +281,7 @@
"expired": "Isteklo",
"expires": "Ističe",
"expiresOn": "Istječe",
"expiry_and_validity": "Istek i valjanost",
"export_backup": "Izvezi sigurnosnu kopiju",
"extra_id": "Dodatni ID:",
"extracted_address_content": "Poslat ćete sredstva primatelju\n${recipient_name}",
@ -308,7 +313,7 @@
"gift_card_is_generated": "Poklon kartica je generirana",
"gift_card_number": "Broj darovne kartice",
"gift_card_redeemed_note": "Poklon kartice koje ste iskoristili pojavit će se ovdje",
"gift_cards": "Ajándékkártya",
"gift_cards": "Darovne kartice",
"gift_cards_unavailable": "Poklon kartice trenutno su dostupne za kupnju samo putem Monera, Bitcoina i Litecoina",
"got_it": "U redu",
"gross_balance": "Bruto bilanca",
@ -384,6 +389,7 @@
"new_template": "novi predložak",
"new_wallet": "Novi novčanik",
"newConnection": "Nova veza",
"no_cards_found": "Nisu pronađene kartice",
"no_id_needed": "Nije potreban ID!",
"no_id_required": "Nije potreban ID. Nadopunite i potrošite bilo gdje",
"no_relay_on_domain": "Ne postoji relej za korisničku domenu ili je relej nedostupan. Odaberite relej za korištenje.",
@ -452,6 +458,7 @@
"pre_seed_button_text": "Razumijem. Prikaži mi moj pristupni izraz",
"pre_seed_description": "Na sljedećoj ćete stranici vidjeti niz ${words} riječi. Radi se o Vašem jedinstvenom i tajnom pristupnom izrazu koji je ujedno i JEDINI način na koji možete oporaviti svoj novčanik u slučaju gubitka ili kvara. VAŠA je odgovornost zapisati ga te pohraniti na sigurno mjesto izvan Cake Wallet aplikacije.",
"pre_seed_title": "VAŽNO",
"prepaid_cards": "Unaprijed plaćene kartice",
"prevent_screenshots": "Spriječite snimke zaslona i snimanje zaslona",
"privacy": "Privatnost",
"privacy_policy": "Pravila privatnosti",
@ -467,6 +474,7 @@
"purple_dark_theme": "Ljubičasta tamna tema",
"qr_fullscreen": "Dodirnite za otvaranje QR koda preko cijelog zaslona",
"qr_payment_amount": "This QR code contains a payment amount. Do you want to overwrite the current value?",
"quantity": "Količina",
"question_to_disable_2fa": "Jeste li sigurni da želite onemogućiti Cake 2FA? 2FA kod više neće biti potreban za pristup novčaniku i određenim funkcijama.",
"receivable_balance": "Stanje potraživanja",
"receive": "Primi",
@ -708,6 +716,7 @@
"tokenID": "iskaznica",
"tor_connection": "Tor veza",
"tor_only": "Samo Tor",
"total": "Ukupno",
"total_saving": "Ukupna ušteda",
"totp_2fa_failure": "Neispravan kod. Pokušajte s drugim kodom ili generirajte novi tajni ključ. Koristite kompatibilnu 2FA aplikaciju koja podržava 8-znamenkasti kod i SHA512.",
"totp_2fa_success": "Uspjeh! Cake 2FA omogućen za ovaj novčanik. Ne zaboravite spremiti svoje mnemoničko sjeme u slučaju da izgubite pristup novčaniku.",
@ -798,6 +807,8 @@
"use_ssl": "Koristi SSL",
"use_suggested": "Koristite predloženo",
"use_testnet": "Koristite TestNet",
"value": "Vrijednost",
"value_type": "Tipa vrijednosti",
"variable_pair_not_supported": "Ovaj par varijabli nije podržan s odabranim burzama",
"verification": "Potvrda",
"verify_with_2fa": "Provjerite s Cake 2FA",

View file

@ -87,6 +87,7 @@
"buy": "Beli",
"buy_alert_content": "Saat ini kami hanya mendukung pembelian Bitcoin, Ethereum, Litecoin, dan Monero. Harap buat atau alihkan ke dompet Bitcoin, Ethereum, Litecoin, atau Monero Anda.",
"buy_bitcoin": "Beli Bitcoin",
"buy_now": "Beli sekarang",
"buy_provider_unavailable": "Penyedia saat ini tidak tersedia.",
"buy_with": "Beli dengan",
"by_cake_pay": "oleh Cake Pay",
@ -94,8 +95,7 @@
"cake_dark_theme": "Tema Kue Gelap",
"cake_pay_account_note": "Daftar hanya dengan alamat email untuk melihat dan membeli kartu. Beberapa di antaranya bahkan tersedia dengan diskon!",
"cake_pay_learn_more": "Beli dan tukar kartu hadiah secara instan di aplikasi!\nGeser ke kanan untuk informasi lebih lanjut.",
"cake_pay_subtitle": "Beli kartu hadiah dengan harga diskon (hanya USA)",
"cake_pay_title": "Kartu Hadiah Cake Pay",
"cake_pay_subtitle": "Beli kartu prabayar di seluruh dunia dan kartu hadiah",
"cake_pay_web_cards_subtitle": "Beli kartu prabayar dan kartu hadiah secara global",
"cake_pay_web_cards_title": "Kartu Web Cake Pay",
"cake_wallet": "Cake Wallet",
@ -123,6 +123,7 @@
"change_wallet_alert_title": "Ganti dompet saat ini",
"choose_account": "Pilih akun",
"choose_address": "\n\nSilakan pilih alamat:",
"choose_card_value": "Pilih nilai kartu",
"choose_derivation": "Pilih dompet dompet",
"choose_from_available_options": "Pilih dari pilihan yang tersedia:",
"choose_one": "Pilih satu",
@ -166,6 +167,7 @@
"copy_address": "Salin Alamat",
"copy_id": "Salin ID",
"copyWalletConnectLink": "Salin tautan WalletConnect dari dApp dan tempel di sini",
"countries": "Negara",
"create_account": "Buat Akun",
"create_backup": "Buat cadangan",
"create_donation_link": "Buat tautan donasi",
@ -178,6 +180,7 @@
"custom": "kustom",
"custom_drag": "Khusus (tahan dan seret)",
"custom_redeem_amount": "Jumlah Tukar Kustom",
"custom_value": "Nilai khusus",
"dark_theme": "Gelap",
"debit_card": "Kartu Debit",
"debit_card_terms": "Penyimpanan dan penggunaan nomor kartu pembayaran Anda (dan kredensial yang sesuai dengan nomor kartu pembayaran Anda) dalam dompet digital ini tertakluk pada Syarat dan Ketentuan persetujuan pemegang kartu yang berlaku dengan penerbit kartu pembayaran, seperti yang berlaku dari waktu ke waktu.",
@ -190,6 +193,7 @@
"delete_wallet": "Hapus dompet",
"delete_wallet_confirm_message": "Apakah Anda yakin ingin menghapus dompet ${wallet_name}?",
"deleteConnectionConfirmationPrompt": "Apakah Anda yakin ingin menghapus koneksi ke",
"denominations": "Denominasi",
"descending": "Menurun",
"description": "Keterangan",
"destination_tag": "Tag tujuan:",
@ -277,6 +281,7 @@
"expired": "Kedaluwarsa",
"expires": "Kadaluarsa",
"expiresOn": "Kadaluarsa pada",
"expiry_and_validity": "Kedaluwarsa dan validitas",
"export_backup": "Ekspor cadangan",
"extra_id": "ID tambahan:",
"extracted_address_content": "Anda akan mengirim dana ke\n${recipient_name}",
@ -308,7 +313,7 @@
"gift_card_is_generated": "Kartu Hadiah telah dibuat",
"gift_card_number": "Nomor Kartu Hadiah",
"gift_card_redeemed_note": "Kartu hadiah yang sudah Anda tukar akan muncul di sini",
"gift_cards": "Kartu Hadiah",
"gift_cards": "Kartu hadiah",
"gift_cards_unavailable": "Kartu hadiah hanya tersedia untuk dibeli dengan Monero, Bitcoin, dan Litecoin saat ini",
"got_it": "Sudah paham",
"gross_balance": "Saldo Kotor",
@ -384,6 +389,7 @@
"new_template": "Template Baru",
"new_wallet": "Dompet Baru",
"newConnection": "Koneksi Baru",
"no_cards_found": "Tidak ada kartu yang ditemukan",
"no_id_needed": "Tidak perlu ID!",
"no_id_required": "Tidak perlu ID. Isi ulang dan belanja di mana saja",
"no_relay_on_domain": "Tidak ada relai untuk domain pengguna atau relai tidak tersedia. Silakan pilih relai yang akan digunakan.",
@ -454,6 +460,7 @@
"pre_seed_button_text": "Saya mengerti. Tampilkan seed saya",
"pre_seed_description": "Di halaman berikutnya Anda akan melihat serangkaian kata ${words}. Ini adalah seed unik dan pribadi Anda dan itu SATU-SATUNYA cara untuk mengembalikan dompet Anda jika hilang atau rusak. Ini adalah TANGGUNG JAWAB Anda untuk menuliskannya dan menyimpan di tempat yang aman di luar aplikasi Cake Wallet.",
"pre_seed_title": "PENTING",
"prepaid_cards": "Kartu prabayar",
"prevent_screenshots": "Cegah tangkapan layar dan perekaman layar",
"privacy": "Privasi",
"privacy_policy": "Kebijakan Privasi",
@ -469,6 +476,7 @@
"purple_dark_theme": "Tema gelap ungu",
"qr_fullscreen": "Tap untuk membuka layar QR code penuh",
"qr_payment_amount": "QR code ini berisi jumlah pembayaran. Apakah Anda ingin menimpa nilai saat ini?",
"quantity": "Kuantitas",
"question_to_disable_2fa": "Apakah Anda yakin ingin menonaktifkan Cake 2FA? Kode 2FA tidak lagi diperlukan untuk mengakses dompet dan fungsi tertentu.",
"receivable_balance": "Saldo piutang",
"receive": "Menerima",
@ -711,6 +719,7 @@
"tokenID": "PENGENAL",
"tor_connection": "koneksi Tor",
"tor_only": "Hanya Tor",
"total": "Total",
"total_saving": "Total Pembayaran",
"totp_2fa_failure": "Kode salah. Silakan coba kode lain atau buat kunci rahasia baru. Gunakan aplikasi 2FA yang kompatibel yang mendukung kode 8 digit dan SHA512.",
"totp_2fa_success": "Kesuksesan! Cake 2FA diaktifkan untuk dompet ini. Ingatlah untuk menyimpan benih mnemonik Anda jika Anda kehilangan akses dompet.",
@ -801,6 +810,8 @@
"use_ssl": "Gunakan SSL",
"use_suggested": "Gunakan yang Disarankan",
"use_testnet": "Gunakan TestNet",
"value": "Nilai",
"value_type": "Jenis Nilai",
"variable_pair_not_supported": "Pasangan variabel ini tidak didukung dengan bursa yang dipilih",
"verification": "Verifikasi",
"verify_with_2fa": "Verifikasi dengan Cake 2FA",

View file

@ -87,6 +87,7 @@
"buy": "Comprare",
"buy_alert_content": "Attualmente supportiamo solo l'acquisto di Bitcoin, Ethereum, Litecoin e Monero. Crea o passa al tuo portafoglio Bitcoin, Ethereum, Litecoin o Monero.",
"buy_bitcoin": "Acquista Bitcoin",
"buy_now": "Acquista ora",
"buy_provider_unavailable": "Provider attualmente non disponibile.",
"buy_with": "Acquista con",
"by_cake_pay": "da Cake Pay",
@ -94,8 +95,7 @@
"cake_dark_theme": "Tema oscuro della torta",
"cake_pay_account_note": "Iscriviti con solo un indirizzo email per vedere e acquistare le carte. Alcune sono anche disponibili con uno sconto!",
"cake_pay_learn_more": "Acquista e riscatta istantaneamente carte regalo nell'app!\nScorri da sinistra a destra per saperne di più.",
"cake_pay_subtitle": "Acquista buoni regalo scontati (solo USA)",
"cake_pay_title": "Carte regalo Cake Pay",
"cake_pay_subtitle": "Acquista carte prepagate in tutto il mondo e carte regalo",
"cake_pay_web_cards_subtitle": "Acquista carte prepagate e carte regalo in tutto il mondo",
"cake_pay_web_cards_title": "Carte Web Cake Pay",
"cake_wallet": "Cake Wallet",
@ -123,6 +123,7 @@
"change_wallet_alert_title": "Cambia portafoglio attuale",
"choose_account": "Scegli account",
"choose_address": "\n\nSi prega di scegliere l'indirizzo:",
"choose_card_value": "Scegli un valore della carta",
"choose_derivation": "Scegli la derivazione del portafoglio",
"choose_from_available_options": "Scegli tra le opzioni disponibili:",
"choose_one": "Scegline uno",
@ -167,6 +168,7 @@
"copy_address": "Copia Indirizzo",
"copy_id": "Copia ID",
"copyWalletConnectLink": "Copia il collegamento WalletConnect dalla dApp e incollalo qui",
"countries": "Paesi",
"create_account": "Crea account",
"create_backup": "Crea backup",
"create_donation_link": "Crea un link per la donazione",
@ -179,6 +181,7 @@
"custom": "personalizzato",
"custom_drag": "Custom (Hold and Drag)",
"custom_redeem_amount": "Importo di riscatto personalizzato",
"custom_value": "Valore personalizzato",
"dark_theme": "Scuro",
"debit_card": "Carta di debito",
"debit_card_terms": "L'archiviazione e l'utilizzo del numero della carta di pagamento (e delle credenziali corrispondenti al numero della carta di pagamento) in questo portafoglio digitale sono soggetti ai Termini e condizioni del contratto applicabile con il titolare della carta con l'emittente della carta di pagamento, come in vigore da tempo al tempo.",
@ -191,6 +194,7 @@
"delete_wallet": "Elimina portafoglio",
"delete_wallet_confirm_message": "Sei sicuro di voler eliminare il portafoglio ${wallet_name}?",
"deleteConnectionConfirmationPrompt": "Sei sicuro di voler eliminare la connessione a",
"denominations": "Denominazioni",
"descending": "Discendente",
"description": "Descrizione",
"destination_tag": "Tag destinazione:",
@ -278,6 +282,7 @@
"expired": "Scaduta",
"expires": "Scade",
"expiresOn": "Scade il",
"expiry_and_validity": "Scadenza e validità",
"export_backup": "Esporta backup",
"extra_id": "Extra ID:",
"extracted_address_content": "Invierai i tuoi fondi a\n${recipient_name}",
@ -385,6 +390,7 @@
"new_template": "Nuovo modello",
"new_wallet": "Nuovo Portafoglio",
"newConnection": "Nuova connessione",
"no_cards_found": "Nessuna carta trovata",
"no_id_needed": "Nessun ID necessario!",
"no_id_required": "Nessun ID richiesto. Ricarica e spendi ovunque",
"no_relay_on_domain": "Non esiste un inoltro per il dominio dell'utente oppure l'inoltro non è disponibile. Scegli un relè da utilizzare.",
@ -454,6 +460,7 @@
"pre_seed_button_text": "Ho capito. Mostrami il seme",
"pre_seed_description": "Nella pagina seguente ti sarà mostrata una serie di parole ${words}. Questo è il tuo seme unico e privato ed è l'UNICO modo per recuperare il tuo portafoglio in caso di perdita o malfunzionamento. E' TUA responsabilità trascriverlo e conservarlo in un posto sicuro fuori dall'app Cake Wallet.",
"pre_seed_title": "IMPORTANTE",
"prepaid_cards": "Carte prepagata",
"prevent_screenshots": "Impedisci screenshot e registrazione dello schermo",
"privacy": "Privacy",
"privacy_policy": "Informativa sulla privacy",
@ -469,6 +476,7 @@
"purple_dark_theme": "Tema oscuro viola",
"qr_fullscreen": "Tocca per aprire il codice QR a schermo intero",
"qr_payment_amount": "Questo codice QR contiene l'ammontare del pagamento. Vuoi sovrascrivere il varlore attuale?",
"quantity": "Quantità",
"question_to_disable_2fa": "Sei sicuro di voler disabilitare Cake 2FA? Non sarà più necessario un codice 2FA per accedere al portafoglio e ad alcune funzioni.",
"receivable_balance": "Bilanciamento creditizio",
"receive": "Ricevi",
@ -710,6 +718,7 @@
"tokenID": "ID",
"tor_connection": "Connessione Tor",
"tor_only": "Solo Tor",
"total": "Totale",
"total_saving": "Risparmio totale",
"totp_2fa_failure": "Codice non corretto. Prova un codice diverso o genera una nuova chiave segreta. Utilizza un'app 2FA compatibile che supporti codici a 8 cifre e SHA512.",
"totp_2fa_success": "Successo! Cake 2FA abilitato per questo portafoglio. Ricordati di salvare il tuo seme mnemonico nel caso in cui perdi l'accesso al portafoglio.",
@ -800,6 +809,8 @@
"use_ssl": "Usa SSL",
"use_suggested": "Usa suggerito",
"use_testnet": "Usa TestNet",
"value": "Valore",
"value_type": "Tipo di valore",
"variable_pair_not_supported": "Questa coppia di variabili non è supportata con gli scambi selezionati",
"verification": "Verifica",
"verify_with_2fa": "Verifica con Cake 2FA",

View file

@ -87,6 +87,7 @@
"buy": "購入",
"buy_alert_content": "現在、ビットコイン、イーサリアム、ライトコイン、モネロの購入のみをサポートしています。ビットコイン、イーサリアム、ライトコイン、またはモネロのウォレットを作成するか、これらのウォレットに切り替えてください。",
"buy_bitcoin": "ビットコインを購入する",
"buy_now": "今すぐ購入",
"buy_provider_unavailable": "現在、プロバイダーは利用できません。",
"buy_with": "で購入",
"by_cake_pay": "by Cake Pay",
@ -94,8 +95,7 @@
"cake_dark_theme": "ケーキ暗いテーマ",
"cake_pay_account_note": "メールアドレスだけでサインアップして、カードを表示して購入できます。割引価格で利用できるカードもあります!",
"cake_pay_learn_more": "アプリですぐにギフトカードを購入して引き換えましょう!\n左から右にスワイプして詳細をご覧ください。",
"cake_pay_subtitle": "割引ギフトカードを購入する (米国のみ)",
"cake_pay_title": "ケーキペイギフトカード",
"cake_pay_subtitle": "世界中のプリペイドカードとギフトカードを購入します",
"cake_pay_web_cards_subtitle": "世界中のプリペイド カードとギフト カードを購入する",
"cake_pay_web_cards_title": "Cake Pay ウェブカード",
"cake_wallet": "Cake Wallet",
@ -123,6 +123,7 @@
"change_wallet_alert_title": "現在のウォレットを変更する",
"choose_account": "アカウントを選択",
"choose_address": "\n\n住所を選択してください",
"choose_card_value": "カード値を選択します",
"choose_derivation": "ウォレット派生を選択します",
"choose_from_available_options": "利用可能なオプションから選択してください:",
"choose_one": "1 つ選択してください",
@ -166,6 +167,7 @@
"copy_address": "住所をコピー",
"copy_id": "IDをコピー",
"copyWalletConnectLink": "dApp から WalletConnect リンクをコピーし、ここに貼り付けます",
"countries": "国",
"create_account": "アカウントの作成",
"create_backup": "バックアップを作成",
"create_donation_link": "寄付リンクを作成",
@ -178,6 +180,7 @@
"custom": "カスタム",
"custom_drag": "カスタム(ホールドとドラッグ)",
"custom_redeem_amount": "カスタム交換金額",
"custom_value": "カスタム値",
"dark_theme": "闇",
"debit_card": "デビットカード",
"debit_card_terms": "このデジタルウォレットでの支払いカード番号(および支払いカード番号に対応する資格情報)の保存と使用には、支払いカード発行者との該当するカード所有者契約の利用規約が適用されます。時々。",
@ -190,6 +193,7 @@
"delete_wallet": "ウォレットを削除",
"delete_wallet_confirm_message": "${wallet_name} ウォレットを削除してもよろしいですか?",
"deleteConnectionConfirmationPrompt": "への接続を削除してもよろしいですか?",
"denominations": "宗派",
"descending": "下降",
"description": "説明",
"destination_tag": "宛先タグ:",
@ -277,6 +281,7 @@
"expired": "期限切れ",
"expires": "Expires",
"expiresOn": "有効期限は次のとおりです",
"expiry_and_validity": "有効期限と有効性",
"export_backup": "バックアップのエクスポート",
"extra_id": "追加ID:",
"extracted_address_content": "に送金します\n${recipient_name}",
@ -385,6 +390,7 @@
"new_template": "新しいテンプレート",
"new_wallet": "新しいウォレット",
"newConnection": "新しい接続",
"no_cards_found": "カードは見つかりません",
"no_id_needed": "IDは必要ありません",
"no_id_required": "IDは必要ありません。どこにでも補充して使用できます",
"no_relay_on_domain": "ユーザーのドメインのリレーが存在しないか、リレーが使用できません。使用するリレーを選択してください。",
@ -453,6 +459,7 @@
"pre_seed_button_text": "わかります。 種を見せて",
"pre_seed_description": "次のページでは、一連の${words}語が表示されます。 これはあなたのユニークでプライベートなシードであり、紛失や誤動作が発生した場合にウォレットを回復する唯一の方法です。 それを書き留めて、Cake Wallet アプリの外の安全な場所に保管するのはあなたの責任です。",
"pre_seed_title": "重要",
"prepaid_cards": "プリペイドカード",
"prevent_screenshots": "スクリーンショットと画面録画を防止する",
"privacy": "プライバシー",
"privacy_policy": "プライバシーポリシー",
@ -468,6 +475,7 @@
"purple_dark_theme": "紫色の暗いテーマ",
"qr_fullscreen": "タップして全画面QRコードを開く",
"qr_payment_amount": "This QR code contains a payment amount. Do you want to overwrite the current value?",
"quantity": "量",
"question_to_disable_2fa": "Cake 2FA を無効にしてもよろしいですか?ウォレットと特定の機能にアクセスするために 2FA コードは必要なくなります。",
"receivable_balance": "売掛金残高",
"receive": "受け取る",
@ -709,6 +717,7 @@
"tokenID": "ID",
"tor_connection": "Tor接続",
"tor_only": "Torのみ",
"total": "合計",
"total_saving": "合計節約額",
"totp_2fa_failure": "コードが正しくありません。 別のコードを試すか、新しい秘密鍵を生成してください。 8 桁のコードと SHA512 をサポートする互換性のある 2FA アプリを使用してください。",
"totp_2fa_success": "成功!このウォレットでは Cake 2FA が有効になっています。ウォレットへのアクセスを失った場合に備えて、ニーモニック シードを忘れずに保存してください。",
@ -799,6 +808,8 @@
"use_ssl": "SSLを使用する",
"use_suggested": "推奨を使用",
"use_testnet": "テストネットを使用します",
"value": "価値",
"value_type": "値タイプ",
"variable_pair_not_supported": "この変数ペアは、選択した取引所ではサポートされていません",
"verification": "検証",
"verify_with_2fa": "Cake 2FA で検証する",

View file

@ -87,6 +87,7 @@
"buy": "구입",
"buy_alert_content": "현재 Bitcoin, Ethereum, Litecoin 및 Monero 구매만 지원합니다. Bitcoin, Ethereum, Litecoin 또는 Monero 지갑을 생성하거나 전환하십시오.",
"buy_bitcoin": "비트 코인 구매",
"buy_now": "지금 구매하십시오",
"buy_provider_unavailable": "제공자는 현재 사용할 수 없습니다.",
"buy_with": "구매",
"by_cake_pay": "Cake Pay로",
@ -94,8 +95,7 @@
"cake_dark_theme": "케이크 다크 테마",
"cake_pay_account_note": "이메일 주소로 가입하면 카드를 보고 구매할 수 있습니다. 일부는 할인된 가격으로 사용 가능합니다!",
"cake_pay_learn_more": "앱에서 즉시 기프트 카드를 구매하고 사용하세요!\n자세히 알아보려면 왼쪽에서 오른쪽으로 스와이프하세요.",
"cake_pay_subtitle": "할인된 기프트 카드 구매(미국만 해당)",
"cake_pay_title": "케이크 페이 기프트 카드",
"cake_pay_subtitle": "전세계 선불 카드와 기프트 카드를 구입하십시오",
"cake_pay_web_cards_subtitle": "전 세계 선불 카드 및 기프트 카드 구매",
"cake_pay_web_cards_title": "케이크페이 웹카드",
"cake_wallet": "Cake Wallet",
@ -123,6 +123,7 @@
"change_wallet_alert_title": "현재 지갑 변경",
"choose_account": "계정을 선택하십시오",
"choose_address": "\n\n주소를 선택하십시오:",
"choose_card_value": "카드 값을 선택하십시오",
"choose_derivation": "지갑 파생을 선택하십시오",
"choose_from_available_options": "사용 가능한 옵션에서 선택:",
"choose_one": "하나 선택",
@ -166,6 +167,7 @@
"copy_address": "주소 복사",
"copy_id": "부 ID",
"copyWalletConnectLink": "dApp에서 WalletConnect 링크를 복사하여 여기에 붙여넣으세요.",
"countries": "국가",
"create_account": "계정 만들기",
"create_backup": "백업 생성",
"create_donation_link": "기부 링크 만들기",
@ -178,6 +180,7 @@
"custom": "커스텀",
"custom_drag": "사용자 정의 (홀드 앤 드래그)",
"custom_redeem_amount": "사용자 지정 상환 금액",
"custom_value": "맞춤 가치",
"dark_theme": "어두운",
"debit_card": "직불 카드",
"debit_card_terms": "이 디지털 지갑에 있는 귀하의 지불 카드 번호(및 귀하의 지불 카드 번호에 해당하는 자격 증명)의 저장 및 사용은 부터 발효되는 지불 카드 발행자와의 해당 카드 소지자 계약의 이용 약관을 따릅니다. 수시로.",
@ -190,6 +193,7 @@
"delete_wallet": "지갑 삭제",
"delete_wallet_confirm_message": "${wallet_name} 지갑을 삭제하시겠습니까?",
"deleteConnectionConfirmationPrompt": "다음 연결을 삭제하시겠습니까?",
"denominations": "교파",
"descending": "내림차순",
"description": "설명",
"destination_tag": "목적지 태그:",
@ -277,6 +281,7 @@
"expired": "만료",
"expires": "만료",
"expiresOn": "만료 날짜",
"expiry_and_validity": "만료와 타당성",
"export_backup": "백업 내보내기",
"extra_id": "추가 ID:",
"extracted_address_content": "당신은에 자금을 보낼 것입니다\n${recipient_name}",
@ -384,6 +389,7 @@
"new_template": "새 템플릿",
"new_wallet": "새 월렛",
"newConnection": "새로운 연결",
"no_cards_found": "카드를 찾지 못했습니다",
"no_id_needed": "ID가 필요하지 않습니다!",
"no_id_required": "신분증이 필요하지 않습니다. 충전하고 어디에서나 사용하세요",
"no_relay_on_domain": "사용자 도메인에 릴레이가 없거나 릴레이를 사용할 수 없습니다. 사용할 릴레이를 선택해주세요.",
@ -453,6 +459,7 @@
"pre_seed_button_text": "이해 했어요. 내 씨앗을 보여줘",
"pre_seed_description": "다음 페이지에서 ${words} 개의 단어를 볼 수 있습니다. 이것은 귀하의 고유하고 개인적인 시드이며 분실 또는 오작동시 지갑을 복구하는 유일한 방법입니다. 기록해두고 Cake Wallet 앱 외부의 안전한 장소에 보관하는 것은 귀하의 책임입니다.",
"pre_seed_title": "중대한",
"prepaid_cards": "선불 카드",
"prevent_screenshots": "스크린샷 및 화면 녹화 방지",
"privacy": "프라이버시",
"privacy_policy": "개인 정보 보호 정책",
@ -468,6 +475,7 @@
"purple_dark_theme": "보라색 어두운 테마",
"qr_fullscreen": "전체 화면 QR 코드를 열려면 탭하세요.",
"qr_payment_amount": "This QR code contains a payment amount. Do you want to overwrite the current value?",
"quantity": "수량",
"question_to_disable_2fa": "Cake 2FA를 비활성화하시겠습니까? 지갑 및 특정 기능에 액세스하는 데 더 이상 2FA 코드가 필요하지 않습니다.",
"receivable_balance": "채권 잔액",
"receive": "받다",
@ -709,6 +717,7 @@
"tokenID": "ID",
"tor_connection": "토르 연결",
"tor_only": "Tor 뿐",
"total": "총",
"total_saving": "총 절감액",
"totp_2fa_failure": "잘못된 코드입니다. 다른 코드를 시도하거나 새 비밀 키를 생성하십시오. 8자리 코드와 SHA512를 지원하는 호환되는 2FA 앱을 사용하세요.",
"totp_2fa_success": "성공! 이 지갑에 케이크 2FA가 활성화되었습니다. 지갑 액세스 권한을 잃을 경우를 대비하여 니모닉 시드를 저장하는 것을 잊지 마십시오.",
@ -799,6 +808,8 @@
"use_ssl": "SSL 사용",
"use_suggested": "추천 사용",
"use_testnet": "TestNet을 사용하십시오",
"value": "값",
"value_type": "가치 유형",
"variable_pair_not_supported": "이 변수 쌍은 선택한 교환에서 지원되지 않습니다.",
"verification": "검증",
"verify_with_2fa": "케이크 2FA로 확인",

View file

@ -87,6 +87,7 @@
"buy": "ဝယ်ပါ။",
"buy_alert_content": "လက်ရှိတွင် ကျွန်ုပ်တို့သည် Bitcoin၊ Ethereum၊ Litecoin နှင့် Monero တို့ကိုသာ ဝယ်ယူမှုကို ပံ့ပိုးပေးပါသည်။ သင်၏ Bitcoin၊ Ethereum၊ Litecoin သို့မဟုတ် Monero ပိုက်ဆံအိတ်ကို ဖန်တီးပါ သို့မဟုတ် ပြောင်းပါ။",
"buy_bitcoin": "Bitcoin ကိုဝယ်ပါ။",
"buy_now": "အခုဝယ်ပါ",
"buy_provider_unavailable": "လက်ရှိတွင်လက်ရှိမရနိုင်ပါ။",
"buy_with": "အတူဝယ်ပါ။",
"by_cake_pay": "Cake Pay ဖြင့်",
@ -94,8 +95,7 @@
"cake_dark_theme": "ကိတ်မုန့် Dark Theme",
"cake_pay_account_note": "ကတ်များကြည့်ရှုဝယ်ယူရန် အီးမေးလ်လိပ်စာတစ်ခုဖြင့် စာရင်းသွင်းပါ။ အချို့ကို လျှော့ဈေးဖြင့်ပင် ရနိုင်သည်။",
"cake_pay_learn_more": "အက်ပ်ရှိ လက်ဆောင်ကတ်များကို ချက်ချင်းဝယ်ယူပြီး ကူပွန်ဖြင့် လဲလှယ်ပါ။\nပိုမိုလေ့လာရန် ဘယ်မှညာသို့ ပွတ်ဆွဲပါ။",
"cake_pay_subtitle": "လျှော့စျေးလက်ဆောင်ကတ်များဝယ်ပါ (USA သာ)",
"cake_pay_title": "ကိတ်မုန့်လက်ဆောင်ကတ်များ",
"cake_pay_subtitle": "Worldwide ကြိုတင်ငွေဖြည့်ကဒ်များနှင့်လက်ဆောင်ကဒ်များကို 0 ယ်ပါ",
"cake_pay_web_cards_subtitle": "ကမ္ဘာတစ်ဝှမ်း ကြိုတင်ငွေပေးကတ်များနှင့် လက်ဆောင်ကတ်များကို ဝယ်ယူပါ။",
"cake_pay_web_cards_title": "Cake Pay ဝဘ်ကတ်များ",
"cake_wallet": "Cake ပိုက်ဆံအိတ်",
@ -123,6 +123,7 @@
"change_wallet_alert_title": "လက်ရှိပိုက်ဆံအိတ်ကို ပြောင်းပါ။",
"choose_account": "အကောင့်ကို ရွေးပါ။",
"choose_address": "\n\nလိပ်စာကို ရွေးပါ-",
"choose_card_value": "ကဒ်တန်ဖိုးတစ်ခုရွေးပါ",
"choose_derivation": "ပိုက်ဆံအိတ်ကိုရွေးချယ်ပါ",
"choose_from_available_options": "ရနိုင်သောရွေးချယ်မှုများမှ ရွေးပါ-",
"choose_one": "တစ်ခုရွေးပါ။",
@ -166,6 +167,7 @@
"copy_address": "လိပ်စာကို ကူးယူပါ။",
"copy_id": "ID ကူးယူပါ။",
"copyWalletConnectLink": "dApp မှ WalletConnect လင့်ခ်ကို ကူးယူပြီး ဤနေရာတွင် ကူးထည့်ပါ။",
"countries": "နိုင်ငံများ",
"create_account": "အကောင့်ပြုလုပ်ပါ",
"create_backup": "အရန်သိမ်းခြင်းကို ဖန်တီးပါ။",
"create_donation_link": "လှူဒါန်းမှုလင့်ခ်ကို ဖန်တီးပါ။",
@ -178,6 +180,7 @@
"custom": "စိတ်ကြိုက်",
"custom_drag": "စိတ်ကြိုက် (Drag)",
"custom_redeem_amount": "စိတ်ကြိုက်သုံးငွေပမာဏ",
"custom_value": "စိတ်ကြိုက်တန်ဖိုး",
"dark_theme": "မှောငျမိုကျသော",
"debit_card": "ဒက်ဘစ်ကတ်",
"debit_card_terms": "ဤဒစ်ဂျစ်တယ်ပိုက်ဆံအိတ်ရှိ သင့်ငွေပေးချေမှုကတ်နံပါတ် (နှင့် သင့်ငွေပေးချေကတ်နံပါတ်နှင့် သက်ဆိုင်သောအထောက်အထားများ) ၏ သိုလှောင်မှုနှင့် အသုံးပြုမှုသည် အချိန်အခါနှင့်အမျှ သက်ရောက်မှုရှိသကဲ့သို့ ကတ်ကိုင်ဆောင်ထားသူ၏ သဘောတူညီချက်၏ စည်းကမ်းသတ်မှတ်ချက်များနှင့် ကိုက်ညီပါသည်။",
@ -190,6 +193,7 @@
"delete_wallet": "ပိုက်ဆံအိတ်ကို ဖျက်ပါ။",
"delete_wallet_confirm_message": "${wallet_name} ပိုက်ဆံအိတ်ကို ဖျက်လိုသည်မှာ သေချာပါသလား။",
"deleteConnectionConfirmationPrompt": "ချိတ်ဆက်မှုကို ဖျက်လိုသည်မှာ သေချာပါသလား။",
"denominations": "ဂိုဏ်းချုပ်ပစ္စည်းများ",
"descending": "ဆင်း",
"description": "ဖော်ပြချက်",
"destination_tag": "ခရီးဆုံးအမှတ်-",
@ -277,6 +281,7 @@
"expired": "သက်တမ်းကုန်သွားပြီ",
"expires": "သက်တမ်းကုန်သည်။",
"expiresOn": "သက်တမ်းကုန်သည်။",
"expiry_and_validity": "သက်တမ်းကုန်ဆုံးခြင်းနှင့်တရားဝင်မှု",
"export_backup": "အရန်ကူးထုတ်ရန်",
"extra_id": "အပို ID-",
"extracted_address_content": "သင်သည် \n${recipient_name} သို့ ရန်ပုံငွေများ ပေးပို့ပါမည်",
@ -384,6 +389,7 @@
"new_template": "ပုံစံအသစ်",
"new_wallet": "ပိုက်ဆံအိတ်အသစ်",
"newConnection": "ချိတ်ဆက်မှုအသစ်",
"no_cards_found": "ကဒ်များမရှိပါ",
"no_id_needed": "ID မလိုအပ်ပါ။",
"no_id_required": "ID မလိုအပ်ပါ။ ငွေဖြည့်ပြီး ဘယ်နေရာမဆို သုံးစွဲပါ။",
"no_relay_on_domain": "အသုံးပြုသူ၏ဒိုမိန်းအတွက် ထပ်ဆင့်လွှင့်ခြင်း မရှိပါ သို့မဟုတ် ထပ်ဆင့်လွှင့်ခြင်း မရနိုင်ပါ။ အသုံးပြုရန် relay ကိုရွေးချယ်ပါ။",
@ -452,6 +458,7 @@
"pre_seed_button_text": "ကျွန်တော်နားလည်ပါတယ်။ ငါ့အမျိုးအနွယ်ကို ပြလော့",
"pre_seed_description": "နောက်စာမျက်နှာတွင် ${words} စကားလုံးများ အတွဲလိုက်ကို တွေ့ရပါမည်။ ၎င်းသည် သင်၏ထူးခြားပြီး သီးသန့်မျိုးစေ့ဖြစ်ပြီး ပျောက်ဆုံးခြင်း သို့မဟုတ် ချွတ်ယွင်းမှုရှိပါက သင့်ပိုက်ဆံအိတ်ကို ပြန်လည်ရယူရန် တစ်ခုတည်းသောနည်းလမ်းဖြစ်သည်။ ၎င်းကို Cake Wallet အက်ပ်၏အပြင်ဘက်တွင် လုံခြုံသောနေရာတွင် သိမ်းဆည်းရန်မှာ သင်၏တာဝန်ဖြစ်သည်။",
"pre_seed_title": "အရေးကြီးသည်။",
"prepaid_cards": "ကြိုတင်ငွေဖြည့်ကဒ်များ",
"prevent_screenshots": "ဖန်သားပြင်ဓာတ်ပုံများနှင့် မျက်နှာပြင်ရိုက်ကူးခြင်းကို တားဆီးပါ။",
"privacy": "ကိုယ်ရေးကိုယ်တာ",
"privacy_policy": "ကိုယ်ရေးအချက်အလက်မူဝါဒ",
@ -467,6 +474,7 @@
"purple_dark_theme": "ခရမ်းရောင် Drwing Theme",
"qr_fullscreen": "မျက်နှာပြင်အပြည့် QR ကုဒ်ကိုဖွင့်ရန် တို့ပါ။",
"qr_payment_amount": "ဤ QR ကုဒ်တွင် ငွေပေးချေမှုပမာဏတစ်ခုပါရှိသည်။ လက်ရှိတန်ဖိုးကို ထပ်ရေးလိုပါသလား။",
"quantity": "အရေအတွက်",
"question_to_disable_2fa": "Cake 2FA ကို ပိတ်လိုသည်မှာ သေချာပါသလား။ ပိုက်ဆံအိတ်နှင့် အချို့သောလုပ်ဆောင်ချက်များကို အသုံးပြုရန်အတွက် 2FA ကုဒ်တစ်ခု မလိုအပ်တော့ပါ။",
"receivable_balance": "လက်ကျန်ငွေ",
"receive": "လက်ခံသည်။",
@ -708,6 +716,7 @@
"tokenID": "အမှတ်သညာ",
"tor_connection": "Tor ချိတ်ဆက်မှု",
"tor_only": "Tor သာ",
"total": "လုံးဝသော",
"total_saving": "စုစုပေါင်းစုဆောင်းငွေ",
"totp_2fa_failure": "ကုဒ်မမှန်ပါ။ ကျေးဇူးပြု၍ အခြားကုဒ်တစ်ခုကို စမ်းကြည့်ပါ သို့မဟုတ် လျှို့ဝှက်သော့အသစ်တစ်ခု ဖန်တီးပါ။ ဂဏန်း ၈ လုံးကုဒ်များနှင့် SHA512 ကို ပံ့ပိုးပေးသည့် တွဲဖက်အသုံးပြုနိုင်သော 2FA အက်ပ်ကို အသုံးပြုပါ။",
"totp_2fa_success": "အောင်မြင် ဤပိုက်ဆံအိတ်အတွက် ကိတ်မုန့် 2FA ကို ဖွင့်ထားသည်။ ပိုက်ဆံအိတ်ဝင်ရောက်ခွင့်ဆုံးရှုံးသွားသောအခါတွင် သင်၏ mnemonic မျိုးစေ့များကို သိမ်းဆည်းရန် မမေ့ပါနှင့်။",
@ -798,6 +807,8 @@
"use_ssl": "SSL ကိုသုံးပါ။",
"use_suggested": "အကြံပြုထားသည်ကို အသုံးပြုပါ။",
"use_testnet": "testnet ကိုသုံးပါ",
"value": "အဘိုး",
"value_type": "Value အမျိုးအစား",
"variable_pair_not_supported": "ရွေးချယ်ထားသော ဖလှယ်မှုများဖြင့် ဤပြောင်းလဲနိုင်သောအတွဲကို ပံ့ပိုးမထားပါ။",
"verification": "စိစစ်ခြင်း။",
"verify_with_2fa": "Cake 2FA ဖြင့် စစ်ဆေးပါ။",

View file

@ -87,6 +87,7 @@
"buy": "Kopen",
"buy_alert_content": "Momenteel ondersteunen we alleen de aankoop van Bitcoin, Ethereum, Litecoin en Monero. Maak of schakel over naar uw Bitcoin-, Ethereum-, Litecoin- of Monero-portemonnee.",
"buy_bitcoin": "Koop Bitcoin",
"buy_now": "Koop nu",
"buy_provider_unavailable": "Provider momenteel niet beschikbaar.",
"buy_with": "Koop met",
"by_cake_pay": "door Cake Pay",
@ -94,8 +95,7 @@
"cake_dark_theme": "Cake Dark Theme",
"cake_pay_account_note": "Meld u aan met alleen een e-mailadres om kaarten te bekijken en te kopen. Sommige zijn zelfs met korting verkrijgbaar!",
"cake_pay_learn_more": "Koop en wissel cadeaubonnen direct in de app in!\nSwipe van links naar rechts voor meer informatie.",
"cake_pay_subtitle": "Koop cadeaubonnen met korting (alleen VS)",
"cake_pay_title": "Cake Pay-cadeaubonnen",
"cake_pay_subtitle": "Koop wereldwijde prepaid -kaarten en cadeaubonnen",
"cake_pay_web_cards_subtitle": "Koop wereldwijd prepaidkaarten en cadeaubonnen",
"cake_pay_web_cards_title": "Cake Pay-webkaarten",
"cake_wallet": "Cake Wallet",
@ -123,6 +123,7 @@
"change_wallet_alert_title": "Wijzig huidige portemonnee",
"choose_account": "Kies account",
"choose_address": "\n\nKies het adres:",
"choose_card_value": "Kies een kaartwaarde",
"choose_derivation": "Kies portemonnee -afleiding",
"choose_from_available_options": "Kies uit de beschikbare opties:",
"choose_one": "Kies er een",
@ -166,6 +167,7 @@
"copy_address": "Adres kopiëren",
"copy_id": "ID kopiëren",
"copyWalletConnectLink": "Kopieer de WalletConnect-link van dApp en plak deze hier",
"countries": "Landen",
"create_account": "Account aanmaken",
"create_backup": "Maak een back-up",
"create_donation_link": "Maak een donatielink aan",
@ -178,6 +180,7 @@
"custom": "aangepast",
"custom_drag": "Custom (vasthouden en slepen)",
"custom_redeem_amount": "Aangepast inwisselbedrag",
"custom_value": "Aangepaste waarde",
"dark_theme": "Donker",
"debit_card": "Debetkaart",
"debit_card_terms": "De opslag en het gebruik van uw betaalkaartnummer (en inloggegevens die overeenkomen met uw betaalkaartnummer) in deze digitale portemonnee zijn onderworpen aan de Algemene voorwaarden van de toepasselijke kaarthouderovereenkomst met de uitgever van de betaalkaart, zoals van kracht vanaf tijd tot tijd.",
@ -190,6 +193,7 @@
"delete_wallet": "Portemonnee verwijderen",
"delete_wallet_confirm_message": "Weet u zeker dat u de portemonnee van ${wallet_name} wilt verwijderen?",
"deleteConnectionConfirmationPrompt": "Weet u zeker dat u de verbinding met",
"denominations": "Denominaties",
"descending": "Aflopend",
"description": "Beschrijving",
"destination_tag": "Bestemmingstag:",
@ -277,6 +281,7 @@
"expired": "Verlopen",
"expires": "Verloopt",
"expiresOn": "Verloopt op",
"expiry_and_validity": "Vervallen en geldigheid",
"export_backup": "Back-up exporteren",
"extra_id": "Extra ID:",
"extracted_address_content": "U stuurt geld naar\n${recipient_name}",
@ -384,6 +389,7 @@
"new_template": "Nieuwe sjabloon",
"new_wallet": "Nieuwe portemonnee",
"newConnection": "Nieuwe verbinding",
"no_cards_found": "Geen kaarten gevonden",
"no_id_needed": "Geen ID nodig!",
"no_id_required": "Geen ID vereist. Opwaarderen en overal uitgeven",
"no_relay_on_domain": "Er is geen relay voor het domein van de gebruiker of de relay is niet beschikbaar. Kies een relais dat u wilt gebruiken.",
@ -452,6 +458,7 @@
"pre_seed_button_text": "Ik begrijp het. Laat me mijn zaad zien",
"pre_seed_description": "Op de volgende pagina ziet u een reeks van ${words} woorden. Dit is uw unieke en persoonlijke zaadje en het is de ENIGE manier om uw portemonnee te herstellen in geval van verlies of storing. Het is JOUW verantwoordelijkheid om het op te schrijven en op een veilige plaats op te slaan buiten de Cake Wallet app.",
"pre_seed_title": "BELANGRIJK",
"prepaid_cards": "Prepaid-kaarten",
"prevent_screenshots": "Voorkom screenshots en schermopname",
"privacy": "Privacy",
"privacy_policy": "Privacybeleid",
@ -467,6 +474,7 @@
"purple_dark_theme": "Paars donker thema",
"qr_fullscreen": "Tik om de QR-code op volledig scherm te openen",
"qr_payment_amount": "This QR code contains a payment amount. Do you want to overwrite the current value?",
"quantity": "Hoeveelheid",
"question_to_disable_2fa": "Weet je zeker dat je Cake 2FA wilt uitschakelen? Er is geen 2FA-code meer nodig om toegang te krijgen tot de portemonnee en bepaalde functies.",
"receivable_balance": "Het saldo",
"receive": "Krijgen",
@ -708,6 +716,7 @@
"tokenID": "ID kaart",
"tor_connection": "Tor-verbinding",
"tor_only": "Alleen Tor",
"total": "Totaal",
"total_saving": "Totale besparingen",
"totp_2fa_failure": "Foute code. Probeer een andere code of genereer een nieuwe geheime sleutel. Gebruik een compatibele 2FA-app die 8-cijferige codes en SHA512 ondersteunt.",
"totp_2fa_success": "Succes! Cake 2FA ingeschakeld voor deze portemonnee. Vergeet niet om uw geheugensteuntje op te slaan voor het geval u de toegang tot de portemonnee kwijtraakt.",
@ -798,6 +807,8 @@
"use_ssl": "Gebruik SSL",
"use_suggested": "Gebruik aanbevolen",
"use_testnet": "Gebruik testnet",
"value": "Waarde",
"value_type": "Waarde type",
"variable_pair_not_supported": "Dit variabelenpaar wordt niet ondersteund met de geselecteerde uitwisselingen",
"verification": "Verificatie",
"verify_with_2fa": "Controleer met Cake 2FA",

View file

@ -87,6 +87,7 @@
"buy": "Kup",
"buy_alert_content": "Obecnie obsługujemy tylko zakup Bitcoin, Ethereum, Litecoin i Monero. Utwórz lub przełącz się na swój portfel Bitcoin, Ethereum, Litecoin lub Monero.",
"buy_bitcoin": "Kup Bitcoin",
"buy_now": "Kup Teraz",
"buy_provider_unavailable": "Dostawca obecnie niedostępny.",
"buy_with": "Kup za pomocą",
"by_cake_pay": "przez Cake Pay",
@ -94,8 +95,7 @@
"cake_dark_theme": "Cake Dark Temat",
"cake_pay_account_note": "Zarejestruj się, używając tylko adresu e-mail, aby przeglądać i kupować karty. Niektóre są nawet dostępne ze zniżką!",
"cake_pay_learn_more": "Kupuj i wykorzystuj karty podarunkowe od razu w aplikacji!\nPrzesuń od lewej do prawej, aby dowiedzieć się więcej.",
"cake_pay_subtitle": "Kup karty upominkowe ze zniżką (tylko USA)",
"cake_pay_title": "Karty podarunkowe Cake Pay",
"cake_pay_subtitle": "Kup na całym świecie karty przedpłacone i karty podarunkowe",
"cake_pay_web_cards_subtitle": "Kupuj na całym świecie karty przedpłacone i karty podarunkowe",
"cake_pay_web_cards_title": "Cake Pay Web Cards",
"cake_wallet": "Cake Wallet",
@ -123,6 +123,7 @@
"change_wallet_alert_title": "Zmień obecny portfel",
"choose_account": "Wybierz konto",
"choose_address": "\n\nWybierz adres:",
"choose_card_value": "Wybierz wartość karty",
"choose_derivation": "Wybierz wyprowadzenie portfela",
"choose_from_available_options": "Wybierz z dostępnych opcji:",
"choose_one": "Wybierz jeden",
@ -166,6 +167,7 @@
"copy_address": "Skopiuj adress",
"copy_id": "skopiuj ID",
"copyWalletConnectLink": "Skopiuj link do WalletConnect z dApp i wklej tutaj",
"countries": "Kraje",
"create_account": "Utwórz konto",
"create_backup": "Utwórz kopię zapasową",
"create_donation_link": "Utwórz link do darowizny",
@ -178,6 +180,7 @@
"custom": "niestandardowy",
"custom_drag": "Niestandardowe (trzymaj i przeciągnij)",
"custom_redeem_amount": "Niestandardowa kwota wykorzystania",
"custom_value": "Wartość niestandardowa",
"dark_theme": "Ciemny",
"debit_card": "Karta debetowa",
"debit_card_terms": "Przechowywanie i używanie numeru karty płatniczej (oraz danych uwierzytelniających odpowiadających numerowi karty płatniczej) w tym portfelu cyfrowym podlega Warunkom odpowiedniej umowy posiadacza karty z wydawcą karty płatniczej, zgodnie z obowiązującym od od czasu do czasu.",
@ -190,6 +193,7 @@
"delete_wallet": "Usuń portfel",
"delete_wallet_confirm_message": "Czy na pewno chcesz usunąć portfel ${wallet_name}?",
"deleteConnectionConfirmationPrompt": "Czy na pewno chcesz usunąć połączenie z",
"denominations": "Wyznaczenia",
"descending": "Schodzenie",
"description": "Opis",
"destination_tag": "Tag docelowy:",
@ -277,6 +281,7 @@
"expired": "Przedawniony",
"expires": "Wygasa",
"expiresOn": "Upływa w dniu",
"expiry_and_validity": "Wygaśnięcie i ważność",
"export_backup": "Eksportuj kopię zapasową",
"extra_id": "Dodatkowy ID:",
"extracted_address_content": "Wysyłasz środki na\n${recipient_name}",
@ -384,6 +389,7 @@
"new_template": "Nowy szablon",
"new_wallet": "Nowy portfel",
"newConnection": "Nowe połączenie",
"no_cards_found": "Nie znaleziono żadnych kart",
"no_id_needed": "Nie potrzeba Dowodu!",
"no_id_required": "Nie wymagamy Dowodu. Doładuj i wydawaj gdziekolwiek",
"no_relay_on_domain": "Brak przekaźnika dla domeny użytkownika lub przekaźnik jest niedostępny. Wybierz przekaźnik, którego chcesz użyć.",
@ -452,6 +458,7 @@
"pre_seed_button_text": "Rozumiem. Pokaż mi moją fraze seed",
"pre_seed_description": "Na następnej stronie zobaczysz serię ${words} słów. To jest Twoja unikalna i prywatna fraza seed i jest to JEDYNY sposób na odzyskanie portfela w przypadku utraty lub awarii telefonu. Twoim obowiązkiem jest zapisanie go i przechowywanie w bezpiecznym miejscu (np. na kartce w SEJFIE).",
"pre_seed_title": "WAŻNY",
"prepaid_cards": "Karty przedpłacone",
"prevent_screenshots": "Zapobiegaj zrzutom ekranu i nagrywaniu ekranu",
"privacy": "Prywatność",
"privacy_policy": "Polityka prywatności",
@ -467,6 +474,7 @@
"purple_dark_theme": "Purple Dark Temat",
"qr_fullscreen": "Dotknij, aby otworzyć pełnoekranowy kod QR",
"qr_payment_amount": "Ten kod QR zawiera kwotę do zapłaty. Czy chcesz nadpisać obecną wartość?",
"quantity": "Ilość",
"question_to_disable_2fa": "Czy na pewno chcesz wyłączyć Cake 2FA? Kod 2FA nie będzie już potrzebny do uzyskania dostępu do portfela i niektórych funkcji.",
"receivable_balance": "Saldo należności",
"receive": "Otrzymaj",
@ -708,6 +716,7 @@
"tokenID": "ID",
"tor_connection": "Połączenie Torem",
"tor_only": "Tylko sieć Tor",
"total": "Całkowity",
"total_saving": "Całkowite oszczędności",
"totp_2fa_failure": "Błędny kod. Spróbuj użyć innego kodu lub wygeneruj nowy tajny klucz. Użyj kompatybilnej aplikacji 2FA, która obsługuje 8-cyfrowe kody i SHA512.",
"totp_2fa_success": "Powodzenie! Cake 2FA włączony dla tego portfela. Pamiętaj, aby zapisać swoje mnemoniczne ziarno na wypadek utraty dostępu do portfela.",
@ -798,6 +807,8 @@
"use_ssl": "Użyj SSL",
"use_suggested": "Użyj sugerowane",
"use_testnet": "Użyj testne",
"value": "Wartość",
"value_type": "Typ wartości",
"variable_pair_not_supported": "Ta para zmiennych nie jest obsługiwana na wybranych giełdach",
"verification": "Weryfikacja",
"verify_with_2fa": "Sprawdź za pomocą Cake 2FA",

View file

@ -87,6 +87,7 @@
"buy": "Comprar",
"buy_alert_content": "Atualmente, oferecemos suporte apenas à compra de Bitcoin, Ethereum, Litecoin e Monero. Crie ou troque para sua carteira Bitcoin, Ethereum, Litecoin ou Monero.",
"buy_bitcoin": "Compre Bitcoin",
"buy_now": "Comprar agora",
"buy_provider_unavailable": "Provedor atualmente indisponível.",
"buy_with": "Compre com",
"by_cake_pay": "por Cake Pay",
@ -94,8 +95,7 @@
"cake_dark_theme": "Bolo tema escuro",
"cake_pay_account_note": "Inscreva-se com apenas um endereço de e-mail para ver e comprar cartões. Alguns estão até com desconto!",
"cake_pay_learn_more": "Compre e resgate vales-presente instantaneamente no app!\nDeslize da esquerda para a direita para saber mais.",
"cake_pay_subtitle": "Compre vales-presente com desconto (somente nos EUA)",
"cake_pay_title": "Cartões de presente de CakePay",
"cake_pay_subtitle": "Compre cartões pré -pagos em todo o mundo e cartões -presente",
"cake_pay_web_cards_subtitle": "Compre cartões pré-pagos e cartões-presente em todo o mundo",
"cake_pay_web_cards_title": "Cartões Cake Pay Web",
"cake_wallet": "Cake Wallet",
@ -123,6 +123,7 @@
"change_wallet_alert_title": "Alterar carteira atual",
"choose_account": "Escolha uma conta",
"choose_address": "\n\nEscolha o endereço:",
"choose_card_value": "Escolha um valor de cartão",
"choose_derivation": "Escolha a derivação da carteira",
"choose_from_available_options": "Escolha entre as opções disponíveis:",
"choose_one": "Escolha um",
@ -166,6 +167,7 @@
"copy_address": "Copiar endereço",
"copy_id": "Copiar ID",
"copyWalletConnectLink": "Copie o link WalletConnect do dApp e cole aqui",
"countries": "Países",
"create_account": "Criar conta",
"create_backup": "Criar backup",
"create_donation_link": "Criar link de doação",
@ -178,6 +180,7 @@
"custom": "personalizado",
"custom_drag": "Personalizado (segure e arraste)",
"custom_redeem_amount": "Valor de resgate personalizado",
"custom_value": "Valor customizado",
"dark_theme": "Sombria",
"debit_card": "Cartão de débito",
"debit_card_terms": "O armazenamento e uso do número do cartão de pagamento (e credenciais correspondentes ao número do cartão de pagamento) nesta carteira digital estão sujeitos aos Termos e Condições do contrato do titular do cartão aplicável com o emissor do cartão de pagamento, em vigor a partir de tempo ao tempo.",
@ -190,6 +193,7 @@
"delete_wallet": "Excluir carteira",
"delete_wallet_confirm_message": "Tem certeza de que deseja excluir a carteira ${wallet_name}?",
"deleteConnectionConfirmationPrompt": "Tem certeza de que deseja excluir a conexão com",
"denominations": "Denominações",
"descending": "descendente",
"description": "Descrição",
"destination_tag": "Tag de destino:",
@ -277,6 +281,7 @@
"expired": "Expirada",
"expires": "Expira",
"expiresOn": "Expira em",
"expiry_and_validity": "Expiração e validade",
"export_backup": "Backup de exportação",
"extra_id": "ID extra:",
"extracted_address_content": "Você enviará fundos para\n${recipient_name}",
@ -385,6 +390,7 @@
"new_template": "Novo modelo",
"new_wallet": "Nova carteira",
"newConnection": "Nova conexão",
"no_cards_found": "Nenhum cartão encontrado",
"no_id_needed": "Nenhum ID necessário!",
"no_id_required": "Não é necessário ID. Recarregue e gaste em qualquer lugar",
"no_relay_on_domain": "Não há uma retransmissão para o domínio do usuário ou a retransmissão está indisponível. Escolha um relé para usar.",
@ -454,6 +460,7 @@
"pre_seed_button_text": "Compreendo. Me mostre minha semente",
"pre_seed_description": "Na próxima página, você verá uma série de ${words} palavras. Esta é a sua semente única e privada e é a ÚNICA maneira de recuperar sua carteira em caso de perda ou mau funcionamento. É SUA responsabilidade anotá-lo e armazená-lo em um local seguro fora do aplicativo Cake Wallet.",
"pre_seed_title": "IMPORTANTE",
"prepaid_cards": "Cartões pré-pagos",
"prevent_screenshots": "Evite capturas de tela e gravação de tela",
"privacy": "Privacidade",
"privacy_policy": "Política de privacidade",
@ -469,6 +476,7 @@
"purple_dark_theme": "Tema escuro roxo",
"qr_fullscreen": "Toque para abrir o código QR em tela cheia",
"qr_payment_amount": "This QR code contains a payment amount. Do you want to overwrite the current value?",
"quantity": "Quantidade",
"question_to_disable_2fa": "Tem certeza de que deseja desativar o Cake 2FA? Um código 2FA não será mais necessário para acessar a carteira e certas funções.",
"receivable_balance": "Saldo a receber",
"receive": "Receber",
@ -710,6 +718,7 @@
"tokenID": "EU IA",
"tor_connection": "Conexão Tor",
"tor_only": "Tor apenas",
"total": "Total",
"total_saving": "Economia total",
"totp_2fa_failure": "Código incorreto. Tente um código diferente ou gere uma nova chave secreta. Use um aplicativo 2FA compatível com códigos de 8 dígitos e SHA512.",
"totp_2fa_success": "Sucesso! Cake 2FA ativado para esta carteira. Lembre-se de salvar sua semente mnemônica caso perca o acesso à carteira.",
@ -800,6 +809,8 @@
"use_ssl": "Use SSL",
"use_suggested": "Uso sugerido",
"use_testnet": "Use testNet",
"value": "Valor",
"value_type": "Tipo de valor",
"variable_pair_not_supported": "Este par de variáveis não é compatível com as trocas selecionadas",
"verification": "Verificação",
"verify_with_2fa": "Verificar com Cake 2FA",

View file

@ -87,6 +87,7 @@
"buy": "Купить",
"buy_alert_content": "В настоящее время мы поддерживаем только покупку биткойнов, Ethereum, Litecoin и Monero. Пожалуйста, создайте или переключитесь на свой кошелек Bitcoin, Ethereum, Litecoin или Monero.",
"buy_bitcoin": "Купить Bitcoin",
"buy_now": "Купить сейчас",
"buy_provider_unavailable": "Поставщик в настоящее время недоступен.",
"buy_with": "Купить с помощью",
"by_cake_pay": "от Cake Pay",
@ -94,8 +95,7 @@
"cake_dark_theme": "Тейт темная тема",
"cake_pay_account_note": "Зарегистрируйтесь, указав только адрес электронной почты, чтобы просматривать и покупать карты. Некоторые даже доступны со скидкой!",
"cake_pay_learn_more": "Мгновенно покупайте и используйте подарочные карты в приложении!\nПроведите по экрану слева направо, чтобы узнать больше.",
"cake_pay_subtitle": "Покупайте подарочные карты со скидкой (только для США)",
"cake_pay_title": "Подарочные карты Cake Pay",
"cake_pay_subtitle": "Купить карты с предоплатой и подарочными картами по всему миру",
"cake_pay_web_cards_subtitle": "Покупайте карты предоплаты и подарочные карты по всему миру",
"cake_pay_web_cards_title": "Веб-карты Cake Pay",
"cake_wallet": "Cake Wallet",
@ -123,6 +123,7 @@
"change_wallet_alert_title": "Изменить текущий кошелек",
"choose_account": "Выберите аккаунт",
"choose_address": "\n\nПожалуйста, выберите адрес:",
"choose_card_value": "Выберите значение карты",
"choose_derivation": "Выберите вывод кошелька",
"choose_from_available_options": "Выберите из доступных вариантов:",
"choose_one": "Выбери один",
@ -166,6 +167,7 @@
"copy_address": "Cкопировать адрес",
"copy_id": "Скопировать ID",
"copyWalletConnectLink": "Скопируйте ссылку WalletConnect из dApp и вставьте сюда.",
"countries": "Страны",
"create_account": "Создать аккаунт",
"create_backup": "Создать резервную копию",
"create_donation_link": "Создать ссылку для пожертвований",
@ -178,6 +180,7 @@
"custom": "обычай",
"custom_drag": "Пользователь (удерживайте и перетаскивайте)",
"custom_redeem_amount": "Пользовательская сумма погашения",
"custom_value": "Пользовательское значение",
"dark_theme": "Темная",
"debit_card": "Дебетовая карта",
"debit_card_terms": "Хранение и использование номера вашей платежной карты (и учетных данных, соответствующих номеру вашей платежной карты) в этом цифровом кошельке регулируются положениями и условиями применимого соглашения держателя карты с эмитентом платежной карты, действующим с время от времени.",
@ -190,6 +193,7 @@
"delete_wallet": "Удалить кошелек",
"delete_wallet_confirm_message": "Вы уверены, что хотите удалить кошелек ${wallet_name}?",
"deleteConnectionConfirmationPrompt": "Вы уверены, что хотите удалить подключение к",
"denominations": "Деноминации",
"descending": "Нисходящий",
"description": "Описание",
"destination_tag": "Целевой тег:",
@ -277,6 +281,7 @@
"expired": "Истекает",
"expires": "Истекает",
"expiresOn": "Годен до",
"expiry_and_validity": "Истечение и достоверность",
"export_backup": "Экспорт резервной копии",
"extra_id": "Дополнительный ID:",
"extracted_address_content": "Вы будете отправлять средства\n${recipient_name}",
@ -384,6 +389,7 @@
"new_template": "Новый шаблон",
"new_wallet": "Новый кошелёк",
"newConnection": "Новое соединение",
"no_cards_found": "Карт не найдено",
"no_id_needed": "Идентификатор не нужен!",
"no_id_required": "Идентификатор не требуется. Пополняйте и тратьте где угодно",
"no_relay_on_domain": "Для домена пользователя реле не существует или реле недоступно. Пожалуйста, выберите реле для использования.",
@ -453,6 +459,7 @@
"pre_seed_button_text": "Понятно. Покажите мнемоническую фразу",
"pre_seed_description": "На следующей странице вы увидите серию из ${words} слов. Это ваша уникальная и личная мнемоническая фраза, и это ЕДИНСТВЕННЫЙ способ восстановить свой кошелек в случае потери или неисправности. ВАМ необходимо записать ее и хранить в надежном месте вне приложения Cake Wallet.",
"pre_seed_title": "ВАЖНО",
"prepaid_cards": "Предоплаченные карты",
"prevent_screenshots": "Предотвратить скриншоты и запись экрана",
"privacy": "Конфиденциальность",
"privacy_policy": "Политика конфиденциальности",
@ -468,6 +475,7 @@
"purple_dark_theme": "Пурпурная темная тема",
"qr_fullscreen": "Нажмите, чтобы открыть полноэкранный QR-код",
"qr_payment_amount": "This QR code contains a payment amount. Do you want to overwrite the current value?",
"quantity": "Количество",
"question_to_disable_2fa": "Вы уверены, что хотите отключить Cake 2FA? Код 2FA больше не потребуется для доступа к кошельку и некоторым функциям.",
"receivable_balance": "Баланс дебиторской задолженности",
"receive": "Получить",
@ -709,6 +717,7 @@
"tokenID": "ИДЕНТИФИКАТОР",
"tor_connection": "Тор соединение",
"tor_only": "Только Tor",
"total": "Общий",
"total_saving": "Общая экономия",
"totp_2fa_failure": "Неверный код. Пожалуйста, попробуйте другой код или создайте новый секретный ключ. Используйте совместимое приложение 2FA, которое поддерживает 8-значные коды и SHA512.",
"totp_2fa_success": "Успех! Для этого кошелька включена двухфакторная аутентификация Cake. Не забудьте сохранить мнемоническое семя на случай, если вы потеряете доступ к кошельку.",
@ -799,6 +808,8 @@
"use_ssl": "Использовать SSL",
"use_suggested": "Использовать предложенный",
"use_testnet": "Используйте Testnet",
"value": "Ценить",
"value_type": "Тип значения",
"variable_pair_not_supported": "Эта пара переменных не поддерживается выбранными биржами.",
"verification": "Проверка",
"verify_with_2fa": "Подтвердить с помощью Cake 2FA",

Some files were not shown because too many files have changed in this diff Show more