diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index fc3be13a..c68d2a11 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -13,6 +13,7 @@ android:label="Oxen Wallet" android:icon="@mipmap/ic_launcher" android:extractNativeLibs="true" + android:usesCleartextTraffic="true" tools:replace="android:extractNativeLibs"> "${status} verbleibende Blöcke"; - static m1(node) => "Möchten Sie den aktuellen Knoten wirklich auf ändern? ${node}?"; + static m1(serviceNodeKey) => "Möchtest du wirkklich dein Stake von ${serviceNodeKey} entsperren?"; - static m2(language) => "Ändern Sie die Sprache zu ${language}?"; + static m2(node) => "Möchten Sie den aktuellen Knoten wirklich auf ändern? ${node}?"; - static m3(amount, fee) => "Transaktion festschreiben\nMenge: ${amount}\nGebühr: ${fee}"; + static m3(language) => "Ändern Sie die Sprache zu ${language}?"; - static m4(key) => "Kopiert ${key} in die Zwischenablage"; + static m4(amount, fee) => "Transaktion festschreiben\nMenge: ${amount}\nGebühr: ${fee}"; - static m5(item, app_store) => "Geben sie NIEMALS ihren Oxen wallet ${item} in einer andere software oder website außer den OFFIZIELLEN Oxen wallets aus dem ${app_store}, der Oxen website, der dem Oxen GitHub.\nMöchtest du wirklich fortfahren?"; + static m5(key) => "Kopiert ${key} in die Zwischenablage"; - static m6(state_error) => "Authentifizierung fehlgeschlagen. ${state_error}"; + static m6(item, app_store) => "Geben sie NIEMALS ihren Oxen wallet ${item} in einer andere software oder website außer den OFFIZIELLEN Oxen wallets aus dem ${app_store}, der Oxen website, der dem Oxen GitHub.\nMöchtest du wirklich fortfahren?"; - static m7(item) => "Geben sie NIEMALS ihren Oxen wallet ${item} weiter!"; + static m7(state_error) => "Authentifizierung fehlgeschlagen. ${state_error}"; - static m8(recipient_name) => "Sie senden Geld an\n${recipient_name}"; + static m8(item) => "Geben sie NIEMALS ihren Oxen wallet ${item} weiter!"; - static m9(name) => "Keine Route definiert für ${name}"; + static m9(recipient_name) => "Sie senden Geld an\n${recipient_name}"; - static m10(transactionPriority) => "Derzeit ist die Priorität auf ${transactionPriority} festgelegt.\nDie Transaktionspriorität kann in den Einstellungen angepasst werden"; + static m10(name) => "Keine Route definiert für ${name}"; - static m11(title) => "${title} in die Zwischenablage kopiert"; + static m11(transactionPriority) => "Derzeit ist die Priorität auf ${transactionPriority} festgelegt.\nDie Transaktionspriorität kann in den Einstellungen angepasst werden"; - static m12(currentVersion) => "Ausführung ${currentVersion}"; + static m12(title) => "${title} in die Zwischenablage kopiert"; - static m13(wallet_name, error) => "Laden fehlgeschlagen ${wallet_name} Wallet. ${error}"; + static m13(currentVersion) => "Ausführung ${currentVersion}"; - static m14(wallet_name, error) => "Fehler beim Entfernen ${wallet_name} Wallet. ${error}"; + static m14(wallet_name, error) => "Laden fehlgeschlagen ${wallet_name} Wallet. ${error}"; - static m15(wallet_name) => "Wallet ${wallet_name} wird geladen"; + static m15(wallet_name, error) => "Fehler beim Entfernen ${wallet_name} Wallet. ${error}"; - static m16(wallet_name) => "Wallet ${wallet_name} entfernen"; + static m16(wallet_name) => "Wallet ${wallet_name} wird geladen"; + + static m17(wallet_name) => "Wallet ${wallet_name} entfernen"; final messages = _notInlinedMessages(_notInlinedMessages); static _notInlinedMessages(_) => { @@ -76,25 +78,26 @@ class MessageLookup extends MessageLookupByLibrary { "authentication" : MessageLookupByLibrary.simpleMessage("Authentifizierung"), "available_balance" : MessageLookupByLibrary.simpleMessage("Verfügbares Guthaben"), "biometric_auth_reason" : MessageLookupByLibrary.simpleMessage("Scannen Sie Ihren Fingerabdruck zur Authentifizierung"), + "body_confirm_unlock_stake" : m1, "cancel" : MessageLookupByLibrary.simpleMessage("Abbrechen"), "change" : MessageLookupByLibrary.simpleMessage("Veränderung"), - "change_current_node" : m1, + "change_current_node" : m2, "change_language" : MessageLookupByLibrary.simpleMessage("Sprache ändern"), - "change_language_to" : m2, + "change_language_to" : m3, "changelog" : MessageLookupByLibrary.simpleMessage("Änderungsprotokoll"), "clear" : MessageLookupByLibrary.simpleMessage("Löschen"), - "commit_transaction_amount_fee" : m3, + "commit_transaction_amount_fee" : m4, "confirm" : MessageLookupByLibrary.simpleMessage("Bestätigen"), "confirm_sending" : MessageLookupByLibrary.simpleMessage("Bestätigen Sie das Senden"), "contact" : MessageLookupByLibrary.simpleMessage("Kontakt"), "contact_name" : MessageLookupByLibrary.simpleMessage("Name des Ansprechpartners"), "continue_text" : MessageLookupByLibrary.simpleMessage("Fortsetzen"), - "copied_key_to_clipboard" : m4, + "copied_key_to_clipboard" : m5, "copied_to_clipboard" : MessageLookupByLibrary.simpleMessage("In die Zwischenablage kopiert"), "copy" : MessageLookupByLibrary.simpleMessage("Kopieren"), "create_new" : MessageLookupByLibrary.simpleMessage("Neu erstellen"), "dangerzone" : MessageLookupByLibrary.simpleMessage("Gefahrenzone"), - "dangerzone_warning" : m5, + "dangerzone_warning" : m6, "delete" : MessageLookupByLibrary.simpleMessage("Löschen"), "digit_pin" : MessageLookupByLibrary.simpleMessage("-stelliger PIN"), "edit" : MessageLookupByLibrary.simpleMessage("Bearbeiten"), @@ -112,9 +115,10 @@ class MessageLookup extends MessageLookupByLibrary { "error_text_node_port" : MessageLookupByLibrary.simpleMessage("Der Knotenport kann nur Nummern zwischen 0 und 65535 enthalten"), "error_text_oxen" : MessageLookupByLibrary.simpleMessage("Der OXEN-Wert kann das verfügbare Guthaben nicht überschreiten.\nDie Anzahl der Nachkommastellen muss kleiner oder gleich 12 sein"), "error_text_payment_id" : MessageLookupByLibrary.simpleMessage("Die Zahlungs-ID kann nur 16 bis 64 hexadezimale Zeichen enthalten"), + "error_text_service_node" : MessageLookupByLibrary.simpleMessage("Service Node Schlüssel können nur 64 hexadezimale Zeichen enthalten"), "error_text_subaddress_name" : MessageLookupByLibrary.simpleMessage("Im Namen der Unteradresse könne die Symbole ` , \' \" nicht enthalten sein\nund muss zwischen 1 und 20 Zeichen lang sein"), "error_text_wallet_name" : MessageLookupByLibrary.simpleMessage("Der Walletname darf nur Buchstaben und Zahlen enthalten\nund muss zwischen 1 und 15 Zeichen lang sein"), - "failed_authentication" : m6, + "failed_authentication" : m7, "faq" : MessageLookupByLibrary.simpleMessage("FAQ"), "fetching" : MessageLookupByLibrary.simpleMessage("aktualisieren"), "filters" : MessageLookupByLibrary.simpleMessage("Filter"), @@ -127,7 +131,7 @@ class MessageLookup extends MessageLookupByLibrary { "keys_title" : MessageLookupByLibrary.simpleMessage("Schlüssel"), "loading_your_wallet" : MessageLookupByLibrary.simpleMessage("Wallet wird geladen"), "login" : MessageLookupByLibrary.simpleMessage("Einloggen"), - "never_give_your" : m7, + "never_give_your" : m8, "new_subaddress_create" : MessageLookupByLibrary.simpleMessage("Erstellen"), "new_subaddress_label_name" : MessageLookupByLibrary.simpleMessage("Name"), "new_subaddress_title" : MessageLookupByLibrary.simpleMessage("Neue Unteradresse"), @@ -140,7 +144,7 @@ class MessageLookup extends MessageLookupByLibrary { "nodes_list_reset_to_default_message" : MessageLookupByLibrary.simpleMessage("Möchten Sie die Einstellungen wirklich auf die Standardeinstellungen zurücksetzen?"), "nothing_staked" : MessageLookupByLibrary.simpleMessage("Noch nichts gestaked"), "ok" : MessageLookupByLibrary.simpleMessage("OK"), - "openalias_alert_content" : m8, + "openalias_alert_content" : m9, "openalias_alert_title" : MessageLookupByLibrary.simpleMessage("OXEN-Empfänger erkannt"), "outgoing" : MessageLookupByLibrary.simpleMessage("Ausgehend"), "oxen_available_balance" : MessageLookupByLibrary.simpleMessage("OXEN verfügbares Guthaben"), @@ -184,7 +188,7 @@ class MessageLookup extends MessageLookupByLibrary { "restore_wallet" : MessageLookupByLibrary.simpleMessage("Wallet wiederherstellen"), "restore_wallet_name" : MessageLookupByLibrary.simpleMessage("Walletname"), "restore_wallet_restore_description" : MessageLookupByLibrary.simpleMessage("Beschreibung zur Wiederherstellung des Wallets"), - "router_no_route" : m9, + "router_no_route" : m10, "save" : MessageLookupByLibrary.simpleMessage("Speichern"), "seed_language_chinese" : MessageLookupByLibrary.simpleMessage("Chinesisch"), "seed_language_choose" : MessageLookupByLibrary.simpleMessage("Bitte wählen Sie die Ausgangssprache"), @@ -204,13 +208,14 @@ class MessageLookup extends MessageLookupByLibrary { "send_estimated_fee" : MessageLookupByLibrary.simpleMessage("Geschätzte Gebühr:"), "send_oxen" : MessageLookupByLibrary.simpleMessage("OXEN Senden"), "send_oxen_address" : MessageLookupByLibrary.simpleMessage("Oxen-Adresse"), - "send_priority" : m10, + "send_priority" : m11, "send_title" : MessageLookupByLibrary.simpleMessage("Senden Sie Oxen"), "send_your_wallet" : MessageLookupByLibrary.simpleMessage("Dein Wallet"), "sending" : MessageLookupByLibrary.simpleMessage("Senden"), "sent" : MessageLookupByLibrary.simpleMessage("Geschickt"), + "service_node_key" : MessageLookupByLibrary.simpleMessage("Service Node Schlüssel"), "settings_all" : MessageLookupByLibrary.simpleMessage("ALLE"), - "settings_allow_biometrical_authentication" : MessageLookupByLibrary.simpleMessage("Biometrische Authentifizierung"), + "settings_allow_biometric_authentication" : MessageLookupByLibrary.simpleMessage("Biometrische Authentifizierung"), "settings_balance_detail" : MessageLookupByLibrary.simpleMessage("Dezimalstellen"), "settings_change_language" : MessageLookupByLibrary.simpleMessage("Sprache ändern"), "settings_change_pin" : MessageLookupByLibrary.simpleMessage("PIN ändern"), @@ -238,7 +243,7 @@ class MessageLookup extends MessageLookupByLibrary { "spend_key_private" : MessageLookupByLibrary.simpleMessage("Ausgabe-Schlüssel (geheim)"), "spend_key_public" : MessageLookupByLibrary.simpleMessage("Ausgabe-Schlüssel (öffentlich)"), "stake_more" : MessageLookupByLibrary.simpleMessage("Mehr staken"), - "stakes" : MessageLookupByLibrary.simpleMessage("Stakes"), + "stake_oxen" : MessageLookupByLibrary.simpleMessage("Oxen staken"), "start_staking" : MessageLookupByLibrary.simpleMessage("Starte zu staken"), "status" : MessageLookupByLibrary.simpleMessage("Status: "), "subaddress_title" : MessageLookupByLibrary.simpleMessage("Unteradressenliste"), @@ -248,11 +253,14 @@ class MessageLookup extends MessageLookupByLibrary { "sync_status_failed_connect" : MessageLookupByLibrary.simpleMessage("Verbindung zum Knoten fehlgeschlagen"), "sync_status_not_connected" : MessageLookupByLibrary.simpleMessage("NICHT VERBUNDEN"), "sync_status_starting_sync" : MessageLookupByLibrary.simpleMessage("STARTEN DER SYNCHRONISIERUNG"), - "sync_status_syncronized" : MessageLookupByLibrary.simpleMessage("SYNCHRONISIERT"), - "sync_status_syncronizing" : MessageLookupByLibrary.simpleMessage("SYNCHRONISIERUNG"), + "sync_status_synchronized" : MessageLookupByLibrary.simpleMessage("SYNCHRONISIERT"), + "sync_status_synchronizing" : MessageLookupByLibrary.simpleMessage("SYNCHRONISIERUNG"), + "title_confirm_unlock_stake" : MessageLookupByLibrary.simpleMessage("Stake entsperren"), + "title_new_stake" : MessageLookupByLibrary.simpleMessage("Neuer Stake"), + "title_stakes" : MessageLookupByLibrary.simpleMessage("Stakes"), "today" : MessageLookupByLibrary.simpleMessage("Heute"), "transaction_details_amount" : MessageLookupByLibrary.simpleMessage("Betrag"), - "transaction_details_copied" : m11, + "transaction_details_copied" : m12, "transaction_details_date" : MessageLookupByLibrary.simpleMessage("Datum"), "transaction_details_height" : MessageLookupByLibrary.simpleMessage("Höhe"), "transaction_details_recipient_address" : MessageLookupByLibrary.simpleMessage("Empfängeradresse"), @@ -263,17 +271,19 @@ class MessageLookup extends MessageLookupByLibrary { "transaction_sent" : MessageLookupByLibrary.simpleMessage("Transaktion gesendet!"), "transactions" : MessageLookupByLibrary.simpleMessage("Transaktionen"), "transactions_by_date" : MessageLookupByLibrary.simpleMessage("Transaktionen nach Datum"), + "unable_unlock_stake" : MessageLookupByLibrary.simpleMessage("Stake Entsperrung nicht möglich"), + "unlock_stake_requested" : MessageLookupByLibrary.simpleMessage("Stake Entsperrung angefragt"), "use" : MessageLookupByLibrary.simpleMessage("Wechseln zu "), - "version" : m12, + "version" : m13, "view_key_private" : MessageLookupByLibrary.simpleMessage("Anzeige-Schlüssel (geheim)"), "view_key_public" : MessageLookupByLibrary.simpleMessage("Anzeige-Schlüssel (öffentlich)"), "wallet_keys" : MessageLookupByLibrary.simpleMessage("Wallet Schlüssel"), "wallet_list_create_new_wallet" : MessageLookupByLibrary.simpleMessage("Neues Wallet erstellen"), - "wallet_list_failed_to_load" : m13, - "wallet_list_failed_to_remove" : m14, + "wallet_list_failed_to_load" : m14, + "wallet_list_failed_to_remove" : m15, "wallet_list_load_wallet" : MessageLookupByLibrary.simpleMessage("Wallet laden"), - "wallet_list_loading_wallet" : m15, - "wallet_list_removing_wallet" : m16, + "wallet_list_loading_wallet" : m16, + "wallet_list_removing_wallet" : m17, "wallet_list_restore_wallet" : MessageLookupByLibrary.simpleMessage("Wallet wiederherstellen"), "wallet_list_title" : MessageLookupByLibrary.simpleMessage("Oxen Wallet"), "wallet_menu" : MessageLookupByLibrary.simpleMessage("Wallet-Menü"), diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index 7b5a6759..fb619c34 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -21,37 +21,39 @@ class MessageLookup extends MessageLookupByLibrary { static m0(status) => "${status} Blocks Remaining"; - static m1(node) => "Are you sure to change current node to ${node}?"; + static m1(serviceNodeKey) => "Do you really want to unlock your stake from ${serviceNodeKey}?"; - static m2(language) => "Change language to ${language}?"; + static m2(node) => "Are you sure to change current node to ${node}?"; - static m3(amount, fee) => "Commit transaction\nAmount: ${amount}\nFee: ${fee}"; + static m3(language) => "Change language to ${language}?"; - static m4(key) => "Copied ${key} to Clipboard"; + static m4(amount, fee) => "Commit transaction\nAmount: ${amount}\nFee: ${fee}"; - static m5(item, app_store) => "NEVER input your Oxen wallet ${item} into any software or website other than the OFFICIAL Oxen wallets downloaded directly from the ${app_store}, the Oxen website, or the Oxen GitHub.\nAre you sure you want to access your wallet ${item}?"; + static m5(key) => "Copied ${key} to Clipboard"; - static m6(state_error) => "Failed authentication. ${state_error}"; + static m6(item, app_store) => "NEVER input your Oxen wallet ${item} into any software or website other than the OFFICIAL Oxen wallets downloaded directly from the ${app_store}, the Oxen website, or the Oxen GitHub.\nAre you sure you want to access your wallet ${item}?"; - static m7(item) => "NEVER give your Oxen wallet ${item} to ANYONE!"; + static m7(state_error) => "Failed authentication. ${state_error}"; - static m8(recipient_name) => "You will be sending funds to\n${recipient_name}"; + static m8(item) => "NEVER give your Oxen wallet ${item} to ANYONE!"; - static m9(name) => "No route defined for ${name}"; + static m9(recipient_name) => "You will be sending funds to\n${recipient_name}"; - static m10(transactionPriority) => "Currently the fee is set at ${transactionPriority} priority.\nTransaction priority can be adjusted in the settings"; + static m10(name) => "No route defined for ${name}"; - static m11(title) => "${title} copied to Clipboard"; + static m11(transactionPriority) => "Currently the fee is set at ${transactionPriority} priority.\nTransaction priority can be adjusted in the settings"; - static m12(currentVersion) => "Version ${currentVersion}"; + static m12(title) => "${title} copied to Clipboard"; - static m13(wallet_name, error) => "Failed to load ${wallet_name} wallet. ${error}"; + static m13(currentVersion) => "Version ${currentVersion}"; - static m14(wallet_name, error) => "Failed to remove ${wallet_name} wallet. ${error}"; + static m14(wallet_name, error) => "Failed to load ${wallet_name} wallet. ${error}"; - static m15(wallet_name) => "Loading ${wallet_name} wallet"; + static m15(wallet_name, error) => "Failed to remove ${wallet_name} wallet. ${error}"; - static m16(wallet_name) => "Removing ${wallet_name} wallet"; + static m16(wallet_name) => "Loading ${wallet_name} wallet"; + + static m17(wallet_name) => "Removing ${wallet_name} wallet"; final messages = _notInlinedMessages(_notInlinedMessages); static _notInlinedMessages(_) => { @@ -76,25 +78,26 @@ class MessageLookup extends MessageLookupByLibrary { "authentication" : MessageLookupByLibrary.simpleMessage("Authentication"), "available_balance" : MessageLookupByLibrary.simpleMessage("Available Balance"), "biometric_auth_reason" : MessageLookupByLibrary.simpleMessage("Scan your fingerprint to authenticate"), + "body_confirm_unlock_stake" : m1, "cancel" : MessageLookupByLibrary.simpleMessage("Cancel"), "change" : MessageLookupByLibrary.simpleMessage("Change"), - "change_current_node" : m1, + "change_current_node" : m2, "change_language" : MessageLookupByLibrary.simpleMessage("Change language"), - "change_language_to" : m2, + "change_language_to" : m3, "changelog" : MessageLookupByLibrary.simpleMessage("Changelog"), "clear" : MessageLookupByLibrary.simpleMessage("Clear"), - "commit_transaction_amount_fee" : m3, + "commit_transaction_amount_fee" : m4, "confirm" : MessageLookupByLibrary.simpleMessage("Confirm"), "confirm_sending" : MessageLookupByLibrary.simpleMessage("Confirm sending"), "contact" : MessageLookupByLibrary.simpleMessage("Contact"), "contact_name" : MessageLookupByLibrary.simpleMessage("Contact Name"), "continue_text" : MessageLookupByLibrary.simpleMessage("Continue"), - "copied_key_to_clipboard" : m4, + "copied_key_to_clipboard" : m5, "copied_to_clipboard" : MessageLookupByLibrary.simpleMessage("Copied to Clipboard"), "copy" : MessageLookupByLibrary.simpleMessage("Copy"), "create_new" : MessageLookupByLibrary.simpleMessage("Create new"), "dangerzone" : MessageLookupByLibrary.simpleMessage("Dangerzone"), - "dangerzone_warning" : m5, + "dangerzone_warning" : m6, "delete" : MessageLookupByLibrary.simpleMessage("Delete"), "digit_pin" : MessageLookupByLibrary.simpleMessage("-digit PIN"), "edit" : MessageLookupByLibrary.simpleMessage("Edit"), @@ -112,9 +115,10 @@ class MessageLookup extends MessageLookupByLibrary { "error_text_node_port" : MessageLookupByLibrary.simpleMessage("Node port can only contain numbers between 0 and 65535"), "error_text_oxen" : MessageLookupByLibrary.simpleMessage("OXEN value can\'t exceed available balance.\nThe number of fraction digits must be less or equal to 12"), "error_text_payment_id" : MessageLookupByLibrary.simpleMessage("Payment ID can only contain from 16 to 64 chars in hex"), + "error_text_service_node" : MessageLookupByLibrary.simpleMessage("A Service Node key can only contain 64 chars in hex"), "error_text_subaddress_name" : MessageLookupByLibrary.simpleMessage("Subaddress name can\'t contain ` , \' \" symbols\nand must be between 1 and 20 characters long"), "error_text_wallet_name" : MessageLookupByLibrary.simpleMessage("Wallet name can only contain letters, numbers\nand must be between 1 and 15 characters long"), - "failed_authentication" : m6, + "failed_authentication" : m7, "faq" : MessageLookupByLibrary.simpleMessage("FAQ"), "fetching" : MessageLookupByLibrary.simpleMessage("Fetching"), "filters" : MessageLookupByLibrary.simpleMessage("Filters"), @@ -127,7 +131,7 @@ class MessageLookup extends MessageLookupByLibrary { "keys_title" : MessageLookupByLibrary.simpleMessage("Keys"), "loading_your_wallet" : MessageLookupByLibrary.simpleMessage("Loading your wallet"), "login" : MessageLookupByLibrary.simpleMessage("Login"), - "never_give_your" : m7, + "never_give_your" : m8, "new_subaddress_create" : MessageLookupByLibrary.simpleMessage("Create"), "new_subaddress_label_name" : MessageLookupByLibrary.simpleMessage("Label name"), "new_subaddress_title" : MessageLookupByLibrary.simpleMessage("New subaddress"), @@ -140,7 +144,7 @@ class MessageLookup extends MessageLookupByLibrary { "nodes_list_reset_to_default_message" : MessageLookupByLibrary.simpleMessage("Are you sure that you want to reset settings to default?"), "nothing_staked" : MessageLookupByLibrary.simpleMessage("Nothing staked yet"), "ok" : MessageLookupByLibrary.simpleMessage("OK"), - "openalias_alert_content" : m8, + "openalias_alert_content" : m9, "openalias_alert_title" : MessageLookupByLibrary.simpleMessage("OXEN Recipient Detected"), "outgoing" : MessageLookupByLibrary.simpleMessage("Outgoing"), "oxen_available_balance" : MessageLookupByLibrary.simpleMessage("OXEN Available Balance"), @@ -184,7 +188,7 @@ class MessageLookup extends MessageLookupByLibrary { "restore_wallet" : MessageLookupByLibrary.simpleMessage("Restore wallet"), "restore_wallet_name" : MessageLookupByLibrary.simpleMessage("Wallet name"), "restore_wallet_restore_description" : MessageLookupByLibrary.simpleMessage("Wallet restore description"), - "router_no_route" : m9, + "router_no_route" : m10, "save" : MessageLookupByLibrary.simpleMessage("Save"), "seed_language_chinese" : MessageLookupByLibrary.simpleMessage("Chinese"), "seed_language_choose" : MessageLookupByLibrary.simpleMessage("Please choose seed language"), @@ -204,13 +208,14 @@ class MessageLookup extends MessageLookupByLibrary { "send_estimated_fee" : MessageLookupByLibrary.simpleMessage("Estimated fee:"), "send_oxen" : MessageLookupByLibrary.simpleMessage("Send OXEN"), "send_oxen_address" : MessageLookupByLibrary.simpleMessage("Oxen address"), - "send_priority" : m10, + "send_priority" : m11, "send_title" : MessageLookupByLibrary.simpleMessage("Send Oxen"), "send_your_wallet" : MessageLookupByLibrary.simpleMessage("Your wallet"), "sending" : MessageLookupByLibrary.simpleMessage("Sending"), "sent" : MessageLookupByLibrary.simpleMessage("Sent"), + "service_node_key" : MessageLookupByLibrary.simpleMessage("Service Node Key"), "settings_all" : MessageLookupByLibrary.simpleMessage("ALL"), - "settings_allow_biometrical_authentication" : MessageLookupByLibrary.simpleMessage("Allow biometrical authentication"), + "settings_allow_biometric_authentication" : MessageLookupByLibrary.simpleMessage("Allow biometric authentication"), "settings_balance_detail" : MessageLookupByLibrary.simpleMessage("Decimals"), "settings_change_language" : MessageLookupByLibrary.simpleMessage("Change language"), "settings_change_pin" : MessageLookupByLibrary.simpleMessage("Change PIN"), @@ -238,7 +243,7 @@ class MessageLookup extends MessageLookupByLibrary { "spend_key_private" : MessageLookupByLibrary.simpleMessage("Spend key (private)"), "spend_key_public" : MessageLookupByLibrary.simpleMessage("Spend key (public)"), "stake_more" : MessageLookupByLibrary.simpleMessage("Stake more"), - "stakes" : MessageLookupByLibrary.simpleMessage("Stakes"), + "stake_oxen" : MessageLookupByLibrary.simpleMessage("Stake Oxen"), "start_staking" : MessageLookupByLibrary.simpleMessage("Start staking"), "status" : MessageLookupByLibrary.simpleMessage("Status: "), "subaddress_title" : MessageLookupByLibrary.simpleMessage("Subaddress list"), @@ -248,11 +253,14 @@ class MessageLookup extends MessageLookupByLibrary { "sync_status_failed_connect" : MessageLookupByLibrary.simpleMessage("FAILED CONNECT TO THE NODE"), "sync_status_not_connected" : MessageLookupByLibrary.simpleMessage("NOT CONNECTED"), "sync_status_starting_sync" : MessageLookupByLibrary.simpleMessage("STARTING SYNC"), - "sync_status_syncronized" : MessageLookupByLibrary.simpleMessage("SYNCHRONIZED"), - "sync_status_syncronizing" : MessageLookupByLibrary.simpleMessage("SYNCHRONIZING"), + "sync_status_synchronized" : MessageLookupByLibrary.simpleMessage("SYNCHRONIZED"), + "sync_status_synchronizing" : MessageLookupByLibrary.simpleMessage("SYNCHRONIZING"), + "title_confirm_unlock_stake" : MessageLookupByLibrary.simpleMessage("Unlock Stake"), + "title_new_stake" : MessageLookupByLibrary.simpleMessage("New Stake"), + "title_stakes" : MessageLookupByLibrary.simpleMessage("Stakes"), "today" : MessageLookupByLibrary.simpleMessage("Today"), "transaction_details_amount" : MessageLookupByLibrary.simpleMessage("Amount"), - "transaction_details_copied" : m11, + "transaction_details_copied" : m12, "transaction_details_date" : MessageLookupByLibrary.simpleMessage("Date"), "transaction_details_height" : MessageLookupByLibrary.simpleMessage("Height"), "transaction_details_recipient_address" : MessageLookupByLibrary.simpleMessage("Recipient address"), @@ -263,17 +271,19 @@ class MessageLookup extends MessageLookupByLibrary { "transaction_sent" : MessageLookupByLibrary.simpleMessage("Transaction sent!"), "transactions" : MessageLookupByLibrary.simpleMessage("Transactions"), "transactions_by_date" : MessageLookupByLibrary.simpleMessage("Transactions by date"), + "unable_unlock_stake" : MessageLookupByLibrary.simpleMessage("Unable to unlock stake"), + "unlock_stake_requested" : MessageLookupByLibrary.simpleMessage("Stake unlock requested"), "use" : MessageLookupByLibrary.simpleMessage("Switch to "), - "version" : m12, + "version" : m13, "view_key_private" : MessageLookupByLibrary.simpleMessage("View key (private)"), "view_key_public" : MessageLookupByLibrary.simpleMessage("View key (public)"), "wallet_keys" : MessageLookupByLibrary.simpleMessage("Wallet keys"), "wallet_list_create_new_wallet" : MessageLookupByLibrary.simpleMessage("Create New Wallet"), - "wallet_list_failed_to_load" : m13, - "wallet_list_failed_to_remove" : m14, + "wallet_list_failed_to_load" : m14, + "wallet_list_failed_to_remove" : m15, "wallet_list_load_wallet" : MessageLookupByLibrary.simpleMessage("Load wallet"), - "wallet_list_loading_wallet" : m15, - "wallet_list_removing_wallet" : m16, + "wallet_list_loading_wallet" : m16, + "wallet_list_removing_wallet" : m17, "wallet_list_restore_wallet" : MessageLookupByLibrary.simpleMessage("Restore Wallet"), "wallet_list_title" : MessageLookupByLibrary.simpleMessage("Oxen Wallet"), "wallet_menu" : MessageLookupByLibrary.simpleMessage("Menu"), diff --git a/lib/generated/intl/messages_fr.dart b/lib/generated/intl/messages_fr.dart index 01902735..f58655ac 100644 --- a/lib/generated/intl/messages_fr.dart +++ b/lib/generated/intl/messages_fr.dart @@ -21,37 +21,39 @@ class MessageLookup extends MessageLookupByLibrary { static m0(status) => "${status} blocs restants"; - static m1(node) => "Voulez-vous vraiment changer le Node actuel vers ${node}?"; + static m1(serviceNodeKey) => "Voulez-vous vraiment débloquer votre mise de${serviceNodeKey}?"; - static m2(language) => "Changez la langue en ${language}?"; + static m2(node) => "Voulez-vous vraiment changer le Node actuel vers ${node}?"; - static m3(amount, fee) => "Valider la transaction\nMontant: ${amount}\nFee: ${fee}"; + static m3(language) => "Changez la langue en ${language}?"; - static m4(key) => "Clé ${key} dans le presse-papiers"; + static m4(amount, fee) => "Valider la transaction\nMontant: ${amount}\nFee: ${fee}"; - static m5(item, app_store) => "Ne JAMAIS saisir vos identifiants de votre Wallet Oxen ${item} dans tout logiciel ou site Web autre que les portefeuilles OFFICIELS Oxen téléchargés directement à partir du ${app_store}, le site internet Oxen, ou Oxen sur GitHub.\nÊtes-vous sûr de vouloir accéder à votre portefeuille ${item}?"; + static m5(key) => "Clé ${key} dans le presse-papiers"; - static m6(state_error) => "Échec de l\'authentification. ${state_error}"; + static m6(item, app_store) => "Ne JAMAIS saisir vos identifiants de votre Wallet Oxen ${item} dans tout logiciel ou site Web autre que les portefeuilles OFFICIELS Oxen téléchargés directement à partir du ${app_store}, le site internet Oxen, ou Oxen sur GitHub.\nÊtes-vous sûr de vouloir accéder à votre portefeuille ${item}?"; - static m7(item) => "Ne donnez JAMAIS votre Wallet Oxen à qui que ce soit! ${item} à qui que ce soit!"; + static m7(state_error) => "Échec de l\'authentification. ${state_error}"; - static m8(recipient_name) => "Vous envoyez de l\'argent à\n${recipient_name}"; + static m8(item) => "Ne donnez JAMAIS votre Wallet Oxen à qui que ce soit! ${item} à qui que ce soit!"; - static m9(name) => "Aucun itinéraire défini pour ${name}"; + static m9(recipient_name) => "Vous envoyez de l\'argent à\n${recipient_name}"; - static m10(transactionPriority) => "Actuellement, la priorité est définie sur ${transactionPriority}.\nLa priorité de transaction peut être ajustée dans les paramètres"; + static m10(name) => "Aucun itinéraire défini pour ${name}"; - static m11(title) => "${title} copié dans le presse-papiers"; + static m11(transactionPriority) => "Actuellement, la priorité est définie sur ${transactionPriority}.\nLa priorité de transaction peut être ajustée dans les paramètres"; - static m12(currentVersion) => "Version ${currentVersion}"; + static m12(title) => "${title} copié dans le presse-papiers"; - static m13(wallet_name, error) => "Échec du chargement du portefeuille ${wallet_name}. ${error}"; + static m13(currentVersion) => "Version ${currentVersion}"; - static m14(wallet_name, error) => "Erreur lors de la suppression ${wallet_name} Wallet. ${error}"; + static m14(wallet_name, error) => "Échec du chargement du portefeuille ${wallet_name}. ${error}"; - static m15(wallet_name) => "chargement du ${wallet_name} wallet"; + static m15(wallet_name, error) => "Erreur lors de la suppression ${wallet_name} Wallet. ${error}"; - static m16(wallet_name) => "Wallet ${wallet_name}"; + static m16(wallet_name) => "chargement du ${wallet_name} wallet"; + + static m17(wallet_name) => "Wallet ${wallet_name}"; final messages = _notInlinedMessages(_notInlinedMessages); static _notInlinedMessages(_) => { @@ -76,25 +78,26 @@ class MessageLookup extends MessageLookupByLibrary { "authentication" : MessageLookupByLibrary.simpleMessage("Authentification"), "available_balance" : MessageLookupByLibrary.simpleMessage("Solde disponible"), "biometric_auth_reason" : MessageLookupByLibrary.simpleMessage("Scannez votre empreinte digitale pour l\'authentification"), + "body_confirm_unlock_stake" : m1, "cancel" : MessageLookupByLibrary.simpleMessage("Annuler"), "change" : MessageLookupByLibrary.simpleMessage("Changement"), - "change_current_node" : m1, + "change_current_node" : m2, "change_language" : MessageLookupByLibrary.simpleMessage("changer la langue"), - "change_language_to" : m2, + "change_language_to" : m3, "changelog" : MessageLookupByLibrary.simpleMessage("Journal des modifications"), "clear" : MessageLookupByLibrary.simpleMessage("clair"), - "commit_transaction_amount_fee" : m3, + "commit_transaction_amount_fee" : m4, "confirm" : MessageLookupByLibrary.simpleMessage("confirmer"), "confirm_sending" : MessageLookupByLibrary.simpleMessage("confirmer l\'envoi"), "contact" : MessageLookupByLibrary.simpleMessage("Contact"), "contact_name" : MessageLookupByLibrary.simpleMessage("Nom du contact"), "continue_text" : MessageLookupByLibrary.simpleMessage("Continuez"), - "copied_key_to_clipboard" : m4, + "copied_key_to_clipboard" : m5, "copied_to_clipboard" : MessageLookupByLibrary.simpleMessage("Copié dans le presse-papiers"), "copy" : MessageLookupByLibrary.simpleMessage("copier"), "create_new" : MessageLookupByLibrary.simpleMessage("Créer un nouveau portefeuille"), "dangerzone" : MessageLookupByLibrary.simpleMessage("zone de danger"), - "dangerzone_warning" : m5, + "dangerzone_warning" : m6, "delete" : MessageLookupByLibrary.simpleMessage("effacer"), "digit_pin" : MessageLookupByLibrary.simpleMessage("-chiffre PIN"), "edit" : MessageLookupByLibrary.simpleMessage("Éditer"), @@ -112,9 +115,10 @@ class MessageLookup extends MessageLookupByLibrary { "error_text_node_port" : MessageLookupByLibrary.simpleMessage("Le port du Node ne peut contenir que des nombres compris entre 0 et 65535"), "error_text_oxen" : MessageLookupByLibrary.simpleMessage("La valeur OXEN ne peut pas dépasser le solde disponible.\nLe nombre de décimales doit être inférieur ou égal à 12"), "error_text_payment_id" : MessageLookupByLibrary.simpleMessage("L\'ID de paiement ne peut contenir que 16 à 64 caractères hexadécimaux"), + "error_text_service_node" : MessageLookupByLibrary.simpleMessage("Une clé de nœud de service ne peut contenir que 64 caractères maximum"), "error_text_subaddress_name" : MessageLookupByLibrary.simpleMessage("Au nom de la sous-adresse, les symboles ` , \' \" ne pas être inclus\net doit comporter entre 1 et 20 caractères"), "error_text_wallet_name" : MessageLookupByLibrary.simpleMessage("Le nom du portefeuille ne peut contenir que des lettres et des chiffres\net doit comporter entre 1 et 15 caractères"), - "failed_authentication" : m6, + "failed_authentication" : m7, "faq" : MessageLookupByLibrary.simpleMessage("FAQ"), "fetching" : MessageLookupByLibrary.simpleMessage("Récupération"), "filters" : MessageLookupByLibrary.simpleMessage("filtres"), @@ -127,7 +131,7 @@ class MessageLookup extends MessageLookupByLibrary { "keys_title" : MessageLookupByLibrary.simpleMessage("Clés"), "loading_your_wallet" : MessageLookupByLibrary.simpleMessage("chargement du portefeuille"), "login" : MessageLookupByLibrary.simpleMessage("Login"), - "never_give_your" : m7, + "never_give_your" : m8, "new_subaddress_create" : MessageLookupByLibrary.simpleMessage("Créer"), "new_subaddress_label_name" : MessageLookupByLibrary.simpleMessage("Nom"), "new_subaddress_title" : MessageLookupByLibrary.simpleMessage("Nouvelle sous-adresse"), @@ -140,7 +144,7 @@ class MessageLookup extends MessageLookupByLibrary { "nodes_list_reset_to_default_message" : MessageLookupByLibrary.simpleMessage("Êtes-vous sûr de vouloir réinitialiser les paramètres par défaut?"), "nothing_staked" : MessageLookupByLibrary.simpleMessage("Aucune contribution pour le moment"), "ok" : MessageLookupByLibrary.simpleMessage("OK"), - "openalias_alert_content" : m8, + "openalias_alert_content" : m9, "openalias_alert_title" : MessageLookupByLibrary.simpleMessage("OXEN-destinataire reconnu"), "outgoing" : MessageLookupByLibrary.simpleMessage("sortant"), "oxen_available_balance" : MessageLookupByLibrary.simpleMessage("OXEN solde disponible"), @@ -184,7 +188,7 @@ class MessageLookup extends MessageLookupByLibrary { "restore_wallet" : MessageLookupByLibrary.simpleMessage("Restaurer un portefeuille"), "restore_wallet_name" : MessageLookupByLibrary.simpleMessage("Nom du portefeuille"), "restore_wallet_restore_description" : MessageLookupByLibrary.simpleMessage("Description de la restauration du portefeuille"), - "router_no_route" : m9, + "router_no_route" : m10, "save" : MessageLookupByLibrary.simpleMessage("Sauvegarder"), "seed_language_chinese" : MessageLookupByLibrary.simpleMessage("Chinois"), "seed_language_choose" : MessageLookupByLibrary.simpleMessage("Veuillez sélectionner la langue source"), @@ -204,13 +208,14 @@ class MessageLookup extends MessageLookupByLibrary { "send_estimated_fee" : MessageLookupByLibrary.simpleMessage("Frais estimés:"), "send_oxen" : MessageLookupByLibrary.simpleMessage("Envoyer OXEN"), "send_oxen_address" : MessageLookupByLibrary.simpleMessage("Adresse Oxen"), - "send_priority" : m10, + "send_priority" : m11, "send_title" : MessageLookupByLibrary.simpleMessage("Envoyer des OXEN"), "send_your_wallet" : MessageLookupByLibrary.simpleMessage("Votre portefeuille"), "sending" : MessageLookupByLibrary.simpleMessage("Envoyer"), "sent" : MessageLookupByLibrary.simpleMessage("expédié"), + "service_node_key" : MessageLookupByLibrary.simpleMessage("Clé de nœud de service"), "settings_all" : MessageLookupByLibrary.simpleMessage("TOUT"), - "settings_allow_biometrical_authentication" : MessageLookupByLibrary.simpleMessage("Authentification biométrique"), + "settings_allow_biometric_authentication" : MessageLookupByLibrary.simpleMessage("Authentification biométrique"), "settings_balance_detail" : MessageLookupByLibrary.simpleMessage("Décimales"), "settings_change_language" : MessageLookupByLibrary.simpleMessage("changer de langue"), "settings_change_pin" : MessageLookupByLibrary.simpleMessage("changer le code PIN"), @@ -238,7 +243,7 @@ class MessageLookup extends MessageLookupByLibrary { "spend_key_private" : MessageLookupByLibrary.simpleMessage("Clé de dépense (secret)"), "spend_key_public" : MessageLookupByLibrary.simpleMessage("Clé de dépense (publique)"), "stake_more" : MessageLookupByLibrary.simpleMessage("Staker plus"), - "stakes" : MessageLookupByLibrary.simpleMessage("Stakes"), + "stake_oxen" : MessageLookupByLibrary.simpleMessage("Stake Oxen"), "start_staking" : MessageLookupByLibrary.simpleMessage("Commencer le staking"), "status" : MessageLookupByLibrary.simpleMessage("Statut: "), "subaddress_title" : MessageLookupByLibrary.simpleMessage("Liste des sous-adresses"), @@ -248,11 +253,14 @@ class MessageLookup extends MessageLookupByLibrary { "sync_status_failed_connect" : MessageLookupByLibrary.simpleMessage("ÉCHEC DE LA CONNEXION AU NODE"), "sync_status_not_connected" : MessageLookupByLibrary.simpleMessage("PAS CONNECTÉ"), "sync_status_starting_sync" : MessageLookupByLibrary.simpleMessage("DÉBUT DE LA SYNCHRONISATION"), - "sync_status_syncronized" : MessageLookupByLibrary.simpleMessage("SYNCHRONISÉ"), - "sync_status_syncronizing" : MessageLookupByLibrary.simpleMessage("SYNCHRONISATION"), + "sync_status_synchronized" : MessageLookupByLibrary.simpleMessage("SYNCHRONISÉ"), + "sync_status_synchronizing" : MessageLookupByLibrary.simpleMessage("SYNCHRONISATION"), + "title_confirm_unlock_stake" : MessageLookupByLibrary.simpleMessage("Déverrouiller Stake"), + "title_new_stake" : MessageLookupByLibrary.simpleMessage("Nouveau Stake"), + "title_stakes" : MessageLookupByLibrary.simpleMessage("Stakes"), "today" : MessageLookupByLibrary.simpleMessage("aujourd\'hui"), "transaction_details_amount" : MessageLookupByLibrary.simpleMessage("Montant"), - "transaction_details_copied" : m11, + "transaction_details_copied" : m12, "transaction_details_date" : MessageLookupByLibrary.simpleMessage("Date"), "transaction_details_height" : MessageLookupByLibrary.simpleMessage("Taille"), "transaction_details_recipient_address" : MessageLookupByLibrary.simpleMessage("Adresse du destinataire"), @@ -263,17 +271,19 @@ class MessageLookup extends MessageLookupByLibrary { "transaction_sent" : MessageLookupByLibrary.simpleMessage("Transaction envoyé!"), "transactions" : MessageLookupByLibrary.simpleMessage("transactions"), "transactions_by_date" : MessageLookupByLibrary.simpleMessage("transactions par date"), + "unable_unlock_stake" : MessageLookupByLibrary.simpleMessage("Impossible de déverrouiller le Stake"), + "unlock_stake_requested" : MessageLookupByLibrary.simpleMessage("Déverrouillage du Stake demandé"), "use" : MessageLookupByLibrary.simpleMessage("Basculer vers "), - "version" : m12, + "version" : m13, "view_key_private" : MessageLookupByLibrary.simpleMessage("Clé d\'observation (secret)"), "view_key_public" : MessageLookupByLibrary.simpleMessage("Clé d\'observation (publique)"), "wallet_keys" : MessageLookupByLibrary.simpleMessage("Clés du portefeuille"), "wallet_list_create_new_wallet" : MessageLookupByLibrary.simpleMessage("Créer un nouveau portefeuille"), - "wallet_list_failed_to_load" : m13, - "wallet_list_failed_to_remove" : m14, + "wallet_list_failed_to_load" : m14, + "wallet_list_failed_to_remove" : m15, "wallet_list_load_wallet" : MessageLookupByLibrary.simpleMessage("Charger le portefeuille"), - "wallet_list_loading_wallet" : m15, - "wallet_list_removing_wallet" : m16, + "wallet_list_loading_wallet" : m16, + "wallet_list_removing_wallet" : m17, "wallet_list_restore_wallet" : MessageLookupByLibrary.simpleMessage("Restaurer le portefeuille"), "wallet_list_title" : MessageLookupByLibrary.simpleMessage("Oxen Wallet"), "wallet_menu" : MessageLookupByLibrary.simpleMessage("Menu du portefeuille"), diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index 51b73766..71162495 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -1276,10 +1276,20 @@ class S { } /// `Stakes` - String get stakes { + String get title_stakes { return Intl.message( 'Stakes', - name: 'stakes', + name: 'title_stakes', + desc: '', + args: [], + ); + } + + /// `New Stake` + String get title_new_stake { + return Intl.message( + 'New Stake', + name: 'title_new_stake', desc: '', args: [], ); @@ -1325,6 +1335,66 @@ class S { ); } + /// `Service Node Key` + String get service_node_key { + return Intl.message( + 'Service Node Key', + name: 'service_node_key', + desc: '', + args: [], + ); + } + + /// `Stake Oxen` + String get stake_oxen { + return Intl.message( + 'Stake Oxen', + name: 'stake_oxen', + desc: '', + args: [], + ); + } + + /// `Unlock Stake` + String get title_confirm_unlock_stake { + return Intl.message( + 'Unlock Stake', + name: 'title_confirm_unlock_stake', + desc: '', + args: [], + ); + } + + /// `Do you really want to unlock your stake from {serviceNodeKey}?` + String body_confirm_unlock_stake(Object serviceNodeKey) { + return Intl.message( + 'Do you really want to unlock your stake from $serviceNodeKey?', + name: 'body_confirm_unlock_stake', + desc: '', + args: [serviceNodeKey], + ); + } + + /// `Stake unlock requested` + String get unlock_stake_requested { + return Intl.message( + 'Stake unlock requested', + name: 'unlock_stake_requested', + desc: '', + args: [], + ); + } + + /// `Unable to unlock stake` + String get unable_unlock_stake { + return Intl.message( + 'Unable to unlock stake', + name: 'unable_unlock_stake', + desc: '', + args: [], + ); + } + /// `Settings` String get settings_title { return Intl.message( @@ -1445,11 +1515,11 @@ class S { ); } - /// `Allow biometrical authentication` - String get settings_allow_biometrical_authentication { + /// `Allow biometric authentication` + String get settings_allow_biometric_authentication { return Intl.message( - 'Allow biometrical authentication', - name: 'settings_allow_biometrical_authentication', + 'Allow biometric authentication', + name: 'settings_allow_biometric_authentication', desc: '', args: [], ); @@ -2055,6 +2125,16 @@ class S { ); } + /// `A Service Node key can only contain 64 chars in hex` + String get error_text_service_node { + return Intl.message( + 'A Service Node key can only contain 64 chars in hex', + name: 'error_text_service_node', + desc: '', + args: [], + ); + } + /// `ban_timeout` String get auth_store_ban_timeout { return Intl.message( @@ -2136,20 +2216,20 @@ class S { } /// `SYNCHRONIZING` - String get sync_status_syncronizing { + String get sync_status_synchronizing { return Intl.message( 'SYNCHRONIZING', - name: 'sync_status_syncronizing', + name: 'sync_status_synchronizing', desc: '', args: [], ); } /// `SYNCHRONIZED` - String get sync_status_syncronized { + String get sync_status_synchronized { return Intl.message( 'SYNCHRONIZED', - name: 'sync_status_syncronized', + name: 'sync_status_synchronized', desc: '', args: [], ); diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index 5838d2f1..4e9dad5c 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -156,11 +156,19 @@ "send_creating_transaction" : "Transaktion erstellen", - "stakes": "Stakes", + "title_stakes": "Stakes", + "title_new_stake": "Neuer Stake", "your_contributions" : "Deine Anteile", "start_staking" : "Starte zu staken", "stake_more" : "Mehr staken", "nothing_staked" : "Noch nichts gestaked", + "service_node_key": "Service Node Schlüssel", + "stake_oxen": "Oxen staken", + "title_confirm_unlock_stake": "Stake entsperren", + "body_confirm_unlock_stake": "Möchtest du wirkklich dein Stake von {serviceNodeKey} entsperren?", + "unlock_stake_requested": "Stake Entsperrung angefragt", + "unable_unlock_stake": "Stake Entsperrung nicht möglich", + "settings_title" : "Einstellungen", "settings_nodes" : "Knoten", @@ -174,7 +182,7 @@ "settings_personal" : "Persönlich", "settings_change_pin" : "PIN ändern", "settings_change_language" : "Sprache ändern", - "settings_allow_biometrical_authentication" : "Biometrische Authentifizierung", + "settings_allow_biometric_authentication" : "Biometrische Authentifizierung", "settings_dark_mode" : "Dunkler Modus", "settings_transactions" : "Transaktionen", "settings_display_on_dashboard_list" : "Anzeige in der Dashboard-Liste", @@ -254,6 +262,7 @@ "error_text_wallet_name" : "Der Walletname darf nur Buchstaben und Zahlen enthalten\nund muss zwischen 1 und 15 Zeichen lang sein", "error_text_keys" : "Walletschlüssel können nur 64 hexadezimale Zeichen enthalten", "error_text_crypto_currency" : "Die Anzahl der Nachkommastellen\nmuss kleiner oder gleich 12 sein.", + "error_text_service_node": "Service Node Schlüssel können nur 64 hexadezimale Zeichen enthalten", "auth_store_ban_timeout" : "Auszeit verbieten", @@ -268,8 +277,8 @@ "hidden_balance" : "Verstecktes Guthaben", - "sync_status_syncronizing" : "SYNCHRONISIERUNG", - "sync_status_syncronized" : "SYNCHRONISIERT", + "sync_status_synchronizing" : "SYNCHRONISIERUNG", + "sync_status_synchronized" : "SYNCHRONISIERT", "sync_status_not_connected" : "NICHT VERBUNDEN", "sync_status_starting_sync" : "STARTEN DER SYNCHRONISIERUNG", "sync_status_failed_connect" : "Verbindung zum Knoten fehlgeschlagen", @@ -299,4 +308,4 @@ "never_give_your": "Geben sie NIEMALS ihren Oxen wallet {item} weiter!", "dangerzone_warning": "Geben sie NIEMALS ihren Oxen wallet {item} in einer andere software oder website außer den OFFIZIELLEN Oxen wallets aus dem {app_store}, der Oxen website, der dem Oxen GitHub.\nMöchtest du wirklich fortfahren?", "keys_title": "Schlüssel" -} \ No newline at end of file +} diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 059d803d..ebf64f17 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -156,11 +156,19 @@ "send_creating_transaction" : "Creating transaction", - "stakes": "Stakes", + "title_stakes": "Stakes", + "title_new_stake": "New Stake", "your_contributions" : "Your Contributions", "start_staking" : "Start staking", "stake_more" : "Stake more", "nothing_staked" : "Nothing staked yet", + "service_node_key": "Service Node Key", + "stake_oxen": "Stake Oxen", + "title_confirm_unlock_stake": "Unlock Stake", + "body_confirm_unlock_stake": "Do you really want to unlock your stake from {serviceNodeKey}?", + "unlock_stake_requested": "Stake unlock requested", + "unable_unlock_stake": "Unable to unlock stake", + "settings_title" : "Settings", "settings_nodes" : "Nodes", @@ -174,7 +182,7 @@ "settings_personal" : "Personal", "settings_change_pin" : "Change PIN", "settings_change_language" : "Change language", - "settings_allow_biometrical_authentication" : "Allow biometrical authentication", + "settings_allow_biometric_authentication" : "Allow biometric authentication", "settings_dark_mode" : "Dark mode", "settings_transactions" : "Transactions", "settings_display_on_dashboard_list" : "Display on dashboard list", @@ -254,6 +262,7 @@ "error_text_wallet_name" : "Wallet name can only contain letters, numbers\nand must be between 1 and 15 characters long", "error_text_keys" : "Wallet keys can only contain 64 chars in hex", "error_text_crypto_currency" : "The number of fraction digits\nmust be less or equal to 12", + "error_text_service_node" : "A Service Node key can only contain 64 chars in hex", "auth_store_ban_timeout" : "ban_timeout", @@ -268,8 +277,8 @@ "hidden_balance" : "Hidden Balance", - "sync_status_syncronizing" : "SYNCHRONIZING", - "sync_status_syncronized" : "SYNCHRONIZED", + "sync_status_synchronizing" : "SYNCHRONIZING", + "sync_status_synchronized" : "SYNCHRONIZED", "sync_status_not_connected" : "NOT CONNECTED", "sync_status_starting_sync" : "STARTING SYNC", "sync_status_failed_connect" : "FAILED CONNECT TO THE NODE", @@ -280,6 +289,7 @@ "transaction_priority_slow" : "Slow", "transaction_priority_blink": "Blink", + "change_language" : "Change language", "change_language_to" : "Change language to {language}?", @@ -299,4 +309,4 @@ "never_give_your": "NEVER give your Oxen wallet {item} to ANYONE!", "dangerzone_warning": "NEVER input your Oxen wallet {item} into any software or website other than the OFFICIAL Oxen wallets downloaded directly from the {app_store}, the Oxen website, or the Oxen GitHub.\nAre you sure you want to access your wallet {item}?", "keys_title": "Keys" -} \ No newline at end of file +} diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 9022e97c..f7669364 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -156,11 +156,19 @@ "send_creating_transaction" : "Créer une transaction", - "stakes": "Stakes", + "title_stakes": "Stakes", + "title_new_stake": "Nouveau Stake", "your_contributions" : "Vos contributions", "start_staking" : "Commencer le staking", "stake_more" : "Staker plus", "nothing_staked" : "Aucune contribution pour le moment", + "service_node_key": "Clé de nœud de service", + "stake_oxen": "Stake Oxen", + "title_confirm_unlock_stake": "Déverrouiller Stake", + "body_confirm_unlock_stake": "Voulez-vous vraiment débloquer votre mise de{serviceNodeKey}?", + "unlock_stake_requested": "Déverrouillage du Stake demandé", + "unable_unlock_stake": "Impossible de déverrouiller le Stake", + "settings_title" : "Paramètres", "settings_nodes" : "Node", @@ -174,7 +182,7 @@ "settings_personal" : "personnel", "settings_change_pin" : "changer le code PIN", "settings_change_language" : "changer de langue", - "settings_allow_biometrical_authentication" : "Authentification biométrique", + "settings_allow_biometric_authentication" : "Authentification biométrique", "settings_dark_mode" : "Mode Sombre", "settings_transactions" : "Transactions", "settings_display_on_dashboard_list" : "Afficher dans la liste du tableau de bord", @@ -254,6 +262,7 @@ "error_text_wallet_name" : "Le nom du portefeuille ne peut contenir que des lettres et des chiffres\net doit comporter entre 1 et 15 caractères", "error_text_keys" : "Les clés de portefeuille ne peuvent contenir que 64 caractères hexadécimaux", "error_text_crypto_currency" : "Le nombre de décimales\nm doit être inférieur ou égal à 12.", + "error_text_service_node" : "Une clé de nœud de service ne peut contenir que 64 caractères maximum", "auth_store_ban_timeout" : "Interdire le délai d'expiration", @@ -268,8 +277,8 @@ "hidden_balance" : "solde caché", - "sync_status_syncronizing" : "SYNCHRONISATION", - "sync_status_syncronized" : "SYNCHRONISÉ", + "sync_status_synchronizing" : "SYNCHRONISATION", + "sync_status_synchronized" : "SYNCHRONISÉ", "sync_status_not_connected" : "PAS CONNECTÉ", "sync_status_starting_sync" : "DÉBUT DE LA SYNCHRONISATION", "sync_status_failed_connect" : "ÉCHEC DE LA CONNEXION AU NODE", @@ -300,4 +309,4 @@ "never_give_your": "Ne donnez JAMAIS votre Wallet Oxen à qui que ce soit! {item} à qui que ce soit!", "dangerzone_warning": "Ne JAMAIS saisir vos identifiants de votre Wallet Oxen {item} dans tout logiciel ou site Web autre que les portefeuilles OFFICIELS Oxen téléchargés directement à partir du {app_store}, le site internet Oxen, ou Oxen sur GitHub.\nÊtes-vous sûr de vouloir accéder à votre portefeuille {item}?", "keys_title": "Clés" -} \ No newline at end of file +} diff --git a/lib/router.dart b/lib/router.dart index 269f84c5..bb8fc224 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -43,12 +43,13 @@ import 'package:oxen_wallet/src/screens/settings/change_language.dart'; import 'package:oxen_wallet/src/screens/settings/settings.dart'; import 'package:oxen_wallet/src/screens/setup_pin_code/setup_pin_code.dart'; import 'package:oxen_wallet/src/screens/show_keys/show_keys_page.dart'; +import 'package:oxen_wallet/src/screens/stake/new_stake_page.dart'; import 'package:oxen_wallet/src/screens/stake/stake_page.dart'; import 'package:oxen_wallet/src/screens/subaddress/new_subaddress_page.dart'; import 'package:oxen_wallet/src/screens/subaddress/subaddress_list_page.dart'; import 'package:oxen_wallet/src/screens/transaction_details/transaction_details_page.dart'; import 'package:oxen_wallet/src/screens/wallet_list/wallet_list_page.dart'; -import 'package:oxen_wallet/src/screens/welcome/create_welcome_page.dart'; +import 'package:oxen_wallet/src/screens/welcome/welcome_page.dart'; import 'package:oxen_wallet/src/stores/account_list/account_list_store.dart'; import 'package:oxen_wallet/src/stores/address_book/address_book_store.dart'; import 'package:oxen_wallet/src/stores/auth/auth_store.dart'; @@ -95,7 +96,7 @@ class Router { Box transactionDescriptions}) { switch (settings.name) { case Routes.welcome: - return MaterialPageRoute(builder: (_) => createWelcomePage()); + return MaterialPageRoute(builder: (_) => WelcomePage()); case Routes.newWalletFromWelcome: return CupertinoPageRoute( @@ -436,6 +437,25 @@ class Router { case Routes.stake: return CupertinoPageRoute(builder: (_) => StakePage()); + case Routes.newStake: + return MaterialPageRoute( + builder: (_) => MultiProvider(providers: [ + ProxyProvider( + update: (_, settingsStore, __) => BalanceStore( + walletService: walletService, + settingsStore: settingsStore, + priceStore: priceStore), + ), + Provider( + create: (_) => SyncStore(walletService: walletService), + ), + Provider( + create: (_) => SendStore( + walletService: walletService, + priceStore: priceStore, + transactionDescriptions: transactionDescriptions)), + ], child: NewStakePage())); + default: return MaterialPageRoute( builder: (_) => Scaffold( diff --git a/lib/routes.dart b/lib/routes.dart index 53987c38..8301fc42 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -43,4 +43,5 @@ class Routes { static const changeLanguage = '/change_language'; static const profile = '/profile'; static const stake = '/stake'; -} \ No newline at end of file + static const newStake = '/stake/new'; +} diff --git a/lib/src/domain/common/encrypt.dart b/lib/src/domain/common/encrypt.dart index 4dd7a7c2..8d83f0fc 100644 --- a/lib/src/domain/common/encrypt.dart +++ b/lib/src/domain/common/encrypt.dart @@ -1,5 +1,4 @@ import 'package:encrypt/encrypt.dart'; -import 'package:password/password.dart'; import 'package:oxen_wallet/.secrets.g.dart' as secrets; String encrypt({String source, String key, int keyLength = 16}) { @@ -20,13 +19,6 @@ String decrypt({String source, String key, int keyLength = 16}) { return decrypted; } -String hash({String source}) { - final algorithm = PBKDF2(); - final hash = Password.hash(source, algorithm); - - return hash; -} - String encodedPinCode({String pin}) { final source = '${secrets.salt}$pin'; diff --git a/lib/src/domain/common/fetch_price.dart b/lib/src/domain/common/fetch_price.dart index 7f2064eb..688af15d 100644 --- a/lib/src/domain/common/fetch_price.dart +++ b/lib/src/domain/common/fetch_price.dart @@ -14,7 +14,7 @@ Future fetchPriceFor({CryptoCurrency crypto, FiatCurrency fiat}) async { final apiPath = '/api/price/$fiatStringed'; final uri = Uri.https(fiatApiAuthority, apiPath); - final response = await get(uri.toString()); + final response = await get(uri); if (response.statusCode != 200) { return 0.0; diff --git a/lib/src/domain/common/qr_scanner.dart b/lib/src/domain/common/qr_scanner.dart index b79fe7c0..4a26bdde 100644 --- a/lib/src/domain/common/qr_scanner.dart +++ b/lib/src/domain/common/qr_scanner.dart @@ -7,7 +7,7 @@ Future presentQRScanner() async { try { final result = await BarcodeScanner.scan(); isQrScannerShown = false; - return result.rawContent; + return result; } catch (e) { isQrScannerShown = false; rethrow; diff --git a/lib/src/domain/services/wallet_service.dart b/lib/src/domain/services/wallet_service.dart index 6b478129..394632d8 100644 --- a/lib/src/domain/services/wallet_service.dart +++ b/lib/src/domain/services/wallet_service.dart @@ -117,6 +117,11 @@ class WalletService extends Wallet { @override TransactionHistory getHistory() => _currentWallet.getHistory(); + @override + Future createStake( + TransactionCreationCredentials credentials) => + _currentWallet.createStake(credentials); + @override Future createTransaction( TransactionCreationCredentials credentials) => diff --git a/lib/src/node/node.dart b/lib/src/node/node.dart index 1f7ad3e6..ec9cd277 100644 --- a/lib/src/node/node.dart +++ b/lib/src/node/node.dart @@ -34,9 +34,6 @@ class Node extends HiveObject { Future> sendRPCRequest(String method, {Map params}) async { - - - Map resultBody; final requestBody = params != null @@ -52,7 +49,6 @@ class Node extends HiveObject { final url = Uri.http(uri, '/json_rpc'); final headers = {'Content-type': 'application/json'}; final body = json.encode(requestBody); - print('Node Port: ${url.port}'); final response = await http.post(url, headers: headers, body: body); resultBody = json.decode(response.body) as Map; diff --git a/lib/src/node/node_list.dart b/lib/src/node/node_list.dart index 5e93d3d5..149db2c6 100644 --- a/lib/src/node/node_list.dart +++ b/lib/src/node/node_list.dart @@ -1,10 +1,13 @@ import 'package:flutter/services.dart'; import 'package:hive/hive.dart'; -import 'package:yaml/yaml.dart'; +import 'package:oxen_wallet/devtools.dart'; import 'package:oxen_wallet/src/node/node.dart'; +import 'package:yaml/yaml.dart'; Future> loadDefaultNodes() async { - final nodesRaw = await rootBundle.loadString('assets/node_list.yml'); + final nodeListFileName = + isTestnet ? 'testnet_node_list.yml' : 'node_list.yml'; + final nodesRaw = await rootBundle.loadString('assets/$nodeListFileName'); final nodes = loadYaml(nodesRaw) as YamlList; return nodes.map((dynamic raw) { diff --git a/lib/src/node/sync_status.dart b/lib/src/node/sync_status.dart index 2f10e27b..27e005c4 100644 --- a/lib/src/node/sync_status.dart +++ b/lib/src/node/sync_status.dart @@ -29,7 +29,7 @@ class SyncedSyncStatus extends SyncStatus { double progress() => 1.0; @override - String title() => S.current.sync_status_syncronized; + String title() => S.current.sync_status_synchronized; } class NotConnectedSyncStatus extends SyncStatus { diff --git a/lib/src/screens/accounts/account_list_page.dart b/lib/src/screens/accounts/account_list_page.dart index 08becf3e..7c8ba35f 100644 --- a/lib/src/screens/accounts/account_list_page.dart +++ b/lib/src/screens/accounts/account_list_page.dart @@ -81,9 +81,7 @@ class AccountListPage extends BasePage { .color), ), onTap: () { - if (isCurrent) { - return; - } + if (isCurrent) return; walletStore.setAccount(account); Navigator.of(context).pop(); diff --git a/lib/src/screens/accounts/account_page.dart b/lib/src/screens/accounts/account_page.dart index 5777c5da..8ecefce6 100644 --- a/lib/src/screens/accounts/account_page.dart +++ b/lib/src/screens/accounts/account_page.dart @@ -1,13 +1,14 @@ -import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; -import 'package:provider/provider.dart'; import 'package:oxen_wallet/generated/l10n.dart'; -import 'package:oxen_wallet/src/stores/account_list/account_list_store.dart'; -import 'package:oxen_wallet/src/widgets/primary_button.dart'; import 'package:oxen_wallet/src/screens/base_page.dart'; +import 'package:oxen_wallet/src/stores/account_list/account_list_store.dart'; import 'package:oxen_wallet/src/wallet/oxen/account.dart'; -import 'package:oxen_wallet/palette.dart'; +import 'package:oxen_wallet/src/widgets/oxen_text_field.dart'; +import 'package:oxen_wallet/src/widgets/primary_button.dart'; +import 'package:oxen_wallet/src/widgets/scollable_with_bottom_section.dart'; +import 'package:provider/provider.dart'; class AccountPage extends BasePage { AccountPage({this.account}); @@ -50,63 +51,49 @@ class AccountFormState extends State { Widget build(BuildContext context) { final accountListStore = Provider.of(context); - return Form( - key: _formKey, - child: Container( - padding: EdgeInsets.all(20.0), - child: Column( - children: [ - Expanded( - child: Center( - child: TextFormField( - decoration: InputDecoration( - hintStyle: TextStyle(color: Theme.of(context).hintColor), + return ScrollableWithBottomSection( + contentPadding: EdgeInsets.all(20), + content: Form( + key: _formKey, + child: Container( + child: Column( + children: [ + Center( + child: OxenTextField( hintText: S.of(context).account, - focusedBorder: UnderlineInputBorder( - borderSide: - BorderSide(color: OxenPalette.teal, width: 2.0)), - enabledBorder: UnderlineInputBorder( - borderSide: BorderSide( - color: Theme.of(context).focusColor, width: 1.0))), - controller: _textController, - validator: (value) { - accountListStore.validateAccountName(value); - return accountListStore.errorMessage; - }, - ), - )), - Observer( - builder: (_) => LoadingPrimaryButton( - onPressed: () async { - if (!_formKey.currentState.validate()) { - return; - } + controller: _textController, + validator: (value) { + accountListStore.validateAccountName(value); + return accountListStore.errorMessage; + }, + ), + ), + ], + ), + )), + bottomSection: Observer( + builder: (_) => LoadingPrimaryButton( + onPressed: () async { + if (!_formKey.currentState.validate()) { + return; + } - if (widget.account != null) { - await accountListStore.renameAccount( - index: widget.account.id, - label: _textController.text); - } else { - await accountListStore.addAccount( - label: _textController.text); - } - Navigator.of(context).pop(_textController.text); - }, - text: - widget.account != null ? 'Rename' : S.of(context).add, - color: Theme.of(context) - .primaryTextTheme - .button - .backgroundColor, - borderColor: Theme.of(context) - .primaryTextTheme - .button - .decorationColor, - isLoading: accountListStore.isAccountCreating, - )) - ], - ), - ), + if (widget.account != null) { + await accountListStore.renameAccount( + index: widget.account.id, label: _textController.text); + } else { + await accountListStore.addAccount( + label: _textController.text); + } + Navigator.of(context).pop(_textController.text); + }, + text: widget.account != null ? 'Rename' : S.of(context).add, + color: + Theme.of(context).primaryTextTheme.button.backgroundColor, + borderColor: + Theme.of(context).primaryTextTheme.button.decorationColor, + isLoading: accountListStore.isAccountCreating, + )), ); } } diff --git a/lib/src/screens/address_book/address_book_page.dart b/lib/src/screens/address_book/address_book_page.dart index 38bdbc84..1850e0f4 100644 --- a/lib/src/screens/address_book/address_book_page.dart +++ b/lib/src/screens/address_book/address_book_page.dart @@ -1,15 +1,16 @@ -import 'package:provider/provider.dart'; -import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; -import 'package:oxen_wallet/routes.dart'; -import 'package:oxen_wallet/palette.dart'; import 'package:oxen_wallet/generated/l10n.dart'; +import 'package:oxen_wallet/palette.dart'; +import 'package:oxen_wallet/routes.dart'; import 'package:oxen_wallet/src/domain/common/crypto_currency.dart'; -import 'package:oxen_wallet/src/stores/address_book/address_book_store.dart'; import 'package:oxen_wallet/src/screens/base_page.dart'; +import 'package:oxen_wallet/src/stores/address_book/address_book_store.dart'; +import 'package:oxen_wallet/src/widgets/oxen_dialog.dart'; +import 'package:provider/provider.dart'; class AddressBookPage extends BasePage { AddressBookPage({this.isEditable = true}); @@ -27,9 +28,7 @@ class AddressBookPage extends BasePage { @override Widget trailing(BuildContext context) { - if (!isEditable) { - return null; - } + if (!isEditable) return null; final addressBookStore = Provider.of(context); @@ -118,7 +117,8 @@ class AddressBookPage extends BasePage { contact.name, style: TextStyle( fontSize: 16.0, - color: Theme.of(context).primaryTextTheme.headline6.color), + color: + Theme.of(context).primaryTextTheme.headline6.color), ), ); @@ -174,7 +174,7 @@ class AddressBookPage extends BasePage { Color _getCurrencyBackgroundColor(CryptoCurrency currency) { Color color; switch (currency) { - case CryptoCurrency.xmr: + case CryptoCurrency.oxen: color = OxenPalette.tealWithOpacity; break; case CryptoCurrency.ada: @@ -240,54 +240,31 @@ class AddressBookPage extends BasePage { } Future showAlertDialog(BuildContext context) async { - return await showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: const Text( - 'Remove contact', - textAlign: TextAlign.center, - ), - content: const Text( - 'Are you sure that you want to remove selected contact?', - textAlign: TextAlign.center, - ), - actions: [ - FlatButton( - onPressed: () => Navigator.of(context).pop( false), - child: const Text('Cancel')), - FlatButton( - onPressed: () => Navigator.of(context).pop(true), - child: const Text('Remove')), - ], - ); + var result = false; + await showConfirmOxenDialog(context, 'Remove contact', + 'Are you sure that you want to remove selected contact?', + onDismiss: (context) => Navigator.pop(context, false), + onConfirm: (context) { + result = true; + Navigator.pop(context, true); + return true; }); + return result; } Future showNameAndAddressDialog( BuildContext context, String name, String address) async { - return await showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: Text( - name, - textAlign: TextAlign.center, - style: TextStyle(fontWeight: FontWeight.bold), - ), - content: Text( - address, - textAlign: TextAlign.center, - ), - actions: [ - FlatButton( - onPressed: () => Navigator.of(context).pop(false), - child: Text('Cancel')), - FlatButton( - onPressed: () => Navigator.of(context).pop(true), - child: Text('Copy')) - ], - ); - }); + var result = false; + await showSimpleOxenDialog( + context, + name, + address, + buttonText: 'Copy', + onPressed: (context) { + result = true; + Navigator.of(context).pop(true); + }, + ); + return result; } } diff --git a/lib/src/screens/address_book/contact_page.dart b/lib/src/screens/address_book/contact_page.dart index 9581953a..6118b963 100644 --- a/lib/src/screens/address_book/contact_page.dart +++ b/lib/src/screens/address_book/contact_page.dart @@ -1,15 +1,16 @@ -import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; -import 'package:provider/provider.dart'; +import 'package:flutter/material.dart'; import 'package:oxen_wallet/generated/l10n.dart'; -import 'package:oxen_wallet/src/domain/common/crypto_currency.dart'; import 'package:oxen_wallet/src/domain/common/contact.dart'; -import 'package:oxen_wallet/src/stores/address_book/address_book_store.dart'; +import 'package:oxen_wallet/src/domain/common/crypto_currency.dart'; import 'package:oxen_wallet/src/screens/base_page.dart'; -import 'package:oxen_wallet/src/widgets/primary_button.dart'; +import 'package:oxen_wallet/src/stores/address_book/address_book_store.dart'; import 'package:oxen_wallet/src/widgets/address_text_field.dart'; +import 'package:oxen_wallet/src/widgets/oxen_dialog.dart'; +import 'package:oxen_wallet/src/widgets/oxen_text_field.dart'; +import 'package:oxen_wallet/src/widgets/primary_button.dart'; import 'package:oxen_wallet/src/widgets/scollable_with_bottom_section.dart'; -import 'package:oxen_wallet/palette.dart'; +import 'package:provider/provider.dart'; class ContactPage extends BasePage { ContactPage({this.contact}); @@ -38,17 +39,17 @@ class ContactFormState extends State { final _currencyTypeController = TextEditingController(); final _addressController = TextEditingController(); - CryptoCurrency _selectectCrypto = CryptoCurrency.oxen; + CryptoCurrency _selectedCrypto = CryptoCurrency.oxen; @override void initState() { super.initState(); if (widget.contact == null) { - _currencyTypeController.text = _selectectCrypto.toString(); + _currencyTypeController.text = _selectedCrypto.toString(); } else { - _selectectCrypto = widget.contact.type; + _selectedCrypto = widget.contact.type; _contactNameController.text = widget.contact.name; - _currencyTypeController.text = _selectectCrypto.toString(); + _currencyTypeController.text = _selectedCrypto.toString(); _addressController.text = widget.contact.address; } } @@ -68,47 +69,63 @@ class ContactFormState extends State { await showDialog( context: context, builder: (BuildContext context) { - return AlertDialog( - title: Text(S.of(context).please_select), - backgroundColor: Theme.of(context).backgroundColor, - content: Container( - height: 150.0, - child: CupertinoPicker( - backgroundColor: Theme.of(context).backgroundColor, - itemExtent: 45.0, - onSelectedItemChanged: (int index) { - selectedCurrency = CryptoCurrency.all[index]; - currencyType = CryptoCurrency.all[index].toString(); - }, - children: - List.generate(CryptoCurrency.all.length, (int index) { - return Center( - child: Text( - CryptoCurrency.all[index].toString(), + return OxenDialog( + body: Container( + padding: EdgeInsets.all(30), + child: Column( + children: [ + Padding( + padding: EdgeInsets.all(15), + child: Text(S.of(context).please_select, + textAlign: TextAlign.center, style: TextStyle( + fontSize: 18, + decoration: TextDecoration.none, color: Theme.of(context) .primaryTextTheme .caption - .color), - ), - ); - })), - ), - actions: [ - FlatButton( - onPressed: () { - Navigator.pop(context); - }, - child: Text(S.of(context).cancel)), - FlatButton( + .color))), + Padding( + padding: EdgeInsets.only(top: 15, bottom: 30), + child: Container( + height: 150.0, + child: CupertinoPicker( + backgroundColor: Theme.of(context).backgroundColor, + itemExtent: 45.0, + onSelectedItemChanged: (int index) { + selectedCurrency = CryptoCurrency.all[index]; + currencyType = CryptoCurrency.all[index].toString(); + }, + children: List.generate(CryptoCurrency.all.length, + (int index) { + return Center( + child: Text( + CryptoCurrency.all[index].toString(), + style: TextStyle( + color: Theme.of(context) + .primaryTextTheme + .caption + .color), + ), + ); + })), + ), + ), + PrimaryButton( + text: S.of(context).ok, + color: + Theme.of(context).primaryTextTheme.button.backgroundColor, + borderColor: + Theme.of(context).primaryTextTheme.button.decorationColor, onPressed: () { - _selectectCrypto = selectedCurrency; + _selectedCrypto = selectedCurrency; _currencyTypeController.text = currencyType; Navigator.of(context).pop(); }, - child: Text(S.of(context).ok)) - ], - ); + ) + ], + ), + )); }); } @@ -122,19 +139,8 @@ class ContactFormState extends State { child: Column( mainAxisSize: MainAxisSize.min, children: [ - TextFormField( - style: TextStyle( - fontSize: 14.0, - color: Theme.of(context).primaryTextTheme.headline5.color), - decoration: InputDecoration( - hintStyle: TextStyle(color: Theme.of(context).hintColor), - hintText: S.of(context).contact_name, - focusedBorder: UnderlineInputBorder( - borderSide: - BorderSide(color: OxenPalette.teal, width: 2.0)), - enabledBorder: UnderlineInputBorder( - borderSide: BorderSide( - color: Theme.of(context).focusColor, width: 1.0))), + OxenTextField( + hintText: S.of(context).contact_name, controller: _contactNameController, validator: (value) { addressBookStore.validateContactName(value); @@ -146,21 +152,7 @@ class ContactFormState extends State { child: InkWell( onTap: () => _setCurrencyType(context), child: IgnorePointer( - child: TextFormField( - style: TextStyle( - fontSize: 14.0, - color: Theme.of(context) - .primaryTextTheme - .headline5 - .color), - decoration: InputDecoration( - focusedBorder: UnderlineInputBorder( - borderSide: BorderSide( - color: OxenPalette.teal, width: 2.0)), - enabledBorder: UnderlineInputBorder( - borderSide: BorderSide( - color: Theme.of(context).focusColor, - width: 1.0))), + child: OxenTextField( controller: _currencyTypeController, ), ), @@ -172,7 +164,7 @@ class ContactFormState extends State { options: [AddressTextFieldOption.qrCode], validator: (value) { addressBookStore.validateAddress(value, - cryptoCurrency: _selectectCrypto); + cryptoCurrency: _selectedCrypto); return addressBookStore.errorMessage; }, ) @@ -185,10 +177,9 @@ class ContactFormState extends State { child: PrimaryButton( onPressed: () { setState(() { - _selectectCrypto = CryptoCurrency.xmr; + _selectedCrypto = CryptoCurrency.xmr; _contactNameController.text = ''; - _currencyTypeController.text = - _selectectCrypto.toString(); + _currencyTypeController.text = _selectedCrypto.toString(); _addressController.text = ''; }); }, @@ -202,23 +193,21 @@ class ContactFormState extends State { Expanded( child: PrimaryButton( onPressed: () async { - if (!_formKey.currentState.validate()) { - return; - } + if (!_formKey.currentState.validate()) return; try { if (widget.contact == null) { final newContact = Contact( name: _contactNameController.text, address: _addressController.text, - type: _selectectCrypto); + type: _selectedCrypto); await addressBookStore.add(contact: newContact); } else { widget.contact.name = _contactNameController.text; widget.contact.address = _addressController.text; widget.contact - .updateCryptoCurrency(currency: _selectectCrypto); + .updateCryptoCurrency(currency: _selectedCrypto); await addressBookStore.update( contact: widget.contact); diff --git a/lib/src/screens/auth/auth_page.dart b/lib/src/screens/auth/auth_page.dart index 616bb623..877bdba0 100644 --- a/lib/src/screens/auth/auth_page.dart +++ b/lib/src/screens/auth/auth_page.dart @@ -125,7 +125,7 @@ class AuthPageState extends State { backgroundColor: Theme.of(context).backgroundColor, border: null, ), - resizeToAvoidBottomPadding: false, + resizeToAvoidBottomInset: false, body: PinCode( (pin, _) => authStore.auth( password: pin.fold('', (ac, val) => ac + '$val')), diff --git a/lib/src/screens/auth/create_login_page.dart b/lib/src/screens/auth/create_login_page.dart index dd215581..daa7e527 100644 --- a/lib/src/screens/auth/create_login_page.dart +++ b/lib/src/screens/auth/create_login_page.dart @@ -1,12 +1,12 @@ import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import 'package:shared_preferences/shared_preferences.dart'; import 'package:oxen_wallet/src/domain/services/user_service.dart'; import 'package:oxen_wallet/src/domain/services/wallet_list_service.dart'; import 'package:oxen_wallet/src/domain/services/wallet_service.dart'; import 'package:oxen_wallet/src/screens/auth/auth_page.dart'; import 'package:oxen_wallet/src/stores/auth/auth_store.dart'; import 'package:oxen_wallet/src/stores/authentication/authentication_store.dart'; +import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; Widget createLoginPage( {@required SharedPreferences sharedPreferences, diff --git a/lib/src/screens/base_page.dart b/lib/src/screens/base_page.dart index c02fb6d1..39962b62 100644 --- a/lib/src/screens/base_page.dart +++ b/lib/src/screens/base_page.dart @@ -14,7 +14,7 @@ abstract class BasePage extends StatelessWidget { Color get backgroundColor => Colors.white; - bool get resizeToAvoidBottomPadding => true; + bool get resizeToAvoidBottomInset => true; AppBarStyle get appBarStyle => AppBarStyle.regular; @@ -108,7 +108,7 @@ abstract class BasePage extends StatelessWidget { return Scaffold( backgroundColor: _isDarkTheme ? Theme.of(context).backgroundColor : backgroundColor, - resizeToAvoidBottomPadding: resizeToAvoidBottomPadding, + resizeToAvoidBottomInset: resizeToAvoidBottomInset, appBar: appBar(context), body: SafeArea(child: body(context)), floatingActionButton: floatingActionButton(context), diff --git a/lib/src/screens/dashboard/wallet_menu.dart b/lib/src/screens/dashboard/wallet_menu.dart index 1c9f9b1c..8138e3bb 100644 --- a/lib/src/screens/dashboard/wallet_menu.dart +++ b/lib/src/screens/dashboard/wallet_menu.dart @@ -3,6 +3,7 @@ import 'package:oxen_wallet/generated/l10n.dart'; import 'package:oxen_wallet/routes.dart'; import 'package:oxen_wallet/src/stores/balance/balance_store.dart'; import 'package:oxen_wallet/src/stores/wallet/wallet_store.dart'; +import 'package:oxen_wallet/src/widgets/oxen_dialog.dart'; import 'package:provider/provider.dart'; class WalletMenu { @@ -25,35 +26,19 @@ class WalletMenu { Navigator.of(context).pushNamed(Routes.rescan); break; case 2: - Provider.of(context).updateFiatBalance(); + context.read().updateFiatBalance(); break; } } Future _presentReconnectAlert(BuildContext context) async { - final walletStore = Provider.of(context); + final walletStore = context.read(); - await showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: Text( - S.of(context).reconnection, - textAlign: TextAlign.center, - ), - content: Text(S.of(context).reconnect_alert_text), - actions: [ - FlatButton( - onPressed: () => Navigator.of(context).pop(), - child: Text(S.of(context).cancel)), - FlatButton( - onPressed: () { - walletStore.reconnect(); - Navigator.of(context).pop(); - }, - child: Text(S.of(context).ok)) - ], - ); - }); + await showSimpleOxenDialog( + context, S.of(context).reconnection, S.of(context).reconnect_alert_text, + onPressed: (context) { + walletStore.reconnect(); + Navigator.of(context).pop(); + }); } } diff --git a/lib/src/screens/faq/faq_page.dart b/lib/src/screens/faq/faq_page.dart index 98747621..a95c1d6c 100644 --- a/lib/src/screens/faq/faq_page.dart +++ b/lib/src/screens/faq/faq_page.dart @@ -49,7 +49,7 @@ class FaqPage extends BasePage { } String getFaqPath(BuildContext context) { - final settingsStore = Provider.of(context); + final settingsStore = context.read(); switch (settingsStore.languageCode) { case 'en': diff --git a/lib/src/screens/nodes/new_node_page.dart b/lib/src/screens/nodes/new_node_page.dart index 111a499d..8b72bd2d 100644 --- a/lib/src/screens/nodes/new_node_page.dart +++ b/lib/src/screens/nodes/new_node_page.dart @@ -1,11 +1,12 @@ -import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; -import 'package:provider/provider.dart'; -import 'package:oxen_wallet/palette.dart'; +import 'package:flutter/material.dart'; import 'package:oxen_wallet/generated/l10n.dart'; +import 'package:oxen_wallet/src/screens/base_page.dart'; import 'package:oxen_wallet/src/stores/node_list/node_list_store.dart'; +import 'package:oxen_wallet/src/widgets/oxen_text_field.dart'; import 'package:oxen_wallet/src/widgets/primary_button.dart'; -import 'package:oxen_wallet/src/screens/base_page.dart'; +import 'package:oxen_wallet/src/widgets/scollable_with_bottom_section.dart'; +import 'package:provider/provider.dart'; class NewNodePage extends BasePage { @override @@ -40,176 +41,97 @@ class NewNodeFormState extends State { Widget build(BuildContext context) { final nodeList = Provider.of(context); - return Form( - key: _formKey, - child: Column( + return ScrollableWithBottomSection( + contentPadding: EdgeInsets.all(0), + content: Form( + key: _formKey, + child: Container( + padding: + EdgeInsets.only(left: 20, right: 20, top: 10, bottom: 30), + child: Column( + children: [ + OxenTextField( + hintText: S.of(context).node_address, + controller: _nodeAddressController, + validator: (value) { + nodeList.validateNodeAddress(value); + return nodeList.errorMessage; + }, + ), + Padding( + padding: EdgeInsets.only(top: 20), + child: OxenTextField( + hintText: S.of(context).node_port, + controller: _nodePortController, + keyboardType: TextInputType.numberWithOptions( + signed: false, decimal: false), + validator: (value) { + nodeList.validateNodePort(value); + return nodeList.errorMessage; + }, + )), + Padding( + padding: EdgeInsets.only(top: 20), + child: OxenTextField( + hintText: S.of(context).login, + controller: _loginController, + validator: (value) => null, + )), + Padding( + padding: EdgeInsets.only(top: 20), + child: OxenTextField( + hintText: S.of(context).password, + controller: _passwordController, + validator: (value) => null, + )), + ], + ))), + bottomSection: Container( + child: Row( children: [ - Expanded( + Flexible( child: Container( - padding: EdgeInsets.only(left: 38.0, right: 38.0, top: 0), - child: Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Row( - children: [ - Expanded( - child: TextFormField( - style: TextStyle(fontSize: 14.0), - decoration: InputDecoration( - hintStyle: - TextStyle(color: Palette.wildDarkBlue), - hintText: S.of(context).node_address, - focusedBorder: UnderlineInputBorder( - borderSide: BorderSide( - color: OxenPalette.teal, width: 2.0)), - enabledBorder: UnderlineInputBorder( - borderSide: BorderSide( - color: Theme.of(context).focusColor, - width: 1.0))), - controller: _nodeAddressController, - validator: (value) { - nodeList.validateNodeAddress(value); - return nodeList.errorMessage; - }, - ), - ) - ], - ), - SizedBox(height: 10.0), - Row( - children: [ - Expanded( - child: TextFormField( - style: TextStyle(fontSize: 14.0), - keyboardType: TextInputType.numberWithOptions( - signed: false, decimal: false), - decoration: InputDecoration( - hintStyle: - TextStyle(color: Palette.wildDarkBlue), - hintText: S.of(context).node_port, - focusedBorder: UnderlineInputBorder( - borderSide: BorderSide( - color: OxenPalette.teal, width: 2.0)), - enabledBorder: UnderlineInputBorder( - borderSide: BorderSide( - color: Theme.of(context).focusColor, - width: 1.0))), - controller: _nodePortController, - validator: (value) { - nodeList.validateNodePort(value); - return nodeList.errorMessage; - }, - ), - ) - ], - ), - SizedBox(height: 10.0), - Row( - children: [ - Expanded( - child: TextFormField( - style: TextStyle(fontSize: 14.0), - decoration: InputDecoration( - hintStyle: - TextStyle(color: Palette.wildDarkBlue), - hintText: S.of(context).login, - focusedBorder: UnderlineInputBorder( - borderSide: BorderSide( - color: OxenPalette.teal, width: 2.0)), - enabledBorder: UnderlineInputBorder( - borderSide: BorderSide( - color: Theme.of(context).focusColor, - width: 1.0))), - controller: _loginController, - validator: (value) => null, - ), - ) - ], - ), - SizedBox(height: 10.0), - Row( - children: [ - Expanded( - child: TextFormField( - style: TextStyle(fontSize: 14.0), - decoration: InputDecoration( - hintStyle: - TextStyle(color: Palette.wildDarkBlue), - hintText: S.of(context).password, - focusedBorder: UnderlineInputBorder( - borderSide: BorderSide( - color: OxenPalette.teal, width: 2.0)), - enabledBorder: UnderlineInputBorder( - borderSide: BorderSide( - color: Theme.of(context).focusColor, - width: 1.0))), - controller: _passwordController, - validator: (value) => null, - ), - ) - ], - ) - ], - ), - ), + padding: EdgeInsets.only(right: 8.0), + child: PrimaryButton( + onPressed: () { + _nodeAddressController.text = ''; + _nodePortController.text = ''; + _loginController.text = ''; + _passwordController.text = ''; + }, + text: S.of(context).reset, + color: + Theme.of(context).accentTextTheme.button.backgroundColor, + borderColor: + Theme.of(context).accentTextTheme.button.decorationColor), )), - Container( - padding: EdgeInsets.only(bottom: 20.0, left: 20.0, right: 20.0), - child: Row( - children: [ - Flexible( - child: Container( - padding: EdgeInsets.only(right: 8.0), - child: PrimaryButton( - onPressed: () { - _nodeAddressController.text = ''; - _nodePortController.text = ''; - _loginController.text = ''; - _passwordController.text = ''; - }, - text: S.of(context).reset, - color: Theme.of(context) - .accentTextTheme - .button - .backgroundColor, - borderColor: Theme.of(context) - .accentTextTheme - .button - .decorationColor), - )), - Flexible( - child: Container( - padding: EdgeInsets.only(left: 8.0), - child: PrimaryButton( - onPressed: () async { - if (!_formKey.currentState.validate()) { - return; - } + Flexible( + child: Container( + padding: EdgeInsets.only(left: 8.0), + child: PrimaryButton( + onPressed: () async { + if (!_formKey.currentState.validate()) { + return; + } - await nodeList.addNode( - address: _nodeAddressController.text, - port: _nodePortController.text, - login: _loginController.text, - password: _passwordController.text); + await nodeList.addNode( + address: _nodeAddressController.text, + port: _nodePortController.text, + login: _loginController.text, + password: _passwordController.text); - Navigator.of(context).pop(); - }, - text: S.of(context).save, - color: Theme.of(context) - .primaryTextTheme - .button - .backgroundColor, - borderColor: Theme.of(context) - .primaryTextTheme - .button - .decorationColor, - ), - )), - ], + Navigator.of(context).pop(); + }, + text: S.of(context).save, + color: + Theme.of(context).primaryTextTheme.button.backgroundColor, + borderColor: + Theme.of(context).primaryTextTheme.button.decorationColor, ), - ) + )), ], - )); + ), + ), + ); } } diff --git a/lib/src/screens/nodes/nodes_list_page.dart b/lib/src/screens/nodes/nodes_list_page.dart index 20b74dab..d64b9cfc 100644 --- a/lib/src/screens/nodes/nodes_list_page.dart +++ b/lib/src/screens/nodes/nodes_list_page.dart @@ -8,6 +8,7 @@ import 'package:oxen_wallet/src/screens/base_page.dart'; import 'package:oxen_wallet/src/screens/nodes/node_indicator.dart'; import 'package:oxen_wallet/src/stores/node_list/node_list_store.dart'; import 'package:oxen_wallet/src/stores/settings/settings_store.dart'; +import 'package:oxen_wallet/src/widgets/oxen_dialog.dart'; import 'package:provider/provider.dart'; class NodeListPage extends BasePage { @@ -28,34 +29,16 @@ class NodeListPage extends BasePage { minWidth: double.minPositive, child: FlatButton( onPressed: () async { - await showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: Text( - S.of(context).node_reset_settings_title, - textAlign: TextAlign.center, - ), - content: Text( - S.of(context).nodes_list_reset_to_default_message, - textAlign: TextAlign.center, - ), - actions: [ - FlatButton( - onPressed: () { - Navigator.pop(context); - }, - child: Text(S.of(context).cancel)), - FlatButton( - onPressed: () async { - Navigator.pop(context); - await nodeList.reset(); - await settings.setCurrentNodeToDefault(); - }, - child: Text(S.of(context).reset)) - ], - ); - }); + await showConfirmOxenDialog( + context, + S.of(context).node_reset_settings_title, + S.of(context).nodes_list_reset_to_default_message, + onFutureConfirm: (context) async { + Navigator.pop(context); + await nodeList.reset(); + await settings.setCurrentNodeToDefault(); + return true; + }); }, child: Text( S.of(context).reset, @@ -149,31 +132,12 @@ class NodeListPageBodyState extends State { }), onTap: () async { if (!isCurrent) { - await showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - content: Text( - S - .of(context) - .change_current_node(node.uri), - textAlign: TextAlign.center, - ), - actions: [ - FlatButton( - onPressed: () => - Navigator.pop(context), - child: Text(S.of(context).cancel)), - FlatButton( - onPressed: () async { - Navigator.of(context).pop(); - await settings.setCurrentNode( - node: node); - }, - child: Text(S.of(context).change)), - ], - ); - }); + await showSimpleOxenDialog(context, '', + S.of(context).change_current_node(node.uri), + onPressed: (context) async { + Navigator.of(context).pop(); + await settings.setCurrentNode(node: node); + }); } }, )); @@ -183,30 +147,19 @@ class NodeListPageBodyState extends State { : Dismissible( key: Key('${node.key}'), confirmDismiss: (direction) async { - return await showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: Text( - S.of(context).remove_node, - textAlign: TextAlign.center, - ), - content: Text( - S.of(context).remove_node_message, - textAlign: TextAlign.center, - ), - actions: [ - FlatButton( - onPressed: () => - Navigator.pop(context, false), - child: Text(S.of(context).cancel)), - FlatButton( - onPressed: () => - Navigator.pop(context, true), - child: Text(S.of(context).remove)), - ], - ); + var result = false; + await showConfirmOxenDialog( + context, + S.of(context).remove_node, + S.of(context).remove_node_message, + onDismiss: (context) => + Navigator.pop(context, false), + onConfirm: (context) { + result = true; + Navigator.pop(context, true); + return true; }); + return result; }, onDismissed: (direction) async => await nodeList.remove(node: node), diff --git a/lib/src/screens/pin_code/pin_code.dart b/lib/src/screens/pin_code/pin_code.dart index b7c6f8fe..afe11ea7 100644 --- a/lib/src/screens/pin_code/pin_code.dart +++ b/lib/src/screens/pin_code/pin_code.dart @@ -54,7 +54,7 @@ class PinCodeState extends State { } void setDefaultPinLength() { - final settingsStore = Provider.of(context); + final settingsStore = context.read(); pinLength = settingsStore.defaultPinLength; changePinLength(pinLength); diff --git a/lib/src/screens/profile/profile_page.dart b/lib/src/screens/profile/profile_page.dart index 8af5fd6d..7edbad99 100644 --- a/lib/src/screens/profile/profile_page.dart +++ b/lib/src/screens/profile/profile_page.dart @@ -57,15 +57,12 @@ class ProfilePageBodyState extends State { onTap: () => Navigator.of(context).pushNamed(Routes.settings)), NavListHeader(title: S.current.wallet_menu), - /* - TODO: Waiting for Implementation NavListArrow( leading: Icon(Icons.attach_money_rounded, color: Theme.of(context).primaryTextTheme.headline6.color), - text: S.current.stakes, + text: S.current.title_stakes, onTap: () => Navigator.of(context).pushNamed(Routes.stake)), - */ NavListArrow( leading: Icon(Icons.contacts_rounded, color: Theme.of(context).primaryTextTheme.headline6.color), diff --git a/lib/src/screens/receive/receive_page.dart b/lib/src/screens/receive/receive_page.dart index a53a0615..b742a778 100644 --- a/lib/src/screens/receive/receive_page.dart +++ b/lib/src/screens/receive/receive_page.dart @@ -1,16 +1,17 @@ -import 'package:flutter/material.dart'; +import 'package:esys_flutter_share/esys_flutter_share.dart'; import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; -import 'package:esys_flutter_share/esys_flutter_share.dart'; -import 'package:provider/provider.dart'; -import 'package:oxen_wallet/routes.dart'; -import 'package:oxen_wallet/palette.dart'; import 'package:oxen_wallet/generated/l10n.dart'; +import 'package:oxen_wallet/palette.dart'; +import 'package:oxen_wallet/routes.dart'; +import 'package:oxen_wallet/src/screens/base_page.dart'; +import 'package:oxen_wallet/src/screens/receive/qr_image.dart'; import 'package:oxen_wallet/src/stores/subaddress_list/subaddress_list_store.dart'; import 'package:oxen_wallet/src/stores/wallet/wallet_store.dart'; -import 'package:oxen_wallet/src/screens/receive/qr_image.dart'; -import 'package:oxen_wallet/src/screens/base_page.dart'; +import 'package:oxen_wallet/src/widgets/oxen_text_field.dart'; +import 'package:provider/provider.dart'; class ReceivePage extends BasePage { @override @@ -79,202 +80,194 @@ class ReceiveBodyState extends State { }); return SafeArea( - child: SingleChildScrollView( - child: Column( - children: [ - Container( - padding: EdgeInsets.all(35.0), - color: Theme.of(context).backgroundColor, - child: Column( - children: [ - Observer(builder: (_) { - return Row( - children: [ - Spacer( - flex: 1, - ), - Flexible( - flex: 2, - child: AspectRatio( - aspectRatio: 1.0, - child: Container( - padding: EdgeInsets.all(5), - color: Colors.white, - child: QrImage( - data: walletStore.subaddress.address + - walletStore.amountValue, - backgroundColor: Colors.transparent, + child: GestureDetector( + onTap: () { + FocusScope.of(context).unfocus(); + }, + child: SingleChildScrollView( + child: Column( + children: [ + Container( + padding: EdgeInsets.all(35.0), + color: Theme.of(context).backgroundColor, + child: Column( + children: [ + Observer(builder: (_) { + return Row( + children: [ + Spacer( + flex: 1, ), - ), - )), - Spacer( - flex: 1, - ) - ], - ); - }), - Observer(builder: (_) { - return Row( + Flexible( + flex: 2, + child: AspectRatio( + aspectRatio: 1.0, + child: Container( + padding: EdgeInsets.all(5), + color: Colors.white, + child: QrImage( + data: walletStore.subaddress.address + + walletStore.amountValue, + backgroundColor: Colors.transparent, + ), + ), + )), + Spacer( + flex: 1, + ) + ], + ); + }), + Observer(builder: (_) { + return Row( + children: [ + Expanded( + child: Container( + padding: EdgeInsets.all(20.0), + child: Center( + child: GestureDetector( + onTap: () { + Clipboard.setData(ClipboardData( + text: walletStore.subaddress.address)); + Scaffold.of(context).showSnackBar(SnackBar( + content: Text( + S.of(context).copied_to_clipboard, + style: TextStyle(color: Colors.white), + ), + backgroundColor: Colors.green, + )); + }, + child: Text( + walletStore.subaddress.address, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 14.0, + fontWeight: FontWeight.w600, + color: Theme.of(context) + .primaryTextTheme + .headline6 + .color), + ), + ), + ), + )) + ], + ); + }), + Row( + children: [ + Expanded( + child: Form( + key: _formKey, + child: OxenTextField( + keyboardType: + TextInputType.numberWithOptions( + decimal: true), + inputFormatters: [ + FilteringTextInputFormatter.deny( + RegExp('[\\-|\\ |\\,]')) + ], + hintText: S.of(context).amount, + validator: (value) { + walletStore.validateAmount(value); + return walletStore.errorMessage; + }, + controller: amountController, + ))) + ], + ) + ], + ), + ), + Row( children: [ Expanded( child: Container( - padding: EdgeInsets.all(20.0), - child: Center( - child: GestureDetector( - onTap: () { - Clipboard.setData(ClipboardData( - text: walletStore.subaddress.address)); - Scaffold.of(context).showSnackBar(SnackBar( - content: Text( - S.of(context).copied_to_clipboard, - style: TextStyle(color: Colors.white), + color: Theme.of(context).accentTextTheme.headline5.color, + child: Column( + children: [ + ListTile( + title: Text( + S.of(context).subaddresses, + style: TextStyle( + fontSize: 16.0, + color: Theme.of(context) + .primaryTextTheme + .headline5 + .color), + ), + trailing: Container( + width: 28.0, + height: 28.0, + decoration: BoxDecoration( + color: Theme.of(context).selectedRowColor, + shape: BoxShape.circle), + child: InkWell( + onTap: () => Navigator.of(context) + .pushNamed(Routes.newSubaddress), + borderRadius: + BorderRadius.all(Radius.circular(14.0)), + child: Icon( + Icons.add, + color: OxenPalette.teal, + size: 22.0, + ), ), - backgroundColor: Colors.green, - )); - }, - child: Text( - walletStore.subaddress.address, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 14.0, - fontWeight: FontWeight.w600, - color: Theme.of(context) - .primaryTextTheme - .headline6 - .color), + ), ), - ), + Divider( + color: Theme.of(context).dividerTheme.color, + height: 1.0, + ) + ], ), )) ], - ); - }), - Row( - children: [ - Expanded( - child: Form( - key: _formKey, - child: TextFormField( - keyboardType: - TextInputType.numberWithOptions(decimal: true), - inputFormatters: [ - FilteringTextInputFormatter.deny( - RegExp('[\\-|\\ |\\,]') - ) - ], - style: TextStyle( - fontSize: 14.0, - ), - decoration: InputDecoration( - hintStyle: TextStyle( - color: Theme.of(context).hintColor), - hintText: S.of(context).amount, - focusedBorder: UnderlineInputBorder( - borderSide: BorderSide( - color: OxenPalette.teal, width: 2.0)), - enabledBorder: UnderlineInputBorder( - borderSide: BorderSide( - color: Theme.of(context).focusColor, - width: 1.0))), - validator: (value) { - walletStore.validateAmount(value); - return walletStore.errorMessage; - }, - autovalidate: true, - controller: amountController, - ))) - ], - ) - ], - ), - ), - Row( - children: [ - Expanded( - child: Container( - color: Theme.of(context).accentTextTheme.headline5.color, - child: Column( - children: [ - ListTile( - title: Text( - S.of(context).subaddresses, - style: TextStyle( - fontSize: 16.0, - color: Theme.of(context) - .primaryTextTheme - .headline5 - .color), - ), - trailing: Container( - width: 28.0, - height: 28.0, - decoration: BoxDecoration( - color: Theme.of(context).selectedRowColor, - shape: BoxShape.circle), - child: InkWell( - onTap: () => Navigator.of(context) - .pushNamed(Routes.newSubaddress), - borderRadius: BorderRadius.all(Radius.circular(14.0)), - child: Icon( - Icons.add, - color: OxenPalette.teal, - size: 22.0, - ), - ), - ), - ), - Divider( - color: Theme.of(context).dividerTheme.color, - height: 1.0, - ) - ], - ), - )) - ], - ), - Observer(builder: (_) { - return ListView.separated( - shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), - itemCount: subaddressListStore.subaddresses.length, - separatorBuilder: (context, i) { - return Divider( - color: Theme.of(context).dividerTheme.color, - height: 1.0, - ); - }, - itemBuilder: (context, i) { - return Observer(builder: (_) { - final subaddress = subaddressListStore.subaddresses[i]; - final isCurrent = - walletStore.subaddress.address == subaddress.address; - final label = subaddress.label.isNotEmpty - ? subaddress.label - : subaddress.address; + ), + Observer(builder: (_) { + return ListView.separated( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: subaddressListStore.subaddresses.length, + separatorBuilder: (context, i) { + return Divider( + color: Theme.of(context).dividerTheme.color, + height: 1.0, + ); + }, + itemBuilder: (context, i) { + return Observer(builder: (_) { + final subaddress = + subaddressListStore.subaddresses[i]; + final isCurrent = walletStore.subaddress.address == + subaddress.address; + final label = subaddress.label.isNotEmpty + ? subaddress.label + : subaddress.address; - return InkWell( - onTap: () => walletStore.setSubaddress(subaddress), - child: Container( - color: isCurrent ? currentColor : notCurrentColor, - child: Column(children: [ - ListTile( - title: Text( - label, - style: TextStyle( - fontSize: 16.0, - color: Theme.of(context) - .primaryTextTheme - .headline5 - .color), - ), - ) - ]), - ), - ); - }); - }); - }) - ], - ))); + return InkWell( + onTap: () => walletStore.setSubaddress(subaddress), + child: Container( + color: isCurrent ? currentColor : notCurrentColor, + child: Column(children: [ + ListTile( + title: Text( + label, + style: TextStyle( + fontSize: 16.0, + color: Theme.of(context) + .primaryTextTheme + .headline5 + .color), + ), + ) + ]), + ), + ); + }); + }); + }) + ], + )))); } } diff --git a/lib/src/screens/root/root.dart b/lib/src/screens/root/root.dart index 7165ddbd..6a4bddee 100644 --- a/lib/src/screens/root/root.dart +++ b/lib/src/screens/root/root.dart @@ -1,23 +1,23 @@ import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:hive/hive.dart'; -import 'package:provider/provider.dart'; -import 'package:shared_preferences/shared_preferences.dart'; import 'package:oxen_wallet/routes.dart'; -import 'package:oxen_wallet/src/stores/authentication/authentication_store.dart'; -import 'package:oxen_wallet/src/stores/price/price_store.dart'; -import 'package:oxen_wallet/src/stores/settings/settings_store.dart'; -import 'package:oxen_wallet/src/stores/wallet/wallet_store.dart'; import 'package:oxen_wallet/src/domain/common/qr_scanner.dart'; import 'package:oxen_wallet/src/domain/services/user_service.dart'; import 'package:oxen_wallet/src/domain/services/wallet_list_service.dart'; import 'package:oxen_wallet/src/domain/services/wallet_service.dart'; -import 'package:oxen_wallet/src/wallet/oxen/transaction/transaction_description.dart'; +import 'package:oxen_wallet/src/screens/auth/auth_page.dart'; import 'package:oxen_wallet/src/screens/auth/create_login_page.dart'; -import 'package:oxen_wallet/src/screens/seed/create_seed_page.dart'; import 'package:oxen_wallet/src/screens/dashboard/create_dashboard_page.dart'; -import 'package:oxen_wallet/src/screens/auth/auth_page.dart'; -import 'package:oxen_wallet/src/screens/welcome/create_welcome_page.dart'; +import 'package:oxen_wallet/src/screens/seed/create_seed_page.dart'; +import 'package:oxen_wallet/src/screens/welcome/welcome_page.dart'; +import 'package:oxen_wallet/src/stores/authentication/authentication_store.dart'; +import 'package:oxen_wallet/src/stores/price/price_store.dart'; +import 'package:oxen_wallet/src/stores/settings/settings_store.dart'; +import 'package:oxen_wallet/src/stores/wallet/wallet_store.dart'; +import 'package:oxen_wallet/src/wallet/oxen/transaction/transaction_description.dart'; +import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; class Root extends StatefulWidget { Root({Key key}) : super(key: key); @@ -43,9 +43,7 @@ class RootState extends State with WidgetsBindingObserver { void didChangeAppLifecycleState(AppLifecycleState state) { switch (state) { case AppLifecycleState.paused: - if (isQrScannerShown) { - return; - } + if (isQrScannerShown) return; if (!_isInactive && _authenticationStore.state == @@ -80,9 +78,7 @@ class RootState extends State with WidgetsBindingObserver { WidgetsBinding.instance.addPostFrameCallback((_) { Navigator.of(context).pushNamed(Routes.unlock, arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) { - if (!isAuthenticatedSuccessfully) { - return; - } + if (!isAuthenticatedSuccessfully) return; setState(() { _postFrameCallback = false; @@ -95,9 +91,7 @@ class RootState extends State with WidgetsBindingObserver { return Observer(builder: (_) { final state = _authenticationStore.state; - if (state == AuthenticationState.denied) { - return createWelcomePage(); - } + if (state == AuthenticationState.denied) return WelcomePage(); if (state == AuthenticationState.readyToLogin) { return createLoginPage( diff --git a/lib/src/screens/send/send_page.dart b/lib/src/screens/send/send_page.dart index f79c8d58..ae0e3b76 100644 --- a/lib/src/screens/send/send_page.dart +++ b/lib/src/screens/send/send_page.dart @@ -19,8 +19,9 @@ import 'package:oxen_wallet/src/stores/sync/sync_store.dart'; import 'package:oxen_wallet/src/stores/wallet/wallet_store.dart'; import 'package:oxen_wallet/src/wallet/oxen/calculate_estimated_fee.dart'; import 'package:oxen_wallet/src/widgets/address_text_field.dart'; -import 'package:oxen_wallet/src/widgets/primary_button.dart'; +import 'package:oxen_wallet/src/widgets/oxen_dialog.dart'; import 'package:oxen_wallet/src/widgets/scollable_with_bottom_section.dart'; +import 'package:oxen_wallet/src/widgets/slide_to_act.dart'; import 'package:provider/provider.dart'; class SendPage extends BasePage { @@ -31,7 +32,7 @@ class SendPage extends BasePage { bool get isModalBackButton => true; @override - bool get resizeToAvoidBottomPadding => false; + bool get resizeToAvoidBottomInset => false; @override Widget body(BuildContext context) => SendForm(); @@ -47,7 +48,7 @@ class SendFormState extends State { final _cryptoAmountController = TextEditingController(); final _fiatAmountController = TextEditingController(); - final _focusNode = FocusNode(); + final _focusNodeAddress = FocusNode(); bool _effectsInstalled = false; @@ -55,37 +56,26 @@ class SendFormState extends State { @override void initState() { - _focusNode.addListener(() { - if (!_focusNode.hasFocus && _addressController.text.isNotEmpty) { - getOpenaliasRecord(context); + _focusNodeAddress.addListener(() { + if (!_focusNodeAddress.hasFocus && _addressController.text.isNotEmpty) { + getOpenAliasRecord(context); } }); super.initState(); } - Future getOpenaliasRecord(BuildContext context) async { + Future getOpenAliasRecord(BuildContext context) async { final sendStore = Provider.of(context); - final isOpenalias = + final isOpenAlias = await sendStore.isOpenaliasRecord(_addressController.text); - if (isOpenalias) { + if (isOpenAlias) { _addressController.text = sendStore.recordAddress; - await showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: Text(S.of(context).openalias_alert_title), - content: Text( - S.of(context).openalias_alert_content(sendStore.recordName)), - actions: [ - FlatButton( - child: Text(S.of(context).ok), - onPressed: () => Navigator.of(context).pop()) - ], - ); - }); + await showSimpleOxenDialog(context, S.of(context).openalias_alert_title, + S.of(context).openalias_alert_content(sendStore.recordName), + onPressed: (_) => Navigator.of(context).pop()); } } @@ -102,320 +92,325 @@ class SendFormState extends State { return ScrollableWithBottomSection( contentPadding: EdgeInsets.all(0), - content: Column( - children: [ - Container( - padding: EdgeInsets.only(left: 38, right: 30), - decoration: BoxDecoration( - color: Theme.of(context).backgroundColor, - boxShadow: [ - BoxShadow( - color: Palette.shadowGrey, - blurRadius: 10, - offset: Offset(0, 12), - ) - ], - border: Border( - top: BorderSide( - width: 1, - color: Theme.of(context) - .accentTextTheme - .subtitle2 - .backgroundColor))), - child: SizedBox( - height: 56, - width: double.infinity, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Observer(builder: (_) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(S.of(context).send_your_wallet, - style: TextStyle( - fontSize: 12, color: OxenPalette.teal)), - Text(walletStore.name, - style: TextStyle( - fontSize: 18, - color: Theme.of(context) - .accentTextTheme - .overline - .color, - height: 1.25)), - ]); - }), - Observer(builder: (context) { - final savedDisplayMode = settingsStore.balanceDisplayMode; - final availableBalance = - savedDisplayMode == BalanceDisplayMode.hiddenBalance - ? '---' - : balanceStore.unlockedBalanceString; - - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(S.current.oxen_available_balance, - style: TextStyle( - fontSize: 12, - color: Theme.of(context) - .accentTextTheme - .overline - .backgroundColor, - )), - Text(availableBalance, - style: TextStyle( - fontSize: 22, - color: Theme.of(context) - .accentTextTheme - .overline - .color, - height: 1.1)), - ]); - }) - ], - ), - ), - ), - Form( - key: _formKey, - child: Container( - padding: - EdgeInsets.only(left: 38, right: 33, top: 10, bottom: 30), - child: Column(children: [ - AddressTextField( - controller: _addressController, - placeholder: S.of(context).send_oxen_address, - focusNode: _focusNode, - onURIScanned: (uri) { - var address = ''; - var amount = ''; - - if (uri != null) { - address = uri.path; - amount = uri.queryParameters['tx_amount']; - } else { - address = uri.toString(); - } - - _addressController.text = address; - _cryptoAmountController.text = amount; - }, - options: [ - AddressTextFieldOption.qrCode, - AddressTextFieldOption.addressBook + content: GestureDetector( + onTap: () { + FocusScope.of(context).unfocus(); + }, + child: Column( + children: [ + Container( + padding: EdgeInsets.only(left: 18, right: 18), + decoration: BoxDecoration( + color: Theme.of(context).backgroundColor, + boxShadow: [ + BoxShadow( + color: Palette.shadowGrey, + blurRadius: 10, + offset: Offset(0, 12), + ) ], - validator: (value) { - sendStore.validateAddress(value, - cryptoCurrency: CryptoCurrency.oxen); - return sendStore.errorMessage; - }, - ), - Padding( - padding: const EdgeInsets.only(top: 20), - child: TextFormField( - style: TextStyle( - fontSize: 18.0, + border: Border( + top: BorderSide( + width: 1, color: Theme.of(context) .accentTextTheme - .overline - .color), - controller: _cryptoAmountController, - keyboardType: TextInputType.numberWithOptions( - signed: false, decimal: true), - inputFormatters: [ - FilteringTextInputFormatter.deny( - RegExp('[\\-|\\ |\\,]')) - ], - decoration: InputDecoration( - prefixIcon: Padding( - padding: EdgeInsets.only(top: 12), - child: Text('OXEN:', + .subtitle2 + .backgroundColor))), + child: SizedBox( + height: 56, + width: double.infinity, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Observer(builder: (_) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(S.of(context).send_your_wallet, style: TextStyle( - fontSize: 18, - color: Theme.of(context) - .accentTextTheme - .overline - .color, - )), - ), - suffixIcon: Container( - width: 1, - padding: EdgeInsets.only(top: 0), - child: Center( - child: InkWell( - onTap: () => sendStore.setSendAll(), - child: Text(S.of(context).all, - style: TextStyle( - fontSize: 10, - color: Theme.of(context) - .accentTextTheme - .overline - .decorationColor))), - ), - ), - hintStyle: TextStyle( - fontSize: 18.0, - color: Theme.of(context).hintColor), - hintText: '0.0000', - focusedBorder: UnderlineInputBorder( - borderSide: BorderSide( - color: OxenPalette.teal, width: 2.0)), - enabledBorder: UnderlineInputBorder( - borderSide: BorderSide( - color: Theme.of(context).focusColor, - width: 1.0))), - validator: (value) { - sendStore.validateOXEN( - value, balanceStore.unlockedBalance); - return sendStore.errorMessage; - }), - ), - Padding( - padding: const EdgeInsets.only(top: 20), - child: TextFormField( - style: TextStyle( - fontSize: 18.0, - color: Theme.of(context) - .accentTextTheme - .overline - .color), - controller: _fiatAmountController, - keyboardType: TextInputType.numberWithOptions( - signed: false, decimal: true), - inputFormatters: [ - FilteringTextInputFormatter.deny( - RegExp('[\\-|\\ |\\,]')) - ], - decoration: InputDecoration( - prefixIcon: Padding( - padding: EdgeInsets.only(top: 12), - child: Text( - '${settingsStore.fiatCurrency.toString()}:', + fontSize: 12, color: OxenPalette.teal)), + Text(walletStore.name, + style: TextStyle( + fontSize: 18, + color: Theme.of(context) + .accentTextTheme + .overline + .color, + height: 1.25)), + ]); + }), + Observer(builder: (context) { + final savedDisplayMode = + settingsStore.balanceDisplayMode; + final availableBalance = + savedDisplayMode == BalanceDisplayMode.hiddenBalance + ? '---' + : balanceStore.unlockedBalanceString; + + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(S.current.oxen_available_balance, style: TextStyle( - fontSize: 18, + fontSize: 12, color: Theme.of(context) .accentTextTheme .overline - .color, + .backgroundColor, )), - ), - hintStyle: TextStyle( - fontSize: 18.0, - color: Theme.of(context).hintColor), - hintText: '0.00', - focusedBorder: UnderlineInputBorder( - borderSide: BorderSide( - color: OxenPalette.teal, width: 2.0)), - enabledBorder: UnderlineInputBorder( - borderSide: BorderSide( - color: Theme.of(context).focusColor, - width: 1.0)))), + Text(availableBalance, + style: TextStyle( + fontSize: 22, + color: Theme.of(context) + .accentTextTheme + .overline + .color, + height: 1.1)), + ]); + }) + ], ), - Padding( - padding: const EdgeInsets.only(top: 12.0, bottom: 10), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(S.of(context).send_estimated_fee, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, + ), + ), + Form( + key: _formKey, + child: Container( + padding: + EdgeInsets.only(left: 18, right: 18, top: 10, bottom: 30), + child: Column(children: [ + AddressTextField( + controller: _addressController, + placeholder: S.of(context).send_oxen_address, + focusNode: _focusNodeAddress, + onURIScanned: (uri) { + var address = ''; + var amount = ''; + + if (uri != null) { + address = uri.path; + amount = uri.queryParameters['tx_amount']; + } else { + address = uri.toString(); + } + + _addressController.text = address; + _cryptoAmountController.text = amount; + }, + options: [ + AddressTextFieldOption.qrCode, + AddressTextFieldOption.addressBook + ], + validator: (value) { + sendStore.validateAddress(value, + cryptoCurrency: CryptoCurrency.oxen); + return sendStore.errorMessage; + }, + ), + Padding( + padding: const EdgeInsets.only(top: 20), + child: TextFormField( + style: TextStyle( + fontSize: 18.0, color: Theme.of(context) .accentTextTheme .overline - .backgroundColor, - )), - Text( - '${calculateEstimatedFee(priority: settingsStore.transactionPriority)} OXEN', - style: TextStyle( - fontSize: 14, + .color), + controller: _cryptoAmountController, + keyboardType: TextInputType.numberWithOptions( + signed: false, decimal: true), + inputFormatters: [ + FilteringTextInputFormatter.deny( + RegExp('[\\-|\\ |\\,]')) + ], + decoration: InputDecoration( + prefixIcon: SizedBox( + width: 75, + child: Padding( + padding: EdgeInsets.only(left: 8, top: 12), + child: Text('OXEN:', + style: TextStyle( + fontSize: 18, + color: Theme.of(context) + .accentTextTheme + .overline + .color, + )), + ), + ), + suffixIcon: Container( + width: 1, + padding: EdgeInsets.only(top: 0), + child: Center( + child: InkWell( + onTap: () => sendStore.setSendAll(), + child: Text(S.of(context).all, + style: TextStyle( + fontSize: 10, + color: Theme.of(context) + .accentTextTheme + .overline + .decorationColor))), + ), + ), + hintStyle: TextStyle( + fontSize: 18.0, + color: Theme.of(context).hintColor), + hintText: '0.0000', + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: OxenPalette.teal, width: 2.0)), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Theme.of(context).focusColor, + width: 1.0)), + errorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: OxenPalette.red, width: 1.0)), + focusedErrorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: OxenPalette.red, width: 1.0)), + errorStyle: TextStyle(color: OxenPalette.red)), + validator: (value) { + sendStore.validateOXEN( + value, balanceStore.unlockedBalance); + return sendStore.errorMessage; + }), + ), + Padding( + padding: const EdgeInsets.only(top: 20), + child: TextFormField( + style: TextStyle( + fontSize: 18.0, + color: Theme.of(context) + .accentTextTheme + .overline + .color), + controller: _fiatAmountController, + keyboardType: TextInputType.numberWithOptions( + signed: false, decimal: true), + inputFormatters: [ + FilteringTextInputFormatter.deny( + RegExp('[\\-|\\ |\\,]')) + ], + decoration: InputDecoration( + prefixIcon: SizedBox( + width: 75, + child: Padding( + padding: EdgeInsets.only(left: 8, top: 12), + child: Text( + '${settingsStore.fiatCurrency.toString()}:', + style: TextStyle( + fontSize: 18, + color: Theme.of(context) + .accentTextTheme + .overline + .color, + )), + ), + ), + hintStyle: TextStyle( + fontSize: 18.0, + color: Theme.of(context).hintColor), + hintText: '0.00', + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: OxenPalette.teal, width: 2.0)), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Theme.of(context).focusColor, + width: 1.0)), + errorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: OxenPalette.red, width: 1.0)), + focusedErrorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: OxenPalette.red, width: 1.0)), + errorStyle: TextStyle(color: OxenPalette.red))), + ), + Padding( + padding: const EdgeInsets.only(top: 12.0, bottom: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(S.of(context).send_estimated_fee, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: Theme.of(context) + .accentTextTheme + .overline + .backgroundColor, + )), + Text( + '${calculateEstimatedFee(priority: settingsStore.transactionPriority)} OXEN', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: Theme.of(context) + .primaryTextTheme + .overline + .backgroundColor, + )) + ], + ), + ), + SizedBox( + width: double.infinity, + child: Text( + S.of(context).send_priority( + settingsStore.transactionPriority.toString()), + style: TextStyle( + fontSize: 12, fontWeight: FontWeight.w500, color: Theme.of(context) .primaryTextTheme - .overline - .backgroundColor, - )) - ], + .subtitle2 + .color, + height: 1.3)), ), - ), - SizedBox( - width: double.infinity, - child: Text( - S.of(context).send_priority( - settingsStore.transactionPriority.toString()), - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Theme.of(context) - .primaryTextTheme - .subtitle2 - .color, - height: 1.3)), - ), - ]), - ), - ) - ], + ]), + ), + ) + ], + ), ), bottomSection: Observer(builder: (_) { - return LoadingPrimaryButton( - onPressed: syncStore.status is SyncedSyncStatus - ? () async { - // Hack. Don't ask me. - FocusScope.of(context).requestFocus(FocusNode()); - - if (_formKey.currentState.validate()) { - await showDialog( - context: context, - builder: (dialogContext) { - return AlertDialog( - title: Text( - S.of(context).send_creating_transaction), - content: Text(S.of(context).confirm_sending), - actions: [ - FlatButton( - child: Text(S.of(context).send), - onPressed: () async { - await Navigator.of(dialogContext) - .popAndPushNamed(Routes.auth, - arguments: (bool - isAuthenticatedSuccessfully, - AuthPageState auth) { - if (!isAuthenticatedSuccessfully) { - return; - } - - Navigator.of(auth.context).pop(); - - sendStore.createTransaction( - address: _addressController.text); - }); - }), - FlatButton( - child: Text(S.of(context).cancel), - onPressed: () => - Navigator.of(context).pop()) - ], - ); - }); - } + return SlideToAct( + text: S.of(context).send_title, + outerColor: Theme.of(context).primaryTextTheme.subtitle2.color, + innerColor: OxenPalette.teal, + onFutureSubmit: syncStore.status is SyncedSyncStatus + ? () async { + if (_formKey.currentState.validate()) { + var isSuccessful = false; + + await Navigator.of(context).pushNamed(Routes.auth, + arguments: (bool isAuthenticatedSuccessfully, + AuthPageState auth) async { + if (!isAuthenticatedSuccessfully) { + isSuccessful = false; + return; + } + + await sendStore.createTransaction( + address: _addressController.text); + + Navigator.of(auth.context).pop(); + isSuccessful = true; + }); + return isSuccessful; + } else { + return false; } - : null, - text: S.of(context).send, - color: Theme.of(context).accentTextTheme.button.backgroundColor, - borderColor: - Theme.of(context).accentTextTheme.button.decorationColor, - isLoading: sendStore.state is CreatingTransaction || - sendStore.state is TransactionCommitting); + } + : null, + ); })); } void _setEffects(BuildContext context) { - if (_effectsInstalled) { - return; - } + if (_effectsInstalled) return; final sendStore = Provider.of(context); @@ -450,68 +445,34 @@ class SendFormState extends State { reaction((_) => sendStore.state, (SendingState state) { if (state is SendingFailed) { WidgetsBinding.instance.addPostFrameCallback((_) { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: Text(S.of(context).error), - content: Text(state.error), - actions: [ - FlatButton( - child: Text(S.of(context).ok), - onPressed: () => Navigator.of(context).pop()) - ], - ); - }); + showSimpleOxenDialog(context, S.of(context).error, state.error, + onPressed: (_) => Navigator.of(context).pop()); }); } if (state is TransactionCreatedSuccessfully) { WidgetsBinding.instance.addPostFrameCallback((_) { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: Text(S.of(context).confirm_sending), - content: Text(S.of(context).commit_transaction_amount_fee( - sendStore.pendingTransaction.amount, - sendStore.pendingTransaction.fee)), - actions: [ - FlatButton( - child: Text(S.of(context).ok), - onPressed: () { - Navigator.of(context).pop(); - sendStore.commitTransaction(); - }), - FlatButton( - child: Text(S.of(context).cancel), - onPressed: () => Navigator.of(context).pop(), - ) - ], - ); - }); + showSimpleOxenDialog( + context, + S.of(context).confirm_sending, + S.of(context).commit_transaction_amount_fee( + sendStore.pendingTransaction.amount, + sendStore.pendingTransaction.fee), onPressed: (_) { + Navigator.of(context).pop(); + sendStore.commitTransaction(); + }); }); } if (state is TransactionCommitted) { WidgetsBinding.instance.addPostFrameCallback((_) { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: Text(S.of(context).sending), - content: Text(S.of(context).transaction_sent), - actions: [ - FlatButton( - child: Text(S.of(context).ok), - onPressed: () { - _addressController.text = ''; - _cryptoAmountController.text = ''; - Navigator.of(context)..pop()..pop(); - }) - ], - ); - }); + showSimpleOxenDialog( + context, S.of(context).sending, S.of(context).transaction_sent, + onPressed: (_) { + _addressController.text = ''; + _cryptoAmountController.text = ''; + Navigator.of(context)..pop()..pop(); + }); }); } }); diff --git a/lib/src/screens/settings/change_language.dart b/lib/src/screens/settings/change_language.dart index e9eed32d..2a8e3102 100644 --- a/lib/src/screens/settings/change_language.dart +++ b/lib/src/screens/settings/change_language.dart @@ -1,10 +1,11 @@ -import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; -import 'package:provider/provider.dart'; +import 'package:flutter/material.dart'; import 'package:oxen_wallet/generated/l10n.dart'; import 'package:oxen_wallet/src/domain/common/language.dart'; -import 'package:oxen_wallet/src/stores/settings/settings_store.dart'; import 'package:oxen_wallet/src/screens/base_page.dart'; +import 'package:oxen_wallet/src/stores/settings/settings_store.dart'; +import 'package:oxen_wallet/src/widgets/oxen_dialog.dart'; +import 'package:provider/provider.dart'; class ChangeLanguage extends BasePage { @override @@ -16,7 +17,8 @@ class ChangeLanguage extends BasePage { final currentLanguage = Provider.of(context); final currentColor = Theme.of(context).selectedRowColor; - final notCurrentColor = Theme.of(context).accentTextTheme.subtitle1.backgroundColor; + final notCurrentColor = + Theme.of(context).accentTextTheme.subtitle1.backgroundColor; return Container( padding: EdgeInsets.only(top: 10.0, bottom: 10.0), @@ -25,8 +27,7 @@ class ChangeLanguage extends BasePage { itemBuilder: (BuildContext context, int index) { final isCurrent = settingsStore.languageCode == null ? false - : languages.keys.elementAt(index) == - settingsStore.languageCode; + : languages.keys.elementAt(index) == settingsStore.languageCode; return Container( margin: EdgeInsets.only(top: 10.0, bottom: 10.0), @@ -36,40 +37,24 @@ class ChangeLanguage extends BasePage { languages.values.elementAt(index), style: TextStyle( fontSize: 16.0, - color: Theme.of(context).primaryTextTheme.headline6.color), + color: + Theme.of(context).primaryTextTheme.headline6.color), ), onTap: () async { if (!isCurrent) { - await showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: Text( - S.of(context).change_language, - textAlign: TextAlign.center, - ), - content: Text( - S.of(context).change_language_to( - languages.values.elementAt(index)), - textAlign: TextAlign.center, - ), - actions: [ - FlatButton( - onPressed: () => Navigator.of(context).pop(), - child: Text(S.of(context).cancel)), - FlatButton( - onPressed: () { - settingsStore.saveLanguageCode( - languageCode: - languages.keys.elementAt(index)); - currentLanguage.setCurrentLanguage( - languages.keys.elementAt(index)); - Navigator.of(context).pop(); - }, - child: Text(S.of(context).change)), - ], - ); - }); + await showSimpleOxenDialog( + context, + S.of(context).change_language, + S.of(context).change_language_to( + languages.values.elementAt(index)), + onPressed: (context) { + settingsStore.saveLanguageCode( + languageCode: languages.keys.elementAt(index)); + currentLanguage.setCurrentLanguage( + languages.keys.elementAt(index)); + Navigator.of(context).pop(); + }, + ); } }, ), diff --git a/lib/src/screens/settings/settings.dart b/lib/src/screens/settings/settings.dart index 7870c556..38610666 100644 --- a/lib/src/screens/settings/settings.dart +++ b/lib/src/screens/settings/settings.dart @@ -68,7 +68,7 @@ class SettingsFormState extends State { } void _setSettingsList() { - final settingsStore = Provider.of(context); + final settingsStore = context.read(); _items.addAll([ SettingsItem( title: S.current.settings_nodes, attribute: Attributes.header), @@ -166,7 +166,7 @@ class SettingsFormState extends State { title: S.current.settings_change_language, attribute: Attributes.arrow), SettingsItem( - title: S.current.settings_allow_biometrical_authentication, + title: S.current.settings_allow_biometric_authentication, attribute: Attributes.switcher), SettingsItem( title: S.current.settings_dark_mode, attribute: Attributes.switcher), @@ -313,7 +313,7 @@ class SettingsFormState extends State { } Future _setBalance(BuildContext context) async { - final settingsStore = Provider.of(context); + final settingsStore = context.read(); final selectedDisplayMode = await presentPicker(context, BalanceDisplayMode.all); @@ -324,7 +324,7 @@ class SettingsFormState extends State { } Future _setBalanceDetail(BuildContext context) async { - final settingsStore = Provider.of(context); + final settingsStore = context.read(); final balanceDetail = await presentPicker(context, AmountDetail.all); if (balanceDetail != null) { @@ -333,7 +333,7 @@ class SettingsFormState extends State { } Future _setCurrency(BuildContext context) async { - final settingsStore = Provider.of(context); + final settingsStore = context.read(); final selectedCurrency = await presentPicker(context, FiatCurrency.all); if (selectedCurrency != null) { @@ -342,7 +342,7 @@ class SettingsFormState extends State { } Future _setTransactionPriority(BuildContext context) async { - final settingsStore = Provider.of(context); + final settingsStore = context.read(); final selectedPriority = await presentPicker(context, OxenTransactionPriority.all); diff --git a/lib/src/screens/settings/widgets/settings_switch_list_row.dart b/lib/src/screens/settings/widgets/settings_switch_list_row.dart index 9d81111f..ca722f38 100644 --- a/lib/src/screens/settings/widgets/settings_switch_list_row.dart +++ b/lib/src/screens/settings/widgets/settings_switch_list_row.dart @@ -27,7 +27,7 @@ class SettingsSwitchListRow extends StatelessWidget { })); } - if (title == S.of(context).settings_allow_biometrical_authentication) { + if (title == S.of(context).settings_allow_biometric_authentication) { return Observer( builder: (_) => StandartSwitch( value: settingsStore.allowBiometricAuthentication, diff --git a/lib/src/screens/show_keys/show_keys_page.dart b/lib/src/screens/show_keys/show_keys_page.dart index 0def28b2..ac3718ec 100644 --- a/lib/src/screens/show_keys/show_keys_page.dart +++ b/lib/src/screens/show_keys/show_keys_page.dart @@ -20,7 +20,7 @@ class ShowKeysPage extends BasePage { final walletKeysStore = Provider.of(context); return Container( - padding: EdgeInsets.only(top: 20.0, bottom: 20.0, left: 20, right: 20), + padding: EdgeInsets.only(top: 20.0, bottom: 20.0, left: 5, right: 5), child: Observer( builder: (_) { final keysMap = { @@ -32,7 +32,7 @@ class ShowKeysPage extends BasePage { return ListView.separated( separatorBuilder: (_, __) => Container( - padding: EdgeInsets.only(left: 30.0, right: 20.0), + padding: EdgeInsets.only(left: 10, right: 10), child: Divider( color: Theme.of(context).dividerTheme.color, height: 1.0)), @@ -43,7 +43,7 @@ class ShowKeysPage extends BasePage { return ListTile( contentPadding: EdgeInsets.only( - top: 10, bottom: 10, left: 30, right: 20), + top: 10, bottom: 10, left: 10, right: 10), onTap: () { Clipboard.setData(ClipboardData( text: keysMap.values.elementAt(index))); @@ -57,7 +57,7 @@ class ShowKeysPage extends BasePage { duration: Duration(seconds: 1), )); }, - title: Text(key + ':', style: TextStyle(fontSize: 16.0)), + title: Text('$key:', style: TextStyle(fontSize: 16.0)), subtitle: Container( padding: EdgeInsets.only(top: 5.0), child: Text(value, diff --git a/lib/src/screens/stake/new_stake_page.dart b/lib/src/screens/stake/new_stake_page.dart new file mode 100644 index 00000000..cc8f97fc --- /dev/null +++ b/lib/src/screens/stake/new_stake_page.dart @@ -0,0 +1,358 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:mobx/mobx.dart'; +import 'package:oxen_wallet/generated/l10n.dart'; +import 'package:oxen_wallet/palette.dart'; +import 'package:oxen_wallet/routes.dart'; +import 'package:oxen_wallet/src/domain/common/balance_display_mode.dart'; +import 'package:oxen_wallet/src/node/sync_status.dart'; +import 'package:oxen_wallet/src/screens/auth/auth_page.dart'; +import 'package:oxen_wallet/src/screens/base_page.dart'; +import 'package:oxen_wallet/src/stores/balance/balance_store.dart'; +import 'package:oxen_wallet/src/stores/send/send_store.dart'; +import 'package:oxen_wallet/src/stores/send/sending_state.dart'; +import 'package:oxen_wallet/src/stores/settings/settings_store.dart'; +import 'package:oxen_wallet/src/stores/sync/sync_store.dart'; +import 'package:oxen_wallet/src/stores/wallet/wallet_store.dart'; +import 'package:oxen_wallet/src/wallet/oxen/calculate_estimated_fee.dart'; +import 'package:oxen_wallet/src/wallet/oxen/transaction/transaction_priority.dart'; +import 'package:oxen_wallet/src/widgets/oxen_text_field.dart'; +import 'package:oxen_wallet/src/widgets/scollable_with_bottom_section.dart'; +import 'package:oxen_wallet/src/widgets/slide_to_act.dart'; +import 'package:provider/provider.dart'; + +class NewStakePage extends BasePage { + @override + String get title => S.current.title_new_stake; + + @override + bool get isModalBackButton => true; + + @override + bool get resizeToAvoidBottomInset => false; + + @override + Widget body(BuildContext context) => NewStakeForm(); +} + +class NewStakeForm extends StatefulWidget { + @override + State createState() => NewStakeFormState(); +} + +class NewStakeFormState extends State { + final _addressController = TextEditingController(); + final _cryptoAmountController = TextEditingController(); + + final _focusNode = FocusNode(); + + bool _effectsInstalled = false; + + final _formKey = GlobalKey(); + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + final settingsStore = Provider.of(context); + final sendStore = Provider.of(context); + sendStore.settingsStore = settingsStore; + final balanceStore = Provider.of(context); + final walletStore = Provider.of(context); + final syncStore = Provider.of(context); + + _setEffects(context); + + return ScrollableWithBottomSection( + contentPadding: EdgeInsets.all(0), + content: Column( + children: [ + Container( + padding: EdgeInsets.only(left: 18, right: 18), + decoration: BoxDecoration( + color: Theme.of(context).backgroundColor, + boxShadow: [ + BoxShadow( + color: Palette.shadowGrey, + blurRadius: 10, + offset: Offset(0, 12), + ) + ], + border: Border( + top: BorderSide( + width: 1, + color: Theme.of(context) + .accentTextTheme + .subtitle2 + .backgroundColor))), + child: SizedBox( + height: 56, + width: double.infinity, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Observer(builder: (_) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(S.of(context).send_your_wallet, + style: TextStyle( + fontSize: 12, color: OxenPalette.teal)), + Text(walletStore.name, + style: TextStyle( + fontSize: 18, + color: Theme.of(context) + .accentTextTheme + .overline + .color, + height: 1.25)), + ]); + }), + Observer(builder: (context) { + final savedDisplayMode = settingsStore.balanceDisplayMode; + final availableBalance = + savedDisplayMode == BalanceDisplayMode.hiddenBalance + ? '---' + : balanceStore.unlockedBalanceString; + + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(S.current.oxen_available_balance, + style: TextStyle( + fontSize: 12, + color: Theme.of(context) + .accentTextTheme + .overline + .backgroundColor, + )), + Text(availableBalance, + style: TextStyle( + fontSize: 22, + color: Theme.of(context) + .accentTextTheme + .overline + .color, + height: 1.1)), + ]); + }) + ], + ), + ), + ), + Form( + key: _formKey, + child: Container( + padding: + EdgeInsets.only(left: 18, right: 18, top: 10, bottom: 30), + child: Column(children: [ + OxenTextField( + controller: _addressController, + hintText: S.of(context).service_node_key, + focusNode: _focusNode, + validator: (value) { + final pattern = RegExp('[0-9a-fA-F]{64}'); + if (!pattern.hasMatch(value)) { + return S.of(context).error_text_service_node; + } + return null; + }, + ), + Padding( + padding: const EdgeInsets.only(top: 20), + child: OxenTextField( + controller: _cryptoAmountController, + validator: (value) { + sendStore.validateOXEN( + value, balanceStore.unlockedBalance); + return sendStore.errorMessage; + }, + hintText: '0.0000 OXEN', + keyboardType: TextInputType.numberWithOptions( + signed: false, decimal: true), + inputFormatters: [ + FilteringTextInputFormatter.deny( + RegExp('[\\-|\\ |\\,]')) + ], + suffixIcon: Container( + width: 1, + padding: EdgeInsets.only(top: 0), + child: Center( + child: InkWell( + onTap: () => sendStore.setSendAll(), + child: Text(S.of(context).all, + style: TextStyle( + fontSize: 10, + color: Theme.of(context) + .accentTextTheme + .overline + .decorationColor))), + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 12.0, bottom: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(S.of(context).send_estimated_fee, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: Theme.of(context) + .accentTextTheme + .overline + .backgroundColor, + )), + Text( + '${calculateEstimatedFee(priority: OxenTransactionPriority.slow)} OXEN', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: Theme.of(context) + .primaryTextTheme + .overline + .backgroundColor, + )) + ], + ), + ) + ]), + ), + ) + ], + ), + bottomSection: Observer(builder: (_) { + return SlideToAct( + text: S.of(context).stake_oxen, + outerColor: Theme.of(context).primaryTextTheme.subtitle2.color, + innerColor: OxenPalette.teal, + onFutureSubmit: syncStore.status is SyncedSyncStatus + ? () async { + if (_formKey.currentState.validate()) { + var isSuccessful = false; + + await Navigator.of(context).pushNamed(Routes.auth, + arguments: (bool isAuthenticatedSuccessfully, + AuthPageState auth) async { + if (!isAuthenticatedSuccessfully) { + isSuccessful = false; + return; + } + + await sendStore.createStake( + address: _addressController.text); + + Navigator.of(auth.context).pop(); + isSuccessful = true; + }); + return isSuccessful; + } else { + return false; + } + } + : null, + ); + })); + } + + void _setEffects(BuildContext context) { + if (_effectsInstalled) { + return; + } + + final sendStore = Provider.of(context); + + reaction((_) => sendStore.cryptoAmount, (String amount) { + if (amount != _cryptoAmountController.text) { + _cryptoAmountController.text = amount; + } + }); + + _cryptoAmountController.addListener(() { + final cryptoAmount = _cryptoAmountController.text; + + if (sendStore.cryptoAmount != cryptoAmount) { + sendStore.changeCryptoAmount(cryptoAmount); + } + }); + + reaction((_) => sendStore.state, (SendingState state) { + if (state is SendingFailed) { + WidgetsBinding.instance.addPostFrameCallback((_) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text(S.of(context).error), + content: Text(state.error), + actions: [ + FlatButton( + child: Text(S.of(context).ok), + onPressed: () => Navigator.of(context).pop()) + ], + ); + }); + }); + } + + if (state is TransactionCreatedSuccessfully) { + WidgetsBinding.instance.addPostFrameCallback((_) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text(S.of(context).confirm_sending), + content: Text(S.of(context).commit_transaction_amount_fee( + sendStore.pendingTransaction.amount, + sendStore.pendingTransaction.fee)), + actions: [ + FlatButton( + child: Text(S.of(context).ok), + onPressed: () { + Navigator.of(context).pop(); + sendStore.commitTransaction(); + }), + FlatButton( + child: Text(S.of(context).cancel), + onPressed: () => Navigator.of(context).pop(), + ) + ], + ); + }); + }); + } + + if (state is TransactionCommitted) { + WidgetsBinding.instance.addPostFrameCallback((_) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text(S.of(context).sending), + content: Text(S.of(context).transaction_sent), + actions: [ + FlatButton( + child: Text(S.of(context).ok), + onPressed: () { + _addressController.text = ''; + _cryptoAmountController.text = ''; + Navigator.of(context)..pop()..pop(); + }) + ], + ); + }); + }); + } + }); + + _effectsInstalled = true; + } +} diff --git a/lib/src/screens/stake/stake_page.dart b/lib/src/screens/stake/stake_page.dart index 880a2a96..81dc7508 100644 --- a/lib/src/screens/stake/stake_page.dart +++ b/lib/src/screens/stake/stake_page.dart @@ -1,30 +1,29 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:oxen_coin/oxen_coin_structs.dart'; import 'package:oxen_coin/stake.dart'; -import 'package:oxen_coin/structs/stake_row.dart'; import 'package:oxen_wallet/generated/l10n.dart'; import 'package:oxen_wallet/palette.dart'; import 'package:oxen_wallet/routes.dart'; +import 'package:oxen_wallet/src/screens/auth/auth_page.dart'; import 'package:oxen_wallet/src/screens/base_page.dart'; -import 'package:oxen_wallet/src/widgets/nav/nav_list_arrow.dart'; +import 'package:oxen_wallet/src/wallet/crypto_amount_format.dart'; +import 'package:oxen_wallet/src/wallet/oxen/oxen_amount_format.dart'; import 'package:oxen_wallet/src/widgets/nav/nav_list_header.dart'; +import 'package:oxen_wallet/src/widgets/nav/nav_list_trailing.dart'; +import 'package:oxen_wallet/src/widgets/oxen_dialog.dart'; + +extension StakeParsing on StakeRow { + double get ownedPercentage { + final percentage = oxenAmountToDouble(amount) / 15000; + if (percentage > 1) return 1; + return percentage; + } +} class StakePage extends BasePage { final _bodyKey = GlobalKey(); - // @override - // Widget trailing(BuildContext context) { - // return SizedBox( - // width: 25, - // child: FlatButton( - // padding: EdgeInsets.all(0), - // onPressed: () => Navigator.of(context).pushNamed(Routes.settings), - // child: Icon(Icons.settings_rounded, - // color: Theme.of(context).primaryTextTheme.caption.color, - // size: 25)), - // ); - // } - @override Widget body(BuildContext context) => StakePageBody(key: _bodyKey); } @@ -42,88 +41,149 @@ class StakePageBodyState extends State { Color get stakeColor => allStakes.isEmpty ? OxenPalette.lightRed : OxenPalette.lime; + int get totalAmountStaked { + var totalAmount = 0; + for (final stake in allStakes) { + totalAmount += stake.amount; + } + return totalAmount; + } + double get stakePercentage { if (allStakes.isEmpty) return 1; - final percentage = allStakes.length / 4; + final percentage = oxenAmountToDouble(totalAmountStaked) / 15000; if (percentage > 1) return 1; return percentage; } @override Widget build(BuildContext context) { - if (allStakes.isNotEmpty) print('Stake ${allStakes[0].getServiceNodeKey()}'); return SingleChildScrollView( - child: Column( - children: [ - ListView( - shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), - children: [ - SizedBox( - height: 220.0, - child: Stack( - children: [ - Center( - child: Container( - width: 200, - height: 200, - child: CircularProgressIndicator( - strokeWidth: 15, - value: stakePercentage, - valueColor: AlwaysStoppedAnimation(stakeColor), - ), + child: ListView( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + children: [ + SizedBox( + height: 220.0, + child: Stack( + children: [ + Center( + child: Container( + width: 200, + height: 200, + child: CircularProgressIndicator( + strokeWidth: 15, + value: stakePercentage, + valueColor: AlwaysStoppedAnimation(stakeColor), ), ), - Center(child: Text(S.current.nothing_staked)), - ], - ), + ), + Center( + child: Text(allStakes.isNotEmpty + ? oxenAmountToString(totalAmountStaked, + detail: AmountDetail.none) + : S.current.nothing_staked)), + ], ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 20), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - IconButton( - icon: Icon(Icons.arrow_upward_rounded), - onPressed: () => Navigator.of(context, rootNavigator: true) - .pushNamed(Routes.send), - ), - Text(allStakes.isEmpty - ? S.current.start_staking - : S.current.stake_more) - ], - ), + ), + Padding( + padding: EdgeInsets.symmetric(vertical: 20), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: Icon(Icons.arrow_upward_rounded), + onPressed: () => Navigator.of(context, rootNavigator: true) + .pushNamed(Routes.newStake), + ), + Text(allStakes.isEmpty + ? S.current.start_staking + : S.current.stake_more) + ], ), - if (allStakes.isNotEmpty) - NavListHeader(title: S.current.your_contributions), - if (allStakes.isNotEmpty) - ListView.builder( - itemCount: allStakes.length, - itemBuilder: (BuildContext context, int index) { - final stake = allStakes[index]; - print(stake); - final nodeName = - '${stake.getServiceNodeKey().substring(0, 8)}...${stake.getServiceNodeKey().substring(-4)}'; + ), + if (allStakes.isNotEmpty) + NavListHeader(title: S.current.your_contributions), + if (allStakes.isNotEmpty) + ListView.builder( + shrinkWrap: true, + itemCount: allStakes.length, + itemBuilder: (BuildContext context, int index) { + final stake = allStakes[index]; + final serviceNodeKey = stake.serviceNodeKey; + final nodeName = + '${serviceNodeKey.substring(0, 12)}...${serviceNodeKey.substring(serviceNodeKey.length - 4)}'; + + return Dismissible( + key: Key(stake.serviceNodeKey), + confirmDismiss: (direction) async { + if (!canRequestUnstake(stake.serviceNodeKey)) { + Scaffold.of(context).showSnackBar(SnackBar( + content: Text(S.of(context).unable_unlock_stake), + backgroundColor: Colors.red, + )); + return false; + } + var isSuccessful = false; + var isAuthenticated = false; + + await Navigator.of(context).pushNamed(Routes.auth, + arguments: (bool isAuthenticatedSuccessfully, + AuthPageState auth) async { + if (isAuthenticatedSuccessfully) { + isAuthenticated = true; + Navigator.of(auth.context).pop(); + } + }); + + if (isAuthenticated) { + await showConfirmOxenDialog( + context, + S.of(context).title_confirm_unlock_stake, + S.of(context).body_confirm_unlock_stake( + stake.serviceNodeKey), + onDismiss: (buildContext) { + isSuccessful = false; + Navigator.of(buildContext).pop(); + }, onConfirm: (buildContext) { + isSuccessful = true; + Navigator.of(buildContext).pop(); + }); + } - return NavListArrow( - leading: CircularProgressIndicator(value: 1), + return isSuccessful; + }, + onDismissed: (direction) async { + await submitStakeUnlock(stake.serviceNodeKey); + Scaffold.of(context).showSnackBar(SnackBar( + content: Text(S.of(context).unlock_stake_requested), + backgroundColor: Colors.green, + )); + }, + direction: DismissDirection.endToStart, + background: Container( + padding: EdgeInsets.only(right: 10.0), + alignment: AlignmentDirectional.centerEnd, + color: OxenPalette.red, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon( + Icons.arrow_downward_sharp, + color: Colors.white, + ) + ], + )), + child: NavListTrailing( + leading: CircularProgressIndicator( + valueColor: + AlwaysStoppedAnimation(stakeColor), + value: stake.ownedPercentage), text: nodeName, - onTap: () => Navigator.of(context) - .pushNamed(Routes.accountList)); - }), - // NavListArrow( - // leading: CircularProgressIndicator(value: 0.5), - // text: '223ade37...8199', - // onTap: () => - // Navigator.of(context).pushNamed(Routes.accountList)), - // NavListArrow( - // leading: CircularProgressIndicator(value: 0.25), - // text: '02b3e662...4c14', - // onTap: () => - // Navigator.of(context).pushNamed(Routes.accountList)) - ], - ), - ], - )); + )); + }), + ], + ), + ); } } diff --git a/lib/src/screens/subaddress/new_subaddress_page.dart b/lib/src/screens/subaddress/new_subaddress_page.dart index 28e1f2b1..5ee0d062 100644 --- a/lib/src/screens/subaddress/new_subaddress_page.dart +++ b/lib/src/screens/subaddress/new_subaddress_page.dart @@ -1,14 +1,15 @@ -import 'package:mobx/mobx.dart'; -import 'package:provider/provider.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:mobx/mobx.dart'; import 'package:oxen_wallet/generated/l10n.dart'; +import 'package:oxen_wallet/src/screens/base_page.dart'; import 'package:oxen_wallet/src/stores/subaddress_creation/subaddress_creation_state.dart'; import 'package:oxen_wallet/src/stores/subaddress_creation/subaddress_creation_store.dart'; +import 'package:oxen_wallet/src/widgets/oxen_text_field.dart'; import 'package:oxen_wallet/src/widgets/primary_button.dart'; -import 'package:oxen_wallet/src/screens/base_page.dart'; -import 'package:oxen_wallet/palette.dart'; +import 'package:oxen_wallet/src/widgets/scollable_with_bottom_section.dart'; +import 'package:provider/provider.dart'; class NewSubaddressPage extends BasePage { @override @@ -22,7 +23,8 @@ class NewSubaddressPage extends BasePage { final subaddressCreationStore = Provider.of(context); - reaction((_) => subaddressCreationStore.state, (SubaddressCreationState state) { + reaction((_) => subaddressCreationStore.state, + (SubaddressCreationState state) { if (state is SubaddressCreatedSuccessfully) { WidgetsBinding.instance .addPostFrameCallback((_) => Navigator.of(context).pop()); @@ -47,55 +49,35 @@ class NewSubaddressFormState extends State { final subaddressCreationStore = Provider.of(context); - return Form( - key: _formKey, - child: Stack(children: [ - Center( - child: Padding( - padding: EdgeInsets.only(left: 35, right: 35), - child: TextFormField( + return ScrollableWithBottomSection( + contentPadding: EdgeInsets.all(20), + content: Form( + key: _formKey, + child: Stack(children: [ + Center( + child: OxenTextField( controller: _labelController, - decoration: InputDecoration( - hintStyle: TextStyle(color: Theme.of(context).hintColor), - hintText: S.of(context).new_subaddress_label_name, - focusedBorder: UnderlineInputBorder( - borderSide: - BorderSide(color: OxenPalette.teal, width: 2.0)), - enabledBorder: UnderlineInputBorder( - borderSide: BorderSide( - color: Theme.of(context).focusColor, - width: 1.0))), + hintText: S.of(context).new_subaddress_label_name, validator: (value) { subaddressCreationStore.validateSubaddressName(value); return subaddressCreationStore.errorMessage; }), ), - ), - Positioned( - bottom: 20, - left: 20, - right: 20, - child: Observer( - builder: (_) => LoadingPrimaryButton( - onPressed: () async { - if (_formKey.currentState.validate()) { - await subaddressCreationStore.add( - label: _labelController.text); - Navigator.of(context).pop(); - } - }, - text: S.of(context).new_subaddress_create, - color: Theme.of(context) - .accentTextTheme - .button - .backgroundColor, - borderColor: Theme.of(context) - .accentTextTheme - .button - .decorationColor, - isLoading: - subaddressCreationStore.state is SubaddressIsCreating), - )) - ])); + ])), + bottomSection: Observer( + builder: (_) => LoadingPrimaryButton( + onPressed: () async { + if (_formKey.currentState.validate()) { + await subaddressCreationStore.add(label: _labelController.text); + Navigator.of(context).pop(); + } + }, + text: S.of(context).new_subaddress_create, + color: Theme.of(context).accentTextTheme.button.backgroundColor, + borderColor: + Theme.of(context).accentTextTheme.button.decorationColor, + isLoading: subaddressCreationStore.state is SubaddressIsCreating), + ), + ); } } diff --git a/lib/src/screens/wallet_list/wallet_list_page.dart b/lib/src/screens/wallet_list/wallet_list_page.dart index 3c38f671..439d3637 100644 --- a/lib/src/screens/wallet_list/wallet_list_page.dart +++ b/lib/src/screens/wallet_list/wallet_list_page.dart @@ -83,7 +83,6 @@ class WalletListBodyState extends State { ? null : presetMenuForWallet(wallet, context), child: Container( - padding: EdgeInsets.only(left: 10.0, right: 10.0), child: ListTile( title: Text( wallet.name, diff --git a/lib/src/screens/wallet_list/wallet_menu.dart b/lib/src/screens/wallet_list/wallet_menu.dart index 35887781..0277f632 100644 --- a/lib/src/screens/wallet_list/wallet_menu.dart +++ b/lib/src/screens/wallet_list/wallet_menu.dart @@ -30,7 +30,7 @@ class WalletMenu { } void action(int index, WalletDescription wallet, bool isCurrentWallet) { - final _walletListStore = Provider.of(context); + final _walletListStore = context.read(); switch (index) { case 0: diff --git a/lib/src/screens/welcome/create_welcome_page.dart b/lib/src/screens/welcome/create_welcome_page.dart deleted file mode 100644 index 0b422bc7..00000000 --- a/lib/src/screens/welcome/create_welcome_page.dart +++ /dev/null @@ -1,4 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:oxen_wallet/src/screens/welcome/welcome_page.dart'; - -Widget createWelcomePage() => WelcomePage(); \ No newline at end of file diff --git a/lib/src/screens/welcome/welcome_page.dart b/lib/src/screens/welcome/welcome_page.dart index 8c5852a2..90129c4c 100644 --- a/lib/src/screens/welcome/welcome_page.dart +++ b/lib/src/screens/welcome/welcome_page.dart @@ -6,16 +6,13 @@ import 'package:oxen_wallet/src/screens/base_page.dart'; import 'package:oxen_wallet/src/widgets/primary_button.dart'; class WelcomePage extends BasePage { - static const _aspectRatioImage = 1.26; static const _baseWidth = 411.43; - final _image = Image.asset('assets/images/welcomeImg.png'); - final _logo = Image.asset('assets/images/oxen.png', width: 60, height: 60); @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Theme.of(context).backgroundColor, - resizeToAvoidBottomPadding: false, + resizeToAvoidBottomInset: false, body: SafeArea(child: body(context))); } @@ -25,15 +22,6 @@ class WelcomePage extends BasePage { final textScaleFactor = _screenWidth < _baseWidth ? 0.76 : 1.0; return Column(children: [ - // Stack( - // alignment: Alignment.center, - // children: [ - // AspectRatio( - // aspectRatio: _aspectRatioImage, - // child: FittedBox(child: _image, fit: BoxFit.fill)), - // Positioned(bottom: 0.0, child: _logo) - // ], - // ), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, diff --git a/lib/src/stores/send/send_store.dart b/lib/src/stores/send/send_store.dart index 07e463be..84628ab8 100644 --- a/lib/src/stores/send/send_store.dart +++ b/lib/src/stores/send/send_store.dart @@ -5,13 +5,14 @@ import 'package:mobx/mobx.dart'; import 'package:oxen_wallet/generated/l10n.dart'; import 'package:oxen_wallet/src/domain/common/crypto_currency.dart'; import 'package:oxen_wallet/src/domain/common/openalias_record.dart'; -import 'package:oxen_wallet/src/wallet/transaction/pending_transaction.dart'; import 'package:oxen_wallet/src/domain/services/wallet_service.dart'; -import 'package:oxen_wallet/src/wallet/oxen/transaction/oxen_transaction_creation_credentials.dart'; -import 'package:oxen_wallet/src/wallet/oxen/transaction/transaction_description.dart'; import 'package:oxen_wallet/src/stores/price/price_store.dart'; import 'package:oxen_wallet/src/stores/send/sending_state.dart'; import 'package:oxen_wallet/src/stores/settings/settings_store.dart'; +import 'package:oxen_wallet/src/wallet/oxen/transaction/oxen_stake_transaction_creation_credentials.dart'; +import 'package:oxen_wallet/src/wallet/oxen/transaction/oxen_transaction_creation_credentials.dart'; +import 'package:oxen_wallet/src/wallet/oxen/transaction/transaction_description.dart'; +import 'package:oxen_wallet/src/wallet/transaction/pending_transaction.dart'; part 'send_store.g.dart'; @@ -57,6 +58,25 @@ abstract class SendStoreBase with Store { NumberFormat _fiatNumberFormat; String _lastRecipientAddress; + @action + Future createStake({String address, String amount}) async { + state = CreatingTransaction(); + + try { + final _amount = amount ?? + (cryptoAmount == S.current.all + ? null + : cryptoAmount.replaceAll(',', '.')); + final credentials = OxenStakeTransactionCreationCredentials( + address: address, amount: _amount); + + _pendingTransaction = await walletService.createStake(credentials); + state = TransactionCreatedSuccessfully(); + } catch (e) { + state = SendingFailed(error: e.toString()); + } + } + @action Future createTransaction({String address, String amount}) async { state = CreatingTransaction(); diff --git a/lib/src/stores/settings/settings_store.dart b/lib/src/stores/settings/settings_store.dart index dd33ff75..4862cd3a 100644 --- a/lib/src/stores/settings/settings_store.dart +++ b/lib/src/stores/settings/settings_store.dart @@ -1,6 +1,7 @@ import 'package:devicelocale/devicelocale.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; import 'package:flutter/services.dart'; import 'package:hive/hive.dart'; import 'package:intl/intl.dart'; @@ -86,7 +87,12 @@ abstract class SettingsStoreBase with Store { sharedPreferences.getBool(allowBiometricAuthenticationKey) ?? false; final enableFiatCurrency = sharedPreferences.getBool(enableFiatCurrencyKey) ?? false; - final savedDarkTheme = sharedPreferences.getBool(currentDarkTheme) ?? false; + + final initialCurrentDarkMode = + SchedulerBinding.instance.window.platformBrightness == Brightness.dark; + final savedDarkTheme = + sharedPreferences.getBool(currentDarkTheme) ?? initialCurrentDarkMode; + final defaultPinLength = sharedPreferences.getInt(currentPinLength) ?? 4; final savedLanguageCode = sharedPreferences.getString(currentLanguageCode) ?? @@ -209,8 +215,7 @@ abstract class SettingsStoreBase with Store { } @action - Future setCurrentBalanceDetail( - {@required AmountDetail balanceDetail}) async { + Future setCurrentBalanceDetail({@required AmountDetail balanceDetail}) async { this.balanceDetail = balanceDetail; await _sharedPreferences.setInt( currentBalanceDetailKey, balanceDetail.index); diff --git a/lib/src/wallet/oxen/account.dart b/lib/src/wallet/oxen/account.dart index a3258363..a7239aeb 100644 --- a/lib/src/wallet/oxen/account.dart +++ b/lib/src/wallet/oxen/account.dart @@ -1,4 +1,4 @@ -import 'package:oxen_coin/structs/account_row.dart'; +import 'package:oxen_coin/oxen_coin_structs.dart'; class Account { Account({this.id, this.label}); diff --git a/lib/src/wallet/oxen/oxen_wallet.dart b/lib/src/wallet/oxen/oxen_wallet.dart index e533db3c..1283db2c 100644 --- a/lib/src/wallet/oxen/oxen_wallet.dart +++ b/lib/src/wallet/oxen/oxen_wallet.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:hive/hive.dart'; +import 'package:oxen_coin/stake.dart' as oxen_stake; import 'package:oxen_coin/transaction_history.dart' as transaction_history; import 'package:oxen_coin/wallet.dart' as oxen_wallet; import 'package:oxen_wallet/src/node/node.dart'; @@ -13,6 +14,7 @@ import 'package:oxen_wallet/src/wallet/oxen/account_list.dart'; import 'package:oxen_wallet/src/wallet/oxen/oxen_balance.dart'; import 'package:oxen_wallet/src/wallet/oxen/subaddress.dart'; import 'package:oxen_wallet/src/wallet/oxen/subaddress_list.dart'; +import 'package:oxen_wallet/src/wallet/oxen/transaction/oxen_stake_transaction_creation_credentials.dart'; import 'package:oxen_wallet/src/wallet/oxen/transaction/oxen_transaction_creation_credentials.dart'; import 'package:oxen_wallet/src/wallet/oxen/transaction/oxen_transaction_history.dart'; import 'package:oxen_wallet/src/wallet/transaction/pending_transaction.dart'; @@ -169,10 +171,12 @@ class OxenWallet extends Wallet { Future getSeed() async => oxen_wallet.getSeed(); @override - Future getFullBalance() async => oxen_wallet.getFullBalance(accountIndex: _account.value.id); + Future getFullBalance() async => + oxen_wallet.getFullBalance(accountIndex: _account.value.id); @override - Future getUnlockedBalance() async => oxen_wallet.getUnlockedBalance(accountIndex: _account.value.id); + Future getUnlockedBalance() async => + oxen_wallet.getUnlockedBalance(accountIndex: _account.value.id); @override int getCurrentHeight() => oxen_wallet.getCurrentHeight(); @@ -291,6 +295,17 @@ class OxenWallet extends Wallet { return _cachedBlockchainHeight; } + @override + Future createStake( + TransactionCreationCredentials credentials) async { + final _credentials = credentials as OxenStakeTransactionCreationCredentials; + final transactionDescription = + await oxen_stake.createStake(_credentials.address, _credentials.amount); + + return PendingTransaction.fromTransactionDescription( + transactionDescription); + } + @override Future createTransaction( TransactionCreationCredentials credentials) async { diff --git a/lib/src/wallet/oxen/subaddress.dart b/lib/src/wallet/oxen/subaddress.dart index 19ba4c53..b9e6581d 100644 --- a/lib/src/wallet/oxen/subaddress.dart +++ b/lib/src/wallet/oxen/subaddress.dart @@ -1,4 +1,4 @@ -import 'package:oxen_coin/structs/subaddress_row.dart'; +import 'package:oxen_coin/oxen_coin_structs.dart'; class Subaddress { Subaddress({this.id, this.address, this.label}); diff --git a/lib/src/wallet/oxen/transaction/oxen_stake_transaction_creation_credentials.dart b/lib/src/wallet/oxen/transaction/oxen_stake_transaction_creation_credentials.dart new file mode 100644 index 00000000..d1f34c97 --- /dev/null +++ b/lib/src/wallet/oxen/transaction/oxen_stake_transaction_creation_credentials.dart @@ -0,0 +1,9 @@ +import 'package:oxen_wallet/src/wallet/transaction/transaction_creation_credentials.dart'; + +class OxenStakeTransactionCreationCredentials + extends TransactionCreationCredentials { + OxenStakeTransactionCreationCredentials({this.address, this.amount}); + + final String address; + final String amount; +} diff --git a/lib/src/wallet/oxen/transaction/oxen_transaction_history.dart b/lib/src/wallet/oxen/transaction/oxen_transaction_history.dart index e0781589..530d85d4 100644 --- a/lib/src/wallet/oxen/transaction/oxen_transaction_history.dart +++ b/lib/src/wallet/oxen/transaction/oxen_transaction_history.dart @@ -1,11 +1,13 @@ import 'dart:core'; + import 'package:flutter/services.dart'; -import 'package:rxdart/rxdart.dart'; import 'package:oxen_coin/transaction_history.dart' as oxen_transaction_history; import 'package:oxen_wallet/src/wallet/transaction/transaction_history.dart'; import 'package:oxen_wallet/src/wallet/transaction/transaction_info.dart'; +import 'package:rxdart/rxdart.dart'; -List _getAllTransactions(dynamic _) => oxen_transaction_history.getAllTransactions() +List _getAllTransactions(dynamic _) => oxen_transaction_history + .getAllTransactions() .map((row) => TransactionInfo.fromRow(row)) .toList(); diff --git a/lib/src/wallet/transaction/pending_transaction.dart b/lib/src/wallet/transaction/pending_transaction.dart index 1376b370..0901a65b 100644 --- a/lib/src/wallet/transaction/pending_transaction.dart +++ b/lib/src/wallet/transaction/pending_transaction.dart @@ -1,6 +1,6 @@ import 'package:flutter/foundation.dart'; import 'package:oxen_coin/transaction_history.dart' as transaction_history; -import 'package:oxen_coin/structs/pending_transaction.dart'; +import 'package:oxen_coin/oxen_coin_structs.dart'; import 'package:oxen_wallet/src/wallet/oxen/oxen_amount_format.dart'; class PendingTransaction { diff --git a/lib/src/wallet/transaction/transaction_info.dart b/lib/src/wallet/transaction/transaction_info.dart index 1bc9926a..12045a5c 100644 --- a/lib/src/wallet/transaction/transaction_info.dart +++ b/lib/src/wallet/transaction/transaction_info.dart @@ -1,5 +1,5 @@ import 'package:oxen_wallet/src/wallet/oxen/oxen_amount_format.dart'; -import 'package:oxen_coin/structs/transaction_info_row.dart'; +import 'package:oxen_coin/oxen_coin_structs.dart'; import 'package:oxen_wallet/src/util/parseBoolFromString.dart'; import 'package:oxen_wallet/src/wallet/transaction/transaction_direction.dart'; import 'package:oxen_wallet/src/domain/common/format_amount.dart'; diff --git a/lib/src/wallet/wallet.dart b/lib/src/wallet/wallet.dart index ddb889b3..4d6727aa 100644 --- a/lib/src/wallet/wallet.dart +++ b/lib/src/wallet/wallet.dart @@ -55,6 +55,9 @@ abstract class Wallet { Future startSync(); + Future createStake( + TransactionCreationCredentials credentials); + Future createTransaction( TransactionCreationCredentials credentials); diff --git a/lib/src/widgets/address_text_field.dart b/lib/src/widgets/address_text_field.dart index 16eebbde..2c91f04a 100644 --- a/lib/src/widgets/address_text_field.dart +++ b/lib/src/widgets/address_text_field.dart @@ -1,3 +1,4 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:oxen_wallet/generated/l10n.dart'; import 'package:oxen_wallet/palette.dart'; @@ -5,6 +6,7 @@ import 'package:oxen_wallet/routes.dart'; import 'package:oxen_wallet/src/domain/common/contact.dart'; import 'package:oxen_wallet/src/domain/common/qr_scanner.dart'; import 'package:oxen_wallet/src/wallet/oxen/subaddress.dart'; +import 'package:oxen_wallet/src/widgets/oxen_text_field.dart'; enum AddressTextFieldOption { qrCode, addressBook, subaddressList } @@ -35,74 +37,66 @@ class AddressTextField extends StatelessWidget { @override Widget build(BuildContext context) { - return TextFormField( - onFieldSubmitted: (_) => FocusScope.of(context).unfocus(), + return OxenTextField( enabled: isActive, controller: controller, focusNode: focusNode, - decoration: InputDecoration( - suffixIcon: SizedBox( - width: prefixIconWidth * options.length + - (spaceBetweenPrefixIcons * options.length), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - SizedBox(width: 5), - if (options.contains(AddressTextFieldOption.qrCode)) ...[ - Container( - width: prefixIconWidth, - height: prefixIconHeight, - child: InkWell( - onTap: () async => _presentQRScanner(context), - child: Container( - decoration: BoxDecoration( - color: Palette.wildDarkBlueWithOpacity, - borderRadius: - BorderRadius.all(Radius.circular(8))), - child: Icon(Icons.qr_code_outlined)), - )) + suffixIcon: Padding( + padding: EdgeInsets.only(right: 10), + child: SizedBox( + width: prefixIconWidth * options.length + + (spaceBetweenPrefixIcons * options.length), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SizedBox(width: 5), + if (options.contains(AddressTextFieldOption.qrCode)) ...[ + Container( + width: prefixIconWidth, + height: prefixIconHeight, + child: InkWell( + onTap: () async => _presentQRScanner(context), + child: Container( + decoration: BoxDecoration( + color: Palette.wildDarkBlueWithOpacity, + borderRadius: + BorderRadius.all(Radius.circular(8))), + child: Icon(Icons.qr_code_outlined)), + )) + ], + if (options.contains(AddressTextFieldOption.addressBook)) ...[ + Container( + width: prefixIconWidth, + height: prefixIconHeight, + child: InkWell( + onTap: () async => _presetAddressBookPicker(context), + child: Container( + decoration: BoxDecoration( + color: Palette.wildDarkBlueWithOpacity, + borderRadius: + BorderRadius.all(Radius.circular(8))), + child: Icon(Icons.contacts_rounded)), + )) + ], + if (options + .contains(AddressTextFieldOption.subaddressList)) ...[ + Container( + width: prefixIconWidth, + height: prefixIconHeight, + child: InkWell( + onTap: () async => _presetSubaddressListPicker(context), + child: Container( + decoration: BoxDecoration( + color: Palette.wildDarkBlueWithOpacity, + borderRadius: + BorderRadius.all(Radius.circular(8))), + child: Icon(Icons.arrow_downward_rounded)), + )) + ], ], - if (options - .contains(AddressTextFieldOption.addressBook)) ...[ - Container( - width: prefixIconWidth, - height: prefixIconHeight, - child: InkWell( - onTap: () async => _presetAddressBookPicker(context), - child: Container( - decoration: BoxDecoration( - color: Palette.wildDarkBlueWithOpacity, - borderRadius: - BorderRadius.all(Radius.circular(8))), - child: Icon(Icons.contacts_rounded)), - )) - ], - if (options - .contains(AddressTextFieldOption.subaddressList)) ...[ - Container( - width: prefixIconWidth, - height: prefixIconHeight, - child: InkWell( - onTap: () async => _presetSubaddressListPicker(context), - child: Container( - decoration: BoxDecoration( - color: Palette.wildDarkBlueWithOpacity, - borderRadius: - BorderRadius.all(Radius.circular(8))), - child: Icon(Icons.arrow_downward_rounded)), - )) - ], - ], - ), - ), - hintStyle: TextStyle(color: Theme.of(context).hintColor), - hintText: placeholder ?? S.current.widgets_address, - focusedBorder: UnderlineInputBorder( - borderSide: BorderSide(color: OxenPalette.teal, width: 2.0)), - enabledBorder: UnderlineInputBorder( - borderSide: - BorderSide(color: Theme.of(context).focusColor, width: 1.0)), - ), + ), + )), + hintText: placeholder ?? S.current.widgets_address, validator: validator, ); } diff --git a/lib/src/widgets/oxen_dialog.dart b/lib/src/widgets/oxen_dialog.dart new file mode 100644 index 00000000..87300f65 --- /dev/null +++ b/lib/src/widgets/oxen_dialog.dart @@ -0,0 +1,188 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; +import 'package:oxen_wallet/generated/l10n.dart'; +import 'package:oxen_wallet/palette.dart'; +import 'package:oxen_wallet/src/widgets/primary_button.dart'; +import 'package:oxen_wallet/src/widgets/slide_to_act.dart'; + +Future showOxenDialog(BuildContext context, Widget child, + {void Function(BuildContext context) onDismiss}) { + return showDialog( + builder: (_) => OxenDialog(body: child, onDismiss: onDismiss), + context: context); +} + +Future showSimpleOxenDialog(BuildContext context, String title, String body, + {String buttonText, + void Function(BuildContext context) onPressed, + void Function(BuildContext context) onDismiss}) { + return showDialog( + builder: (_) => SimpleOxenDialog(title, body, + buttonText: buttonText, onDismiss: onDismiss, onPressed: onPressed), + context: context); +} + +Future showConfirmOxenDialog(BuildContext context, String title, String body, + {void Function(BuildContext context) onConfirm, + Future Function(BuildContext context) onFutureConfirm, + void Function(BuildContext context) onDismiss}) { + return showDialog( + builder: (_) => ConfirmOxenDialog(title, body, + onDismiss: onDismiss, + onConfirm: onConfirm, + onFutureConfirm: onFutureConfirm), + context: context); +} + +class OxenDialog extends StatelessWidget { + OxenDialog({this.body, this.onDismiss}); + + final void Function(BuildContext context) onDismiss; + final Widget body; + + void _onDismiss(BuildContext context) { + if (onDismiss == null) { + Navigator.of(context).pop(); + } else { + onDismiss(context); + } + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () => _onDismiss(context), + child: Container( + color: Colors.transparent, + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0), + child: Container( + decoration: BoxDecoration(color: Colors.white.withOpacity(0.55)), + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Container( + width: double.infinity, + decoration: BoxDecoration( + color: Theme.of(context).backgroundColor, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(20), + topRight: Radius.circular(20))), + child: body), + ], + ), + ), + ), + ), + ); + } +} + +class SimpleOxenDialog extends StatelessWidget { + SimpleOxenDialog(this.title, this.body, + {this.buttonText, this.onPressed, this.onDismiss}); + + final String title; + final String body; + final String buttonText; + final void Function(BuildContext context) onPressed; + final void Function(BuildContext context) onDismiss; + + @override + Widget build(BuildContext context) { + return OxenDialog( + onDismiss: onDismiss, + body: Container( + padding: EdgeInsets.all(30), + child: Column( + children: [ + Padding( + padding: EdgeInsets.all(15), + child: Text(title, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 18, + decoration: TextDecoration.none, + color: Theme.of(context) + .primaryTextTheme + .caption + .color))), + Padding( + padding: EdgeInsets.only(top: 15, bottom: 30), + child: Text(body, + style: TextStyle( + fontSize: 15, + decoration: TextDecoration.none, + color: Theme.of(context) + .primaryTextTheme + .caption + .color))), + PrimaryButton( + text: buttonText ?? S.of(context).ok, + color: + Theme.of(context).primaryTextTheme.button.backgroundColor, + borderColor: + Theme.of(context).primaryTextTheme.button.decorationColor, + onPressed: () { + if (onPressed != null) onPressed(context); + }) + ], + ), + )); + } +} + +class ConfirmOxenDialog extends StatelessWidget { + ConfirmOxenDialog(this.title, this.body, + {this.onFutureConfirm, this.onConfirm, this.onDismiss}); + + final String title; + final String body; + final Future Function(BuildContext context) onFutureConfirm; + final void Function(BuildContext context) onConfirm; + final void Function(BuildContext context) onDismiss; + + @override + Widget build(BuildContext context) { + return OxenDialog( + onDismiss: onDismiss, + body: Container( + padding: EdgeInsets.all(30), + child: Column( + children: [ + Padding( + padding: EdgeInsets.all(15), + child: Text(title, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 18, + decoration: TextDecoration.none, + color: Theme.of(context) + .primaryTextTheme + .caption + .color))), + Padding( + padding: EdgeInsets.only(top: 15, bottom: 30), + child: Text(body, + style: TextStyle( + fontSize: 15, + decoration: TextDecoration.none, + color: Theme.of(context) + .primaryTextTheme + .caption + .color))), + SlideToAct( + text: S.of(context).ok, + outerColor: Theme.of(context).primaryTextTheme.subtitle2.color, + innerColor: OxenPalette.teal, + onFutureSubmit: onFutureConfirm != null + ? () async => await onFutureConfirm(context) + : null, + onSubmit: onConfirm != null ? () => onConfirm(context) : null, + ) + ], + ), + )); + } +} diff --git a/lib/src/widgets/oxen_text_field.dart b/lib/src/widgets/oxen_text_field.dart new file mode 100644 index 00000000..dc031126 --- /dev/null +++ b/lib/src/widgets/oxen_text_field.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:oxen_wallet/palette.dart'; + +class OxenTextField extends StatelessWidget { + OxenTextField( + {this.enabled = true, + this.hintText, + this.keyboardType, + this.controller, + this.validator, + this.inputFormatters, + this.prefixIcon, + this.suffixIcon, + this.focusNode}); + + final bool enabled; + final String hintText; + final TextInputType keyboardType; + final TextEditingController controller; + final String Function(String) validator; + final List inputFormatters; + final Widget prefixIcon; + final Widget suffixIcon; + final FocusNode focusNode; + + @override + Widget build(BuildContext context) { + return TextFormField( + onFieldSubmitted: (_) => FocusScope.of(context).unfocus(), + enabled: enabled, + controller: controller, + focusNode: focusNode, + style: TextStyle( + fontSize: 18.0, + color: Theme.of(context).accentTextTheme.overline.color), + keyboardType: keyboardType, + inputFormatters: inputFormatters, + decoration: InputDecoration( + prefixIcon: prefixIcon, + suffixIcon: suffixIcon, + hintStyle: + TextStyle(fontSize: 18.0, color: Theme.of(context).hintColor), + hintText: hintText, + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: OxenPalette.teal, width: 2.0)), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Theme.of(context).focusColor, width: 1.0)), + errorBorder: OutlineInputBorder( + borderSide: BorderSide(color: OxenPalette.red, width: 1.0)), + focusedErrorBorder: OutlineInputBorder( + borderSide: BorderSide(color: OxenPalette.red, width: 1.0)), + errorStyle: TextStyle(color: OxenPalette.red)), + validator: validator); + } +} diff --git a/lib/src/widgets/picker.dart b/lib/src/widgets/picker.dart index 7e34e56b..a7e56bfe 100644 --- a/lib/src/widgets/picker.dart +++ b/lib/src/widgets/picker.dart @@ -1,4 +1,5 @@ import 'dart:ui'; + import 'package:flutter/material.dart'; class Picker extends StatelessWidget { @@ -33,7 +34,11 @@ class Picker extends StatelessWidget { child: Container( width: double.infinity, height: pickerHeight, - color: Theme.of(context).backgroundColor, + decoration: BoxDecoration( + color: Theme.of(context).backgroundColor, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(20), + topRight: Radius.circular(20))), child: ListView.separated( itemCount: items.length + 1, separatorBuilder: (_, index) => index == 0 @@ -46,7 +51,6 @@ class Picker extends StatelessWidget { return Container( height: 100, width: double.infinity, - color: Theme.of(context).backgroundColor, child: Center( child: Text( title, @@ -88,7 +92,7 @@ class Picker extends StatelessWidget { fontWeight: FontWeight.normal, fontFamily: 'Lato', color: index == selectedAtIndex - ? Color.fromRGBO(138, 80, 255, 1) + ? Color.fromRGBO(138, 80, 255, 1.0) : Theme.of(context) .primaryTextTheme .caption diff --git a/lib/src/widgets/present_picker.dart b/lib/src/widgets/present_picker.dart index ed305d0f..70ed63c5 100644 --- a/lib/src/widgets/present_picker.dart +++ b/lib/src/widgets/present_picker.dart @@ -1,6 +1,9 @@ -import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; import 'package:oxen_wallet/generated/l10n.dart'; +import 'package:oxen_wallet/src/widgets/primary_button.dart'; + +import 'oxen_dialog.dart'; Future presentPicker( BuildContext context, List list) async { @@ -9,36 +12,59 @@ Future presentPicker( return await showDialog( context: context, builder: (BuildContext context) { - return AlertDialog( - title: Text(S.of(context).please_select), - backgroundColor: Theme.of(context).backgroundColor, - content: Container( - height: 150.0, - child: CupertinoPicker( - backgroundColor: Theme.of(context).backgroundColor, - itemExtent: 45.0, - onSelectedItemChanged: (int index) => _value = list[index], - children: List.generate( - list.length, - (index) => Center( - child: Text( - list[index].toString(), - style: TextStyle( - color: Theme.of(context) - .primaryTextTheme - .caption - .color), - ), - ))), - ), - actions: [ - FlatButton( - onPressed: () => Navigator.of(context).pop(), - child: Text(S.of(context).cancel)), - FlatButton( - onPressed: () => Navigator.of(context).pop(_value), - child: Text(S.of(context).ok)) - ], - ); + return OxenDialog( + body: Container( + padding: EdgeInsets.all(30), + child: Column( + children: [ + Padding( + padding: EdgeInsets.all(15), + child: Text(S.of(context).please_select, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 18, + decoration: TextDecoration.none, + color: Theme.of(context) + .primaryTextTheme + .caption + .color))), + Padding( + padding: EdgeInsets.only(top: 15, bottom: 30), + child: Container( + height: 150.0, + child: CupertinoPicker( + backgroundColor: Theme.of(context).backgroundColor, + itemExtent: 45.0, + onSelectedItemChanged: (int index) => + _value = list[index], + children: List.generate( + list.length, + (index) => Center( + child: Text( + list[index].toString(), + style: TextStyle( + color: Theme.of(context) + .primaryTextTheme + .caption + .color), + ), + ))), + ), + ), + PrimaryButton( + text: S.of(context).ok, + color: Theme.of(context) + .primaryTextTheme + .button + .backgroundColor, + borderColor: Theme.of(context) + .primaryTextTheme + .button + .decorationColor, + onPressed: () => Navigator.of(context).pop(_value), + ) + ], + ), + )); }); -} \ No newline at end of file +} diff --git a/lib/src/widgets/slide_to_act.dart b/lib/src/widgets/slide_to_act.dart new file mode 100644 index 00000000..c6fc84b0 --- /dev/null +++ b/lib/src/widgets/slide_to_act.dart @@ -0,0 +1,393 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; + +class SlideToAct extends StatefulWidget { + const SlideToAct({ + Key key, + this.sliderButtonIconSize = 24, + this.sliderButtonIconPadding = 16, + this.sliderButtonYOffset = 0, + this.height = 70, + this.outerColor, + this.borderRadius = 52, + this.elevation = 6, + this.animationDuration = const Duration(milliseconds: 300), + this.reversed = false, + this.alignment = Alignment.center, + this.submittedIcon, + this.onFutureSubmit, + this.onSubmit, + this.child, + this.innerColor, + this.text, + this.sliderButtonIcon, + }) : super(key: key); + + /// The size of the sliding icon + final double sliderButtonIconSize; + + /// Tha padding of the sliding icon + final double sliderButtonIconPadding; + + /// The offset on the y axis of the slider icon + final double sliderButtonYOffset; + + /// The child that is rendered instead of the default Text widget + final Widget child; + + /// The height of the component + final double height; + + /// The color of the inner circular button, of the tick icon of the text. + /// If not set, this attribute defaults to primaryIconTheme. + final Color innerColor; + + /// The color of the external area and of the arrow icon. + /// If not set, this attribute defaults to accentColor from your theme. + final Color outerColor; + + /// The text showed in the default Text widget + final String text; + + /// The borderRadius of the sliding icon and of the background + final double borderRadius; + + /// Callback called on submit + final Future Function() onFutureSubmit; + final VoidCallback onSubmit; + + /// Elevation of the component + final double elevation; + + /// The widget to render instead of the default icon + final Widget sliderButtonIcon; + + /// The widget to render instead of the default submitted icon + final Widget submittedIcon; + + /// The duration of the animations + final Duration animationDuration; + + /// If true the widget will be reversed + final bool reversed; + + /// the alignment of the widget once it's submitted + final Alignment alignment; + + @override + SlideToActState createState() => SlideToActState(); +} + +/// Use a GlobalKey to access the state. This is the only way to call [SlideToActState.reset] +class SlideToActState extends State with TickerProviderStateMixin { + final GlobalKey _containerKey = GlobalKey(); + final GlobalKey _sliderKey = GlobalKey(); + double _dx = 0; + double _maxDx = 0; + + double get _progress => _dx == 0 ? 0 : _dx / _maxDx; + double _endDx = 0; + double _dz = 1; + double _initialContainerWidth, _containerWidth; + double _checkAnimationDx = 0; + bool submitted = false; + AnimationController _checkAnimationController, + _shrinkAnimationController, + _resizeAnimationController, + _cancelAnimationController; + + @override + Widget build(BuildContext context) { + if (widget.onSubmit != null && widget.onFutureSubmit != null) { + throw Exception('onSubmit and onFutureSubmit can\'t be used both'); + } + + return Align( + alignment: widget.alignment, + child: Transform( + alignment: Alignment.center, + transform: Matrix4.rotationY(widget.reversed ? pi : 0), + child: Container( + key: _containerKey, + height: widget.height, + width: _containerWidth, + constraints: _containerWidth != null + ? null + : BoxConstraints.expand(height: widget.height), + child: Material( + elevation: widget.elevation, + color: widget.outerColor ?? Theme.of(context).accentColor, + borderRadius: BorderRadius.circular(widget.borderRadius), + child: submitted + ? Transform( + alignment: Alignment.center, + transform: Matrix4.rotationY(widget.reversed ? pi : 0), + child: Center( + child: Stack( + clipBehavior: Clip.antiAlias, + children: [ + widget.submittedIcon ?? + Icon( + Icons.done, + color: widget.innerColor ?? + Theme.of(context).primaryIconTheme.color, + ), + Positioned.fill( + right: 0, + child: Transform( + transform: Matrix4.rotationY( + _checkAnimationDx * (pi / 2)), + alignment: Alignment.centerRight, + child: Container( + color: widget.outerColor ?? + Theme.of(context).accentColor, + ), + ), + ), + ], + ), + ), + ) + : Stack( + alignment: Alignment.center, + clipBehavior: Clip.none, + children: [ + Opacity( + opacity: 1 - 1 * _progress, + child: Transform( + alignment: Alignment.center, + transform: + Matrix4.rotationY(widget.reversed ? pi : 0), + child: widget.child ?? + Text( + widget.text ?? 'Slide to act', + textAlign: TextAlign.center, + style: TextStyle( + color: widget.innerColor ?? + Theme.of(context).primaryIconTheme.color, + fontSize: 24, + ), + ), + ), + ), + Positioned( + left: widget.sliderButtonYOffset, + child: Transform.scale( + scale: _dz, + origin: Offset(_dx, 0), + child: Transform.translate( + offset: Offset(_dx, 0), + child: Container( + key: _sliderKey, + child: GestureDetector( + onHorizontalDragUpdate: onHorizontalDragUpdate, + onHorizontalDragEnd: (details) async { + _endDx = _dx; + if (_progress <= 0.8) { + await _cancelAnimation(); + } else { + if (widget.onFutureSubmit != null) { + await widget.onFutureSubmit(); + } else if (widget.onSubmit != null) { + widget.onSubmit(); + } + await _cancelAnimation(); + } + }, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0), + child: Material( + borderRadius: BorderRadius.circular( + widget.borderRadius), + child: Container( + padding: EdgeInsets.all( + widget.sliderButtonIconPadding), + child: Transform.rotate( + angle: -pi * _progress, + child: Center( + child: widget.sliderButtonIcon ?? + Icon( + Icons.arrow_forward, + size: + widget.sliderButtonIconSize, + color: widget.outerColor ?? + Theme.of(context) + .accentColor, + ), + ), + ), + ), + color: widget.innerColor ?? + Theme.of(context) + .primaryIconTheme + .color, + ), + ), + ), + ), + ), + ), + ), + ], + ), + ), + ), + ), + ); + } + + void onHorizontalDragUpdate(DragUpdateDetails details) { + setState(() { + _dx = (_dx + details.delta.dx).clamp(0.0, _maxDx) as double; + }); + } + + /// Call this method to revert the animations + Future reset() async { + await _checkAnimationController.reverse().orCancel; + + submitted = false; + + await _shrinkAnimationController.reverse().orCancel; + + await _resizeAnimationController.reverse().orCancel; + + await _cancelAnimation(); + } + + Future _checkAnimation() async { + _checkAnimationController.reset(); + + final animation = Tween( + begin: 0, + end: 1, + ).animate(CurvedAnimation( + parent: _checkAnimationController, + curve: Curves.slowMiddle, + )); + + animation.addListener(() { + if (mounted) { + setState(() { + _checkAnimationDx = animation.value; + }); + } + }); + await _checkAnimationController.forward().orCancel; + } + + Future _shrinkAnimation() async { + _shrinkAnimationController.reset(); + + final diff = _initialContainerWidth - widget.height; + final animation = Tween( + begin: 0, + end: 1, + ).animate(CurvedAnimation( + parent: _shrinkAnimationController, + curve: Curves.easeOutCirc, + )); + + animation.addListener(() { + if (mounted) { + setState(() { + _containerWidth = _initialContainerWidth - (diff * animation.value); + }); + } + }); + + setState(() { + submitted = true; + }); + await _shrinkAnimationController.forward().orCancel; + } + + Future _resizeAnimation() async { + _resizeAnimationController.reset(); + + final animation = Tween( + begin: 0, + end: 1, + ).animate(CurvedAnimation( + parent: _resizeAnimationController, + curve: Curves.easeInBack, + )); + + animation.addListener(() { + if (mounted) { + setState(() { + _dz = 1 - animation.value; + }); + } + }); + await _resizeAnimationController.forward().orCancel; + } + + Future _cancelAnimation() async { + _cancelAnimationController.reset(); + final animation = Tween( + begin: 0, + end: 1, + ).animate(CurvedAnimation( + parent: _cancelAnimationController, + curve: Curves.fastOutSlowIn, + )); + + animation.addListener(() { + if (mounted) { + setState(() { + _dx = (_endDx - (_endDx * animation.value)); + }); + } + }); + await _cancelAnimationController.forward().orCancel; + } + + @override + void initState() { + super.initState(); + + _cancelAnimationController = AnimationController( + vsync: this, + duration: widget.animationDuration, + ); + _checkAnimationController = AnimationController( + vsync: this, + duration: widget.animationDuration, + ); + _shrinkAnimationController = AnimationController( + vsync: this, + duration: widget.animationDuration, + ); + + _resizeAnimationController = AnimationController( + vsync: this, + duration: widget.animationDuration, + ); + + WidgetsBinding.instance.addPostFrameCallback((_) { + final containerBox = + _containerKey.currentContext.findRenderObject() as RenderBox; + _containerWidth = containerBox.size.width; + _initialContainerWidth = _containerWidth; + + final sliderBox = + _sliderKey.currentContext.findRenderObject() as RenderBox; + final sliderWidth = sliderBox.size.width; + + _maxDx = + _containerWidth - (sliderWidth / 2) - 40 - widget.sliderButtonYOffset; + }); + } + + @override + void dispose() { + _cancelAnimationController.dispose(); + _checkAnimationController.dispose(); + _shrinkAnimationController.dispose(); + _resizeAnimationController.dispose(); + super.dispose(); + } +} diff --git a/oxen_coin/ios/Classes/oxen_api.cpp b/oxen_coin/ios/Classes/oxen_api.cpp index 9ee209e9..a7d98627 100644 --- a/oxen_coin/ios/Classes/oxen_api.cpp +++ b/oxen_coin/ios/Classes/oxen_api.cpp @@ -183,6 +183,20 @@ extern "C" } }; + struct StakeUnlockResult + { + bool success; + char *msg; + Oxen::PendingTransaction *pendingTransaction; + + StakeUnlockResult(bool _success, char *_msg, Oxen::PendingTransaction *_pendingTransaction) + { + success = _success; + msg = _msg; + pendingTransaction = _pendingTransaction; + } + }; + Oxen::Wallet *m_wallet; Oxen::TransactionHistory *m_transaction_history; OxenWalletListener *m_listener; @@ -506,26 +520,51 @@ extern "C" return stakes; } -// bool stake_create(char *service_node_key, char *amount, Utf8Box &error, PendingTransactionRaw &pendingTransaction) -// { -// nice(19); -// -// Oxen::PendingTransaction *transaction; -// std:string error_msg; -// -// transaction = m_wallet->stakePending(std::string(service_node_key), std::string(amount), *error_msg); -// -// int status = transaction->status().first; -// -// if (status == Oxen::PendingTransaction::Status::Status_Error || status == Oxen::PendingTransaction::Status::Status_Critical) -// { -// error = Utf8Box(strdup(transaction->status().second.c_str())); -// return false; -// } -// -// pendingTransaction = PendingTransactionRaw(transaction); -// return true; -// } + EXPORT + bool stake_create(char *service_node_key, char *amount, Utf8Box &error, PendingTransactionRaw &pendingTransaction) + { + nice(19); + + Oxen::PendingTransaction *transaction; + + uint64_t _amount = Oxen::Wallet::amountFromString(std::string(amount)); + transaction = m_wallet->stakePending(std::string(service_node_key), _amount); + + int status = transaction->status().first; + + if (status == Oxen::PendingTransaction::Status::Status_Error || status == Oxen::PendingTransaction::Status::Status_Critical) + { + error = Utf8Box(strdup(transaction->status().second.c_str())); + return false; + } + + pendingTransaction = PendingTransactionRaw(transaction); + return true; + } + + EXPORT + bool can_request_stake_unlock(char *service_node_key) + { + std::unique_ptr stakeUnlockResult{m_wallet->canRequestStakeUnlock(service_node_key)}; + return stakeUnlockResult->success(); + } + + EXPORT + bool submit_stake_unlock(char *service_node_key, Utf8Box &error, PendingTransactionRaw &pendingTransaction) + { + std::unique_ptr stakeUnlockResult{m_wallet->requestStakeUnlock(service_node_key)}; + + if (stakeUnlockResult->success()) + { + pendingTransaction = stakeUnlockResult->ptx(); + return true; + } + else + { + error = Utf8Box(strdup(stakeUnlockResult->msg().c_str())); + return false; + } + } EXPORT uint64_t transaction_estimate_fee(uint32_t priority, uint32_t recipients) diff --git a/oxen_coin/lib/account_list.dart b/oxen_coin/lib/account_list.dart index 9b25c384..9620f6f7 100644 --- a/oxen_coin/lib/account_list.dart +++ b/oxen_coin/lib/account_list.dart @@ -1,37 +1,14 @@ import 'dart:ffi'; -import 'package:ffi/ffi.dart'; import 'package:flutter/foundation.dart'; -import 'package:oxen_coin/oxen_api.dart'; -import 'package:oxen_coin/structs/account_row.dart'; -import 'package:oxen_coin/util/signatures.dart'; -import 'package:oxen_coin/util/types.dart'; +import 'package:oxen_coin/oxen_coin_structs.dart'; +import 'package:oxen_coin/src/native/account_list.dart' as account_list; -final accountSizeNative = oxenApi - .lookup>('account_size') - .asFunction(); - -final accountRefreshNative = oxenApi - .lookup>('account_refresh') - .asFunction(); - -final accountGetAllNative = oxenApi - .lookup>('account_get_all') - .asFunction(); - -final accountAddNewNative = oxenApi - .lookup>('account_add_row') - .asFunction(); - -final accountSetLabelNative = oxenApi - .lookup>('account_set_label_row') - .asFunction(); - -void refreshAccounts() => accountRefreshNative(); +void refreshAccounts() => account_list.accountRefreshNative(); List getAllAccount() { - final size = accountSizeNative(); - final accountAddressesPointer = accountGetAllNative(); + final size = account_list.accountSizeNative(); + final accountAddressesPointer = account_list.accountGetAllNative(); final accountAddresses = accountAddressesPointer.asTypedList(size); return accountAddresses @@ -39,25 +16,13 @@ List getAllAccount() { .toList(); } -void addAccountSync({String label}) { - final labelPointer = Utf8.toUtf8(label); - accountAddNewNative(labelPointer); - free(labelPointer); -} - -void setLabelForAccountSync({int accountIndex, String label}) { - final labelPointer = Utf8.toUtf8(label); - accountSetLabelNative(accountIndex, labelPointer); - free(labelPointer); -} - -void _addAccount(String label) => addAccountSync(label: label); +void _addAccount(String label) => account_list.addAccountSync(label: label); void _setLabelForAccount(Map args) { final label = args['label'] as String; final accountIndex = args['accountIndex'] as int; - setLabelForAccountSync(label: label, accountIndex: accountIndex); + account_list.setLabelForAccountSync(label: label, accountIndex: accountIndex); } Future addAccount({String label}) async => compute(_addAccount, label); diff --git a/oxen_coin/lib/oxen_coin_structs.dart b/oxen_coin/lib/oxen_coin_structs.dart new file mode 100644 index 00000000..cb097357 --- /dev/null +++ b/oxen_coin/lib/oxen_coin_structs.dart @@ -0,0 +1,7 @@ +library oxen_coin; + +export 'src/structs/account_row.dart'; +export 'src/structs/pending_transaction.dart'; +export 'src/structs/stake_row.dart'; +export 'src/structs/subaddress_row.dart'; +export 'src/structs/transaction_info_row.dart'; diff --git a/oxen_coin/lib/exceptions/connection_to_node_exception.dart b/oxen_coin/lib/src/exceptions/connection_to_node_exception.dart similarity index 100% rename from oxen_coin/lib/exceptions/connection_to_node_exception.dart rename to oxen_coin/lib/src/exceptions/connection_to_node_exception.dart diff --git a/oxen_coin/lib/exceptions/creation_transaction_exception.dart b/oxen_coin/lib/src/exceptions/creation_transaction_exception.dart similarity index 100% rename from oxen_coin/lib/exceptions/creation_transaction_exception.dart rename to oxen_coin/lib/src/exceptions/creation_transaction_exception.dart diff --git a/oxen_coin/lib/exceptions/setup_wallet_exception.dart b/oxen_coin/lib/src/exceptions/setup_wallet_exception.dart similarity index 100% rename from oxen_coin/lib/exceptions/setup_wallet_exception.dart rename to oxen_coin/lib/src/exceptions/setup_wallet_exception.dart diff --git a/oxen_coin/lib/exceptions/wallet_creation_exception.dart b/oxen_coin/lib/src/exceptions/wallet_creation_exception.dart similarity index 100% rename from oxen_coin/lib/exceptions/wallet_creation_exception.dart rename to oxen_coin/lib/src/exceptions/wallet_creation_exception.dart diff --git a/oxen_coin/lib/exceptions/wallet_restore_from_keys_exception.dart b/oxen_coin/lib/src/exceptions/wallet_restore_from_keys_exception.dart similarity index 100% rename from oxen_coin/lib/exceptions/wallet_restore_from_keys_exception.dart rename to oxen_coin/lib/src/exceptions/wallet_restore_from_keys_exception.dart diff --git a/oxen_coin/lib/exceptions/wallet_restore_from_seed_exception.dart b/oxen_coin/lib/src/exceptions/wallet_restore_from_seed_exception.dart similarity index 100% rename from oxen_coin/lib/exceptions/wallet_restore_from_seed_exception.dart rename to oxen_coin/lib/src/exceptions/wallet_restore_from_seed_exception.dart diff --git a/oxen_coin/lib/src/native/account_list.dart b/oxen_coin/lib/src/native/account_list.dart new file mode 100644 index 00000000..942abca7 --- /dev/null +++ b/oxen_coin/lib/src/native/account_list.dart @@ -0,0 +1,38 @@ +import 'dart:ffi'; + +import 'package:ffi/ffi.dart'; +import 'package:oxen_coin/src/oxen_api.dart'; +import 'package:oxen_coin/src/util/signatures.dart'; +import 'package:oxen_coin/src/util/types.dart'; + +final accountSizeNative = oxenApi + .lookup>('account_size') + .asFunction(); + +final accountRefreshNative = oxenApi + .lookup>('account_refresh') + .asFunction(); + +final accountGetAllNative = oxenApi + .lookup>('account_get_all') + .asFunction(); + +final accountAddNewNative = oxenApi + .lookup>('account_add_row') + .asFunction(); + +final accountSetLabelNative = oxenApi + .lookup>('account_set_label_row') + .asFunction(); + +void addAccountSync({String label}) { + final labelPointer = Utf8.toUtf8(label); + accountAddNewNative(labelPointer); + free(labelPointer); +} + +void setLabelForAccountSync({int accountIndex, String label}) { + final labelPointer = Utf8.toUtf8(label); + accountSetLabelNative(accountIndex, labelPointer); + free(labelPointer); +} diff --git a/oxen_coin/lib/src/native/stake.dart b/oxen_coin/lib/src/native/stake.dart new file mode 100644 index 00000000..ee8f8021 --- /dev/null +++ b/oxen_coin/lib/src/native/stake.dart @@ -0,0 +1,81 @@ +import 'dart:ffi'; + +import 'package:ffi/ffi.dart'; +import 'package:oxen_coin/oxen_coin_structs.dart'; +import 'package:oxen_coin/src/exceptions/creation_transaction_exception.dart'; +import 'package:oxen_coin/src/oxen_api.dart'; +import 'package:oxen_coin/src/structs/ut8_box.dart'; +import 'package:oxen_coin/src/util/signatures.dart'; +import 'package:oxen_coin/src/util/types.dart'; + +final stakeCountNative = oxenApi + .lookup>('stake_count') + .asFunction(); + +final stakeGetAllNative = oxenApi + .lookup>('stake_get_all') + .asFunction(); + +final stakeCreateNative = oxenApi + .lookup>('stake_create') + .asFunction(); + +final canRequestUnstakeNative = oxenApi + .lookup>('can_request_stake_unlock') + .asFunction(); + +final submitStakeUnlockNative = oxenApi + .lookup>('submit_stake_unlock') + .asFunction(); + +PendingTransactionDescription createStakeSync( + String serviceNodeKey, String amount) { + final serviceNodeKeyPointer = Utf8.toUtf8(serviceNodeKey); + final amountPointer = amount != null ? Utf8.toUtf8(amount) : nullptr; + final errorMessagePointer = allocate(); + final pendingTransactionRawPointer = allocate(); + final created = stakeCreateNative(serviceNodeKeyPointer, amountPointer, + errorMessagePointer, pendingTransactionRawPointer) != + 0; + + free(serviceNodeKeyPointer); + + if (amountPointer != nullptr) { + free(amountPointer); + } + + if (!created) { + final message = errorMessagePointer.ref.getValue(); + free(errorMessagePointer); + throw CreationTransactionException(message: message); + } + + return PendingTransactionDescription( + amount: pendingTransactionRawPointer.ref.amount, + fee: pendingTransactionRawPointer.ref.fee, + hash: pendingTransactionRawPointer.ref.getHash(), + pointerAddress: pendingTransactionRawPointer.address); +} + +PendingTransactionDescription submitStakeUnlockSync(String serviceNodeKey) { + final serviceNodeKeyPointer = Utf8.toUtf8(serviceNodeKey); + final errorMessagePointer = allocate(); + final pendingTransactionRawPointer = allocate(); + final created = submitStakeUnlockNative(serviceNodeKeyPointer, + errorMessagePointer, pendingTransactionRawPointer) != + 0; + + free(serviceNodeKeyPointer); + + if (!created) { + final message = errorMessagePointer.ref.getValue(); + free(errorMessagePointer); + throw CreationTransactionException(message: message); + } + + return PendingTransactionDescription( + amount: pendingTransactionRawPointer.ref.amount, + fee: pendingTransactionRawPointer.ref.fee, + hash: pendingTransactionRawPointer.ref.getHash(), + pointerAddress: pendingTransactionRawPointer.address); +} diff --git a/oxen_coin/lib/src/native/subaddress_list.dart b/oxen_coin/lib/src/native/subaddress_list.dart new file mode 100644 index 00000000..5bdc3c9e --- /dev/null +++ b/oxen_coin/lib/src/native/subaddress_list.dart @@ -0,0 +1,40 @@ +import 'dart:ffi'; + +import 'package:ffi/ffi.dart'; +import 'package:oxen_coin/src/oxen_api.dart'; +import 'package:oxen_coin/src/util/signatures.dart'; +import 'package:oxen_coin/src/util/types.dart'; + +final subaddressSizeNative = oxenApi + .lookup>('subaddress_size') + .asFunction(); + +final subaddressRefreshNative = oxenApi + .lookup>('subaddress_refresh') + .asFunction(); + +final subaddressGetAllNative = oxenApi + .lookup>('subaddress_get_all') + .asFunction(); + +final subaddressAddNewNative = oxenApi + .lookup>('subaddress_add_row') + .asFunction(); + +final subaddressSetLabelNative = oxenApi + .lookup>('subaddress_set_label') + .asFunction(); + +void addSubaddressSync({int accountIndex, String label}) { + final labelPointer = Utf8.toUtf8(label); + subaddressAddNewNative(accountIndex, labelPointer); + free(labelPointer); +} + +void setLabelForSubaddressSync( + {int accountIndex, int addressIndex, String label}) { + final labelPointer = Utf8.toUtf8(label); + + subaddressSetLabelNative(accountIndex, addressIndex, labelPointer); + free(labelPointer); +} diff --git a/oxen_coin/lib/src/native/transaction_history.dart b/oxen_coin/lib/src/native/transaction_history.dart new file mode 100644 index 00000000..aebd9a81 --- /dev/null +++ b/oxen_coin/lib/src/native/transaction_history.dart @@ -0,0 +1,80 @@ +import 'dart:ffi'; + +import 'package:ffi/ffi.dart'; +import 'package:oxen_coin/oxen_coin_structs.dart'; +import 'package:oxen_coin/src/exceptions/creation_transaction_exception.dart'; +import 'package:oxen_coin/src/oxen_api.dart'; +import 'package:oxen_coin/src/structs/ut8_box.dart'; +import 'package:oxen_coin/src/util/signatures.dart'; +import 'package:oxen_coin/src/util/types.dart'; + +final transactionsRefreshNative = oxenApi + .lookup>('transactions_refresh') + .asFunction(); + +final transactionsCountNative = oxenApi + .lookup>('transactions_count') + .asFunction(); + +final transactionsGetAllNative = oxenApi + .lookup>('transactions_get_all') + .asFunction(); + +final transactionCreateNative = oxenApi + .lookup>('transaction_create') + .asFunction(); + +final transactionCommitNative = oxenApi + .lookup>('transaction_commit') + .asFunction(); + +final transactionEstimateFeeNative = oxenApi + .lookup>( + 'transaction_estimate_fee') + .asFunction(); + +PendingTransactionDescription createTransactionSync( + {String address, String amount, int priorityRaw, int accountIndex = 0}) { + final addressPointer = Utf8.toUtf8(address); + final amountPointer = amount != null ? Utf8.toUtf8(amount) : nullptr; + final errorMessagePointer = allocate(); + final pendingTransactionRawPointer = allocate(); + final created = transactionCreateNative( + addressPointer, + amountPointer, + priorityRaw, + accountIndex, + errorMessagePointer, + pendingTransactionRawPointer) != + 0; + + free(addressPointer); + + if (amountPointer != nullptr) { + free(amountPointer); + } + + if (!created) { + final message = errorMessagePointer.ref.getValue(); + free(errorMessagePointer); + throw CreationTransactionException(message: message); + } + + return PendingTransactionDescription( + amount: pendingTransactionRawPointer.ref.amount, + fee: pendingTransactionRawPointer.ref.fee, + hash: pendingTransactionRawPointer.ref.getHash(), + pointerAddress: pendingTransactionRawPointer.address); +} + +void commitTransaction({Pointer transactionPointer}) { + final errorMessagePointer = allocate(); + final isCommited = + transactionCommitNative(transactionPointer, errorMessagePointer) != 0; + + if (!isCommited) { + final message = errorMessagePointer.ref.getValue(); + free(errorMessagePointer); + throw CreationTransactionException(message: message); + } +} diff --git a/oxen_coin/lib/src/native/wallet.dart b/oxen_coin/lib/src/native/wallet.dart new file mode 100644 index 00000000..c2f8f37a --- /dev/null +++ b/oxen_coin/lib/src/native/wallet.dart @@ -0,0 +1,178 @@ +import 'dart:async'; +import 'dart:ffi'; +import 'dart:typed_data'; + +import 'package:ffi/ffi.dart'; +import 'package:flutter/services.dart'; +import 'package:oxen_coin/src/exceptions/setup_wallet_exception.dart'; +import 'package:oxen_coin/src/oxen_api.dart'; +import 'package:oxen_coin/src/util/convert_utf8_to_string.dart'; +import 'package:oxen_coin/src/util/signatures.dart'; +import 'package:oxen_coin/src/util/types.dart'; + +int _boolToInt(bool value) => value ? 1 : 0; + +final statusSyncChannel = + BasicMessageChannel('oxen_coin.sync_listener', BinaryCodec()); + +final oxenMethodChannel = MethodChannel('oxen_coin'); + +final getFileNameNative = oxenApi + .lookup>('get_filename') + .asFunction(); + +final getSeedNative = + oxenApi.lookup>('seed').asFunction(); + +final getAddressNative = oxenApi + .lookup>('get_address') + .asFunction(); + +final getFullBalanceNative = oxenApi + .lookup>('get_full_balance') + .asFunction(); + +final getUnlockedBalanceNative = oxenApi + .lookup>('get_unlocked_balance') + .asFunction(); + +final getCurrentHeightNative = oxenApi + .lookup>('get_current_height') + .asFunction(); + +final getNodeHeightNative = oxenApi + .lookup>('get_node_height') + .asFunction(); + +final isConnectedNative = oxenApi + .lookup>('is_connected') + .asFunction(); + +final setupNodeNative = oxenApi + .lookup>('setup_node') + .asFunction(); + +final startRefreshNative = oxenApi + .lookup>('start_refresh') + .asFunction(); + +final connecToNodeNative = oxenApi + .lookup>('connect_to_node') + .asFunction(); + +final setRefreshFromBlockHeightNative = oxenApi + .lookup>( + 'set_refresh_from_block_height') + .asFunction(); + +final setRecoveringFromSeedNative = oxenApi + .lookup>( + 'set_recovering_from_seed') + .asFunction(); + +final storeNative = + oxenApi.lookup>('store').asFunction(); + +final setListenerNative = + oxenApi.lookupFunction('set_listener'); + +final getSyncingHeightNative = oxenApi + .lookup>('get_syncing_height') + .asFunction(); + +final isNeededToRefreshNative = oxenApi + .lookup>('is_needed_to_refresh') + .asFunction(); + +final isNewTransactionExistNative = oxenApi + .lookup>( + 'is_new_transaction_exist') + .asFunction(); + +final getSecretViewKeyNative = oxenApi + .lookup>('secret_view_key') + .asFunction(); + +final getPublicViewKeyNative = oxenApi + .lookup>('public_view_key') + .asFunction(); + +final getSecretSpendKeyNative = oxenApi + .lookup>('secret_spend_key') + .asFunction(); + +final getPublicSpendKeyNative = oxenApi + .lookup>('public_spend_key') + .asFunction(); + +final closeCurrentWalletNative = oxenApi + .lookup>('close_current_wallet') + .asFunction(); + +final onStartupNative = oxenApi + .lookup>('on_startup') + .asFunction(); + +final rescanBlockchainAsyncNative = oxenApi + .lookup>('rescan_blockchain') + .asFunction(); + +int getNodeHeightSync() => getNodeHeightNative(); + +bool isConnectedSync() => isConnectedNative() != 0; + +bool setupNodeSync( + {String address, + String login, + String password, + bool useSSL = false, + bool isLightWallet = false}) { + final addressPointer = Utf8.toUtf8(address); + Pointer loginPointer; + Pointer passwordPointer; + + if (login != null) { + loginPointer = Utf8.toUtf8(login); + } + + if (password != null) { + passwordPointer = Utf8.toUtf8(password); + } + + final errorMessagePointer = allocate(); + final isSetupNode = setupNodeNative( + addressPointer, + loginPointer, + passwordPointer, + _boolToInt(useSSL), + _boolToInt(isLightWallet), + errorMessagePointer) != + 0; + + free(addressPointer); + free(loginPointer); + free(passwordPointer); + + if (!isSetupNode) { + throw SetupWalletException( + message: convertUTF8ToString(pointer: errorMessagePointer)); + } + + return isSetupNode; +} + +void startRefreshSync() => startRefreshNative(); + +Future connectToNode() async => connecToNodeNative() != 0; + +void setRefreshFromBlockHeight({int height}) => + setRefreshFromBlockHeightNative(height); + +void setRecoveringFromSeed({bool isRecovery}) => + setRecoveringFromSeedNative(_boolToInt(isRecovery)); + +void storeSync() { + final pathPointer = Utf8.toUtf8(''); + storeNative(pathPointer); + free(pathPointer); +} diff --git a/oxen_coin/lib/src/native/wallet_manager.dart b/oxen_coin/lib/src/native/wallet_manager.dart new file mode 100644 index 00000000..4c74dff3 --- /dev/null +++ b/oxen_coin/lib/src/native/wallet_manager.dart @@ -0,0 +1,130 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; +import 'package:oxen_coin/src/oxen_api.dart'; +import 'package:oxen_coin/src/util/convert_utf8_to_string.dart'; +import 'package:oxen_coin/src/util/signatures.dart'; +import 'package:oxen_coin/src/util/types.dart'; +import 'package:oxen_coin/src/exceptions/wallet_creation_exception.dart'; +import 'package:oxen_coin/src/exceptions/wallet_restore_from_keys_exception.dart'; +import 'package:oxen_coin/src/exceptions/wallet_restore_from_seed_exception.dart'; + +final createWalletNative = oxenApi + .lookup>('create_wallet') + .asFunction(); + +final restoreWalletFromSeedNative = oxenApi + .lookup>( + 'restore_wallet_from_seed') + .asFunction(); + +final restoreWalletFromKeysNative = oxenApi + .lookup>( + 'restore_wallet_from_keys') + .asFunction(); + +final isWalletExistNative = oxenApi + .lookup>('is_wallet_exist') + .asFunction(); + +final loadWalletNative = oxenApi + .lookup>('load_wallet') + .asFunction(); + +void createWalletSync( + {String path, String password, String language, int nettype = 0}) { + final pathPointer = Utf8.toUtf8(path); + final passwordPointer = Utf8.toUtf8(password); + final languagePointer = Utf8.toUtf8(language); + final errorMessagePointer = allocate(); + final isWalletCreated = createWalletNative(pathPointer, passwordPointer, + languagePointer, nettype, errorMessagePointer) != + 0; + + free(pathPointer); + free(passwordPointer); + free(languagePointer); + + if (!isWalletCreated) { + throw WalletCreationException( + message: convertUTF8ToString(pointer: errorMessagePointer)); + } +} + +bool isWalletExistSync({String path}) { + final pathPointer = Utf8.toUtf8(path); + final isExist = isWalletExistNative(pathPointer) != 0; + + free(pathPointer); + + return isExist; +} + +void restoreWalletFromSeedSync( + {String path, + String password, + String seed, + int nettype = 0, + int restoreHeight = 0}) { + final pathPointer = Utf8.toUtf8(path); + final passwordPointer = Utf8.toUtf8(password); + final seedPointer = Utf8.toUtf8(seed); + final errorMessagePointer = allocate(); + final isWalletRestored = restoreWalletFromSeedNative( + pathPointer, + passwordPointer, + seedPointer, + nettype, + restoreHeight, + errorMessagePointer) != + 0; + + free(pathPointer); + free(passwordPointer); + free(seedPointer); + + if (!isWalletRestored) { + throw WalletRestoreFromSeedException( + message: convertUTF8ToString(pointer: errorMessagePointer)); + } +} + +void restoreWalletFromKeysSync( + {String path, + String password, + String language, + String address, + String viewKey, + String spendKey, + int nettype = 0, + int restoreHeight = 0}) { + final pathPointer = Utf8.toUtf8(path); + final passwordPointer = Utf8.toUtf8(password); + final languagePointer = Utf8.toUtf8(language); + final addressPointer = Utf8.toUtf8(address); + final viewKeyPointer = Utf8.toUtf8(viewKey); + final spendKeyPointer = Utf8.toUtf8(spendKey); + final errorMessagePointer = allocate(); + final isWalletRestored = restoreWalletFromKeysNative( + pathPointer, + passwordPointer, + languagePointer, + addressPointer, + viewKeyPointer, + spendKeyPointer, + nettype, + restoreHeight, + errorMessagePointer) != + 0; + + free(pathPointer); + free(passwordPointer); + free(languagePointer); + free(addressPointer); + free(viewKeyPointer); + free(spendKeyPointer); + + if (!isWalletRestored) { + throw WalletRestoreFromKeysException( + message: convertUTF8ToString(pointer: errorMessagePointer)); + } +} diff --git a/oxen_coin/lib/oxen_api.dart b/oxen_coin/lib/src/oxen_api.dart similarity index 100% rename from oxen_coin/lib/oxen_api.dart rename to oxen_coin/lib/src/oxen_api.dart diff --git a/oxen_coin/lib/structs/account_row.dart b/oxen_coin/lib/src/structs/account_row.dart similarity index 100% rename from oxen_coin/lib/structs/account_row.dart rename to oxen_coin/lib/src/structs/account_row.dart diff --git a/oxen_coin/lib/structs/pending_transaction.dart b/oxen_coin/lib/src/structs/pending_transaction.dart similarity index 100% rename from oxen_coin/lib/structs/pending_transaction.dart rename to oxen_coin/lib/src/structs/pending_transaction.dart diff --git a/oxen_coin/lib/src/structs/stake_row.dart b/oxen_coin/lib/src/structs/stake_row.dart new file mode 100644 index 00000000..9aad0a05 --- /dev/null +++ b/oxen_coin/lib/src/structs/stake_row.dart @@ -0,0 +1,12 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; + +class StakeRow extends Struct { + Pointer _serviceNodeKey; + + @Uint64() + int _amount; + + String get serviceNodeKey => Utf8.fromUtf8(_serviceNodeKey); + int get amount => _amount; +} diff --git a/oxen_coin/lib/structs/subaddress_row.dart b/oxen_coin/lib/src/structs/subaddress_row.dart similarity index 100% rename from oxen_coin/lib/structs/subaddress_row.dart rename to oxen_coin/lib/src/structs/subaddress_row.dart diff --git a/oxen_coin/lib/structs/transaction_info_row.dart b/oxen_coin/lib/src/structs/transaction_info_row.dart similarity index 100% rename from oxen_coin/lib/structs/transaction_info_row.dart rename to oxen_coin/lib/src/structs/transaction_info_row.dart diff --git a/oxen_coin/lib/structs/ut8_box.dart b/oxen_coin/lib/src/structs/ut8_box.dart similarity index 100% rename from oxen_coin/lib/structs/ut8_box.dart rename to oxen_coin/lib/src/structs/ut8_box.dart diff --git a/oxen_coin/lib/util/convert_utf8_to_string.dart b/oxen_coin/lib/src/util/convert_utf8_to_string.dart similarity index 100% rename from oxen_coin/lib/util/convert_utf8_to_string.dart rename to oxen_coin/lib/src/util/convert_utf8_to_string.dart diff --git a/oxen_coin/lib/util/signatures.dart b/oxen_coin/lib/src/util/signatures.dart similarity index 91% rename from oxen_coin/lib/util/signatures.dart rename to oxen_coin/lib/src/util/signatures.dart index 1756c113..475ce6e3 100644 --- a/oxen_coin/lib/util/signatures.dart +++ b/oxen_coin/lib/src/util/signatures.dart @@ -1,8 +1,8 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; -import 'package:oxen_coin/structs/pending_transaction.dart'; -import 'package:oxen_coin/structs/ut8_box.dart'; +import 'package:oxen_coin/oxen_coin_structs.dart'; +import 'package:oxen_coin/src/structs/ut8_box.dart'; typedef create_wallet = Int8 Function( Pointer, Pointer, Pointer, Int32, Pointer); @@ -114,6 +114,11 @@ typedef stake_create = Int8 Function( Pointer error, Pointer pendingTransaction); +typedef can_request_unstake = Int8 Function(Pointer serviceNodeKey); + +typedef submit_stake_unlock = Int8 Function(Pointer serviceNodeKey, + Pointer error, Pointer pendingTransaction); + typedef secret_view_key = Pointer Function(); typedef public_view_key = Pointer Function(); diff --git a/oxen_coin/lib/util/types.dart b/oxen_coin/lib/src/util/types.dart similarity index 90% rename from oxen_coin/lib/util/types.dart rename to oxen_coin/lib/src/util/types.dart index 9c01ea32..fe6dbf3c 100644 --- a/oxen_coin/lib/util/types.dart +++ b/oxen_coin/lib/src/util/types.dart @@ -1,8 +1,8 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; -import 'package:oxen_coin/structs/pending_transaction.dart'; -import 'package:oxen_coin/structs/ut8_box.dart'; +import 'package:oxen_coin/oxen_coin_structs.dart'; +import 'package:oxen_coin/src/structs/ut8_box.dart'; typedef CreateWallet = int Function( Pointer, Pointer, Pointer, int, Pointer); @@ -112,6 +112,11 @@ typedef StakeCreate = int Function( Pointer error, Pointer pendingTransaction); +typedef CanRequestUnstake = int Function(Pointer serviceNodeKey); + +typedef SubmitStakeUnlock = int Function(Pointer serviceNodeKey, + Pointer error, Pointer pendingTransaction); + typedef SecretViewKey = Pointer Function(); typedef PublicViewKey = Pointer Function(); diff --git a/oxen_coin/lib/stake.dart b/oxen_coin/lib/stake.dart index 61275577..e3507656 100644 --- a/oxen_coin/lib/stake.dart +++ b/oxen_coin/lib/stake.dart @@ -2,31 +2,14 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; import 'package:flutter/foundation.dart'; -import 'package:oxen_coin/exceptions/creation_transaction_exception.dart'; -import 'package:oxen_coin/oxen_api.dart'; -import 'package:oxen_coin/structs/pending_transaction.dart'; -import 'package:oxen_coin/structs/stake_row.dart'; -import 'package:oxen_coin/structs/ut8_box.dart'; -import 'package:oxen_coin/util/signatures.dart'; -import 'package:oxen_coin/util/types.dart'; +import 'package:oxen_coin/oxen_coin_structs.dart'; +import 'package:oxen_coin/src/native/stake.dart' as stake_native; -final stakeCountNative = oxenApi - .lookup>('stake_count') - .asFunction(); - -final stakeGetAllNative = oxenApi - .lookup>('stake_get_all') - .asFunction(); - -final stakeCreateNative = oxenApi - .lookup>('stake_create') - .asFunction(); - -int countOfTransactions() => stakeCountNative(); +int countOfTransactions() => stake_native.stakeCountNative(); List getAllStakes() { - final size = stakeCountNative(); - final stakePointer = stakeGetAllNative(); + final size = countOfTransactions(); + final stakePointer = stake_native.stakeGetAllNative(); final stakeAddresses = stakePointer.asTypedList(size); return stakeAddresses @@ -34,41 +17,27 @@ List getAllStakes() { .toList(); } -PendingTransactionDescription createStakeSync(String address, String amount) { - final addressPointer = Utf8.toUtf8(address); - final amountPointer = amount != null ? Utf8.toUtf8(amount) : nullptr; - final errorMessagePointer = allocate(); - final pendingTransactionRawPointer = allocate(); - final created = stakeCreateNative(addressPointer, amountPointer, - errorMessagePointer, pendingTransactionRawPointer) != - 0; - - free(addressPointer); +PendingTransactionDescription _createStakeSync(Map args) { + final serviceNodeKey = args['service_node_key'] as String; + final amount = args['amount'] as String; - if (amountPointer != nullptr) { - free(amountPointer); - } + return stake_native.createStakeSync(serviceNodeKey, amount); +} - if (!created) { - final message = errorMessagePointer.ref.getValue(); - free(errorMessagePointer); - throw CreationTransactionException(message: message); - } +Future createStake( + String serviceNodeKey, String amount) => + compute(_createStakeSync, + {'service_node_key': serviceNodeKey, 'amount': amount}); - return PendingTransactionDescription( - amount: pendingTransactionRawPointer.ref.amount, - fee: pendingTransactionRawPointer.ref.fee, - hash: pendingTransactionRawPointer.ref.getHash(), - pointerAddress: pendingTransactionRawPointer.address); +bool canRequestUnstake(String serviceNodeKey) { + final serviceNodeKeyPointer = Utf8.toUtf8(serviceNodeKey); + return stake_native.canRequestUnstakeNative(serviceNodeKeyPointer) != 0; } -PendingTransactionDescription _createStakeSync(Map args) { - final address = args['address'] as String; - final amount = args['amount'] as String; - - return createStakeSync(address, amount); +PendingTransactionDescription _submitStakeUnlockSync(Map args) { + final serviceNodeKey = args['service_node_key'] as String; + return stake_native.submitStakeUnlockSync(serviceNodeKey); } -Future createStake( - {String address, String amount}) => - compute(_createStakeSync, {'address': address, 'amount': amount}); +Future submitStakeUnlock(String serviceNodeKey) => + compute(_submitStakeUnlockSync, {'service_node_key': serviceNodeKey}); diff --git a/oxen_coin/lib/structs/stake_row.dart b/oxen_coin/lib/structs/stake_row.dart deleted file mode 100644 index b4a50b64..00000000 --- a/oxen_coin/lib/structs/stake_row.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'dart:ffi'; -import 'package:ffi/ffi.dart'; - -class StakeRow extends Struct { - @Int64() - int amount; - Pointer serviceNodeKey; - - String getServiceNodeKey() => Utf8.fromUtf8(serviceNodeKey); - int getAmount() => amount; -} diff --git a/oxen_coin/lib/subaddress_list.dart b/oxen_coin/lib/subaddress_list.dart index 89149e68..d426a6e2 100644 --- a/oxen_coin/lib/subaddress_list.dart +++ b/oxen_coin/lib/subaddress_list.dart @@ -1,38 +1,15 @@ import 'dart:ffi'; -import 'package:ffi/ffi.dart'; import 'package:flutter/foundation.dart'; -import 'package:oxen_coin/oxen_api.dart'; -import 'package:oxen_coin/structs/subaddress_row.dart'; -import 'package:oxen_coin/util/signatures.dart'; -import 'package:oxen_coin/util/types.dart'; - -final subaddressSizeNative = oxenApi - .lookup>('subaddress_size') - .asFunction(); - -final subaddressRefreshNative = oxenApi - .lookup>('subaddress_refresh') - .asFunction(); - -final subaddressGetAllNative = oxenApi - .lookup>('subaddress_get_all') - .asFunction(); - -final subaddressAddNewNative = oxenApi - .lookup>('subaddress_add_row') - .asFunction(); - -final subaddressSetLabelNative = oxenApi - .lookup>('subaddress_set_label') - .asFunction(); +import 'package:oxen_coin/src/native/subaddress_list.dart' as subaddress_list; +import 'package:oxen_coin/oxen_coin_structs.dart'; void refreshSubaddresses({int accountIndex}) => - subaddressRefreshNative(accountIndex); + subaddress_list.subaddressRefreshNative(accountIndex); List getAllSubaddresses() { - final size = subaddressSizeNative(); - final subaddressAddressesPointer = subaddressGetAllNative(); + final size = subaddress_list.subaddressSizeNative(); + final subaddressAddressesPointer = subaddress_list.subaddressGetAllNative(); final subaddressAddresses = subaddressAddressesPointer.asTypedList(size); return subaddressAddresses @@ -40,25 +17,11 @@ List getAllSubaddresses() { .toList(); } -void addSubaddressSync({int accountIndex, String label}) { - final labelPointer = Utf8.toUtf8(label); - subaddressAddNewNative(accountIndex, labelPointer); - free(labelPointer); -} - -void setLabelForSubaddressSync( - {int accountIndex, int addressIndex, String label}) { - final labelPointer = Utf8.toUtf8(label); - - subaddressSetLabelNative(accountIndex, addressIndex, labelPointer); - free(labelPointer); -} - void _addSubaddress(Map args) { final label = args['label'] as String; final accountIndex = args['accountIndex'] as int; - addSubaddressSync(accountIndex: accountIndex, label: label); + subaddress_list.addSubaddressSync(accountIndex: accountIndex, label: label); } void _setLabelForSubaddress(Map args) { @@ -66,7 +29,7 @@ void _setLabelForSubaddress(Map args) { final accountIndex = args['accountIndex'] as int; final addressIndex = args['addressIndex'] as int; - setLabelForSubaddressSync( + subaddress_list.setLabelForSubaddressSync( accountIndex: accountIndex, addressIndex: addressIndex, label: label); } diff --git a/oxen_coin/lib/transaction_history.dart b/oxen_coin/lib/transaction_history.dart index daa2eafd..f92cca03 100644 --- a/oxen_coin/lib/transaction_history.dart +++ b/oxen_coin/lib/transaction_history.dart @@ -1,50 +1,20 @@ import 'dart:ffi'; -import 'package:ffi/ffi.dart'; import 'package:flutter/foundation.dart'; -import 'package:oxen_coin/oxen_api.dart'; -import 'package:oxen_coin/util/signatures.dart'; -import 'package:oxen_coin/util/types.dart'; -import 'package:oxen_coin/structs/ut8_box.dart'; -import 'package:oxen_coin/structs/transaction_info_row.dart'; -import 'package:oxen_coin/structs/pending_transaction.dart'; -import 'package:oxen_coin/exceptions/creation_transaction_exception.dart'; +import 'package:oxen_coin/oxen_coin_structs.dart'; +import 'package:oxen_coin/src/native/transaction_history.dart' + as transaction_history; -final transactionsRefreshNative = oxenApi - .lookup>('transactions_refresh') - .asFunction(); +void refreshTransactions() => transaction_history.transactionsRefreshNative(); -final transactionsCountNative = oxenApi - .lookup>('transactions_count') - .asFunction(); - -final transactionsGetAllNative = oxenApi - .lookup>('transactions_get_all') - .asFunction(); - -final transactionCreateNative = oxenApi - .lookup>('transaction_create') - .asFunction(); - -final transactionCommitNative = oxenApi - .lookup>('transaction_commit') - .asFunction(); - -final transactionEstimateFeeNative = oxenApi - .lookup>( - 'transaction_estimate_fee') - .asFunction(); - -void refreshTransactions() => transactionsRefreshNative(); - -int countOfTransactions() => transactionsCountNative(); +int countOfTransactions() => transaction_history.transactionsCountNative(); int estimateTransactionFee(int priorityRaw, {int recipients = 1}) => - transactionEstimateFeeNative(priorityRaw, recipients); + transaction_history.transactionEstimateFeeNative(priorityRaw, recipients); List getAllTransactions() { - final size = transactionsCountNative(); - final transactionsPointer = transactionsGetAllNative(); + final size = transaction_history.transactionsCountNative(); + final transactionsPointer = transaction_history.transactionsGetAllNative(); final transactionsAddresses = transactionsPointer.asTypedList(size); return transactionsAddresses @@ -52,54 +22,10 @@ List getAllTransactions() { .toList(); } -PendingTransactionDescription createTransactionSync( - {String address, String amount, int priorityRaw, int accountIndex = 0}) { - final addressPointer = Utf8.toUtf8(address); - final amountPointer = amount != null ? Utf8.toUtf8(amount) : nullptr; - final errorMessagePointer = allocate(); - final pendingTransactionRawPointer = allocate(); - final created = transactionCreateNative( - addressPointer, - amountPointer, - priorityRaw, - accountIndex, - errorMessagePointer, - pendingTransactionRawPointer) != - 0; - - free(addressPointer); - - if (amountPointer != nullptr) { - free(amountPointer); - } - - if (!created) { - final message = errorMessagePointer.ref.getValue(); - free(errorMessagePointer); - throw CreationTransactionException(message: message); - } - - return PendingTransactionDescription( - amount: pendingTransactionRawPointer.ref.amount, - fee: pendingTransactionRawPointer.ref.fee, - hash: pendingTransactionRawPointer.ref.getHash(), - pointerAddress: pendingTransactionRawPointer.address); -} - -void commitTransactionFromPointerAddress({int address}) => commitTransaction( - transactionPointer: Pointer.fromAddress(address)); - -void commitTransaction({Pointer transactionPointer}) { - final errorMessagePointer = allocate(); - final isCommited = - transactionCommitNative(transactionPointer, errorMessagePointer) != 0; - - if (!isCommited) { - final message = errorMessagePointer.ref.getValue(); - free(errorMessagePointer); - throw CreationTransactionException(message: message); - } -} +void commitTransactionFromPointerAddress({int address}) => + transaction_history.commitTransaction( + transactionPointer: + Pointer.fromAddress(address)); PendingTransactionDescription _createTransactionSync(Map args) { final address = args['address'] as String; @@ -107,7 +33,7 @@ PendingTransactionDescription _createTransactionSync(Map args) { final priorityRaw = args['priorityRaw'] as int; final accountIndex = args['accountIndex'] as int; - return createTransactionSync( + return transaction_history.createTransactionSync( address: address, amount: amount, priorityRaw: priorityRaw, diff --git a/oxen_coin/lib/wallet.dart b/oxen_coin/lib/wallet.dart index 7fb9ce01..293b21ac 100644 --- a/oxen_coin/lib/wallet.dart +++ b/oxen_coin/lib/wallet.dart @@ -1,23 +1,13 @@ import 'dart:async'; -import 'dart:typed_data'; import 'dart:ffi'; +import 'dart:typed_data'; + import 'package:ffi/ffi.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; -import 'package:oxen_coin/oxen_api.dart'; -import 'package:oxen_coin/util/convert_utf8_to_string.dart'; -import 'package:oxen_coin/util/signatures.dart'; -import 'package:oxen_coin/util/types.dart'; -import 'package:oxen_coin/exceptions/setup_wallet_exception.dart'; - -// Listener event types constants - -const newBlockEvent = 0; -const refreshedEvent = 1; -const updatedEvent = 2; -const moneyReceivedEvent = 3; -const moneySpentEvent = 4; -const unconfirmedMoneyReceivedEvent = 5; +import 'package:oxen_coin/src/exceptions/setup_wallet_exception.dart'; +import 'package:oxen_coin/src/native/wallet.dart' as oxen_wallet; +import 'package:oxen_coin/src/util/convert_utf8_to_string.dart'; int _boolToInt(bool value) => value ? 1 : 0; @@ -26,130 +16,32 @@ final statusSyncChannel = final oxenMethodChannel = MethodChannel('oxen_coin'); -final getFileNameNative = oxenApi - .lookup>('get_filename') - .asFunction(); - -final getSeedNative = - oxenApi.lookup>('seed').asFunction(); - -final getAddressNative = oxenApi - .lookup>('get_address') - .asFunction(); - -final getFullBalanceNative = oxenApi - .lookup>('get_full_balance') - .asFunction(); - -final getUnlockedBalanceNative = oxenApi - .lookup>('get_unlocked_balance') - .asFunction(); - -final getCurrentHeightNative = oxenApi - .lookup>('get_current_height') - .asFunction(); - -final getNodeHeightNative = oxenApi - .lookup>('get_node_height') - .asFunction(); - -final isConnectedNative = oxenApi - .lookup>('is_connected') - .asFunction(); - -final setupNodeNative = oxenApi - .lookup>('setup_node') - .asFunction(); - -final startRefreshNative = oxenApi - .lookup>('start_refresh') - .asFunction(); - -final connecToNodeNative = oxenApi - .lookup>('connect_to_node') - .asFunction(); - -final setRefreshFromBlockHeightNative = oxenApi - .lookup>( - 'set_refresh_from_block_height') - .asFunction(); - -final setRecoveringFromSeedNative = oxenApi - .lookup>( - 'set_recovering_from_seed') - .asFunction(); +int getSyncingHeight() => oxen_wallet.getSyncingHeightNative(); -final storeNative = - oxenApi.lookup>('store').asFunction(); +bool isNeededToRefresh() => oxen_wallet.isNeededToRefreshNative() != 0; -final setListenerNative = oxenApi.lookupFunction< - Void Function(), void Function()>('set_listener'); +bool isNewTransactionExist() => oxen_wallet.isNewTransactionExistNative() != 0; -final getSyncingHeightNative = oxenApi - .lookup>('get_syncing_height') - .asFunction(); +String getFilename() => + convertUTF8ToString(pointer: oxen_wallet.getFileNameNative()); -final isNeededToRefreshNative = oxenApi - .lookup>('is_needed_to_refresh') - .asFunction(); - -final isNewTransactionExistNative = oxenApi - .lookup>( - 'is_new_transaction_exist') - .asFunction(); - -final getSecretViewKeyNative = oxenApi - .lookup>('secret_view_key') - .asFunction(); - -final getPublicViewKeyNative = oxenApi - .lookup>('public_view_key') - .asFunction(); - -final getSecretSpendKeyNative = oxenApi - .lookup>('secret_spend_key') - .asFunction(); - -final getPublicSpendKeyNative = oxenApi - .lookup>('public_spend_key') - .asFunction(); - -final closeCurrentWalletNative = oxenApi - .lookup>('close_current_wallet') - .asFunction(); - -final onStartupNative = oxenApi - .lookup>('on_startup') - .asFunction(); - -final rescanBlockchainAsyncNative = oxenApi - .lookup>('rescan_blockchain') - .asFunction(); - -int getSyncingHeight() => getSyncingHeightNative(); - -bool isNeededToRefresh() => isNeededToRefreshNative() != 0; - -bool isNewTransactionExist() => isNewTransactionExistNative() != 0; - -String getFilename() => convertUTF8ToString(pointer: getFileNameNative()); - -String getSeed() => convertUTF8ToString(pointer: getSeedNative()); +String getSeed() => convertUTF8ToString(pointer: oxen_wallet.getSeedNative()); String getAddress({int accountIndex = 0, int addressIndex = 0}) => - convertUTF8ToString(pointer: getAddressNative(accountIndex, addressIndex)); + convertUTF8ToString( + pointer: oxen_wallet.getAddressNative(accountIndex, addressIndex)); int getFullBalance({int accountIndex = 0}) => - getFullBalanceNative(accountIndex); + oxen_wallet.getFullBalanceNative(accountIndex); int getUnlockedBalance({int accountIndex = 0}) => - getUnlockedBalanceNative(accountIndex); + oxen_wallet.getUnlockedBalanceNative(accountIndex); -int getCurrentHeight() => getCurrentHeightNative(); +int getCurrentHeight() => oxen_wallet.getCurrentHeightNative(); -int getNodeHeightSync() => getNodeHeightNative(); +int getNodeHeightSync() => oxen_wallet.getNodeHeightNative(); -bool isConnectedSync() => isConnectedNative() != 0; +bool isConnectedSync() => oxen_wallet.isConnectedNative() != 0; bool setupNodeSync( {String address, @@ -170,7 +62,7 @@ bool setupNodeSync( } final errorMessagePointer = allocate(); - final isSetupNode = setupNodeNative( + final isSetupNode = oxen_wallet.setupNodeNative( addressPointer, loginPointer, passwordPointer, @@ -191,35 +83,29 @@ bool setupNodeSync( return isSetupNode; } -void startRefreshSync() => startRefreshNative(); +void startRefreshSync() => oxen_wallet.startRefreshNative(); -Future connectToNode() async => connecToNodeNative() != 0; +Future connectToNode() async => oxen_wallet.connecToNodeNative() != 0; void setRefreshFromBlockHeight({int height}) => - setRefreshFromBlockHeightNative(height); + oxen_wallet.setRefreshFromBlockHeightNative(height); void setRecoveringFromSeed({bool isRecovery}) => - setRecoveringFromSeedNative(_boolToInt(isRecovery)); - -void storeSync() { - final pathPointer = Utf8.toUtf8(''); - storeNative(pathPointer); - free(pathPointer); -} + oxen_wallet.setRecoveringFromSeedNative(_boolToInt(isRecovery)); -void closeCurrentWallet() => closeCurrentWalletNative(); +void closeCurrentWallet() => oxen_wallet.closeCurrentWalletNative(); String getSecretViewKey() => - convertUTF8ToString(pointer: getSecretViewKeyNative()); + convertUTF8ToString(pointer: oxen_wallet.getSecretViewKeyNative()); String getPublicViewKey() => - convertUTF8ToString(pointer: getPublicViewKeyNative()); + convertUTF8ToString(pointer: oxen_wallet.getPublicViewKeyNative()); String getSecretSpendKey() => - convertUTF8ToString(pointer: getSecretSpendKeyNative()); + convertUTF8ToString(pointer: oxen_wallet.getSecretSpendKeyNative()); String getPublicSpendKey() => - convertUTF8ToString(pointer: getPublicSpendKeyNative()); + convertUTF8ToString(pointer: oxen_wallet.getPublicSpendKeyNative()); class SyncListener { SyncListener(this.onNewBlock, this.onNewTransaction) { @@ -250,39 +136,39 @@ class SyncListener { _initialSyncHeight = 0; _updateSyncInfoTimer ??= Timer.periodic(Duration(milliseconds: 1200), (_) async { - if (isNewTransactionExist()) { - onNewTransaction?.call(); - } + if (isNewTransactionExist()) { + onNewTransaction?.call(); + } - var syncHeight = getSyncingHeight(); + var syncHeight = getSyncingHeight(); - if (syncHeight <= 0) { - syncHeight = getCurrentHeight(); - } + if (syncHeight <= 0) { + syncHeight = getCurrentHeight(); + } - if (_initialSyncHeight <= 0) { - _initialSyncHeight = syncHeight; - } + if (_initialSyncHeight <= 0) { + _initialSyncHeight = syncHeight; + } - final bchHeight = await getNodeHeightOrUpdate(syncHeight); + final bchHeight = await getNodeHeightOrUpdate(syncHeight); - if (_lastKnownBlockHeight == syncHeight || syncHeight == null) { - return; - } + if (_lastKnownBlockHeight == syncHeight || syncHeight == null) { + return; + } - _lastKnownBlockHeight = syncHeight; - final track = bchHeight - _initialSyncHeight; - final diff = track - (bchHeight - syncHeight); - final ptc = diff <= 0 ? 0.0 : diff / track; - final left = bchHeight - syncHeight; + _lastKnownBlockHeight = syncHeight; + final track = bchHeight - _initialSyncHeight; + final diff = track - (bchHeight - syncHeight); + final ptc = diff <= 0 ? 0.0 : diff / track; + final left = bchHeight - syncHeight; - if (syncHeight < 0 || left < 0) { - return; - } + if (syncHeight < 0 || left < 0) { + return; + } - // 1. Actual new height; 2. Blocks left to finish; 3. Progress in percents; - onNewBlock?.call(syncHeight, left, ptc); - }); + // 1. Actual new height; 2. Blocks left to finish; 3. Progress in percents; + onNewBlock?.call(syncHeight, left, ptc); + }); } void stop() => _updateSyncInfoTimer?.cancel(); @@ -291,13 +177,13 @@ class SyncListener { SyncListener setListeners(void Function(int, int, double) onNewBlock, void Function() onNewTransaction) { final listener = SyncListener(onNewBlock, onNewTransaction); - setListenerNative(); + oxen_wallet.setListenerNative(); return listener; } -void onStartup() => onStartupNative(); +void onStartup() => oxen_wallet.onStartupNative(); -void _storeSync(Object _) => storeSync(); +void _storeSync(Object _) => oxen_wallet.storeSync(); bool _setupNodeSync(Map args) { final address = args['address'] as String; @@ -340,4 +226,4 @@ Future isConnected() => compute(_isConnected, 0); Future getNodeHeight() => compute(_getNodeHeight, 0); -void rescanBlockchainAsync() => rescanBlockchainAsyncNative(); +void rescanBlockchainAsync() => oxen_wallet.rescanBlockchainAsyncNative(); diff --git a/oxen_coin/lib/wallet_manager.dart b/oxen_coin/lib/wallet_manager.dart index 189ae0e1..30b6868c 100644 --- a/oxen_coin/lib/wallet_manager.dart +++ b/oxen_coin/lib/wallet_manager.dart @@ -1,140 +1,12 @@ -import 'dart:ffi'; import 'package:ffi/ffi.dart'; import 'package:flutter/foundation.dart'; -import 'package:oxen_coin/oxen_api.dart'; -import 'package:oxen_coin/util/convert_utf8_to_string.dart'; -import 'package:oxen_coin/util/signatures.dart'; -import 'package:oxen_coin/util/types.dart'; -import 'package:oxen_coin/exceptions/wallet_creation_exception.dart'; -import 'package:oxen_coin/exceptions/wallet_restore_from_keys_exception.dart'; -import 'package:oxen_coin/exceptions/wallet_restore_from_seed_exception.dart'; - -final createWalletNative = oxenApi - .lookup>('create_wallet') - .asFunction(); - -final restoreWalletFromSeedNative = oxenApi - .lookup>( - 'restore_wallet_from_seed') - .asFunction(); - -final restoreWalletFromKeysNative = oxenApi - .lookup>( - 'restore_wallet_from_keys') - .asFunction(); - -final isWalletExistNative = oxenApi - .lookup>('is_wallet_exist') - .asFunction(); - -final loadWalletNative = oxenApi - .lookup>('load_wallet') - .asFunction(); - -void createWalletSync( - {String path, String password, String language, int nettype = 0}) { - final pathPointer = Utf8.toUtf8(path); - final passwordPointer = Utf8.toUtf8(password); - final languagePointer = Utf8.toUtf8(language); - final errorMessagePointer = allocate(); - final isWalletCreated = createWalletNative(pathPointer, passwordPointer, - languagePointer, nettype, errorMessagePointer) != - 0; - - free(pathPointer); - free(passwordPointer); - free(languagePointer); - - if (!isWalletCreated) { - throw WalletCreationException( - message: convertUTF8ToString(pointer: errorMessagePointer)); - } -} - -bool isWalletExistSync({String path}) { - final pathPointer = Utf8.toUtf8(path); - final isExist = isWalletExistNative(pathPointer) != 0; - - free(pathPointer); - - return isExist; -} - -void restoreWalletFromSeedSync( - {String path, - String password, - String seed, - int nettype = 0, - int restoreHeight = 0}) { - final pathPointer = Utf8.toUtf8(path); - final passwordPointer = Utf8.toUtf8(password); - final seedPointer = Utf8.toUtf8(seed); - final errorMessagePointer = allocate(); - final isWalletRestored = restoreWalletFromSeedNative( - pathPointer, - passwordPointer, - seedPointer, - nettype, - restoreHeight, - errorMessagePointer) != - 0; - - free(pathPointer); - free(passwordPointer); - free(seedPointer); - - if (!isWalletRestored) { - throw WalletRestoreFromSeedException( - message: convertUTF8ToString(pointer: errorMessagePointer)); - } -} - -void restoreWalletFromKeysSync( - {String path, - String password, - String language, - String address, - String viewKey, - String spendKey, - int nettype = 0, - int restoreHeight = 0}) { - final pathPointer = Utf8.toUtf8(path); - final passwordPointer = Utf8.toUtf8(password); - final languagePointer = Utf8.toUtf8(language); - final addressPointer = Utf8.toUtf8(address); - final viewKeyPointer = Utf8.toUtf8(viewKey); - final spendKeyPointer = Utf8.toUtf8(spendKey); - final errorMessagePointer = allocate(); - final isWalletRestored = restoreWalletFromKeysNative( - pathPointer, - passwordPointer, - languagePointer, - addressPointer, - viewKeyPointer, - spendKeyPointer, - nettype, - restoreHeight, - errorMessagePointer) != - 0; - - free(pathPointer); - free(passwordPointer); - free(languagePointer); - free(addressPointer); - free(viewKeyPointer); - free(spendKeyPointer); - - if (!isWalletRestored) { - throw WalletRestoreFromKeysException( - message: convertUTF8ToString(pointer: errorMessagePointer)); - } -} +import 'package:oxen_coin/src/native/wallet_manager.dart' as wallet_manager; void loadWallet({String path, String password, int nettype = 0}) { final pathPointer = Utf8.toUtf8(path); final passwordPointer = Utf8.toUtf8(password); - loadWalletNative(pathPointer, passwordPointer, nettype); + wallet_manager.loadWalletNative(pathPointer, passwordPointer, nettype); free(pathPointer); free(passwordPointer); } @@ -145,7 +17,8 @@ void _createWallet(Map args) { final language = args['language'] as String; final nettype = args['nettype'] as int; - createWalletSync(path: path, password: password, language: language, nettype: nettype); + wallet_manager.createWalletSync( + path: path, password: password, language: language, nettype: nettype); } void _restoreFromSeed(Map args) { @@ -155,8 +28,12 @@ void _restoreFromSeed(Map args) { final restoreHeight = args['restoreHeight'] as int; final nettype = args['nettype'] as int; - restoreWalletFromSeedSync( - path: path, password: password, seed: seed, restoreHeight: restoreHeight, nettype: nettype); + wallet_manager.restoreWalletFromSeedSync( + path: path, + password: password, + seed: seed, + restoreHeight: restoreHeight, + nettype: nettype); } void _restoreFromKeys(Map args) { @@ -169,7 +46,7 @@ void _restoreFromKeys(Map args) { final viewKey = args['viewKey'] as String; final spendKey = args['spendKey'] as String; - restoreWalletFromKeysSync( + wallet_manager.restoreWalletFromKeysSync( path: path, password: password, language: language, @@ -183,7 +60,8 @@ void _restoreFromKeys(Map args) { Future _openWallet(Map args) async => loadWallet(path: args['path'], password: args['password']); -bool _isWalletExist(String path) => isWalletExistSync(path: path); +bool _isWalletExist(String path) => + wallet_manager.isWalletExistSync(path: path); void openWallet({String path, String password, int nettype = 0}) async => loadWallet(path: path, password: password, nettype: nettype); diff --git a/privacy-policy.md b/privacy-policy.md index f789dda0..4dfcf651 100644 --- a/privacy-policy.md +++ b/privacy-policy.md @@ -23,7 +23,7 @@ Information We Collect Automatically When You Use the Services we can view and access: Apple may collect IP address where you downloaded the app, your name, phone number Apple ID, etc. -Oxen does not programatically collect any data about your usage of the Oxen Mobile Wallet, we can +Oxen does not programmatically collect any data about your usage of the Oxen Mobile Wallet, we can only view the statistics that Apple keeps. ### 3. Use of Information diff --git a/pubspec.lock b/pubspec.lock index e7a082d7..d34fb6e2 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,42 +7,42 @@ packages: name: _fe_analyzer_shared url: "https://pub.dartlang.org" source: hosted - version: "12.0.0" + version: "14.0.0" analyzer: dependency: transitive description: name: analyzer url: "https://pub.dartlang.org" source: hosted - version: "0.40.6" + version: "0.41.2" archive: dependency: transitive description: name: archive url: "https://pub.dartlang.org" source: hosted - version: "2.0.13" + version: "3.1.2" args: dependency: transitive description: name: args url: "https://pub.dartlang.org" source: hosted - version: "1.6.0" + version: "2.0.0" asn1lib: dependency: transitive description: name: asn1lib url: "https://pub.dartlang.org" source: hosted - version: "0.6.5" + version: "1.0.0" async: dependency: transitive description: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0-nullsafety.1" + version: "2.5.0" auto_size_text: dependency: "direct main" description: @@ -56,189 +56,189 @@ packages: name: barcode_scan url: "https://pub.dartlang.org" source: hosted - version: "3.0.1" + version: "2.0.2" basic_utils: dependency: "direct main" description: name: basic_utils url: "https://pub.dartlang.org" source: hosted - version: "1.9.3" + version: "3.0.0-nullsafety.3" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.1" + version: "2.1.0" build: dependency: transitive description: name: build url: "https://pub.dartlang.org" source: hosted - version: "1.6.0" + version: "1.6.2" build_config: dependency: transitive description: name: build_config url: "https://pub.dartlang.org" source: hosted - version: "0.4.5" + version: "0.4.6" build_daemon: dependency: transitive description: name: build_daemon url: "https://pub.dartlang.org" source: hosted - version: "2.1.4" + version: "2.1.10" build_resolvers: dependency: "direct dev" description: name: build_resolvers url: "https://pub.dartlang.org" source: hosted - version: "1.5.1" + version: "1.5.3" build_runner: dependency: "direct dev" description: name: build_runner url: "https://pub.dartlang.org" source: hosted - version: "1.10.11" + version: "1.11.5" build_runner_core: dependency: transitive description: name: build_runner_core url: "https://pub.dartlang.org" source: hosted - version: "6.1.5" + version: "6.1.10" built_collection: dependency: transitive description: name: built_collection url: "https://pub.dartlang.org" source: hosted - version: "4.3.2" + version: "5.0.0" built_value: dependency: transitive description: name: built_value url: "https://pub.dartlang.org" source: hosted - version: "7.1.0" + version: "8.0.4" characters: dependency: transitive description: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.3" + version: "1.1.0" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0" checked_yaml: dependency: transitive description: name: checked_yaml url: "https://pub.dartlang.org" source: hosted - version: "1.0.2" + version: "1.0.4" cli_util: dependency: transitive description: name: cli_util url: "https://pub.dartlang.org" source: hosted - version: "0.1.4" + version: "0.3.0" clock: dependency: transitive description: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.1" + version: "1.1.0" code_builder: dependency: transitive description: name: code_builder url: "https://pub.dartlang.org" source: hosted - version: "3.4.0" + version: "3.7.0" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.15.0-nullsafety.3" + version: "1.15.0" convert: dependency: transitive description: name: convert url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" + version: "3.0.0" crypto: dependency: transitive description: name: crypto url: "https://pub.dartlang.org" source: hosted - version: "2.1.4" + version: "3.0.1" cupertino_icons: dependency: "direct main" description: name: cupertino_icons url: "https://pub.dartlang.org" source: hosted - version: "0.1.3" + version: "1.0.2" dart_style: dependency: transitive description: name: dart_style url: "https://pub.dartlang.org" source: hosted - version: "1.3.9" + version: "1.3.12" dartx: dependency: transitive description: name: dartx url: "https://pub.dartlang.org" source: hosted - version: "0.5.0" + version: "0.7.1" date_range_picker: dependency: "direct main" description: name: date_range_picker url: "https://pub.dartlang.org" source: hosted - version: "1.0.6" + version: "1.0.7" devicelocale: dependency: "direct main" description: name: devicelocale url: "https://pub.dartlang.org" source: hosted - version: "0.2.3" + version: "0.4.1" dio: dependency: "direct main" description: name: dio url: "https://pub.dartlang.org" source: hosted - version: "3.0.7" + version: "4.0.0" encrypt: dependency: "direct main" description: name: encrypt url: "https://pub.dartlang.org" source: hosted - version: "4.0.2" + version: "5.0.0" esys_flutter_share: dependency: "direct main" description: @@ -252,7 +252,7 @@ packages: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0" ffi: dependency: transitive description: @@ -266,14 +266,14 @@ packages: name: file url: "https://pub.dartlang.org" source: hosted - version: "5.2.1" + version: "6.1.0" fixnum: dependency: transitive description: name: fixnum url: "https://pub.dartlang.org" source: hosted - version: "0.10.11" + version: "1.0.0" flutter: dependency: "direct main" description: flutter @@ -292,7 +292,7 @@ packages: name: flutter_launcher_icons url: "https://pub.dartlang.org" source: hosted - version: "0.7.5" + version: "0.9.0" flutter_localizations: dependency: "direct main" description: flutter @@ -304,28 +304,28 @@ packages: name: flutter_mobx url: "https://pub.dartlang.org" source: hosted - version: "1.1.0+1" + version: "1.1.0+2" flutter_plugin_android_lifecycle: dependency: transitive description: name: flutter_plugin_android_lifecycle url: "https://pub.dartlang.org" source: hosted - version: "1.0.8" + version: "2.0.1" flutter_secure_storage: dependency: "direct main" description: name: flutter_secure_storage url: "https://pub.dartlang.org" source: hosted - version: "3.3.3" + version: "4.1.0" flutter_slidable: dependency: "direct main" description: name: flutter_slidable url: "https://pub.dartlang.org" source: hosted - version: "0.5.5" + version: "0.5.7" flutter_test: dependency: "direct dev" description: flutter @@ -342,7 +342,7 @@ packages: name: glob url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "2.0.1" graphs: dependency: transitive description: @@ -356,7 +356,7 @@ packages: name: hive url: "https://pub.dartlang.org" source: hosted - version: "1.4.4+1" + version: "1.6.0-nullsafety.2" hive_flutter: dependency: "direct main" description: @@ -377,7 +377,7 @@ packages: name: http url: "https://pub.dartlang.org" source: hosted - version: "0.12.2" + version: "0.13.1" http_multi_server: dependency: transitive description: @@ -391,84 +391,84 @@ packages: name: http_parser url: "https://pub.dartlang.org" source: hosted - version: "3.1.4" + version: "4.0.0" image: dependency: transitive description: name: image url: "https://pub.dartlang.org" source: hosted - version: "2.1.12" + version: "3.0.2" intl: dependency: "direct main" description: name: intl url: "https://pub.dartlang.org" source: hosted - version: "0.16.1" + version: "0.17.0" io: dependency: transitive description: name: io url: "https://pub.dartlang.org" source: hosted - version: "0.3.4" + version: "0.3.5" js: dependency: transitive description: name: js url: "https://pub.dartlang.org" source: hosted - version: "0.6.2" + version: "0.6.3" json_annotation: dependency: transitive description: name: json_annotation url: "https://pub.dartlang.org" source: hosted - version: "3.0.1" + version: "4.0.1" local_auth: dependency: "direct main" description: name: local_auth url: "https://pub.dartlang.org" source: hosted - version: "0.6.2+3" + version: "1.1.2" logging: dependency: transitive description: name: logging url: "https://pub.dartlang.org" source: hosted - version: "0.11.4" + version: "1.0.1" matcher: dependency: transitive description: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10-nullsafety.1" + version: "0.12.10" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.3" + version: "1.3.0" mime: dependency: transitive description: name: mime url: "https://pub.dartlang.org" source: hosted - version: "0.9.6+3" + version: "1.0.0" mobx: dependency: "direct main" description: name: mobx url: "https://pub.dartlang.org" source: hosted - version: "1.2.1+1" + version: "1.2.1+4" mobx_codegen: dependency: "direct dev" description: @@ -476,20 +476,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.2" - node_interop: - dependency: transitive - description: - name: node_interop - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.1" - node_io: + nested: dependency: transitive description: - name: node_io + name: nested url: "https://pub.dartlang.org" source: hosted - version: "1.1.1" + version: "1.0.0" oxen_coin: dependency: "direct main" description: @@ -510,28 +503,21 @@ packages: name: package_info url: "https://pub.dartlang.org" source: hosted - version: "0.4.1" - password: - dependency: "direct main" - description: - name: password - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.0" + version: "2.0.0" path: dependency: transitive description: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety.1" + version: "1.8.0" path_provider: dependency: "direct main" description: name: path_provider url: "https://pub.dartlang.org" source: hosted - version: "1.6.11" + version: "1.6.28" path_provider_linux: dependency: transitive description: @@ -545,7 +531,7 @@ packages: name: path_provider_macos url: "https://pub.dartlang.org" source: hosted - version: "0.0.4+3" + version: "0.0.4+8" path_provider_platform_interface: dependency: transitive description: @@ -566,91 +552,77 @@ packages: name: pedantic url: "https://pub.dartlang.org" source: hosted - version: "1.9.0" + version: "1.11.0" petitparser: dependency: transitive description: name: petitparser url: "https://pub.dartlang.org" source: hosted - version: "2.4.0" + version: "4.0.2" platform: dependency: transitive description: name: platform url: "https://pub.dartlang.org" source: hosted - version: "2.2.1" + version: "3.0.0" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.0.2" + version: "1.0.3" pointycastle: dependency: transitive description: name: pointycastle url: "https://pub.dartlang.org" source: hosted - version: "1.0.2" + version: "3.0.1" pool: dependency: transitive description: name: pool url: "https://pub.dartlang.org" source: hosted - version: "1.4.0" + version: "1.5.0" process: dependency: transitive description: name: process url: "https://pub.dartlang.org" source: hosted - version: "3.0.13" - protobuf: - dependency: transitive - description: - name: protobuf - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.1" + version: "4.2.1" provider: dependency: "direct main" description: name: provider url: "https://pub.dartlang.org" source: hosted - version: "3.2.0" + version: "5.0.0" pub_semver: dependency: transitive description: name: pub_semver url: "https://pub.dartlang.org" source: hosted - version: "1.4.4" + version: "2.0.0" pubspec_parse: dependency: transitive description: name: pubspec_parse url: "https://pub.dartlang.org" source: hosted - version: "0.1.5" + version: "0.1.8" qr: dependency: "direct main" description: name: qr url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" - quiver: - dependency: transitive - description: - name: quiver - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.3" + version: "2.0.0" rxdart: dependency: "direct main" description: @@ -658,13 +630,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.22.6" - share: - dependency: "direct main" - description: - name: share - url: "https://pub.dartlang.org" - source: hosted - version: "0.6.4+3" shared_preferences: dependency: "direct main" description: @@ -678,14 +643,14 @@ packages: name: shared_preferences_linux url: "https://pub.dartlang.org" source: hosted - version: "0.0.2+1" + version: "0.0.2+4" shared_preferences_macos: dependency: transitive description: name: shared_preferences_macos url: "https://pub.dartlang.org" source: hosted - version: "0.0.1+10" + version: "0.0.1+11" shared_preferences_platform_interface: dependency: transitive description: @@ -706,21 +671,21 @@ packages: name: shared_preferences_windows url: "https://pub.dartlang.org" source: hosted - version: "0.0.1+3" + version: "0.0.2+3" shelf: dependency: transitive description: name: shelf url: "https://pub.dartlang.org" source: hosted - version: "0.7.7" + version: "1.1.0" shelf_web_socket: dependency: transitive description: name: shelf_web_socket url: "https://pub.dartlang.org" source: hosted - version: "0.2.3" + version: "0.2.4+1" sky_engine: dependency: transitive description: flutter @@ -732,175 +697,175 @@ packages: name: source_gen url: "https://pub.dartlang.org" source: hosted - version: "0.9.8" + version: "0.9.10+3" source_span: dependency: transitive description: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety.2" + version: "1.8.0" stack_trace: dependency: transitive description: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.10.0-nullsafety.1" + version: "1.10.0" stream_channel: dependency: transitive description: name: stream_channel url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.1" + version: "2.1.0" stream_transform: dependency: transitive description: name: stream_transform url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "2.0.0" string_scanner: dependency: transitive description: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.1" + version: "1.1.0" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19-nullsafety.2" + version: "0.2.19" time: dependency: transitive description: name: time url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "2.0.0" timing: dependency: transitive description: name: timing url: "https://pub.dartlang.org" source: hosted - version: "0.1.1+2" + version: "0.1.1+3" typed_data: dependency: transitive description: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.3" + version: "1.3.0" url_launcher: dependency: "direct main" description: name: url_launcher url: "https://pub.dartlang.org" source: hosted - version: "5.7.10" + version: "6.0.3" url_launcher_linux: dependency: transitive description: name: url_launcher_linux url: "https://pub.dartlang.org" source: hosted - version: "0.0.1+1" + version: "2.0.0" url_launcher_macos: dependency: transitive description: name: url_launcher_macos url: "https://pub.dartlang.org" source: hosted - version: "0.0.1+7" + version: "2.0.0" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.0.9" + version: "2.0.1" url_launcher_web: dependency: transitive description: name: url_launcher_web url: "https://pub.dartlang.org" source: hosted - version: "0.1.5+1" + version: "2.0.0" url_launcher_windows: dependency: transitive description: name: url_launcher_windows url: "https://pub.dartlang.org" source: hosted - version: "0.0.1+3" + version: "2.0.0" uuid: dependency: "direct main" description: name: uuid url: "https://pub.dartlang.org" source: hosted - version: "2.2.2" + version: "3.0.3" vector_math: dependency: transitive description: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.3" + version: "2.1.0" watcher: dependency: transitive description: name: watcher url: "https://pub.dartlang.org" source: hosted - version: "0.9.7+15" + version: "1.0.0" web_socket_channel: dependency: transitive description: name: web_socket_channel url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.0" win32: dependency: transitive description: name: win32 url: "https://pub.dartlang.org" source: hosted - version: "1.7.4" + version: "1.7.4+1" xdg_directories: dependency: transitive description: name: xdg_directories url: "https://pub.dartlang.org" source: hosted - version: "0.1.0" + version: "0.1.2" xml: dependency: transitive description: name: xml url: "https://pub.dartlang.org" source: hosted - version: "3.6.1" + version: "5.0.2" yaml: dependency: "direct main" description: name: yaml url: "https://pub.dartlang.org" source: hosted - version: "2.2.1" + version: "3.1.0" sdks: - dart: ">=2.10.0 <2.11.0" - flutter: ">=1.22.0 <2.0.0" + dart: ">=2.12.0 <3.0.0" + flutter: ">=1.26.0-17.6.pre" diff --git a/pubspec.yaml b/pubspec.yaml index 77bea5a0..db02fd99 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,14 +11,14 @@ description: A Wallet for Oxen. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.0.3+3 +version: 1.0.4+4 # keytool -genkey -v -keystore c:\Users\konst\key.jks -storetype JKS -keyalg RSA -keysize 2048 -validity 10000 -alias key environment: sdk: ">=2.7.0 <3.0.0" - flutter: ">=1.17.5" + flutter: ">=1.22.6" dependencies: flutter: @@ -26,40 +26,39 @@ dependencies: flutter_localizations: sdk: flutter flutter_cupertino_localizations: ^1.0.1 - intl: ^0.16.1 - url_launcher: ^5.7.10 - qr: ^1.3.0 - uuid: ^2.2.2 + intl: ^0.17.0 + url_launcher: ^6.0.3 + qr: ^2.0.0 + uuid: ^3.0.3 shared_preferences: ^0.5.12+4 - flutter_secure_storage: ^3.2.1+1 - provider: ^3.1.0 + flutter_secure_storage: ^4.1.0 + provider: ^5.0.0 rxdart: ^0.22.2 - yaml: ^2.1.16 - barcode_scan: any - http: ^0.12.0+2 - path_provider: ^1.6.11 + yaml: ^3.1.0 + barcode_scan: any # outdated and should be replaced + http: ^0.13.1 + path_provider: ^1.6.28 mobx: ^1.2.1+1 - flutter_mobx: 1.1.0+1 - flutter_slidable: ^0.5.3 - share: ^0.6.2+1 + flutter_mobx: ^1.1.0+2 + flutter_slidable: ^0.5.7 + # share: ^0.6.2+1 esys_flutter_share: ^1.0.2 - date_range_picker: ^1.0.6 - dio: 3.0.7 + date_range_picker: ^1.0.7 + dio: ^4.0.0 oxen_coin: path: ./oxen_coin hive: ^1.4.4+1 hive_flutter: ^0.3.1 - local_auth: ^0.6.2+3 - package_info: ^0.4.0+13 - devicelocale: ^0.2.1 + local_auth: ^1.1.0 + package_info: ^2.0.0 + devicelocale: ^0.4.1 auto_size_text: ^2.1.0 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^0.1.2 - encrypt: ^4.0.0 - password: ^1.0.0 - basic_utils: ^1.0.8 + cupertino_icons: ^1.0.2 + encrypt: ^5.0.0 + basic_utils: ^3.0.0-nullsafety.3 dev_dependencies: flutter_test: @@ -68,7 +67,7 @@ dev_dependencies: build_resolvers: ^1.5.1 mobx_codegen: ^1.1.0+1 hive_generator: ^0.8.1 - flutter_launcher_icons: ^0.7.4 + flutter_launcher_icons: ^0.9.0 pedantic: ^1.8.0 @@ -95,6 +94,7 @@ flutter: assets: - assets/images/ - assets/node_list.yml + - assets/testnet_node_list.yml - assets/changelog.yml - assets/text/ - assets/faq/