From 2e8fc2a7c6da3636dd7a3f7758bd1ff3aba0b7f6 Mon Sep 17 00:00:00 2001 From: cppethereum Date: Fri, 27 Jan 2023 23:38:45 -0500 Subject: [PATCH 01/59] Appending Indonesian language to list in README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 96f59e704..2a77aecb7 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,7 @@ Edit the applicable `strings_XX.arb` file in `res/values/` and open a pull reque - Arabic - Turkish - Burmese +- Indonesian ## Add a new language From 0e78def2fd830151784eabc571a603e82afbf3a7 Mon Sep 17 00:00:00 2001 From: cppethereum Date: Sat, 28 Jan 2023 00:36:16 -0500 Subject: [PATCH 02/59] amending language_service.dart for Indonesian --- lib/entities/language_service.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/entities/language_service.dart b/lib/entities/language_service.dart index 2f3443c02..d0aa75c81 100644 --- a/lib/entities/language_service.dart +++ b/lib/entities/language_service.dart @@ -23,6 +23,7 @@ class LanguageService { 'ar': 'العربية (Arabic)', 'tr': 'Türkçe (Turkish)', 'my': 'မြန်မာ (Burmese)', + 'id': 'Bahasa Indonesia (Indonesian)', }; static const Map localeCountryCode = { @@ -45,6 +46,7 @@ class LanguageService { 'ar': 'sau', 'tr': 'tur', 'my': 'mmr', + 'id': 'ind', }; static final list = {}; From 8eba55d114fed22e5ca0a8566af9074b964d7b2f Mon Sep 17 00:00:00 2001 From: cppethereum Date: Sat, 28 Jan 2023 00:37:25 -0500 Subject: [PATCH 03/59] Indonesian translation strings --- res/values/strings_id.arb | 670 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 670 insertions(+) create mode 100644 res/values/strings_id.arb diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb new file mode 100644 index 000000000..b69f7641b --- /dev/null +++ b/res/values/strings_id.arb @@ -0,0 +1,670 @@ +{ + "welcome" : "Selamat datang di", + "cake_wallet" : "Cake Wallet", + "first_wallet_text" : "Dompet luar biasa untuk Monero, Bitcoin, Litecoin, dan Haven", + "please_make_selection" : "Silahkan membuat pilihan di bawah ini untuk membuat atau memulihkan dompet Anda.", + "create_new" : "Buat Dompet Baru", + "restore_wallet" : "Pulihkan Dompet", + + "monero_com": "Monero.com Oleh Cake Wallet", + "monero_com_wallet_text": "Dompet luar biasa untuk Monero", + + "haven_app": "Haven Oleh Cake Wallet", + "haven_app_wallet_text": "Dompet luar biasa untuk Haven", + + "accounts" : "Akun", + "edit" : "Edit", + "account" : "Akun", + "add" : "Menambahkan", + + + "address_book" : "Buku Alamat", + "contact" : "Kontak", + "please_select" : "Silakan pilih:", + "cancel" : "Batal", + "ok" : "OK", + "contact_name" : "Nama Kontak", + "reset" : "Reset", + "save" : "Simpan", + "address_remove_contact" : "Hapus kontak", + "address_remove_content" : "Apakah Anda yakin ingin menghapus kontak yang dipilih?", + + "authenticated" : "Terotentikasi", + "authentication" : "Otentikasi", + "failed_authentication" : "Otentikasi gagal. ${state_error}", + + + "wallet_menu" : "Menu", + "Blocks_remaining" : "${status} Blok Tersisa", + "please_try_to_connect_to_another_node" : "Silakan coba untuk terhubung ke node lain", + "xmr_hidden" : "Tersembunyi", + "xmr_available_balance" : "Saldo Tersedia", + "xmr_full_balance" : "Saldo Penuh", + "send" : "Mengirim", + "receive" : "Menerima", + "transactions" : "Transaksi", + "incoming" : "Masuk", + "outgoing" : "Keluar", + "transactions_by_date" : "Transaksi berdasarkan tanggal", + "trades" : "Perdagangan", + "filter_by": "Filter berdasarkan", + "today" : "Hari ini", + "yesterday" : "Kemarin", + "received" : "Diterima", + "sent" : "Dikirim", + "pending" : " (pending)", + "rescan" : "Pindai ulang", + "reconnect" : "Sambungkan kembali", + "wallets" : "Dompet", + "show_seed" : "Tampilkan seed", + "show_keys" : "Tampilkan seed/kunci", + "address_book_menu" : "Buku alamat", + "reconnection" : "Koneksi kembali", + "reconnect_alert_text" : "Apakah Anda yakin ingin menyambungkan kembali?", + + + "exchange" : "Tukar", + "clear" : "Hapus", + "refund_address" : "Alamat pengembalian", + "change_exchange_provider" : "Ganti Penyedia Tukar", + "you_will_send" : "Konversi dari", + "you_will_get" : "Konversi ke", + "amount_is_guaranteed" : "Jumlah penerimaan dijamin", + "amount_is_estimate" : "Jumlah penerimaan diperkirakan", + "powered_by" : "Didukung oleh ${title}", + "error" : "Kesalahan", + "estimated" : "Diperkirakan", + "min_value" : "Min: ${value} ${currency}", + "max_value" : "Max: ${value} ${currency}", + "change_currency" : "Ganti Mata Uang", + "overwrite_amount" : "Timpa jumlah", + "qr_payment_amount" : "QR code ini berisi jumlah pembayaran. Apakah Anda ingin menimpa nilai saat ini?", + + "copy_id" : "Salin ID", + "exchange_result_write_down_trade_id" : "Silakan salin atau tulis ID perdagangan untuk melanjutkan.", + "trade_id" : "ID Perdagangan:", + "copied_to_clipboard" : "Disalin ke Clipboard", + "saved_the_trade_id" : "Saya telah menyimpan ID perdagangan", + "fetching" : "Mengambil", + "id" : "ID: ", + "amount" : "Jumlah: ", + "payment_id" : "ID Pembayaran: ", + "status" : "Status: ", + "offer_expires_in" : "Penawaran kedaluwarsa dalam: ", + "trade_is_powered_by" : "Perdagangan ini didukung oleh ${provider}", + "copy_address" : "Salin Alamat", + "exchange_result_confirm" : "Dengan menekan tombol konfirmasi, Anda akan mengirimkan ${fetchingLabel} ${from} dari dompet Anda yang disebut ${walletName} ke alamat yang ditampilkan di bawah. Anda juga dapat mengirim dari dompet eksternal Anda ke alamat/QR code di bawah.\n\nSilakan tekan konfirmasi untuk melanjutkan atau kembali untuk mengubah jumlah.", + "exchange_result_description" : "Anda harus mengirimkan minimal ${fetchingLabel} ${from} ke alamat yang ditampilkan di halaman berikutnya. Jika Anda mengirimkan jumlah yang lebih rendah dari ${fetchingLabel} ${from} maka uang tersebut mungkin tidak akan diubah dan mungkin tidak akan dikembalikan.", + "exchange_result_write_down_ID" : "*Silakan salin atau tulis ID Anda yang ditampilkan di atas.", + "confirm" : "Konfirmasi", + "confirm_sending" : "Konfirmasi pengiriman", + "commit_transaction_amount_fee" : "Lakukan transaksi\nJumlah: ${amount}\nBiaya: ${fee}", + "sending" : "Mengirim", + "transaction_sent" : "Transaksi terkirim!", + "expired" : "Kedaluwarsa", + "time" : "${minutes}m ${seconds}s", + "send_xmr" : "Kirim XMR", + "exchange_new_template" : "Template baru", + + "faq" : "Pertanyaan yang Sering Diajukan", + + + "enter_your_pin" : "Masukkan PIN Anda", + "loading_your_wallet" : "Memuat dompet Anda", + + + "new_wallet" : "Dompet Baru", + "wallet_name" : "Nama Dompet", + "continue_text" : "Lanjutkan", + "choose_wallet_currency" : "Silahkan pilih mata uang dompet:", + + "node_new" : "Node Baru", + "node_address" : "Alamat Node", + "node_port" : "Port Node", + "login" : "Masuk", + "password" : "Kata Sandi", + "nodes" : "Node", + "node_reset_settings_title" : "Atur ulang pengaturan", + "nodes_list_reset_to_default_message" : "Apakah Anda yakin ingin mengatur ulang pengaturan ke default?", + "change_current_node" : "Apakah Anda yakin ingin mengubah node saat ini menjadi ${node}?", + "change" : "Ubah", + "remove_node" : "Hapus node", + "remove_node_message" : "Apakah Anda yakin ingin menghapus node yang dipilih?", + "remove" : "Hapus", + "delete" : "Hapus", + "add_new_node" : "Tambah node baru", + "change_current_node_title" : "Ubah node saat ini", + "node_test" : "Uji", + "node_connection_successful" : "Koneksi berhasil", + "node_connection_failed" : "Koneksi gagal", + "new_node_testing" : "Pengujian node baru", + + + "use" : "Beralih ke ", + "digit_pin" : "-digit PIN", + + "share_address" : "Bagikan alamat", + "receive_amount" : "Jumlah", + "subaddresses" : "Sub-alamat", + "addresses" : "Alamat", + "scan_qr_code" : "Scan kode QR untuk mendapatkan alamat", + "qr_fullscreen" : "Tap untuk membuka layar QR code penuh", + "rename" : "Ganti nama", + "choose_account" : "Pilih akun", + "create_new_account" : "Buat akun baru", + "accounts_subaddresses" : "Akun dan sub-alamat", + + + "restore_restore_wallet" : "Pulihkan Dompet", + "restore_title_from_seed_keys" : "Pulihkan dari seed/kunci", + "restore_description_from_seed_keys" : "Dapatkan kembali dompet Anda dari seed/kunci yang Anda simpan di tempat yang aman", + "restore_next" : "Selanjutnya", + "restore_title_from_backup" : "Pulihkan dari cadangan", + "restore_description_from_backup" : "Anda dapat memulihkan seluruh aplikasi Cake Wallet dari file cadangan Anda", + "restore_seed_keys_restore" : "Pulihkan Seed/Kunci", + "restore_title_from_seed" : "Pulihkan dari seed", + "restore_description_from_seed" : "Pulihkan dompet Anda dari kombinasi kode 25 atau 13 kata", + "restore_title_from_keys" : "Pulihkan dari kunci", + "restore_description_from_keys" : "Pulihkan dompet Anda dari tombol yang dihasilkan yang disimpan dari kunci pribadi Anda", + "restore_wallet_name" : "Nama dompet", + "restore_address" : "Alamat", + "restore_view_key_private" : "Lihat kunci (pribadi)", + "restore_spend_key_private" : "Habiskan kunci (pribadi)", + "restore_recover" : "Pulihkan", + "restore_wallet_restore_description" : "Deskripsi pemulihan dompet", + "restore_new_seed" : "Seed baru", + "restore_active_seed" : "Seed aktif", + "restore_bitcoin_description_from_seed" : "Pulihkan dompet Anda dari kombinasi kode 24 kata", + "restore_bitcoin_description_from_keys" : "Pulihkan dompet Anda dari string WIF yang dihasilkan dari private keys Anda", + "restore_bitcoin_title_from_keys" : "Pulihkan dari WIF", + "restore_from_date_atau_blockheight" : "Silakan masukkan tanggal beberapa hari sebelum Anda membuat dompet ini. Atau jika Anda tahu blockheight, silakan masukkannya sebagai gantinya", + + + "seed_reminder" : "Silakan tulis ini di tempat yang aman jika kamu kehilangan atau menghapus ponselmu", + "seed_title" : "Bibit", + "seed_share" : "Bagikan bibit", + "copy" : "Salin", + + + "seed_language_choose" : "Silakan pilih bahasa bibit:", + "seed_choose" : "Pilih bahasa bibit", + "seed_language_next" : "Selanjutnya", + "seed_language_english" : "Inggris", + "seed_language_chinese" : "Cina", + "seed_language_dutch" : "Belanda", + "seed_language_german" : "Jerman", + "seed_language_japanese" : "Jepang", + "seed_language_portuguese" : "Portugis", + "seed_language_russian" : "Rusia", + "seed_language_spanish" : "Spanyol", + "seed_language_french": "Perancis", + "seed_language_italian": "Italia", + + + "send_title" : "Kirim", + "send_your_wallet" : "Dompetmu", + "send_address" : "Alamat ${cryptoCurrency}", + "send_payment_id" : "ID Pembayaran (opsional)", + "all" : "SEMUA", + "send_error_minimum_value" : "Nilai minimum jumlah adalah 0.01", + "send_error_currency" : "Mata uang hanya dapat berisi angka", + "send_estimated_fee" : "Biaya yang diperkirakan:", + "send_priority" : "Saat ini biaya diatur dengan prioritas ${transactionPriority}.\nPrioritas transaksi dapat diubah pada pengaturan", + "send_creating_transaction" : "Membuat transaksi", + "send_templates" : "Template", + "send_new" : "Baru", + "send_amount" : "Jumlah:", + "send_fee" : "Biaya:", + "send_name" : "Nama", + "send_got_it" : "Sudah paham", + "send_sending" : "Mengirim...", + "send_success" : "${crypto}mu berhasil dikirim", + + + "settings_title" : "Pengaturan", + "settings_nodes" : "Nodes", + "settings_current_node" : "Node saat ini", + "settings_wallets" : "Dompet", + "settings_display_balance" : "Tampilkan saldo", + "settings_currency" : "Mata uang", + "settings_fee_priority" : "Prioritas biaya", + "settings_save_recipient_address" : "Simpan alamat penerima", + "settings_personal" : "Pribadi", + "settings_change_pin" : "Ganti PIN", + "settings_change_language" : "Ganti bahasa", + "settings_allow_biometrical_authentication" : "Izinkan otentikasi biometrik", + "settings_dark_mode" : "Mode gelap", + "settings_transactions" : "Transaksi", + "settings_trades" : "Perdagangan", + "settings_display_on_dashboard_list" : "Tampilkan di daftar dashboard", + "settings_all" : "SEMUA", + "settings_only_trades" : "Hanya perdagangan", + "settings_only_transactions" : "Hanya transaksi", + "settings_none" : "Tidak ada", + "settings_support" : "Dukungan", + "settings_terms_and_conditions" : "Syarat dan Ketentuan", + "pin_is_incorrect" : "PIN salah", + + + "setup_pin" : "Pasang PIN", + "enter_your_pin_again" : "Masukkan PIN Anda lagi", + "setup_successful" : "PIN Anda telah berhasil diatur!", + + "wallet_keys" : "Seed/kunci dompet", + "wallet_seed" : "Seed dompet", + "private_key" : "Kunci privat", + "public_key" : "Kunci publik", + "view_key_private" : "Kunci tampilan (privat)", + "view_key_public" : "Kunci tampilan (publik)", + "spend_key_private" : "Kunci pengeluaran (privat)", + "spend_key_public" : "Kunci pengeluaran (publik)", + "copied_key_to_clipboard" : "Kunci ${key} disalin ke Clipboard", + + "new_subaddress_title" : "Alamat baru", + "new_subaddress_label_name" : "Nama label", + "new_subaddress_create" : "Buat", + + "address_label" : "Label alamat", + + "subaddress_title" : "Daftar sub-alamat", + + "trade_details_title" : "Detail Transaksi", + "trade_details_id" : "ID", + "trade_details_state" : "Status", + "trade_details_fetching" : "Mengambil", + "trade_details_provider" : "Penyedia", + "trade_details_created_at" : "Dibuat pada", + "trade_details_pair" : "Pasangan", + "trade_details_copied" : "${title} disalin ke Clipboard", + + "trade_history_title" : "Riwayat Transaksi", + + + "transaction_details_title" : "Rincian Transaksi", + "transaction_details_transaction_id" : "ID Transaksi", + "transaction_details_date" : "Tanggal", + "transaction_details_height" : "Tinggi", + "transaction_details_amount" : "Jumlah", + "transaction_details_fee" : "Biaya", + "transaction_details_copied" : "${title} disalin ke Clipboard", + "transaction_details_recipient_address" : "Alamat Penerima", + + "wallet_list_title" : "Dompet Monero", + "wallet_list_create_new_wallet" : "Buat Dompet Baru", + "wallet_list_restore_wallet" : "Pulihkan Dompet", + "wallet_list_load_wallet" : "Muat dompet", + "wallet_list_loading_wallet" : "Memuat ${wallet_name} dompet", + "wallet_list_failed_to_load" : "Gagal memuat ${wallet_name} dompet. ${error}", + "wallet_list_removing_wallet" : "Menghapus ${wallet_name} dompet", + "wallet_list_failed_to_remove" : "Gagal menghapus ${wallet_name} dompet. ${error}", + + "widgets_address" : "Alamat", + "widgets_restore_from_blockheight" : "Pulihkan dari tinggi blok", + "widgets_restore_from_date" : "Pulihkan dari tanggal", + "widgets_or" : "atau", + "widgets_seed" : "Biji", + + "router_no_route" : "Tidak ada rute yang ditentukan untuk ${name}", + + + "error_text_account_name" : "Nama akun hanya dapat berisi huruf, angka\ndan harus antara 1 dan 15 karakter panjang", + "error_text_contact_name" : "Nama kontak tidak boleh berisi simbol `, ' \"\ndan harus antara 1 dan 32 karakter panjang", + "error_text_address" : "Alamat dompet harus sesuai dengan tipe\nmata uang kripto", + "error_text_node_address" : "Silakan masukkan alamat iPv4", + "error_text_node_port" : "Port node hanya dapat berisi angka antara 0 dan 65535", + "error_text_payment_id" : "ID pembayaran hanya dapat berisi dari 16 hingga 64 karakter dalam hex", + "error_text_xmr" : "Nilai XMR tidak boleh melebihi saldo yang tersedia.\nJumlah digit pecahan harus kurang atau sama dengan 12", + "error_text_fiat" : "Nilai jumlah tidak boleh melebihi saldo yang tersedia.\nJumlah digit pecahan harus kurang atau sama dengan 2", + "error_text_subaddress_name" : "Nama subalamat tidak boleh berisi simbol `, ' \"\ndan harus antara 1 dan 20 karakter panjang", + "error_text_amount" : "Jumlah hanya dapat berisi angka", + "error_text_wallet_name" : "Nama dompet hanya dapat berisi huruf, angka, _ - simbol\ndan harus antara 1 dan 33 karakter panjang", + "error_text_keys" : "Kunci dompet hanya dapat berisi 64 karakter dalam hex", + "error_text_crypto_currency" : "Jumlah digit pecahan harus kurang atau sama dengan 12", + "error_text_minimal_limit" : "Perdagangan untuk ${provider} tidak dibuat. Jumlah kurang dari minimal: ${min} ${currency}", + "error_text_maximum_limit" : "Perdagangan untuk ${provider} tidak dibuat. Jumlah lebih dari maksimal: ${max} ${currency}", + "error_text_limits_loading_failed" : "Perdagangan untuk ${provider} tidak dibuat. Gagal memuat batas", + "error_text_template" : "Nama template dan alamat tidak boleh berisi simbol ` , ' \"\ndan harus antara 1 dan 106 karakter panjang", + + + "auth_store_ban_timeout" : "ban_timeout", + "auth_store_banned_for" : "Dilarang selama ", + "auth_store_banned_minutes" : "menit", + "auth_store_incorrect_password" : "PIN yang salah", + "wallet_store_monero_wallet" : "Dompet Monero", + "wallet_restoration_store_incorrect_seed_length" : "Panjang seed yang salah", + + + "full_balance" : "Saldo Penuh", + "available_balance" : "Saldo Tersedia", + "hidden_balance" : "Saldo Tersembunyi", + + "sync_status_syncronizing" : "SEDANG SINKRONISASI", + "sync_status_syncronized" : "SUDAH TERSINKRONISASI", + "sync_status_not_connected" : "TIDAK TERHUBUNG", + "sync_status_starting_sync" : "MULAI SINKRONISASI", + "sync_status_failed_connect" : "GAGAL TERHUBUNG", + "sync_status_connecting" : "MENGHUBUNGKAN", + "sync_status_connected" : "TERHUBUNG", + "sync_status_attempting_sync" : "MENCOBA SINKRONISASI", + + "transaction_priority_slow" : "Lambat", + "transaction_priority_regular" : "Normal", + "transaction_priority_medium" : "Sedang", + "transaction_priority_fast" : "Cepat", + "transaction_priority_fastest" : "Tercepat", + + "trade_for_not_created" : "Perdagangan untuk ${title} belum dibuat.", + "trade_not_created" : "Perdagangan belum dibuat", + "trade_id_not_found" : "Perdagangan ${tradeId} dari ${title} tidak ditemukan.", + "trade_not_found" : "Perdagangan tidak ditemukan.", + + "trade_state_pending" : "Menunggu", + "trade_state_confirming" : "Menegkonfirmasi", + "trade_state_trading" : "Berdagang", + "trade_state_traded" : "Telah Berdagang", + "trade_state_complete" : "Selesai", + "trade_state_to_be_created" : "Akan dibuat", + "trade_state_unpaid" : "Belum dibayar", + "trade_state_underpaid" : "Kurang bayar", + "trade_state_paid_unconfirmed" : "Dibayar belum dikonfirmasi", + "trade_state_paid" : "Dibayar", + "trade_state_btc_sent" : "Btc dikirim", + "trade_state_timeout" : "Waktu habis", + "trade_state_created" : "Dibuat", + "trade_state_finished" : "Selesai", + + "change_language" : "Ganti bahasa", + "change_language_to" : "Ganti bahasa ke ${language}?", + + "paste" : "Tempel", + "restore_from_seed_placeholder" : "Silakan masukkan atau tempel seed Anda di sini", + "add_new_word" : "Tambahkan kata baru", + "incorrect_seed" : "Teks yang dimasukkan tidak valid.", + + "biometric_auth_reason" : "Pindai sidik jari Anda untuk mengautentikasi", + "version" : "Versi ${currentVersion}", + + "openalias_alert_title" : "Alamat Terdeteksi", + "openalias_alert_content" : "Anda akan mengirim dana ke\n${recipient_name}", + + "card_address" : "Alamat:", + "buy" : "Beli", + "sell": "Jual", + + "placeholder_transactions" : "Transaksi Anda akan ditampilkan di sini", + "placeholder_contacts" : "Kontak Anda akan ditampilkan di sini", + + "template" : "Template", + "confirm_delete_template" : "Tindakan ini akan menghapus template ini. Apakah Anda ingin melanjutkan?", + "confirm_delete_wallet" : "Tindakan ini akan menghapus dompet ini. Apakah Anda ingin melanjutkan?", + + "picker_description" : "Untuk memilih ChangeNOW atau MorphToken, silakan ubah pasangan perdagangan Anda terlebih dahulu", + + "change_wallet_alert_title" : "Ganti dompet saat ini", + "change_wallet_alert_content" : "Apakah Anda ingin mengganti dompet saat ini ke ${wallet_name}?", + + "creating_new_wallet" : "Membuat dompet baru", + "creating_new_wallet_error" : "Error: ${description}", + + "seed_alert_title" : "Perhatian", + "seed_alert_content" : "Seed adalah satu-satunya cara untuk mengembalikan dompet Anda. Apakah Anda sudah menuliskannya?", + "seed_alert_back" : "Kembali", + "seed_alert_yes" : "Ya, sudah", + + "exchange_sync_alert_content" : "Silakan tunggu sampai dompet Anda tersinkronisasi", + + "pre_seed_title" : "PENTING", + "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_button_text" : "Saya mengerti. Tampilkan seed saya", + + "xmr_to_error" : "XMR.TO error", + "xmr_to_error_description" : "Jumlah tidak valid. Batas maksimal 8 digit setelah koma", + + "provider_error" : "${provider} error", + + "use_ssl" : "Gunakan SSL", + "trusted" : "Dipercayai", + + "color_theme" : "Tema warna", + "light_theme" : "Terang", + "bright_theme" : "Cerah", + "dark_theme" : "Gelap", + "enter_your_note" : "Masukkan catatan Anda...", + "note_optional" : "Catatan (opsional)", + "note_tap_to_change" : "Catatan (tap untuk mengubah)", + "view_in_block_explorer" : "Lihat di Block Explorer", + "view_transaction_on" : "Lihat Transaksi di ", + "transaction_key" : "Kunci transaksi", + "confirmations" : "Konfirmasi", + "recipient_address" : "Alamat penerima", + + "extra_id" : "ID tambahan:", + "destination_tag" : "Tag tujuan:", + "memo" : "Memo:", + + "backup" : "Cadangan", + "change_password" : "Ubah kata sandi", + "backup_password" : "Kata sandi cadangan", + "write_down_backup_password" : "Silakan menuliskan kata sandi cadangan Anda, yang digunakan untuk impor file cadangan Anda.", + "export_backup" : "Ekspor cadangan", + "save_backup_password" : "Pastikan Anda telah menyimpan kata sandi cadangan Anda. Anda tidak akan dapat mengimpor file cadangan Anda tanpa itu.", + "backup_file" : "File cadangan", + + "edit_backup_password" : "Edit Kata Sandi Cadangan", + "save_backup_password_alert" : "Simpan kata sandi cadangan", + "change_backup_password_alert" : "File cadangan sebelumnya tidak akan tersedia untuk diimpor dengan kata sandi cadangan baru. Kata sandi cadangan baru hanya akan digunakan untuk file cadangan baru. Apakah Anda yakin ingin mengubah kata sandi cadangan?", + + "enter_backup_password" : "Masukkan kata sandi cadangan di sini", + "select_backup_file" : "Pilih file cadangan", + "import" : "Impor", + "please_select_backup_file" : "Silakan pilih file cadangan dan masukkan kata sandi cadangan.", + + "fixed_rate" : "Rate tetap", + "fixed_rate_alert" : "Anda akan dapat memasukkan jumlah penerimaan saat mode rate tetap dicentang. Apakah Anda ingin beralih ke mode rate tetap?", + + "xlm_extra_info" : "Jangan lupa untuk menentukan ID Memo saat mengirim transaksi XLM untuk pertukaran", + "xrp_extra_info" : "Jangan lupa untuk menentukan Tag Tujuan saat mengirim transaksi XRP untuk pertukaran", + + "exchange_incorrect_current_wallet_for_xmr" : "Jika Anda ingin menukar XMR dari saldo Monero Cake Wallet Anda, silakan beralih ke dompet Monero Anda terlebih dahulu.", + "confirmed" : "Dikonfirmasi", + "unconfirmed" : "Tidak dikonfirmasi", + "displayable" : "Dapat ditampilkan", + + "submit_request" : "kirim permintaan", + + "buy_alert_content" : "Saat ini kami hanya mendukung pembelian Bitcoin dan Litecoin. Untuk membeli Bitcoin atau Litecoin, silakan buat atau beralih ke dompet Bitcoin atau Litecoin Anda.", + "sell_alert_content": "Saat ini kami hanya mendukung penjualan Bitcoin. Untuk menjual Bitcoin, silakan buat atau beralih ke dompet Bitcoin Anda.", + + "outdated_electrum_wallet_description" : "Dompet Bitcoin baru yang dibuat di Cake sekarang memiliki biji semai 24 kata. Wajib bagi Anda untuk membuat dompet Bitcoin baru dan mentransfer semua dana Anda ke dompet 24 kata baru, dan berhenti menggunakan dompet dengan biji semai 12 kata. Silakan lakukan ini segera untuk mengamankan dana Anda.", + "understand" : "Saya mengerti", + + "apk_update" : "Pembaruan APK", + + "buy_bitcoin" : "Beli Bitcoin", + "buy_with" : "Beli dengan", + "moonpay_alert_text" : "Nilai jumlah harus lebih atau sama dengan ${minAmount} ${fiatCurrency}", + + "outdated_electrum_wallet_receive_warning": "Jika dompet ini memiliki biji semai 12 kata dan dibuat di Cake, JANGAN deposit Bitcoin ke dalam dompet ini. BTC apapun yang ditransfer ke dompet ini mungkin hilang. Buat dompet 24 kata baru (ketuk menu di pojok kanan atas, pilih Dompet, pilih Buat Dompet Baru, lalu pilih Bitcoin) dan SEGERA pindahkan BTC Anda ke sana. Dompet BTC (24 kata) baru dari Cake aman", + "do_not_show_me": "Jangan tampilkan ini lagi", + + "unspent_coins_title" : "Koin yang tidak terpakai", + "unspent_coins_details_title" : "Rincian koin yang tidak terpakai", + "freeze" : "Freeze", + "frozen" : "Dibekukan", + "coin_control" : "Kontrol koin (opsional)", + + "address_detected" : "Alamat terdeteksi", + "address_from_domain" : "Alamat ini dari ${domain} di Unstoppable Domains", + + "add_receiver" : "Tambahkan penerima lain (opsional)", + + "manage_yats" : "Kelola Yats", + "yat_alert_title" : "Kirim dan terima crypto dengan lebih mudah dengan Yat", + "yat_alert_content" : "Pengguna Cake Wallet sekarang dapat mengirim dan menerima semua mata uang favorit mereka dengan nama pengguna berbasis emoji yang satu-of-a-kind.", + "get_your_yat" : "Dapatkan Yat Anda", + "connect_an_existing_yat" : "Hubungkan Yat yang ada", + "connect_yats": "Hubungkan Yats", + "yat_address" : "Alamat Yat", + "yat" : "Yat", + "address_from_yat" : "Alamat ini berasal dari ${emoji} di Yat", + "yat_error" : "Kesalahan Yat", + "yat_error_content" : "Tidak ada alamat yang terkait dengan Yat ini. Coba Yat lain", + "choose_address" : "\n\nSilakan pilih alamat:", + "yat_popup_title" : "Alamat dompet Anda dapat diubah menjadi emoji.", + "yat_popup_content" : "Anda sekarang dapat mengirim dan menerima crypto di Cake Wallet dengan Yat Anda - nama pengguna berbasis emoji yang pendek. Kelola Yats kapan saja pada layar pengaturan", + "second_intro_title" : "Satu alamat emoji untuk menguasainya semua", + "second_intro_content" : "Yat Anda adalah satu alamat emoji yang unik yang menggantikan semua alamat heksadesimal panjang Anda untuk semua mata uang Anda.", + "third_intro_title" : "Yat bermain baik dengan yang lain", + "third_intro_content" : "Yats hidup di luar Cake Wallet juga. Setiap alamat dompet di dunia dapat diganti dengan Yat!", + "learn_more" : "Pelajari Lebih Lanjut", + "search": "Cari", + "search_language": "Cari bahasa", + "search_currency": "Cari mata uang", + "new_template" : "Template Baru", + "electrum_address_disclaimer": "Kami menghasilkan alamat baru setiap kali Anda menggunakan satu, tetapi alamat sebelumnya tetap berfungsi", + "wallet_name_exists": "Nama dompet sudah ada. Silakan pilih nama yang berbeda atau ganti nama dompet yang lain terlebih dahulu.", + "market_place": "Pasar", + "cake_pay_title": "Kartu Hadiah Cake Pay", + "cake_pay_subtitle": "Beli kartu hadiah dengan harga diskon (hanya USA)", + "cake_pay_web_cards_title": "Kartu Web Cake Pay", + "cake_pay_web_cards_subtitle": "Beli kartu prabayar dan kartu hadiah secara global", + "about_cake_pay": "Cake Pay memungkinkan Anda untuk dengan mudah membeli kartu hadiah dengan aset virtual, yang dapat digunakan segera di lebih dari 150.000 pedagang di Amerika Serikat.", + "cake_pay_account_note": "Daftar hanya dengan alamat email untuk melihat dan membeli kartu. Beberapa di antaranya bahkan tersedia dengan diskon!", + "already_have_account": "Sudah punya akun?", + "create_account": "Buat Akun", + "privacy_policy": "Kebijakan Privasi", + "welcome_to_cakepay": "Selamat Datang di Cake Pay!", + "sign_up": "Daftar", + "forgot_password": "Lupa Kata Sandi", + "reset_password": "Atur Ulang Kata Sandi", + "gift_cards": "Kartu Hadiah", + "setup_your_debit_card": "Pasang kartu debit Anda", + "no_id_required": "Tidak perlu ID. Isi ulang dan belanja di mana saja", + "how_to_use_card": "Bagaimana menggunakan kartu ini", + "purchase_gift_card": "Beli Kartu Hadiah", + "verification": "Verifikasi", + "fill_code": "Silakan isi kode verifikasi yang diterima di email Anda", + "dont_get_code": "Tidak mendapatkan kode?", + "resend_code": "Silakan kirim ulang", + "debit_card": "Kartu Debit", + "cakepay_prepaid_card": "Kartu Debit Prabayar CakePay", + "no_id_needed": "Tidak perlu ID!", + "frequently_asked_questions": "Pertanyaan yang sering diajukan", + "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.", + "please_reference_document": "Silakan referensikan dokumen di bawah ini untuk informasi lebih lanjut.", + "cardholder_agreement": "Persetujuan Pemegang Kartu", + "e_sign_consent": "E-Sign Consent", + "agree_and_continue": "Setuju & Lanjutkan", + "email_address": "Alamat Email", + "agree_to": "Dengan membuat akun Anda setuju dengan ", + "and": "dan", + "enter_code": "Masukkan kode", + "congratulations": "Selamat!", + "you_now_have_debit_card": "Anda sekarang memiliki kartu debit", + "min_amount" : "Min: ${value}", + "max_amount" : "Max: ${value}", + "enter_amount": "Masukkan Jumlah", + "billing_address_info": "Jika diminta alamat billing, berikan alamat pengiriman Anda", + "order_physical_card": "Pesan Kartu Fisik", + "add_value": "Tambahkan nilai", + "activate": "Aktifkan", + "get_a": "Dapatkan ", + "digital_and_physical_card": " kartu debit pra-bayar digital dan fisik", + "get_card_note": " yang dapat Anda muat ulang dengan mata uang digital. Tidak perlu informasi tambahan!", + "signup_for_card_accept_terms": "Daftar untuk kartu dan terima syarat dan ketentuan.", + "add_fund_to_card": "Tambahkan dana pra-bayar ke kartu (hingga ${value})", + "use_card_info_two": "Dana dikonversi ke USD ketika disimpan dalam akun pra-bayar, bukan dalam mata uang digital.", + "use_card_info_three": "Gunakan kartu digital secara online atau dengan metode pembayaran tanpa kontak.", + "optionally_order_card": "Opsional memesan kartu fisik.", + "hide_details" : "Sembunyikan Rincian", + "show_details" : "Tampilkan Rincian", + "upto": "hingga ${value}", + "discount": "Hemat ${value}%", + "gift_card_amount": "Jumlah Kartu Hadiah", + "bill_amount": "Jumlah Tagihan", + "you_pay": "Anda Membayar", + "tip": "Tip:", + "custom": "kustom", + "by_cake_pay": "oleh Cake Pay", + "expires": "Kadaluarsa", + "mm": "MM", + "yy": "YY", + "online": "Online", + "offline": "Offline", + "gift_card_number": "Nomor Kartu Hadiah", + "pin_number": "Nomor PIN", + "total_saving": "Total Pembayaran", + "last_30_days": "30 hari terakhir", + "avg_savings": "Rata-rata Pembayaran", + "view_all": "Lihat Semua", + "active_cards": "Kartu Aktif", + "delete_account": "Hapus Akun", + "cards": "Kartu", + "active": "Aktif", + "redeemed": "Ditukar", + "gift_card_balance_note": "Kartu hadiah dengan saldo yang tersisa akan muncul di sini", + "gift_card_redeemed_note": "Kartu hadiah yang sudah Anda tukar akan muncul di sini", + "logout": "Keluar", + "add_tip": "Tambahkan Tip", + "percentageOf": "dari ${amount}", + "is_percentage": "adalah", + "search_category": "Cari kategori", + "mark_as_redeemed": "Tandai sebagai Ditukar", + "more_options": "Opsi Lainnya", + "awaiting_payment_confirmation": "Menunggu Konfirmasi Pembayaran", + "transaction_sent_notice": "Jika layar tidak bergerak setelah 1 menit, periksa block explorer dan email Anda.", + "agree": "Setuju", + "in_store": "Di Toko", + "generating_gift_card": "Membuat Kartu Hadiah", + "payment_was_received": "Pembayaran Anda telah diterima.", + "proceed_after_one_minute": "Jika layar tidak bergerak setelah 1 menit, periksa email Anda.", + "order_id": "ID Pesanan", + "gift_card_is_generated": "Kartu Hadiah telah dibuat", + "open_gift_card": "Buka Kartu Hadiah", + "contact_support": "Hubungi Dukungan", + "gift_cards_unavailable": "Kartu hadiah hanya tersedia untuk dibeli dengan Monero, Bitcoin, dan Litecoin saat ini", + "introducing_cake_pay": "Perkenalkan Cake Pay!", + "cake_pay_learn_more": "Beli dan tukar kartu hadiah secara instan di aplikasi!\nGeser ke kanan untuk informasi lebih lanjut.", + "automatic": "Otomatis", + "fixed_pair_not_supported": "Pasangan tetap ini tidak didukung dengan bursa yang dipilih", + "variable_pair_not_supported": "Pasangan variabel ini tidak didukung dengan bursa yang dipilih", + "none_of_selected_providers_can_exchange": "Tidak ada dari penyedia yang dipilih yang dapat melakukan pertukaran ini", + "choose_one": "Pilih satu", + "choose_from_available_options": "Pilih dari pilihan yang tersedia:", + "custom_redeem_amount": "Jumlah Tukar Kustom", + "add_custom_redemption": "Tambahkan Tukar Kustom", + "remaining": "sisa", + "delete_wallet": "Hapus dompet", + "delete_wallet_confirm_message" : "Apakah Anda yakin ingin menghapus dompet ${wallet_name}?", + "low_fee": "Biaya rendah", + "low_fee_alert": "Anda saat ini menggunakan prioritas biaya jaringan rendah. Ini dapat menyebabkan tunggu yang lama, kurs yang berbeda, atau perdagangan yang dibatalkan. Kami merekomendasikan menetapkan biaya yang lebih tinggi untuk pengalaman yang lebih baik.", + "ignor": "Abaikan", + "use_suggested": "Gunakan yang Disarankan", + "do_not_share_warning_text" : "Jangan berikan ini pada siapapun, termasuk dukungan.\n\nDana Anda bisa dan akan dicuri!", + "help": "bantuan", + "all_transactions": "Semua transaksi", + "all_trades": "Semua perdagangan", + "connection_sync": "Koneksi dan sinkronisasi", + "security_and_backup": "Keamanan dan cadangan", + "create_backup": "Buat cadangan", + "privacy_settings": "Pengaturan privasi", + "privacy": "Privasi", + "display_settings": "Pengaturan tampilan", + "other_settings": "Pengaturan lainnya", + "require_pin_after": "Meminta PIN setelah", + "always": "Selalu", + "minutes_to_pin_code": "${minute} menit", + "disable_exchange": "Nonaktifkan pertukaran", + "advanced_privacy_settings": "Pengaturan Privasi Lanjutan", + "settings_can_be_changed_later": "Pengaturan ini dapat diubah nanti di pengaturan aplikasi", + "add_custom_node": "Tambahkan Node Kustom Baru", + "disable_fiat": "Nonaktifkan fiat", + "fiat_api": "API fiat", + "disabled": "Dinonaktifkan", + "enabled": "Diaktifkan", + "tor_only": "Hanya Tor", + "unmatched_currencies": "Mata uang dompet Anda saat ini tidak cocok dengan yang ditandai QR", + "orbot_running_alert": "Pastikan Orbot sedang berjalan sebelum menghubungkan ke node ini.", + "contact_list_contacts": "Kontak", + "contact_list_wallets": "Dompet Saya" +} From 076ec525469f86a82031e96a2c950ab917afdace Mon Sep 17 00:00:00 2001 From: cppethereum Date: Sat, 28 Jan 2023 00:49:37 -0500 Subject: [PATCH 04/59] Updating idn language code ISO 3166-1 alpha-3 --- lib/entities/language_service.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/entities/language_service.dart b/lib/entities/language_service.dart index d0aa75c81..3bb096b19 100644 --- a/lib/entities/language_service.dart +++ b/lib/entities/language_service.dart @@ -46,7 +46,7 @@ class LanguageService { 'ar': 'sau', 'tr': 'tur', 'my': 'mmr', - 'id': 'ind', + 'id': 'idn', }; static final list = {}; From 4444a075c65762a4f02946b30cf1939b9c5b6aea Mon Sep 17 00:00:00 2001 From: holecekp Date: Sat, 4 Feb 2023 15:42:41 +0100 Subject: [PATCH 05/59] Add Czech language --- README.md | 1 + lib/entities/language_service.dart | 2 + res/values/strings_cs.arb | 688 +++++++++++++++++++++++++++++ 3 files changed, 691 insertions(+) create mode 100644 res/values/strings_cs.arb diff --git a/README.md b/README.md index 96f59e704..6e84068a2 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,7 @@ Edit the applicable `strings_XX.arb` file in `res/values/` and open a pull reque - Arabic - Turkish - Burmese +- Czech ## Add a new language diff --git a/lib/entities/language_service.dart b/lib/entities/language_service.dart index 2f3443c02..7fe5e8304 100644 --- a/lib/entities/language_service.dart +++ b/lib/entities/language_service.dart @@ -23,6 +23,7 @@ class LanguageService { 'ar': 'العربية (Arabic)', 'tr': 'Türkçe (Turkish)', 'my': 'မြန်မာ (Burmese)', + 'cs': 'čeština (Czech)', }; static const Map localeCountryCode = { @@ -45,6 +46,7 @@ class LanguageService { 'ar': 'sau', 'tr': 'tur', 'my': 'mmr', + 'cs': 'czk', }; static final list = {}; diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb new file mode 100644 index 000000000..cc66b8144 --- /dev/null +++ b/res/values/strings_cs.arb @@ -0,0 +1,688 @@ +{ + "welcome" : "Vítejte v", + "cake_wallet" : "Cake Wallet", + "first_wallet_text" : "Úžasná peněženka pro Monero, Bitcoin, Litecoin a Haven", + "please_make_selection" : "Prosím vyberte si níže, jestli chcete vytvořit, nebo obnovit peněženku.", + "create_new" : "Vytvořit novou peněženku", + "restore_wallet" : "Obnovit peněženku", + + "monero_com": "Monero.com od Cake Wallet", + "monero_com_wallet_text": "Úžasná peněženka pro Monero", + + "haven_app": "Haven od Cake Wallet", + "haven_app_wallet_text": "Úžasná peněženka pro Haven", + + "accounts" : "Účty", + "edit" : "Upravit", + "account" : "Účet", + "add" : "Přidat", + + + "address_book" : "Adresář", + "contact" : "Kontakt", + "please_select" : "Zvolte si:", + "cancel" : "Zrušit", + "ok" : "OK", + "contact_name" : "Jméno kontaktu", + "reset" : "Vymazat", + "save" : "Uložit", + "address_remove_contact" : "Smazat kontakt", + "address_remove_content" : "Opravdu chcete smazat označený kontakt?", + + + "authenticated" : "Ověřeno", + "authentication" : "Ověřování", + "failed_authentication" : "Ověřování selhalo. ${state_error}", + + + "wallet_menu" : "Menu", + "Blocks_remaining" : "Zbývá ${status} bloků", + "please_try_to_connect_to_another_node" : "Zkuste se prosím připojit k jinému uzlu", + "xmr_hidden" : "Skryto", + "xmr_available_balance" : "Zůstatek (dostupný)", + "xmr_full_balance" : "Zůstatek (celkový)", + "send" : "Poslat", + "receive" : "Přijmout", + "transactions" : "Transakce", + "incoming" : "Příchozí", + "outgoing" : "Odchozí", + "transactions_by_date" : "Transakce podle data", + "trades" : "Obchody", + "filter_by": "Filtrovat podle", + "today" : "Dnes", + "yesterday" : "Včera", + "received" : "Přijato", + "sent" : "Odesláno", + "pending" : " (čeká)", + "rescan" : "Znovu prohledat", + "reconnect" : "Znovu připojit", + "wallets" : "Peněženky", + "show_seed" : "Zobrazit seed", + "show_keys" : "Zobrazit seed/klíče", + "address_book_menu" : "Adresář", + "reconnection" : "Znovu připojit", + "reconnect_alert_text" : "Opravdu se chcete znovu připojit?", + + + "exchange" : "Směnit", + "clear" : "Smazat", + "refund_address" : "Adresa pro vrácení", + "change_exchange_provider" : "Změnit směnárnu", + "you_will_send" : "Směnit z", + "you_will_get" : "Směnit na", + "amount_is_guaranteed" : "Částka, kterou dostanete, je konečná", + "amount_is_estimate" : "Částka, kterou dostanete, je jen odhad.", + "powered_by" : "Zajišťuje ${title}", + "error" : "Chyba", + "estimated" : "Odhadováno", + "min_value" : "Min: ${value} ${currency}", + "max_value" : "Max: ${value} ${currency}", + "change_currency" : "Změnit měnu", + "overwrite_amount" : "Přepsat částku", + "qr_payment_amount" : "Tento QR kód obsahuje i částku. Chcete přepsat současnou hodnotu?", + + "copy_id" : "Kopírovat ID", + "exchange_result_write_down_trade_id" : "Prosím zkopírujte si, nebo zapište si ID transakce (trade ID) pro pokračování.", + "trade_id" : "ID transakce (trade ID):", + "copied_to_clipboard" : "Zkopírováno do schránky", + "saved_the_trade_id" : "Uložil jsem si ID transakce (trade ID)", + "fetching" : "Načítá se", + "id" : "ID: ", + "amount" : "Částka: ", + "payment_id" : "ID platby: ", + "status" : "Status: ", + "offer_expires_in" : "Nabídka vyprší: ", + "trade_is_powered_by" : "Tento obchod zajišťuje ${provider}", + "copy_address" : "Zkopírovat adresu", + "exchange_result_confirm" : "Po stisknutí Potvrdit odešlete ${fetchingLabel} ${from} ze své peněženky s názvem ${walletName} na adresu uvedenou níže. Nebo můžete prostředky poslat ze své externí peněženky na níže uvedenou adresu/QR kód.\n\nProsím stiskněte Potvrdit pro pokračování, nebo se vraťte zpět pro změnu částky.", + "exchange_result_description" : "Musíte poslat minimálně ${fetchingLabel} ${from} na adresu uvedenou na následující stránce. Pokud pošlete nižší částku než ${fetchingLabel} ${from} nemusí dojít ke směně, ani k jejímu vrácení.", + "exchange_result_write_down_ID" : "*Prosím zkopírujte si, nebo zapište si výše uvedené ID.", + "confirm" : "Potvrdit", + "confirm_sending" : "Potvrdit odeslání", + "commit_transaction_amount_fee" : "Odeslat transakci\nČástka: ${amount}\nPoplatek: ${fee}", + "sending" : "Odesílání", + "transaction_sent" : "Transakce odeslána!", + "expired" : "Vypršelo", + "time" : "${minutes}m ${seconds}s", + "send_xmr" : "Odeslat XMR", + "exchange_new_template" : "Nová šablona", + + "faq" : "FAQ", + + + "enter_your_pin" : "Zadejte svůj PIN", + "loading_your_wallet" : "Načítám peněženku", + + + "new_wallet" : "Nová peněženka", + "wallet_name" : "Název peněženky", + "continue_text" : "Pokračovat", + "choose_wallet_currency" : "Prosím zvolte si měnu pro peněženku:", + + + "node_new" : "Nový uzel", + "node_address" : "Adresa uzlu", + "node_port" : "Port uzlu", + "login" : "Login", + "password" : "Heslo", + "nodes" : "Uzly", + "node_reset_settings_title" : "Zrušit nastavení", + "nodes_list_reset_to_default_message" : "Opravdu chcete zrušit nastavení a vrátit výchozí hodnotu?", + "change_current_node" : "Opravdu chcete změnit současný uzel na ${node}?", + "change" : "Změnit", + "remove_node" : "Odstranit uzel", + "remove_node_message" : "Opravdu chcete odstranit označený uzel?", + "remove" : "Odstranit", + "delete" : "Smazat", + "add_new_node" : "Přidat nový uzel", + "change_current_node_title" : "Změnit současný uzel", + "node_test" : "Otestovat", + "node_connection_successful" : "Připojení bylo úspěšné", + "node_connection_failed" : "Připojení selhalo", + "new_node_testing" : "Testování nového uzlu", + + + "use" : "Přepnout na ", + "digit_pin" : "-číselný PIN", + + + "share_address" : "Sdílet adresu", + "receive_amount" : "Částka", + "subaddresses" : "Subadresy", + "addresses" : "Adresy", + "scan_qr_code" : "Naskenujte QR kód pro získání adresy", + "qr_fullscreen" : "Poklepáním otevřete QR kód na celé obrazovce", + "rename" : "Přejmenovat", + "choose_account" : "Zvolte částku", + "create_new_account" : "Vytvořit nový účet", + "accounts_subaddresses" : "Účty a subadresy", + + + "restore_restore_wallet" : "Obnovit peněženku", + "restore_title_from_seed_keys" : "Obnovit ze seedu/klíčů", + "restore_description_from_seed_keys" : "Obnovte svou peněženku ze seedu/klíčů, které jste si uložili na bezpečném místě", + "restore_next" : "Další", + "restore_title_from_backup" : "Obnovit ze zálohy", + "restore_description_from_backup" : "Můžete obnovit celou Cake Wallet aplikaci ze souboru se zálohou", + "restore_seed_keys_restore" : "Obnovit ze seedu/klíčů", + "restore_title_from_seed" : "Obnovit ze seedu", + "restore_description_from_seed" : "Obnovte svou peněženku pomocí kombinace 25, nebo 13 slov", + "restore_title_from_keys" : "Obnovit z klíčů", + "restore_description_from_keys" : "Obnovte svou peněženku pomocí generovaných stisků kláves uložených z vašich soukromých klíčů", + "restore_wallet_name" : "Jméno peněženky", + "restore_address" : "Adresa", + "restore_view_key_private" : "Klíč pro zobrazení (soukromý)", + "restore_spend_key_private" : "Klíč pro platby (soukromý)", + "restore_recover" : "Obnovit", + "restore_wallet_restore_description" : "Popis obnovení peněženky", + "restore_new_seed" : "Nový seed", + "restore_active_seed" : "Aktivní seed", + "restore_bitcoin_description_from_seed" : "Obnovte svou peněženku pomocí kombinace 24 slov", + "restore_bitcoin_description_from_keys" : "Obnovte svou peněženku pomocí vygenerovaného WIF řetězce z vašich soukromých klíčů", + "restore_bitcoin_title_from_keys" : "Obnovit z WIF", + "restore_from_date_or_blockheight" : "Prosím zadejte datum z doby několik dnů před tím, než jste si zakládali tuto peněženku. Nebo místo toho zadejte výšku bloku, pokud ji znáte.", + + + "seed_reminder" : "Prosím zapište si toto pro případ ztráty, nebo poškození telefonu", + "seed_title" : "Seed", + "seed_share" : "Sdílet seed", + "copy" : "Kopírovat", + + + "seed_language_choose" : "Prosím zvolte si jazyk seedu:", + "seed_choose" : "Zvolte si jazyk seedu", + "seed_language_next" : "Další", + "seed_language_english" : "Angličtina", + "seed_language_chinese" : "Čínština", + "seed_language_dutch" : "Nizozemština", + "seed_language_german" : "Němčina", + "seed_language_japanese" : "Japonština", + "seed_language_portuguese" : "Portugalština", + "seed_language_russian" : "Ruština", + "seed_language_spanish" : "Španělština", + "seed_language_french": "Francouzština", + "seed_language_italian": "Italština", + + + "send_title" : "Poslat", + "send_your_wallet" : "Vaše peněženka", + "send_address" : "${cryptoCurrency} adresa", + "send_payment_id" : "ID platby (nepovinné)", + "all" : "VŠE", + "send_error_minimum_value" : "Minimální částka je 0,01", + "send_error_currency" : "Měna může obsahovat pouze čísla", + "send_estimated_fee" : "Odhadovaný poplatek:", + "send_priority" : "Momentálně je poplatek nastaven na prioritu: ${transactionPriority}.\nPriorita transakce může být upravena v nastavení.", + "send_creating_transaction" : "Vytváření transakce", + "send_templates" : "Šablony", + "send_new" : "Nová", + "send_amount" : "Částka:", + "send_fee" : "Poplatek:", + "send_name" : "Název", + "send_got_it" : "Rozumím", + "send_sending" : "Odesílání...", + "send_success" : "Vaše ${crypto} bylo úspěšně odesláno", + + + "settings_title" : "Nastavení", + "settings_nodes" : "Uzly", + "settings_current_node" : "Aktuální uzel", + "settings_wallets" : "Peněženky", + "settings_display_balance" : "Zobrazovat zůstatek", + "settings_currency" : "Měna", + "settings_fee_priority" : "Priorita (poplatky)", + "settings_save_recipient_address" : "Ukládat adresu příjemce", + "settings_personal" : "Osobní", + "settings_change_pin" : "Změnit PIN", + "settings_change_language" : "Změnit jazyk", + "settings_allow_biometrical_authentication" : "Povolit biometrické ověření", + "settings_dark_mode" : "Tmavý režim", + "settings_transactions" : "Transakce", + "settings_trades" : "Obchody", + "settings_display_on_dashboard_list" : "Zobrazit na seznamu na dashboardu", + "settings_all" : "VŠE", + "settings_only_trades" : "Pouze obchody", + "settings_only_transactions" : "Pouze transakce", + "settings_none" : "Žádný", + "settings_support" : "Podpora", + "settings_terms_and_conditions" : "Obchodní podmínky", + "pin_is_incorrect" : "PIN není správný", + + + "setup_pin" : "Nastavit PIN", + "enter_your_pin_again" : "Zadejte znovu svůj PIN", + "setup_successful" : "Váš PIN byl úspěšně nastaven!", + + + "wallet_keys" : "Seed/klíče peněženky", + "wallet_seed" : "Seed peněženky", + "private_key" : "Soukromý klíč", + "public_key" : "Veřejný klíč", + "view_key_private" : "Klíč pro zobrazení (soukromý)", + "view_key_public" : "Klíč pro zobrazení (veřejný)", + "spend_key_private" : "Klíč pro platby (soukromý)", + "spend_key_public" : "Klíč pro platby (veřejný)", + "copied_key_to_clipboard" : "Zkopírován ${key} do schránky", + + + "new_subaddress_title" : "Nová adresa", + "new_subaddress_label_name" : "Popisek", + "new_subaddress_create" : "Vytvořit", + + "address_label" : "Popisek adresy", + + "subaddress_title" : "Seznam subadres", + + + "trade_details_title" : "Podrobnosti k obchodu", + "trade_details_id" : "ID", + "trade_details_state" : "Stav", + "trade_details_fetching" : "Získávám", + "trade_details_provider" : "Poskytovatel", + "trade_details_created_at" : "Vytvořeno v", + "trade_details_pair" : "Pár", + "trade_details_copied" : "${title} zkopírováno do schránky", + + + "trade_history_title" : "Historie obchodů", + + + "transaction_details_title" : "Podrobnosti o transakci", + "transaction_details_transaction_id" : "ID transakce", + "transaction_details_date" : "Datum", + "transaction_details_height" : "Výška", + "transaction_details_amount" : "Částka", + "transaction_details_fee" : "Poplatek", + "transaction_details_copied" : "${title} zkopírováno do schránky", + "transaction_details_recipient_address" : "Adresa příjemce", + + + "wallet_list_title" : "Monero Wallet", + "wallet_list_create_new_wallet" : "Vytvořit novou peněženku", + "wallet_list_restore_wallet" : "Obnovit peněženku", + "wallet_list_load_wallet" : "Načíst peněženku", + "wallet_list_loading_wallet" : "Načítám ${wallet_name} peněženku", + "wallet_list_failed_to_load" : "Chyba při načítání ${wallet_name} peněženky. ${error}", + "wallet_list_removing_wallet" : "Odstraňuji ${wallet_name} peněženku", + "wallet_list_failed_to_remove" : "Chyba při odstraňování ${wallet_name} peněženky. ${error}", + + + "widgets_address" : "Adresa", + "widgets_restore_from_blockheight" : "Obnovit z výšky bloku", + "widgets_restore_from_date" : "Obnovit z data", + "widgets_or" : "nebo", + "widgets_seed" : "Seed", + + + "router_no_route" : "Pro ${name} není definována žádná cesta", + + + "error_text_account_name" : "Název účtu může obsahovat jen písmena a čísla\na musí mít délku 1 až 15 znaků", + "error_text_contact_name" : "Jméno kontaktu nemůže obsahovat symboly ` , ' \" \na musí mít délku 1 až 32 znaků", + "error_text_address" : "Adresa peněženky musí odpovídat typu\nkryptoměny", + "error_text_node_address" : "prosím zadejte IPv4 adresu", + "error_text_node_port" : "Port uzlu musí být číslo mezi 0 a 65535", + "error_text_payment_id" : "ID platby se musí skládat z 16 až 64 hexadecimálních znaků", + "error_text_xmr" : "Hodnota XMR nemůže překročit dostupný zůstatek.\nPočet desetinných míst musí být menší, nebo roven 12", + "error_text_fiat" : "Částka nemůže překročit dostupný zůstatek.\nPočet desetinných míst musí být menší, nebo roven 2", + "error_text_subaddress_name" : "Subadresa nemůže obsahovat symboly ` , ' \" \na musí mít délku 1 až 20 znaků", + "error_text_amount" : "Částka může obsahovat pouze čísla", + "error_text_wallet_name" : "Jméno peněženky může obsahovat pouze písmena, čísla, symbol _ \na musí mít délku 1 až 33 znaků", + "error_text_keys" : "Klíče peněženky musí obsahovat 64 hexadecimálních znaků", + "error_text_crypto_currency" : "Počet desetinných míst\nmusí být menší, nebo roven 12", + "error_text_minimal_limit" : "Obchod pro ${provider} nebyl vytvořen. Částka je menší než minimální hodnota: ${min} ${currency}", + "error_text_maximum_limit" : "Obchod pro ${provider} nebyl vytvořen. Částka je větší než maximální hodnota: ${max} ${currency}", + "error_text_limits_loading_failed" : "Obchod pro ${provider} nebyl vytvořen. Selhalo načítání limitů", + "error_text_template" : "Jméno šablony a adresa nemohou obsahovat symboly ` , ' \" \na musí mít délku 1 až 106 znaků", + + + "auth_store_ban_timeout" : "ban_timeout", + "auth_store_banned_for" : "Zablokován na ", + "auth_store_banned_minutes" : " minut", + "auth_store_incorrect_password" : "Nesprávný PIN", + "wallet_store_monero_wallet" : "Monero Wallet", + "wallet_restoration_store_incorrect_seed_length" : "Nesprávná délka seedu", + + + "full_balance" : "Celkový zůstatek", + "available_balance" : "Dostupný zůstatek", + "hidden_balance" : "Skrytý zůstatek", + + + "sync_status_syncronizing" : "SYNCHRONIZUJI", + "sync_status_syncronized" : "SYNCHRONIZOVÁNO", + "sync_status_not_connected" : "NEPŘIPOJENO", + "sync_status_starting_sync" : "SPOUŠTĚNÍ SYNCHRONIZACE", + "sync_status_failed_connect" : "ODPOJENO", + "sync_status_connecting" : "PŘIPOJOVÁNÍ", + "sync_status_connected" : "PŘIPOJENO", + "sync_status_attempting_sync" : "ZAHAJUJI SYNCHR.", + + + "transaction_priority_slow" : "Pomalá", + "transaction_priority_regular" : "Běžná", + "transaction_priority_medium" : "Střední", + "transaction_priority_fast" : "Rychlá", + "transaction_priority_fastest" : "Nejrychlejší", + + + "trade_for_not_created" : "Obchod pro ${title} nebyl vytvořen.", + "trade_not_created" : "Obchod nebyl vytvořen", + "trade_id_not_found" : "Obchod ${tradeId} z ${title} nenalezen.", + "trade_not_found" : "Obchod nenalezen.", + + + "trade_state_pending" : "Čekající", + "trade_state_confirming" : "Ověřování", + "trade_state_trading" : "Obchoduji", + "trade_state_traded" : "Zobchodováno", + "trade_state_complete" : "Kompletní", + "trade_state_to_be_created" : "Bude vytvořen", + "trade_state_unpaid" : "Nezaplaceno", + "trade_state_underpaid" : "Zaplaceno méně", + "trade_state_paid_unconfirmed" : "Nepotvrzeně zaplaceno", + "trade_state_paid" : "Zaplaceno", + "trade_state_btc_sent" : "BTC odesláno", + "trade_state_timeout" : "Vypršel časový limit", + "trade_state_created" : "Vytvořeno", + "trade_state_finished" : "Hotovo", + + "change_language" : "Změnit jazyk", + "change_language_to" : "Změnit jazyk na ${language}?", + + "paste" : "Vložit", + "restore_from_seed_placeholder" : "Prosím zadejte, nebo vložte ze schránky svůj seed.", + "add_new_word" : "Přidat nové slovo", + "incorrect_seed" : "Zadaný text není správný.", + + "biometric_auth_reason" : "Naskenujte otisk prstu pro ověření", + "version" : "Verze ${currentVersion}", + + "extracted_address_content" : "Prostředky budete posílat na\n${recipient_name}", + + "card_address" : "Adresa:", + "buy" : "Koupit", + "sell": "Prodat", + + "placeholder_transactions" : "Vaše transakce budou zobrazeny zde", + "placeholder_contacts" : "Vaše kontakty budou zobrazeny zde", + + "template" : "Šablona", + "confirm_delete_template" : "Tato akce smaže tuto šablonu. Přejete si pokračovat?", + "confirm_delete_wallet" : "Tato akce smaže tuto peněženku. Přejete si pokračovat?", + + "picker_description" : "Pro volbu ChangeNOW, nebo MorphToken si prosím vyberte nejprve pár pro obchodování", + + "change_wallet_alert_title" : "Přepnout peněženku", + "change_wallet_alert_content" : "Opravdu chcete změnit aktivní peněženku na ${wallet_name}?", + + "creating_new_wallet" : "Vytvářím novou peněženku", + "creating_new_wallet_error" : "Chyba: ${description}", + + "seed_alert_title" : "Pozor", + "seed_alert_content" : "Tento seed představuje jedinou možnost, jak obnovit peněženku. Zapsali jste si ho?", + "seed_alert_back" : "Zpět", + "seed_alert_yes" : "Ano", + + "exchange_sync_alert_content" : "Prosím počkejte, dokud nebude vaše peněženka synchronizována", + + "pre_seed_title" : "DŮLEŽITÉ", + "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_button_text" : "Rozumím. Ukaž mi můj seed.", + + "xmr_to_error" : "XMR.TO chyba", + "xmr_to_error_description" : "Neplatná částka. Maximálně lze použít 8 desetinných míst", + + "provider_error" : "${provider} chyba", + + "use_ssl" : "Použít SSL", + "trusted" : "Důvěřovat", + + "color_theme" : "Barevný motiv", + "light_theme" : "Světlý", + "bright_theme" : "Jasný", + "dark_theme" : "Tmavý", + "enter_your_note" : "Zadejte poznámku…", + "note_optional" : "Poznámka (nepovinné)", + "note_tap_to_change" : "Poznámka (poklepáním upravit)", + "view_in_block_explorer" : "Zobrazit v Block Exploreru", + "view_transaction_on" : "Zobrazit transakci na ", + "transaction_key" : "Klíč transakce", + "confirmations" : "Potvrzení", + "recipient_address" : "Adresa příjemce", + + "extra_id" : "Extra ID:", + "destination_tag" : "Destination Tag:", + "memo" : "Memo:", + + "backup" : "Záloha", + "change_password" : "Změnit heslo", + "backup_password" : "Heslo pro zálohy", + "write_down_backup_password" : "Prosím zapište si své heslo pro zálohy, které se používá pro import vašich souborů se zálohami.", + "export_backup" : "Exportovat zálohu", + "save_backup_password" : "Prosím ujistěte se, že máte uschováno heslo pro zálohy. Bez něj nebudete moci naimportovat soubory se zálohami.", + "backup_file" : "Soubor se zálohou", + + "edit_backup_password" : "Upravit heslo pro zálohy", + "save_backup_password_alert" : "Uložit heslo pro zálohy", + "change_backup_password_alert" : "Vaše předchozí soubory se zálohami nebude možné naimportovat s novým heslem. Nové heslo bude použito pouze pro nové zálohy. Opravdu chcete změnit heslo pro zálohy?", + + "enter_backup_password" : "Zde zadejte své heslo pro zálohy", + "select_backup_file" : "Vybrat soubor se zálohou", + "import" : "Importovat", + "please_select_backup_file" : "Prosím vyberte soubor se zálohou a zadejte heslo pro zálohy.", + + "fixed_rate" : "Pevný kurz", + "fixed_rate_alert" : "Když je zvolený pevný kurz, můžete zadat konkrétní částku, kterou chcete dostat. Chcete se přepnout do režimu s pevným kurzem?", + + "xlm_extra_info" : "Prosím nezapomeňte zadat Memo ID, když posíláte XLM transakce ke směně", + "xrp_extra_info" : "Prosím nezapomeňte zadat Destination Tag, když posíláte XRP transakce ke směně", + + "exchange_incorrect_current_wallet_for_xmr" : "Pokud chcete směnit XMR z Monero částky v Cake Wallet, prosím přepněte se nejprve do své Monero peněženky.", + "confirmed" : "Potvrzeno", + "unconfirmed" : "Nepotvrzeno", + "displayable" : "Zobrazitelné", + + "submit_request" : "odeslat požadavek", + + "buy_alert_content" : "V současné době podporujeme nákup pouze Bitcoinu a Litecoinu. Pro nákup Bitcoinu, nebo Litecoinu si prosím vytvořte Bitcoinovou, nebo Litecoinovou peněženku, nebo se do ní přepněte.", + "sell_alert_content": "V současné době podporujeme pouze prodej Bitcoinu. Pro prodej Bitcoinu si prosím vytvořte Bitcoinovou peněženku, nebo se do ní přepněte.", + + "outdated_electrum_wallet_description" : "Nové Bitcoinové peněženky vytvořené v Cake mají nyní seed se 24 slovy. Je třeba si vytvořit novou Bitcoinovou peněženku se 24 slovy, převést na ni všechny prostředky a přestat používat seed se 12 slovy. Prosím udělejte to hned pro zabezpečení svých prostředků.", + "understand" : "Rozumím", + + "apk_update" : "aktualizace APK", + + "buy_bitcoin" : "Nakoupit Bitcoin", + "buy_with" : "Nakoupit pomocí", + "moonpay_alert_text" : "Částka musí být větší nebo rovna ${minAmount} ${fiatCurrency}", + + "outdated_electrum_wallet_receive_warning": "Tato peněženka má seed se 12 slovy a byla vytvořena pomocí Cake, NEUKLÁDEJTE Bitcoin na tuto peněženku. Jakékoliv BTC převedené na tuto peněženku může být ztraceno. Vytvořte si novou peněženku s 24 slovy (otevřete menu vpravo nahoře, vyberte Peněženky, zvolte Vytvořit novou peněženku a pak zvolte Bitcoin) a IHNED tam přesuňte své BTC. Nové (24-slovní) BTC peněženky z Cake jsou bezpečné", + "do_not_show_me": "Příště nezobrazovat", + + "unspent_coins_title" : "Neutracené mince", + "unspent_coins_details_title" : "Podrobnosti o neutracených mincích", + "freeze" : "Zmrazit", + "frozen" : "Zmraženo", + "coin_control" : "Volba mincí (nepovinné)", + + "address_detected" : "Adresa detekována", + "address_from_domain" : "Tato adresa je z ${domain} na Unstoppable Domains", + + "add_receiver" : "Přidat dalšího příjemce (nepovinné)", + + "manage_yats" : "Spravovat Yaty", + "yat_alert_title" : "Posílejte a přijímejte crypto jednodušeji s Yat", + "yat_alert_content" : "Uživatelé Cake Wallet mohou nyní posílat a přijímat všechny své oblíbené měny pomocí něco jako uživatelského jména tvořeného emoji.", + "get_your_yat" : "Získat Yat", + "connect_an_existing_yat" : "Připojit existující Yat", + "connect_yats": "Připojit Yaty", + "yat_address" : "Yat adresa", + "yat" : "Yat", + "address_from_yat" : "Tato adresa je z ${emoji} na Yatu", + "yat_error" : "Yat chyba", + "yat_error_content" : "Žádná adresa není spojena s tímto Yatem. Zkuste jiný Yat", + "choose_address" : "\n\nProsím vyberte adresu:", + "yat_popup_title" : "Adresa Vaší peněženky může být emojifikována.", + "yat_popup_content" : "Nyní můžete posílat a přijímat crypto v Cake Wallet se svým Yatem - krátkým uživatelským jménem složeným z emoji. Spravujte kdykoliv Yaty na stránce s nastavením", + "second_intro_title" : "Jedna emoji adresa vládne všem", + "second_intro_content" : "Váš Yat je jediná unikátní emoji adresa, která nahrazuje všechny Vaše dlouhé hexadecimální adresy pro všechny Vaše měny.", + "third_intro_title" : "Yat dobře spolupracuje s ostatními", + "third_intro_content" : "Yat existuje i mimo Cake Wallet. Jakákoliv adresa peněženky na světě může být nahrazena Yatem!", + "learn_more" : "Zjistit více", + "search": "Hledat", + "search_language": "Hledat jazyk", + "search_currency": "Hledat měnu", + "new_template" : "Nová šablona", + "electrum_address_disclaimer": "Po každém použití je generována nová adresa, ale předchozí adresy také stále fungují", + "wallet_name_exists": "Peněženka s tímto názvem už existuje. Prosím zvolte si jiný název, nebo nejprve přejmenujte nejprve druhou peněženku.", + "market_place": "Obchod", + "cake_pay_title": "Cake Pay dárkové karty", + "cake_pay_subtitle": "Kupte si zlevněné dárkové karty (pouze USA)", + "cake_pay_web_cards_title": "Cake Pay webové karty", + "cake_pay_web_cards_subtitle": "Kupte si celosvětové předplacené a dárkové karty", + "about_cake_pay": "Cake Pay umožňuje jednoduše nakupovat dárkové karty pomocí virtuálních prostředků, které lze okamžitě uplatnit u více než 150 000 obchodníků ve Spojených státech.", + "cake_pay_account_note": "Přihlaste se svou e-mailovou adresou pro zobrazení a nákup karet. Některé jsou dostupné ve slevě!", + "already_have_account": "Máte už účet?", + "create_account": "Vytvořit účet", + "privacy_policy": "Zásady ochrany soukromí", + "welcome_to_cakepay": "Vítejte v Cake Pay!", + "sign_up": "Registrovat se", + "forgot_password": "Zapomenuté heslo", + "reset_password": "Resetovat heslo", + "gift_cards": "Dárkové karty", + "setup_your_debit_card": "Nastavit debetní kartu", + "no_id_required": "Žádní ID není potřeba. Dobijte si a utrácejte kdekoliv", + "how_to_use_card": "Jak použít tuto kartu", + "purchase_gift_card": "Objednat dárkovou kartu", + "verification": "Ověření", + "fill_code": "Prosím vyplňte ověřovací kód zaslaný na Váš e-mail", + "dont_get_code": "Nepřišel Vám kód?", + "resend_code": "Prosím poslat znovu", + "debit_card": "Debetní karta", + "cakepay_prepaid_card": "CakePay předplacená debetní karta", + "no_id_needed": "Žádné ID není potřeba!", + "frequently_asked_questions": "Často kladené otázky", + "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).", + "please_reference_document": "Více informací naleznete v dokumentu níže.", + "cardholder_agreement": "Smlouva držitele karty", + "e_sign_consent": "E-Sign souhlas", + "agree_and_continue": "Souhlasím & pokračovat", + "email_address": "E-mailová adresa", + "agree_to": "Vytvořením účtu souhlasíte s ", + "and": "a", + "enter_code": "Zadejte kód", + "congratulations": "Gratulujeme!", + "you_now_have_debit_card": "Nyní máte debetní kartu", + "min_amount" : "Min: ${value}", + "max_amount" : "Max: ${value}", + "enter_amount": "Zadejte částku", + "billing_address_info": "Při dotazu na fakturační adresu, zadejte svou doručovací adresu", + "order_physical_card": "Objednat fyzickou kartu", + "add_value": "Přidat hodnotu", + "activate": "Aktivovat", + "get_a": "Získejte ", + "digital_and_physical_card": " digitální a fyzické předplacené debetní karty,", + "get_card_note": " které můžete nabít digitální měnou. Žádné další informace nejsou vyžadovány!", + "signup_for_card_accept_terms": "Zaregistrujte se pro kartu a souhlaste s podmínkami.", + "add_fund_to_card": "Všechny předplacené prostředky na kartě (až ${value})", + "use_card_info_two": "Prostředky jsou převedeny na USD, když jsou drženy na předplaceném účtu, nikoliv na digitální měnu.", + "use_card_info_three": "Použijte tuto digitální kartu online nebo bezkontaktními platebními metodami.", + "optionally_order_card": "Volitelně objednat fyzickou kartu.", + "hide_details" : "Skrýt detaily", + "show_details" : "Zobrazit detaily", + "upto": "až ${value}", + "discount": "Ušetříte ${value}%", + "gift_card_amount": "Hodnota dárkové karty", + "bill_amount": "Účtovaná částka", + "you_pay": "Zaplatíte", + "tip": "Spropitné:", + "custom": "vlastní", + "by_cake_pay": "od Cake Pay", + "expires": "Vyprší", + "mm": "MM", + "yy": "YY", + "online": "Online", + "offline": "Offline", + "gift_card_number": "Číslo dárkové karty", + "pin_number": "Číslo PIN", + "total_saving": "Celkem ušetřeno", + "last_30_days": "Posledních 30 dnů", + "avg_savings": "Prům. ušetřeno", + "view_all": "Zobrazit vše", + "active_cards": "Aktivní karty", + "delete_account": "Smazat účet", + "cards": "Karty", + "active": "Aktivní", + "redeemed": "Uplatněné", + "gift_card_balance_note": "Dárkové karty se zbývající částkou se zobrazí zde", + "gift_card_redeemed_note": "Dárkové karty, které jste uplatnili, se zobrazí zde", + "logout": "Odhlásit", + "add_tip": "Přidat spropitné", + "percentageOf": "z ${amount}", + "is_percentage": "je", + "search_category": "Hledat kategorii", + "mark_as_redeemed": "Označit jako uplatněný", + "more_options": "Více možností", + "awaiting_payment_confirmation": "Čeká se na potvrzení platby", + "transaction_sent_notice": "Pokud proces nepokročí během 1 minuty, zkontrolujte block explorer a svůj e-mail.", + "agree": "Souhlasím", + "in_store": "V obchodě", + "generating_gift_card": "Generuji dárkovou kartu", + "payment_was_received": "Vaše platba byla přijata.", + "proceed_after_one_minute": "Pokud proces nepokročí během 1 minuty, zkontrolujte svůj e-mail.", + "order_id": "ID objednávky", + "gift_card_is_generated": "Generuje se dárková karta", + "open_gift_card": "Otevřít dárkovou kartu", + "contact_support": "Kontaktovat podporu", + "gift_cards_unavailable": "Dárkové karty jsou v tuto chvíli dostupné pro zakoupení pouze pomocí Monera, Bitcoinu a Litecoinu.", + "introducing_cake_pay": "Představujeme Cake Pay!", + "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.", + "automatic": "Automatický", + "fixed_pair_not_supported": "Tento pár s pevným kurzem není ve zvolené směnárně podporován", + "variable_pair_not_supported": "Tento pár s tržním kurzem není ve zvolené směnárně podporován", + "none_of_selected_providers_can_exchange": "Žádný ze zvolených poskytovatelů nemůže provést tuto směnu", + "choose_one": "Zvolte si", + "choose_from_available_options": "Zvolte si z dostupných možností:", + "custom_redeem_amount": "Vlastní částka pro uplatnění", + "add_custom_redemption": "Přidat vlastní uplatnění", + "remaining": "zbývá", + "delete_wallet": "Smazat peněženku", + "delete_wallet_confirm_message" : "Opravdu chcete smazat ${wallet_name} peněženku?", + "low_fee": "Nízký poplatek", + "low_fee_alert": "Momentálně máte nastavené nízké poplatky pro transakce. To může způsobovat dlouhé čekání, změnu směnného kurzu, nebo zrušení směny. Doporučujeme nastavit vyšší poplatek.", + "ignor": "Ignorovat", + "use_suggested": "Použít doporučený", + "do_not_share_warning_text" : "Toto nesdílejte s nikým jiným, ani s podporou.\n\nJinak mohou být Vaše prostředky ukradeny!", + "help": "pomoc", + "all_transactions": "Všechny transakce", + "all_trades": "Všechny obchody", + "connection_sync": "Připojení a synch.", + "security_and_backup": "Bezpečnost a zálohy", + "create_backup": "Vytvořit zálohu", + "privacy_settings": "Nastavení soukromí", + "privacy": "Soukromí", + "display_settings": "Nastavení zobrazení", + "other_settings": "Další nastavení", + "require_pin_after": "Vyžadovat PIN po", + "always": "Vždy", + "minutes_to_pin_code": "${minute} minutách", + "disable_exchange": "Zakázat směnárny", + "advanced_privacy_settings": "Pokročilá nastavení soukromí", + "settings_can_be_changed_later": "Tato nastavení mohou být změněna později v nastavení v této aplikaci", + "add_custom_node": "Přidat vlastní uzel", + "disable_fiat": "Zakázat fiat", + "fiat_api": "Fiat API", + "disabled": "Zakázáno", + "enabled": "Povoleno", + "tor_only": "Pouze Tor", + "unmatched_currencies": "Měna vaší současné peněženky neodpovídá té v naskenovaném QR kódu", + "orbot_running_alert": "Prosím ujistěte se, že je Orbot spuštěný, před tím, než se připojíte k tomuto uzlu.", + "contact_list_contacts": "Kontakty", + "contact_list_wallets": "Moje peněženky", + "bitcoin_payments_require_1_confirmation": "U plateb Bitcoinem je vyžadováno alespoň 1 potvrzení, což může trvat 20 minut i déle. Děkujeme za vaši trpělivost! Až bude platba potvrzena, budete informováni e-mailem.", + "send_to_this_address" : "Poslat ${currency} ${tag}na tuto adresu", + "arrive_in_this_address" : "${currency} ${tag}přijde na tuto adresu", + "do_not_send": "Neodesílat", + "error_dialog_content": "Nastala chyba.\n\nProsím odešlete zprávu o chybě naší podpoře, aby mohli zajistit opravu." +} From 032a8c8c3387ab9cb87d54fc4aa21977dc30266a Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Mon, 6 Feb 2023 21:20:43 +0200 Subject: [PATCH 06/59] Add trocador exchange provider --- assets/images/trocador.png | Bin 0 -> 10417 bytes lib/entities/preferences_key.dart | 1 + .../changenow_exchange_provider.dart | 1 + lib/exchange/exchange_provider.dart | 1 + .../exchange_provider_description.dart | 27 +- .../trocador/trocador_exchange_provider.dart | 268 ++++++++++++++++++ lib/exchange/trocador/trocador_request.dart | 21 ++ .../screens/dashboard/widgets/trade_row.dart | 75 +++-- .../advanced_privacy_settings_page.dart | 13 +- lib/src/screens/settings/privacy_page.dart | 31 +- lib/store/dashboard/trade_filter_store.dart | 16 +- lib/store/settings_store.dart | 23 +- .../advanced_privacy_settings_view_model.dart | 37 ++- .../dashboard/dashboard_view_model.dart | 8 +- .../exchange/exchange_trade_view_model.dart | 4 + .../exchange/exchange_view_model.dart | 16 +- .../settings/privacy_settings_view_model.dart | 14 +- lib/view_model/trade_details_view_model.dart | 4 + tool/utils/secret_key.dart | 1 + 19 files changed, 453 insertions(+), 108 deletions(-) create mode 100644 assets/images/trocador.png create mode 100644 lib/exchange/trocador/trocador_exchange_provider.dart create mode 100644 lib/exchange/trocador/trocador_request.dart diff --git a/assets/images/trocador.png b/assets/images/trocador.png new file mode 100644 index 0000000000000000000000000000000000000000..67c9f221c8586f309e22acf4853735ffdd4c3e15 GIT binary patch literal 10417 zcmdUV_dlFX)ILcbB!Uzr!77pHB}HE?YO3CQ3DLVH>J8T=`^zjEvX!sq$l8-|uS^Z~S$KPkwLm+`Y?riOPf{H0AD1a(JVPOuEoWY_>O!5=>X;c;SG)ZKz?Uef3I~Hy8-iH#%N=H8w$$ZKGt~dDt?Q{bh-_Tgd z7Za zorv|7{eWdS3oo2wD0k6O23!knCEJ5AN8DQbRe7x7!Y`GyIeXa8XpM109s1#?UvW)x}(3* zT?Qko=ktiIo8g9je&Y9mmDI0kB?O|(-2OPQT@LH!H?XiY$Eu1Sx_&9|Al~;5Gj1H%>iQJf$LYnSh{%_wqbAh z36sXo9u&0YK^u4EaOouV7HGL$T`hs{54s=9l7qL%-|jP8U(d7JL8yWC$PG52O znt;)+XW+^Iny4quQt~GRPMt4r|1dj$m4haGsv+2u^4cYgPSp+vP5RVX6lh%otz(W| zjf#{O7HwzBCnNQ;RT>&m?L!}~MqOR#tIQqJ3ORGQe62KKBMa)D32O#T>IbJ{u9$4T|ovxE`f&mfgT z70e>uW3pUkNO1N9#IZt8uS?4b(dUS{*s(0-A{tr{<9ds&3*IrQi_JWS>oUv+?mM9x zB+c9AM`>sDBC4I5ZxsKiPPupnQT; zw88}&_-DmfU&nDO@)ahFvs$11elJunxL-kuQ=fhlwyQ1JJVA9t+oLa-xmvat;Uin? zFvrPxaDD_R(_rORsMl} zC}WP#FjW2rzr7#aT|TXqgFWB+Fi7)Bb<(S2k1!BzR)A|11<0o|}h z*ck0~=JF5qgIE|<*={9uw*K& zTpe8oxsZZ==_uD+eGw_Ghzipx)or~a&5MIYD@ZzhBC)wZFV*s^I7$t`s2=g|e`5Yp z^5#0j_VFuEZrZ*`MI7g+@AMAa;Ah-YK!q7-(RD}I-_kgS4a1KfVt6Rt96Dr& zX#b3Szq7lLG1YVA=+=7Y8O_S>_=c3zC*j^|NV8TfVl)5~vq=ALHj$QcEv?wF{BT3* zfgy=GXYfL$);G_vdaOo_#~V(oce3;m_Pkk7LzXjTaF@R;p_IOG)AVt@MK9Vbn%7$6 zKhTzN`8g?Sre4UvZ=xzF2@=i(92fG_`=(e^++|ip)LerwOQwnMtvm>t?NmBEtl?i$ zEIIkX!Aq2l1eR`4Oh$Ez@wa$r+cyPMSQdWMwmRnOkjp)_`|d)z|9X7lS%?REAhfFjjrBF>B_h{AjIl)Td&QyY-IcCF0HC2Q@HdOE(G=LGGfu6#sk{n{|^MdvWa#yKqb9+BCZ$Nj=W zG;QRcJ{uMOE58=YE}fJ$unGNyT-Ym`Nr5U_`^U2hUHM~{OABe^nCdg>hQdSJGB7*# zS{q9oY?CJCA0GZiQk9Q0Vl14Xp*R2Xt7b;G_ZKyDXxyL5%d@_|OLs_je|79w$haNM z?EZKnCXn93rS_Viv7f3Z=L&{L9DHxrIv^>J#0>WTabcP^Kg~eRwl%<7_wkrQEas`rG?@2$Zu-g8A>j_wN{^G#2eE^!IqF? zsEnF$SqgTED(*1)ap*3JJ~kvZ-Kt{s<)P~nr2DQbaYw2AgDPTIoXm8vOecWJQO!Qd zEKqR2U@`dnOE%v;oSaQbn-%YgUf1gL3@6I4n**_?KyHo<(%! z_5fi*R3x+Iq->W>b!{~NZGQa zN&$^UMQ7fE974xk{rTk(5azDHDN$X*15X24;1}Cmrt+I2bZ>UpCDi@Q$`F{PEcg+& zcJ0aNo9DZq1lUm|NLIwL%!gaiKb*8qp#@hq7o{!gy{7JMrS=F@_y6W@K=j4U5UOLc z$1-XeX{g~9&!1I5IQSk(tYD1S26b#6irrD-3P8`T{g6Z6thv*=LD@JC38f;Pm)c`ZyEhw4+_QwubEirrYEEsHc9Q!ku=~+WNQdSd zRg#-z0mRR=g0p;LLd?6-{EAbnTf4T29Es?sOoCOA=4_&VlFObK#k-ubGoxaAe4-s) z>I!32V1nHl>jH^^&bcpc0XU*Z8}kHmeEKktcBtjuxpg{6hN!R;ZBImjiS9-TQgp0m z=5{PP?AWi^)f9YDgKVSePWrb@&;ABl-QG7uD)rZl+z-qW((C5;H%NBa6G_Ko7IO6t zhmCQZ-?#tTRU{-wuVfkCrI&~n7j^VBy$5w(+Q{y(Q<)s(P6~j>qL)ndN+@5Lr^33a zao#2EcJlW@kf^q!X3@rF+MBKqjrYtv#U)i?kH()=oY8emLt~<$gPttZwpw%OYt{_Me3i+wL5?N>Ut5);xp% zV?Pxef)phL(^Vx2Wk53U7+2d6^@yW6|1Vx0JM)Yo#9rQ^U*zXUWl=o*@-1Sv>3VXq zcVC#XN(E1_=l)xbYGP##wt7=t1REJefo(uk_AtM44_NoL3@N#?T+#7#d?>rr{|{fs z$4vZ*l~z=y$2!}EHx)BZRMpXaIZ9Vc)*;(h<@XjWl9Rlb)r=ok@I-p) z7f8?H#bSs=v=Td(5c$wWOx*Ao7tr$X58Njt(_=CbX=RobZCnKG7V^RyYTv|>Ol_8O z48-h>^Yw2Lv$zlfj9UBGUTjT_6xo}yA1rZ-VT)Xb`Xlt7Rq(v^+%GRQSPsO7UBp(} zJLWgZjhCL^SiAe}YnL4y^+Zb~3S5>pQmdk*_ina2?9FSEPGK_-yFK8N8}l{h65@eK z4xRE3?`^*sPw8ZOocJWKU7M`PecTq1*^9*W%ml@v*Ix<@>$uWTrfNI7AFob(w}w|4 z#OLqIE)+dHX`25*k!C!R?iN&VPZWKVcc0^%t~JaQE6gM`C}Wd$>Eq)7rmqQuqQK0|46%}H|=8PQhg5Fubi7!38`=RHFWgleir^>&Z{tW zA&)Jiwmg1s^+cP(lTx%Q0pE1)&SNY-`R$xCa(d>aHxtSRr>Ht)?I(+e^Y0Znd06Hf z6o?Mb^A;w*CvN$lyAwNd`Ndaxu7H9%bDp?mrKE|g9kRjZ#RK_I6SgRQlAJwbhxL?p zD`s*T_hTDew@Ec>UKj65SAx+A%OS>22jBl+{5bvD?Y{_M_8c2fB%)4crY+D zo4RFJ#oadi%DK+?i}OoYBmL5k_x#fABtCl574e9}H$C6zRdBb`$Q{dqBqZuVRZX71 zZk|1U5j{sMRP~_D*`T69t%5}rML8B3Ra>+zvB@33yvBUd;qFtOJ*;N#=P(1K2D!&2h^LYva`n{8r~HcU!>5t&?Z)PKyO#lC)3$)Lznz4;j&Quu)T+1mT8$T-L0b?ns~?1e9oB;96j za%$ftHsPDk>-`meu_JFe?{&F?@I0_;5O-8Mc25lHNV3z32tu0ObbvhvBc(CgyUmi2 zF_SOm1w++xpUd+!cZbZS%NxpvG4qRh@iBrL1OHm5Y{|0|zJQ;SIO{~cz^3|!;Ai#S z*w|;4XWVY}dU9L1s2CQUv?td1rG%8^Af#vS=*wD?VRJi+xE6_1Z&XRBm?F&zWp)4#{UqnS=Z=gFCrZ9FMnPx4MMA8i-(@aQE+AG@WLe*)k1bH->zLN@vYXs z8GnoQSM zrdYy~d&c4j4HY;wzg>BKjf%&vGu=j>EpsXw32A1ab^=>-M7e_sehLS(6GK6>`(bX^*r6mVI)G{*ori3f@U&Cf@~KDbt{LGu}RL?N?N;P!Co^91MD zu0!Da5{E*lQ`AslSO8n$k>|^30H1r%bOcX(&RhgqmKB={0j&pGIkcjFY_;>bIve|%vAE9*4`I8#KeMsT9ckMn5dw_da4y4{i)7X z>93hA7OOg)f14I`D8*|+9Ue!*IeqUDKMIo3DtfwumW@T!u=ayfk8$GlDApD&F^F<$ zIyIN?K56d#lXYbO)=fi9%B^g>yT0>ywoFET=5Z|I!zxav{0S5M@!y{LnpB*l<0h9M z@#ERS&i2V^(p*b~S<6EZ&!!KzxuoL)tKWLkMLb9hR}qzIfzcWq+vl@QIXiB87#V98 zrMl3d)4miQ9mA6S97_(FPv4yRD(CgDmt^6UtTfNDw9{f%HP%$CBuwI)v5dSKb+}me ziPb*S`~ji=?GF7*j~tlIbrk{}u0?9XRwP?r56DSaV=bw%iVL(^H#zh{zC-OeL~R=z!c`p{sJ z#|K`ST$yed_GZ@=mVx=&$1X4+dN^?xq#h+*Selk|&&_03nH#rqRy~pm@m~7ziiz+w z_n+2r+}9&bgrQKdT7%TQgZy(+>*k=NSWw;&<7+E(tbl5h({6OvX<<+>HgVq)H=rOvKhv+A-eFIHC zvx8IFycpCYPWZRDJN);qM@s%KT^As^mPCwL2cB=A$jA(QH+m;GD!u}ZMJTX-y&39M=LNb8+=w`deMGjKh%_ZK$rdK9ScX_4egp?5UrFD+zp?QI&1M($DnXMJxHV{1E6sW&F!@7Yv}93V zhsZu=^5{z3OpeG|y+hLa@XY5C+2-w+DN8w}uB-hE2eo5+!&Y#A!#sP_xbEB$_OzVD zcOAQvHWn?9=xuyoLGA7bbYo%Ik#H4a1WBVlW(Y^ z5EGjxq9UwwY(!)FC_wXvh&(`Rw$0x%o#JbIXZQOxlT61!;l~WTEZ{)GRp`I$yZ6_a zmx$J!Mk=PvlY-yA`Yx(a!75G3@5?4IK!1qgibcaAvBieU7;Tp&_tw~&)CouT6N{Lw zDOkiKiZ`|_^_1Ow1M_c@NuHK!S<*XHA zHOa2EbXC?m#012mnP|E-1!pH9ke=F~rgX0NYffXrmUCw)uo7$IR##)3v2`ERv!-y5 zQ3!SQb4F@?4FXX|Im88GMgUW?v{HTA@Uv;-kL(8(Z~l`>#*PV-0d4w-<-(a;^GC$0 zT93G<-Jm`TI?9FAyDeP%)0-c-50>;%PP}lpk19JU|79{wG_^lLNSRvB3Zov9Re(m_ zVnb1GM~Hns7gSB{Pe zfj|jq!K_EA69w(uZL-llLrVWJ-~8oeJE%h+abM;qh~6hVyde&;Xdb+{V!~B$Q8Se4 zCPL^5UcjTkLTi7I>{NNx1QfYK?&a-zl7ofKb$PWjL&W}otoe%EX&Zq%^*=e}0NuLoilPw|Gh z%b&^sIW#9MhR#015?2_6F^qMq8VCnPpd74j+M+BZULZV_+If;p7@~-2!zK00DNf!M z?QL1pOudoAm#x;|7V$^>D4Rw0uXBf;;?$*x7XAra&oc7k*|BnIx!4m@rzhP!mulA)$VKLu_3W#zwbAkS95UXB^ ze2CF?fKtj#zwt{mNAmtvF~lqloJ61a)=kQQb=QAf2rjd2~g z*XbN)-_VpO03$N_S?zc@)qvJEu!4WM?n+k~XEhF~A|;#T_-bND4>&dBOF_BW-u z+h9~c&Xk;UJC7>fztC0Eg)fqm&&lsPN%N^LaQr~vr1dQ{{yOLAT$RE_UaC(hdbl?F z-|})crKN!Z8(#Qb;+(X~T!B3@h0ChfW!MOC+>I=!56q{Q!2)i_LceI6m)e^OusAO* zXCEYk=uG=`pc6s-!LbKD2LX7H$)+88QFj3J>x^2vw(;IzZ=|s?rRF&*D+0EvD@t1L zo`aVT?h3es1xwV`RRhH#3*WEO?|Y5ysLrP3cS%D3^a;Dm)GG&Bofg4W7`>F-v1 zUm0rKSZv%au?HbObAZ4HN=w$DuxYLopab29ayrF2_sE|6Hf57P+a-==S*QYxP-32O zgvHTXH5%!w^|o+@SBN?UuU(_f!1rISE*eQ`!YIlX>xzs@ie5>5=#aj43G{saF;z6& zdjV6OZIzeXy6YFW##-hG;MI2*AXtFSng2{vn~8@}ZSyCI73;2J$9u>NulS9sPXpB# zlH6dlBn;SBApsWAV%<#$(yK8wgMhp#31AatDsCmjBc|uj8?7!fJg$n9vV0o&{(t3` zQawKA1om86X=kk{brHR|pxlh6#|L#@YGoLMLVHs&A*7e#R?M>$^W4R2r?03nlllK+ z6gLZ{k8(1r5qT-2GefAzv3zpBew7bi35c#tk2p^qt)yzBrLaZ2hcF`z6xXK$6Z)tM z$VLMjK3zbmjE4T~dL)7n3x!qafE<9CZflixg3mgk_Ao1CoaJN3ms~q&=IoyJ<#h zZ2a5n(vQz_@SIQOP4cHaEmf>@DVEc3__ymYoXSaUMexFxqs3}%tJbd7--zU**u1AF zR4b-1MFljtiE49fQ5mG!4F;jDzt8KoS@yj0t|l~?Z(lRH;wh6*1*ADmT*ATAG=c~` zU8%={xiZU`cYgx_;6Y!c+~tMcB-4T|Kb+L3N+u+ssd0@v9j~;~+R;a|C1&N3!rcb4 zFBt9JnJWH(bsZe%)pUHzvV~X@tHFsnPU?b{RaEY;s+5KoL4KHLYC-@S0-niR4p5J# z$!{tM->YUXoWVx2{&k&Bp;GhE z>Xr>kVs%KH)>XZ$xDe>+K=pR^H0|{)ileMEDJ=E3hlUc54M<<)vhX+E_odAm24Elh zB7W~aq#Q8bH7}+;?SoOV06hl2;abTBG?1S_X;3I|(h@DHzD?`g4&u_X5{^?4g!2Zi zZr4y*qcWA(31hTxgK(oi0PniB`i(ewnTXgk12(?oCU}XVXgZ0zSvg6%)=)}Vb&>g$ zVb|mLC4PC_k=U^Y-d0#}HtV-yoZ8AaR-BZK3`ZU8Own+0}%6FBT07 zT6zOEAOpv;-SF?mJ@JQ1T#w|>;8a4BC3H?zkw%uM{Q`QFcEm>K-DfXh`TBf0CqeM# z_cQGj8|f;XxZcTB4p6F7;vhQ!yO7tvdNzY~B9U#}JIfsVpF#-g7EuXqz;=*LAqu4h zE7)$HS9Vla7brbQ!&ID?W9)IC3k;B83yfAVm%|>YoFVfkq;X18ih-C)c6RW(s$R8X z{W>VIcx_&qIR;e)^S@#Xc6M8lN`FDcPoso+;eeJX!WtMm-Qe$@JfFtyoAYcceGGbD z#Ni`iK(J53d>wG6Bny^(gVLTl2BZ`FYzmZY{gASIQV)!e@ypk4>svKb0md_#1bhU$ z|EpG6sx!==vp?so$?6u?&-rCI8QWI1%OPVoNWCG!2FEVR^(Xmj*B*mHnvKwgfz{|t zJo)H;g3BRoHwh3K!L}mKgK!5}-h)80;FluK!~5~0wAYpEyeKWZw?)wpz^kFDxPmzc z7`R|K>PEzk5d?X+;me}vr=ar4+!nf8M@p-9ywL4>Yk@WttIe`-D%cH3Q3zG(7Dcti zX5y8AG&}e|=Y1W11{S2mD<{*B3@jdWom-m~Hjh8J*-Zl4B~>ZhY4;bSg!wYzO3IT8 zbc83RqDv3=oXDo`yFk#62JN+jV!tW?KQ;80?;ea`1 ztaWN~sDQ9g`#L2nvF{#mn2D>;usfW9!N^Z33Zq*Q4M3ScKs$4$z+ zI}sA%gm$4?+c false; @override String toString() => title; diff --git a/lib/exchange/exchange_provider_description.dart b/lib/exchange/exchange_provider_description.dart index 2fd231085..e545f69ce 100644 --- a/lib/exchange/exchange_provider_description.dart +++ b/lib/exchange/exchange_provider_description.dart @@ -1,31 +1,30 @@ import 'package:cw_core/enumerable_item.dart'; -class ExchangeProviderDescription extends EnumerableItem - with Serializable { - const ExchangeProviderDescription({ - required String title, - required int raw, - required this.image, - this.horizontalLogo = false}) +class ExchangeProviderDescription extends EnumerableItem with Serializable { + const ExchangeProviderDescription( + {required String title, required int raw, required this.image, this.horizontalLogo = false}) : super(title: title, raw: raw); final bool horizontalLogo; final String image; - static const xmrto = ExchangeProviderDescription(title: 'XMR.TO', raw: 0, image: 'assets/images/xmrto.png'); + static const xmrto = + ExchangeProviderDescription(title: 'XMR.TO', raw: 0, image: 'assets/images/xmrto.png'); static const changeNow = ExchangeProviderDescription(title: 'ChangeNOW', raw: 1, image: 'assets/images/changenow.png'); static const morphToken = ExchangeProviderDescription(title: 'MorphToken', raw: 2, image: 'assets/images/morph.png'); - static const sideShift = + static const sideShift = ExchangeProviderDescription(title: 'SideShift', raw: 3, image: 'assets/images/sideshift.png'); - static const simpleSwap = - ExchangeProviderDescription(title: 'SimpleSwap', raw: 4, image: 'assets/images/simpleSwap.png'); + static const simpleSwap = ExchangeProviderDescription( + title: 'SimpleSwap', raw: 4, image: 'assets/images/simpleSwap.png'); - static const all = - ExchangeProviderDescription(title: 'All trades', raw: 5, image:''); + static const trocador = + ExchangeProviderDescription(title: 'Trocador', raw: 5, image: 'assets/images/trocador.png'); + + static const all = ExchangeProviderDescription(title: 'All trades', raw: 6, image: ''); static ExchangeProviderDescription deserialize({required int raw}) { switch (raw) { @@ -40,6 +39,8 @@ class ExchangeProviderDescription extends EnumerableItem case 4: return simpleSwap; case 5: + return trocador; + case 6: return all; default: throw Exception('Unexpected token: $raw for ExchangeProviderDescription deserialize'); diff --git a/lib/exchange/trocador/trocador_exchange_provider.dart b/lib/exchange/trocador/trocador_exchange_provider.dart new file mode 100644 index 000000000..18ea27895 --- /dev/null +++ b/lib/exchange/trocador/trocador_exchange_provider.dart @@ -0,0 +1,268 @@ +import 'dart:convert'; + +import 'package:cake_wallet/exchange/exchange_pair.dart'; +import 'package:cake_wallet/exchange/exchange_provider.dart'; +import 'package:cake_wallet/exchange/trade_state.dart'; +import 'package:cake_wallet/exchange/trocador/trocador_request.dart'; +import 'package:cw_core/crypto_currency.dart'; +import 'package:cake_wallet/exchange/trade_request.dart'; +import 'package:cake_wallet/exchange/trade.dart'; +import 'package:cake_wallet/exchange/limits.dart'; +import 'package:cake_wallet/exchange/exchange_provider_description.dart'; +import 'package:cake_wallet/.secrets.g.dart' as secrets; +import 'package:http/http.dart'; + +class TrocadorExchangeProvider extends ExchangeProvider { + TrocadorExchangeProvider() + : _lastUsedRateId = '', + super(pairList: _supportedPairs()); + + static const List _notSupported = [ + CryptoCurrency.xhv, + CryptoCurrency.dcr, + CryptoCurrency.oxt, + CryptoCurrency.pivx, + CryptoCurrency.scrt, + CryptoCurrency.stx, + CryptoCurrency.bttc, + CryptoCurrency.zaddr, + CryptoCurrency.usdcpoly, + CryptoCurrency.maticpoly, + ]; + + static List _supportedPairs() { + final supportedCurrencies = + CryptoCurrency.all.where((element) => !_notSupported.contains(element)).toList(); + + return supportedCurrencies + .map((i) => supportedCurrencies.map((k) => ExchangePair(from: i, to: k, reverse: true))) + .expand((i) => i) + .toList(); + } + + static const onionApiAuthority = 'trocadorfyhlu27aefre5u7zri66gudtzdyelymftvr4yjwcxhfaqsid.onion'; + static const clearNetAuthority = 'trocador.app'; + static const apiKey = secrets.trocadorApiKey; + static const newRatePath = '/api/new_rate'; + static const createTradePath = 'api/new_trade'; + static const tradePath = 'api/trade'; + String _lastUsedRateId; + + @override + Future checkIsAvailable() async => true; + + @override + Future createTrade({required TradeRequest request, required bool isFixedRateMode}) { + final _request = request as TrocadorRequest; + return _createTrade(request: _request, isFixedRateMode: isFixedRateMode); + } + + Future _createTrade({ + required TrocadorRequest request, + required bool isFixedRateMode, + }) async { + final params = { + 'api_key': apiKey, + 'ticker_from': request.from.title.toLowerCase(), + 'ticker_to': request.to.title.toLowerCase(), + 'network_from': _networkFor(request.from), + 'network_to': _networkFor(request.to), + 'payment': isFixedRateMode ? 'True' : 'False', + 'min_kycrating': 'C', + 'markup': '3', + 'best_only': 'True', + if (!isFixedRateMode) 'amount_from': request.fromAmount, + if (isFixedRateMode) 'amount_to': request.toAmount, + 'address': request.address, + 'refund': request.refundAddress + }; + + if (isFixedRateMode) { + await fetchRate( + from: request.from, + to: request.to, + amount: double.tryParse(request.toAmount) ?? 0, + isFixedRateMode: true, + isReceiveAmount: true, + ); + params['id'] = _lastUsedRateId; + } + + final String apiAuthority = shouldUseOnionAddress ? await _getAuthority() : clearNetAuthority; + + final uri = Uri.https(apiAuthority, createTradePath, params); + final response = await get(uri); + + if (response.statusCode == 400) { + final responseJSON = json.decode(response.body) as Map; + final error = responseJSON['error'] as String; + final message = responseJSON['message'] as String; + throw Exception('${error}\n$message'); + } + + if (response.statusCode != 200) { + throw Exception('Unexpected http status: ${response.statusCode}'); + } + + final responseJSON = json.decode(response.body) as Map; + final id = responseJSON['trade_id'] as String; + final inputAddress = responseJSON['address_provider'] as String; + final refundAddress = responseJSON['refund_address'] as String; + final status = responseJSON['status'] as String; + final state = TradeState.deserialize(raw: status); + final payoutAddress = responseJSON['address_user'] as String; + final date = responseJSON['date'] as String; + + return Trade( + id: id, + from: request.from, + to: request.to, + provider: description, + inputAddress: inputAddress, + refundAddress: refundAddress, + state: state, + createdAt: DateTime.tryParse(date)?.toLocal(), + amount: responseJSON['amount_from']?.toString() ?? request.fromAmount, + payoutAddress: payoutAddress); + } + + @override + ExchangeProviderDescription get description => ExchangeProviderDescription.trocador; + + @override + Future fetchLimits( + {required CryptoCurrency from, + required CryptoCurrency to, + required bool isFixedRateMode}) async { + //TODO: implement limits from trocador api + return Limits( + min: 0.0, + ); + } + + @override + Future fetchRate( + {required CryptoCurrency from, + required CryptoCurrency to, + required double amount, + required bool isFixedRateMode, + required bool isReceiveAmount}) async { + try { + if (amount == 0) { + return 0.0; + } + + final String apiAuthority = shouldUseOnionAddress ? await _getAuthority() : clearNetAuthority; + + final params = { + 'api_key': apiKey, + 'ticker_from': from.title.toLowerCase(), + 'ticker_to': to.title.toLowerCase(), + 'network_from': _networkFor(from), + 'network_to': _networkFor(to), + 'amount_from': amount.toString(), + 'amount_to': amount.toString(), + 'payment': isFixedRateMode ? 'True' : 'False', + 'min_kycrating': 'C', + 'markup': '3', + 'best_only': 'True', + }; + + final uri = Uri.https(apiAuthority, newRatePath, params); + final response = await get(uri); + final responseJSON = json.decode(response.body) as Map; + final fromAmount = double.parse(responseJSON['amount_from'].toString()); + final toAmount = double.parse(responseJSON['amount_to'].toString()); + final rateId = responseJSON['trade_id'] as String? ?? ''; + + if (rateId.isNotEmpty) { + _lastUsedRateId = rateId; + } + + return isReceiveAmount ? (amount / fromAmount) : (toAmount / amount); + } catch (e) { + print(e.toString()); + return 0.0; + } + } + + @override + Future findTradeById({required String id}) async { + final String apiAuthority = shouldUseOnionAddress ? await _getAuthority() : clearNetAuthority; + final uri = Uri.https(apiAuthority, tradePath, {'api_key': apiKey, 'id': id}); + return get(uri).then((response) { + if (response.statusCode != 200) { + throw Exception('Unexpected http status: ${response.statusCode}'); + } + + final responseListJson = json.decode(response.body) as List; + + final responseJSON = responseListJson.first; + final id = responseJSON['trade_id'] as String; + final inputAddress = responseJSON['address_user'] as String; + final refundAddress = responseJSON['refund_address'] as String; + final payoutAddress = responseJSON['address_provider'] as String; + final fromAmount = responseJSON['amount_from']?.toString() ?? '0'; + final from = CryptoCurrency.fromString(responseJSON['ticker_from'] as String); + final to = CryptoCurrency.fromString(responseJSON['ticker_to'] as String); + final state = TradeState.deserialize(raw: responseJSON['status'] as String); + final date = DateTime.parse(responseJSON['date'] as String); + + return Trade( + id: id, + from: from, + to: to, + provider: description, + inputAddress: inputAddress, + refundAddress: refundAddress, + createdAt: date, + amount: fromAmount, + state: state, + payoutAddress: payoutAddress, + ); + }); + } + + @override + bool get isAvailable => true; + + @override + bool get isEnabled => true; + + @override + bool get supportsFixedRate => true; + + @override + bool get shouldUseOnionAddress => true; + + @override + String get title => 'Trocador'; + + String _networkFor(CryptoCurrency currency) { + switch (currency) { + case CryptoCurrency.usdt: + return CryptoCurrency.btc.title.toLowerCase(); + default: + return currency.tag != null ? _normalizeTag(currency.tag!) : 'Mainnet'; + } + } + + String _normalizeTag(String tag) { + switch (tag) { + case 'ETH': + return 'ERC20'; + default: + return tag.toLowerCase(); + } + } + + Future _getAuthority() async { + try { + final uri = Uri.https(onionApiAuthority, '/api/trade'); + await get(uri); + return onionApiAuthority; + } catch (e) { + return clearNetAuthority; + } + } +} diff --git a/lib/exchange/trocador/trocador_request.dart b/lib/exchange/trocador/trocador_request.dart new file mode 100644 index 000000000..fbb8fdc84 --- /dev/null +++ b/lib/exchange/trocador/trocador_request.dart @@ -0,0 +1,21 @@ +import 'package:cake_wallet/exchange/trade_request.dart'; +import 'package:cw_core/crypto_currency.dart'; + +class TrocadorRequest extends TradeRequest { + TrocadorRequest( + {required this.from, + required this.to, + required this.address, + required this.fromAmount, + required this.toAmount, + required this.refundAddress, + required this.isReverse}); + + CryptoCurrency from; + CryptoCurrency to; + String address; + String fromAmount; + String toAmount; + String refundAddress; + bool isReverse; +} diff --git a/lib/src/screens/dashboard/widgets/trade_row.dart b/lib/src/screens/dashboard/widgets/trade_row.dart index 21952140e..3b613b0e2 100644 --- a/lib/src/screens/dashboard/widgets/trade_row.dart +++ b/lib/src/screens/dashboard/widgets/trade_row.dart @@ -9,7 +9,8 @@ class TradeRow extends StatelessWidget { required this.to, required this.createdAtFormattedDate, this.onTap, - this.formattedAmount,}); + this.formattedAmount, + }); final VoidCallback? onTap; final ExchangeProviderDescription provider; @@ -35,47 +36,40 @@ class TradeRow extends StatelessWidget { SizedBox(width: 12), Expanded( child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('${from.toString()} → ${to.toString()}', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Theme.of(context).accentTextTheme!.headline2!.backgroundColor! - )), - formattedAmount != null - ? Text(formattedAmount! + ' ' + amountCrypto, - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Theme.of(context).accentTextTheme!.headline2!.backgroundColor! - )) - : Container() - ]), - SizedBox(height: 5), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - if (createdAtFormattedDate != null) - Text(createdAtFormattedDate!, - style: TextStyle( - fontSize: 14, - color: Theme.of(context).textTheme! - .overline!.backgroundColor!)) - ]) - ], - ) - ) + mainAxisSize: MainAxisSize.min, + children: [ + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + Text('${from.toString()} → ${to.toString()}', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: Theme.of(context).accentTextTheme!.headline2!.backgroundColor!)), + formattedAmount != null + ? Text(formattedAmount! + ' ' + amountCrypto, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: + Theme.of(context).accentTextTheme!.headline2!.backgroundColor!)) + : Container() + ]), + SizedBox(height: 5), + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + if (createdAtFormattedDate != null) + Text(createdAtFormattedDate!, + style: TextStyle( + fontSize: 14, + color: Theme.of(context).textTheme!.overline!.backgroundColor!)) + ]) + ], + )) ], ), )); } - Image? _getPoweredImage(ExchangeProviderDescription provider) { - Image? image; + Widget? _getPoweredImage(ExchangeProviderDescription provider) { + Widget? image; switch (provider) { case ExchangeProviderDescription.xmrto: @@ -93,10 +87,15 @@ class TradeRow extends StatelessWidget { case ExchangeProviderDescription.simpleSwap: image = Image.asset('assets/images/simpleSwap.png', width: 36, height: 36); break; + case ExchangeProviderDescription.trocador: + image = ClipRRect( + borderRadius: BorderRadius.circular(50), + child: Image.asset('assets/images/trocador.png', width: 36, height: 36)); + break; default: image = null; } return image; } -} \ No newline at end of file +} diff --git a/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart b/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart index 6c2f996df..2a9f4d291 100644 --- a/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart +++ b/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart @@ -1,7 +1,11 @@ +import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/src/screens/nodes/widgets/node_form.dart'; +import 'package:cake_wallet/src/screens/settings/widgets/settings_choices_cell.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart'; import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart'; import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart'; +import 'package:cake_wallet/view_model/settings/choices_list_item.dart'; +import 'package:cake_wallet/view_model/settings/switcher_list_item.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/generated/i18n.dart'; @@ -48,9 +52,14 @@ class _AdvancedPrivacySettingsBodyState extends State>().map( (item) => Observer( - builder: (_) => SettingsSwitcherCell( + builder: (_) => SettingsChoicesCell(item) + ), + ), + ...widget.privacySettingsViewModel.settings.whereType().map( + (item) => Observer( + builder: (_) => SettingsSwitcherCell( title: item.title, value: item.value(), onValueChange: item.onValueChange, diff --git a/lib/src/screens/settings/privacy_page.dart b/lib/src/screens/settings/privacy_page.dart index 2f15cc225..974f9a3d7 100644 --- a/lib/src/screens/settings/privacy_page.dart +++ b/lib/src/screens/settings/privacy_page.dart @@ -1,6 +1,9 @@ +import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:cake_wallet/src/screens/settings/widgets/settings_choices_cell.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart'; +import 'package:cake_wallet/view_model/settings/choices_list_item.dart'; import 'package:cake_wallet/view_model/settings/privacy_settings_view_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -21,18 +24,22 @@ class PrivacyPage extends BasePage { return Column( mainAxisSize: MainAxisSize.min, children: [ - SettingsSwitcherCell( - title: S.current.disable_fiat, - value: _privacySettingsViewModel.isFiatDisabled, - onValueChange: (BuildContext context, bool value) { - _privacySettingsViewModel.setFiatMode(value); - }), - SettingsSwitcherCell( - title: S.current.disable_exchange, - value: _privacySettingsViewModel.disableExchange, - onValueChange: (BuildContext context, bool value) { - _privacySettingsViewModel.setEnableExchange(value); - }), + SettingsChoicesCell( + ChoicesListItem( + title: S.current.fiat_api, + items: FiatApiMode.all, + selectedItem: _privacySettingsViewModel.fiatApi, + onItemSelected: (FiatApiMode mode) => _privacySettingsViewModel.setFiatMode(mode), + ), + ), + SettingsChoicesCell( + ChoicesListItem( + title: S.current.exchange, + items: FiatApiMode.all, + selectedItem: _privacySettingsViewModel.exchangeStatus, + onItemSelected: (FiatApiMode mode) => _privacySettingsViewModel.setEnableExchange(mode), + ), + ), SettingsSwitcherCell( title: S.current.settings_save_recipient_address, value: _privacySettingsViewModel.shouldSaveRecipientAddress, diff --git a/lib/store/dashboard/trade_filter_store.dart b/lib/store/dashboard/trade_filter_store.dart index 87fa749a9..c772a35d6 100644 --- a/lib/store/dashboard/trade_filter_store.dart +++ b/lib/store/dashboard/trade_filter_store.dart @@ -12,7 +12,8 @@ abstract class TradeFilterStoreBase with Store { displayChangeNow = true, displaySideShift = true, displayMorphToken = true, - displaySimpleSwap = true; + displaySimpleSwap = true, + displayTrocador = true; @observable bool displayXMRTO; @@ -29,8 +30,11 @@ abstract class TradeFilterStoreBase with Store { @observable bool displaySimpleSwap; + @observable + bool displayTrocador; + @computed - bool get displayAllTrades => displayChangeNow && displaySideShift && displaySimpleSwap; + bool get displayAllTrades => displayChangeNow && displaySideShift && displaySimpleSwap && displayTrocador; @action void toggleDisplayExchange(ExchangeProviderDescription provider) { @@ -50,6 +54,9 @@ abstract class TradeFilterStoreBase with Store { case ExchangeProviderDescription.morphToken: displayMorphToken = !displayMorphToken; break; + case ExchangeProviderDescription.trocador: + displayTrocador = !displayTrocador; + break; case ExchangeProviderDescription.all: if (displayAllTrades) { displayChangeNow = false; @@ -57,12 +64,14 @@ abstract class TradeFilterStoreBase with Store { displayXMRTO = false; displayMorphToken = false; displaySimpleSwap = false; + displayTrocador = false; } else { displayChangeNow = true; displaySideShift = true; displayXMRTO = true; displayMorphToken = true; displaySimpleSwap = true; + displayTrocador = true; } break; } @@ -88,7 +97,8 @@ abstract class TradeFilterStoreBase with Store { ExchangeProviderDescription.morphToken) ||(displaySimpleSwap && item.trade.provider == - ExchangeProviderDescription.simpleSwap)) + ExchangeProviderDescription.simpleSwap) + ||(displayTrocador && item.trade.provider == ExchangeProviderDescription.trocador)) .toList() : _trades; } diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index b10e0d08d..989fdae33 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -31,7 +31,7 @@ abstract class SettingsStoreBase with Store { required bool initialSaveRecipientAddress, required FiatApiMode initialFiatMode, required bool initialAllowBiometricalAuthentication, - required bool initialExchangeEnabled, + required FiatApiMode initialExchangeStatus, required ThemeBase initialTheme, required int initialPinLength, required String initialLanguageCode, @@ -53,7 +53,7 @@ abstract class SettingsStoreBase with Store { shouldSaveRecipientAddress = initialSaveRecipientAddress, fiatApiMode = initialFiatMode, allowBiometricalAuthentication = initialAllowBiometricalAuthentication, - disableExchange = initialExchangeEnabled, + exchangeStatus = initialExchangeStatus, currentTheme = initialTheme, pinCodeLength = initialPinLength, languageCode = initialLanguageCode, @@ -153,9 +153,9 @@ abstract class SettingsStoreBase with Store { PreferencesKey.currentBalanceDisplayModeKey, mode.serialize())); reaction( - (_) => disableExchange, - (bool disableExchange) => sharedPreferences.setBool( - PreferencesKey.disableExchangeKey, disableExchange)); + (_) => exchangeStatus, + (FiatApiMode mode) => sharedPreferences.setInt( + PreferencesKey.exchangeStatusKey, mode.serialize())); this .nodes @@ -192,7 +192,7 @@ abstract class SettingsStoreBase with Store { bool allowBiometricalAuthentication; @observable - bool disableExchange; + FiatApiMode exchangeStatus; @observable ThemeBase currentTheme; @@ -284,8 +284,9 @@ abstract class SettingsStoreBase with Store { final allowBiometricalAuthentication = sharedPreferences .getBool(PreferencesKey.allowBiometricalAuthenticationKey) ?? false; - final disableExchange = sharedPreferences - .getBool(PreferencesKey.disableExchangeKey) ?? false; + final exchangeStatus = FiatApiMode.deserialize( + raw: sharedPreferences + .getInt(PreferencesKey.exchangeStatusKey) ?? FiatApiMode.enabled.raw); final legacyTheme = (sharedPreferences.getBool(PreferencesKey.isDarkThemeLegacy) ?? false) ? ThemeType.dark.index @@ -354,7 +355,7 @@ abstract class SettingsStoreBase with Store { initialSaveRecipientAddress: shouldSaveRecipientAddress, initialFiatMode: currentFiatApiMode, initialAllowBiometricalAuthentication: allowBiometricalAuthentication, - initialExchangeEnabled: disableExchange, + initialExchangeStatus: exchangeStatus, initialTheme: savedTheme, actionlistDisplayMode: actionListDisplayMode, initialPinLength: pinLength, @@ -400,7 +401,9 @@ abstract class SettingsStoreBase with Store { allowBiometricalAuthentication = sharedPreferences .getBool(PreferencesKey.allowBiometricalAuthenticationKey) ?? allowBiometricalAuthentication; - disableExchange = sharedPreferences.getBool(PreferencesKey.disableExchangeKey) ?? disableExchange; + exchangeStatus = FiatApiMode.deserialize( + raw: sharedPreferences + .getInt(PreferencesKey.exchangeStatusKey) ?? FiatApiMode.enabled.raw); final legacyTheme = (sharedPreferences.getBool(PreferencesKey.isDarkThemeLegacy) ?? false) ? ThemeType.dark.index diff --git a/lib/view_model/advanced_privacy_settings_view_model.dart b/lib/view_model/advanced_privacy_settings_view_model.dart index f800e3418..80b62fdd0 100644 --- a/lib/view_model/advanced_privacy_settings_view_model.dart +++ b/lib/view_model/advanced_privacy_settings_view_model.dart @@ -1,5 +1,7 @@ import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/store/settings_store.dart'; +import 'package:cake_wallet/view_model/settings/choices_list_item.dart'; +import 'package:cake_wallet/view_model/settings/settings_list_item.dart'; import 'package:cake_wallet/view_model/settings/switcher_list_item.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:mobx/mobx.dart'; @@ -14,18 +16,19 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store { AdvancedPrivacySettingsViewModelBase(this.type, this._settingsStore) : _addCustomNode = false { settings = [ - SwitcherListItem( - title: S.current.disable_fiat, - value: () => _settingsStore.fiatApiMode == FiatApiMode.disabled, - onValueChange: (_, bool value) => setFiatMode(value), - ), - SwitcherListItem( - title: S.current.disable_exchange, - value: () => _settingsStore.disableExchange, - onValueChange: (_, bool value) { - _settingsStore.disableExchange = value; - }, - ), + ChoicesListItem( + title: S.current.fiat_api, + items: FiatApiMode.all, + selectedItem: _settingsStore.fiatApiMode, + onItemSelected: (FiatApiMode mode) => setFiatMode(mode), + ), + + ChoicesListItem( + title: S.current.exchange, + items: FiatApiMode.all, + selectedItem: _settingsStore.exchangeStatus, + onItemSelected: (FiatApiMode mode) => _settingsStore.exchangeStatus = mode, + ), SwitcherListItem( title: S.current.add_custom_node, value: () => _addCustomNode, @@ -34,7 +37,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store { ]; } - late List settings; + late List settings; @observable bool _addCustomNode = false; @@ -46,11 +49,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store { bool get addCustomNode => _addCustomNode; @action - void setFiatMode(bool value) { - if (value) { - _settingsStore.fiatApiMode = FiatApiMode.disabled; - return; - } - _settingsStore.fiatApiMode = FiatApiMode.enabled; + void setFiatMode(FiatApiMode value) { + _settingsStore.fiatApiMode = value; } } diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 57720d92f..dab2bafd1 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/wallet_type_utils.dart'; import 'package:cw_core/transaction_history.dart'; import 'package:cw_core/balance.dart'; @@ -96,6 +97,11 @@ abstract class DashboardViewModelBase with Store { caption: ExchangeProviderDescription.simpleSwap.title, onChanged: () => tradeFilterStore .toggleDisplayExchange(ExchangeProviderDescription.simpleSwap)), + FilterItem( + value: () => tradeFilterStore.displayTrocador, + caption: ExchangeProviderDescription.trocador.title, + onChanged: () => tradeFilterStore + .toggleDisplayExchange(ExchangeProviderDescription.trocador)), ] }, subname = '', @@ -268,7 +274,7 @@ abstract class DashboardViewModelBase with Store { settingsStore.shouldShowYatPopup = shouldShow; @computed - bool get isEnabledExchangeAction => !settingsStore.disableExchange; + bool get isEnabledExchangeAction => settingsStore.exchangeStatus != FiatApiMode.disabled; @observable bool hasExchangeAction; diff --git a/lib/view_model/exchange/exchange_trade_view_model.dart b/lib/view_model/exchange/exchange_trade_view_model.dart index 541b74396..94c874979 100644 --- a/lib/view_model/exchange/exchange_trade_view_model.dart +++ b/lib/view_model/exchange/exchange_trade_view_model.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:cake_wallet/exchange/sideshift/sideshift_exchange_provider.dart'; import 'package:cake_wallet/exchange/simpleswap/simpleswap_exchange_provider.dart'; +import 'package:cake_wallet/exchange/trocador/trocador_exchange_provider.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cake_wallet/exchange/changenow/changenow_exchange_provider.dart'; @@ -46,6 +47,9 @@ abstract class ExchangeTradeViewModelBase with Store { case ExchangeProviderDescription.simpleSwap: _provider = SimpleSwapExchangeProvider(); break; + case ExchangeProviderDescription.trocador: + _provider = TrocadorExchangeProvider(); + break; } _updateItems(); diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index 5c1f696b8..f9f5683d9 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -7,6 +7,8 @@ import 'package:cake_wallet/exchange/sideshift/sideshift_exchange_provider.dart' import 'package:cake_wallet/exchange/sideshift/sideshift_request.dart'; import 'package:cake_wallet/exchange/simpleswap/simpleswap_exchange_provider.dart'; import 'package:cake_wallet/exchange/simpleswap/simpleswap_request.dart'; +import 'package:cake_wallet/exchange/trocador/trocador_exchange_provider.dart'; +import 'package:cake_wallet/exchange/trocador/trocador_request.dart'; import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/crypto_currency.dart'; @@ -60,7 +62,7 @@ abstract class ExchangeViewModelBase with Store { limitsState = LimitsInitialState(), receiveCurrency = wallet.currency, depositCurrency = wallet.currency, - providerList = [ChangeNowExchangeProvider(), SideShiftExchangeProvider(), SimpleSwapExchangeProvider()], + providerList = [ChangeNowExchangeProvider(), SideShiftExchangeProvider(), SimpleSwapExchangeProvider(), TrocadorExchangeProvider()], selectedProviders = ObservableList() { const excludeDepositCurrencies = [CryptoCurrency.btt, CryptoCurrency.nano]; const excludeReceiveCurrencies = [CryptoCurrency.xlm, CryptoCurrency.xrp, @@ -449,6 +451,18 @@ abstract class ExchangeViewModelBase with Store { amount = isFixedRateMode ? receiveAmount : depositAmount; } + if (provider is TrocadorExchangeProvider) { + request = TrocadorRequest( + from: depositCurrency, + to: receiveCurrency, + fromAmount: depositAmount.replaceAll(',', '.'), + toAmount: receiveAmount.replaceAll(',', '.'), + refundAddress: depositAddress, + address: receiveAddress, + isReverse: isFixedRateMode); + amount = isFixedRateMode ? receiveAmount : depositAmount; + } + amount = amount.replaceAll(',', '.'); if (limitsState is LimitsLoadedSuccessfully) { diff --git a/lib/view_model/settings/privacy_settings_view_model.dart b/lib/view_model/settings/privacy_settings_view_model.dart index f8c3e5b50..db345d0f2 100644 --- a/lib/view_model/settings/privacy_settings_view_model.dart +++ b/lib/view_model/settings/privacy_settings_view_model.dart @@ -12,27 +12,23 @@ abstract class PrivacySettingsViewModelBase with Store { final SettingsStore _settingsStore; @computed - bool get disableExchange => _settingsStore.disableExchange; + FiatApiMode get exchangeStatus => _settingsStore.exchangeStatus; @computed bool get shouldSaveRecipientAddress => _settingsStore.shouldSaveRecipientAddress; @computed - bool get isFiatDisabled => _settingsStore.fiatApiMode == FiatApiMode.disabled; + FiatApiMode get fiatApi => _settingsStore.fiatApiMode; @action void setShouldSaveRecipientAddress(bool value) => _settingsStore.shouldSaveRecipientAddress = value; @action - void setEnableExchange(bool value) => _settingsStore.disableExchange = value; + void setEnableExchange(FiatApiMode value) => _settingsStore.exchangeStatus = value; @action - void setFiatMode(bool value) { - if (value) { - _settingsStore.fiatApiMode = FiatApiMode.disabled; - return; - } - _settingsStore.fiatApiMode = FiatApiMode.enabled; + void setFiatMode(FiatApiMode value) { + _settingsStore.fiatApiMode = value; } } diff --git a/lib/view_model/trade_details_view_model.dart b/lib/view_model/trade_details_view_model.dart index 5a1f78774..97bb40fe4 100644 --- a/lib/view_model/trade_details_view_model.dart +++ b/lib/view_model/trade_details_view_model.dart @@ -6,6 +6,7 @@ import 'package:cake_wallet/exchange/morphtoken/morphtoken_exchange_provider.dar import 'package:cake_wallet/exchange/sideshift/sideshift_exchange_provider.dart'; import 'package:cake_wallet/exchange/simpleswap/simpleswap_exchange_provider.dart'; import 'package:cake_wallet/exchange/trade.dart'; +import 'package:cake_wallet/exchange/trocador/trocador_exchange_provider.dart'; import 'package:cake_wallet/exchange/xmrto/xmrto_exchange_provider.dart'; import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/utils/date_formatter.dart'; @@ -48,6 +49,9 @@ abstract class TradeDetailsViewModelBase with Store { case ExchangeProviderDescription.simpleSwap: _provider = SimpleSwapExchangeProvider(); break; + case ExchangeProviderDescription.trocador: + _provider = TrocadorExchangeProvider(); + break; } items = ObservableList(); diff --git a/tool/utils/secret_key.dart b/tool/utils/secret_key.dart index e5202a829..ff74da8a7 100644 --- a/tool/utils/secret_key.dart +++ b/tool/utils/secret_key.dart @@ -29,6 +29,7 @@ class SecretKey { SecretKey('anypayToken', () => ''), SecretKey('onramperApiKey', () => ''), SecretKey('ioniaClientId', () => ''), + SecretKey('trocadorApiKey', () => ''), ]; final String name; From 37045578c27f3edc368fd4a5f5b71f148af62815 Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Mon, 6 Feb 2023 21:42:17 +0200 Subject: [PATCH 07/59] Check if fixed rate --- lib/exchange/trocador/trocador_exchange_provider.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/exchange/trocador/trocador_exchange_provider.dart b/lib/exchange/trocador/trocador_exchange_provider.dart index 18ea27895..6babc8e94 100644 --- a/lib/exchange/trocador/trocador_exchange_provider.dart +++ b/lib/exchange/trocador/trocador_exchange_provider.dart @@ -160,8 +160,8 @@ class TrocadorExchangeProvider extends ExchangeProvider { 'ticker_to': to.title.toLowerCase(), 'network_from': _networkFor(from), 'network_to': _networkFor(to), - 'amount_from': amount.toString(), - 'amount_to': amount.toString(), + if (!isFixedRateMode) 'amount_from': amount.toString(), + if (isFixedRateMode) 'amount_to': amount.toString(), 'payment': isFixedRateMode ? 'True' : 'False', 'min_kycrating': 'C', 'markup': '3', From a5179939615ddbb2314090abda4d2b466daa19de Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Mon, 6 Feb 2023 22:20:46 +0200 Subject: [PATCH 08/59] add trocador key to secrets --- .github/workflows/pr_test_build.yml | 2 ++ lib/exchange/trocador/trocador_exchange_provider.dart | 5 +++-- tool/utils/secret_key.dart | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index c3c61865f..1766bbe86 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -111,6 +111,8 @@ jobs: echo "const anypayToken = '${{ secrets.ANY_PAY_TOKEN }}';" >> lib/.secrets.g.dart echo "const ioniaClientId = '${{ secrets.IONIA_CLIENT_ID }}';" >> lib/.secrets.g.dart echo "const twitterBearerToken = '${{ secrets.TWITTER_BEARER_TOKEN }}';" >> lib/.secrets.g.dart + echo "const trocadorApiKey = '${{ secrets.TROCADOR_API_KEY }}';" >> lib/.secrets.g.dart + echo "const trocadorExchangeMarkup = '${{ secrets.TROCADOR_EXCHANGE_MARKUP }}';" >> lib/.secrets.g.dart - name: Rename app run: sed -i -e "s/\${APP_NAME}/$GITHUB_HEAD_REF/g" /opt/android/cake_wallet/android/app/src/main/AndroidManifest.xml diff --git a/lib/exchange/trocador/trocador_exchange_provider.dart b/lib/exchange/trocador/trocador_exchange_provider.dart index 6babc8e94..9785b67c2 100644 --- a/lib/exchange/trocador/trocador_exchange_provider.dart +++ b/lib/exchange/trocador/trocador_exchange_provider.dart @@ -43,6 +43,7 @@ class TrocadorExchangeProvider extends ExchangeProvider { static const onionApiAuthority = 'trocadorfyhlu27aefre5u7zri66gudtzdyelymftvr4yjwcxhfaqsid.onion'; static const clearNetAuthority = 'trocador.app'; static const apiKey = secrets.trocadorApiKey; + static const markup = secrets.trocadorExchangeMarkup; static const newRatePath = '/api/new_rate'; static const createTradePath = 'api/new_trade'; static const tradePath = 'api/trade'; @@ -69,7 +70,7 @@ class TrocadorExchangeProvider extends ExchangeProvider { 'network_to': _networkFor(request.to), 'payment': isFixedRateMode ? 'True' : 'False', 'min_kycrating': 'C', - 'markup': '3', + 'markup': markup, 'best_only': 'True', if (!isFixedRateMode) 'amount_from': request.fromAmount, if (isFixedRateMode) 'amount_to': request.toAmount, @@ -164,7 +165,7 @@ class TrocadorExchangeProvider extends ExchangeProvider { if (isFixedRateMode) 'amount_to': amount.toString(), 'payment': isFixedRateMode ? 'True' : 'False', 'min_kycrating': 'C', - 'markup': '3', + 'markup': markup, 'best_only': 'True', }; diff --git a/tool/utils/secret_key.dart b/tool/utils/secret_key.dart index 8ac236088..68308d1c6 100644 --- a/tool/utils/secret_key.dart +++ b/tool/utils/secret_key.dart @@ -25,6 +25,7 @@ class SecretKey { SecretKey('onramperApiKey', () => ''), SecretKey('ioniaClientId', () => ''), SecretKey('trocadorApiKey', () => ''), + SecretKey('trocadorExchangeMarkup', () => '3'), SecretKey('twitterBearerToken', () => ''), ]; From bf73fb24e575faaedadea451ac86dd9a73d8acf9 Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Mon, 6 Feb 2023 23:18:54 +0200 Subject: [PATCH 09/59] [skip ci] remove default value for trocador --- tool/utils/secret_key.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tool/utils/secret_key.dart b/tool/utils/secret_key.dart index 68308d1c6..c27bc5ff7 100644 --- a/tool/utils/secret_key.dart +++ b/tool/utils/secret_key.dart @@ -25,7 +25,7 @@ class SecretKey { SecretKey('onramperApiKey', () => ''), SecretKey('ioniaClientId', () => ''), SecretKey('trocadorApiKey', () => ''), - SecretKey('trocadorExchangeMarkup', () => '3'), + SecretKey('trocadorExchangeMarkup', () => ''), SecretKey('twitterBearerToken', () => ''), ]; From f770e095a1b765984aa4e8bff5ccb04592b0cc9a Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Tue, 7 Feb 2023 17:12:34 +0200 Subject: [PATCH 10/59] Fix advance privacy setting set state --- .../advanced_privacy_settings_page.dart | 66 ++++++++++--------- .../advanced_privacy_settings_view_model.dart | 47 +++++-------- 2 files changed, 53 insertions(+), 60 deletions(-) diff --git a/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart b/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart index 2a9f4d291..7d720e4e5 100644 --- a/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart +++ b/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart @@ -5,7 +5,6 @@ import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell. import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart'; import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart'; import 'package:cake_wallet/view_model/settings/choices_list_item.dart'; -import 'package:cake_wallet/view_model/settings/switcher_list_item.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/generated/i18n.dart'; @@ -49,38 +48,43 @@ class _AdvancedPrivacySettingsBodyState extends State>().map( - (item) => Observer( - builder: (_) => SettingsChoicesCell(item) - ), - ), - ...widget.privacySettingsViewModel.settings.whereType().map( - (item) => Observer( - builder: (_) => SettingsSwitcherCell( - title: item.title, - value: item.value(), - onValueChange: item.onValueChange, + content: Observer( + builder: (_) => Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SettingsChoicesCell( + ChoicesListItem( + title: S.current.fiat_api, + items: FiatApiMode.all, + selectedItem: widget.privacySettingsViewModel.fiatApi, + onItemSelected: (FiatApiMode mode) => + widget.privacySettingsViewModel.setFiatMode(mode), ), ), - ), - Observer( - builder: (_) { - if (widget.privacySettingsViewModel.addCustomNode) { - return Padding( - padding: EdgeInsets.only(left: 24, right: 24, top: 24), - child: NodeForm( - formKey: _formKey, - nodeViewModel: widget.nodeViewModel, - ), - ); - } - return const SizedBox(); - }, - ), - ], + SettingsChoicesCell( + ChoicesListItem( + title: S.current.exchange, + items: FiatApiMode.all, + selectedItem: widget.privacySettingsViewModel.exchangeStatus, + onItemSelected: (FiatApiMode mode) => + widget.privacySettingsViewModel.setEnableExchange(mode), + ), + ), + SettingsSwitcherCell( + title: S.current.add_custom_node, + value: widget.privacySettingsViewModel.addCustomNode, + onValueChange: (_, __) => widget.privacySettingsViewModel.toggleAddCustomNode(), + ), + if (widget.privacySettingsViewModel.addCustomNode) + Padding( + padding: EdgeInsets.only(left: 24, right: 24, top: 24), + child: NodeForm( + formKey: _formKey, + nodeViewModel: widget.nodeViewModel, + ), + ) + ], + ), ), bottomSectionPadding: EdgeInsets.all(24), bottomSection: Column( diff --git a/lib/view_model/advanced_privacy_settings_view_model.dart b/lib/view_model/advanced_privacy_settings_view_model.dart index 80b62fdd0..7d526047d 100644 --- a/lib/view_model/advanced_privacy_settings_view_model.dart +++ b/lib/view_model/advanced_privacy_settings_view_model.dart @@ -1,11 +1,7 @@ import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/store/settings_store.dart'; -import 'package:cake_wallet/view_model/settings/choices_list_item.dart'; -import 'package:cake_wallet/view_model/settings/settings_list_item.dart'; -import 'package:cake_wallet/view_model/settings/switcher_list_item.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:mobx/mobx.dart'; -import 'package:cake_wallet/generated/i18n.dart'; part 'advanced_privacy_settings_view_model.g.dart'; @@ -13,36 +9,19 @@ class AdvancedPrivacySettingsViewModel = AdvancedPrivacySettingsViewModelBase with _$AdvancedPrivacySettingsViewModel; abstract class AdvancedPrivacySettingsViewModelBase with Store { - AdvancedPrivacySettingsViewModelBase(this.type, this._settingsStore) - : _addCustomNode = false { - settings = [ - ChoicesListItem( - title: S.current.fiat_api, - items: FiatApiMode.all, - selectedItem: _settingsStore.fiatApiMode, - onItemSelected: (FiatApiMode mode) => setFiatMode(mode), - ), - - ChoicesListItem( - title: S.current.exchange, - items: FiatApiMode.all, - selectedItem: _settingsStore.exchangeStatus, - onItemSelected: (FiatApiMode mode) => _settingsStore.exchangeStatus = mode, - ), - SwitcherListItem( - title: S.current.add_custom_node, - value: () => _addCustomNode, - onValueChange: (_, bool value) => _addCustomNode = value, - ), - ]; - } + AdvancedPrivacySettingsViewModelBase(this.type, this._settingsStore) : _addCustomNode = false; - late List settings; + @computed + FiatApiMode get exchangeStatus => _settingsStore.exchangeStatus; + + @computed + FiatApiMode get fiatApi => _settingsStore.fiatApiMode; @observable bool _addCustomNode = false; final WalletType type; + final SettingsStore _settingsStore; @computed @@ -50,6 +29,16 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store { @action void setFiatMode(FiatApiMode value) { - _settingsStore.fiatApiMode = value; + _settingsStore.fiatApiMode = value; + } + + @action + void setEnableExchange(FiatApiMode value) { + _settingsStore.exchangeStatus = value; + } + + @action + void toggleAddCustomNode() { + _addCustomNode = !_addCustomNode; } } From f1b0a5adb6369b66d2e104083f5a1bfe4dd1485c Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Tue, 7 Feb 2023 19:39:39 +0200 Subject: [PATCH 11/59] Add trocador provider and password to transaction details --- lib/exchange/trade.dart | 51 +++++++++++-------- .../trocador/trocador_exchange_provider.dart | 12 +++++ lib/view_model/trade_details_view_model.dart | 47 ++++++++--------- 3 files changed, 67 insertions(+), 43 deletions(-) diff --git a/lib/exchange/trade.dart b/lib/exchange/trade.dart index f20a080b7..70dfa5713 100644 --- a/lib/exchange/trade.dart +++ b/lib/exchange/trade.dart @@ -8,21 +8,25 @@ part 'trade.g.dart'; @HiveType(typeId: Trade.typeId) class Trade extends HiveObject { - Trade( - {required this.id, - required this.amount, - ExchangeProviderDescription? provider, - CryptoCurrency? from, - CryptoCurrency? to, - TradeState? state, - this.createdAt, - this.expiredAt, - this.inputAddress, - this.extraId, - this.outputTransaction, - this.refundAddress, - this.walletId, - this.payoutAddress}) { + Trade({ + required this.id, + required this.amount, + ExchangeProviderDescription? provider, + CryptoCurrency? from, + CryptoCurrency? to, + TradeState? state, + this.createdAt, + this.expiredAt, + this.inputAddress, + this.extraId, + this.outputTransaction, + this.refundAddress, + this.walletId, + this.payoutAddress, + this.password, + this.providerId, + this.providerName, + }) { if (provider != null) { providerRaw = provider.raw; } @@ -92,16 +96,23 @@ class Trade extends HiveObject { @HiveField(13) String? payoutAddress; + @HiveField(14) + String? password; + + @HiveField(15) + String? providerId; + + @HiveField(16) + String? providerName; + static Trade fromMap(Map map) { return Trade( id: map['id'] as String, - provider: ExchangeProviderDescription.deserialize( - raw: map['provider'] as int), + provider: ExchangeProviderDescription.deserialize(raw: map['provider'] as int), from: CryptoCurrency.deserialize(raw: map['input'] as int), to: CryptoCurrency.deserialize(raw: map['output'] as int), - createdAt: map['date'] != null - ? DateTime.fromMillisecondsSinceEpoch(map['date'] as int) - : null, + createdAt: + map['date'] != null ? DateTime.fromMillisecondsSinceEpoch(map['date'] as int) : null, amount: map['amount'] as String, walletId: map['wallet_id'] as String); } diff --git a/lib/exchange/trocador/trocador_exchange_provider.dart b/lib/exchange/trocador/trocador_exchange_provider.dart index 9785b67c2..ecdeebe84 100644 --- a/lib/exchange/trocador/trocador_exchange_provider.dart +++ b/lib/exchange/trocador/trocador_exchange_provider.dart @@ -113,6 +113,9 @@ class TrocadorExchangeProvider extends ExchangeProvider { final state = TradeState.deserialize(raw: status); final payoutAddress = responseJSON['address_user'] as String; final date = responseJSON['date'] as String; + final password = responseJSON['password'] as String; + final providerId = responseJSON['id_provider'] as String; + final providerName = responseJSON['provider'] as String; return Trade( id: id, @@ -122,6 +125,9 @@ class TrocadorExchangeProvider extends ExchangeProvider { inputAddress: inputAddress, refundAddress: refundAddress, state: state, + password: password, + providerId: providerId, + providerName: providerName, createdAt: DateTime.tryParse(date)?.toLocal(), amount: responseJSON['amount_from']?.toString() ?? request.fromAmount, payoutAddress: payoutAddress); @@ -208,6 +214,9 @@ class TrocadorExchangeProvider extends ExchangeProvider { final to = CryptoCurrency.fromString(responseJSON['ticker_to'] as String); final state = TradeState.deserialize(raw: responseJSON['status'] as String); final date = DateTime.parse(responseJSON['date'] as String); + final password = responseJSON['password'] as String; + final providerId = responseJSON['id_provider'] as String; + final providerName = responseJSON['provider'] as String; return Trade( id: id, @@ -220,6 +229,9 @@ class TrocadorExchangeProvider extends ExchangeProvider { amount: fromAmount, state: state, payoutAddress: payoutAddress, + password: password, + providerId: providerId, + providerName: providerName, ); }); } diff --git a/lib/view_model/trade_details_view_model.dart b/lib/view_model/trade_details_view_model.dart index 97bb40fe4..3a6f1ec74 100644 --- a/lib/view_model/trade_details_view_model.dart +++ b/lib/view_model/trade_details_view_model.dart @@ -23,16 +23,15 @@ import 'package:cake_wallet/src/screens/trade_details/trade_details_status_item. import 'package:url_launcher/url_launcher.dart'; part 'trade_details_view_model.g.dart'; -class TradeDetailsViewModel = TradeDetailsViewModelBase - with _$TradeDetailsViewModel; +class TradeDetailsViewModel = TradeDetailsViewModelBase with _$TradeDetailsViewModel; abstract class TradeDetailsViewModelBase with Store { TradeDetailsViewModelBase({ required Trade tradeForDetails, required this.trades, - required this.settingsStore}) - : items = ObservableList(), - trade = tradeForDetails { + required this.settingsStore, + }) : items = ObservableList(), + trade = tradeForDetails { switch (trade.provider) { case ExchangeProviderDescription.xmrto: _provider = XMRTOExchangeProvider(); @@ -46,7 +45,7 @@ abstract class TradeDetailsViewModelBase with Store { case ExchangeProviderDescription.sideShift: _provider = SideShiftExchangeProvider(); break; - case ExchangeProviderDescription.simpleSwap: + case ExchangeProviderDescription.simpleSwap: _provider = SimpleSwapExchangeProvider(); break; case ExchangeProviderDescription.trocador: @@ -100,12 +99,7 @@ abstract class TradeDetailsViewModelBase with Store { items.clear(); items.add( - DetailsListStatusItem( - title: S.current.trade_details_state, - value: trade.state != null - ? trade.state.toString() - : S.current.trade_details_fetching) - ); + DetailsListStatusItem(title: S.current.trade_details_state, value: trade.state.toString())); items.add(TradeDetailsListCardItem.tradeDetails( id: trade.id, @@ -118,15 +112,11 @@ abstract class TradeDetailsViewModelBase with Store { }, )); - if (trade.provider != null) { - items.add(StandartListItem( - title: S.current.trade_details_provider, - value: trade.provider.toString())); - } + items.add(StandartListItem( + title: S.current.trade_details_provider, value: trade.provider.toString())); if (trade.provider == ExchangeProviderDescription.changeNow) { - final buildURL = - 'https://changenow.io/exchange/txs/${trade.id.toString()}'; + final buildURL = 'https://changenow.io/exchange/txs/${trade.id.toString()}'; items.add(TrackTradeListItem( title: 'Track', value: buildURL, @@ -137,14 +127,25 @@ abstract class TradeDetailsViewModelBase with Store { if (trade.provider == ExchangeProviderDescription.sideShift) { final buildURL = 'https://sideshift.ai/orders/${trade.id.toString()}'; - items.add(TrackTradeListItem( - title: 'Track', value: buildURL, onTap: () => launch(buildURL))); + items.add(TrackTradeListItem(title: 'Track', value: buildURL, onTap: () => launch(buildURL))); } if (trade.provider == ExchangeProviderDescription.simpleSwap) { final buildURL = 'https://simpleswap.io/exchange?id=${trade.id.toString()}'; - items.add(TrackTradeListItem( - title: 'Track', value: buildURL, onTap: () => launch(buildURL))); + items.add(TrackTradeListItem(title: 'Track', value: buildURL, onTap: () => launch(buildURL))); + } + + if (trade.provider == ExchangeProviderDescription.trocador) { + final buildURL = 'https://trocador.app/en/checkout/${trade.id.toString()}'; + items.add(TrackTradeListItem(title: 'Track', value: buildURL, onTap: () => launch(buildURL))); + + items.add(StandartListItem( + title: '${trade.providerName} ${S.current.id.toUpperCase()}', + value: trade.providerId ?? '')); + + if (trade.password != null && trade.password!.isNotEmpty) + items.add(StandartListItem( + title: '${trade.providerName} ${S.current.password}', value: trade.password ?? '')); } } } From 5f6b4477064cd88eda09d8ecc8e8a4478b9b2072 Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Thu, 9 Feb 2023 18:24:59 +0200 Subject: [PATCH 12/59] Fix _network normalize function --- lib/exchange/trocador/trocador_exchange_provider.dart | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/exchange/trocador/trocador_exchange_provider.dart b/lib/exchange/trocador/trocador_exchange_provider.dart index ecdeebe84..cfbc5001a 100644 --- a/lib/exchange/trocador/trocador_exchange_provider.dart +++ b/lib/exchange/trocador/trocador_exchange_provider.dart @@ -252,12 +252,7 @@ class TrocadorExchangeProvider extends ExchangeProvider { String get title => 'Trocador'; String _networkFor(CryptoCurrency currency) { - switch (currency) { - case CryptoCurrency.usdt: - return CryptoCurrency.btc.title.toLowerCase(); - default: - return currency.tag != null ? _normalizeTag(currency.tag!) : 'Mainnet'; - } + return currency.tag != null ? _normalizeTag(currency.tag!) : 'Mainnet'; } String _normalizeTag(String tag) { From 6711d9530f7ed89dd9e767b948470f5b2b620c72 Mon Sep 17 00:00:00 2001 From: Justin Ehrenhofer Date: Thu, 9 Feb 2023 10:52:24 -0600 Subject: [PATCH 13/59] Normalize remaining tags/networks Also rename Cake `C-CHAIN` tag to `AVAXC`, which will result in a clearer situation for user if we later add USDT (AVAXC), for example. --- cw_core/lib/crypto_currency.dart | 2 +- .../trocador/trocador_exchange_provider.dart | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/cw_core/lib/crypto_currency.dart b/cw_core/lib/crypto_currency.dart index 90051f1f7..6c4bec03a 100644 --- a/cw_core/lib/crypto_currency.dart +++ b/cw_core/lib/crypto_currency.dart @@ -117,7 +117,7 @@ class CryptoCurrency extends EnumerableItem with Serializable { static const xusd = CryptoCurrency(title: 'XUSD', tag: 'XHV', raw: 29, name: 'xusd'); static const ape = CryptoCurrency(title: 'APE', tag: 'ETH', fullName: 'ApeCoin', raw: 30, name: 'ape', iconPath: 'assets/images/ape_icon.png'); - static const avaxc = CryptoCurrency(title: 'AVAX', tag: 'C-CHAIN', raw: 31, name: 'avaxc', iconPath: 'assets/images/avaxc_icon.png'); + static const avaxc = CryptoCurrency(title: 'AVAX', tag: 'AVAXC', raw: 31, name: 'avaxc', iconPath: 'assets/images/avaxc_icon.png'); static const btt = CryptoCurrency(title: 'BTT', tag: 'ETH', fullName: 'BitTorrent', raw: 32, name: 'btt', iconPath: 'assets/images/btt_icon.png'); static const bttc = CryptoCurrency(title: 'BTTC', tag: 'TRX', fullName: 'BitTorrent-NEW', raw: 33, name: 'bttc', iconPath: 'assets/images/bttbsc_icon.png'); static const doge = CryptoCurrency(title: 'DOGE', fullName: 'Dogecoin', raw: 34, name: 'doge', iconPath: 'assets/images/doge_icon.png'); diff --git a/lib/exchange/trocador/trocador_exchange_provider.dart b/lib/exchange/trocador/trocador_exchange_provider.dart index cfbc5001a..3bf52040a 100644 --- a/lib/exchange/trocador/trocador_exchange_provider.dart +++ b/lib/exchange/trocador/trocador_exchange_provider.dart @@ -26,8 +26,6 @@ class TrocadorExchangeProvider extends ExchangeProvider { CryptoCurrency.stx, CryptoCurrency.bttc, CryptoCurrency.zaddr, - CryptoCurrency.usdcpoly, - CryptoCurrency.maticpoly, ]; static List _supportedPairs() { @@ -252,7 +250,18 @@ class TrocadorExchangeProvider extends ExchangeProvider { String get title => 'Trocador'; String _networkFor(CryptoCurrency currency) { - return currency.tag != null ? _normalizeTag(currency.tag!) : 'Mainnet'; + switch (currency) { + case CryptoCurrency.eth: + return 'ERC20'; + case CryptoCurrency.maticpoly: + return 'Mainnet'; + case CryptoCurrency.usdcpoly: + return 'MATIC'; + case CryptoCurrency.zec: + return 'Mainnet'; + default: + return currency.tag != null ? _normalizeTag(currency.tag!) : 'Mainnet'; + } } String _normalizeTag(String tag) { From 891a9d3368189ca710c952c2a4a6d757659a0c6f Mon Sep 17 00:00:00 2001 From: Justin Ehrenhofer Date: Mon, 13 Feb 2023 08:51:26 -0600 Subject: [PATCH 14/59] Add 5 new assets and map TRX -> TRC20 --- lib/exchange/trocador/trocador_exchange_provider.dart | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/exchange/trocador/trocador_exchange_provider.dart b/lib/exchange/trocador/trocador_exchange_provider.dart index 3bf52040a..457757ea7 100644 --- a/lib/exchange/trocador/trocador_exchange_provider.dart +++ b/lib/exchange/trocador/trocador_exchange_provider.dart @@ -18,13 +18,8 @@ class TrocadorExchangeProvider extends ExchangeProvider { super(pairList: _supportedPairs()); static const List _notSupported = [ - CryptoCurrency.xhv, - CryptoCurrency.dcr, - CryptoCurrency.oxt, - CryptoCurrency.pivx, CryptoCurrency.scrt, CryptoCurrency.stx, - CryptoCurrency.bttc, CryptoCurrency.zaddr, ]; @@ -268,6 +263,8 @@ class TrocadorExchangeProvider extends ExchangeProvider { switch (tag) { case 'ETH': return 'ERC20'; + case 'TRX': + return 'TRC20'; default: return tag.toLowerCase(); } From 1fe1b2fd87cf8ab8c4b1fd5dc79bd0d05b0ef25e Mon Sep 17 00:00:00 2001 From: Dimitris Apostolou Date: Sun, 5 Feb 2023 00:59:57 +0200 Subject: [PATCH 15/59] Fix typos --- README.md | 4 ++-- lib/anypay/any_pay_payment.dart | 2 +- lib/core/wallet_loading_service.dart | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 96f59e704..bd37590b4 100644 --- a/README.md +++ b/README.md @@ -90,12 +90,12 @@ Edit the applicable `strings_XX.arb` file in `res/values/` and open a pull reque - French - German - Italian -- Portugese +- Portuguese - Dutch - Polish - Croatian - Russian -- Ukranian +- Ukrainian - Hindi - Japanese - Chinese diff --git a/lib/anypay/any_pay_payment.dart b/lib/anypay/any_pay_payment.dart index 37bf7949e..0657b0526 100644 --- a/lib/anypay/any_pay_payment.dart +++ b/lib/anypay/any_pay_payment.dart @@ -57,7 +57,7 @@ class AnyPayPayment { List get outAddresses { return instructions - .map((instuction) => instuction.outputs.map((out) => out.address)) + .map((instruction) => instruction.outputs.map((out) => out.address)) .expand((e) => e) .toList(); } diff --git a/lib/core/wallet_loading_service.dart b/lib/core/wallet_loading_service.dart index 5bae5b346..761c6acce 100644 --- a/lib/core/wallet_loading_service.dart +++ b/lib/core/wallet_loading_service.dart @@ -22,13 +22,13 @@ class WalletLoadingService { final wallet = await walletService.openWallet(name, password); if (type == WalletType.monero) { - await upateMoneroWalletPassword(wallet); + await updateMoneroWalletPassword(wallet); } return wallet; } - Future upateMoneroWalletPassword(WalletBase wallet) async { + Future updateMoneroWalletPassword(WalletBase wallet) async { final key = PreferencesKey.moneroWalletUpdateV1Key(wallet.name); var isPasswordUpdated = sharedPreferences.getBool(key) ?? false; @@ -37,8 +37,8 @@ class WalletLoadingService { } final password = generateWalletPassword(); - // Save new generated password with backup key for case - // if wallet will change password, but it will faild to updated in secure storage + // Save new generated password with backup key for case where + // wallet will change password, but it will fail to update in secure storage final bakWalletName = '#__${wallet.name}_bak__#'; await keyService.saveWalletPassword(walletName: bakWalletName, password: password); await wallet.changePassword(password); From b5542d9f7daed4efdd9d774a3467870c15cd0b7a Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Mon, 13 Feb 2023 20:41:18 +0200 Subject: [PATCH 16/59] Add share files to share utils and unify the fix for ipad --- lib/src/screens/backup/backup_page.dart | 15 ++++-------- lib/utils/share_util.dart | 32 +++++++++++++++++++++---- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/lib/src/screens/backup/backup_page.dart b/lib/src/screens/backup/backup_page.dart index a055066c0..f819e88e5 100644 --- a/lib/src/screens/backup/backup_page.dart +++ b/lib/src/screens/backup/backup_page.dart @@ -1,10 +1,9 @@ import 'dart:io'; import 'package:cake_wallet/palette.dart'; +import 'package:cake_wallet/utils/share_util.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; -import 'package:share_plus/share_plus.dart'; -import 'package:cross_file/cross_file.dart'; import 'package:cake_wallet/utils/show_bar.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/generated/i18n.dart'; @@ -110,7 +109,7 @@ class BackupPage extends BasePage { if (Platform.isAndroid) { onExportAndroid(context, backup); } else { - await share(backup); + await share(backup, context); } }, actionLeftButton: () => Navigator.of(dialogContext).pop()); @@ -140,18 +139,14 @@ class BackupPage extends BasePage { }, actionLeftButton: () async { Navigator.of(dialogContext).pop(); - await share(backup); + await share(backup, context); }); }); } - Future share(BackupExportFile backup) async { - const mimeType = 'application/*'; + Future share(BackupExportFile backup, BuildContext context) async { final path = await backupViewModelBase.saveBackupFileLocally(backup); - await Share.shareXFiles([XFile( - path, - name: backup.name, - mimeType: mimeType)]); + await ShareUtil.shareFile(filePath: path, fileName: backup.name, context: context); await backupViewModelBase.removeBackupFileLocally(backup); } } diff --git a/lib/utils/share_util.dart b/lib/utils/share_util.dart index 5b33399c7..518590fdd 100644 --- a/lib/utils/share_util.dart +++ b/lib/utils/share_util.dart @@ -1,13 +1,37 @@ import 'package:flutter/material.dart'; import 'package:share_plus/share_plus.dart'; +import 'package:cross_file/cross_file.dart'; class ShareUtil { - static void share({required String text, required BuildContext context}) { - final box = context.findRenderObject() as RenderBox?; + static const _mimeType = 'application/*'; + static void share({required String text, required BuildContext context}) { Share.share( text, - sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size, + sharePositionOrigin: _sharePosition(context), ); } -} \ No newline at end of file + + static Future shareFile({ + required String filePath, + required String fileName, + required BuildContext context, + }) async { + Share.shareXFiles( + [ + XFile( + filePath, + name: fileName, + mimeType: _mimeType, + ) + ], + sharePositionOrigin: _sharePosition(context), + ); + } + + static Rect? _sharePosition(BuildContext context) { + final box = context.findRenderObject() as RenderBox?; + + return box!.localToGlobal(Offset.zero) & box.size; + } +} From f49695f4179948df945ec4a87172982b696ef181 Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Mon, 13 Feb 2023 20:44:23 +0200 Subject: [PATCH 17/59] Fix support links/emails not opening --- .../screens/settings/widgets/settings_link_provider_cell.dart | 4 ++-- lib/view_model/support_view_model.dart | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/src/screens/settings/widgets/settings_link_provider_cell.dart b/lib/src/screens/settings/widgets/settings_link_provider_cell.dart index 51fcc16d5..1b67f60e4 100644 --- a/lib/src/screens/settings/widgets/settings_link_provider_cell.dart +++ b/lib/src/screens/settings/widgets/settings_link_provider_cell.dart @@ -30,6 +30,6 @@ class SettingsLinkProviderCell extends StandardListRow { color: Palette.blueCraiola)); static void _launchUrl(String url) async { - if (await canLaunch(url)) await launch(url, forceSafariVC: false); + await launch(url, forceSafariVC: false); } -} \ No newline at end of file +} diff --git a/lib/view_model/support_view_model.dart b/lib/view_model/support_view_model.dart index 3b919e750..4e602089a 100644 --- a/lib/view_model/support_view_model.dart +++ b/lib/view_model/support_view_model.dart @@ -7,7 +7,6 @@ import 'package:flutter/material.dart'; import 'package:mobx/mobx.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:cake_wallet/wallet_type_utils.dart'; -import 'package:cake_wallet/wallet_type_utils.dart'; part 'support_view_model.g.dart'; @@ -19,7 +18,7 @@ abstract class SupportViewModelBase with Store { RegularListItem( title: S.current.faq, handler: (BuildContext context) async { - if (await canLaunch(url)) await launch(url); + await launch(url); }, ), LinkListItem( From 6d7ce369bf08d1b05bc59c88000b9f9daa976214 Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Mon, 13 Feb 2023 23:05:08 +0200 Subject: [PATCH 18/59] Add connection timed out error to ignored errors [skip ci] --- lib/utils/exception_handler.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/utils/exception_handler.dart b/lib/utils/exception_handler.dart index 4cbebdb5d..ed9f00fcb 100644 --- a/lib/utils/exception_handler.dart +++ b/lib/utils/exception_handler.dart @@ -125,6 +125,7 @@ class ExceptionHandler { "errno = 9", // SocketException: Bad file descriptor "errno = 32", // SocketException: Write failed (OS Error: Broken pipe) "errno = 60", // SocketException: Operation timed out + "errno = 110", // SocketException: Connection timed out "errno = 54", // SocketException: Connection reset by peer "errno = 49", // SocketException: Can't assign requested address "errno = 28", // OS Error: No space left on device From 54dab5883f283ec4897f9c62d17cf37dd36795bb Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Wed, 15 Feb 2023 17:50:48 +0200 Subject: [PATCH 19/59] Check for context first before showing popup --- lib/src/screens/dashboard/dashboard_page.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/src/screens/dashboard/dashboard_page.dart b/lib/src/screens/dashboard/dashboard_page.dart index c21365aa4..173e8c4de 100644 --- a/lib/src/screens/dashboard/dashboard_page.dart +++ b/lib/src/screens/dashboard/dashboard_page.dart @@ -229,7 +229,8 @@ class DashboardPage extends BasePage { } await Future.delayed(Duration(seconds: 1)); - await showPopUp( + if (context.mounted) { + await showPopUp( context: context, builder: (BuildContext context) { return AlertWithOneAction( @@ -239,6 +240,7 @@ class DashboardPage extends BasePage { buttonText: S.of(context).understand, buttonAction: () => Navigator.of(context).pop()); }); + } }); var needToPresentYat = false; From 7b35604ff586ecd7d040a5a941bdd60aa6f5827f Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Mon, 20 Feb 2023 22:17:41 +0200 Subject: [PATCH 20/59] Add app version and device info to error report --- android/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- cw_haven/pubspec.yaml | 2 +- cw_monero/pubspec.yaml | 2 +- lib/utils/exception_handler.dart | 88 ++++++++++++++++++- 5 files changed, 90 insertions(+), 6 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 04c2af566..692e8dfb1 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.5.10' + ext.kotlin_version = '1.6.21' repositories { google() jcenter() diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index b7ca2e6de..733c691d3 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip diff --git a/cw_haven/pubspec.yaml b/cw_haven/pubspec.yaml index 28f2c315e..7a5ac6aa4 100644 --- a/cw_haven/pubspec.yaml +++ b/cw_haven/pubspec.yaml @@ -12,7 +12,7 @@ environment: dependencies: flutter: sdk: flutter - ffi: ^1.1.2 + ffi: ^2.0.1 http: ^0.13.4 path_provider: ^2.0.11 mobx: ^2.0.7+4 diff --git a/cw_monero/pubspec.yaml b/cw_monero/pubspec.yaml index 23e8782cb..6d5041dfa 100644 --- a/cw_monero/pubspec.yaml +++ b/cw_monero/pubspec.yaml @@ -12,7 +12,7 @@ environment: dependencies: flutter: sdk: flutter - ffi: ^1.1.2 + ffi: ^2.0.1 http: ^0.13.4 path_provider: ^2.0.11 mobx: ^2.0.7+4 diff --git a/lib/utils/exception_handler.dart b/lib/utils/exception_handler.dart index 4cbebdb5d..8aefe7bc6 100644 --- a/lib/utils/exception_handler.dart +++ b/lib/utils/exception_handler.dart @@ -1,4 +1,3 @@ -import 'dart:convert'; import 'dart:io'; import 'package:cake_wallet/entities/preferences_key.dart'; @@ -6,9 +5,11 @@ import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/main.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; +import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mailer/flutter_mailer.dart'; +import 'package:package_info/package_info.dart'; import 'package:path_provider/path_provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -31,7 +32,7 @@ class ExceptionHandler { ==========================================================\n\n'''; await file.writeAsString( - jsonEncode(exception) + separator, + "$exception $separator", mode: FileMode.append, ); } @@ -42,6 +43,8 @@ class ExceptionHandler { final file = File('${appDocDir.path}/error.txt'); + await _addDeviceInfo(file); + final MailOptions mailOptions = MailOptions( subject: 'Mobile App Issue', recipients: ['support@cakewallet.com'], @@ -130,4 +133,85 @@ class ExceptionHandler { "errno = 28", // OS Error: No space left on device "PERMISSION_NOT_GRANTED", ]; + + static Future _addDeviceInfo(File file) async { + final packageInfo = await PackageInfo.fromPlatform(); + final currentVersion = packageInfo.version; + + final deviceInfoPlugin = DeviceInfoPlugin(); + Map deviceInfo = {}; + + if (Platform.isAndroid) { + deviceInfo = _readAndroidBuildData(await deviceInfoPlugin.androidInfo); + deviceInfo["Platform"] = "Android"; + } else if (Platform.isIOS) { + deviceInfo = _readIosDeviceInfo(await deviceInfoPlugin.iosInfo); + deviceInfo["Platform"] = "iOS"; + } else if (Platform.isLinux) { + deviceInfo = _readLinuxDeviceInfo(await deviceInfoPlugin.linuxInfo); + deviceInfo["Platform"] = "Linux"; + } else if (Platform.isMacOS) { + deviceInfo = _readMacOsDeviceInfo(await deviceInfoPlugin.macOsInfo); + deviceInfo["Platform"] = "MacOS"; + } else if (Platform.isWindows) { + deviceInfo = _readWindowsDeviceInfo(await deviceInfoPlugin.windowsInfo); + deviceInfo["Platform"] = "Windows"; + } + + await file.writeAsString( + "App Version: $currentVersion\n\nDevice Info $deviceInfo", + mode: FileMode.append, + ); + } + + static Map _readAndroidBuildData(AndroidDeviceInfo build) { + return { + 'brand': build.brand, + 'device': build.device, + 'manufacturer': build.manufacturer, + 'model': build.model, + 'product': build.product, + }; + } + + static Map _readIosDeviceInfo(IosDeviceInfo data) { + return { + 'systemName': data.systemName, + 'systemVersion': data.systemVersion, + 'model': data.model, + 'localizedModel': data.localizedModel, + }; + } + + static Map _readLinuxDeviceInfo(LinuxDeviceInfo data) { + return { + 'name': data.name, + 'version': data.version, + 'versionCodename': data.versionCodename, + 'versionId': data.versionId, + 'prettyName': data.prettyName, + 'buildId': data.buildId, + 'variant': data.variant, + 'variantId': data.variantId, + }; + } + + static Map _readMacOsDeviceInfo(MacOsDeviceInfo data) { + return { + 'arch': data.arch, + 'model': data.model, + 'kernelVersion': data.kernelVersion, + 'osRelease': data.osRelease, + }; + } + + static Map _readWindowsDeviceInfo(WindowsDeviceInfo data) { + return { + 'majorVersion': data.majorVersion, + 'minorVersion': data.minorVersion, + 'buildNumber': data.buildNumber, + 'productType': data.productType, + 'productName': data.productName, + }; + } } From 37ea2d341f2a305265b0dc21b92cbc110fdb41eb Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Mon, 20 Feb 2023 22:25:54 +0200 Subject: [PATCH 21/59] Add Exception handler to report loading wallet issues --- lib/reactions/on_authentication_state_change.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/reactions/on_authentication_state_change.dart b/lib/reactions/on_authentication_state_change.dart index edadf33b0..7521170e6 100644 --- a/lib/reactions/on_authentication_state_change.dart +++ b/lib/reactions/on_authentication_state_change.dart @@ -1,4 +1,5 @@ import 'package:cake_wallet/routes.dart'; +import 'package:cake_wallet/utils/exception_handler.dart'; import 'package:flutter/widgets.dart'; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/entities/load_current_wallet.dart'; @@ -16,8 +17,9 @@ void startAuthenticationStateChange(AuthenticationStore authenticationStore, if (state == AuthenticationState.installed) { try { await loadCurrentWallet(); - } catch (e) { - loginError = e; + } catch (error, stack) { + loginError = error; + ExceptionHandler.onError(FlutterErrorDetails(exception: error, stack: stack)); } return; } From d589e72ae8a618a49af57afa131ccfc82b48b013 Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Tue, 21 Feb 2023 00:03:57 +0200 Subject: [PATCH 22/59] update file picker version --- pubspec_base.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec_base.yaml b/pubspec_base.yaml index 2caa9052f..6086d698a 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -55,7 +55,7 @@ dependencies: another_flushbar: ^1.12.29 archive: ^3.3.0 cryptography: ^2.0.5 - file_picker: ^4.6.1 + file_picker: ^5.2.5 unorm_dart: ^0.2.0 # check unorm_dart for usage and for replace permission_handler: ^10.0.0 From 38b7a2ff45aec1767b4c5d9b9aea7209cddf20f9 Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Tue, 21 Feb 2023 01:28:30 +0200 Subject: [PATCH 23/59] Add device info dependency to pubspec_base.yaml --- pubspec_base.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/pubspec_base.yaml b/pubspec_base.yaml index 6086d698a..079ba7c76 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -62,6 +62,7 @@ dependencies: device_display_brightness: ^0.0.6 platform_device_id: ^1.0.1 flutter_mailer: ^2.0.2 + device_info_plus: ^8.1.0 cake_backup: git: url: https://github.com/cake-tech/cake_backup.git From db1fdac2b50e752ea81d16dfdf0a0f8ef74f17c0 Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Wed, 22 Feb 2023 04:15:18 +0200 Subject: [PATCH 24/59] Add connection timed out error to ignored exceptions [skip ci] --- lib/utils/exception_handler.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/utils/exception_handler.dart b/lib/utils/exception_handler.dart index 8aefe7bc6..55d27e6a1 100644 --- a/lib/utils/exception_handler.dart +++ b/lib/utils/exception_handler.dart @@ -128,6 +128,7 @@ class ExceptionHandler { "errno = 9", // SocketException: Bad file descriptor "errno = 32", // SocketException: Write failed (OS Error: Broken pipe) "errno = 60", // SocketException: Operation timed out + "errno = 110", // SocketException: Connection timed out "errno = 54", // SocketException: Connection reset by peer "errno = 49", // SocketException: Can't assign requested address "errno = 28", // OS Error: No space left on device From cf5cee946b5c99230dbe0ae2f9a13e3b56592209 Mon Sep 17 00:00:00 2001 From: cr0mll Date: Sat, 25 Feb 2023 19:34:23 +0200 Subject: [PATCH 25/59] Added Bulgarian language --- lib/entities/language_service.dart | 2 + res/values/strings_bg.arb | 688 +++++++++++++++++++++++++++++ 2 files changed, 690 insertions(+) create mode 100644 res/values/strings_bg.arb diff --git a/lib/entities/language_service.dart b/lib/entities/language_service.dart index 2f3443c02..dda28c14b 100644 --- a/lib/entities/language_service.dart +++ b/lib/entities/language_service.dart @@ -23,6 +23,7 @@ class LanguageService { 'ar': 'العربية (Arabic)', 'tr': 'Türkçe (Turkish)', 'my': 'မြန်မာ (Burmese)', + 'bg': 'Български (Bulgarian)' }; static const Map localeCountryCode = { @@ -45,6 +46,7 @@ class LanguageService { 'ar': 'sau', 'tr': 'tur', 'my': 'mmr', + 'bg': 'bgr' }; static final list = {}; diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb new file mode 100644 index 000000000..32e9ed0c5 --- /dev/null +++ b/res/values/strings_bg.arb @@ -0,0 +1,688 @@ +{ + "welcome" : "Добре дошли в", + "cake_wallet" : "Cake Wallet", + "first_wallet_text" : "Невероятен портфейл за Monero, Bitcoin, Litecoin и Haven", + "please_make_selection" : "Моля, изберете отдолу за създаване или възстановяване на портфейл.", + "create_new" : "Създаване на нов портфейл", + "restore_wallet" : "Възстановяване на портфейл", + + "monero_com": "Monero.com от Cake Wallet", + "monero_com_wallet_text": "Невероятен портфейл за Monero", + + "haven_app": "Haven от Cake Wallet", + "haven_app_wallet_text": "Невероятен портфейл за Haven", + + "accounts" : "Профили", + "edit" : "Промени", + "account" : "Профил", + "add" : "Добави", + + + "address_book" : "Адресна книга", + "contact" : "Контакт", + "please_select" : "Моля, изберете:", + "cancel" : "Откажи", + "ok" : "Ок", + "contact_name" : "Име на контакт", + "reset" : "Нулиране", + "save" : "Запази", + "address_remove_contact" : "Премахни контакт", + "address_remove_content" : "Сигурни ли сте, че искате да премахнете избрания контакт?", + + + "authenticated" : "Удостоверено", + "authentication" : "Удостоверяване", + "failed_authentication" : "Неуспешно удостоверяване. ${state_error}", + + + "wallet_menu" : "Меню", + "Blocks_remaining" : "${status} оставащи блока", + "please_try_to_connect_to_another_node" : "Моля, опитайте се да се свържете към друг node.", + "xmr_hidden" : "Скрит", + "xmr_available_balance" : "Наличен баланс", + "xmr_full_balance" : "Пълен баланс", + "send" : "Изпрати", + "receive" : "Получи", + "transactions" : "Транзакции", + "incoming" : "Входящи", + "outgoing" : "Изходящи", + "transactions_by_date" : "Транзакции по дата", + "trades" : "Trades", + "filter_by": "Филтрирай по", + "today" : "Днес", + "yesterday" : "Вчера", + "received" : "Получени", + "sent" : "Изпратени", + "pending" : " (чакащи)", + "rescan" : "Сканирай отново", + "reconnect" : "Reconnect", + "wallets" : "Портфейли", + "show_seed" : "Покажи seed", + "show_keys" : "Покажи seed/keys", + "address_book_menu" : "Адресна книга", + "reconnection" : "Свързване отново", + "reconnect_alert_text" : "Сигурни ли сте, че искате да се свържете отново?", + + + "exchange" : "Exchange", + "clear" : "Изчисти", + "refund_address" : "Refund address", + "change_exchange_provider" : "Промяна на Exchange Provider", + "you_will_send" : "Обръщане от", + "you_will_get" : "Обръщане в", + "amount_is_guaranteed" : "Сумата за получаване е гарантирана", + "amount_is_estimate" : "Сумата за получаване е ", + "powered_by" : "Powered by ${title}", + "error" : "Грешка", + "estimated" : "Изчислено", + "min_value" : "Мин: ${value} ${currency}", + "max_value" : "Макс: ${value} ${currency}", + "change_currency" : "Смени валута", + "overwrite_amount" : "Промени сума", + "qr_payment_amount" : "Този QR код съдържа сума за плащане. Искате ли да промените стойността?", + + "copy_id" : "Копиране на ID", + "exchange_result_write_down_trade_id" : "Моля, запишете trade ID-то, за да продължите.", + "trade_id" : "Trade ID:", + "copied_to_clipboard" : "Копирано", + "saved_the_trade_id" : "Запазих trade ID-то", + "fetching" : "Обработване", + "id" : "ID: ", + "amount" : "Сума: ", + "payment_id" : "Payment ID: ", + "status" : "Статус: ", + "offer_expires_in" : "Предложението изтича след: ", + "trade_is_powered_by" : "This trade is powered by ${provider}", + "copy_address" : "Copy Address", + "exchange_result_confirm" : "By pressing confirm, you will be sending ${fetchingLabel} ${from} from your wallet called ${walletName} to the address shown below. Or you can send from your external wallet to the below address/QR code.\n\nPlease press confirm to continue or go back to change the amounts.", + "exchange_result_description" : "You must send a minimum of ${fetchingLabel} ${from} to the address shown on the next page. If you send an amount lower than ${fetchingLabel} ${from} it may not get converted and it may not be refunded.", + "exchange_result_write_down_ID" : "*Please copy or write down your ID shown above.", + "confirm" : "Потвърждаване", + "confirm_sending" : "Потвърждаване на изпращането", + "commit_transaction_amount_fee" : "Изпълняване на транзакция\nСума: ${amount}\nТакса: ${fee}", + "sending" : "Изпращане", + "transaction_sent" : "Сумата е изпратена!", + "expired" : "Изтекло", + "time" : "${minutes} мин ${seconds} сек", + "send_xmr" : "Изпращане на XMR", + "exchange_new_template" : "Нов шаблон", + + "faq" : "FAQ", + + + "enter_your_pin" : "Въведете PIN", + "loading_your_wallet" : "Зареждане на портфейл", + + + "new_wallet" : "Нов портфейл", + "wallet_name" : "Име на портфейл", + "continue_text" : "Напред", + "choose_wallet_currency" : "Изберете валута за портфейла:", + + + "node_new" : "Нов Node", + "node_address" : "Нов адрес", + "node_port" : "Node порт", + "login" : "Влизане", + "password" : "Парола", + "nodes" : "Nodes", + "node_reset_settings_title" : "Възстановяване на настройки", + "nodes_list_reset_to_default_message" : "Сигурни ли сте, че искате да възстановите фабричните настройки?", + "change_current_node" : "Сигурни ли сте, че искате да промените сегашния node на ${node}?", + "change" : "Промени", + "remove_node" : "Премахни node", + "remove_node_message" : "Сигурни ли сте, че искате да премахнете избрания node?", + "remove" : "Премахни", + "delete" : "Изтрий", + "add_new_node" : "Добави нов node", + "change_current_node_title" : "Промени сегашния node", + "node_test" : "Тест", + "node_connection_successful" : "Връзката бе установена успешно", + "node_connection_failed" : "Връзката не можа да бъде установена", + "new_node_testing" : "Тестване на нов node", + + + "use" : "Смяна на ", + "digit_pin" : "-цифрен PIN", + + + "share_address" : "Сподели адрес", + "receive_amount" : "Сума", + "subaddresses" : "Подадреси", + "addresses" : "Адреси", + "scan_qr_code" : "Сканирайте QR кода, за да получите адреса", + "qr_fullscreen" : "Натиснете, за да отворите QR кода на цял екран", + "rename" : "Промяна на името", + "choose_account" : "Избиране на профил", + "create_new_account" : "Създаване на нов профил", + "accounts_subaddresses" : "Профили и подадреси", + + + "restore_restore_wallet" : "Възстановяване на портфейл", + "restore_title_from_seed_keys" : "Възстановяване от seed/keys", + "restore_description_from_seed_keys" : "Възстановете своя портфейл от seed/keys, които сте съхранили на сигурно място", + "restore_next" : "Next", + "restore_title_from_backup" : "Възстановяване от резервно копие", + "restore_description_from_backup" : "Можете да възстановите цялото приложение Cake Wallet от своя резервен файл", + "restore_seed_keys_restore" : "Възстановяне от Seed/Keys", + "restore_title_from_seed" : "Възстановяване от seed", + "restore_description_from_seed" : "Възстановяване на портфейл от кода от 13 или 25 думи", + "restore_title_from_keys" : "Възстановяване от keys", + "restore_description_from_keys" : "Възстановяване на портфейл от генерираните от Вашите тайни ключове клавиши", + "restore_wallet_name" : "Име на портфейл", + "restore_address" : "Адреси", + "restore_view_key_private" : "View key (таен)", + "restore_spend_key_private" : "Spend key (публичен)", + "restore_recover" : "Възстановяване", + "restore_wallet_restore_description" : "Описание на възстановяване на портфейл", + "restore_new_seed" : "Нов seed", + "restore_active_seed" : "Активиране на seed", + "restore_bitcoin_description_from_seed" : "Възстановяване на портфейл чрез код от 24 думи", + "restore_bitcoin_description_from_keys" : "Възстановяване на портфейл чрез WIF, изведен от Вашите private keys", + "restore_bitcoin_title_from_keys" : "Възстановяване от WIF", + "restore_from_date_or_blockheight" : "Моля, въведете дата няколко дни преди създаването на този портфейл. Ако знаете blockheight-а, въведето него вместо това", + + + "seed_reminder" : "Моля, запишете го в случай на загуба на устройството.", + "seed_title" : "Seed", + "seed_share" : "Споделяне на seed", + "copy" : "Копиране", + + + "seed_language_choose" : "Моля, изберете език на seed-а:", + "seed_choose" : "Изберете език на seed-а", + "seed_language_next" : "Следващ", + "seed_language_english" : "Английски", + "seed_language_chinese" : "Китайски", + "seed_language_dutch" : "Нидерландски", + "seed_language_german" : "Немски", + "seed_language_japanese" : "Японски", + "seed_language_portuguese" : "Португалски", + "seed_language_russian" : "Руски", + "seed_language_spanish" : "Испански", + "seed_language_french": "Френски", + "seed_language_italian": "Италиански", + + + "send_title" : "Изпращане", + "send_your_wallet" : "Вашият портфейл", + "send_address" : "${cryptoCurrency} адрес", + "send_payment_id" : "Payment ID (не е задължително)", + "all" : "ALL", + "send_error_minimum_value" : "Минималната сума е 0.01", + "send_error_currency" : "Валутата може да съдържа само числа", + "send_estimated_fee" : "Изчислена такса:", + "send_priority" : "В момента таксата е на ${transactionPriority} приоритетност.\nПриоритетността на транзакцията може да бъде променена в настройките", + "send_creating_transaction" : "Създаване на транзакция", + "send_templates" : "Шаблони", + "send_new" : "Ново", + "send_amount" : "Сума:", + "send_fee" : "Такса:", + "send_name" : "Име", + "send_got_it" : "Готово", + "send_sending" : "Изпращане...", + "send_success" : "Вашите ${crypto} бяха успешно изпратени", + + + "settings_title" : "Настройки", + "settings_nodes" : "Nodes", + "settings_current_node" : "Сегашен node", + "settings_wallets" : "Портфейли", + "settings_display_balance" : "Показване на баланс", + "settings_currency" : "Валута", + "settings_fee_priority" : "Таксова приоритетност", + "settings_save_recipient_address" : "Запазване адрес на получател", + "settings_personal" : "Лични", + "settings_change_pin" : "Промяна на PIN", + "settings_change_language" : "Промяна на езика", + "settings_allow_biometrical_authentication" : "Позволяване на биометрично удостоверяване.", + "settings_dark_mode" : "Тъмен режим", + "settings_transactions" : "Транзакции", + "settings_trades" : "Сделки", + "settings_display_on_dashboard_list" : "Показване на таблото", + "settings_all" : "Всичко", + "settings_only_trades" : "Само сделки", + "settings_only_transactions" : "Само транзакции", + "settings_none" : "Липсва", + "settings_support" : "Поддръжка", + "settings_terms_and_conditions" : "Условия", + "pin_is_incorrect" : "Грешен PIN", + + + "setup_pin" : "Настройване на PIN", + "enter_your_pin_again" : "Въведете своя PIN отново", + "setup_successful" : "Вашият PIN бе успешно настроен!", + + + "wallet_keys" : "Seed/keys на портфейла", + "wallet_seed" : "Seed на портфейла", + "private_key" : "Таен ключ", + "public_key" : "Публичен ключ", + "view_key_private" : "View key (таен)", + "view_key_public" : "View key (публичен)", + "spend_key_private" : "Spend key (таен)", + "spend_key_public" : "Spend key (публичен)", + "copied_key_to_clipboard" : "Копиран ключ: ${key}", + + + "new_subaddress_title" : "Нов адрес", + "new_subaddress_label_name" : "Име на Label", + "new_subaddress_create" : "Създаване", + + "address_label" : "Адресен label", + + "subaddress_title" : "Лист от подадреси", + + + "trade_details_title" : "Подробности на сделката", + "trade_details_id" : "ID", + "trade_details_state" : "Статус", + "trade_details_fetching" : "Обработка", + "trade_details_provider" : "Provider", + "trade_details_created_at" : "Създадено", + "trade_details_pair" : "Pair", + "trade_details_copied" : "${title} копирано", + + + "trade_history_title" : "История на сделките", + + + "transaction_details_title" : "Подробности на транзакцията", + "transaction_details_transaction_id" : "Transaction ID", + "transaction_details_date" : "Дата", + "transaction_details_height" : "Height", + "transaction_details_amount" : "Сума", + "transaction_details_fee" : "Такса", + "transaction_details_copied" : "${title} копирано", + "transaction_details_recipient_address" : "Адрес на получател", + + + "wallet_list_title" : "Monero портфейл", + "wallet_list_create_new_wallet" : "Създаване на нов портфейл", + "wallet_list_restore_wallet" : "Възстановяване на портфейл", + "wallet_list_load_wallet" : "Зареждане на портфейл", + "wallet_list_loading_wallet" : "Зареждане на портфейл ${wallet_name}", + "wallet_list_failed_to_load" : "Грешка при зареждането на портфейл ${wallet_name}. ${error}", + "wallet_list_removing_wallet" : "Премахване на портфейл ${wallet_name}", + "wallet_list_failed_to_remove" : "Грешка при премахването на портфейл${wallet_name}. ${error}", + + + "widgets_address" : "Адрес", + "widgets_restore_from_blockheight" : "Възстановяване от blockheight", + "widgets_restore_from_date" : "Възстановяване от дата", + "widgets_or" : "или", + "widgets_seed" : "Seed", + + + "router_no_route" : "Няма дефиниран път за ${name}", + + + "error_text_account_name" : "Името на профила може да съдържа само букви и числа \nи трябва да е между 1 и 15 символа", + "error_text_contact_name" : "Името на контакта не може да съдържа символите ` , ' \" \nи и трябва да е между 1 и 32 символа", + "error_text_address" : "Адресът на портфейла трябва да отговаря \n на вида криптовалута", + "error_text_node_address" : "Моля, въведете iPv4 адрес", + "error_text_node_port" : "Node port-ът е цяло число между 0 и 65535", + "error_text_payment_id" : "Payment ID-то може да съдържа само между 16 и 64 шестнайсетични символа", + "error_text_xmr" : "XMR сумата не може да надхвърля наличния баланс.\nБроят на цифрите след десетичната запетая може да бъде най-много 12", + "error_text_fiat" : "Сумата не може да надвишава наличния баланс.\nThe number of fraction digits must be less or equal to 2", + "error_text_subaddress_name" : "Името на подадреса не може да съдържат символите ` , ' \" \n и трябва да е между 1 и 20 символа", + "error_text_amount" : "Сумата може да съдържа само числа", + "error_text_wallet_name" : "Името на портфейла може да съдържа само букви, цифри, и символите "_" и "-" \n и трябва да е между 1 и 33 символа", + "error_text_keys" : "Ключовете за портфейл може да съдържат само 64 шестнайсетични символа", + "error_text_crypto_currency" : "Броят на цифрите след десетичната запетая\nможе да бъде най-много 12", + "error_text_minimal_limit" : "Сделка за ${provider} не беше създадена. Сумата е по-малко от минималната: ${min} ${currency}", + "error_text_maximum_limit" : "Сделка за ${provider} не беше създадена. Сумата надвишава максималната: ${max} ${currency}", + "error_text_limits_loading_failed" : "Сделка за ${provider} не беше създадена. Неуспешно зареждане на лимити", + "error_text_template" : "Имената на шаблони и адреси не могат да съдържат ` , ' \" \nи трябва да са между 1 и 106 символа.", + + + "auth_store_ban_timeout" : "ban_timeout", + "auth_store_banned_for" : "Забрана за ", + "auth_store_banned_minutes" : " минути", + "auth_store_incorrect_password" : "Грешен PIN", + "wallet_store_monero_wallet" : "Monero портфейл", + "wallet_restoration_store_incorrect_seed_length" : "Грешна дължина на seed-а", + + + "full_balance" : "Пълен баланс", + "available_balance" : "Наличен баланс", + "hidden_balance" : "Скрит баланс", + + + "sync_status_syncronizing" : "СИНХРОНИЗИРАНЕ", + "sync_status_syncronized" : "СИНХРОНИЗИРАНО", + "sync_status_not_connected" : "НЯМА ВРЪЗКА", + "sync_status_starting_sync" : "ЗАПОЧВАНЕ НА СИНХРОНИЗАЦИЯ", + "sync_status_failed_connect" : "НЕУСПЕШНО СВЪРЗВАНЕ", + "sync_status_connecting" : "СВЪРЗВАНЕ", + "sync_status_connected" : "СВЪРЗВАНЕ", + "sync_status_attempting_sync" : "ОПИТ ЗА СИНХРОНИЗАЦИЯ", + + + "transaction_priority_slow" : "Бавно", + "transaction_priority_regular" : "Обичайно", + "transaction_priority_medium" : "Средно", + "transaction_priority_fast" : "Бързо", + "transaction_priority_fastest" : "Най-бързо", + + + "trade_for_not_created" : "Сделка за ${title} не бе създадена.", + "trade_not_created" : "Сделка не бе създадена.", + "trade_id_not_found" : "Сделка ${tradeId} на ${title} не бе намерена.", + "trade_not_found" : "Сделката не бе намерена.", + + + "trade_state_pending" : "Изчаква се", + "trade_state_confirming" : "Потвърждава се", + "trade_state_trading" : "Trading", + "trade_state_traded" : "Traded", + "trade_state_complete" : "Завършено", + "trade_state_to_be_created" : "Изчаква създаване", + "trade_state_unpaid" : "Неплатено", + "trade_state_underpaid" : "Недостатъчно плащане", + "trade_state_paid_unconfirmed" : "Непотвърдено плащане", + "trade_state_paid" : "Платено", + "trade_state_btc_sent" : "Btc изпратен", + "trade_state_timeout" : "Време за изчакване", + "trade_state_created" : "Създадено", + "trade_state_finished" : "Завършено", + + "change_language" : "Смяна на езика", + "change_language_to" : "Смяна на езика на ${language}?", + + "paste" : "Поставяне", + "restore_from_seed_placeholder" : "Моля, въведете своя seed тук", + "add_new_word" : "Добавяне на нова дума", + "incorrect_seed" : "Въведеният текст е невалиден.", + + "biometric_auth_reason" : "Сканирайте своя пръстов отпечатък", + "version" : "Версия ${currentVersion}", + + "extracted_address_content" : "Ще изпратите средства на \n${recipient_name}", + + "card_address" : "Адрес:", + "buy" : "Купуване", + "sell": "Продаване", + + "placeholder_transactions" : "Вашите транзакции ще се покажат тук", + "placeholder_contacts" : "Вашите контакти ще се покажат тук", + + "template" : "Шаблон", + "confirm_delete_template" : "Този шаблон ще бъде изтрит. Искате ли да продължите?", + "confirm_delete_wallet" : "Този портфейл ще бъде изтрит. Искате ли да продължите?", + + "picker_description" : "За да изберете ChangeNOW или MorphToken, моля, първо променете своя trading pair", + + "change_wallet_alert_title" : "Смяна на сегашния портфейл", + "change_wallet_alert_content" : "Искате ли да смените сегашния портфейл на ${wallet_name}?", + + "creating_new_wallet" : "Създаване на нов портфейл", + "creating_new_wallet_error" : "Грешка: ${description}", + + "seed_alert_title" : "Внимание", + "seed_alert_content" : "Seed-ът е единственият начин да възстановите портфейла си. Записахте ли го?", + "seed_alert_back" : "Назад", + "seed_alert_yes" : "Да", + + "exchange_sync_alert_content" : "Моля, изчакайте синхронизирането на Вашия портфейл", + + "pre_seed_title" : "ВАЖНО", + "pre_seed_description" : "На следващата страница ще видите поредица от ${words} думи. Това е вашият таен личен seed и е единственият начин да възстановите портфейла си. Отговорността за съхранението му на сигурно място извън приложението на Cake Wallet е изцяло ВАША.", + "pre_seed_button_text" : "Разбирам. Покажи seed", + + "xmr_to_error" : "XMR.TO грешка", + "xmr_to_error_description" : "Невалидна сума - най-много 8 цифри след десетичната запетая", + + "provider_error" : "Грешка на ${provider} ", + + "use_ssl" : "Използване на SSL", + "trusted" : "Надежден", + + "color_theme" : "Цвят", + "light_theme" : "Светло", + "bright_theme" : "Ярко", + "dark_theme" : "Тъмно", + "enter_your_note" : "Въвеждане на бележка…", + "note_optional" : "Бележка (не е задължително)", + "note_tap_to_change" : "Бележка (натиснете за промяна)", + "view_in_block_explorer" : "Вижте в Block Explorer", + "view_transaction_on" : "Вижте транзакция на ", + "transaction_key" : "Transaction Key", + "confirmations" : "потвърждения", + "recipient_address" : "Адрес на получател", + + "extra_id" : "Допълнително ID:", + "destination_tag" : "Destination tag:", + "memo" : "Мемо:", + + "backup" : "Резервно копие", + "change_password" : "Смяна на парола", + "backup_password" : "Парола за възстановяване", + "write_down_backup_password" : "Моля, запишете своята парола за възстановяване. Тя се използва за импортиране на резервни копия на Вашите файлове.", + "export_backup" : "Експортиране на резервно копие", + "save_backup_password" : "Моля, запишете своята парола за възстановяване. Импортирането на резервни копия не е възможно без нея.", + "backup_file" : "Резервно копие", + + "edit_backup_password" : "Промяна на паролата за възстановяване", + "save_backup_password_alert" : "Запазване на паролата за възстановяване", + "change_backup_password_alert" : "Предишните резервни копия не могат да бъдат импортирани с новата парола. Те ще се използва само за нови такива. Are you sure that you want to change backup password?", + + "enter_backup_password" : "Въведете парола за възстановяване", + "select_backup_file" : "Избор на резервно копие", + "import" : "Импортиране", + "please_select_backup_file" : "Моля, изберете резервно копие и въведете парола за възстановяване.", + + "fixed_rate" : "Постоянен обменен курс", + "fixed_rate_alert" : "Ще можете да въведете сумата за получаване, когато е избранен постоянен обменен курс. Искате ли да изберете постоянен обменен курс?", + + "xlm_extra_info" : "Не забравяйте да дадете Memo ID-то, докато изпращате XLM транзакцията за обмена", + "xrp_extra_info" : "Не забравяйте да дадете Destination Tag-а, когато изпращате XRP транзакцията за обмена", + + "exchange_incorrect_current_wallet_for_xmr" : "Ако искате да обмените XMR от своя Cake Wallet Monero баланс, първо изберете своя Monero портфейл.", + "confirmed" : "Потвърдено", + "unconfirmed" : "Непотвърдено", + "displayable" : "Възможност за показване", + + "submit_request" : "изпращане на заявка", + + "buy_alert_content" : "В момента поддържаме покупката само на Bitcoin и Litecoin. За да закупите Bitcoin или Litecoin, създайте или изберете своя Bitcoin или Litecoin портфейл.", + "sell_alert_content": "В момента поддържаме само продажбата на Bitcoin. За да продавате Bitcoin, създайте или изберете своя Bitcoin портфейл.", + + "outdated_electrum_wallet_description" : "Нови Bitcoin портфейли, създадени в Cake, сега имат seed от 24 думи. Трябва да създадете нов Bitcoin адрес и да прехвърлите всичките си средства в него и веднага да спрете използването на стари портфейли. Моля, напревете това незабавно, за да подсигурите средствата си.", + "understand" : "Разбирам", + + "apk_update" : "APK ъпдейт", + + "buy_bitcoin" : "Купуване на Bitcoin", + "buy_with" : "Купуване чрез", + "moonpay_alert_text" : "Сумата трябва да бъде най-малко ${minAmount} ${fiatCurrency}", + + "outdated_electrum_wallet_receive_warning": "Ако този адрес има seed от 12 думи и е създаден чрез Cake, НЕ добавяйте Bitcoin в него. Всякакъв Bitcoin, изпратен на този адрес, може да бъде загубен завинаги. Създайте нов портфейл от 24 думи (натиснете менюто горе, вдясно, изберете Портфейли, изберете Създаване на нов портфейл, след това изберете Bitcoin) и НЕЗАБАВНО преместете своя Bitcoin там. Нови (такива с 24 думи) Bitcoin портфейли от Cake са надеждни", + "do_not_show_me": "Не показвай повече това", + + "unspent_coins_title" : "Неизползвани монети", + "unspent_coins_details_title" : "Подробности за неизползваните монети", + "freeze" : "Замразяване", + "frozen" : "Замразени", + "coin_control" : "Управление на монетите (не е задължително)", + + "address_detected" : "Открит е адрес", + "address_from_domain" : "Този адрес е от ${domain} на Unstoppable Domains", + + "add_receiver" : "Добавяне на друг получател (не е задължително)", + + "manage_yats" : "Управление на Yats", + "yat_alert_title" : "Търгувайте с крипто много по-лесно чрез Yat", + "yat_alert_content" : "Потребителите на Cake Wallet вече могат да изпращат и получават любимите си валути чрез неповторимо потребителско име от емоджита.", + "get_your_yat" : "Получете своя Yat", + "connect_an_existing_yat" : "Добавете съществуващ Yat", + "connect_yats": "Добавете Yats", + "yat_address" : "Yat Адрес", + "yat" : "Yat", + "address_from_yat" : "Този адрес е от ${emoji} в Yat", + "yat_error" : "Yat грешка", + "yat_error_content" : "Няма адреси, свързани с този Yat. Опитайте с друг Yat", + "choose_address" : "\n\nМоля, изберете адреса:", + "yat_popup_title" : "Адресът на вашия портфейл може да съдържа емоджита.", + "yat_popup_content" : "Вече можете да изпращате и да получавате крипто в Cake Wallet с вашия Yat - кратко потребителско име във формата на емоджи. Управлявайте своите Yats по всяко време в настройките", + "second_intro_title" : "Един емоджи адрес, обединяващ всички останали.", + "second_intro_content" : "Вашият Yat е уникален адрес във формата на емоджи, който играе ролята на всички Ваши дълги шестнайсетични портфейли за всяка валута.", + "third_intro_title" : "Yat добре се сработва с други", + "third_intro_content" : "Yats също живее извън Cake Wallet. Всеки адрес на портфейл може да бъде заменен с Yat!", + "learn_more" : "Научете още", + "search": "Търсене", + "search_language": "Търсене на език", + "search_currency": "Търсене на валута", + "new_template" : "Нов шаблон", + "electrum_address_disclaimer": "Нови адреси се генерират всеки път, когато използвате този, но и предишните продължават да работят", + "wallet_name_exists": "Вече има портфейл с това име. Моля, изберете друго име или преименувайте другия портфейл.", + "market_place": "Магазин", + "cake_pay_title": "Cake Pay Gift Карти", + "cake_pay_subtitle": "Купете гифткарти на намалени цени (само за САЩ)", + "cake_pay_web_cards_title": "Cake Pay Онлайн Карти", + "cake_pay_web_cards_subtitle": "Купете световно признати предплатени и гифт карти", + "about_cake_pay": "Cake Pay Ви позволява лесно да купувате предплатени карти, които веднага могат да се използват с над 150,000 търговци на територията на САЩ.", + "cake_pay_account_note": "Регистрайте се само с един имейл, за да виждате и купувате карти. За някои има дори и отстъпка!", + "already_have_account": "Вече имате профил?", + "create_account": "Създаване на профил", + "privacy_policy": "Политика за поверителността", + "welcome_to_cakepay": "Добре дошли в Cake Pay!", + "sign_up": "Регистрация", + "forgot_password": "Забравена парола", + "reset_password": "Нулиране на парола", + "gift_cards": "Gift Карти", + "setup_your_debit_card": "Настройте своята дебитна карта", + "no_id_required": "Без нужда от документ за самоличност. Използвайте навсякъде", + "how_to_use_card": "Как се ползва тази карта", + "purchase_gift_card": "Купуване на Gift Card", + "verification": "Потвърждаване", + "fill_code": "Моля, въведето кода за потвърждаване, изпратен на Вашия имейл", + "dont_get_code": "Не получихте код?", + "resend_code": "Повторно изпращане", + "debit_card": "Дебитна карта", + "cakepay_prepaid_card": "CakePay предплатена дебитна карта", + "no_id_needed": "Без нужда от документ за самоличност!", + "frequently_asked_questions": "Често задавани въпроси", + "debit_card_terms": "Съхранението и използването на данните от вашата платежна карта в този дигитален портфейл подлежат на условията на съответното съгласие за картодържец от издателя на картата.", + "please_reference_document": "Моля, вижте документите по-долу за повече информация.", + "cardholder_agreement": "Съгласие за картодържец", + "e_sign_consent": "E-Sign съгласие", + "agree_and_continue": "Съгласяване и продължаване", + "email_address": "Имейл адрес", + "agree_to": "Чрез създаването на акаунт вие се съгласявате с ", + "and": "и", + "enter_code": "Въведете код", + "congratulations": "Поздравления!", + "you_now_have_debit_card": "Вече имате дебитна карта", + "min_amount" : "Мин: ${value}", + "max_amount" : "Макс: ${value}", + "enter_amount": "Въведете сума", + "billing_address_info": "Ако Ви попитат за билинг адрес, въведето своя адрес за доставка", + "order_physical_card": "Поръчка на физическа карта", + "add_value": "Добавяне на стойност", + "activate": "Активиране", + "get_a": "Вземете ", + "digital_and_physical_card": " дигитална или физическа предплатена дебитна карта", + "get_card_note": ", която можете да заредите с дигитална валута. Без нужда от допълнителна информация!", + "signup_for_card_accept_terms": "Регистрайте се за картата и приемете условията.", + "add_fund_to_card": "Добавете предплатени средства в картите (до ${value})", + "use_card_info_two": "Средствата се обръщат в USD, когато биват запазени в предплатената карта, а не в дигитална валута.", + "use_card_info_three": "Използвайте дигиталната карта онлайн или чрез безконтактен метод на плащане.", + "optionally_order_card": "По желание поръчайте и физическа карта.", + "hide_details" : "Скриване на подробностите", + "show_details" : "Показване на подробностите", + "upto": "до ${value}", + "discount": "Спестете ${value}%", + "gift_card_amount": "Сума в Gift Card", + "bill_amount": "Искана сума", + "you_pay": "Вие плащате", + "tip": "Tip:", + "custom": "персонализирано", + "by_cake_pay": "от Cake Pay", + "expires": "Изтича", + "mm": "мм", + "yy": "гг", + "online": "Онлайн", + "offline": "Офлайн", + "gift_card_number": "Номер на Gift Card", + "pin_number": "PIN код", + "total_saving": "Общо спестявания", + "last_30_days": "Последните 30 дни", + "avg_savings": "Средни спестявания", + "view_all": "Виж всички", + "active_cards": "Активни карти", + "delete_account": "Изтриване на акаунт", + "cards": "Карти", + "active": "Активиране", + "redeemed": "Използвани", + "gift_card_balance_note": "Гифткарти с наличен баланс ще се покажат тук", + "gift_card_redeemed_note": "Използваните гифткарти ще се покажат тук", + "logout": "Logout", + "add_tip": "Add Tip", + "percentageOf": "от ${amount}", + "is_percentage": "е", + "search_category": "Търсене в категория", + "mark_as_redeemed": "Отбележи като използван", + "more_options": "Още настройки", + "awaiting_payment_confirmation": "Чака се потвърждение на плащането", + "transaction_sent_notice": "Ако процесът продължи повече от 1 минута, проверете някой block explorer и своя имейл.", + "agree": "Съгласен/а съм", + "in_store": "In Store", + "generating_gift_card": "Създаване на Gift Card", + "payment_was_received": "Плащането бе получено.", + "proceed_after_one_minute": "Ако процесът продължи повече от 1 минута, проверете своя имейл.", + "order_id": "ID на поръчка", + "gift_card_is_generated": "Gift Card бе създадена", + "open_gift_card": "Отвори Gift Card", + "contact_support": "Свържи се с отдел поддръжка", + "gift_cards_unavailable": "В момента гифткарти могат да бъдат закупени само с Monero, Bitcoin и Litecoin", + "introducing_cake_pay": "Запознайте се с Cake Pay!", + "cake_pay_learn_more": "Купете и използвайте гифткарти директно в приложението!\nПлъзнете отляво надясно, за да научите още.", + "automatic": "Автоматично", + "fixed_pair_not_supported": "Този fixed pair не се поддържа от избраната борса", + "variable_pair_not_supported": "Този variable pair не се поддържа от избраната борса", + "none_of_selected_providers_can_exchange": "Нито един от избраните provider-ъри не може да направи този превод", + "choose_one": "Изберете едно", + "choose_from_available_options": "Изберете от следните опции:", + "custom_redeem_amount": "Персонализирана сума за използване", + "add_custom_redemption": "Добавете персонализиран Redemption", + "remaining": "оставащи", + "delete_wallet": "Изтриване на портфейл", + "delete_wallet_confirm_message" : "Сигурни ли сте, че искате да изтриете протфейла ${wallet_name}?", + "low_fee": "Ниска такса", + "low_fee_alert": "Използвате ниска приоритетност в мрежата. Това може да доведе до дълго чакане, различни обменни курсове или отказани сделки. Препоръчваме използването на по-висока такса.", + "ignor": "Игнориране", + "use_suggested": "Използване на предложеното", + "do_not_share_warning_text" : "Не споделяйте това с никого, дори и отдел поддръжка.\n\nПарите Ви могат и ще бъдат откраднати!", + "help": "Помощ", + "all_transactions": "Всички транзакции", + "all_trades": "Всички сделкки", + "connection_sync": "Свързване и синхронизиране", + "security_and_backup": "Сигурност и резервни копия", + "create_backup": "Създаване на резервно копие", + "privacy_settings": "Настройки за поверителност", + "privacy": "Поверителност", + "display_settings": "Настройки на екрана", + "other_settings": "Други настройки", + "require_pin_after": "Въведете PIN след", + "always": "Винаги", + "minutes_to_pin_code": "${minute} минути", + "disable_exchange": "Деактивиране на борса", + "advanced_privacy_settings": "Допълнителни настройки за поверителност", + "settings_can_be_changed_later": "Тези настройки могат да бъдат променени по-късно от приложението", + "add_custom_node": "Добавяне на нов персонализиран Node", + "disable_fiat": "Деактивиране на fiat", + "fiat_api": "Fiat API", + "disabled": "Деактивирано", + "enabled": "Активирано", + "tor_only": "Само чрез Tor", + "unmatched_currencies": "Валутата на този портфейл не съвпада с тази от сканирания QR код", + "orbot_running_alert": "Моля, включете Orbot преди да свържете към този node.", + "contact_list_contacts": "Контакти", + "contact_list_wallets": "Моите портфейли", + "bitcoin_payments_require_1_confirmation": "Плащанията с Bitcoin изискват потвърждение, което може да отнеме 20 минути или повече. Благодарим за търпението! Ще получите имейл, когато плащането е потвърдено.", + "send_to_this_address" : "Send ${currency} ${tag}to this address", + "arrive_in_this_address" : "${currency} ${tag}ще отидат на този адрес", + "do_not_send": "Не изпращай", + "error_dialog_content": "Получихме грешка.\n\nМоля, изпратете доклада до нашия отдел поддръжка, за да подобрим приложението." +} From 2c43f29d64adc344e463a4b1b32da721780e2d1a Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Mon, 27 Feb 2023 15:59:20 +0200 Subject: [PATCH 26/59] Check if context is still mounted before showing a popup [skip ci] --- lib/src/screens/dashboard/dashboard_page.dart | 5 +++-- lib/src/screens/dashboard/widgets/address_page.dart | 4 +++- lib/src/screens/send/send_page.dart | 4 +++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/src/screens/dashboard/dashboard_page.dart b/lib/src/screens/dashboard/dashboard_page.dart index c21365aa4..699e887ac 100644 --- a/lib/src/screens/dashboard/dashboard_page.dart +++ b/lib/src/screens/dashboard/dashboard_page.dart @@ -8,7 +8,6 @@ import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/cupertino.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/menu_widget.dart'; @@ -229,7 +228,8 @@ class DashboardPage extends BasePage { } await Future.delayed(Duration(seconds: 1)); - await showPopUp( + if (context.mounted) { + await showPopUp( context: context, builder: (BuildContext context) { return AlertWithOneAction( @@ -239,6 +239,7 @@ class DashboardPage extends BasePage { buttonText: S.of(context).understand, buttonAction: () => Navigator.of(context).pop()); }); + } }); var needToPresentYat = false; diff --git a/lib/src/screens/dashboard/widgets/address_page.dart b/lib/src/screens/dashboard/widgets/address_page.dart index 9a9a2c070..a09eb2ba8 100644 --- a/lib/src/screens/dashboard/widgets/address_page.dart +++ b/lib/src/screens/dashboard/widgets/address_page.dart @@ -117,7 +117,8 @@ class AddressPage extends BasePage { } await Future.delayed(Duration(seconds: 1)); - await showPopUp( + if (context.mounted) { + await showPopUp( context: context, builder: (BuildContext context) { return AlertWithTwoActions( @@ -131,6 +132,7 @@ class AddressPage extends BasePage { Navigator.of(context).pop(); }); }); + } }); return KeyboardActions( diff --git a/lib/src/screens/send/send_page.dart b/lib/src/screens/send/send_page.dart index 33e199487..881536944 100644 --- a/lib/src/screens/send/send_page.dart +++ b/lib/src/screens/send/send_page.dart @@ -379,7 +379,8 @@ class SendPage extends BasePage { if (state is ExecutedSuccessfullyState) { WidgetsBinding.instance.addPostFrameCallback((_) { - showPopUp( + if (context.mounted) { + showPopUp( context: context, builder: (BuildContext context) { return ConfirmSendingAlert( @@ -423,6 +424,7 @@ class SendPage extends BasePage { }, actionLeftButton: () => Navigator.of(context).pop()); }); + } }); } From f470723ece0d0f3403834341dcd30394151c8cdb Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Mon, 27 Feb 2023 16:07:22 +0200 Subject: [PATCH 27/59] Add try/catch block in case launch url throws a platform exception --- .../screens/settings/widgets/settings_link_provider_cell.dart | 4 +++- lib/view_model/support_view_model.dart | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/src/screens/settings/widgets/settings_link_provider_cell.dart b/lib/src/screens/settings/widgets/settings_link_provider_cell.dart index 1b67f60e4..e64d6543b 100644 --- a/lib/src/screens/settings/widgets/settings_link_provider_cell.dart +++ b/lib/src/screens/settings/widgets/settings_link_provider_cell.dart @@ -30,6 +30,8 @@ class SettingsLinkProviderCell extends StandardListRow { color: Palette.blueCraiola)); static void _launchUrl(String url) async { - await launch(url, forceSafariVC: false); + try { + await launch(url, forceSafariVC: false); + } catch (e) {} } } diff --git a/lib/view_model/support_view_model.dart b/lib/view_model/support_view_model.dart index 4e602089a..15c25968f 100644 --- a/lib/view_model/support_view_model.dart +++ b/lib/view_model/support_view_model.dart @@ -18,7 +18,9 @@ abstract class SupportViewModelBase with Store { RegularListItem( title: S.current.faq, handler: (BuildContext context) async { - await launch(url); + try { + await launch(url); + } catch (e) {} }, ), LinkListItem( From 72d679ca250be208715c072d0b69bfadaa85c88c Mon Sep 17 00:00:00 2001 From: Justin Ehrenhofer Date: Mon, 27 Feb 2023 09:50:27 -0600 Subject: [PATCH 28/59] Delete Refund Policy It was never used, and we don't need it here. --- Refund Pollicy.md | 27 --------------------------- 1 file changed, 27 deletions(-) delete mode 100644 Refund Pollicy.md diff --git a/Refund Pollicy.md b/Refund Pollicy.md deleted file mode 100644 index d2e13f5ef..000000000 --- a/Refund Pollicy.md +++ /dev/null @@ -1,27 +0,0 @@ -# Refund Policy - -This Refund Policy covers returns, refunds, and cancellations for your purchases from Cake Technologies, LLC (“Company,” “we,” or “us”). - -### Returns - -We do not accept returns of cryptocurrencies. Once cryptocurrencies are delivered to the provided wallet address, we cannot accept a return. If we allow you to sell cryptocurrencies to us, you may optionally engage in another trade to convert the cryptocurrency to another asset at the price at that point in time. - -### Refunds - -We do not issue refunds except in the case of an error resulting in a non-delivery of cryptocurrencies. In this case, we will decide whether to complete the trade or refund the account. We endeavor to complete an investigation to determine whether an error occurred and to make this decision within ten (10) days of receipt of your error report, and we will do so in any case within ninety (90) days. We reserve the right to issue a refund for the equivalent amount in another asset at the exchange rate then offered by us should we determine an error occurred. - -### Cancellations - -Once you make a payment to us, trades cannot be cancelled. In nearly all cases, we deliver cryptocurrency to your address in a matter of seconds or minutes. If you have not received your purchased cryptocurrency within six (6) hours of completing payment and have confirmed that your wallet is connected to a reliable node and fully synchronized with the network, please contact us. - -### Network Fees - -We may charge a network fee for the delivery of cryptocurrencies. This will be clearly shown in the checkout page. If we issue a refund, we may choose to deduct any network fees from the refund, including any network fees incurred in processing the refund. - -### Fraud - -If you believe a cryptocurrency purchase was conducted fraudulently, please alert your bank or card issuer, as applicable, and follow their instructions. Please be aware that fraudulent transactions may result in the loss of your money with no recourse. - -### Contact Us - -If you believe that there has been an error in processing your payment, please contact us at support@cakewallet.com. \ No newline at end of file From 9c26912ce948d4cad6734bdfa8e849a97f0a76ca Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Mon, 27 Feb 2023 18:28:30 +0200 Subject: [PATCH 29/59] Add Socket exception to ignored errors [skip ci] --- lib/utils/exception_handler.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/utils/exception_handler.dart b/lib/utils/exception_handler.dart index 55d27e6a1..8285b9c52 100644 --- a/lib/utils/exception_handler.dart +++ b/lib/utils/exception_handler.dart @@ -131,6 +131,7 @@ class ExceptionHandler { "errno = 110", // SocketException: Connection timed out "errno = 54", // SocketException: Connection reset by peer "errno = 49", // SocketException: Can't assign requested address + "errno = 57", // SocketException: Read failed (OS Error: Socket is not connected) "errno = 28", // OS Error: No space left on device "PERMISSION_NOT_GRANTED", ]; From c30bbc75b145564c19ff8b0eaf8c9d5b783a30c5 Mon Sep 17 00:00:00 2001 From: MihailKovachev Date: Tue, 28 Feb 2023 10:04:39 +0200 Subject: [PATCH 30/59] Updated Readme with supported languages --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8e94080cb..e18db31a0 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,7 @@ Edit the applicable `strings_XX.arb` file in `res/values/` and open a pull reque - Turkish - Burmese - Urdu +- Bulgarian ## Add a new language From 25ec44b4e515b1628ee53db2fd2dda744f8b66ac Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Tue, 28 Feb 2023 15:37:20 +0200 Subject: [PATCH 31/59] Add missing Nano currency image [skip ci] --- cw_core/lib/crypto_currency.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cw_core/lib/crypto_currency.dart b/cw_core/lib/crypto_currency.dart index 90051f1f7..00bfc237c 100644 --- a/cw_core/lib/crypto_currency.dart +++ b/cw_core/lib/crypto_currency.dart @@ -94,7 +94,7 @@ class CryptoCurrency extends EnumerableItem with Serializable { static const eos = CryptoCurrency(title: 'EOS', fullName: 'EOS', raw: 7, name: 'eos', iconPath: 'assets/images/eos_icon.png'); static const eth = CryptoCurrency(title: 'ETH', fullName: 'Ethereum', raw: 8, name: 'eth', iconPath: 'assets/images/eth_icon.png'); static const ltc = CryptoCurrency(title: 'LTC', fullName: 'Litecoin', raw: 9, name: 'ltc', iconPath: 'assets/images/litecoin-ltc_icon.png'); - static const nano = CryptoCurrency(title: 'NANO', raw: 10, name: 'nano'); + static const nano = CryptoCurrency(title: 'NANO', raw: 10, name: 'nano', iconPath: 'assets/images/nano.png'); static const trx = CryptoCurrency(title: 'TRX', fullName: 'TRON', raw: 11, name: 'trx', iconPath: 'assets/images/trx_icon.png'); static const usdt = CryptoCurrency(title: 'USDT', tag: 'OMNI', fullName: 'USDT Tether', raw: 12, name: 'usdt', iconPath: 'assets/images/usdt_icon.png'); static const usdterc20 = CryptoCurrency(title: 'USDT', tag: 'ETH', fullName: 'USDT Tether', raw: 13, name: 'usdterc20', iconPath: 'assets/images/usdterc20_icon.png'); From ee67bc76d716c07cb91420f3f1194bcf8339ea4d Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Tue, 28 Feb 2023 16:18:06 +0200 Subject: [PATCH 32/59] Fix translation issue --- res/values/strings_bg.arb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index 32e9ed0c5..2099abc19 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -327,7 +327,7 @@ "error_text_fiat" : "Сумата не може да надвишава наличния баланс.\nThe number of fraction digits must be less or equal to 2", "error_text_subaddress_name" : "Името на подадреса не може да съдържат символите ` , ' \" \n и трябва да е между 1 и 20 символа", "error_text_amount" : "Сумата може да съдържа само числа", - "error_text_wallet_name" : "Името на портфейла може да съдържа само букви, цифри, и символите "_" и "-" \n и трябва да е между 1 и 33 символа", + "error_text_wallet_name" : "Името на портфейла може да съдържа само букви, цифри, и символите _ и - \n и трябва да е между 1 и 33 символа", "error_text_keys" : "Ключовете за портфейл може да съдържат само 64 шестнайсетични символа", "error_text_crypto_currency" : "Броят на цифрите след десетичната запетая\nможе да бъде най-много 12", "error_text_minimal_limit" : "Сделка за ${provider} не беше създадена. Сумата е по-малко от минималната: ${min} ${currency}", From 8419767c4f39dc33cd3ac974322bb826d3e0ddd1 Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Tue, 28 Feb 2023 18:12:24 +0200 Subject: [PATCH 33/59] Add limit query to trocador --- .../trocador/trocador_exchange_provider.dart | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/lib/exchange/trocador/trocador_exchange_provider.dart b/lib/exchange/trocador/trocador_exchange_provider.dart index 457757ea7..49503a1d0 100644 --- a/lib/exchange/trocador/trocador_exchange_provider.dart +++ b/lib/exchange/trocador/trocador_exchange_provider.dart @@ -40,6 +40,7 @@ class TrocadorExchangeProvider extends ExchangeProvider { static const newRatePath = '/api/new_rate'; static const createTradePath = 'api/new_trade'; static const tradePath = 'api/trade'; + static const coinPath = 'api/coin'; String _lastUsedRateId; @override @@ -134,9 +135,34 @@ class TrocadorExchangeProvider extends ExchangeProvider { {required CryptoCurrency from, required CryptoCurrency to, required bool isFixedRateMode}) async { - //TODO: implement limits from trocador api + + final params = { + 'api_key': apiKey, + 'ticker': from.title.toLowerCase(), + 'name': from.name, + }; + + final String apiAuthority = shouldUseOnionAddress ? await _getAuthority() : clearNetAuthority; + final uri = Uri.https(apiAuthority, coinPath, params); + + final response = await get(uri); + + if (response.statusCode != 200) { + throw Exception('Unexpected http status: ${response.statusCode}'); + } + + final responseJSON = json.decode(response.body) as List; + + if (responseJSON.isEmpty) { + throw Exception('No data'); + } + + final coinJson = responseJSON.first as Map; + + return Limits( - min: 0.0, + min: coinJson['minimum'] as double, + max: coinJson['maximum'] as double, ); } From f65f7b892c96906e2bf7bdf94bbd75448149ba46 Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Tue, 28 Feb 2023 18:13:21 +0200 Subject: [PATCH 34/59] Fix translation issue [skip ci] --- res/values/strings_bg.arb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index 32e9ed0c5..2099abc19 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -327,7 +327,7 @@ "error_text_fiat" : "Сумата не може да надвишава наличния баланс.\nThe number of fraction digits must be less or equal to 2", "error_text_subaddress_name" : "Името на подадреса не може да съдържат символите ` , ' \" \n и трябва да е между 1 и 20 символа", "error_text_amount" : "Сумата може да съдържа само числа", - "error_text_wallet_name" : "Името на портфейла може да съдържа само букви, цифри, и символите "_" и "-" \n и трябва да е между 1 и 33 символа", + "error_text_wallet_name" : "Името на портфейла може да съдържа само букви, цифри, и символите _ и - \n и трябва да е между 1 и 33 символа", "error_text_keys" : "Ключовете за портфейл може да съдържат само 64 шестнайсетични символа", "error_text_crypto_currency" : "Броят на цифрите след десетичната запетая\nможе да бъде най-много 12", "error_text_minimal_limit" : "Сделка за ${provider} не беше създадена. Сумата е по-малко от минималната: ${min} ${currency}", From ce4b1cd4aefbee242e2492e62e9d6154af969ea5 Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Tue, 28 Feb 2023 18:23:21 +0200 Subject: [PATCH 35/59] Update fiat API version Enable Tor support for Fiat --- lib/core/fiat_conversion_service.dart | 46 +++++++++++------- lib/reactions/bootstrap.dart | 2 + lib/reactions/fiat_rate_update.dart | 4 +- .../on_current_fiat_api_mode_change.dart | 25 ++++++++++ lib/reactions/on_current_fiat_change.dart | 7 ++- lib/reactions/on_current_wallet_change.dart | 6 ++- lib/src/screens/settings/privacy_page.dart | 48 +++++++++++-------- .../settings/privacy_settings_view_model.dart | 10 +--- 8 files changed, 97 insertions(+), 51 deletions(-) create mode 100644 lib/reactions/on_current_fiat_api_mode_change.dart diff --git a/lib/core/fiat_conversion_service.dart b/lib/core/fiat_conversion_service.dart index f4ec3775b..96dff9614 100644 --- a/lib/core/fiat_conversion_service.dart +++ b/lib/core/fiat_conversion_service.dart @@ -4,18 +4,30 @@ import 'dart:convert'; import 'package:flutter/foundation.dart'; import 'package:http/http.dart'; -const fiatApiAuthority = 'fiat-api.cakewallet.com'; -const fiatApiPath = '/v1/rates'; +const _fiatApiClearNetAuthority = 'fiat-api.cakewallet.com'; +const _fiatApiOnionAuthority = 'n4z7bdcmwk2oyddxvzaap3x2peqcplh3pzdy7tpkk5ejz5n4mhfvoxqd.onion'; +const _fiatApiPath = '/v2/rates'; Future _fetchPrice(Map args) async { final crypto = args['crypto'] as CryptoCurrency; final fiat = args['fiat'] as FiatCurrency; + final torOnly = args['torOnly'] as bool; double price = 0.0; + print("@@@@@@@@@@@@@@"); + print(crypto); + print(fiat); + print(torOnly); try { - final fiatStringified = fiat.toString(); - final uri = Uri.https(fiatApiAuthority, fiatApiPath, - {'convert': fiatStringified}); + final uri = Uri.https( + torOnly ? _fiatApiOnionAuthority : _fiatApiClearNetAuthority, + _fiatApiPath, + { + 'interval_count': '1', + 'base': crypto.toString(), + 'quote': fiat.toString(), + }, + ); final response = await get(uri); if (response.statusCode != 200) { @@ -23,14 +35,12 @@ Future _fetchPrice(Map args) async { } final responseJSON = json.decode(response.body) as Map; - final data = responseJSON['data'] as List; + final results = responseJSON['results'] as Map; - for (final item in data) { - if (item['symbol'] == crypto.title) { - price = item['quote'][fiatStringified]['price'] as double; - break; - } + if (results.isNotEmpty) { + price = results.values.first as double; } + print(price); return price; } catch (e) { @@ -38,12 +48,14 @@ Future _fetchPrice(Map args) async { } } -Future _fetchPriceAsync( - CryptoCurrency crypto, FiatCurrency fiat) async => - compute(_fetchPrice, {'fiat': fiat, 'crypto': crypto}); +Future _fetchPriceAsync(CryptoCurrency crypto, FiatCurrency fiat, bool torOnly) async => + compute(_fetchPrice, {'fiat': fiat, 'crypto': crypto, 'torOnly': torOnly}); class FiatConversionService { - static Future fetchPrice( - CryptoCurrency crypto, FiatCurrency fiat) async => - await _fetchPriceAsync(crypto, fiat); + static Future fetchPrice({ + required CryptoCurrency crypto, + required FiatCurrency fiat, + required bool torOnly, + }) async => + await _fetchPriceAsync(crypto, fiat, torOnly); } diff --git a/lib/reactions/bootstrap.dart b/lib/reactions/bootstrap.dart index 4b65ed9d2..6451c92c5 100644 --- a/lib/reactions/bootstrap.dart +++ b/lib/reactions/bootstrap.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'package:cake_wallet/reactions/fiat_rate_update.dart'; +import 'package:cake_wallet/reactions/on_current_fiat_api_mode_change.dart'; import 'package:cake_wallet/reactions/on_current_node_change.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/widgets.dart'; @@ -31,6 +32,7 @@ Future bootstrap(GlobalKey navigatorKey) async { startCurrentWalletChangeReaction( appStore, settingsStore, fiatConversionStore); startCurrentFiatChangeReaction(appStore, settingsStore, fiatConversionStore); + startCurrentFiatApiModeChangeReaction(appStore, settingsStore, fiatConversionStore); startOnCurrentNodeChangeReaction(appStore); startFiatRateUpdate(appStore, settingsStore, fiatConversionStore); } diff --git a/lib/reactions/fiat_rate_update.dart b/lib/reactions/fiat_rate_update.dart index 48c61d8ca..f9ddbef52 100644 --- a/lib/reactions/fiat_rate_update.dart +++ b/lib/reactions/fiat_rate_update.dart @@ -26,7 +26,9 @@ Future startFiatRateUpdate( } else { fiatConversionStore.prices[appStore.wallet!.currency] = await FiatConversionService.fetchPrice( - appStore.wallet!.currency, settingsStore.fiatCurrency); + crypto: appStore.wallet!.currency, + fiat: settingsStore.fiatCurrency, + torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly); } } catch (e) { print(e); diff --git a/lib/reactions/on_current_fiat_api_mode_change.dart b/lib/reactions/on_current_fiat_api_mode_change.dart new file mode 100644 index 000000000..5bcaeef4c --- /dev/null +++ b/lib/reactions/on_current_fiat_api_mode_change.dart @@ -0,0 +1,25 @@ +import 'package:cake_wallet/entities/fiat_api_mode.dart'; +import 'package:mobx/mobx.dart'; +import 'package:cake_wallet/core/fiat_conversion_service.dart'; +import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart'; +import 'package:cake_wallet/store/settings_store.dart'; +import 'package:cake_wallet/store/app_store.dart'; + +ReactionDisposer? _onCurrentFiatCurrencyChangeDisposer; + +void startCurrentFiatApiModeChangeReaction(AppStore appStore, + SettingsStore settingsStore, FiatConversionStore fiatConversionStore) { + _onCurrentFiatCurrencyChangeDisposer?.reaction.dispose(); + _onCurrentFiatCurrencyChangeDisposer = reaction( + (_) => settingsStore.fiatApiMode, (FiatApiMode fiatApiMode) async { + if (appStore.wallet == null || fiatApiMode == FiatApiMode.disabled) { + return; + } + + fiatConversionStore.prices[appStore.wallet!.currency] = + await FiatConversionService.fetchPrice( + crypto: appStore.wallet!.currency, + fiat: settingsStore.fiatCurrency, + torOnly: fiatApiMode == FiatApiMode.torOnly); + }); +} diff --git a/lib/reactions/on_current_fiat_change.dart b/lib/reactions/on_current_fiat_change.dart index 5170c4576..873984940 100644 --- a/lib/reactions/on_current_fiat_change.dart +++ b/lib/reactions/on_current_fiat_change.dart @@ -18,7 +18,10 @@ void startCurrentFiatChangeReaction(AppStore appStore, } final cryptoCurrency = appStore.wallet!.currency; - fiatConversionStore.prices[appStore.wallet!.currency] = - await FiatConversionService.fetchPrice(cryptoCurrency, fiatCurrency); + fiatConversionStore.prices[cryptoCurrency] = + await FiatConversionService.fetchPrice( + crypto: cryptoCurrency, + fiat: fiatCurrency, + torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly); }); } diff --git a/lib/reactions/on_current_wallet_change.dart b/lib/reactions/on_current_wallet_change.dart index 270415820..2325fe382 100644 --- a/lib/reactions/on_current_wallet_change.dart +++ b/lib/reactions/on_current_wallet_change.dart @@ -71,7 +71,7 @@ void startCurrentWalletChangeReaction(AppStore appStore, await updateHavenRate(fiatConversionStore); } - if (wallet.walletInfo.address?.isEmpty ?? true) { + if (wallet.walletInfo.address.isEmpty) { wallet.walletInfo.address = wallet.walletAddresses.address; if (wallet.walletInfo.isInBox) { @@ -95,7 +95,9 @@ void startCurrentWalletChangeReaction(AppStore appStore, fiatConversionStore.prices[wallet.currency] = 0; fiatConversionStore.prices[wallet.currency] = await FiatConversionService.fetchPrice( - wallet.currency, settingsStore.fiatCurrency); + crypto: wallet.currency, + fiat: settingsStore.fiatCurrency, + torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly); } catch (e) { print(e.toString()); } diff --git a/lib/src/screens/settings/privacy_page.dart b/lib/src/screens/settings/privacy_page.dart index 2f15cc225..8fd6bab82 100644 --- a/lib/src/screens/settings/privacy_page.dart +++ b/lib/src/screens/settings/privacy_page.dart @@ -1,6 +1,9 @@ +import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:cake_wallet/src/screens/settings/widgets/settings_choices_cell.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart'; +import 'package:cake_wallet/view_model/settings/choices_list_item.dart'; import 'package:cake_wallet/view_model/settings/privacy_settings_view_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -18,29 +21,32 @@ class PrivacyPage extends BasePage { return Container( padding: EdgeInsets.only(top: 10), child: Observer(builder: (_) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - SettingsSwitcherCell( - title: S.current.disable_fiat, - value: _privacySettingsViewModel.isFiatDisabled, + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + SettingsChoicesCell( + ChoicesListItem( + title: S.current.fiat_api, + items: FiatApiMode.all, + selectedItem: _privacySettingsViewModel.fiatApiMode, + onItemSelected: (FiatApiMode fiatApiMode) => + _privacySettingsViewModel.setFiatMode(fiatApiMode), + ), + ), + SettingsSwitcherCell( + title: S.current.disable_exchange, + value: _privacySettingsViewModel.disableExchange, onValueChange: (BuildContext context, bool value) { - _privacySettingsViewModel.setFiatMode(value); + _privacySettingsViewModel.setEnableExchange(value); }), - SettingsSwitcherCell( - title: S.current.disable_exchange, - value: _privacySettingsViewModel.disableExchange, - onValueChange: (BuildContext context, bool value) { - _privacySettingsViewModel.setEnableExchange(value); - }), - SettingsSwitcherCell( - title: S.current.settings_save_recipient_address, - value: _privacySettingsViewModel.shouldSaveRecipientAddress, - onValueChange: (BuildContext _, bool value) { - _privacySettingsViewModel.setShouldSaveRecipientAddress(value); - }) - ], - ); + SettingsSwitcherCell( + title: S.current.settings_save_recipient_address, + value: _privacySettingsViewModel.shouldSaveRecipientAddress, + onValueChange: (BuildContext _, bool value) { + _privacySettingsViewModel.setShouldSaveRecipientAddress(value); + }), + ], + ); }), ); } diff --git a/lib/view_model/settings/privacy_settings_view_model.dart b/lib/view_model/settings/privacy_settings_view_model.dart index f8c3e5b50..efa7cdc36 100644 --- a/lib/view_model/settings/privacy_settings_view_model.dart +++ b/lib/view_model/settings/privacy_settings_view_model.dart @@ -18,7 +18,7 @@ abstract class PrivacySettingsViewModelBase with Store { bool get shouldSaveRecipientAddress => _settingsStore.shouldSaveRecipientAddress; @computed - bool get isFiatDisabled => _settingsStore.fiatApiMode == FiatApiMode.disabled; + FiatApiMode get fiatApiMode => _settingsStore.fiatApiMode; @action void setShouldSaveRecipientAddress(bool value) => _settingsStore.shouldSaveRecipientAddress = value; @@ -27,12 +27,6 @@ abstract class PrivacySettingsViewModelBase with Store { void setEnableExchange(bool value) => _settingsStore.disableExchange = value; @action - void setFiatMode(bool value) { - if (value) { - _settingsStore.fiatApiMode = FiatApiMode.disabled; - return; - } - _settingsStore.fiatApiMode = FiatApiMode.enabled; - } + void setFiatMode(FiatApiMode fiatApiMode) => _settingsStore.fiatApiMode = fiatApiMode; } From 18af7e7661bfbcecc859e5270536172a750c0d0b Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Tue, 28 Feb 2023 18:36:39 +0200 Subject: [PATCH 36/59] remove print statements [skip ci] --- lib/core/fiat_conversion_service.dart | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/core/fiat_conversion_service.dart b/lib/core/fiat_conversion_service.dart index 96dff9614..95c1e23a4 100644 --- a/lib/core/fiat_conversion_service.dart +++ b/lib/core/fiat_conversion_service.dart @@ -13,10 +13,6 @@ Future _fetchPrice(Map args) async { final fiat = args['fiat'] as FiatCurrency; final torOnly = args['torOnly'] as bool; double price = 0.0; - print("@@@@@@@@@@@@@@"); - print(crypto); - print(fiat); - print(torOnly); try { final uri = Uri.https( @@ -40,7 +36,6 @@ Future _fetchPrice(Map args) async { if (results.isNotEmpty) { price = results.values.first as double; } - print(price); return price; } catch (e) { From 2c5b44426fcb32196af2354e6d6821d0f74226b9 Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Tue, 28 Feb 2023 20:34:51 +0200 Subject: [PATCH 37/59] Upgrade flutter packages --- .github/workflows/pr_test_build.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 1766bbe86..a6a66cac2 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -77,6 +77,11 @@ jobs: run: | cd /opt/android/cake_wallet flutter packages pub run tool/generate_localization.dart + + - name: Upgrade flutter packages + run: | + cd /opt/android/cake_wallet + flutter pub upgrade - name: Build generated code run: | From 2c9f17e728df856c790e0b4e44dc77a069746b08 Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Tue, 28 Feb 2023 20:45:03 +0200 Subject: [PATCH 38/59] Upgrade flutter packages --- .github/workflows/pr_test_build.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index a6a66cac2..60dd1bc68 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -81,7 +81,10 @@ jobs: - name: Upgrade flutter packages run: | cd /opt/android/cake_wallet - flutter pub upgrade + cd cw_core && flutter pub upgrade && cd .. + cd cw_monero && flutter pub upgrade && cd .. + cd cw_bitcoin && flutter pub upgrade && cd .. + cd cw_haven && flutter pub upgrade && cd .. - name: Build generated code run: | From 9b173f4fc82aca61213cec6fe3dfd4b19920d2ab Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Wed, 1 Mar 2023 05:12:36 +0200 Subject: [PATCH 39/59] Separate test build apk from overriding the original app --- .github/workflows/pr_test_build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index f8496ffcd..0be2a5e68 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -5,7 +5,7 @@ on: branches: [ main ] jobs: - test: + PR_test_build: runs-on: ubuntu-20.04 env: @@ -36,6 +36,7 @@ jobs: cd cake_wallet/scripts/android/ ./install_ndk.sh source ./app_env.sh cakewallet + export APP_ANDROID_PACKAGE="com.cakewallet.cake_wallet.test" ./app_config.sh - name: Cache Externals From 23128dbe3c73b94f2daa787f0de913f46fbcc688 Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Wed, 1 Mar 2023 05:40:52 +0200 Subject: [PATCH 40/59] Separate test build apk from overriding the original app --- .github/workflows/pr_test_build.yml | 3 +-- scripts/android/app_env.sh | 12 +++++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 0be2a5e68..759aa218d 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -35,8 +35,7 @@ jobs: git clone https://github.com/cake-tech/cake_wallet.git --branch $GITHUB_HEAD_REF cd cake_wallet/scripts/android/ ./install_ndk.sh - source ./app_env.sh cakewallet - export APP_ANDROID_PACKAGE="com.cakewallet.cake_wallet.test" + source ./app_env.sh cakewallet.test ./app_config.sh - name: Cache Externals diff --git a/scripts/android/app_env.sh b/scripts/android/app_env.sh index eabd192ce..26b5a399b 100644 --- a/scripts/android/app_env.sh +++ b/scripts/android/app_env.sh @@ -9,8 +9,9 @@ APP_ANDROID_PACKAGE="" MONERO_COM="monero.com" CAKEWALLET="cakewallet" HAVEN="haven" +CAKEWALLET_TEST="cakewallet.test" -TYPES=($MONERO_COM $CAKEWALLET $HAVEN) +TYPES=($MONERO_COM $CAKEWALLET $HAVEN $CAKEWALLET_TEST) APP_ANDROID_TYPE=$1 MONERO_COM_NAME="Monero.com" @@ -31,6 +32,8 @@ HAVEN_BUILD_NUMBER=1 HAVEN_BUNDLE_ID="com.cakewallet.haven" HAVEN_PACKAGE="com.cakewallet.haven" +CAKEWALLET_TEST_PACKAGE="com.cakewallet.cake_wallet.test" + if ! [[ " ${TYPES[*]} " =~ " ${APP_ANDROID_TYPE} " ]]; then echo "Wrong app type." return 1 2>/dev/null @@ -59,6 +62,13 @@ case $APP_ANDROID_TYPE in APP_ANDROID_BUNDLE_ID=$HAVEN_BUNDLE_ID APP_ANDROID_PACKAGE=$HAVEN_PACKAGE ;; + $CAKEWALLET_TEST) + APP_ANDROID_NAME=$CAKEWALLET_NAME + APP_ANDROID_VERSION=$CAKEWALLET_VERSION + APP_ANDROID_BUILD_NUMBER=$CAKEWALLET_BUILD_NUMBER + APP_ANDROID_BUNDLE_ID=$CAKEWALLET_BUNDLE_ID + APP_ANDROID_PACKAGE=CAKEWALLET_TEST_PACKAGE + ;; esac export APP_ANDROID_TYPE From 1481ebc22920e0d830298aa9319aafad8b592cee Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Wed, 1 Mar 2023 06:34:10 +0200 Subject: [PATCH 41/59] Separate test build apk from overriding the original app --- scripts/android/app_env.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/android/app_env.sh b/scripts/android/app_env.sh index 26b5a399b..c456f8466 100644 --- a/scripts/android/app_env.sh +++ b/scripts/android/app_env.sh @@ -32,7 +32,7 @@ HAVEN_BUILD_NUMBER=1 HAVEN_BUNDLE_ID="com.cakewallet.haven" HAVEN_PACKAGE="com.cakewallet.haven" -CAKEWALLET_TEST_PACKAGE="com.cakewallet.cake_wallet.test" +CAKEWALLET_TEST_PACKAGE="com.cakewallet.test" if ! [[ " ${TYPES[*]} " =~ " ${APP_ANDROID_TYPE} " ]]; then echo "Wrong app type." From 4ab7edf90b517c97258f8cbefd5f19abb3d53f42 Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Wed, 1 Mar 2023 07:01:17 +0200 Subject: [PATCH 42/59] Separate test build apk from overriding the original app --- .github/workflows/pr_test_build.yml | 6 +++++- scripts/android/app_env.sh | 14 ++------------ 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 759aa218d..bd158a84d 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -35,7 +35,11 @@ jobs: git clone https://github.com/cake-tech/cake_wallet.git --branch $GITHUB_HEAD_REF cd cake_wallet/scripts/android/ ./install_ndk.sh - source ./app_env.sh cakewallet.test + source ./app_env.sh cakewallet + APP_ANDROID_PACKAGE="com.cakewallet.test" + export APP_ANDROID_PACKAGE="com.cakewallet.test" + set APP_ANDROID_PACKAGE="com.cakewallet.test" + env APP_ANDROID_PACKAGE="com.cakewallet.test" ./app_config.sh - name: Cache Externals diff --git a/scripts/android/app_env.sh b/scripts/android/app_env.sh index c456f8466..d2bee1773 100644 --- a/scripts/android/app_env.sh +++ b/scripts/android/app_env.sh @@ -9,9 +9,8 @@ APP_ANDROID_PACKAGE="" MONERO_COM="monero.com" CAKEWALLET="cakewallet" HAVEN="haven" -CAKEWALLET_TEST="cakewallet.test" -TYPES=($MONERO_COM $CAKEWALLET $HAVEN $CAKEWALLET_TEST) +TYPES=($MONERO_COM $CAKEWALLET $HAVEN) APP_ANDROID_TYPE=$1 MONERO_COM_NAME="Monero.com" @@ -32,8 +31,6 @@ HAVEN_BUILD_NUMBER=1 HAVEN_BUNDLE_ID="com.cakewallet.haven" HAVEN_PACKAGE="com.cakewallet.haven" -CAKEWALLET_TEST_PACKAGE="com.cakewallet.test" - if ! [[ " ${TYPES[*]} " =~ " ${APP_ANDROID_TYPE} " ]]; then echo "Wrong app type." return 1 2>/dev/null @@ -62,13 +59,6 @@ case $APP_ANDROID_TYPE in APP_ANDROID_BUNDLE_ID=$HAVEN_BUNDLE_ID APP_ANDROID_PACKAGE=$HAVEN_PACKAGE ;; - $CAKEWALLET_TEST) - APP_ANDROID_NAME=$CAKEWALLET_NAME - APP_ANDROID_VERSION=$CAKEWALLET_VERSION - APP_ANDROID_BUILD_NUMBER=$CAKEWALLET_BUILD_NUMBER - APP_ANDROID_BUNDLE_ID=$CAKEWALLET_BUNDLE_ID - APP_ANDROID_PACKAGE=CAKEWALLET_TEST_PACKAGE - ;; esac export APP_ANDROID_TYPE @@ -76,4 +66,4 @@ export APP_ANDROID_NAME export APP_ANDROID_VERSION export APP_ANDROID_BUILD_NUMBER export APP_ANDROID_BUNDLE_ID -export APP_ANDROID_PACKAGE +export APP_ANDROID_PACKAGE \ No newline at end of file From c3c1999048410edcddd896a5f6b995302ee5f156 Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Wed, 1 Mar 2023 15:39:34 +0200 Subject: [PATCH 43/59] Change package name when renaming test app --- .github/workflows/pr_test_build.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index bd158a84d..790316617 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -117,7 +117,9 @@ jobs: echo "const twitterBearerToken = '${{ secrets.TWITTER_BEARER_TOKEN }}';" >> lib/.secrets.g.dart - name: Rename app - run: sed -i -e "s/\${APP_NAME}/$GITHUB_HEAD_REF/g" /opt/android/cake_wallet/android/app/src/main/AndroidManifest.xml + run: | + sed -i -e "s/\${APP_NAME}/$GITHUB_HEAD_REF/g" /opt/android/cake_wallet/android/app/src/main/AndroidManifest.xml + sed -i -e "s/\com.cakewallet.cake_wallet/com.cakewallet.cake_wallet.test/g" /opt/android/cake_wallet/android/app/src/main/AndroidManifest.xml - name: Build run: | From 247ec24e46693d3248d9a0e69d89a5aaf28020e2 Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Wed, 1 Mar 2023 15:47:16 +0200 Subject: [PATCH 44/59] Re-organize ignored errors in ascending order of errno [skip ci] --- lib/utils/exception_handler.dart | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/utils/exception_handler.dart b/lib/utils/exception_handler.dart index 8285b9c52..51a525394 100644 --- a/lib/utils/exception_handler.dart +++ b/lib/utils/exception_handler.dart @@ -124,15 +124,16 @@ class ExceptionHandler { _ignoredErrors.any((element) => error.contains(element)); static const List _ignoredErrors = const [ - "errno = 103", // SocketException: Software caused connection abort "errno = 9", // SocketException: Bad file descriptor - "errno = 32", // SocketException: Write failed (OS Error: Broken pipe) - "errno = 60", // SocketException: Operation timed out - "errno = 110", // SocketException: Connection timed out - "errno = 54", // SocketException: Connection reset by peer - "errno = 49", // SocketException: Can't assign requested address - "errno = 57", // SocketException: Read failed (OS Error: Socket is not connected) "errno = 28", // OS Error: No space left on device + "errno = 32", // SocketException: Write failed (OS Error: Broken pipe) + "errno = 49", // SocketException: Can't assign requested address + "errno = 54", // SocketException: Connection reset by peer + "errno = 57", // SocketException: Read failed (OS Error: Socket is not connected) + "errno = 60", // SocketException: Operation timed out + "errno = 103", // SocketException: Software caused connection abort + "errno = 104", // SocketException: Connection reset by peer + "errno = 110", // SocketException: Connection timed out "PERMISSION_NOT_GRANTED", ]; From dbfbadffdaef761abad0b493b27714c51fb22b01 Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Wed, 1 Mar 2023 15:50:31 +0200 Subject: [PATCH 45/59] Filter to use Tor only exchange --- lib/src/widgets/check_box_picker.dart | 16 ++++++++-------- .../exchange/exchange_view_model.dart | 19 ++++++++++++++++++- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/lib/src/widgets/check_box_picker.dart b/lib/src/widgets/check_box_picker.dart index e2f817fc4..80461e26d 100644 --- a/lib/src/widgets/check_box_picker.dart +++ b/lib/src/widgets/check_box_picker.dart @@ -38,7 +38,7 @@ class CheckBoxPickerState extends State { Column( mainAxisSize: MainAxisSize.min, children: [ - if (widget.title?.isNotEmpty ?? false) + if (widget.title.isNotEmpty) Container( padding: EdgeInsets.symmetric(horizontal: 24), child: Text( @@ -58,7 +58,7 @@ class CheckBoxPickerState extends State { child: ClipRRect( borderRadius: BorderRadius.all(Radius.circular(30)), child: Container( - color: Theme.of(context).accentTextTheme!.headline6!.color!, + color: Theme.of(context).accentTextTheme.headline6!.color!, child: ConstrainedBox( constraints: BoxConstraints( maxHeight: MediaQuery.of(context).size.height * 0.65, @@ -70,7 +70,7 @@ class CheckBoxPickerState extends State { child: Stack( alignment: Alignment.center, children: [ - (items?.length ?? 0) > 3 + (items.length) > 3 ? Scrollbar( controller: controller, child: itemsList(), @@ -95,14 +95,14 @@ class CheckBoxPickerState extends State { Widget itemsList() { return Container( - color: Theme.of(context).accentTextTheme!.headline6!.backgroundColor!, + color: Theme.of(context).accentTextTheme.headline6!.backgroundColor!, child: ListView.separated( padding: EdgeInsets.zero, controller: controller, shrinkWrap: true, separatorBuilder: (context, index) => widget.isSeparated ? Divider( - color: Theme.of(context).accentTextTheme!.headline6!.backgroundColor!, + color: Theme.of(context).accentTextTheme.headline6!.backgroundColor!, height: 1, ) : const SizedBox(), @@ -121,13 +121,13 @@ class CheckBoxPickerState extends State { }, child: Container( height: 55, - color: Theme.of(context).accentTextTheme!.headline6!.color!, + color: Theme.of(context).accentTextTheme.headline6!.color!, padding: EdgeInsets.only(left: 24, right: 24), child: CheckboxListTile( value: item.value, activeColor: item.value ? Palette.blueCraiola - : Theme.of(context).accentTextTheme!.subtitle1!.decorationColor!, + : Theme.of(context).accentTextTheme.subtitle1!.decorationColor!, checkColor: Colors.white, title: widget.displayItem?.call(item) ?? Text( @@ -138,7 +138,7 @@ class CheckBoxPickerState extends State { fontWeight: FontWeight.w600, color: item.isDisabled ? Colors.grey.withOpacity(0.5) - : Theme.of(context).primaryTextTheme!.headline6!.color!, + : Theme.of(context).primaryTextTheme.headline6!.color!, decoration: TextDecoration.none, ), ), diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index f9f5683d9..7921115c6 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:collection'; import 'dart:convert'; +import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/exchange/sideshift/sideshift_exchange_provider.dart'; import 'package:cake_wallet/exchange/sideshift/sideshift_request.dart'; @@ -62,8 +63,9 @@ abstract class ExchangeViewModelBase with Store { limitsState = LimitsInitialState(), receiveCurrency = wallet.currency, depositCurrency = wallet.currency, - providerList = [ChangeNowExchangeProvider(), SideShiftExchangeProvider(), SimpleSwapExchangeProvider(), TrocadorExchangeProvider()], + providerList = [], selectedProviders = ObservableList() { + _setProviders(); const excludeDepositCurrencies = [CryptoCurrency.btt, CryptoCurrency.nano]; const excludeReceiveCurrencies = [CryptoCurrency.xlm, CryptoCurrency.xrp, CryptoCurrency.bnb, CryptoCurrency.btt, CryptoCurrency.nano]; @@ -126,6 +128,13 @@ abstract class ExchangeViewModelBase with Store { final TradesStore tradesStore; final SharedPreferences sharedPreferences; + final _allProviders = [ + ChangeNowExchangeProvider(), + SideShiftExchangeProvider(), + SimpleSwapExchangeProvider(), + TrocadorExchangeProvider(), + ]; + @observable ExchangeProvider? provider; @@ -683,4 +692,12 @@ abstract class ExchangeViewModelBase with Store { break; } } + + void _setProviders(){ + if (_settingsStore.exchangeStatus == FiatApiMode.torOnly) { + providerList = _allProviders.where((provider) => provider.shouldUseOnionAddress).toList(); + } else { + providerList = _allProviders; + } + } } From bfaf5d11d1324e103a356cdba9e62d549505cf1e Mon Sep 17 00:00:00 2001 From: Omar Hatem Date: Wed, 1 Mar 2023 16:01:28 +0200 Subject: [PATCH 46/59] Update pr_test_build.yml --- .github/workflows/pr_test_build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 790316617..0638ac095 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -119,7 +119,7 @@ jobs: - name: Rename app run: | sed -i -e "s/\${APP_NAME}/$GITHUB_HEAD_REF/g" /opt/android/cake_wallet/android/app/src/main/AndroidManifest.xml - sed -i -e "s/\com.cakewallet.cake_wallet/com.cakewallet.cake_wallet.test/g" /opt/android/cake_wallet/android/app/src/main/AndroidManifest.xml + sed -i -e "s/com.cakewallet.cake_wallet/com.cakewallet.test/g" /opt/android/cake_wallet/android/app/src/main/AndroidManifest.xml - name: Build run: | From 1219cd2c14212850618879b83bc27b38e934ff82 Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Wed, 1 Mar 2023 16:57:55 +0200 Subject: [PATCH 47/59] Rename package along with app name in app.properties --- .github/workflows/pr_test_build.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 0638ac095..cdf537cdb 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -36,10 +36,6 @@ jobs: cd cake_wallet/scripts/android/ ./install_ndk.sh source ./app_env.sh cakewallet - APP_ANDROID_PACKAGE="com.cakewallet.test" - export APP_ANDROID_PACKAGE="com.cakewallet.test" - set APP_ANDROID_PACKAGE="com.cakewallet.test" - env APP_ANDROID_PACKAGE="com.cakewallet.test" ./app_config.sh - name: Cache Externals @@ -117,9 +113,7 @@ jobs: echo "const twitterBearerToken = '${{ secrets.TWITTER_BEARER_TOKEN }}';" >> lib/.secrets.g.dart - name: Rename app - run: | - sed -i -e "s/\${APP_NAME}/$GITHUB_HEAD_REF/g" /opt/android/cake_wallet/android/app/src/main/AndroidManifest.xml - sed -i -e "s/com.cakewallet.cake_wallet/com.cakewallet.test/g" /opt/android/cake_wallet/android/app/src/main/AndroidManifest.xml + run: echo -e "id=com.cakewallet.test\nname=$GITHUB_HEAD_REF" > /opt/android/cake_wallet/android/app.properties - name: Build run: | From 861616fda2da5006d5ed6efcb32ca45d1f82743e Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Wed, 1 Mar 2023 23:24:52 +0200 Subject: [PATCH 48/59] use Http protocol for onion connection --- lib/core/fiat_conversion_service.dart | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/lib/core/fiat_conversion_service.dart b/lib/core/fiat_conversion_service.dart index 95c1e23a4..66997675d 100644 --- a/lib/core/fiat_conversion_service.dart +++ b/lib/core/fiat_conversion_service.dart @@ -12,18 +12,23 @@ Future _fetchPrice(Map args) async { final crypto = args['crypto'] as CryptoCurrency; final fiat = args['fiat'] as FiatCurrency; final torOnly = args['torOnly'] as bool; + + final Map queryParams = { + 'interval_count': '1', + 'base': crypto.toString(), + 'quote': fiat.toString(), + }; + double price = 0.0; try { - final uri = Uri.https( - torOnly ? _fiatApiOnionAuthority : _fiatApiClearNetAuthority, - _fiatApiPath, - { - 'interval_count': '1', - 'base': crypto.toString(), - 'quote': fiat.toString(), - }, - ); + late final Uri uri; + if (torOnly) { + uri = Uri.http(_fiatApiOnionAuthority, _fiatApiPath, queryParams); + } else { + uri = Uri.https(_fiatApiClearNetAuthority, _fiatApiPath, queryParams); + } + final response = await get(uri); if (response.statusCode != 200) { From b0175719b93a4297d842b569887b5f214f779f96 Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Wed, 1 Mar 2023 23:44:15 +0200 Subject: [PATCH 49/59] Fix issues from code review --- .github/workflows/pr_test_build.yml | 8 -- lib/entities/default_settings_migration.dart | 16 +++- lib/entities/exchange_api_mode.dart | 39 +++++++++ lib/exchange/exchange_provider.dart | 2 +- .../trocador/trocador_exchange_provider.dart | 24 ++++-- .../advanced_privacy_settings_page.dart | 85 +++++++++++-------- lib/src/screens/settings/privacy_page.dart | 21 +++-- lib/store/settings_store.dart | 15 ++-- .../advanced_privacy_settings_view_model.dart | 13 ++- .../exchange/exchange_view_model.dart | 10 ++- .../settings/privacy_settings_view_model.dart | 15 ++-- 11 files changed, 165 insertions(+), 83 deletions(-) create mode 100644 lib/entities/exchange_api_mode.dart diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 60dd1bc68..1766bbe86 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -77,14 +77,6 @@ jobs: run: | cd /opt/android/cake_wallet flutter packages pub run tool/generate_localization.dart - - - name: Upgrade flutter packages - run: | - cd /opt/android/cake_wallet - cd cw_core && flutter pub upgrade && cd .. - cd cw_monero && flutter pub upgrade && cd .. - cd cw_bitcoin && flutter pub upgrade && cd .. - cd cw_haven && flutter pub upgrade && cd .. - name: Build generated code run: | diff --git a/lib/entities/default_settings_migration.dart b/lib/entities/default_settings_migration.dart index 3c8d9fbbe..b1bb144be 100644 --- a/lib/entities/default_settings_migration.dart +++ b/lib/entities/default_settings_migration.dart @@ -1,10 +1,12 @@ import 'dart:io' show File, Platform; import 'package:cake_wallet/bitcoin/bitcoin.dart'; +import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cw_core/pathForWallet.dart'; import 'package:cake_wallet/entities/secret_store_key.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:hive/hive.dart'; +import 'package:share_plus/share_plus.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cw_core/wallet_type.dart'; @@ -142,7 +144,9 @@ Future defaultSettingsMigration( case 19: await validateBitcoinSavedTransactionPriority(sharedPreferences); break; - + case 20: + await migrateExchangeStatus(sharedPreferences); + break; default: break; } @@ -501,3 +505,13 @@ Future changeDefaultHavenNode( await node.save(); }); } + +Future migrateExchangeStatus(SharedPreferences sharedPreferences) async { + final isExchangeDisabled = sharedPreferences.getBool(PreferencesKey.disableExchangeKey); + if (isExchangeDisabled == null) { + return; + } + + await sharedPreferences.setInt(PreferencesKey.exchangeStatusKey, isExchangeDisabled + ? ExchangeApiMode.disabled.raw : ExchangeApiMode.enabled.raw); +} diff --git a/lib/entities/exchange_api_mode.dart b/lib/entities/exchange_api_mode.dart new file mode 100644 index 000000000..0b8b575a5 --- /dev/null +++ b/lib/entities/exchange_api_mode.dart @@ -0,0 +1,39 @@ +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cw_core/enumerable_item.dart'; + +class ExchangeApiMode extends EnumerableItem with Serializable { + const ExchangeApiMode({required String title, required int raw}) : super(title: title, raw: raw); + + static const all = [ExchangeApiMode.enabled, ExchangeApiMode.torOnly, ExchangeApiMode.disabled]; + + static const enabled = ExchangeApiMode(raw: 0, title: 'Enabled'); + static const torOnly = ExchangeApiMode(raw: 1, title: 'Tor only'); + static const disabled = ExchangeApiMode(raw: 2, title: 'Disabled'); + + static ExchangeApiMode deserialize({required int raw}) { + switch (raw) { + case 0: + return enabled; + case 1: + return torOnly; + case 2: + return disabled; + default: + throw Exception('Unexpected token: $raw for ExchangeApiMode deserialize'); + } + } + + @override + String toString() { + switch (this) { + case ExchangeApiMode.enabled: + return S.current.enabled; + case ExchangeApiMode.torOnly: + return S.current.tor_only; + case ExchangeApiMode.disabled: + return S.current.disabled; + default: + return ''; + } + } +} \ No newline at end of file diff --git a/lib/exchange/exchange_provider.dart b/lib/exchange/exchange_provider.dart index 99e6da913..cc81a21f6 100644 --- a/lib/exchange/exchange_provider.dart +++ b/lib/exchange/exchange_provider.dart @@ -14,7 +14,7 @@ abstract class ExchangeProvider { bool get isAvailable; bool get isEnabled; bool get supportsFixedRate; - bool get shouldUseOnionAddress => false; + bool get supportsOnionAddress => false; @override String toString() => title; diff --git a/lib/exchange/trocador/trocador_exchange_provider.dart b/lib/exchange/trocador/trocador_exchange_provider.dart index 49503a1d0..750ffb98a 100644 --- a/lib/exchange/trocador/trocador_exchange_provider.dart +++ b/lib/exchange/trocador/trocador_exchange_provider.dart @@ -13,9 +13,11 @@ import 'package:cake_wallet/.secrets.g.dart' as secrets; import 'package:http/http.dart'; class TrocadorExchangeProvider extends ExchangeProvider { - TrocadorExchangeProvider() + TrocadorExchangeProvider({this.useTorOnly = false}) : _lastUsedRateId = '', super(pairList: _supportedPairs()); + + bool useTorOnly; static const List _notSupported = [ CryptoCurrency.scrt, @@ -83,7 +85,7 @@ class TrocadorExchangeProvider extends ExchangeProvider { params['id'] = _lastUsedRateId; } - final String apiAuthority = shouldUseOnionAddress ? await _getAuthority() : clearNetAuthority; + final String apiAuthority = await _getAuthority(); final uri = Uri.https(apiAuthority, createTradePath, params); final response = await get(uri); @@ -142,7 +144,7 @@ class TrocadorExchangeProvider extends ExchangeProvider { 'name': from.name, }; - final String apiAuthority = shouldUseOnionAddress ? await _getAuthority() : clearNetAuthority; + final String apiAuthority = await _getAuthority(); final uri = Uri.https(apiAuthority, coinPath, params); final response = await get(uri); @@ -178,7 +180,7 @@ class TrocadorExchangeProvider extends ExchangeProvider { return 0.0; } - final String apiAuthority = shouldUseOnionAddress ? await _getAuthority() : clearNetAuthority; + final String apiAuthority = await _getAuthority(); final params = { 'api_key': apiKey, @@ -214,7 +216,7 @@ class TrocadorExchangeProvider extends ExchangeProvider { @override Future findTradeById({required String id}) async { - final String apiAuthority = shouldUseOnionAddress ? await _getAuthority() : clearNetAuthority; + final String apiAuthority = await _getAuthority(); final uri = Uri.https(apiAuthority, tradePath, {'api_key': apiKey, 'id': id}); return get(uri).then((response) { if (response.statusCode != 200) { @@ -265,7 +267,7 @@ class TrocadorExchangeProvider extends ExchangeProvider { bool get supportsFixedRate => true; @override - bool get shouldUseOnionAddress => true; + bool get supportsOnionAddress => true; @override String get title => 'Trocador'; @@ -297,11 +299,21 @@ class TrocadorExchangeProvider extends ExchangeProvider { } Future _getAuthority() async { + if(!supportsOnionAddress){ + return clearNetAuthority; + } + try { + if (useTorOnly) { + return onionApiAuthority; + } + final uri = Uri.https(onionApiAuthority, '/api/trade'); await get(uri); + return onionApiAuthority; } catch (e) { + return clearNetAuthority; } } diff --git a/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart b/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart index 7d720e4e5..42ba0debb 100644 --- a/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart +++ b/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/src/screens/nodes/widgets/node_form.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_choices_cell.dart'; @@ -48,43 +49,55 @@ class _AdvancedPrivacySettingsBodyState extends State Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SettingsChoicesCell( - ChoicesListItem( - title: S.current.fiat_api, - items: FiatApiMode.all, - selectedItem: widget.privacySettingsViewModel.fiatApi, - onItemSelected: (FiatApiMode mode) => - widget.privacySettingsViewModel.setFiatMode(mode), - ), - ), - SettingsChoicesCell( - ChoicesListItem( - title: S.current.exchange, - items: FiatApiMode.all, - selectedItem: widget.privacySettingsViewModel.exchangeStatus, - onItemSelected: (FiatApiMode mode) => - widget.privacySettingsViewModel.setEnableExchange(mode), - ), - ), - SettingsSwitcherCell( - title: S.current.add_custom_node, - value: widget.privacySettingsViewModel.addCustomNode, - onValueChange: (_, __) => widget.privacySettingsViewModel.toggleAddCustomNode(), - ), - if (widget.privacySettingsViewModel.addCustomNode) - Padding( - padding: EdgeInsets.only(left: 24, right: 24, top: 24), - child: NodeForm( - formKey: _formKey, - nodeViewModel: widget.nodeViewModel, + content: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Observer( + builder: (_) { + return SettingsSwitcherCell( + title: S.current.disable_fiat, + value: widget.privacySettingsViewModel.fiatApi == FiatApiMode.disabled, + onValueChange: (BuildContext context, bool value) { + widget.privacySettingsViewModel.setFiatMode(value); + }); + } + ), + Observer( + builder: (_) { + return SettingsChoicesCell( + ChoicesListItem( + title: S.current.exchange, + items: ExchangeApiMode.all, + selectedItem: widget.privacySettingsViewModel.exchangeStatus, + onItemSelected: (ExchangeApiMode mode) => + widget.privacySettingsViewModel.setEnableExchange(mode), ), - ) - ], - ), + ); + } + ), + Observer( + builder: (_) { + return Column( + children: [ + SettingsSwitcherCell( + title: S.current.add_custom_node, + value: widget.privacySettingsViewModel.addCustomNode, + onValueChange: (_, __) => widget.privacySettingsViewModel.toggleAddCustomNode(), + ), + if (widget.privacySettingsViewModel.addCustomNode) + Padding( + padding: EdgeInsets.only(left: 24, right: 24, top: 24), + child: NodeForm( + formKey: _formKey, + nodeViewModel: widget.nodeViewModel, + ), + ) + ], + ); + } + ), + + ], ), bottomSectionPadding: EdgeInsets.all(24), bottomSection: Column( diff --git a/lib/src/screens/settings/privacy_page.dart b/lib/src/screens/settings/privacy_page.dart index 974f9a3d7..cc6acc826 100644 --- a/lib/src/screens/settings/privacy_page.dart +++ b/lib/src/screens/settings/privacy_page.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; @@ -24,20 +25,18 @@ class PrivacyPage extends BasePage { return Column( mainAxisSize: MainAxisSize.min, children: [ + SettingsSwitcherCell( + title: S.current.disable_fiat, + value: _privacySettingsViewModel.isFiatDisabled, + onValueChange: (BuildContext context, bool value) { + _privacySettingsViewModel.setFiatMode(value); + }), SettingsChoicesCell( - ChoicesListItem( - title: S.current.fiat_api, - items: FiatApiMode.all, - selectedItem: _privacySettingsViewModel.fiatApi, - onItemSelected: (FiatApiMode mode) => _privacySettingsViewModel.setFiatMode(mode), - ), - ), - SettingsChoicesCell( - ChoicesListItem( + ChoicesListItem( title: S.current.exchange, - items: FiatApiMode.all, + items: ExchangeApiMode.all, selectedItem: _privacySettingsViewModel.exchangeStatus, - onItemSelected: (FiatApiMode mode) => _privacySettingsViewModel.setEnableExchange(mode), + onItemSelected: (ExchangeApiMode mode) => _privacySettingsViewModel.setEnableExchange(mode), ), ), SettingsSwitcherCell( diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index 989fdae33..b6e5a7549 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -1,4 +1,5 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart'; +import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/pin_code_required_duration.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cw_core/transaction_priority.dart'; @@ -31,7 +32,7 @@ abstract class SettingsStoreBase with Store { required bool initialSaveRecipientAddress, required FiatApiMode initialFiatMode, required bool initialAllowBiometricalAuthentication, - required FiatApiMode initialExchangeStatus, + required ExchangeApiMode initialExchangeStatus, required ThemeBase initialTheme, required int initialPinLength, required String initialLanguageCode, @@ -154,7 +155,7 @@ abstract class SettingsStoreBase with Store { reaction( (_) => exchangeStatus, - (FiatApiMode mode) => sharedPreferences.setInt( + (ExchangeApiMode mode) => sharedPreferences.setInt( PreferencesKey.exchangeStatusKey, mode.serialize())); this @@ -192,7 +193,7 @@ abstract class SettingsStoreBase with Store { bool allowBiometricalAuthentication; @observable - FiatApiMode exchangeStatus; + ExchangeApiMode exchangeStatus; @observable ThemeBase currentTheme; @@ -284,9 +285,9 @@ abstract class SettingsStoreBase with Store { final allowBiometricalAuthentication = sharedPreferences .getBool(PreferencesKey.allowBiometricalAuthenticationKey) ?? false; - final exchangeStatus = FiatApiMode.deserialize( + final exchangeStatus = ExchangeApiMode.deserialize( raw: sharedPreferences - .getInt(PreferencesKey.exchangeStatusKey) ?? FiatApiMode.enabled.raw); + .getInt(PreferencesKey.exchangeStatusKey) ?? ExchangeApiMode.enabled.raw); final legacyTheme = (sharedPreferences.getBool(PreferencesKey.isDarkThemeLegacy) ?? false) ? ThemeType.dark.index @@ -401,9 +402,9 @@ abstract class SettingsStoreBase with Store { allowBiometricalAuthentication = sharedPreferences .getBool(PreferencesKey.allowBiometricalAuthenticationKey) ?? allowBiometricalAuthentication; - exchangeStatus = FiatApiMode.deserialize( + exchangeStatus = ExchangeApiMode.deserialize( raw: sharedPreferences - .getInt(PreferencesKey.exchangeStatusKey) ?? FiatApiMode.enabled.raw); + .getInt(PreferencesKey.exchangeStatusKey) ?? ExchangeApiMode.enabled.raw); final legacyTheme = (sharedPreferences.getBool(PreferencesKey.isDarkThemeLegacy) ?? false) ? ThemeType.dark.index diff --git a/lib/view_model/advanced_privacy_settings_view_model.dart b/lib/view_model/advanced_privacy_settings_view_model.dart index 7d526047d..1bc2ecd9f 100644 --- a/lib/view_model/advanced_privacy_settings_view_model.dart +++ b/lib/view_model/advanced_privacy_settings_view_model.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/store/settings_store.dart'; import 'package:cw_core/wallet_type.dart'; @@ -12,7 +13,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store { AdvancedPrivacySettingsViewModelBase(this.type, this._settingsStore) : _addCustomNode = false; @computed - FiatApiMode get exchangeStatus => _settingsStore.exchangeStatus; + ExchangeApiMode get exchangeStatus => _settingsStore.exchangeStatus; @computed FiatApiMode get fiatApi => _settingsStore.fiatApiMode; @@ -28,12 +29,16 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store { bool get addCustomNode => _addCustomNode; @action - void setFiatMode(FiatApiMode value) { - _settingsStore.fiatApiMode = value; + void setFiatMode(bool value) { + if (value) { + _settingsStore.fiatApiMode = FiatApiMode.disabled; + return; + } + _settingsStore.fiatApiMode = FiatApiMode.enabled; } @action - void setEnableExchange(FiatApiMode value) { + void setEnableExchange(ExchangeApiMode value) { _settingsStore.exchangeStatus = value; } diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index 7921115c6..20a5a8e76 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -56,6 +56,7 @@ abstract class ExchangeViewModelBase with Store { isDepositAddressEnabled = false, isReceiveAddressEnabled = false, isReceiveAmountEditable = false, + _providerUseTorOnly = false, receiveCurrencies = [], depositCurrencies = [], limits = Limits(min: 0, max: 0), @@ -65,6 +66,7 @@ abstract class ExchangeViewModelBase with Store { depositCurrency = wallet.currency, providerList = [], selectedProviders = ObservableList() { + _providerUseTorOnly = _settingsStore.exchangeStatus == FiatApiMode.torOnly; _setProviders(); const excludeDepositCurrencies = [CryptoCurrency.btt, CryptoCurrency.nano]; const excludeReceiveCurrencies = [CryptoCurrency.xlm, CryptoCurrency.xrp, @@ -121,18 +123,18 @@ abstract class ExchangeViewModelBase with Store { _calculateBestRate(); }); } - + bool _providerUseTorOnly; final WalletBase wallet; final Box trades; final ExchangeTemplateStore _exchangeTemplateStore; final TradesStore tradesStore; final SharedPreferences sharedPreferences; - final _allProviders = [ + List get _allProviders => [ ChangeNowExchangeProvider(), SideShiftExchangeProvider(), SimpleSwapExchangeProvider(), - TrocadorExchangeProvider(), + TrocadorExchangeProvider(useTorOnly: _providerUseTorOnly), ]; @observable @@ -695,7 +697,7 @@ abstract class ExchangeViewModelBase with Store { void _setProviders(){ if (_settingsStore.exchangeStatus == FiatApiMode.torOnly) { - providerList = _allProviders.where((provider) => provider.shouldUseOnionAddress).toList(); + providerList = _allProviders.where((provider) => provider.supportsOnionAddress).toList(); } else { providerList = _allProviders; } diff --git a/lib/view_model/settings/privacy_settings_view_model.dart b/lib/view_model/settings/privacy_settings_view_model.dart index db345d0f2..1919b1944 100644 --- a/lib/view_model/settings/privacy_settings_view_model.dart +++ b/lib/view_model/settings/privacy_settings_view_model.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/store/settings_store.dart'; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/entities/fiat_api_mode.dart'; @@ -12,23 +13,27 @@ abstract class PrivacySettingsViewModelBase with Store { final SettingsStore _settingsStore; @computed - FiatApiMode get exchangeStatus => _settingsStore.exchangeStatus; + ExchangeApiMode get exchangeStatus => _settingsStore.exchangeStatus; @computed bool get shouldSaveRecipientAddress => _settingsStore.shouldSaveRecipientAddress; @computed - FiatApiMode get fiatApi => _settingsStore.fiatApiMode; + bool get isFiatDisabled => _settingsStore.fiatApiMode == FiatApiMode.disabled; @action void setShouldSaveRecipientAddress(bool value) => _settingsStore.shouldSaveRecipientAddress = value; @action - void setEnableExchange(FiatApiMode value) => _settingsStore.exchangeStatus = value; + void setEnableExchange(ExchangeApiMode value) => _settingsStore.exchangeStatus = value; @action - void setFiatMode(FiatApiMode value) { - _settingsStore.fiatApiMode = value; + void setFiatMode(bool value) { + if (value) { + _settingsStore.fiatApiMode = FiatApiMode.disabled; + return; + } + _settingsStore.fiatApiMode = FiatApiMode.enabled; } } From 9f952cda4076eb28094d368d8ccf4cb3276563ac Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Wed, 1 Mar 2023 23:46:05 +0200 Subject: [PATCH 50/59] Remove disableExchangeKey from sharedpreference --- lib/entities/default_settings_migration.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/entities/default_settings_migration.dart b/lib/entities/default_settings_migration.dart index b1bb144be..94fc7ede8 100644 --- a/lib/entities/default_settings_migration.dart +++ b/lib/entities/default_settings_migration.dart @@ -514,4 +514,6 @@ Future migrateExchangeStatus(SharedPreferences sharedPreferences) async { await sharedPreferences.setInt(PreferencesKey.exchangeStatusKey, isExchangeDisabled ? ExchangeApiMode.disabled.raw : ExchangeApiMode.enabled.raw); + + await sharedPreferences.remove(PreferencesKey.disableExchangeKey); } From a6b03c4a8189b5b3ab75cafbd3493ab8f4a8647f Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Wed, 1 Mar 2023 23:51:44 +0200 Subject: [PATCH 51/59] Add file to dependecies --- pubspec_base.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/pubspec_base.yaml b/pubspec_base.yaml index 2caa9052f..7ff933520 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -56,6 +56,7 @@ dependencies: archive: ^3.3.0 cryptography: ^2.0.5 file_picker: ^4.6.1 + file: ^6.1.4 unorm_dart: ^0.2.0 # check unorm_dart for usage and for replace permission_handler: ^10.0.0 From d4b5f24d5e88422e6e1486e78e3599fb19ed8c0c Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Thu, 2 Mar 2023 00:00:47 +0200 Subject: [PATCH 52/59] Add file to dependecies to core --- cw_core/pubspec.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/cw_core/pubspec.yaml b/cw_core/pubspec.yaml index 50503361c..e33aeb803 100644 --- a/cw_core/pubspec.yaml +++ b/cw_core/pubspec.yaml @@ -13,6 +13,7 @@ dependencies: flutter: sdk: flutter http: ^0.13.4 + file: ^6.1.4 path_provider: ^2.0.11 mobx: ^2.0.7+4 flutter_mobx: ^2.0.6+1 From 8f85f99a98f937cdc785bb8f6a48860ee67ab109 Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Thu, 2 Mar 2023 16:58:04 +0200 Subject: [PATCH 53/59] Fixate MobX version to avoid un-expected behavior --- pubspec_base.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec_base.yaml b/pubspec_base.yaml index 2caa9052f..1e6a27cb7 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -22,7 +22,7 @@ dependencies: barcode_scan2: ^4.2.1 http: ^0.13.4 path_provider: ^2.0.11 - mobx: ^2.0.7+4 + mobx: 2.0.7+4 flutter_mobx: ^2.0.6+1 flutter_slidable: ^2.0.0 share_plus: ^4.0.10 From 15237d5f799953c7c317325869603d6ce1c328ac Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Thu, 2 Mar 2023 17:13:25 +0200 Subject: [PATCH 54/59] Fix issues from code review --- lib/core/backup_service.dart | 10 +++++----- lib/exchange/trocador/trocador_exchange_provider.dart | 2 +- .../new_wallet/advanced_privacy_settings_page.dart | 2 +- lib/src/screens/settings/privacy_page.dart | 2 +- .../advanced_privacy_settings_view_model.dart | 2 +- lib/view_model/dashboard/dashboard_view_model.dart | 3 ++- lib/view_model/exchange/exchange_view_model.dart | 11 ++++++----- .../settings/privacy_settings_view_model.dart | 2 +- pubspec_base.yaml | 1 - 9 files changed, 18 insertions(+), 17 deletions(-) diff --git a/lib/core/backup_service.dart b/lib/core/backup_service.dart index ffcb9eb4c..0439e9fb4 100644 --- a/lib/core/backup_service.dart +++ b/lib/core/backup_service.dart @@ -217,7 +217,7 @@ class BackupService { final fiatApiMode = data[PreferencesKey.currentFiatApiModeKey] as int?; final currentPinLength = data[PreferencesKey.currentPinLength] as int?; final currentTheme = data[PreferencesKey.currentTheme] as int?; - final disableExchange = data[PreferencesKey.disableExchangeKey] as bool?; + final exchangeStatus = data[PreferencesKey.exchangeStatusKey] as bool?; final currentDefaultSettingsMigrationVersion = data[PreferencesKey.currentDefaultSettingsMigrationVersion] as int?; final moneroTransactionPriority = data[PreferencesKey.moneroTransactionPriority] as int?; final bitcoinTransactionPriority = data[PreferencesKey.bitcoinTransactionPriority] as int?; @@ -280,9 +280,9 @@ class BackupService { await _sharedPreferences.setInt( PreferencesKey.currentTheme, currentTheme); - if (disableExchange != null) + if (exchangeStatus != null) await _sharedPreferences.setBool( - PreferencesKey.disableExchangeKey, disableExchange); + PreferencesKey.exchangeStatusKey, exchangeStatus); if (currentDefaultSettingsMigrationVersion != null) await _sharedPreferences.setInt( @@ -431,8 +431,8 @@ class BackupService { _sharedPreferences.getInt(PreferencesKey.displayActionListModeKey), PreferencesKey.currentTheme: _sharedPreferences.getInt(PreferencesKey.currentTheme), - PreferencesKey.disableExchangeKey: - _sharedPreferences.getBool(PreferencesKey.disableExchangeKey), + PreferencesKey.exchangeStatusKey: + _sharedPreferences.getBool(PreferencesKey.exchangeStatusKey), PreferencesKey.currentDefaultSettingsMigrationVersion: _sharedPreferences .getInt(PreferencesKey.currentDefaultSettingsMigrationVersion), PreferencesKey.bitcoinTransactionPriority: diff --git a/lib/exchange/trocador/trocador_exchange_provider.dart b/lib/exchange/trocador/trocador_exchange_provider.dart index 750ffb98a..e289b4e0b 100644 --- a/lib/exchange/trocador/trocador_exchange_provider.dart +++ b/lib/exchange/trocador/trocador_exchange_provider.dart @@ -308,7 +308,7 @@ class TrocadorExchangeProvider extends ExchangeProvider { return onionApiAuthority; } - final uri = Uri.https(onionApiAuthority, '/api/trade'); + final uri = Uri.https(onionApiAuthority, tradePath); await get(uri); return onionApiAuthority; diff --git a/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart b/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart index 42ba0debb..a82ddaf4e 100644 --- a/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart +++ b/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart @@ -70,7 +70,7 @@ class _AdvancedPrivacySettingsBodyState extends State - widget.privacySettingsViewModel.setEnableExchange(mode), + widget.privacySettingsViewModel.setExchangeApiMode(mode), ), ); } diff --git a/lib/src/screens/settings/privacy_page.dart b/lib/src/screens/settings/privacy_page.dart index cc6acc826..5322c488f 100644 --- a/lib/src/screens/settings/privacy_page.dart +++ b/lib/src/screens/settings/privacy_page.dart @@ -36,7 +36,7 @@ class PrivacyPage extends BasePage { title: S.current.exchange, items: ExchangeApiMode.all, selectedItem: _privacySettingsViewModel.exchangeStatus, - onItemSelected: (ExchangeApiMode mode) => _privacySettingsViewModel.setEnableExchange(mode), + onItemSelected: (ExchangeApiMode mode) => _privacySettingsViewModel.setExchangeApiMode(mode), ), ), SettingsSwitcherCell( diff --git a/lib/view_model/advanced_privacy_settings_view_model.dart b/lib/view_model/advanced_privacy_settings_view_model.dart index 1bc2ecd9f..fad5fff34 100644 --- a/lib/view_model/advanced_privacy_settings_view_model.dart +++ b/lib/view_model/advanced_privacy_settings_view_model.dart @@ -38,7 +38,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store { } @action - void setEnableExchange(ExchangeApiMode value) { + void setExchangeApiMode(ExchangeApiMode value) { _settingsStore.exchangeStatus = value; } diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index dab2bafd1..4bc6e577d 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/wallet_type_utils.dart'; import 'package:cw_core/transaction_history.dart'; @@ -274,7 +275,7 @@ abstract class DashboardViewModelBase with Store { settingsStore.shouldShowYatPopup = shouldShow; @computed - bool get isEnabledExchangeAction => settingsStore.exchangeStatus != FiatApiMode.disabled; + bool get isEnabledExchangeAction => settingsStore.exchangeStatus != ExchangeApiMode.disabled; @observable bool hasExchangeAction; diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index 20a5a8e76..d0698990c 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:collection'; import 'dart:convert'; +import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/exchange/sideshift/sideshift_exchange_provider.dart'; @@ -56,7 +57,7 @@ abstract class ExchangeViewModelBase with Store { isDepositAddressEnabled = false, isReceiveAddressEnabled = false, isReceiveAmountEditable = false, - _providerUseTorOnly = false, + _useTorOnly = false, receiveCurrencies = [], depositCurrencies = [], limits = Limits(min: 0, max: 0), @@ -66,7 +67,7 @@ abstract class ExchangeViewModelBase with Store { depositCurrency = wallet.currency, providerList = [], selectedProviders = ObservableList() { - _providerUseTorOnly = _settingsStore.exchangeStatus == FiatApiMode.torOnly; + _useTorOnly = _settingsStore.exchangeStatus == ExchangeApiMode.torOnly; _setProviders(); const excludeDepositCurrencies = [CryptoCurrency.btt, CryptoCurrency.nano]; const excludeReceiveCurrencies = [CryptoCurrency.xlm, CryptoCurrency.xrp, @@ -123,7 +124,7 @@ abstract class ExchangeViewModelBase with Store { _calculateBestRate(); }); } - bool _providerUseTorOnly; + bool _useTorOnly; final WalletBase wallet; final Box trades; final ExchangeTemplateStore _exchangeTemplateStore; @@ -134,7 +135,7 @@ abstract class ExchangeViewModelBase with Store { ChangeNowExchangeProvider(), SideShiftExchangeProvider(), SimpleSwapExchangeProvider(), - TrocadorExchangeProvider(useTorOnly: _providerUseTorOnly), + TrocadorExchangeProvider(useTorOnly: _useTorOnly), ]; @observable @@ -696,7 +697,7 @@ abstract class ExchangeViewModelBase with Store { } void _setProviders(){ - if (_settingsStore.exchangeStatus == FiatApiMode.torOnly) { + if (_settingsStore.exchangeStatus == ExchangeApiMode.torOnly) { providerList = _allProviders.where((provider) => provider.supportsOnionAddress).toList(); } else { providerList = _allProviders; diff --git a/lib/view_model/settings/privacy_settings_view_model.dart b/lib/view_model/settings/privacy_settings_view_model.dart index 1919b1944..1d58fc323 100644 --- a/lib/view_model/settings/privacy_settings_view_model.dart +++ b/lib/view_model/settings/privacy_settings_view_model.dart @@ -25,7 +25,7 @@ abstract class PrivacySettingsViewModelBase with Store { void setShouldSaveRecipientAddress(bool value) => _settingsStore.shouldSaveRecipientAddress = value; @action - void setEnableExchange(ExchangeApiMode value) => _settingsStore.exchangeStatus = value; + void setExchangeApiMode(ExchangeApiMode value) => _settingsStore.exchangeStatus = value; @action void setFiatMode(bool value) { diff --git a/pubspec_base.yaml b/pubspec_base.yaml index 7ff933520..2caa9052f 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -56,7 +56,6 @@ dependencies: archive: ^3.3.0 cryptography: ^2.0.5 file_picker: ^4.6.1 - file: ^6.1.4 unorm_dart: ^0.2.0 # check unorm_dart for usage and for replace permission_handler: ^10.0.0 From d5c4bd0236032878f138ffbd8ad3625dfacf112d Mon Sep 17 00:00:00 2001 From: Godwin Asuquo Date: Thu, 2 Mar 2023 17:43:42 +0200 Subject: [PATCH 55/59] change type to int --- lib/core/backup_service.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/backup_service.dart b/lib/core/backup_service.dart index 0439e9fb4..3cb434efe 100644 --- a/lib/core/backup_service.dart +++ b/lib/core/backup_service.dart @@ -217,7 +217,7 @@ class BackupService { final fiatApiMode = data[PreferencesKey.currentFiatApiModeKey] as int?; final currentPinLength = data[PreferencesKey.currentPinLength] as int?; final currentTheme = data[PreferencesKey.currentTheme] as int?; - final exchangeStatus = data[PreferencesKey.exchangeStatusKey] as bool?; + final exchangeStatus = data[PreferencesKey.exchangeStatusKey] as int?; final currentDefaultSettingsMigrationVersion = data[PreferencesKey.currentDefaultSettingsMigrationVersion] as int?; final moneroTransactionPriority = data[PreferencesKey.moneroTransactionPriority] as int?; final bitcoinTransactionPriority = data[PreferencesKey.bitcoinTransactionPriority] as int?; @@ -281,7 +281,7 @@ class BackupService { PreferencesKey.currentTheme, currentTheme); if (exchangeStatus != null) - await _sharedPreferences.setBool( + await _sharedPreferences.setInt( PreferencesKey.exchangeStatusKey, exchangeStatus); if (currentDefaultSettingsMigrationVersion != null) @@ -432,7 +432,7 @@ class BackupService { PreferencesKey.currentTheme: _sharedPreferences.getInt(PreferencesKey.currentTheme), PreferencesKey.exchangeStatusKey: - _sharedPreferences.getBool(PreferencesKey.exchangeStatusKey), + _sharedPreferences.getInt(PreferencesKey.exchangeStatusKey), PreferencesKey.currentDefaultSettingsMigrationVersion: _sharedPreferences .getInt(PreferencesKey.currentDefaultSettingsMigrationVersion), PreferencesKey.bitcoinTransactionPriority: From ddbf03a2f86d6c928e783d7d2ed43eff5be054dc Mon Sep 17 00:00:00 2001 From: Omar Hatem Date: Thu, 2 Mar 2023 18:10:40 +0200 Subject: [PATCH 56/59] Update pull_request_template.md --- .github/pull_request_template.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index d7c1b7241..4eb4ffac5 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -7,3 +7,7 @@ Please include a summary of the changes and which issue is fixed / feature is ad # Pull Request - Checklist - [ ] Initial Manual Tests Passed +- [ ] Double check modified code and verify it with the feature/task requirements +- [ ] Formate code +- [ ] Look for code duplication +- [ ] Clear naming for variables and methods From de629873386403c29b2d5f64462193c5ac81702e Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Thu, 2 Mar 2023 19:13:00 +0200 Subject: [PATCH 57/59] Add Fiat API mode to advanced privacy settings page --- .../advanced_privacy_settings_page.dart | 68 +++++++++---------- .../advanced_privacy_settings_view_model.dart | 18 ++--- 2 files changed, 36 insertions(+), 50 deletions(-) diff --git a/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart b/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart index a82ddaf4e..05ff65889 100644 --- a/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart +++ b/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart @@ -52,39 +52,37 @@ class _AdvancedPrivacySettingsBodyState extends State( title: S.current.disable_fiat, - value: widget.privacySettingsViewModel.fiatApi == FiatApiMode.disabled, - onValueChange: (BuildContext context, bool value) { - widget.privacySettingsViewModel.setFiatMode(value); - }); - } - ), - Observer( - builder: (_) { - return SettingsChoicesCell( - ChoicesListItem( - title: S.current.exchange, - items: ExchangeApiMode.all, - selectedItem: widget.privacySettingsViewModel.exchangeStatus, - onItemSelected: (ExchangeApiMode mode) => - widget.privacySettingsViewModel.setExchangeApiMode(mode), + items: FiatApiMode.all, + selectedItem: widget.privacySettingsViewModel.fiatApiMode, + onItemSelected: (FiatApiMode mode) => + widget.privacySettingsViewModel.setFiatApiMode(mode), + ), + ); + }), + Observer(builder: (_) { + return SettingsChoicesCell( + ChoicesListItem( + title: S.current.exchange, + items: ExchangeApiMode.all, + selectedItem: widget.privacySettingsViewModel.exchangeStatus, + onItemSelected: (ExchangeApiMode mode) => + widget.privacySettingsViewModel.setExchangeApiMode(mode), + ), + ); + }), + Observer(builder: (_) { + return Column( + children: [ + SettingsSwitcherCell( + title: S.current.add_custom_node, + value: widget.privacySettingsViewModel.addCustomNode, + onValueChange: (_, __) => widget.privacySettingsViewModel.toggleAddCustomNode(), ), - ); - } - ), - Observer( - builder: (_) { - return Column( - children: [ - SettingsSwitcherCell( - title: S.current.add_custom_node, - value: widget.privacySettingsViewModel.addCustomNode, - onValueChange: (_, __) => widget.privacySettingsViewModel.toggleAddCustomNode(), - ), - if (widget.privacySettingsViewModel.addCustomNode) + if (widget.privacySettingsViewModel.addCustomNode) Padding( padding: EdgeInsets.only(left: 24, right: 24, top: 24), child: NodeForm( @@ -92,11 +90,9 @@ class _AdvancedPrivacySettingsBodyState extends State _settingsStore.exchangeStatus; @computed - FiatApiMode get fiatApi => _settingsStore.fiatApiMode; + FiatApiMode get fiatApiMode => _settingsStore.fiatApiMode; @observable bool _addCustomNode = false; @@ -29,21 +29,11 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store { bool get addCustomNode => _addCustomNode; @action - void setFiatMode(bool value) { - if (value) { - _settingsStore.fiatApiMode = FiatApiMode.disabled; - return; - } - _settingsStore.fiatApiMode = FiatApiMode.enabled; - } + void setFiatApiMode(FiatApiMode fiatApiMode) => _settingsStore.fiatApiMode = fiatApiMode; @action - void setExchangeApiMode(ExchangeApiMode value) { - _settingsStore.exchangeStatus = value; - } + void setExchangeApiMode(ExchangeApiMode value) => _settingsStore.exchangeStatus = value; @action - void toggleAddCustomNode() { - _addCustomNode = !_addCustomNode; - } + void toggleAddCustomNode() => _addCustomNode = !_addCustomNode; } From 29e9bb2181ef60e419d67938f62afa8e5d39ca9c Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Thu, 2 Mar 2023 19:24:52 +0200 Subject: [PATCH 58/59] Call Onion API from http --- .../trocador/trocador_exchange_provider.dart | 79 ++++++++----------- 1 file changed, 35 insertions(+), 44 deletions(-) diff --git a/lib/exchange/trocador/trocador_exchange_provider.dart b/lib/exchange/trocador/trocador_exchange_provider.dart index e289b4e0b..1c2a85163 100644 --- a/lib/exchange/trocador/trocador_exchange_provider.dart +++ b/lib/exchange/trocador/trocador_exchange_provider.dart @@ -16,7 +16,7 @@ class TrocadorExchangeProvider extends ExchangeProvider { TrocadorExchangeProvider({this.useTorOnly = false}) : _lastUsedRateId = '', super(pairList: _supportedPairs()); - + bool useTorOnly; static const List _notSupported = [ @@ -85,9 +85,7 @@ class TrocadorExchangeProvider extends ExchangeProvider { params['id'] = _lastUsedRateId; } - final String apiAuthority = await _getAuthority(); - - final uri = Uri.https(apiAuthority, createTradePath, params); + final uri = await _getUri(createTradePath, params); final response = await get(uri); if (response.statusCode == 400) { @@ -137,31 +135,28 @@ class TrocadorExchangeProvider extends ExchangeProvider { {required CryptoCurrency from, required CryptoCurrency to, required bool isFixedRateMode}) async { - - final params = { - 'api_key': apiKey, - 'ticker': from.title.toLowerCase(), - 'name': from.name, - }; - - final String apiAuthority = await _getAuthority(); - final uri = Uri.https(apiAuthority, coinPath, params); - - final response = await get(uri); + final params = { + 'api_key': apiKey, + 'ticker': from.title.toLowerCase(), + 'name': from.name, + }; - if (response.statusCode != 200) { - throw Exception('Unexpected http status: ${response.statusCode}'); - } + final uri = await _getUri(coinPath, params); + + final response = await get(uri); + + if (response.statusCode != 200) { + throw Exception('Unexpected http status: ${response.statusCode}'); + } + + final responseJSON = json.decode(response.body) as List; + + if (responseJSON.isEmpty) { + throw Exception('No data'); + } + + final coinJson = responseJSON.first as Map; - final responseJSON = json.decode(response.body) as List; - - if (responseJSON.isEmpty) { - throw Exception('No data'); - } - - final coinJson = responseJSON.first as Map; - - return Limits( min: coinJson['minimum'] as double, max: coinJson['maximum'] as double, @@ -180,8 +175,6 @@ class TrocadorExchangeProvider extends ExchangeProvider { return 0.0; } - final String apiAuthority = await _getAuthority(); - final params = { 'api_key': apiKey, 'ticker_from': from.title.toLowerCase(), @@ -196,7 +189,7 @@ class TrocadorExchangeProvider extends ExchangeProvider { 'best_only': 'True', }; - final uri = Uri.https(apiAuthority, newRatePath, params); + final uri = await _getUri(newRatePath, params); final response = await get(uri); final responseJSON = json.decode(response.body) as Map; final fromAmount = double.parse(responseJSON['amount_from'].toString()); @@ -216,8 +209,7 @@ class TrocadorExchangeProvider extends ExchangeProvider { @override Future findTradeById({required String id}) async { - final String apiAuthority = await _getAuthority(); - final uri = Uri.https(apiAuthority, tradePath, {'api_key': apiKey, 'id': id}); + final uri = await _getUri(tradePath, {'api_key': apiKey, 'id': id}); return get(uri).then((response) { if (response.statusCode != 200) { throw Exception('Unexpected http status: ${response.statusCode}'); @@ -298,23 +290,22 @@ class TrocadorExchangeProvider extends ExchangeProvider { } } - Future _getAuthority() async { - if(!supportsOnionAddress){ - return clearNetAuthority; - } + Future _getUri(String createTradePath, Map queryParams) async { + if (!supportsOnionAddress) { + return Uri.https(clearNetAuthority, tradePath, queryParams); + } + + if (useTorOnly) { + return Uri.http(onionApiAuthority, tradePath, queryParams); + } try { - if (useTorOnly) { - return onionApiAuthority; - } - - final uri = Uri.https(onionApiAuthority, tradePath); + final uri = Uri.http(onionApiAuthority, tradePath); await get(uri); - return onionApiAuthority; + return Uri.http(onionApiAuthority, tradePath, queryParams); } catch (e) { - - return clearNetAuthority; + return Uri.https(clearNetAuthority, tradePath, queryParams); } } } From 60f47e5e9fed8d83252014a4814eca045b6fd0b5 Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Thu, 2 Mar 2023 21:13:19 +0200 Subject: [PATCH 59/59] Fix calling wrong path --- .../trocador/trocador_exchange_provider.dart | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/exchange/trocador/trocador_exchange_provider.dart b/lib/exchange/trocador/trocador_exchange_provider.dart index 1c2a85163..98726a265 100644 --- a/lib/exchange/trocador/trocador_exchange_provider.dart +++ b/lib/exchange/trocador/trocador_exchange_provider.dart @@ -290,22 +290,23 @@ class TrocadorExchangeProvider extends ExchangeProvider { } } - Future _getUri(String createTradePath, Map queryParams) async { + Future _getUri(String path, Map queryParams) async { if (!supportsOnionAddress) { - return Uri.https(clearNetAuthority, tradePath, queryParams); + return Uri.https(clearNetAuthority, path, queryParams); } + final uri = Uri.http(onionApiAuthority, path, queryParams); + if (useTorOnly) { - return Uri.http(onionApiAuthority, tradePath, queryParams); + return uri; } try { - final uri = Uri.http(onionApiAuthority, tradePath); await get(uri); - return Uri.http(onionApiAuthority, tradePath, queryParams); + return uri; } catch (e) { - return Uri.https(clearNetAuthority, tradePath, queryParams); + return Uri.https(clearNetAuthority, path, queryParams); } } }