mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-25 05:58:42 +01:00
New design (#2797)
* refactor node form UI to use new ListItem model * add new list item row types and widgets * add style wrapper * remove print [skip ci] * Integrate new design mockup with ViewModel (#2653) * feat: add Lightning Network support for Bitcoin wallets * refactor: rename `fiatConvertationStore` to `fiatConversionStore` for consistency and update related occurrences across codebase * feat: enhance address validation with Lightning Network invoice support for BTC & refactor wallet type/token checks in view model * feat: add support for Lightning invoice detection, refactor MWEB deposit/withdraw actions, and integrate Lightning transaction creation with updated priority handling * feat: add method to retrieve unused Spark deposit address for Bitcoin wallets * feat: add Breez API key support and update secrets handling for Bitcoin Lightning wallet integration in workflows * chore: update Breez SDK dependency to version 0.3.4 in pubspec files * Add bitcoin secrets config [skip ci] * feat: extend Lightning wallet functionality with transaction history fetching * feat: add LNURL-pay address detection and support in address parsing flow for Bitcoin Lightning integration * refactor: simplify `ReceivePageOption` logic * refactor: centralize `PaymentURI` generation logic across wallet types * feat: enhance `PaymentURI` handling with asynchronous support and Lightning-specific functionality * refactor: streamline `PaymentURI` logic and remove redundant URI implementations across wallet types * refactor: remove redundant debug print statement from `bitcoin_wallet_addresses.dart` * refactor: improve consistency in widget styling and centralized label logic, add Bitcoin Lightning deposit/withdraw support * feat: reload balance and tx history after sending a lightning transaction * feat: improve address formatting for human-readable addresses and update the default LNURL domain * fix: merge conflicts * feat: add error handling for LightningWallet initialization and adjust transaction direction logic * integrate homepage from new ui mockup * fix import * feat: add Lightning Network support for Bitcoin wallets * refactor: rename `fiatConvertationStore` to `fiatConversionStore` for consistency and update related occurrences across codebase * feat: enhance address validation with Lightning Network invoice support for BTC & refactor wallet type/token checks in view model * feat: add support for Lightning invoice detection, refactor MWEB deposit/withdraw actions, and integrate Lightning transaction creation with updated priority handling * feat: add method to retrieve unused Spark deposit address for Bitcoin wallets * feat: add Breez API key support and update secrets handling for Bitcoin Lightning wallet integration in workflows * chore: update Breez SDK dependency to version 0.3.4 in pubspec files * Add bitcoin secrets config [skip ci] * feat: extend Lightning wallet functionality with transaction history fetching * feat: add LNURL-pay address detection and support in address parsing flow for Bitcoin Lightning integration * refactor: simplify `ReceivePageOption` logic * refactor: centralize `PaymentURI` generation logic across wallet types * feat: enhance `PaymentURI` handling with asynchronous support and Lightning-specific functionality * refactor: streamline `PaymentURI` logic and remove redundant URI implementations across wallet types * refactor: remove redundant debug print statement from `bitcoin_wallet_addresses.dart` * refactor: improve consistency in widget styling and centralized label logic, add Bitcoin Lightning deposit/withdraw support * feat: reload balance and tx history after sending a lightning transaction * feat: improve address formatting for human-readable addresses and update the default LNURL domain * fix: merge conflicts * feat: add error handling for LightningWallet initialization and adjust transaction direction logic * integrate homepage from new ui mockup * fix import * add new-ui dir to pubspec_base * minor layout fixes * cleanup navbar logic * Modify navbar behaviour * smooth color change when selecting navbar options * open old ui pages with new ui action buttons * feat: working navbar in new ui * fix: minor sizing tweaks * CW-1266-integrate-bitcoin-lightning-through-spark-sdk (#2623) * feat: add Lightning Network support for Bitcoin wallets * refactor: rename `fiatConvertationStore` to `fiatConversionStore` for consistency and update related occurrences across codebase * feat: enhance address validation with Lightning Network invoice support for BTC & refactor wallet type/token checks in view model * feat: add support for Lightning invoice detection, refactor MWEB deposit/withdraw actions, and integrate Lightning transaction creation with updated priority handling * feat: add method to retrieve unused Spark deposit address for Bitcoin wallets * feat: add Breez API key support and update secrets handling for Bitcoin Lightning wallet integration in workflows * chore: update Breez SDK dependency to version 0.3.4 in pubspec files * Add bitcoin secrets config [skip ci] * feat: extend Lightning wallet functionality with transaction history fetching * feat: add LNURL-pay address detection and support in address parsing flow for Bitcoin Lightning integration * refactor: simplify `ReceivePageOption` logic * refactor: centralize `PaymentURI` generation logic across wallet types * feat: enhance `PaymentURI` handling with asynchronous support and Lightning-specific functionality * refactor: streamline `PaymentURI` logic and remove redundant URI implementations across wallet types * refactor: remove redundant debug print statement from `bitcoin_wallet_addresses.dart` * refactor: improve consistency in widget styling and centralized label logic, add Bitcoin Lightning deposit/withdraw support * feat: reload balance and tx history after sending a lightning transaction * feat: improve address formatting for human-readable addresses and update the default LNURL domain * fix: merge conflicts * feat: add error handling for LightningWallet initialization and adjust transaction direction logic * feat: enable private transactions by default in LightningWallet and update Breez SDK version to 0.4.2 * minor fixes [skip ci] * chore: fix some minor issues in comments (#2654) Signed-off-by: black5box <black5box@outlook.com> * fix: handle send-all functionality for LightningWallet transactions and adjust amount calculation logic * fix-german (#2659) * chore: update German localization strings for consistency and accuracy * chore: update German localization strings for consistency and accuracy [skip-ci] * feat: add LNURL support for address validation and LightningWallet compatibility, enhance error handling for OpenCryptoPay * fix: adjust LightningWallet amount parsing from 9 to 8 decimal places * feat: add LNURL support in LightningPaymentRequest and LightningWallet * Fix navigation gradient (#2657) * Fix navigation gradient * Fix CONFIG_ARGS formatting in app_config.sh (#2660) * fix: block wrongly parsed addresses (#2656) * fix: block wrongly parsed addresses * fix: move parsed address check into handlePaymentFlow method * fix: Handle QR URLs separately for pay anything flow * feat: add electrum seed support for Lightning * refactor: improve `parseFixed` logic and add comprehensive unit tests for edge cases (#2661) * Update cw_bitcoin/lib/bitcoin_wallet.dart [skip ci] * resolve conflict issue --------- Signed-off-by: black5box <black5box@outlook.com> Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> Co-authored-by: black5box <black5box@outlook.com> Co-authored-by: tuxsudo <tuxsudo@tux.pizza> Co-authored-by: cyan <cyjan@mrcyjanek.net> Co-authored-by: David Adegoke <64401859+Blazebrain@users.noreply.github.com> * remove old code [skip ci] * fix: page widgets rebuilt every time * chore: formatting * remove unused navbar * fix: lightning balance on balance card in lightning mode * fix: return if uri == null * feat: trade history in new ui * feat: enhance balance model with secondary asset handling * feat: unique card colors and icons for coins * wip: card customization * fix: merge conflict * feat: customizable balance card * Fix some spacings and test sizing * Fix formatting * feat: add Lightning Network support for send flow and fee handling adjustments * refactor: remove unnecessary debug print statement in send flow for Lightning Network balance * feat: integrate OpenCryptoPay QR handling in send flow and improve scan action handling logic * feat: add maxDepositClaimFee parameter for Lightning wallet configuration * Switch balance card to RoundedSuperellipse and set color list to Wrap * Fix * Switch to scroll view * refactor: update navigation route from `receive` to `addressPage` in coin action row * feat: bloc-based viewmodel for card customizer * feat: mobx-independent balance card customizer vm * minor fixes * copyWith constructor for cleaner state changes * refactor: wrap `HistorySection` with `Observer` for reactive state updates, update navigation for "Buy" action, and fix incorrect balance property * feat: enhance Bitcoin receive flow with Lightning and SegWit options, update address page registration, and localize coin action labels * feat: add helper methods for Lightning and SegWit receive page options in Bitcoin configuration * feat: add Lightning transaction type handling and enable navigation to transaction details from history * Add more special card types * feat: new ui settings page * feat: add Lightning actions to balance cards, refactor card rendering logic * refactor: update settings page localization, hide unused settings items * feat: add secrets generation for cw_bitcoin, including breezApiKey * Lightning switcher animation (#2725) * Properly animate bitcoin/lightning switcher * Cleanup and fix sizing * Final cleanup * Fix action button colors (#2732) * feat: add hardware wallet type handling and improve parsed address reset flow * Refactor settings page to use generic row items (#2745) * fix android back button in settings on android, force iphone-style transitions on all platforms * move settings page to generic row impl * fix imports * Balance card fixes (#2729) * Update balance card sizing * Fix spacing * Initial font changes (#2731) * Initial font changes * More font changes * feat: integrate Lightning transaction support and improve transaction history UX - Enhanced dashboard to compute confirmations using the first and last transactions. - Updated `BitcoinWallet` to subscribe for and manage Lightning transactions. - Improved `HistoryTile` layout and introduced new leading icons for Lightning transactions. * feat: add unclaimed deposit actions and enhance Lightning event handling * release changes [skip ci] * Enhanced card customizer (#2744) * enhanced card customizer * Fix conflicts * Add missing icons + adjust spacing --------- Co-authored-by: tuxsudo <tuxsudo@tux.pizza> * feat: enhance Lightning invoice handling and address validation - Added support for identifying non-zero amount Lightning invoices. - Refined invoice matching with updated regex patterns. - Improved UI to conditionally display addresses in sending confirmations. - Adjusted logic to handle zero-value Lightning invoices. * feat: add `isPayjoinAvailable` computed property and update Payjoin visibility logic * feat: improve Lightning wallet handling and block explorer logic * update patch version [skip ci] --------- Signed-off-by: black5box <black5box@outlook.com> Co-authored-by: Konstantin Ullrich <konstantinullrich12@gmail.com> Co-authored-by: OmarHatem <omarh.ismail1@gmail.com> Co-authored-by: tuxsudo <tuxsudo@tux.pizza> Co-authored-by: black5box <black5box@outlook.com> Co-authored-by: cyan <cyjan@mrcyjanek.net> Co-authored-by: David Adegoke <64401859+Blazebrain@users.noreply.github.com> * minor new ui fixes * padding for new row separators * remove debug prints * New design improve nav bar (#2763) * tweak navigation bar touch targets and sizing * feat: add tests for identifying zero and non-zero amount Lightning invoices (#2772) * feat: add tests for identifying zero and non-zero amount Lightning invoices - Added unit tests to validate recognition of Bolt11 zero-amount and non-zero-amount invoices. - Enhanced `isBolt11ZeroInvoice` function to handle `lightning:` prefixed invoices. * fix: correct currency selection logic for Lightning wallets in Send ViewModel * feat: improve Lightning wallet initialization and address generation - Updated `init` method in `LightningWallet` to return a boolean for initialization success, adding error handling for failed setups. - Integrated dynamic name generation using `generateName` for Lightning wallet addresses. * refactor: relocate `generate_name` to `cw_core` and update imports * chore: update breez sdk (#2789) * Wire tokens and NFTs to new Home page (#2804) * wiring for assets on home page * fix imports * wire nft page in new ui * fix nft display in new ui * fix totalWidth calc * add tokens button + bugfixes * add tokens button + bugfixes * fix late initialization * fix history section exception * fixes * Add haptic feedback (#2813) * Add haptic feedback * Add haptic feedback to correct ActionButton * Return bottom gradient (#2808) * Add IgnorePointer to bottom nav gradient (#2822) * New design send page (#2791) * new ui send page (wip) * coin control page improvements * fee settings, new send confirmation modal * minor aesthetic fixes * load network name without cw_bitcoin import * recipient dot cleanup * minor logic fixes * ui polish * - add aliaspay support - fix wording for max button - fix back button action in modals * Sizing changes * dismiss transaction on modal close * sizing fix * - fix remove recipient icon - properly round amounts - fix coin control page spacing * - fix remove recipient icon - properly round amounts - fix coin control page spacing * - fix button sizing - add description for fees page * Update lib/view_model/unspent_coins/unspent_coins_list_view_model.dart [skip ci] * Update lib/view_model/send/send_view_model.dart [skip ci] --------- Co-authored-by: tuxsudo <tuxsudo@tux.pizza> Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * Fix conflicts with dev * Fix conflicts with dev * Add zcash receive options to new design flow * wire in missing settings in new ui * up migration version * formatting * translations * lint.sh * New design receive page (#2834) * feat: viewmodel on new receive page (wip) * feat: address type selector for new ui receive page * fix: minor fixes to touch targets and tap anims * minor ui fix * wip receive page changes * receive page + address selection page * receive page + address selection page * add font * add svg * minor ui fixes * receive page fixes/improvements * change receive page modal design * further ui polish * icons for infobox * fix restricted import * fix closing label modal * - fix balance card appearance on address page - fix search bar position - fix mweb position in address type selector * receive page fixes * fix anonpay * remove restricted import * fixes * fix receive on zec * simplify chain id * lint fix * fix migration numbering Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * fix migration version Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * fix settings * readability * fix formatting * move dismiss infobox to vm * disable addr rotation on zcash * remove _setEffects * translations --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * New UI Sync bar (#2831) * syncbar * small styling fixes * modify duration * lint.sh * syncHeavy wallet types Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * update translations, add pull-down refresh, separate top bar components to files --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * alphabetize * New design balance card icons (#2824) * Add the other crypto card icons * Update Zano, add Zcash and Arbitrum * Update default card style and available gradients * Fix broken preview * Fix animation * Color combinations fix * Minor updates * Add decred icon * if(!mounted) return; * New design minor fixes (#2842) * More minor fixes * Update modal sheet behaviour * Update missing currency image links * List row and navigation bar tweaks * Minor navbar fix * New UI Swap page (#2829) * new ui send page (wip) * coin control page improvements * fee settings, new send confirmation modal * minor aesthetic fixes * load network name without cw_bitcoin import * recipient dot cleanup * minor logic fixes * ui polish * - add aliaspay support - fix wording for max button - fix back button action in modals * Sizing changes * dismiss transaction on modal close * sizing fix * - fix remove recipient icon - properly round amounts - fix coin control page spacing * - fix remove recipient icon - properly round amounts - fix coin control page spacing * - fix button sizing - add description for fees page * swap page * fixes * further fixes * merge * png -> svg * minor condition changes * lint.sh * fix swapsxyz isCentralized Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * translations, readability improvements, remove debug rows * cleanup * alphabetize strings * small bugfixes --------- Co-authored-by: tuxsudo <tuxsudo@tux.pizza> Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * fix new ui coin control page layout (#2843) * New design lightning flows (#2821) * new ui send page (wip) * coin control page improvements * fee settings, new send confirmation modal * minor aesthetic fixes * load network name without cw_bitcoin import * recipient dot cleanup * minor logic fixes * ui polish * - add aliaspay support - fix wording for max button - fix back button action in modals * Sizing changes * dismiss transaction on modal close * sizing fix * - fix remove recipient icon - properly round amounts - fix coin control page spacing * - fix remove recipient icon - properly round amounts - fix coin control page spacing * - fix button sizing - add description for fees page * lightning flow wip * lightning flow wip #2 * lightning withdraw flow * lightning deposit flow (wip, currently works but really janky) * send from external + bugfixes * merge fixes * merge fixes * fixes * Update lib/new-ui/widgets/send_page/l2_action_wallet_selector.dart --------- Co-authored-by: tuxsudo <tuxsudo@tux.pizza> Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * fix covered create wallet button * fix: close modal after transaction commitment in Send ViewModel (#2858) * New design tweaks (#2847) * Add more haptic feedback * Fix background gradients and modify SafeArea in home_page.dart * Fix history padding, fix divider color, disable Charts, update base_page.dart appbar style, fix nav bar border + height on iOS * Lightning Username setup flow for new UI (#2845) * lightning username edit/create flow * fix button color * fix layout * fixes * remove character limitation for internal builds * remove dependency on cw_bitcoin * fix username requirements * fixes * condition fix * Update lib/new-ui/pages/lightning_username_page.dart --------- Co-authored-by: OmarHatem <omarh.ismail1@gmail.com> * keyboard hide overlay for iphone * New design UI tweaks (#2870) * Fix nav height on Android * Fix blue gradient * Update transactions view and minor UI tweaks * CW1011-Allow-users-to-view-bitcoin-amounts-in-sats (#2678) * feat: introduce `formatFixed` integration and sats display preference * feat: enable display of Bitcoin amounts in satoshis and refactor amount formatting logic * refactor: consolidate fee formatting logic and add support for MWEB availability check * feat: add support for satoshi-based amount display and enhance fiat conversion logic * feat: add support for satoshi-based amount parsing, formatting, and display preferences across buy/sell flow and UI adjustments * feat: implement `AmountParsingProxy` with unit tests for parsing and formatting crypto amounts in various display modes * feat: replace `preferBalanceInSats` with `displayAmountsInSatoshi` and introduce `AmountParsingProxy` for unified crypto amount handling across the app * refactor: streamline amount formatting logic and enhance unspent coins handling with `AmountParsingProxy` integration for unified crypto processing * refactor: replace direct bitcoin amount formatting with `AmountParsingProxy` and remove debug print in `exchange_view_model` * refactor: replace `getIt<AmountParsingProxy>` with `_appStore.amountParsingProxy`, remove redundant dependencies across view models and adjust related imports * refactor: remove redundant `AmountParsingProxy` registration in DI setup to simplify dependency management [skip ci] * feat: add getCryptoSymbol to simplify the Symbol selection between Sats and Bitcoin * fix: merge conflict * Revert "fix: merge conflict" This reverts commit 6debdaa0466747d6fcc85d6bd0be115e6fb4d856. * fix import [skip ci] * feat: remove number formatter from exchangeVM * refactor: replace `SettingsStore` with `AppStore` across view models, integrate `AmountParsingProxy` for amount formatting, and clean up redundant imports * refactor: remove debug print statements from `getCryptoOutputAmount` in `AmountParsingProxy` * feat: enhance fee and deposit amount formatting with crypto symbols and improved parsing * feat: integrate `AmountParsingProxy` for improved Bitcoin amount parsing in `PaymentURI` and fiat-to-crypto conversion * feat: append crypto symbols to Bitcoin transaction amounts in dashboard view * feat: improve crypto amount parsing in SendCard widget and Output view model using `AmountParsingProxy` * feat: conditionally show Bitcoin amount display setting for Bitcoin wallets and refactor `DisplaySettingsViewModel` to use `AppStore` * refactor: clean up redundant imports in `bitcoin_wallet.dart` and `electrum_wallet.dart` * feat: append crypto symbols to fee values in send and cake pay card widgets for improved clarity * refactor: replace `currencyTitle` with `currency` across widgets and integrate `AmountParsingProxy` for improved crypto amount formatting and parsing * refactor: improve validation logic and amount parsing in SendCard and Output widgets * refactor: standardize amount parsing and formatting logic with `AmountParsingProxy` across ViewModels and widgets * refactor: `AppStore` remove debug prints * refactor: rename amount parsing/formatting methods for better clarity in tests [skip ci] * fix: AmountParsingProxy and optimize `cryptoAmount` handling in BuySellViewModel * fix: adjust balance calculation for Monero and Wownero to exclude unlocked amount from full balance * fix: correct `_appStore` usage and format adjustments in `SendViewModel` * feat: enhance satoshi on new-ui * feat: add computed property for `amountParsingProxy` in AppStore * feat: localize Bitcoin amount display option and update asset history UI * feat: improve exchange amount processing and refactor fiat conversion values --------- Co-authored-by: OmarHatem <omarh.ismail1@gmail.com> * New design unconfirmed balance widget (#2861) * unconfirmed balance display widget for new ui * unconfirmed balance display widget for new ui * merge * alphabetize translations * New UI fixes (#2860) * fix asset top bar * improve wordmark in large qr mode on receive * receive option fixes * exchange -> swap * send page fixes * update pubspec base * go to home on wallet change * nicer card customizer * don't show account name for single-account wallets * special card design for lightning * fiat amount bar improvement * update translations * add sending indicator * fix non-number input to receive amount * de-jank fiat input * nicer balance card animation when switching btc-ln * nicer balance card animation when switching btc-ln * fix backup password input * display time+date in history * fix error on buy * fix incorrect wallet showing in wallets tab * fix wallet list layout * only show coin control option on hasCoinControl * better error on send page * fiat value in coin control * add zano to isSyncHeeavy * fix keys in settings * fix assets condition * fix import * remove BasePage dependency in RescanPage * add missing settings * show send/receive in swap confirm sheet * fix receive background on ios * fix assets spacing * fix trade tile sizing * fix padding * fix icon in wallet select modal * change text on swap initial modal * spacing * l2 send external modal styling * fix null on swap confirm sheet * change infobox text * add chain icon to asset widgets * add chain icon to asset widgets * disable swap page reset * fix wallet list page layout * revert merge conflict issue [skip ci] --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * Account Customizer for new UI Monero (#2859) * new ui monero account management (wip) * account reordering wip * account reordering wip * account reordering (works) * cards view compact mode * format * translation * name generation * fix for non-xmr wallets * } * allow reset order, fallback on improperly saved order * translation * fix for pre-reorder wallet loading * fix loading * - save cards on modal slide-down - always load active account as first * fix card customizer close anim * fixes * fix condition * merge * fix migration versions * use updated flutter rust bridge in lightning and payjoin * mvp fixes * new ui mweb mask/unmask * fallback for balance card amount format * New UI tweaks (#2872) * Update lightning transaction history view * Add BNB card design * Color, padding, and radius fixes * Fix wallet list bottom section & gradient * Update Apps page * reformat * nicer sat picker * fix keyboard * chain badges * enhance styling for card customizer * mweb settings icon * mweb chain badge * update wording in settings * bnb chain icon * always round send amount * always round all amount * token placeholder * Fix ModernButton colors (#2888) * Fix ModernButton colors * Minor * guard for unconfirmed balance widget * fix error on wallet switch * New design mvp fixes (#2890) * mvp fixes * fallback for balance card amount format * reformat * nicer sat picker * fix keyboard * chain badges * enhance styling for card customizer * bnb chain icon * always round send amount * always round all amount * guard for unconfirmed balance widget * fix error on wallet switch * Update lib/new-ui/pages/account_customizer.dart Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * fixes --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * Install Protoc in reusable build workflow Added step to install Protoc before building. * Fix protobuf-compiler installation step Update package list before installing protobuf-compiler. * Modify Protoc installation command to handle update errors Change the install command for Protoc to avoid failure on update. * Streamline Lightning wallet balance management and crypto handling (#2893) * refactor: streamline crypto amount handling and improve Lightning balance management - Removed redundancy in balance calculations for Lightning wallets. - Centralized crypto symbol creation with `selectedCryptoCurrencySymbol`. - Enhanced crypto amount handling and formatting with `AmountParsingProxy`. - Simplified logic for displaying Lightning balances across UI components. * refactor: remove sats display logic and unused formatting from BalanceCard widget * chore: update breez-sdk-spark-flutter to latest commit * feat: enhance crypto amount formatting with localized separators and max decimals * fixes * fix: handle null balance records in Lightning wallet cards * New design add validators (#2894) * add validation, fix node form * Add validator to send amount input field Added a validator to the amount input field. --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * Disable 'addresses' button on receive screen for BTC LN (#2896) * feat: Disable addresses button on receive screen for BTC LN * chore: remove debug print in uri computation * feat: introduce swap action toggle and UI updates * feat: conditionally display Zcash card setting for Zcash wallets in Display Settings * Fix leading button color (#2898) * fix amount format * fix loadCards * pre-beta fixes * cyjan: fix: order being null for certain index * pre-beta fixes v2 (#2905) * minor context check [skip ci] * persist fee priority for zcash (#2907) * Add support for Zcash address detection and amount logic refactor (#2906) * refactor: consolidate crypto amount handling with `displayAmount` and `cryptoCurrencySymbol`, improve fiat conversion logic, and optimize token currency management * refactor: simplify `displayAmount` logic and remove unused `_rawAmount` property * feat: add support for Zcash transparent address detection and simplify amount conversion logic * Improve Lightning invoice handling and crypto formatting (#2908) * fix: update Lightning address regex to support additional formats and prefixes * fix: conditionally render advanced settings dropdown based on fees or coin control availability * feat: enhance address detection, Lightning invoice handling, and crypto formatting * feat: improve Lightning invoice parsing and crypto amount formatting on send page * chore: remove unnecessary exception printing in `getBolt11Amount` function * Fix lightning swap * import fix (hide Mac;) * parse -> tryParse * Transaction Commited -> Transaction Sent! * feat: enable clipboard paste for deposit and receive addresses in swap page (#2909) * fix: resolving LNURLS (#2911) * new UI pre-beta fixes (#2910) * fixes * parse -> tryParse * change sat display settings style * minor fix until hive is removed [skip ci] * Remove redundant logic and improve send page features (#2913) * fix: update crypto amount parsing logic in send confirmation sheet * feat: Added checkbox to toggle Litecoin MWEB coins on the send page. fix: LNUrlPayRecord * fix: remove redundant `.withLocalSeperator()` from fiat balance formatting logic * fix: update Bitcoin amount display titles to align with consistent formatting standards * force boolean to update (#2916) * force boolean to update * fix bnb balance * fixes #4 (#2917) * only display sats for btc wallets in card customizer * fix: resolve async handling of Lightning transaction history in `fetchTransactions` method (#2919) * New design pre beta fixes 5 (#2918) * improve l2 deposit/withdraw flow layouts * add copied indicator * SafeArea * fix addr rotation * fix styling of lightning switcher * no raster graphics in new ui please * optimization for assets/history section * remove unused bloc provider * optimize dashboard + fix duplicated name * disable lightning mode on wallet change * remove broken animation * safearea around nodes page * fix rescan page layout * fix exception on really fast switching * remove broken blur * card customizer autosave * remove debug print * add better fallbacks for currency titles * revert due to broken animation * fix send page crash * fix chain title for arb * fix cupertino date picker * Update lib/new-ui/widgets/receive_page/receive_bottom_buttons.dart [skip ci] --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * feat: add focus management and external address validation to send page forms - Introduced `focusNode` to `NewSendAddressInput` for better input handling. - Enhanced address validation by synchronizing with parsed external addresses. - Updated logic for non-MWEB unspent coin type in send actions. - Suppressed unnecessary exception logs in Bitcoin wallet transactions. * New design swap page fixes (#2923) * swap page fixes * add scroll controller * comment * tweak refresh pulldown (#2924) * add copy button to l2 send external modal (#2925) * account customizer padding * feat: lightning transaction fetching and Lightning wallet integration (#2927) - Allow nullable `height` in `ElectrumTransactionInfo`. - Add `fromDate` support for filtered Lightning transaction history retrieval. - Simplify Lightning address handling logic in wallet addresses. - Optimize Lightning wallet transaction syncing based on the last Lightning transaction date. - Clean up imports in `electrum_transaction_history.dart`. * New balance handeling (#2931) * feat: lightning transaction fetching and Lightning wallet integration - Allow nullable `height` in `ElectrumTransactionInfo`. - Add `fromDate` support for filtered Lightning transaction history retrieval. - Simplify Lightning address handling logic in wallet addresses. - Optimize Lightning wallet transaction syncing based on the last Lightning transaction date. - Clean up imports in `electrum_transaction_history.dart`. * feat: add support for Lightning balance in wallet snapshot and initialization * Disable Lightning switcher for hardware wallets (#2932) * fix: disable Lightning switcher for hardware wallets * fix: update wallet type check to use `isSoftwareWallet` for Lightning support * New design pre beta fixes 6 (#2929) * fix swaps from ln * prevent crash if ln balance isn't loaded yet * fix currency icons disappearing on swap confirm sheet * fix swap fiat validation * display ln at top on swap currency selector in btc wallets * update icon for card options * display default fiat and crypto at top of currency picker * fix fiat amount bar formatting * add modal scroll controller to provider logs * revert premature optimization (sizing issue) * New design pre beta fixes 7 (#2935) * fix swaps from ln * prevent crash if ln balance isn't loaded yet * fix currency icons disappearing on swap confirm sheet * fix swap fiat validation * display ln at top on swap currency selector in btc wallets * update icon for card options * display default fiat and crypto at top of currency picker * fix fiat amount bar formatting * add modal scroll controller to provider logs * revert premature optimization (sizing issue) * copyable trade id * disable swaptrade on ln * set isFixedRate properly * update color * display trocador provider on swap confirmation sheet * add "exchange rate is fixed" text --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * updated l2 modal (#2934) * display names for send addr input (#2936) * fix: MWEB toggle logic on send page (#2938) * feat: add retry logic to `getAddress` in Lightning wallet (#2939) * Enhance crypto address handling and add Lightning invoice support (#2937) * feat: enhance crypto address handling and add Lightning invoice support - Added `_overrideFromCryptoCurrency` and `_overrideToCryptoCurrency` for Zcash unified and shielded addresses. - Improved Lightning invoice generation with `getLightningInvoice` for Bitcoin wallets. - Updated error handling in exchange provider to accommodate API changes. - Introduced Lightning invoice fallback for deposit and receive addresses in ExchangeViewModel. * feat: Added `_overrideFromCryptoCurrency` and `_overrideToCryptoCurrency` for Zcash unified and shielded addresses. * fix pay anything on paste button (#2940) * Prevent multiple paste triggering any pay flow * fixup fiat input (#2941) * New design small fixes (#2920) * Fix optical symmetry for swipe to send text * Update nav bar icons * Top bar and card tweaks * Update paddings --------- Co-authored-by: malik1004x <malikowskirobert@gmail.com> * fix erc20 balance display (#2944) * fix erc20 balance display * route through cw_evm * New design beta fixes (#2943) * fix asset tile layout * fix card customizer keyboard jumping * fix cardsview exception * fix swap modal height * remove syncbar percentage * add copy icon to trade id * fix trade confirmation appearing twice * remove useless mweb setting * add tag to contact currency selector text * prettify picker for ln * fix receive crash * always route to NewSendPage * handle ln amount on paste * handle ln amount on payment request creation * prettify ln errors * readd "blocks" word * register deeplinks * Enhance bitcoin error message [skip ci] * fix support chat page (#2946) * new-design-fix-confirm-sheet-parsing-error (#2950) * feat: use bigint instead of doubles * feat: use bigint instead of doubles * feat: use bigint instead of doubles * fix: also show sats in confirm sheet * fix: also show sats in confirm sheet * feat: localize amounts and cap amount in tx history (#2951) * fix: linux CI * feat: localize token balances (#2954) * fix: send amount hww (#2955) * chore: use latest breez sdk (#2953) * fix addAddressWord * fixes (#2957) * receive fixes (#2959) * fix: stabilize android build by increasing jvm heap size (#2958) * fix: lighting by having the new SDK Save into a new location (#2961) * New design rc fixes (#2960) * additional safeguards for isSyncHeavy check * fix account name getting reset * update base icon * fix units in swap * readd memos to zec * New design minor UI (#2956) * Wrap text for list item widgets * Update lightning address and QR icons * Update near intents icon * Fix share issue * Fix (#2963) * fix tx amount formatting * rename currency * change wording for zec memo * fix base icon * fix app icon script [skip ci] * New design rc fixes 2 (#2965) * fix account name changes * fix spacing on addresses page * add payjoin copy modal * improve memo ui * potential fix for ci * Set default address for zcash and bitcoin for buy flow (#2979) * new monero.com icons (#2980) * parse and validate deposit amount from api (#2967) * generic fixes * minor context fix [skip ci] * Generic fixes (#2982) - solana send all bug - pasting SOL address triggering payanything flow - add more address types for payanything detection * rescan fix * about page (#2974) Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * update settings icons/wording (#2973) * Changelog modal (New UI) (#2971) * add new changelog modal * typo * print -> printV * Update lib/new-ui/widgets/changelog_modal.dart [skip ci] --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * fix node form (#2970) * new-design-add-breez-logging (#2984) * feat: add Lightning log export functionality for Bitcoin wallets * feat: add multilingual support for Lightning log export strings * feat: add Lightning support toggle in settings and translations for multiple languages * refactor: simplify `setUseLightning` by removing redundant store update * Update cw_bitcoin/lib/electrum_wallet.dart [skip ci] --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * minor fixes [skip ci] * minor context fixe [skip ci] * fix: make walletinfoid unique to prevent duplicates (#2969) * fix: walletconnect not working in new design builds (#2993) * new-design-fix-mweb-coin-enabling (#2995) * feat: add unspent coin type handling to asset details modal send button * fix: improve German translations for wallet, seed, and Lightning-related strings * fix: update Dutch translations to correct terminology and formatting #2992 (#2997) * fix: drop gn (#3000) * vulnerable seeds popup on new ui (#3004) * update mac os monero.com icons (#3003) * Add Payjoin log export and improve logging behavior (#2999) * fix: suppress exception logging in `getAddress` method of Lightning wallet - Added a try-catch block to avoid unnecessary exception logs during Lightning address retries. * fix: update fallback message for missing Lightning address detection * feat: add Payjoin log export and logging enhancements * fix: localize "Export Payjoin Logs" string in settings page * fix: litecoin mweb balance being included in the second asset (#2998) * Minor fixes (#2994) * Minor fixes * Update lib/new-ui/new_dashboard.dart [skip ci] --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * New design post release fixes (#3002) * fix: only throw on deserialize if kDebugMode is true (#2976) This closes #2972, in debug mode we will still catch these issues but if somehow that happens on production it will fallback to safe default * fix: update docs and dockerfile (#2975) * fix: missing steps for linux build [skip ci] (#2985) * move balance to bigint * move balance to bigint * fix electrum issue * fix unconfirmed balance * fixes * Fix minor issue in Spanish translation (#2991) * Fix minor issue in French translation (#2987) * Fix minor issues in German translation (#2986) * update constructor calls in configure.dart * fixes * remove expnded * V6.0.1 Early Release Candidate --------- Co-authored-by: cyan <cyjan@mrcyjanek.net> Co-authored-by: Edwin den Boer <woordenboer@planet.nl> Co-authored-by: OmarHatem <omarh.ismail1@gmail.com> * Swaps xyz swap enhancements (#2615) * refactor token cache and limit handling * Improve SwapsXYZ external send logic * Improve SwapsXYZ fixed-rate handling * disable fixedRate for SwapsXyz * SwapsXYZ contract call * Add track url for swaps xyz * add arbitrum support * add check for unsupported source tokens * enable SwapsXyzExchangeProvider in provider list * refactor SwapsXYZ provider and trade flow handling * Update exchange_trade_view_model.dart * validate evm call-data * Revert "validate evm call-data" * fix msg value for ERC-20 transfers * fix flag for swapsXYZ send check * Update send_view_model.dart * handle Swaps.xyz method selectors & approvals * fix evm approvals * Improve EVM token approval & receipt handling * merge split token transfers in evm tx * fix token allowance check for approvals * add sourceToken validation to EVM tx creation * prioritizing incoming transactions over fetched outgoing ones * compute net flow when merging transactions * add truncateTrailingText to list items and widgets --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> Co-authored-by: Serhii-Borodenko <17529954+Serhii-Borodenko@users.noreply.github.com> * fix wownero balance (#3007) * fix keyboard (#3006) * fix android build * check if stream is closed [skip ci] * [skip ci] lint * fix: populate amount field (#3009) * fix address rotation exception (#3011) * copy uri with amount from recieve page (#3010) * add "transaction sent!" to swap confirmation sheet (#3012) * fix wallet change on token swaps (#3013) * Fix fee estimation and recipient logic in Lightning Network (#3014) * fix: update `hasMultiRecipient` logic to include non-Lightning coin type check * fix: Lightning Network fee estimation and parsing logic * fix: adjust `sumByBigInt` logic to handle `sendAll` flag in send page * fix: move sqlite migrations to sqlite.dart (#3008) * fix: move sqlite migrations to sqlite.dart fix: backup restore not initializing SQL properly * Update cw_core/lib/db/sqlite.dart --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * feat: update currency symbol handling and extend Electrum transaction details (#3016) * Use isCentralized for truncation trade id [skip ci] * fix: Incorrect token amount for tokens and filter out new spam transactions after swaps (#3015) * New design release swap fixes (#3018) * fix deserialize ln * fix get wallet type for bnb tokens * show extraId for swaps * chore: translate using gpt-5.2 (#3021) * add NEAR send-all validation * tiny ui fixes (#3023) * use double parsing for amount comparison[skip ci] * adjust lightning address error message * feat: add BTC LN support to available methods and payment data handling * feat: add cached Lightning address handling for improved performance (#3025) - Introduced `cachedAddress` field to store and reuse the last fetched Lightning address. - Updated `getLightningAddress` method to fallback on `cachedAddress` if no new address is fetched. - Extended wallet constructors and snapshots to include `cachedLightningAddress` for persistence. * Revert transaction history color * Minor * Apply suggestion from @malik1004x * new-design-post-release-fixes-3 (#3028) * fix unnecessary account switching * fix exception on bad payment amount * fallback for currencies on external swap modal * increase balance card touch target * don't use lightning addresses for exchange * increase max length for ln encoding * don't show infobox on ln receive * fix showing old tx details (null tx on createTransaction) * remove unused import * refactor: relocate `_logStream` declaration in `LightningWallet` for better code organization (#3029) * monero build fix update build numbers fix best rate null [skip ci] * hide swap on mweb asset * minor context fix [skip ci] * Fix Silent payment case * new-ui: fix changelog typo (#3036) Fix typo in changelog for account management. * Modify date format to Month DD YYYY * feat: docker based ci * Fix 2FA continue button location (#3043) * fix: Desktop wallet bugs (#2968) * fix: Desktop wallet bugs - repeated wallets post migration - newly created wallets not showing up - extra large components on desktop, needed maxwidth * chore: Remove duplicate logic * chore: remove formatting * refactor: streamline updateList method and handle multiple calls with a future based mutex * refactor: enhance swipe to confirm with dynamic width handling and improved drag threshold logic * Improve error handling and duplicate invoice handling in Lightning wallet (#3047) * fix: handle duplicate Lightning invoice payments gracefully * fix: improved error handling in Lightning wallet * General improvements (#3030) * General Improvements - Make borderRadius for CTAs uniform 18px - Identify hardware wallets in title of Seeds/Keys page - Make entire wallet tile clickable in wallet list * chore: Simplify check * Update lib/view_model/wallet_keys_view_model.dart [skip ci] --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * homescreen improvements (#3034) * Homescreen improvements - Increase touch area for card editing icon - Tapping any card in the background should move it ino the foreground without closing the account tab - make top left chain icon primary color with full opacity when syncing and 20% when done - add tor indicator to top bar when enabled * fix: remove unneeded color filter * refactor: Adjust structure for tor and sync indicators * minor fixes (#3045) * Enhance LNURL payment integration (#3041) * feat: enhance LNURL support and Lightning payment handling - Improve LNURL decoding and encoding with expanded prefix handling. - Add support for parsing and validating LNURL payment invoices. - Introduce logic for fetching LNURL payment request amounts. - Update payment flow to integrate LNURL and Lightning-specific conditions. - Refactor BOLT11 prefix validation and amount extraction logic. * fix: simplify LNURL invoice validation by removing redundant BOLT11 check --------- Signed-off-by: black5box <black5box@outlook.com> Co-authored-by: Serhii <borodenko.sv@gmail.com> Co-authored-by: malik1004x <malikowskirobert@gmail.com> Co-authored-by: Konstantin Ullrich <konstantinullrich12@gmail.com> Co-authored-by: tuxsudo <tuxsudo@tux.pizza> Co-authored-by: black5box <black5box@outlook.com> Co-authored-by: cyan <cyjan@mrcyjanek.net> Co-authored-by: David Adegoke <64401859+Blazebrain@users.noreply.github.com> Co-authored-by: Konstantin Ullrich <konstantin@cakewallet.com> Co-authored-by: Edwin den Boer <woordenboer@planet.nl> Co-authored-by: Serhii <17529954+serhii-bor@users.noreply.github.com> Co-authored-by: Serhii-Borodenko <17529954+Serhii-Borodenko@users.noreply.github.com> Co-authored-by: rottenwheel <92872541+rottenwheel@users.noreply.github.com>
This commit is contained in:
co-authored by
Serhii
malik1004x
Konstantin Ullrich
tuxsudo
black5box
cyan
David Adegoke
Konstantin Ullrich
Edwin den Boer
Serhii
Serhii-Borodenko
rottenwheel
parent
18c36acc14
commit
084fd6bb1e
@@ -1,4 +1,5 @@
|
||||
import 'package:another_flushbar/flushbar.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/modern_button.dart';
|
||||
import 'package:cake_wallet/utils/show_bar.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -32,7 +33,6 @@ class AuthPage extends StatefulWidget {
|
||||
class AuthPagePinCodeStateImpl extends AuthPageState<AuthPage> {
|
||||
final _key = GlobalKey<ScaffoldState>();
|
||||
final _pinCodeKey = GlobalKey<PinCodeState>();
|
||||
final _backArrowImageDarkTheme = Image.asset('assets/images/close_button.png');
|
||||
ReactionDisposer? _reaction;
|
||||
Flushbar<void>? _authBar;
|
||||
Flushbar<void>? _progressBar;
|
||||
@@ -127,26 +127,23 @@ class AuthPagePinCodeStateImpl extends AuthPageState<AuthPage> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
key: _key,
|
||||
appBar: CupertinoNavigationBar(
|
||||
leading: widget.closable
|
||||
? Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: SizedBox(
|
||||
height: 37,
|
||||
width: 37,
|
||||
child: InkWell(
|
||||
onTap: () => Navigator.of(context).pop(),
|
||||
child: _backArrowImageDarkTheme,
|
||||
),
|
||||
))
|
||||
: Container(),
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
border: null),
|
||||
resizeToAvoidBottomInset: false,
|
||||
body: PinCode((pin, _) => widget.authViewModel.auth(password: pin), (_) => null,
|
||||
widget.authViewModel.pinLength, false, _pinCodeKey));
|
||||
return Container(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 16.0),
|
||||
child: Scaffold(
|
||||
key: _key,
|
||||
appBar: CupertinoNavigationBar(
|
||||
leading: widget.closable
|
||||
? ModernButton(size: 36, icon: Icon(Icons.close), onPressed: Navigator.of(context).pop, iconColor: Theme.of(context).colorScheme.onSurfaceVariant)
|
||||
: Container(),
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
border: null),
|
||||
resizeToAvoidBottomInset: false,
|
||||
body: PinCode((pin, _) => widget.authViewModel.auth(password: pin), (_) => null,
|
||||
widget.authViewModel.pinLength, false, _pinCodeKey)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void dismissFlushBar(Flushbar<dynamic>? bar) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'dart:io';
|
||||
import 'dart:ui';
|
||||
import 'package:cake_wallet/core/execution_state.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
@@ -12,6 +13,7 @@ import 'package:cake_wallet/utils/show_bar.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/backup_view_model.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
@@ -116,7 +118,9 @@ class BackupPage extends BasePage {
|
||||
leftButtonText: S.of(context).seed_alert_back,
|
||||
actionRightButton: () async {
|
||||
Navigator.of(dialogContext).pop();
|
||||
final backup = await backupViewModelBase.exportBackup();
|
||||
final backupFuture = backupViewModelBase.exportBackup();
|
||||
showPersistentActionOverlay(context, backupFuture, text: S.of(context).creating_backup);
|
||||
final backup = await backupFuture;
|
||||
|
||||
if (backup == null) {
|
||||
return;
|
||||
@@ -178,3 +182,53 @@ class BackupPage extends BasePage {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void showPersistentActionOverlay(BuildContext context, Future action, {String text = ""}) async {
|
||||
final navigator = Navigator.of(context, rootNavigator: true);
|
||||
showPopUp(
|
||||
useRootNavigator: true,
|
||||
context: context,
|
||||
builder: (_) => PersistentActionOverlay(text: text),
|
||||
barrierDismissible: false);
|
||||
try {
|
||||
await action;
|
||||
} finally {
|
||||
// this way even if action throws, it'll still pop and not softlock the app
|
||||
navigator.pop();
|
||||
}
|
||||
}
|
||||
|
||||
class PersistentActionOverlay extends StatelessWidget {
|
||||
const PersistentActionOverlay({super.key, required this.text});
|
||||
|
||||
final String text;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(20),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
color: Theme.of(context).colorScheme.surfaceContainer.withAlpha(80)),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(20),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SizedBox(width: 36, height: 36, child: CupertinoActivityIndicator()),
|
||||
if (text.isNotEmpty)
|
||||
Text(text, style: TextStyle(fontSize: 24, fontWeight: FontWeight.w500))
|
||||
],
|
||||
),
|
||||
),
|
||||
))),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,75 +1,89 @@
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/view_model/edit_backup_password_view_model.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
class EditBackupPasswordPage extends BasePage {
|
||||
EditBackupPasswordPage(this.editBackupPasswordViewModel)
|
||||
: textEditingController = TextEditingController() {
|
||||
textEditingController.text = editBackupPasswordViewModel.backupPassword;
|
||||
class EditBackupPasswordPage extends StatefulWidget {
|
||||
EditBackupPasswordPage(this.editBackupPasswordViewModel);
|
||||
|
||||
final EditBackupPasswordViewModel editBackupPasswordViewModel;
|
||||
|
||||
@override
|
||||
State<EditBackupPasswordPage> createState() => _EditBackupPasswordPageState();
|
||||
}
|
||||
|
||||
class _EditBackupPasswordPageState extends State<EditBackupPasswordPage> {
|
||||
final textEditingController = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
textEditingController.text = widget.editBackupPasswordViewModel.backupPassword;
|
||||
textEditingController
|
||||
.addListener(() => editBackupPasswordViewModel.backupPassword = textEditingController.text);
|
||||
|
||||
reaction((_) => editBackupPasswordViewModel.backupPassword, (_) {
|
||||
if (textEditingController.text != editBackupPasswordViewModel.backupPassword) {
|
||||
textEditingController.text = editBackupPasswordViewModel.backupPassword;
|
||||
.addListener(() {
|
||||
if(textEditingController.text != widget.editBackupPasswordViewModel.backupPassword) {
|
||||
widget.editBackupPasswordViewModel.backupPassword = textEditingController.text;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
final EditBackupPasswordViewModel editBackupPasswordViewModel;
|
||||
final TextEditingController textEditingController;
|
||||
|
||||
@override
|
||||
String get title => S.current.edit_backup_password;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(left: 24, right: 24),
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
Center(
|
||||
child: Observer(
|
||||
builder: (_) => BaseTextFormField(
|
||||
autofocus: true,
|
||||
enableSuggestions: false,
|
||||
autocorrect: false,
|
||||
keyboardType: TextInputType.visiblePassword,
|
||||
controller: textEditingController,
|
||||
textStyle: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
Widget build(BuildContext context) {
|
||||
return Column(children: [
|
||||
ModalTopBar(
|
||||
title: S.of(context).edit_backup_password,
|
||||
leadingIcon: Icon(Icons.arrow_back_ios_new),
|
||||
onLeadingPressed: Navigator.of(context).pop,
|
||||
),
|
||||
Expanded(
|
||||
child: SafeArea(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 24, right: 24, bottom: MediaQuery.of(context).viewInsets.bottom),
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
Center(
|
||||
child: Observer(
|
||||
builder: (_) => TextFormField(
|
||||
enableSuggestions: false,
|
||||
autocorrect: false,
|
||||
keyboardType: TextInputType.visiblePassword,
|
||||
controller: textEditingController,
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
child: Observer(
|
||||
builder: (_) => PrimaryButton(
|
||||
onPressed: () => onSave(context),
|
||||
text: S.of(context).save,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
isDisabled: !widget.editBackupPasswordViewModel.canSave,
|
||||
),
|
||||
),
|
||||
bottom: 24,
|
||||
left: 0,
|
||||
right: 0,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
child: Observer(
|
||||
builder: (_) => PrimaryButton(
|
||||
onPressed: () => onSave(context),
|
||||
text: S.of(context).save,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
isDisabled: !editBackupPasswordViewModel.canSave,
|
||||
),
|
||||
),
|
||||
bottom: 24,
|
||||
left: 0,
|
||||
right: 0,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
]);
|
||||
}
|
||||
|
||||
void onSave(BuildContext context) {
|
||||
FocusScope.of(context).unfocus();
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (dialogContext) {
|
||||
@@ -79,7 +93,8 @@ class EditBackupPasswordPage extends BasePage {
|
||||
rightButtonText: S.of(context).ok,
|
||||
leftButtonText: S.of(context).cancel,
|
||||
actionRightButton: () async {
|
||||
await editBackupPasswordViewModel.save();
|
||||
widget.editBackupPasswordViewModel.backupPassword = textEditingController.text;
|
||||
await widget.editBackupPasswordViewModel.save();
|
||||
Navigator.of(dialogContext).pop();
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pop();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:cake_wallet/new-ui/widgets/modern_button.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/themes/core/material_base_theme.dart';
|
||||
import 'package:cake_wallet/themes/core/theme_store.dart';
|
||||
@@ -82,20 +83,17 @@ abstract class BasePage extends StatelessWidget {
|
||||
}
|
||||
|
||||
return MergeSemantics(
|
||||
child: SizedBox(
|
||||
height: 37,
|
||||
width: 37,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 4.0),
|
||||
child: ButtonTheme(
|
||||
minWidth: double.minPositive,
|
||||
child: Semantics(
|
||||
label: S.of(context).seed_alert_back,
|
||||
child: TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: Theme.of(context).colorScheme.onSurface,
|
||||
overlayColor: WidgetStateColor.resolveWith((states) => Colors.transparent),
|
||||
),
|
||||
child: ModernButton(
|
||||
size: 37,
|
||||
icon: Icon(CupertinoIcons.back),
|
||||
iconColor: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
onPressed: () => onClose(context),
|
||||
child: backButton(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -180,11 +178,11 @@ abstract class BasePage extends StatelessWidget {
|
||||
child: Observer(
|
||||
builder: (context) {
|
||||
final backgroundImage = getIt.get<SettingsStore>().backgroundImage;
|
||||
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
image: backgroundImage.isNotEmpty
|
||||
? DecorationImage(
|
||||
image: FileImage(File(backgroundImage)),
|
||||
@@ -193,21 +191,24 @@ abstract class BasePage extends StatelessWidget {
|
||||
: null,
|
||||
// color: Colors.grey[200],
|
||||
),
|
||||
child: Scaffold(
|
||||
key: _scaffoldKey,
|
||||
backgroundColor: pageBackgroundColor(context),
|
||||
resizeToAvoidBottomInset: resizeToAvoidBottomInset,
|
||||
extendBodyBehindAppBar: extendBodyBehindAppBar,
|
||||
endDrawer: endDrawer,
|
||||
appBar: appBar(context),
|
||||
body: SafeArea(
|
||||
left: false,
|
||||
right: false,
|
||||
top: false,
|
||||
bottom: Platform.isAndroid,
|
||||
child: body(context),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top:6.0),
|
||||
child: Scaffold(
|
||||
key: _scaffoldKey,
|
||||
backgroundColor: pageBackgroundColor(context),
|
||||
resizeToAvoidBottomInset: resizeToAvoidBottomInset,
|
||||
extendBodyBehindAppBar: extendBodyBehindAppBar,
|
||||
endDrawer: endDrawer,
|
||||
appBar: appBar(context),
|
||||
body: SafeArea(
|
||||
left: false,
|
||||
right: false,
|
||||
top: false,
|
||||
bottom: Platform.isAndroid,
|
||||
child: body(context),
|
||||
),
|
||||
floatingActionButton: floatingActionButton(context),
|
||||
),
|
||||
floatingActionButton: floatingActionButton(context),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'package:cake_wallet/buy/sell_buy_states.dart';
|
||||
import 'package:cake_wallet/core/address_validator.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
@@ -12,7 +11,7 @@ import 'package:cake_wallet/src/screens/exchange/widgets/mobile_exchange_cards_s
|
||||
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/provider_optoin_tile.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/trail_button.dart';
|
||||
import 'package:cake_wallet/src/screens/send/widgets/extract_address_from_parsed.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
@@ -56,7 +55,7 @@ class BuySellPage extends BasePage {
|
||||
bool get extendBodyBehindAppBar => true;
|
||||
|
||||
@override
|
||||
AppBarStyle get appBarStyle => AppBarStyle.transparent;
|
||||
AppBarStyle get appBarStyle => AppBarStyle.completelyTransparent;
|
||||
|
||||
@override
|
||||
Function(BuildContext)? get pushToNextWidget => (context) {
|
||||
@@ -256,9 +255,7 @@ class BuySellPage extends BasePage {
|
||||
}
|
||||
|
||||
void _setReactions(BuildContext context, BuySellViewModel buySellViewModel) {
|
||||
if (_isReactionsSet) {
|
||||
return;
|
||||
}
|
||||
if (_isReactionsSet) return;
|
||||
|
||||
final fiatAmountController = fiatCurrencyKey.currentState!.amountController;
|
||||
final cryptoAmountController = cryptoCurrencyKey.currentState!.amountController;
|
||||
@@ -269,28 +266,27 @@ class BuySellPage extends BasePage {
|
||||
|
||||
reaction(
|
||||
(_) => buySellViewModel.wallet.name,
|
||||
(String _) => _onWalletNameChange(
|
||||
(_) => _onWalletNameChange(
|
||||
buySellViewModel, buySellViewModel.cryptoCurrency, cryptoCurrencyKey));
|
||||
|
||||
reaction(
|
||||
(_) => buySellViewModel.cryptoCurrency,
|
||||
(CryptoCurrency currency) =>
|
||||
_onCurrencyChange(currency, buySellViewModel, cryptoCurrencyKey));
|
||||
(currency) => _onCurrencyChange(currency, buySellViewModel, cryptoCurrencyKey));
|
||||
|
||||
reaction((_) => buySellViewModel.fiatCurrency,
|
||||
(FiatCurrency currency) => _onCurrencyChange(currency, buySellViewModel, fiatCurrencyKey));
|
||||
(currency) => _onCurrencyChange(currency, buySellViewModel, fiatCurrencyKey));
|
||||
|
||||
reaction((_) => buySellViewModel.fiatAmount, (String amount) {
|
||||
reaction((_) => buySellViewModel.fiatAmount, (amount) {
|
||||
if (fiatCurrencyKey.currentState!.amountController.text != amount) {
|
||||
fiatCurrencyKey.currentState!.amountController.text = amount;
|
||||
}
|
||||
});
|
||||
|
||||
reaction((_) => buySellViewModel.isCryptoCurrencyAddressEnabled, (bool isEnabled) {
|
||||
reaction((_) => buySellViewModel.isCryptoCurrencyAddressEnabled, (isEnabled) {
|
||||
cryptoCurrencyKey.currentState!.isAddressEditable(isEditable: isEnabled);
|
||||
});
|
||||
|
||||
reaction((_) => buySellViewModel.cryptoAmount, (String amount) {
|
||||
reaction((_) => buySellViewModel.cryptoAmount, (amount) {
|
||||
if (cryptoCurrencyKey.currentState!.amountController.text != amount) {
|
||||
cryptoCurrencyKey.currentState!.amountController.text = amount;
|
||||
}
|
||||
@@ -453,6 +449,7 @@ class BuySellPage extends BasePage {
|
||||
fillColor: buySellViewModel.isBuyAction
|
||||
? Theme.of(context).colorScheme.surfaceContainerLow
|
||||
: Theme.of(context).colorScheme.surfaceContainer,
|
||||
useSatoshis: buySellViewModel.useSatoshi,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import 'package:cake_wallet/core/wallet_name_validator.dart';
|
||||
import 'package:cake_wallet/entities/generate_name.dart';
|
||||
import 'package:cw_core/generate_name.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_hardware_restore_view_model.dart';
|
||||
|
||||
@@ -50,7 +50,7 @@ class SelectDeviceManufacturerPage extends BasePage {
|
||||
colorFilter: _colorFilter,
|
||||
),
|
||||
hardwareWalletType: HardwareWalletType.trezor,
|
||||
tag: S.current.new_tag,
|
||||
// tag: S.current.new_tag,
|
||||
),
|
||||
if (Platform.isAndroid) ...[
|
||||
_DeviceManufacturer(
|
||||
@@ -60,7 +60,7 @@ class SelectDeviceManufacturerPage extends BasePage {
|
||||
colorFilter: _colorFilter,
|
||||
),
|
||||
hardwareWalletType: HardwareWalletType.bitbox,
|
||||
tag: S.current.new_tag,
|
||||
// tag: S.current.new_tag,
|
||||
),
|
||||
],
|
||||
_DeviceManufacturer(
|
||||
@@ -70,7 +70,7 @@ class SelectDeviceManufacturerPage extends BasePage {
|
||||
colorFilter: _colorFilter,
|
||||
),
|
||||
hardwareWalletType: HardwareWalletType.cupcake,
|
||||
tag: S.current.new_tag,
|
||||
// tag: S.current.new_tag,
|
||||
),
|
||||
_DeviceManufacturer(
|
||||
image: SvgPicture.asset(
|
||||
@@ -79,7 +79,7 @@ class SelectDeviceManufacturerPage extends BasePage {
|
||||
colorFilter: _colorFilter,
|
||||
),
|
||||
hardwareWalletType: HardwareWalletType.coldcard,
|
||||
tag: S.current.new_tag,
|
||||
// tag: S.current.new_tag,
|
||||
),
|
||||
// _DeviceManufacturer(
|
||||
// image: SvgPicture.asset(
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
import 'package:cake_wallet/core/wallet_name_validator.dart';
|
||||
import 'package:cake_wallet/entities/generate_name.dart';
|
||||
import 'package:cw_core/generate_name.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_hardware_restore_view_model.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
@@ -149,10 +148,9 @@ class _SelectHardwareWalletAccountFormState extends State<SelectHardwareWalletAc
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: SelectButton(
|
||||
image: Image.asset(
|
||||
getCryptoCurrencyForWalletListItem(
|
||||
getCryptoCurrencyIconForWalletListItem(
|
||||
_walletHardwareRestoreVM.type,
|
||||
).iconPath ??
|
||||
'',
|
||||
),
|
||||
height: 24,
|
||||
width: 24,
|
||||
),
|
||||
|
||||
@@ -8,8 +8,10 @@ import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/filter_list_widget.dart';
|
||||
import 'package:cake_wallet/src/screens/wallet_list/filtered_list.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/src/widgets/gradient_background.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||
import 'package:cake_wallet/utils/address_formatter.dart';
|
||||
import 'package:cake_wallet/utils/feature_flag.dart';
|
||||
import 'package:cake_wallet/utils/show_bar.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
|
||||
@@ -21,23 +23,28 @@ import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
|
||||
class ContactListPage extends BasePage {
|
||||
ContactListPage(this.contactListViewModel, this.authService);
|
||||
ContactListPage(this.contactListViewModel, this.authService, {this.showAddButton = false});
|
||||
|
||||
final ContactListViewModel contactListViewModel;
|
||||
final AuthService authService;
|
||||
final bool showAddButton;
|
||||
|
||||
@override
|
||||
bool get gradientBackground => true;
|
||||
|
||||
@override
|
||||
String get title => S.current.address_book;
|
||||
|
||||
@override
|
||||
Widget? trailing(BuildContext context) {
|
||||
if(!showAddButton) return SizedBox.shrink();
|
||||
return MergeSemantics(
|
||||
child: Container(
|
||||
width: 32.0,
|
||||
height: 32.0,
|
||||
width: 36.0,
|
||||
height: 36.0,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
),
|
||||
child: Semantics(
|
||||
label: S.of(context).add_contact,
|
||||
@@ -47,7 +54,7 @@ class ContactListPage extends BasePage {
|
||||
children: <Widget>[
|
||||
Icon(
|
||||
Icons.add,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
size: 22.0,
|
||||
),
|
||||
ButtonTheme(
|
||||
@@ -111,57 +118,59 @@ class _ContactPageBodyState extends State<ContactPageBody> with SingleTickerProv
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(),
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 24, right: 24, bottom: 8),
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: TabBar(
|
||||
return GradientBackground(
|
||||
scaffold: Padding(
|
||||
padding: const EdgeInsets.only(),
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 24, right: 24, bottom: 8),
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: TabBar(
|
||||
controller: _tabController,
|
||||
splashFactory: NoSplash.splashFactory,
|
||||
indicatorSize: TabBarIndicatorSize.label,
|
||||
isScrollable: true,
|
||||
labelStyle: Theme.of(context).textTheme.bodyLarge!.copyWith(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
unselectedLabelStyle: Theme.of(context).textTheme.bodyLarge!.copyWith(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
indicatorColor: Theme.of(context).colorScheme.primary,
|
||||
indicatorPadding: EdgeInsets.zero,
|
||||
labelPadding: EdgeInsets.only(right: 24),
|
||||
tabAlignment: TabAlignment.start,
|
||||
dividerColor: Colors.transparent,
|
||||
padding: EdgeInsets.zero,
|
||||
tabs: [
|
||||
Tab(text: S.of(context).wallets),
|
||||
Tab(text: S.of(context).contact_list_contacts),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: TabBarView(
|
||||
controller: _tabController,
|
||||
splashFactory: NoSplash.splashFactory,
|
||||
indicatorSize: TabBarIndicatorSize.label,
|
||||
isScrollable: true,
|
||||
labelStyle: Theme.of(context).textTheme.bodyLarge!.copyWith(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
unselectedLabelStyle: Theme.of(context).textTheme.bodyLarge!.copyWith(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
indicatorColor: Theme.of(context).colorScheme.primary,
|
||||
indicatorPadding: EdgeInsets.zero,
|
||||
labelPadding: EdgeInsets.only(right: 24),
|
||||
tabAlignment: TabAlignment.start,
|
||||
dividerColor: Colors.transparent,
|
||||
padding: EdgeInsets.zero,
|
||||
tabs: [
|
||||
Tab(text: S.of(context).wallets),
|
||||
Tab(text: S.of(context).contact_list_contacts),
|
||||
children: [
|
||||
Observer(
|
||||
builder: (final BuildContext context) => _buildWalletContacts(context),
|
||||
),
|
||||
ContactListBody(
|
||||
contactListViewModel: widget.contactListViewModel,
|
||||
tabController: _tabController,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: TabBarView(
|
||||
controller: _tabController,
|
||||
children: [
|
||||
Observer(
|
||||
builder: (final BuildContext context) => _buildWalletContacts(context),
|
||||
),
|
||||
ContactListBody(
|
||||
contactListViewModel: widget.contactListViewModel,
|
||||
tabController: _tabController,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -176,6 +185,7 @@ class _ContactPageBodyState extends State<ContactPageBody> with SingleTickerProv
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
padding: EdgeInsets.only(bottom: 128),
|
||||
itemCount: groupedContacts.length * 2,
|
||||
itemBuilder: (context, index) {
|
||||
if (index.isOdd) {
|
||||
@@ -208,8 +218,8 @@ class _ContactPageBodyState extends State<ContactPageBody> with SingleTickerProv
|
||||
expandedAlignment: Alignment.topLeft,
|
||||
backgroundColor: Theme.of(context).colorScheme.surfaceContainer,
|
||||
collapsedBackgroundColor: Theme.of(context).colorScheme.surfaceContainer,
|
||||
collapsedShape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
collapsedShape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(18)),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(18)),
|
||||
children: groupContacts.map((contact) => generateRaw(context, contact)).toList(),
|
||||
),
|
||||
);
|
||||
@@ -244,7 +254,7 @@ class _ContactPageBodyState extends State<ContactPageBody> with SingleTickerProv
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||
borderRadius: BorderRadius.all(Radius.circular(18)),
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
),
|
||||
margin: const EdgeInsets.only(top: 4, bottom: 4, left: 16, right: 16),
|
||||
@@ -317,7 +327,7 @@ class _ContactListBodyState extends State<ContactListBody> {
|
||||
? widget.contactListViewModel.contacts
|
||||
: widget.contactListViewModel.contactsToShow;
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
backgroundColor: Colors.transparent,
|
||||
body: Container(
|
||||
child: FilteredList(
|
||||
list: contacts,
|
||||
@@ -371,7 +381,7 @@ class _ContactListBodyState extends State<ContactListBody> {
|
||||
Container(
|
||||
key: Key('${contact.name}'),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(8)),
|
||||
borderRadius: BorderRadius.all(Radius.circular(18)),
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
),
|
||||
margin: const EdgeInsets.only(top: 4, bottom: 4, left: 16, right: 16),
|
||||
@@ -431,40 +441,43 @@ class _ContactListBodyState extends State<ContactListBody> {
|
||||
'assets/images/filter_icon.png',
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
);
|
||||
return MergeSemantics(
|
||||
child: SizedBox(
|
||||
height: 58,
|
||||
width: 58,
|
||||
child: ButtonTheme(
|
||||
minWidth: double.minPositive,
|
||||
child: Semantics(
|
||||
container: true,
|
||||
child: GestureDetector(
|
||||
onTap: () async {
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (context) => FilterListWidget(
|
||||
initalType: contactListViewModel.orderType,
|
||||
initalAscending: contactListViewModel.ascending,
|
||||
onClose: (bool ascending, FilterListOrderType type) async {
|
||||
contactListViewModel.setAscending(ascending);
|
||||
await contactListViewModel.setOrderType(type);
|
||||
},
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: FeatureFlag.hasNewUi ? 48 : 0),
|
||||
child: MergeSemantics(
|
||||
child: SizedBox(
|
||||
height: 58,
|
||||
width: 58,
|
||||
child: ButtonTheme(
|
||||
minWidth: double.minPositive,
|
||||
child: Semantics(
|
||||
container: true,
|
||||
child: GestureDetector(
|
||||
onTap: () async {
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (context) => FilterListWidget(
|
||||
initalType: contactListViewModel.orderType,
|
||||
initalAscending: contactListViewModel.ascending,
|
||||
onClose: (bool ascending, FilterListOrderType type) async {
|
||||
contactListViewModel.setAscending(ascending);
|
||||
await contactListViewModel.setOrderType(type);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Semantics(
|
||||
label: 'Transaction Filter',
|
||||
button: true,
|
||||
enabled: true,
|
||||
child: Container(
|
||||
height: 36,
|
||||
width: 36,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
),
|
||||
child: filterIcon,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Semantics(
|
||||
label: 'Transaction Filter',
|
||||
button: true,
|
||||
enabled: true,
|
||||
child: Container(
|
||||
height: 36,
|
||||
width: 36,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
),
|
||||
child: filterIcon,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -501,7 +514,7 @@ class DialogService {
|
||||
alertContentTextWidget: AddressFormatter.buildSegmentedAddress(
|
||||
address: contact.address,
|
||||
textAlign: TextAlign.center,
|
||||
walletType: cryptoCurrencyToWalletType(contact.type),
|
||||
walletType: cryptoCurrencyOrTokenToWalletType(contact.type),
|
||||
evenTextStyle: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
fontSize: 16,
|
||||
decoration: TextDecoration.none,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:cake_wallet/core/address_validator.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/send_page/send_address_input.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cw_core/currency.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -12,8 +13,7 @@ import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/address_text_field.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker.dart';
|
||||
|
||||
@@ -22,18 +22,21 @@ class ContactPage extends BasePage {
|
||||
: _formKey = GlobalKey<FormState>(),
|
||||
_nameController = TextEditingController(),
|
||||
_addressController = TextEditingController(),
|
||||
_currencyTypeController = TextEditingController() {
|
||||
_currencyTypeController = TextEditingController(),
|
||||
addressFocusNode = FocusNode(){
|
||||
_nameController.text = contactViewModel.name;
|
||||
_addressController.text = contactViewModel.address;
|
||||
_nameController.addListener(() => contactViewModel.name = _nameController.text);
|
||||
_addressController.addListener(() => contactViewModel.address = _addressController.text);
|
||||
|
||||
autorun((_) => _currencyTypeController.text = contactViewModel.currency?.toString() ?? '');
|
||||
autorun((_) => _currencyTypeController.text = "${contactViewModel.currency?.toString() ?? ""} ${contactViewModel.currency?.tag != null ? "(${contactViewModel.currency?.tag})" : ""}");
|
||||
}
|
||||
|
||||
@override
|
||||
String get title => S.current.contact;
|
||||
|
||||
FocusNode addressFocusNode;
|
||||
|
||||
final ContactViewModel contactViewModel;
|
||||
final GlobalKey<FormState> _formKey;
|
||||
final TextEditingController _nameController;
|
||||
@@ -89,15 +92,17 @@ class ContactPage extends BasePage {
|
||||
if (contactViewModel.currency != null)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20),
|
||||
child: AddressTextField(
|
||||
controller: _addressController,
|
||||
options: [
|
||||
AddressTextFieldOption.paste,
|
||||
AddressTextFieldOption.qrCode,
|
||||
],
|
||||
buttonColor: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
iconColor: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
validator: AddressValidator(type: contactViewModel.currency!),
|
||||
child: Observer(
|
||||
builder: (_) {
|
||||
return NewSendAddressInput(
|
||||
focusNode: addressFocusNode,
|
||||
displayName: contactViewModel.displayName,
|
||||
addressController: _addressController,
|
||||
onEditingComplete: ()=> contactViewModel.extractParsedAddress(context),
|
||||
validator: AddressValidator(type: contactViewModel.currency!),
|
||||
selectedCurrency: contactViewModel.currency!,
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
@@ -123,6 +128,10 @@ class ContactPage extends BasePage {
|
||||
child: Observer(
|
||||
builder: (_) => PrimaryButton(
|
||||
onPressed: () async {
|
||||
FocusScope.of(context).unfocus();
|
||||
|
||||
await contactViewModel.extractParsedAddress(context);
|
||||
|
||||
if (_formKey.currentState != null && !_formKey.currentState!.validate()) {
|
||||
return;
|
||||
}
|
||||
@@ -187,5 +196,11 @@ class ContactPage extends BasePage {
|
||||
_onContactSavedSuccessfully(context);
|
||||
}
|
||||
});
|
||||
|
||||
reaction((_)=>contactViewModel.address, (val){
|
||||
if(val != _addressController.text) {
|
||||
_addressController.text = val;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sidebar_wrapper.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/pages/cake_features_page.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/page_indicator.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/new_main_navbar_widget.dart';
|
||||
import 'package:cake_wallet/src/screens/wallet_connect/widgets/bottom_sheet/bottom_sheet_listener_widget.dart';
|
||||
import 'package:cake_wallet/src/screens/wallet_connect/services/bottom_sheet_service.dart';
|
||||
import 'package:cake_wallet/src/widgets/evm_switcher.dart';
|
||||
@@ -25,7 +26,6 @@ import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/menu_widget.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/pages/balance/balance_page.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/pages/navigation_dock.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/pages/transactions_page.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
|
||||
@@ -304,9 +304,11 @@ class _DashboardPageView extends BasePage {
|
||||
),
|
||||
),
|
||||
),
|
||||
NavigationDock(
|
||||
NewMainNavBar(
|
||||
dashboardViewModel: dashboardViewModel,
|
||||
)
|
||||
selectedIndex: 0,
|
||||
onItemTap: (index) {}
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
||||
@@ -8,7 +8,7 @@ import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/checkbox_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/warning_box_widget.dart';
|
||||
import 'package:cake_wallet/themes/core/theme_extension.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
|
||||
@@ -36,16 +36,16 @@ class HomeSettingsPage extends BasePage {
|
||||
onItemSelected: _homeSettingsViewModel.setSortBalanceBy,
|
||||
),
|
||||
),
|
||||
Divider(color: Theme.of(context).colorScheme.outlineVariant),
|
||||
Observer(
|
||||
builder: (_) => SettingsSwitcherCell(
|
||||
title: S.of(context).pin_at_top(_homeSettingsViewModel.nativeToken.title),
|
||||
value: _homeSettingsViewModel.pinNativeToken,
|
||||
onValueChange: (_, bool value) {
|
||||
_homeSettingsViewModel.setPinNativeToken(value);
|
||||
},
|
||||
),
|
||||
),
|
||||
// Divider(color: Theme.of(context).colorScheme.outlineVariant),
|
||||
// Observer(
|
||||
// builder: (_) => SettingsSwitcherCell(
|
||||
// title: S.of(context).pin_at_top(_homeSettingsViewModel.nativeToken.title),
|
||||
// value: _homeSettingsViewModel.pinNativeToken,
|
||||
// onValueChange: (_, bool value) {
|
||||
// _homeSettingsViewModel.setPinNativeToken(value);
|
||||
// },
|
||||
// ),
|
||||
// ),
|
||||
Divider(color: Theme.of(context).colorScheme.outlineVariant),
|
||||
const SizedBox(height: 20),
|
||||
Row(
|
||||
|
||||
@@ -177,7 +177,7 @@ class AddressPage extends BasePage {
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Theme.of(context).colorScheme.surfaceContainer),
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
@@ -315,8 +315,7 @@ class AddressPage extends BasePage {
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (addressListViewModel.type == WalletType.bitcoin ||
|
||||
addressListViewModel.type == WalletType.litecoin) {
|
||||
if ([WalletType.bitcoin, WalletType.litecoin].contains(addressListViewModel.type)) {
|
||||
addressListViewModel.setAddressType(bitcoin!.getBitcoinAddressType(option));
|
||||
}
|
||||
if (addressListViewModel.type == WalletType.zcash) {
|
||||
|
||||
@@ -22,7 +22,7 @@ class BalancePage extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return Observer(
|
||||
builder: (context) {
|
||||
final isNFTActivated = isNFTACtivatedChain(dashboardViewModel.type, dashboardViewModel.wallet.chainId);
|
||||
final isNFTActivated = isNFTACtivatedChain(dashboardViewModel.type, chainId: dashboardViewModel.wallet.chainId);
|
||||
return DefaultTabController(
|
||||
key: ValueKey<bool>(isNFTActivated),
|
||||
length: isNFTActivated ? 2 : 1,
|
||||
|
||||
@@ -12,7 +12,9 @@ import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/unspent_coin_type.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
@@ -218,7 +220,7 @@ class BalanceRowWidget extends StatelessWidget {
|
||||
if (currency.isPotentialScam)
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
|
||||
margin: EdgeInsets.only(top: 4),
|
||||
margin: const EdgeInsets.only(top: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.errorContainer,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
@@ -246,7 +248,7 @@ class BalanceRowWidget extends StatelessWidget {
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(height: 26),
|
||||
const SizedBox(height: 26),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
@@ -259,7 +261,7 @@ class BalanceRowWidget extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
const SizedBox(height: 8),
|
||||
AutoSizeText(
|
||||
frozenBalance,
|
||||
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
|
||||
@@ -270,14 +272,12 @@ class BalanceRowWidget extends StatelessWidget {
|
||||
maxLines: 1,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
const SizedBox(height: 4),
|
||||
if (!isTestnet)
|
||||
Text(
|
||||
frozenFiatBalance,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||
height: 1,
|
||||
),
|
||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(height: 1),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -285,7 +285,7 @@ class BalanceRowWidget extends StatelessWidget {
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(height: 24),
|
||||
const SizedBox(height: 24),
|
||||
Text(
|
||||
'${additionalBalanceLabel}',
|
||||
textAlign: TextAlign.center,
|
||||
@@ -294,7 +294,7 @@ class BalanceRowWidget extends StatelessWidget {
|
||||
height: 1,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
const SizedBox(height: 8),
|
||||
AutoSizeText(
|
||||
additionalBalance,
|
||||
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
|
||||
@@ -305,14 +305,12 @@ class BalanceRowWidget extends StatelessWidget {
|
||||
maxLines: 1,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
const SizedBox(height: 4),
|
||||
if (!isTestnet)
|
||||
Text(
|
||||
'${additionalFiatBalance}',
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||
height: 1,
|
||||
),
|
||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(height: 1),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -335,15 +333,6 @@ class BalanceRowWidget extends StatelessWidget {
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
),
|
||||
// boxShadow: [
|
||||
// BoxShadow(
|
||||
// color: Theme.of(context)
|
||||
// .extension<BalancePageTheme>()!
|
||||
// .cardBorderColor
|
||||
// .withAlpha(50),
|
||||
// spreadRadius: dashboardViewModel.getShadowSpread(),
|
||||
// blurRadius: dashboardViewModel.getShadowBlur())
|
||||
// ],
|
||||
),
|
||||
child: TextButton(
|
||||
onPressed: _showToast,
|
||||
@@ -362,27 +351,48 @@ class BalanceRowWidget extends StatelessWidget {
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Container(
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
child: ImageIcon(
|
||||
AssetImage('assets/images/mweb_logo.png'),
|
||||
size: 40,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
'MWEB',
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
height: 1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
ImageIcon(
|
||||
AssetImage('assets/images/mweb_logo.png'),
|
||||
size: 40,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
'MWEB',
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
height: 1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
if (currency == CryptoCurrency.btc)
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
'assets/images/lightning-icon.svg',
|
||||
width: 40,
|
||||
height: 40,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
'Lightning',
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
height: 1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -394,15 +404,11 @@ class BalanceRowWidget extends StatelessWidget {
|
||||
children: [
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () => launchUrl(
|
||||
Uri.parse(
|
||||
"https://docs.cakewallet.com/cryptos/litecoin#mweb"),
|
||||
mode: LaunchMode.externalApplication,
|
||||
),
|
||||
onTap: onPressedHelp,
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
'${secondAvailableBalanceLabel}',
|
||||
secondAvailableBalanceLabel,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color:
|
||||
@@ -436,7 +442,7 @@ class BalanceRowWidget extends StatelessWidget {
|
||||
SizedBox(height: 6),
|
||||
if (!isTestnet)
|
||||
Text(
|
||||
'${secondAvailableFiatBalance}',
|
||||
secondAvailableFiatBalance,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
fontSize: 16,
|
||||
@@ -464,7 +470,7 @@ class BalanceRowWidget extends StatelessWidget {
|
||||
children: [
|
||||
SizedBox(height: 24),
|
||||
Text(
|
||||
'${secondAdditionalBalanceLabel}',
|
||||
secondAdditionalBalanceLabel,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
@@ -500,31 +506,15 @@ class BalanceRowWidget extends StatelessWidget {
|
||||
),
|
||||
IntrinsicHeight(
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Semantics(
|
||||
label: S.of(context).litecoin_mweb_pegin,
|
||||
label: depositToL2Label,
|
||||
child: OutlinedButton(
|
||||
onPressed: () {
|
||||
final mwebAddress =
|
||||
bitcoin!.getUnusedMwebAddress(dashboardViewModel.wallet);
|
||||
PaymentRequest? paymentRequest = null;
|
||||
if ((mwebAddress?.isNotEmpty ?? false)) {
|
||||
paymentRequest = PaymentRequest.fromUri(
|
||||
Uri.parse("litecoin:${mwebAddress}"));
|
||||
}
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
Routes.send,
|
||||
arguments: {
|
||||
'paymentRequest': paymentRequest,
|
||||
'coinTypeToSpendFrom': UnspentCoinType.nonMweb,
|
||||
},
|
||||
);
|
||||
},
|
||||
onPressed: () => depositToL2(context),
|
||||
style: OutlinedButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
side: BorderSide(
|
||||
@@ -537,7 +527,7 @@ class BalanceRowWidget extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(vertical: 12),
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
@@ -549,7 +539,7 @@ class BalanceRowWidget extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
S.of(context).litecoin_mweb_pegin,
|
||||
depositToL2Label,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onPrimary,
|
||||
fontWeight: FontWeight.w700,
|
||||
@@ -564,25 +554,9 @@ class BalanceRowWidget extends StatelessWidget {
|
||||
SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Semantics(
|
||||
label: S.of(context).litecoin_mweb_pegout,
|
||||
label: withdrawFromL2Label,
|
||||
child: OutlinedButton(
|
||||
onPressed: () {
|
||||
final litecoinAddress =
|
||||
bitcoin!.getUnusedSegwitAddress(dashboardViewModel.wallet);
|
||||
PaymentRequest? paymentRequest = null;
|
||||
if ((litecoinAddress?.isNotEmpty ?? false)) {
|
||||
paymentRequest = PaymentRequest.fromUri(
|
||||
Uri.parse("litecoin:${litecoinAddress}"));
|
||||
}
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
Routes.send,
|
||||
arguments: {
|
||||
'paymentRequest': paymentRequest,
|
||||
'coinTypeToSpendFrom': UnspentCoinType.mweb,
|
||||
},
|
||||
);
|
||||
},
|
||||
onPressed: () => withdrawFromL2(context),
|
||||
style: OutlinedButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
side: BorderSide(
|
||||
@@ -607,7 +581,7 @@ class BalanceRowWidget extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
S.of(context).litecoin_mweb_pegout,
|
||||
withdrawFromL2Label,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
@@ -625,7 +599,7 @@ class BalanceRowWidget extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -635,20 +609,74 @@ class BalanceRowWidget extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
// double getShadowSpread(){
|
||||
// double spread = 3;
|
||||
// else if (!dashboardViewModel.settingsStore.currentTheme.isDark) spread = 3;
|
||||
// else if (dashboardViewModel.settingsStore.currentTheme.isDark) spread = 1;
|
||||
// return spread;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// double getShadowBlur(){
|
||||
// double blur = 7;
|
||||
// else if (dashboardViewModel.settingsStore.currentTheme.isDark) blur = 7;
|
||||
// else if (dashboardViewModel.settingsStore.currentTheme.isDark) blur = 3;
|
||||
// return blur;
|
||||
// }
|
||||
String get depositToL2Label => dashboardViewModel.type == WalletType.litecoin
|
||||
? S.current.litecoin_mweb_pegin
|
||||
: S.current.bitcoin_lightning_deposit;
|
||||
|
||||
String get withdrawFromL2Label => dashboardViewModel.type == WalletType.litecoin
|
||||
? S.current.litecoin_mweb_pegout
|
||||
: S.current.bitcoin_lightning_withdraw;
|
||||
|
||||
Future<void> depositToL2(BuildContext context) async {
|
||||
PaymentRequest? paymentRequest = null;
|
||||
|
||||
if (dashboardViewModel.type == WalletType.litecoin) {
|
||||
final depositAddress = bitcoin!.getUnusedMwebAddress(dashboardViewModel.wallet);
|
||||
if ((depositAddress?.isNotEmpty ?? false)) {
|
||||
paymentRequest = PaymentRequest.fromUri(Uri.parse("litecoin:$depositAddress"));
|
||||
}
|
||||
} else if (dashboardViewModel.type == WalletType.bitcoin) {
|
||||
final depositAddress = await bitcoin!.getUnusedSpakDepositAddress(dashboardViewModel.wallet);
|
||||
if ((depositAddress?.isNotEmpty ?? false)) {
|
||||
paymentRequest = PaymentRequest.fromUri(Uri.parse("bitcoin:$depositAddress"));
|
||||
}
|
||||
}
|
||||
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
Routes.send,
|
||||
arguments: {
|
||||
'paymentRequest': paymentRequest,
|
||||
'coinTypeToSpendFrom': UnspentCoinType.nonMweb,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> withdrawFromL2(BuildContext context) async {
|
||||
PaymentRequest? paymentRequest = null;
|
||||
UnspentCoinType unspentCoinType = UnspentCoinType.any;
|
||||
final withdrawAddress = bitcoin!.getUnusedSegwitAddress(dashboardViewModel.wallet);
|
||||
|
||||
if (dashboardViewModel.type == WalletType.litecoin) {
|
||||
if ((withdrawAddress?.isNotEmpty ?? false)) {
|
||||
paymentRequest = PaymentRequest.fromUri(Uri.parse("litecoin:$withdrawAddress"));
|
||||
}
|
||||
unspentCoinType = UnspentCoinType.mweb;
|
||||
} else if (dashboardViewModel.type == WalletType.bitcoin) {
|
||||
if ((withdrawAddress?.isNotEmpty ?? false)) {
|
||||
paymentRequest = PaymentRequest.fromUri(Uri.parse("bitcoin:$withdrawAddress"));
|
||||
}
|
||||
unspentCoinType = UnspentCoinType.lightning;
|
||||
}
|
||||
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
Routes.send,
|
||||
arguments: {
|
||||
'paymentRequest': paymentRequest,
|
||||
'coinTypeToSpendFrom': unspentCoinType,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void onPressedHelp() {
|
||||
var helpUri = Uri.parse("https://docs.cakewallet.com/cryptos/bitcoin#lightning");
|
||||
if (dashboardViewModel.type == WalletType.litecoin) {
|
||||
helpUri = Uri.parse("https://docs.cakewallet.com/cryptos/litecoin#mweb");
|
||||
}
|
||||
|
||||
launchUrl(helpUri, mode: LaunchMode.externalApplication);
|
||||
}
|
||||
|
||||
void _showBalanceDescription(BuildContext context, String content) {
|
||||
showPopUp<void>(context: context, builder: (_) => InformationPage(information: content));
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/apps_widget.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/dashboard_card_widget.dart';
|
||||
import 'package:cake_wallet/utils/feature_flag.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/cake_features_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
||||
@@ -12,6 +15,7 @@ import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:cake_wallet/src/widgets/gradient_background.dart';
|
||||
|
||||
class CakeFeaturesPage extends StatelessWidget {
|
||||
CakeFeaturesPage({required this.dashboardViewModel, required this.cakeFeaturesViewModel});
|
||||
@@ -21,112 +25,195 @@ class CakeFeaturesPage extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 24, top: 16),
|
||||
child: Text(
|
||||
S.of(context).apps,
|
||||
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: ListView(
|
||||
children: <Widget>[
|
||||
SizedBox(height: 2),
|
||||
DashBoardRoundedCardWidget(
|
||||
shadowBlur: dashboardViewModel.getShadowBlur(),
|
||||
shadowSpread: dashboardViewModel.getShadowSpread(),
|
||||
onTap: () {
|
||||
if (Platform.isMacOS) {
|
||||
_launchUrl("buy.cakepay.com");
|
||||
} else {
|
||||
_navigatorToGiftCardsPage(context);
|
||||
}
|
||||
},
|
||||
title: 'Cake Pay',
|
||||
subTitle: S.of(context).cake_pay_subtitle,
|
||||
image: Image.asset(
|
||||
'assets/images/cakepay.png',
|
||||
height: 74,
|
||||
width: 70,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
Observer(builder: (_) {
|
||||
if (dashboardViewModel.wallet.chainId == 1) {
|
||||
return DashBoardRoundedCardWidget(
|
||||
shadowBlur: dashboardViewModel.getShadowBlur(),
|
||||
shadowSpread: dashboardViewModel.getShadowSpread(),
|
||||
onTap: () => Navigator.of(context).pushNamed(Routes.dEuroSavings),
|
||||
title: S.of(context).deuro_savings,
|
||||
subTitle: S.of(context).deuro_savings_subtitle,
|
||||
image: Image.asset(
|
||||
'assets/images/deuro_icon.png',
|
||||
height: 80,
|
||||
width: 80,
|
||||
fit: BoxFit.cover,
|
||||
return GradientBackground(
|
||||
scaffold: SafeArea(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildHeader(context),
|
||||
Expanded(
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
return SingleChildScrollView(
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(minHeight: constraints.maxHeight),
|
||||
child: IntrinsicHeight(
|
||||
child: !FeatureFlag.hasNewUi ? _buildOldUi(context) : _buildNewUi(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return const SizedBox();
|
||||
}),
|
||||
DashBoardRoundedCardWidget(
|
||||
shadowBlur: dashboardViewModel.getShadowBlur(),
|
||||
shadowSpread: dashboardViewModel.getShadowSpread(),
|
||||
onTap: () => _launchUrl("cake.nano-gpt.com"),
|
||||
title: "NanoGPT",
|
||||
subTitle: S.of(context).nanogpt_subtitle,
|
||||
image: Image.asset(
|
||||
'assets/images/nanogpt.png',
|
||||
height: 80,
|
||||
width: 80,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
},
|
||||
),
|
||||
SizedBox(height: 125),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeader(BuildContext context) {
|
||||
if (!FeatureFlag.hasNewUi) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 24, top: 16, bottom: 16),
|
||||
child: Text(
|
||||
S.of(context).apps,
|
||||
style: Theme.of(context).textTheme.headlineLarge?.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Align(
|
||||
alignment: Alignment.center,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 24, bottom: 24),
|
||||
child: Text(
|
||||
S.of(context).apps,
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
fontSize: 18.0,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildOldUi(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
const SizedBox(height: 2),
|
||||
DashBoardRoundedCardWidget(
|
||||
shadowBlur: dashboardViewModel.getShadowBlur(),
|
||||
shadowSpread: dashboardViewModel.getShadowSpread(),
|
||||
onTap: () => _onCakePayTap(context),
|
||||
title: 'Cake Pay',
|
||||
subTitle: S.of(context).cake_pay_subtitle,
|
||||
image: Image.asset('assets/images/cakepay.png', height: 74, width: 70, fit: BoxFit.cover),
|
||||
),
|
||||
Observer(builder: (_) {
|
||||
if (dashboardViewModel.type == WalletType.ethereum) {
|
||||
return DashBoardRoundedCardWidget(
|
||||
shadowBlur: dashboardViewModel.getShadowBlur(),
|
||||
shadowSpread: dashboardViewModel.getShadowSpread(),
|
||||
onTap: () => Navigator.of(context).pushNamed(Routes.dEuroSavings),
|
||||
title: S.of(context).deuro_savings,
|
||||
subTitle: S.of(context).deuro_savings_subtitle,
|
||||
image: Image.asset('assets/images/deuro_icon.png', height: 80, width: 80, fit: BoxFit.cover),
|
||||
);
|
||||
}
|
||||
return const SizedBox();
|
||||
}),
|
||||
DashBoardRoundedCardWidget(
|
||||
shadowBlur: dashboardViewModel.getShadowBlur(),
|
||||
shadowSpread: dashboardViewModel.getShadowSpread(),
|
||||
onTap: () => _launchUrl("cake.nano-gpt.com"),
|
||||
title: "NanoGPT",
|
||||
subTitle: S.of(context).nanogpt_subtitle,
|
||||
image: Image.asset('assets/images/nanogpt.png', height: 80, width: 80, fit: BoxFit.cover),
|
||||
),
|
||||
const Spacer(),
|
||||
const SizedBox(height: 125),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildNewUi(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 24, bottom: 8),
|
||||
child: CakeImageWidget(imageUrl: "assets/new-ui/by-cakelabs.svg", height: 20, color: Theme.of(context).colorScheme.onSurfaceVariant),
|
||||
),
|
||||
),
|
||||
AppsWidget(
|
||||
isWide: true,
|
||||
isCake: true,
|
||||
onTap: () => _onCakePayTap(context),
|
||||
title: 'Cake Pay',
|
||||
subTitle: S.of(context).cake_pay_subtitle,
|
||||
image: 'assets/images/cakepay.png',
|
||||
),
|
||||
AppsWidget(
|
||||
isWide: true,
|
||||
isLink: true,
|
||||
isCake: true,
|
||||
onTap: () => _launchUrl("cupcakewallet.com"),
|
||||
title: "Cupcake",
|
||||
subTitle: "Turn your old phone into your new hardware wallet with our new app",
|
||||
image: 'assets/images/cupcake.png',
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 24, top: 16, bottom: 8),
|
||||
child: Text(
|
||||
"Featured Apps",
|
||||
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
AppsWidget(
|
||||
isWide: true,
|
||||
isLink: true,
|
||||
onTap: () => _launchUrl("cake.nano-gpt.com"),
|
||||
title: "NanoGPT",
|
||||
subTitle: S.of(context).nanogpt_subtitle,
|
||||
image: 'assets/images/nanogpt.png',
|
||||
),
|
||||
Observer(builder: (_) {
|
||||
if (dashboardViewModel.type == WalletType.ethereum) {
|
||||
return AppsWidget(
|
||||
isWide: true,
|
||||
onTap: () => Navigator.of(context).pushNamed(Routes.dEuroSavings),
|
||||
title: S.of(context).deuro_savings,
|
||||
subTitle: S.of(context).deuro_savings_subtitle,
|
||||
image: 'assets/images/deuro_icon.png',
|
||||
);
|
||||
}
|
||||
return const SizedBox();
|
||||
}),
|
||||
const Spacer(),
|
||||
const SizedBox(height: 125),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void _onCakePayTap(BuildContext context) {
|
||||
if (Platform.isMacOS) {
|
||||
_launchUrl("buy.cakepay.com");
|
||||
} else {
|
||||
_navigatorToGiftCardsPage(context);
|
||||
}
|
||||
}
|
||||
|
||||
void _launchUrl(String url) {
|
||||
try {
|
||||
launchUrl(
|
||||
Uri.https(url),
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
launchUrl(Uri.https(url), mode: LaunchMode.externalApplication);
|
||||
} catch (e) {
|
||||
printV(e);
|
||||
}
|
||||
}
|
||||
|
||||
void _navigatorToGiftCardsPage(BuildContext context) {
|
||||
final walletType = dashboardViewModel.type;
|
||||
|
||||
switch (walletType) {
|
||||
case WalletType.haven:
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.of(context).error,
|
||||
alertContent: S.of(context).gift_cards_unavailable,
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop());
|
||||
});
|
||||
break;
|
||||
default:
|
||||
Navigator.pushNamed(context, Routes.cakePayCardsPage);
|
||||
if (dashboardViewModel.type == WalletType.haven) {
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.of(context).error,
|
||||
alertContent: S.of(context).gift_cards_unavailable,
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop());
|
||||
});
|
||||
} else {
|
||||
Navigator.pushNamed(context, Routes.cakePayCardsPage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,23 +36,24 @@ class NFTDetailsPage extends BasePage {
|
||||
|
||||
@override
|
||||
Widget trailing(BuildContext context) {
|
||||
final menuButton = Image.asset(
|
||||
'assets/images/menu.png',
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
);
|
||||
|
||||
return Container(
|
||||
alignment: Alignment.centerRight,
|
||||
width: 40,
|
||||
child: TextButton(
|
||||
// FIX-ME: Style
|
||||
//highlightColor: Colors.transparent,
|
||||
//splashColor: Colors.transparent,
|
||||
//padding: EdgeInsets.all(0),
|
||||
onPressed: () => onOpenEndDrawer(),
|
||||
child: Semantics(label: S.of(context).wallet_menu, child: menuButton),
|
||||
),
|
||||
);
|
||||
return SizedBox.shrink();
|
||||
// final menuButton = Image.asset(
|
||||
// 'assets/images/menu.png',
|
||||
// color: Theme.of(context).colorScheme.onSurface,
|
||||
// );
|
||||
//
|
||||
// return Container(
|
||||
// alignment: Alignment.centerRight,
|
||||
// width: 40,
|
||||
// child: TextButton(
|
||||
// // FIX-ME: Style
|
||||
// //highlightColor: Colors.transparent,
|
||||
// //splashColor: Colors.transparent,
|
||||
// //padding: EdgeInsets.all(0),
|
||||
// onPressed: () => onOpenEndDrawer(),
|
||||
// child: Semantics(label: S.of(context).wallet_menu, child: menuButton),
|
||||
// ),
|
||||
// );
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -139,7 +139,7 @@ class _ImportNFTPage extends BasePage {
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
onPressed: () async {
|
||||
await nftViewModel.importNFT(tokenAddressController.text, tokenIDController.text);
|
||||
Navigator.pop(context);
|
||||
if (context.mounted && Navigator.of(context).canPop()) Navigator.pop(context);
|
||||
},
|
||||
);
|
||||
}),
|
||||
|
||||
@@ -50,20 +50,16 @@ class _NFTListingPageState extends State<NFTListingPage> {
|
||||
),
|
||||
),
|
||||
if (widget.nftViewModel.isLoading)
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(
|
||||
backgroundColor: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
Center(
|
||||
child: CircularProgressIndicator(
|
||||
backgroundColor: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
)
|
||||
else
|
||||
Expanded(
|
||||
child: NFTListWidget(nftViewModel: widget.nftViewModel),
|
||||
),
|
||||
NFTListWidget(nftViewModel: widget.nftViewModel)
|
||||
],
|
||||
);
|
||||
},
|
||||
@@ -95,26 +91,38 @@ class NFTListWidget extends StatelessWidget {
|
||||
if (isSolana) {
|
||||
if (nftViewModel.solanaNftAssetModels.isEmpty) return emptyMessage;
|
||||
|
||||
return ListView.separated(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 16),
|
||||
separatorBuilder: (context, index) => const SizedBox(height: 8),
|
||||
itemCount: nftViewModel.solanaNftAssetModels.length,
|
||||
itemBuilder: (context, index) {
|
||||
final nftAsset = nftViewModel.solanaNftAssetModels[index];
|
||||
return SolanaNFTTileWidget(nftAsset: nftAsset);
|
||||
},
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: 64),
|
||||
child: ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 16),
|
||||
separatorBuilder: (context, index) => const SizedBox(height: 8),
|
||||
itemCount: nftViewModel.solanaNftAssetModels.length,
|
||||
itemBuilder: (context, index) {
|
||||
final nftAsset = nftViewModel.solanaNftAssetModels[index];
|
||||
return SolanaNFTTileWidget(nftAsset: nftAsset);
|
||||
},
|
||||
),
|
||||
);
|
||||
} else {
|
||||
if (nftViewModel.nftAssetByWalletModels.isEmpty) return emptyMessage;
|
||||
|
||||
return ListView.separated(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 16),
|
||||
separatorBuilder: (context, index) => const SizedBox(height: 8),
|
||||
itemCount: nftViewModel.nftAssetByWalletModels.length,
|
||||
itemBuilder: (context, index) {
|
||||
final nftAsset = nftViewModel.nftAssetByWalletModels[index];
|
||||
return NFTTileWidget(nftAsset: nftAsset);
|
||||
},
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: 64),
|
||||
child: ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 16),
|
||||
separatorBuilder: (context, index) => const SizedBox(height: 8),
|
||||
itemCount: nftViewModel.nftAssetByWalletModels.length,
|
||||
itemBuilder: (context, index) {
|
||||
final nftAsset = nftViewModel.nftAssetByWalletModels[index];
|
||||
return NFTTileWidget(nftAsset: nftAsset);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -155,8 +155,8 @@ class TransactionsPage extends StatelessWidget {
|
||||
),
|
||||
currency: "BTC",
|
||||
state: item.status,
|
||||
amount: bitcoin!
|
||||
.formatterBitcoinAmountToString(amount: session.amount.toInt()),
|
||||
amount: dashboardViewModel.appStore.amountParsingProxy
|
||||
.getDisplayCryptoString(session.amount.toInt(), CryptoCurrency.btc),
|
||||
createdAt: DateFormat('HH:mm').format(session.inProgressSince!),
|
||||
isSending: session.isSenderSession,
|
||||
);
|
||||
@@ -173,18 +173,22 @@ class TransactionsPage extends StatelessWidget {
|
||||
return tradeFrom != null && tradeTo != null
|
||||
? Observer(
|
||||
builder: (_) => TradeRow(
|
||||
key: item.key,
|
||||
onTap: () => Navigator.of(context)
|
||||
.pushNamed(Routes.tradeDetails, arguments: trade),
|
||||
swapState: trade.state,
|
||||
provider: trade.provider,
|
||||
from: tradeFrom,
|
||||
to: tradeTo,
|
||||
createdAtFormattedDate: trade.createdAt != null
|
||||
? DateFormat('HH:mm').format(trade.createdAt!)
|
||||
: null,
|
||||
formattedAmount: item.tradeFormattedAmount,
|
||||
formattedReceiveAmount: item.tradeFormattedReceiveAmount),
|
||||
key: item.key,
|
||||
onTap: () => Navigator.of(context)
|
||||
.pushNamed(Routes.tradeDetails, arguments: trade),
|
||||
swapState: trade.state,
|
||||
provider: trade.provider,
|
||||
title: "$tradeFrom → $tradeTo",
|
||||
fromSymbol: dashboardViewModel.appStore.amountParsingProxy
|
||||
.getCryptoSymbol(tradeFrom),
|
||||
toSymbol: dashboardViewModel.appStore.amountParsingProxy
|
||||
.getCryptoSymbol(tradeTo),
|
||||
createdAtFormattedDate: trade.createdAt != null
|
||||
? DateFormat("HH:mm").format(trade.createdAt!)
|
||||
: null,
|
||||
formattedAmount: item.tradeFormattedAmount,
|
||||
formattedReceiveAmount: item.tradeFormattedReceiveAmount,
|
||||
),
|
||||
)
|
||||
: Container();
|
||||
}
|
||||
|
||||
@@ -1,133 +1,133 @@
|
||||
import 'package:cake_wallet/core/execution_state.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/keyboard_hide_overlay.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/sign_form.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/verify_form.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/sign_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:keyboard_actions/keyboard_actions.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:smooth_page_indicator/smooth_page_indicator.dart';
|
||||
|
||||
class SignPage extends BasePage {
|
||||
SignPage(this.signViewModel)
|
||||
: signFormKey = GlobalKey<SignFormState>(),
|
||||
verifyFormKey = GlobalKey<VerifyFormState>(),
|
||||
_pages = [],
|
||||
_controller = PageController(initialPage: 0) {
|
||||
class SignPage extends StatefulWidget {
|
||||
SignPage(this.signViewModel);
|
||||
|
||||
|
||||
final SignViewModel signViewModel;
|
||||
|
||||
|
||||
@override
|
||||
State<SignPage> createState() => _SignPageState();
|
||||
}
|
||||
|
||||
class _SignPageState extends State<SignPage> {
|
||||
List<Widget> _pages = [];
|
||||
final signFormKey = GlobalKey<SignFormState>();
|
||||
final verifyFormKey = GlobalKey<VerifyFormState>();
|
||||
final PageController _controller = PageController(initialPage: 0) ;
|
||||
|
||||
bool _isEffectsInstalled = false;
|
||||
|
||||
void initState() {
|
||||
super.initState();
|
||||
_pages.add(SignForm(
|
||||
key: signFormKey,
|
||||
type: signViewModel.wallet.type,
|
||||
chainId: signViewModel.wallet.chainId,
|
||||
includeAddress: signViewModel.signIncludesAddress,
|
||||
type: widget.signViewModel.wallet.type,
|
||||
chainId: widget.signViewModel.wallet.chainId,
|
||||
includeAddress: widget.signViewModel.signIncludesAddress,
|
||||
));
|
||||
_pages.add(VerifyForm(
|
||||
key: verifyFormKey,
|
||||
type: signViewModel.wallet.type,
|
||||
chainId: signViewModel.wallet.chainId,
|
||||
type: widget.signViewModel.wallet.type,
|
||||
chainId: widget.signViewModel.wallet.chainId,
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget middle(BuildContext context) => Observer(
|
||||
builder: (_) {
|
||||
return Text(
|
||||
S.current.sign_verify_title,
|
||||
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 18,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
final SignViewModel signViewModel;
|
||||
final PageController _controller;
|
||||
final List<Widget> _pages;
|
||||
final GlobalKey<SignFormState> signFormKey;
|
||||
final GlobalKey<VerifyFormState> verifyFormKey;
|
||||
bool _isEffectsInstalled = false;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
Widget build(BuildContext context) {
|
||||
_setEffects(context);
|
||||
|
||||
return KeyboardActions(
|
||||
config: KeyboardActionsConfig(
|
||||
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
|
||||
keyboardBarColor: Theme.of(context).colorScheme.surfaceVariant,
|
||||
nextFocus: false,
|
||||
actions: [
|
||||
KeyboardActionsItem(
|
||||
focusNode: FocusNode(),
|
||||
toolbarButtons: [(_) => KeyboardDoneButton()],
|
||||
)
|
||||
],
|
||||
),
|
||||
child: Container(
|
||||
height: 0,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: PageView.builder(
|
||||
onPageChanged: (page) {
|
||||
signViewModel.isSigning = page == 0;
|
||||
},
|
||||
controller: _controller,
|
||||
itemCount: _pages.length,
|
||||
itemBuilder: (_, index) => SingleChildScrollView(child: _pages[index]),
|
||||
),
|
||||
),
|
||||
if (_pages.length > 1)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: SmoothPageIndicator(
|
||||
controller: _controller,
|
||||
count: _pages.length,
|
||||
effect: ColorTransitionEffect(
|
||||
spacing: 6.0,
|
||||
radius: 6.0,
|
||||
dotWidth: 6.0,
|
||||
dotHeight: 6.0,
|
||||
dotColor: Theme.of(context).colorScheme.outline.withOpacity(0.5),
|
||||
activeDotColor: Theme.of(context).colorScheme.outline,
|
||||
return Container(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: SafeArea(
|
||||
top:false,
|
||||
child: Column(
|
||||
children: [
|
||||
ModalTopBar(title: S.current.sign_verify_title,
|
||||
leadingIcon: Icon(Icons.arrow_back_ios_new),
|
||||
onLeadingPressed: Navigator
|
||||
.of(context)
|
||||
.pop,),
|
||||
Expanded(
|
||||
child: KeyboardHideOverlay(
|
||||
child: Container(
|
||||
height: 0,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: PageView.builder(
|
||||
onPageChanged: (page) {
|
||||
widget.signViewModel.isSigning = page == 0;
|
||||
},
|
||||
controller: _controller,
|
||||
itemCount: _pages.length,
|
||||
itemBuilder: (_, index) => SingleChildScrollView(child: _pages[index]),
|
||||
),
|
||||
),
|
||||
if (_pages.length > 1)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: SmoothPageIndicator(
|
||||
controller: _controller,
|
||||
count: _pages.length,
|
||||
effect: ColorTransitionEffect(
|
||||
spacing: 6.0,
|
||||
radius: 6.0,
|
||||
dotWidth: 6.0,
|
||||
dotHeight: 6.0,
|
||||
dotColor: Theme.of(context).colorScheme.outline.withOpacity(0.5),
|
||||
activeDotColor: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20, bottom: 24, left: 24, right: 24),
|
||||
child: Column(
|
||||
children: [
|
||||
Observer(
|
||||
builder: (context) {
|
||||
return LoadingPrimaryButton(
|
||||
onPressed: () async {
|
||||
await _confirmForm(context);
|
||||
},
|
||||
text: widget.signViewModel.isSigning
|
||||
? S.current.sign_message
|
||||
: S.current.verify_message,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
isLoading: widget.signViewModel.state is IsExecutingState,
|
||||
isDisabled: widget.signViewModel.state is IsExecutingState,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20, bottom: 24, left: 24, right: 24),
|
||||
child: Column(
|
||||
children: [
|
||||
Observer(
|
||||
builder: (context) {
|
||||
return LoadingPrimaryButton(
|
||||
onPressed: () async {
|
||||
await _confirmForm(context);
|
||||
},
|
||||
text: signViewModel.isSigning
|
||||
? S.current.sign_message
|
||||
: S.current.verify_message,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
isLoading: signViewModel.state is IsExecutingState,
|
||||
isDisabled: signViewModel.state is IsExecutingState,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -139,7 +139,7 @@ class SignPage extends BasePage {
|
||||
}
|
||||
_isEffectsInstalled = true;
|
||||
|
||||
reaction((_) => signViewModel.state, (ExecutionState state) {
|
||||
reaction((_) => widget.signViewModel.state, (ExecutionState state) {
|
||||
if (state is FailureState) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
showPopUp<void>(
|
||||
@@ -150,14 +150,14 @@ class SignPage extends BasePage {
|
||||
alertContent: state.error,
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () {
|
||||
if (Navigator.canPop(context)) Navigator.of(context).pop();
|
||||
if (context.mounted && Navigator.canPop(context)) Navigator.of(context).pop();
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
if (state is ExecutedSuccessfullyState) {
|
||||
if (signViewModel.isSigning) {
|
||||
if (widget.signViewModel.isSigning) {
|
||||
signFormKey.currentState!.signatureController.text = state.payload as String;
|
||||
} else {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
@@ -182,18 +182,18 @@ class SignPage extends BasePage {
|
||||
Future<void> _confirmForm(BuildContext context) async {
|
||||
FocusManager.instance.primaryFocus?.unfocus();
|
||||
|
||||
if (signViewModel.isSigning) {
|
||||
if (widget.signViewModel.isSigning) {
|
||||
String message = signFormKey.currentState!.messageController.text;
|
||||
String? address;
|
||||
if (signViewModel.signIncludesAddress) {
|
||||
if (widget.signViewModel.signIncludesAddress) {
|
||||
address = signFormKey.currentState!.addressController.text;
|
||||
}
|
||||
await signViewModel.sign(message, address: address);
|
||||
await widget.signViewModel.sign(message, address: address);
|
||||
} else {
|
||||
String message = verifyFormKey.currentState!.messageController.text;
|
||||
String signature = verifyFormKey.currentState!.signatureController.text;
|
||||
String address = verifyFormKey.currentState!.addressController.text;
|
||||
await signViewModel.verify(message, signature, address: address);
|
||||
await widget.signViewModel.verify(message, signature, address: address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class ActionButton extends StatelessWidget {
|
||||
ActionButton({
|
||||
|
||||
@@ -0,0 +1,314 @@
|
||||
import 'dart:io';
|
||||
import 'dart:ui';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:cake_wallet/entities/new_main_actions.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
||||
|
||||
class NewMainNavBar extends StatefulWidget {
|
||||
const NewMainNavBar({
|
||||
super.key,
|
||||
required this.dashboardViewModel,
|
||||
required this.selectedIndex,
|
||||
required this.onItemTap,
|
||||
});
|
||||
|
||||
final DashboardViewModel dashboardViewModel;
|
||||
final int selectedIndex;
|
||||
final Function(int index) onItemTap;
|
||||
|
||||
static const barHeight = 68.0;
|
||||
static final barBottomPadding = Platform.isIOS ? 22.0 : 12.0;
|
||||
|
||||
@override
|
||||
State<NewMainNavBar> createState() => _NEWNewMainNavBarState();
|
||||
}
|
||||
|
||||
class _NEWNewMainNavBarState extends State<NewMainNavBar> {
|
||||
|
||||
|
||||
static const iconBoxWidth = 48.0;
|
||||
static const iconWidth = 28.0;
|
||||
static const iconHeight = 28.0;
|
||||
static const iconHorizontalPadding = 18.0;
|
||||
|
||||
static const pillIconWidth = 24.0;
|
||||
static const pillIconHeight = 24.0;
|
||||
static const pillIconSpacing = 18.0;
|
||||
static const pillHorizontalPadding = 20.0;
|
||||
|
||||
static const barBorderRadius = 50.0;
|
||||
static const pillBorderRadius = 50.0;
|
||||
|
||||
static const barHorizontalPadding = 6.0;
|
||||
|
||||
static const barResizeDuration = Duration(milliseconds: 300);
|
||||
static const inactiveIconMoveDuration = Duration(milliseconds: 300);
|
||||
static const inactiveIconFadeDuration = Duration(milliseconds: 300);
|
||||
static const inactiveIconAppearDuration = Duration(milliseconds: 300);
|
||||
static const pillMoveDuration = Duration(milliseconds: 250);
|
||||
static const pillResizeDuration = Duration(milliseconds: 250);
|
||||
static const iconColorChangeDuration = Duration(milliseconds: 200);
|
||||
|
||||
static const pillTextStyle = TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
);
|
||||
|
||||
bool _firstFrame = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!mounted) return;
|
||||
setState(() => _firstFrame = false);
|
||||
});
|
||||
}
|
||||
|
||||
void _onItemTap(int index) {
|
||||
// if (index == widget.selectedIndex) return;
|
||||
//
|
||||
// setState(() {
|
||||
// widget.selectedIndex = index;
|
||||
// });
|
||||
|
||||
widget.onItemTap(index);
|
||||
|
||||
NewMainActions.all[index].onTap.call();
|
||||
}
|
||||
|
||||
// Estimate pill width based on text length
|
||||
double _estimatePillWidthForAction(
|
||||
BuildContext context, NewMainActions action,
|
||||
{Color? color}) {
|
||||
final text = action.name(context);
|
||||
final textPainter = TextPainter(
|
||||
text: TextSpan(
|
||||
text: text,
|
||||
style: pillTextStyle.copyWith(color: color),
|
||||
),
|
||||
maxLines: 1,
|
||||
textDirection: TextDirection.ltr,
|
||||
)..layout();
|
||||
return pillIconWidth +
|
||||
pillIconSpacing +
|
||||
textPainter.width +
|
||||
pillHorizontalPadding;
|
||||
}
|
||||
|
||||
double calcLeft(int index, double pillWidth) {
|
||||
final baseOffset = iconBoxWidth * index;
|
||||
|
||||
double additionalSpacing;
|
||||
if (index > widget.selectedIndex)
|
||||
additionalSpacing = pillWidth - iconBoxWidth;
|
||||
else
|
||||
additionalSpacing = 0;
|
||||
|
||||
return baseOffset + additionalSpacing;
|
||||
}
|
||||
|
||||
double calcBarWidth(double pillWidth, int visibleActionsLength) {
|
||||
return (iconWidth + iconHorizontalPadding) * visibleActionsLength + (pillWidth - iconWidth) + barHorizontalPadding + pillIconSpacing / double.infinity - 2;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final backgroundColor = theme.colorScheme.surfaceContainer.withAlpha(127);
|
||||
final pillColor = theme.colorScheme.onSurface.withAlpha(25);
|
||||
final activeColor = theme.colorScheme.onSurface;
|
||||
final inactiveColor = theme.colorScheme.primary;
|
||||
|
||||
final visibleActions = NewMainActions.all
|
||||
.where(
|
||||
(action) => action.canShow?.call(widget.dashboardViewModel) ?? true)
|
||||
.toList();
|
||||
|
||||
final pillWidth = _estimatePillWidthForAction(
|
||||
context, visibleActions[widget.selectedIndex],
|
||||
color: activeColor);
|
||||
|
||||
final barWidth = calcBarWidth(pillWidth, visibleActions.length);
|
||||
|
||||
final currentAction = visibleActions[widget.selectedIndex];
|
||||
|
||||
return Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: SafeArea(
|
||||
bottom: !(Platform.isIOS),
|
||||
top: false,
|
||||
child: Padding(
|
||||
// tux PLEASE consult me (malik) before removing this padding.
|
||||
padding: EdgeInsets.only(bottom: NewMainNavBar.barBottomPadding),
|
||||
child: AnimatedContainer(
|
||||
duration: barResizeDuration,
|
||||
curve: Curves.easeOutCubic,
|
||||
width: barWidth,
|
||||
child: ClipRSuperellipse(
|
||||
borderRadius: BorderRadius.circular(barBorderRadius),
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 3, sigmaY: 3),
|
||||
child: Container(
|
||||
height: NewMainNavBar.barHeight,
|
||||
decoration: ShapeDecoration(
|
||||
color: backgroundColor,
|
||||
shape: RoundedSuperellipseBorder(borderRadius: BorderRadius.circular(barBorderRadius),
|
||||
side: const BorderSide(color: Color(0x14FFFFFF), width: 1),
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: barHorizontalPadding),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
AnimatedPill(
|
||||
left: calcLeft(widget.selectedIndex, pillWidth),
|
||||
pillColor: pillColor,
|
||||
currentAction: currentAction,
|
||||
pillIconHeight: pillIconHeight,
|
||||
pillIconWidth: pillIconWidth,
|
||||
pillIconSpacing: pillIconSpacing,
|
||||
pillBorderRadius: pillBorderRadius,
|
||||
contentColor: activeColor,
|
||||
estimateWidthForAction: pillWidth,
|
||||
pillTextStyle: pillTextStyle,
|
||||
pillMoveDuration: pillMoveDuration,
|
||||
pillResizeDuration: pillResizeDuration,
|
||||
),
|
||||
for (int i = 0; i < visibleActions.length; i++)
|
||||
AnimatedPositioned(
|
||||
duration: pillResizeDuration,
|
||||
width: iconBoxWidth,
|
||||
left: calcLeft(i, pillWidth)+((i == widget.selectedIndex) ? iconHorizontalPadding/100 : 0),
|
||||
curve: Curves.easeOutCubic,
|
||||
child: InkWell(
|
||||
splashFactory: NoSplash.splashFactory,
|
||||
splashColor: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(pillBorderRadius),
|
||||
onTap: () => _onItemTap(i),
|
||||
child: AnimatedContainer(
|
||||
duration: _firstFrame
|
||||
? Duration.zero
|
||||
: inactiveIconMoveDuration,
|
||||
curve: Curves.easeOutCubic,
|
||||
width:
|
||||
i == widget.selectedIndex ? pillWidth : iconBoxWidth,
|
||||
alignment: Alignment.center,
|
||||
child: AnimatedAlign(
|
||||
duration: inactiveIconFadeDuration,
|
||||
curve: Curves.easeOutCubic,
|
||||
alignment: Alignment.center,
|
||||
child: AnimatedScale(
|
||||
duration: inactiveIconAppearDuration,
|
||||
curve: Curves.easeOutCubic,
|
||||
scale: (i == widget.selectedIndex) ? 0.857 : 1.0,
|
||||
child: TweenAnimationBuilder<Color?>(
|
||||
tween: ColorTween(
|
||||
begin: (i == widget.selectedIndex) ? inactiveColor : activeColor,
|
||||
end: (i==widget.selectedIndex) ? activeColor : inactiveColor,
|
||||
),
|
||||
duration: iconColorChangeDuration,
|
||||
builder: (context, value, child) {
|
||||
return Container(
|
||||
height: NewMainNavBar.barHeight,
|
||||
child: SvgPicture.asset(
|
||||
visibleActions[i].image,
|
||||
width: iconWidth,
|
||||
height: iconHeight,
|
||||
//fit: BoxFit.scaleDown,
|
||||
colorFilter: ColorFilter.mode(
|
||||
value ?? inactiveColor,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AnimatedPill extends StatelessWidget {
|
||||
const AnimatedPill({
|
||||
super.key,
|
||||
required this.left,
|
||||
required this.pillColor,
|
||||
required this.contentColor,
|
||||
required this.pillIconHeight,
|
||||
required this.pillIconWidth,
|
||||
required this.pillBorderRadius,
|
||||
required this.currentAction,
|
||||
required this.estimateWidthForAction,
|
||||
required this.pillIconSpacing,
|
||||
required this.pillTextStyle,
|
||||
required this.pillMoveDuration,
|
||||
required this.pillResizeDuration,
|
||||
});
|
||||
|
||||
final double left;
|
||||
final Color pillColor;
|
||||
final Color contentColor;
|
||||
final double pillIconHeight;
|
||||
final double pillIconWidth;
|
||||
final double pillBorderRadius;
|
||||
final NewMainActions currentAction;
|
||||
final double estimateWidthForAction;
|
||||
final double pillIconSpacing;
|
||||
final TextStyle pillTextStyle;
|
||||
final Duration pillMoveDuration;
|
||||
final Duration pillResizeDuration;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedPositioned(
|
||||
duration: pillMoveDuration,
|
||||
curve: Curves.easeOutCubic,
|
||||
left: left,
|
||||
top: 6,
|
||||
bottom: 6,
|
||||
child: AnimatedContainer(
|
||||
duration: pillResizeDuration,
|
||||
curve: Curves.easeOutCubic,
|
||||
width: estimateWidthForAction,
|
||||
decoration: ShapeDecoration(
|
||||
color: pillColor,
|
||||
shape: RoundedSuperellipseBorder(borderRadius: BorderRadius.circular(pillBorderRadius)),
|
||||
),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
alignment: Alignment.center,
|
||||
child: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Padding(padding: EdgeInsets.only(left: pillIconWidth + 2),
|
||||
child: Text(
|
||||
currentAction.name(context),
|
||||
style: pillTextStyle.copyWith(color: contentColor),
|
||||
overflow: TextOverflow.fade,
|
||||
softWrap: false,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -25,15 +25,13 @@ class SignForm extends StatefulWidget {
|
||||
|
||||
class SignFormState extends State<SignForm> with AutomaticKeepAliveClientMixin {
|
||||
SignFormState()
|
||||
: formKey = GlobalKey<FormState>(),
|
||||
messageController = TextEditingController(),
|
||||
: messageController = TextEditingController(),
|
||||
addressController = TextEditingController(),
|
||||
signatureController = TextEditingController();
|
||||
|
||||
final TextEditingController messageController;
|
||||
final TextEditingController addressController;
|
||||
final TextEditingController signatureController;
|
||||
final GlobalKey<FormState> formKey;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -55,35 +53,33 @@ class SignFormState extends State<SignForm> with AutomaticKeepAliveClientMixin {
|
||||
padding: EdgeInsets.only(left: 24, right: 24),
|
||||
child: Column(
|
||||
children: [
|
||||
Form(
|
||||
key: formKey,
|
||||
child: Column(
|
||||
children: [
|
||||
AddressTextField(
|
||||
controller: messageController,
|
||||
placeholder: S.current.message,
|
||||
options: [AddressTextFieldOption.paste],
|
||||
buttonColor: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
fillColor: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
if (widget.includeAddress) ...[
|
||||
const SizedBox(height: 20),
|
||||
AddressTextField(
|
||||
controller: addressController,
|
||||
options: [
|
||||
AddressTextFieldOption.paste,
|
||||
AddressTextFieldOption.walletAddresses
|
||||
],
|
||||
buttonColor: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
onSelectedContact: (contact) {
|
||||
addressController.text = contact.address;
|
||||
},
|
||||
selectedCurrency: walletTypeToCryptoCurrency(widget.type, chainId: widget.chainId),
|
||||
fillColor: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
AddressTextField(
|
||||
controller: messageController,
|
||||
placeholder: S.current.message,
|
||||
options: [AddressTextFieldOption.paste],
|
||||
buttonColor: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
fillColor: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
if (widget.includeAddress) ...[
|
||||
const SizedBox(height: 20),
|
||||
AddressTextField(
|
||||
controller: addressController,
|
||||
options: [
|
||||
AddressTextFieldOption.paste,
|
||||
AddressTextFieldOption.walletAddresses
|
||||
],
|
||||
],
|
||||
)),
|
||||
buttonColor: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
onSelectedContact: (contact) {
|
||||
addressController.text = contact.address;
|
||||
},
|
||||
selectedCurrency: walletTypeToCryptoCurrency(widget.type, chainId: widget.chainId),
|
||||
fillColor: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/utils/image_utill.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import "package:cake_wallet/exchange/trade_state.dart";
|
||||
import "package:cake_wallet/palette.dart";
|
||||
import "package:cake_wallet/utils/image_utill.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:cake_wallet/exchange/exchange_provider_description.dart";
|
||||
|
||||
class TradeRow extends StatelessWidget {
|
||||
TradeRow({
|
||||
required this.provider,
|
||||
required this.from,
|
||||
required this.to,
|
||||
required this.title,
|
||||
required this.fromSymbol,
|
||||
required this.toSymbol,
|
||||
required this.createdAtFormattedDate,
|
||||
this.onTap,
|
||||
this.formattedAmount,
|
||||
@@ -20,8 +20,9 @@ class TradeRow extends StatelessWidget {
|
||||
|
||||
final VoidCallback? onTap;
|
||||
final ExchangeProviderDescription provider;
|
||||
final CryptoCurrency from;
|
||||
final CryptoCurrency to;
|
||||
final String fromSymbol;
|
||||
final String toSymbol;
|
||||
final String title;
|
||||
final String? createdAtFormattedDate;
|
||||
final String? formattedAmount;
|
||||
final String? formattedReceiveAmount;
|
||||
@@ -29,9 +30,6 @@ class TradeRow extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final amountCrypto = from.toString();
|
||||
final receiveAmountCrypto = to.toString();
|
||||
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
@@ -73,7 +71,7 @@ class TradeRow extends StatelessWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'${from.toString()} → ${to.toString()}',
|
||||
title,
|
||||
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
@@ -81,7 +79,7 @@ class TradeRow extends StatelessWidget {
|
||||
),
|
||||
formattedAmount != null
|
||||
? Text(
|
||||
formattedAmount! + ' ' + amountCrypto,
|
||||
"$formattedAmount $fromSymbol",
|
||||
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
@@ -106,7 +104,7 @@ class TradeRow extends StatelessWidget {
|
||||
: Container(),
|
||||
formattedReceiveAmount != null
|
||||
? Text(
|
||||
formattedReceiveAmount! + ' ' + receiveAmountCrypto,
|
||||
"$formattedReceiveAmount $toSymbol",
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
|
||||
@@ -20,15 +20,13 @@ class VerifyForm extends StatefulWidget {
|
||||
|
||||
class VerifyFormState extends State<VerifyForm> with AutomaticKeepAliveClientMixin {
|
||||
VerifyFormState()
|
||||
: formKey = GlobalKey<FormState>(),
|
||||
messageController = TextEditingController(),
|
||||
: messageController = TextEditingController(),
|
||||
addressController = TextEditingController(),
|
||||
signatureController = TextEditingController();
|
||||
|
||||
final TextEditingController messageController;
|
||||
final TextEditingController addressController;
|
||||
final TextEditingController signatureController;
|
||||
final GlobalKey<FormState> formKey;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -48,38 +46,35 @@ class VerifyFormState extends State<VerifyForm> with AutomaticKeepAliveClientMix
|
||||
super.build(context);
|
||||
return Container(
|
||||
padding: EdgeInsets.only(left: 24, right: 24),
|
||||
child: Form(
|
||||
key: formKey,
|
||||
child: Column(
|
||||
children: [
|
||||
AddressTextField(
|
||||
controller: messageController,
|
||||
placeholder: S.current.message,
|
||||
options: [AddressTextFieldOption.paste],
|
||||
buttonColor: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
fillColor: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
AddressTextField(
|
||||
controller: addressController,
|
||||
options: [AddressTextFieldOption.paste, AddressTextFieldOption.walletAddresses],
|
||||
buttonColor: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
fillColor: Theme.of(context).colorScheme.surface,
|
||||
onSelectedContact: (contact) {
|
||||
addressController.text = contact.address;
|
||||
},
|
||||
selectedCurrency: walletTypeToCryptoCurrency(widget.type, chainId: widget.chainId),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
AddressTextField(
|
||||
controller: signatureController,
|
||||
placeholder: S.current.signature,
|
||||
options: [AddressTextFieldOption.paste],
|
||||
buttonColor: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
fillColor: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
AddressTextField(
|
||||
controller: messageController,
|
||||
placeholder: S.current.message,
|
||||
options: [AddressTextFieldOption.paste],
|
||||
buttonColor: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
fillColor: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
AddressTextField(
|
||||
controller: addressController,
|
||||
options: [AddressTextFieldOption.paste, AddressTextFieldOption.walletAddresses],
|
||||
buttonColor: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
fillColor: Theme.of(context).colorScheme.surface,
|
||||
onSelectedContact: (contact) {
|
||||
addressController.text = contact.address;
|
||||
},
|
||||
selectedCurrency: walletTypeToCryptoCurrency(widget.type, chainId: widget.chainId),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
AddressTextField(
|
||||
controller: signatureController,
|
||||
placeholder: S.current.signature,
|
||||
options: [AddressTextFieldOption.paste],
|
||||
buttonColor: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
fillColor: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||
|
||||
class DevExchangeProviderLogsPage extends BasePage {
|
||||
final ExchangeProviderLogsViewModel viewModel;
|
||||
@@ -266,6 +267,7 @@ class _LogsList extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return Observer(
|
||||
builder: (_) => ListView.builder(
|
||||
controller: ModalScrollController.of(context),
|
||||
itemCount: viewModel.filteredLogs.length,
|
||||
itemBuilder: (context, index) {
|
||||
final log = viewModel.filteredLogs[index];
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:flutter/services.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/wallet_type_utils.dart';
|
||||
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||
|
||||
class DisclaimerPage extends BasePage {
|
||||
DisclaimerPage({this.isReadOnly = false});
|
||||
@@ -59,6 +60,8 @@ class DisclaimerBodyState extends State<DisclaimerPageBody> {
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
SingleChildScrollView(
|
||||
controller: ModalScrollController.of(context),
|
||||
physics: ClampingScrollPhysics(),
|
||||
padding: EdgeInsets.only(left: 24.0, right: 24.0),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
|
||||
@@ -32,7 +32,7 @@ import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange/widgets/exchange_card.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart';
|
||||
import 'package:cake_wallet/core/address_validator.dart';
|
||||
@@ -615,7 +615,7 @@ class ExchangePage extends BasePage {
|
||||
try {
|
||||
exchangeViewModel.receiveCurrency =
|
||||
CryptoCurrency.fromString(initialPaymentRequest!.scheme);
|
||||
exchangeViewModel.receiveAmount = initialPaymentRequest!.amount;
|
||||
exchangeViewModel.setCanonicalReceiveAmount(initialPaymentRequest!.amount);
|
||||
exchangeViewModel.receiveAddress = initialPaymentRequest!.address;
|
||||
} catch (e) {
|
||||
printV('error: ${e.toString()}');
|
||||
@@ -718,6 +718,7 @@ class ExchangePage extends BasePage {
|
||||
? AmountValidator(
|
||||
isAutovalidate: true,
|
||||
currency: exchangeViewModel.depositCurrency,
|
||||
amountParsingProxy: exchangeViewModel.amountParsingProxy,
|
||||
minValue: exchangeViewModel.limits.min.toString(),
|
||||
maxValue: exchangeViewModel.limits.max.toString(),
|
||||
).call(value)
|
||||
@@ -734,6 +735,7 @@ class ExchangePage extends BasePage {
|
||||
exchangeViewModel.depositAddress =
|
||||
await fetchParsedAddress(context, domain, exchangeViewModel.depositCurrency);
|
||||
},
|
||||
useSatoshis: exchangeViewModel.useSatoshiDeposit,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -765,6 +767,7 @@ class ExchangePage extends BasePage {
|
||||
? AmountValidator(
|
||||
isAutovalidate: true,
|
||||
currency: exchangeViewModel.receiveCurrency,
|
||||
amountParsingProxy: exchangeViewModel.amountParsingProxy,
|
||||
minValue: exchangeViewModel.limits.min.toString(),
|
||||
maxValue: exchangeViewModel.limits.max.toString(),
|
||||
).call(value)
|
||||
@@ -781,6 +784,7 @@ class ExchangePage extends BasePage {
|
||||
exchangeViewModel.receiveAddress =
|
||||
await fetchParsedAddress(context, domain, exchangeViewModel.receiveCurrency);
|
||||
},
|
||||
useSatoshis: exchangeViewModel.useSatoshisReceive,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange/widgets/exchange_card.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart';
|
||||
import 'package:cake_wallet/core/amount_validator.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange/widgets/present_provider_picker.dart';
|
||||
@@ -129,8 +129,10 @@ class ExchangeTemplatePage extends BasePage {
|
||||
Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
borderColor: Theme.of(context).colorScheme.outlineVariant,
|
||||
fillColor: Theme.of(context).colorScheme.surfaceContainer,
|
||||
currencyValueValidator:
|
||||
AmountValidator(currency: exchangeViewModel.depositCurrency),
|
||||
currencyValueValidator: AmountValidator(
|
||||
currency: exchangeViewModel.depositCurrency,
|
||||
amountParsingProxy: exchangeViewModel.amountParsingProxy,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -160,8 +162,10 @@ class ExchangeTemplatePage extends BasePage {
|
||||
Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
borderColor: Theme.of(context).colorScheme.outlineVariant,
|
||||
fillColor: Theme.of(context).colorScheme.surfaceContainerLow,
|
||||
currencyValueValidator:
|
||||
AmountValidator(currency: exchangeViewModel.receiveCurrency),
|
||||
currencyValueValidator: AmountValidator(
|
||||
currency: exchangeViewModel.receiveCurrency,
|
||||
amountParsingProxy: exchangeViewModel.amountParsingProxy,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -11,7 +11,6 @@ class CurrencyPicker extends StatefulWidget {
|
||||
required this.onItemSelected,
|
||||
this.title,
|
||||
this.hintText,
|
||||
this.isMoneroWallet = false,
|
||||
this.isConvertFrom = false,
|
||||
super.key});
|
||||
|
||||
@@ -19,7 +18,6 @@ class CurrencyPicker extends StatefulWidget {
|
||||
final List<Currency> items;
|
||||
final String? title;
|
||||
final Function(Currency) onItemSelected;
|
||||
final bool isMoneroWallet;
|
||||
final bool isConvertFrom;
|
||||
final String? hintText;
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ class ExchangeCard<T extends Currency> extends StatefulWidget {
|
||||
this.onPushPasteButton,
|
||||
this.onPushAddressBookButton,
|
||||
this.onDispose,
|
||||
this.useSatoshis = false,
|
||||
required this.cardInstanceName,
|
||||
}) : super(key: key);
|
||||
|
||||
@@ -82,6 +83,7 @@ class ExchangeCard<T extends Currency> extends StatefulWidget {
|
||||
final Function()? onDispose;
|
||||
final String cardInstanceName;
|
||||
final Color fillColor;
|
||||
final bool useSatoshis;
|
||||
|
||||
@override
|
||||
ExchangeCardState<T> createState() => ExchangeCardState<T>();
|
||||
@@ -224,8 +226,8 @@ class ExchangeCardState<T extends Currency> extends State<ExchangeCard<T>> {
|
||||
currencyAmountTextFieldWidgetKey:
|
||||
ValueKey('${_cardInstanceName}_currency_amount_textfield_widget_key'),
|
||||
imageArrow: widget.imageArrow,
|
||||
selectedCurrency: _selectedCurrency.toString(),
|
||||
selectedCurrencyDecimals: _selectedCurrency.decimals,
|
||||
selectedCurrency: widget.useSatoshis ? "SATS" : "$_selectedCurrency",
|
||||
selectedCurrencyDecimals: widget.useSatoshis ? 0 : _selectedCurrency.decimals,
|
||||
amountFocusNode: widget.amountFocusNode,
|
||||
amountController: amountController,
|
||||
onTapPicker: () => _presentPicker(context),
|
||||
@@ -383,7 +385,7 @@ class ExchangeCardState<T extends Currency> extends State<ExchangeCard<T>> {
|
||||
onTap: () async {
|
||||
final contact = await Navigator.of(context).pushNamed(
|
||||
Routes.pickerAddressBook,
|
||||
arguments: widget.initialCurrency,
|
||||
arguments: [widget.initialCurrency, true],
|
||||
);
|
||||
|
||||
if (contact is ContactBase) {
|
||||
@@ -452,7 +454,6 @@ class ExchangeCardState<T extends Currency> extends State<ExchangeCard<T>> {
|
||||
selectedAtIndex: widget.currencies.indexOf(_selectedCurrency),
|
||||
items: widget.currencies,
|
||||
hintText: S.of(context).search_currency,
|
||||
isMoneroWallet: _isMoneroWallet,
|
||||
isConvertFrom: widget.hasRefundAddress,
|
||||
onItemSelected: (Currency item) => widget.onCurrencySelected(item as T),
|
||||
),
|
||||
|
||||
@@ -86,6 +86,7 @@ class _BuySellOptionButtonsState extends State<BuySellOptionButtons> {
|
||||
Expanded(
|
||||
flex: 5,
|
||||
child: SelectButton(
|
||||
center:true,
|
||||
height: 44,
|
||||
text: S.of(context).buy,
|
||||
isSelected: isBuySelected,
|
||||
@@ -107,6 +108,7 @@ class _BuySellOptionButtonsState extends State<BuySellOptionButtons> {
|
||||
Expanded(
|
||||
flex: 5,
|
||||
child: SelectButton(
|
||||
center:true,
|
||||
height: 44,
|
||||
text: S.of(context).sell,
|
||||
isSelected: !isBuySelected,
|
||||
|
||||
@@ -11,7 +11,7 @@ import 'package:cake_wallet/view_model/exchange/exchange_trade_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
|
||||
|
||||
class ExchangeTradeExternalSendPage extends BasePage {
|
||||
ExchangeTradeExternalSendPage({required this.exchangeTradeViewModel});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:cake_wallet/entities/parsed_address.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
@@ -10,6 +11,7 @@ import 'package:cake_wallet/src/widgets/bottom_sheet/confirm_sending_bottom_shee
|
||||
import 'package:cake_wallet/src/widgets/bottom_sheet/info_bottom_sheet_widget.dart';
|
||||
import 'package:cake_wallet/utils/request_review_handler.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cake_wallet/view_model/send/output.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -22,7 +24,7 @@ import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange_trade/widgets/timer_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
|
||||
void showInformation(ExchangeTradeViewModel exchangeTradeViewModel, BuildContext context) {
|
||||
@@ -176,8 +178,8 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
|
||||
textColor: widget.exchangeTradeViewModel.isSendable
|
||||
? Theme.of(context).colorScheme.onSecondaryContainer
|
||||
: Theme.of(context).colorScheme.onPrimary,
|
||||
isDisabled: widget.exchangeTradeViewModel.isSwapsXyzSendingEVMTokenSwap,
|
||||
),
|
||||
|
||||
SizedBox(height: 16),
|
||||
Observer(
|
||||
builder: (_) {
|
||||
@@ -293,49 +295,54 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
|
||||
if (state is ExecutedSuccessfullyState) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
final trade = widget.exchangeTradeViewModel.trade;
|
||||
|
||||
final isSwapsXyz = trade.provider == ExchangeProviderDescription.swapsXyz;
|
||||
final isEVMWallet = widget.exchangeTradeViewModel.sendViewModel.isEVMWallet;
|
||||
|
||||
final amountValue = isSwapsXyz && isEVMWallet
|
||||
final amountValue = isSwapsXyz && isEVMWallet && !widget.exchangeTradeViewModel.isSwapsXYZCanSendFromExternal
|
||||
? trade.amount
|
||||
: widget.exchangeTradeViewModel.sendViewModel.pendingTransaction!.amountFormatted;
|
||||
|
||||
final fiatAmountValue = isSwapsXyz && isEVMWallet && !widget.exchangeTradeViewModel.isSwapsXYZCanSendFromExternal
|
||||
? widget.exchangeTradeViewModel.sendViewModel.calculateTransactionFiatAmount(amountValue)
|
||||
: widget.exchangeTradeViewModel.sendViewModel.pendingTransactionFiatAmountFormatted;
|
||||
|
||||
if (context.mounted) {
|
||||
final result = await showModalBottomSheet<bool>(
|
||||
context: context,
|
||||
isDismissible: false,
|
||||
isScrollControlled: true,
|
||||
builder: (BuildContext bottomSheetContext) {
|
||||
builder: (bottomSheetContext) {
|
||||
final sendVM = widget.exchangeTradeViewModel.sendViewModel;
|
||||
|
||||
return ConfirmSendingBottomSheet(
|
||||
key: ValueKey('exchange_trade_page_confirm_sending_bottom_sheet_key'),
|
||||
footerType: FooterType.slideActionButton,
|
||||
isSlideActionEnabled:
|
||||
widget.exchangeTradeViewModel.sendViewModel.isReadyForSend,
|
||||
walletType: widget.exchangeTradeViewModel.sendViewModel.walletType,
|
||||
isSlideActionEnabled: sendVM.isReadyForSend,
|
||||
walletType: sendVM.walletType,
|
||||
titleText: S.of(bottomSheetContext).confirm_transaction,
|
||||
titleIconPath:
|
||||
widget.exchangeTradeViewModel.sendViewModel.selectedCryptoCurrency.iconPath,
|
||||
titleIconPath: sendVM.selectedCryptoCurrency.iconPath,
|
||||
currency: widget.exchangeTradeViewModel.sendViewModel.selectedCryptoCurrency,
|
||||
amount: S.of(bottomSheetContext).send_amount,
|
||||
amountValue: amountValue,
|
||||
fiatAmountValue: widget
|
||||
.exchangeTradeViewModel.sendViewModel.pendingTransactionFiatAmountFormatted,
|
||||
fee:
|
||||
isEVMCompatibleChain(widget.exchangeTradeViewModel.sendViewModel.walletType)
|
||||
? S.of(bottomSheetContext).send_estimated_fee
|
||||
: S.of(bottomSheetContext).send_fee,
|
||||
feeValue: widget
|
||||
.exchangeTradeViewModel.sendViewModel.pendingTransaction!.feeFormatted,
|
||||
feeFiatAmount: widget.exchangeTradeViewModel.sendViewModel
|
||||
.pendingTransactionFeeFiatAmountFormatted,
|
||||
outputs: widget.exchangeTradeViewModel.sendViewModel.outputs,
|
||||
amountValue: sendVM.amountParsingProxy
|
||||
.getDisplayCryptoAmount(amountValue, sendVM.selectedCryptoCurrency),
|
||||
fiatAmountValue: fiatAmountValue,
|
||||
fee: isEVMCompatibleChain(sendVM.walletType)
|
||||
? S.of(bottomSheetContext).send_estimated_fee
|
||||
: S.of(bottomSheetContext).send_fee,
|
||||
feeValue: "${sendVM.amountParsingProxy.getDisplayCryptoAmount(
|
||||
sendVM.pendingTransaction!.feeFormattedValue,
|
||||
sendVM.selectedCryptoCurrency)} ${sendVM.amountParsingProxy
|
||||
.getCryptoSymbol(sendVM.wallet.currency)}",
|
||||
feeFiatAmount: sendVM.pendingTransactionFeeFiatAmountFormatted,
|
||||
outputs: sendVM.outputs,
|
||||
onSlideActionComplete: () async {
|
||||
if (bottomSheetContext.mounted && Navigator.canPop(bottomSheetContext)) {
|
||||
Navigator.of(bottomSheetContext).pop(true);
|
||||
}
|
||||
widget.exchangeTradeViewModel.sendViewModel.commitTransaction(context);
|
||||
if (bottomSheetContext.mounted && Navigator.canPop(bottomSheetContext)) Navigator.of(bottomSheetContext).pop(true);
|
||||
|
||||
sendVM.commitTransaction(context);
|
||||
widget.exchangeTradeViewModel.registerSwapsXyzTransaction();
|
||||
},
|
||||
amountParsingProxy: sendVM.amountParsingProxy,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -217,7 +217,7 @@ class FeeSelectionWidget extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
'${output.estimatedFee} ${feesViewModel.currency}',
|
||||
'${output.estimatedFee} ${feesViewModel.amountParsingProxy.getCryptoSymbol(feesViewModel.currency)}',
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
|
||||
@@ -14,7 +14,7 @@ import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
|
||||
|
||||
class NanoChangeRepPage extends BasePage {
|
||||
NanoChangeRepPage({required SettingsStore settingsStore, required WalletBase wallet})
|
||||
|
||||
@@ -12,7 +12,7 @@ import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
|
||||
@@ -84,7 +84,7 @@ class _AdvancedPrivacySettingsBody extends StatefulWidget {
|
||||
class _AdvancedPrivacySettingsBodyState extends State<_AdvancedPrivacySettingsBody> {
|
||||
final TextEditingController passphraseController = TextEditingController();
|
||||
final TextEditingController confirmPassphraseController = TextEditingController();
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
final _formKey = GlobalKey<NodeFormState>();
|
||||
final _passphraseFormKey = GlobalKey<FormState>();
|
||||
bool? testnetValue;
|
||||
|
||||
@@ -274,7 +274,7 @@ class _AdvancedPrivacySettingsBodyState extends State<_AdvancedPrivacySettingsBo
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 24, right: 24, top: 24),
|
||||
child: NodeForm(
|
||||
formKey: _formKey,
|
||||
key: _formKey,
|
||||
nodeViewModel: widget.nodeViewModel,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:cake_wallet/core/execution_state.dart';
|
||||
import 'package:cake_wallet/core/wallet_name_validator.dart';
|
||||
import 'package:cake_wallet/entities/generate_name.dart';
|
||||
import 'package:cw_core/generate_name.dart';
|
||||
import 'package:cake_wallet/entities/seed_type.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/main.dart';
|
||||
@@ -13,7 +13,7 @@ import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/gradient_background.dart';
|
||||
import 'package:cake_wallet/src/widgets/picker.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/seed_language_picker.dart';
|
||||
import 'package:cake_wallet/src/widgets/seed_language_selector.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
@@ -203,7 +203,7 @@ class _WalletNameFormState extends State<WalletNameForm> {
|
||||
icon: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(6.0),
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
width: 34,
|
||||
@@ -267,7 +267,7 @@ class _WalletNameFormState extends State<WalletNameForm> {
|
||||
child: SelectButton(
|
||||
key: ValueKey('new_wallet_page_monero_seed_type_button_key'),
|
||||
text: widget._seedSettingsViewModel.moneroSeedType.title,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
onTap: () async {
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
@@ -291,7 +291,7 @@ class _WalletNameFormState extends State<WalletNameForm> {
|
||||
builder: (BuildContext build) => Padding(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: SeedLanguageSelector(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
key: _languageSelectorKey,
|
||||
buttonKey: ValueKey('new_wallet_page_seed_language_selector_button_key'),
|
||||
initialSelected: defaultSeedLanguage,
|
||||
|
||||
@@ -9,7 +9,7 @@ import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart';
|
||||
import 'package:cake_wallet/src/screens/setup_2fa/widgets/popup_cancellable_alert.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/search_bar_widget.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
@@ -37,19 +37,19 @@ class NewWalletTypePage extends BasePage {
|
||||
|
||||
@override
|
||||
Function(BuildContext)? get pushToNextWidget => (context) {
|
||||
FocusScopeNode currentFocus = FocusScope.of(context);
|
||||
if (!currentFocus.hasPrimaryFocus) {
|
||||
currentFocus.focusedChild?.unfocus();
|
||||
}
|
||||
};
|
||||
FocusScopeNode currentFocus = FocusScope.of(context);
|
||||
if (!currentFocus.hasPrimaryFocus) {
|
||||
currentFocus.focusedChild?.unfocus();
|
||||
}
|
||||
};
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) => WalletTypeForm(
|
||||
walletImage: currentTheme.isDark ? walletTypeImage : walletTypeLightImage,
|
||||
isCreate: newWalletTypeArguments.isCreate,
|
||||
onTypeSelected: newWalletTypeArguments.onTypeSelected,
|
||||
hardwareWalletType: newWalletTypeArguments.hardwareWalletType,
|
||||
);
|
||||
walletImage: currentTheme.isDark ? walletTypeImage : walletTypeLightImage,
|
||||
isCreate: newWalletTypeArguments.isCreate,
|
||||
onTypeSelected: newWalletTypeArguments.onTypeSelected,
|
||||
hardwareWalletType: newWalletTypeArguments.hardwareWalletType,
|
||||
);
|
||||
}
|
||||
|
||||
class WalletTypeForm extends StatefulWidget {
|
||||
@@ -86,10 +86,10 @@ class WalletTypeFormState extends State<WalletTypeForm> {
|
||||
void initState() {
|
||||
types = filteredTypes = availableWalletTypes
|
||||
.where((element) =>
|
||||
!widget.isHardwareWallet ||
|
||||
DeviceConnectionType.supportedConnectionTypes(
|
||||
element, widget.hardwareWalletType!, Platform.isIOS)
|
||||
.isNotEmpty)
|
||||
!widget.isHardwareWallet ||
|
||||
DeviceConnectionType.supportedConnectionTypes(
|
||||
element, widget.hardwareWalletType!, Platform.isIOS)
|
||||
.isNotEmpty)
|
||||
.toList();
|
||||
super.initState();
|
||||
|
||||
@@ -116,8 +116,8 @@ class WalletTypeFormState extends State<WalletTypeForm> {
|
||||
S.of(context).choose_wallet_currency,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
@@ -132,12 +132,12 @@ class WalletTypeFormState extends State<WalletTypeForm> {
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
...filteredTypes.map(
|
||||
(type) => Padding(
|
||||
(type) => Padding(
|
||||
padding: EdgeInsets.only(top: 12),
|
||||
child: SelectButton(
|
||||
key: ValueKey('new_wallet_type_${type.name}_button_key'),
|
||||
image: Image.asset(
|
||||
getCryptoCurrencyForWalletListItem(type).iconPath ?? '',
|
||||
getCryptoCurrencyIconForWalletListItem(type),
|
||||
height: 24,
|
||||
width: 24,
|
||||
),
|
||||
@@ -148,7 +148,7 @@ class WalletTypeFormState extends State<WalletTypeForm> {
|
||||
onTap: () => setState(() => selected = type),
|
||||
deviceConnectionTypes: widget.isHardwareWallet
|
||||
? DeviceConnectionType.supportedConnectionTypes(
|
||||
type, widget.hardwareWalletType!, Platform.isIOS)
|
||||
type, widget.hardwareWalletType!, Platform.isIOS)
|
||||
: [],
|
||||
),
|
||||
),
|
||||
@@ -201,4 +201,4 @@ class WalletTypeFormState extends State<WalletTypeForm> {
|
||||
Navigator.of(context).pushNamed(Routes.walletGroupDescription, arguments: selected!);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,7 +95,7 @@ class WalletGroupsDisplayBody extends StatelessWidget {
|
||||
isSelected:
|
||||
walletGroupsDisplayViewModel.selectedSingleWallet == wallet,
|
||||
leadingWidget: Image.asset(
|
||||
getCryptoCurrencyForWalletListItem(wallet.type).iconPath!,
|
||||
getCryptoCurrencyIconForWalletListItem(wallet.type),
|
||||
width: 32,
|
||||
height: 32,
|
||||
),
|
||||
@@ -146,14 +146,16 @@ class WalletGroupsDisplayBody extends StatelessWidget {
|
||||
|
||||
Future<void> onTypeSelected(BuildContext context) async {
|
||||
final mnemonic = await walletGroupsDisplayViewModel.getSelectedWalletMnemonic();
|
||||
Navigator.of(context).pushNamed(
|
||||
Routes.newWallet,
|
||||
arguments: NewWalletArguments(
|
||||
type: walletGroupsDisplayViewModel.type,
|
||||
mnemonic: mnemonic,
|
||||
isChildWallet: true,
|
||||
),
|
||||
);
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pushNamed(
|
||||
Routes.newWallet,
|
||||
arguments: NewWalletArguments(
|
||||
type: walletGroupsDisplayViewModel.type,
|
||||
mnemonic: mnemonic,
|
||||
isChildWallet: true,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,8 +67,8 @@ class GroupedWalletExpansionTile extends StatelessWidget {
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 6),
|
||||
child: ExpansionTile(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
||||
collapsedShape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
||||
shape: RoundedSuperellipseBorder(borderRadius: BorderRadius.circular(18)),
|
||||
collapsedShape: RoundedSuperellipseBorder(borderRadius: BorderRadius.circular(18)),
|
||||
collapsedBackgroundColor: backgroundColor,
|
||||
backgroundColor: backgroundColor,
|
||||
onExpansionChanged: onExpansionChanged,
|
||||
@@ -80,7 +80,13 @@ class GroupedWalletExpansionTile extends StatelessWidget {
|
||||
EdgeInsets.symmetric(vertical: 1, horizontal: !isCurrentlySelectedWallet ? 16 : 0),
|
||||
iconColor: effectiveArrowColor,
|
||||
collapsedIconColor: effectiveArrowColor,
|
||||
leading: leadingWidget,
|
||||
leading: (childWallets.isEmpty && onTitleTapped != null && leadingWidget != null)
|
||||
? GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: onTitleTapped,
|
||||
child: leadingWidget,
|
||||
)
|
||||
: leadingWidget,
|
||||
trailing: trailingWidget ?? (childWallets.isEmpty ? SizedBox.shrink() : null),
|
||||
title: GestureDetector(
|
||||
onTap: onTitleTapped,
|
||||
@@ -123,7 +129,7 @@ class GroupedWalletExpansionTile extends StatelessWidget {
|
||||
: SizedBox(width: 7),
|
||||
SizedBox(width: 24),
|
||||
Image.asset(
|
||||
getCryptoCurrencyForWalletListItem(item.type).iconPath!,
|
||||
getCryptoCurrencyIconForWalletListItem(item.type),
|
||||
width: 32,
|
||||
height: 32,
|
||||
),
|
||||
|
||||
@@ -17,7 +17,7 @@ class SelectButton extends StatelessWidget {
|
||||
this.deviceConnectionTypes,
|
||||
this.borderRadius,
|
||||
this.padding,
|
||||
super.key,
|
||||
super.key, this.center = false,
|
||||
});
|
||||
|
||||
final Widget? image;
|
||||
@@ -34,6 +34,7 @@ class SelectButton extends StatelessWidget {
|
||||
final Color? borderColor;
|
||||
final BorderRadius? borderRadius;
|
||||
final EdgeInsets? padding;
|
||||
final bool center;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -64,10 +65,9 @@ class SelectButton extends StatelessWidget {
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
height: height,
|
||||
padding: padding ?? EdgeInsets.only(left: 30, right: 30),
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: borderRadius ?? BorderRadius.all(Radius.circular(15)),
|
||||
borderRadius: borderRadius ?? BorderRadius.all(Radius.circular(18)),
|
||||
color: backgroundColor,
|
||||
border: borderColor != null ? Border.all(color: borderColor!, width: 2) : null,
|
||||
),
|
||||
@@ -78,12 +78,13 @@ class SelectButton extends StatelessWidget {
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisAlignment: center ? MainAxisAlignment.center : MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
if(!center) SizedBox(width:16),
|
||||
image ?? Offstage(),
|
||||
Padding(
|
||||
padding: image != null ? EdgeInsets.only(left: 15) : EdgeInsets.only(left: 0),
|
||||
padding: image != null ? EdgeInsets.only(left: 14) : EdgeInsets.only(left: 0),
|
||||
child: Text(
|
||||
text,
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
@@ -96,7 +97,8 @@ class SelectButton extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
...trailingIcons
|
||||
...trailingIcons,
|
||||
if(!center) SizedBox(width:16),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,53 +1,51 @@
|
||||
import 'package:cake_wallet/core/execution_state.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/keyboard_hide_overlay.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
|
||||
import 'package:cake_wallet/src/screens/nodes/widgets/node_form.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
class NodeCreateOrEditPage extends BasePage {
|
||||
class NodeCreateOrEditPage extends StatefulWidget {
|
||||
NodeCreateOrEditPage({
|
||||
required this.nodeCreateOrEditViewModel,
|
||||
this.editingNode,
|
||||
this.isSelected,
|
||||
this.type,
|
||||
}) : _formKey = GlobalKey<FormState>();
|
||||
});
|
||||
|
||||
final GlobalKey<FormState> _formKey;
|
||||
final NodeCreateOrEditViewModel nodeCreateOrEditViewModel;
|
||||
final Node? editingNode;
|
||||
final bool? isSelected;
|
||||
final WalletType? type;
|
||||
|
||||
@override
|
||||
String get title => editingNode != null ? S.current.edit_node : S.current.node_new;
|
||||
State<NodeCreateOrEditPage> createState() => _NodeCreateOrEditPageState();
|
||||
}
|
||||
|
||||
class _NodeCreateOrEditPageState extends State<NodeCreateOrEditPage> {
|
||||
final _nodeFormKey = GlobalKey<NodeFormState>();
|
||||
|
||||
|
||||
@override
|
||||
Widget trailing(BuildContext context) => IconButton(
|
||||
onPressed: () => nodeCreateOrEditViewModel.scanQRCodeForNewNode(context),
|
||||
splashColor: Colors.transparent,
|
||||
highlightColor: Colors.transparent,
|
||||
hoverColor: Colors.transparent,
|
||||
icon: Image.asset('assets/images/qr_code_icon.png'),
|
||||
);
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
void initState() {
|
||||
super.initState();
|
||||
reaction(
|
||||
(_) => nodeCreateOrEditViewModel.connectionState,
|
||||
(ExecutionState state) {
|
||||
(_) => widget.nodeCreateOrEditViewModel.connectionState,
|
||||
(ExecutionState state) {
|
||||
if (state is ExecutedSuccessfullyState) {
|
||||
WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) => showPopUp<void>(
|
||||
(_) => showPopUp<void>(
|
||||
context: context,
|
||||
builder: (context) => AlertWithOneAction(
|
||||
alertTitle: S.of(context).new_node_testing,
|
||||
@@ -63,7 +61,7 @@ class NodeCreateOrEditPage extends BasePage {
|
||||
|
||||
if (state is FailureState) {
|
||||
WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) => showPopUp<void>(
|
||||
(_) => showPopUp<void>(
|
||||
context: context,
|
||||
builder: (context) => AlertWithOneAction(
|
||||
alertTitle: S.of(context).error,
|
||||
@@ -77,78 +75,104 @@ class NodeCreateOrEditPage extends BasePage {
|
||||
},
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.only(left: 24, right: 24),
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: const EdgeInsets.only(bottom: 24.0, top: 8),
|
||||
content: NodeForm(
|
||||
formKey: _formKey,
|
||||
nodeViewModel: nodeCreateOrEditViewModel,
|
||||
editingNode: editingNode,
|
||||
type: type,
|
||||
),
|
||||
bottomSectionPadding: const EdgeInsets.only(bottom: 24),
|
||||
bottomSection: Observer(
|
||||
builder: (_) => Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
ModalTopBar(
|
||||
title: widget.editingNode != null ? S.current.edit_node : S.current.node_new,
|
||||
leadingIcon: Icon(Icons.arrow_back_ios_new),
|
||||
onLeadingPressed: Navigator.of(context).pop,
|
||||
trailingIcon: SvgPicture.asset(
|
||||
"assets/new-ui/scan.svg",width:24,height:24,
|
||||
colorFilter: ColorFilter.mode(Theme.of(context).colorScheme.primary, BlendMode.srcIn),
|
||||
),
|
||||
onTrailingPressed: () => widget.nodeCreateOrEditViewModel.scanQRCodeForNewNode(context),
|
||||
),
|
||||
Expanded(
|
||||
child: KeyboardHideOverlay(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: LoadingPrimaryButton(
|
||||
onPressed: () async {
|
||||
final confirmed = await showPopUp<bool>(
|
||||
context: context,
|
||||
builder: (context) => AlertWithTwoActions(
|
||||
alertTitle: S.of(context).remove_node,
|
||||
alertContent: S.of(context).remove_node_message,
|
||||
rightButtonText: S.of(context).remove,
|
||||
leftButtonText: S.of(context).cancel,
|
||||
actionRightButton: () => Navigator.pop(context, true),
|
||||
actionLeftButton: () => Navigator.pop(context, false),
|
||||
padding: const EdgeInsets.only(left: 18, right: 18),
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: const EdgeInsets.only(bottom: 24.0, top: 8),
|
||||
content: NodeForm(
|
||||
key: _nodeFormKey,
|
||||
nodeViewModel: widget.nodeCreateOrEditViewModel,
|
||||
),
|
||||
bottomSectionPadding: const EdgeInsets.only(bottom: 24),
|
||||
bottomSection: Observer(
|
||||
builder: (_) => Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: LoadingPrimaryButton(
|
||||
onPressed: () async {
|
||||
final confirmed = await showPopUp<bool>(
|
||||
context: context,
|
||||
builder: (context) => AlertWithTwoActions(
|
||||
alertTitle: S.of(context).remove_node,
|
||||
alertContent: S.of(context).remove_node_message,
|
||||
rightButtonText: S.of(context).remove,
|
||||
leftButtonText: S.of(context).cancel,
|
||||
actionRightButton: () => Navigator.pop(context, true),
|
||||
actionLeftButton: () => Navigator.pop(context, false),
|
||||
),
|
||||
) ??
|
||||
false;
|
||||
|
||||
if (confirmed) {
|
||||
await widget.editingNode!.delete();
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
text: S.of(context).delete,
|
||||
isDisabled: widget.editingNode == null ||
|
||||
!widget.nodeCreateOrEditViewModel.isReady ||
|
||||
(widget.isSelected ?? false),
|
||||
color: Theme.of(context).colorScheme.errorContainer,
|
||||
textColor: Theme.of(context).colorScheme.onErrorContainer,
|
||||
),
|
||||
),
|
||||
) ??
|
||||
false;
|
||||
|
||||
if (confirmed) {
|
||||
await editingNode!.delete();
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
text: S.of(context).delete,
|
||||
isDisabled: editingNode == null ||
|
||||
!nodeCreateOrEditViewModel.isReady ||
|
||||
(isSelected ?? false),
|
||||
color: Theme.of(context).colorScheme.errorContainer,
|
||||
textColor: Theme.of(context).colorScheme.onErrorContainer,
|
||||
),
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: PrimaryButton(
|
||||
onPressed: () async {
|
||||
if (_nodeFormKey.currentState != null && !_nodeFormKey.currentState!.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
await widget.nodeCreateOrEditViewModel.save(
|
||||
editingNode: widget.editingNode, saveAsCurrent: widget.isSelected ?? false);
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
text: S.of(context).save,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
isDisabled: (!widget.nodeCreateOrEditViewModel.isReady) ||
|
||||
(widget.nodeCreateOrEditViewModel.connectionState is IsExecutingState),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: PrimaryButton(
|
||||
onPressed: () async {
|
||||
if (_formKey.currentState != null && !_formKey.currentState!.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
await nodeCreateOrEditViewModel.save(
|
||||
editingNode: editingNode, saveAsCurrent: isSelected ?? false);
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
text: S.of(context).save,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
isDisabled: (!nodeCreateOrEditViewModel.isReady) ||
|
||||
(nodeCreateOrEditViewModel.connectionState is IsExecutingState),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -11,13 +11,12 @@ import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
|
||||
|
||||
class PowNodeCreateOrEditPage extends BasePage {
|
||||
PowNodeCreateOrEditPage(
|
||||
{required this.nodeCreateOrEditViewModel, this.editingNode, this.isSelected})
|
||||
: _formKey = GlobalKey<FormState>(),
|
||||
_addressController = TextEditingController(),
|
||||
: _addressController = TextEditingController(),
|
||||
_portController = TextEditingController(),
|
||||
_loginController = TextEditingController(),
|
||||
_passwordController = TextEditingController() {
|
||||
@@ -55,7 +54,6 @@ class PowNodeCreateOrEditPage extends BasePage {
|
||||
.addListener(() => nodeCreateOrEditViewModel.password = _passwordController.text);
|
||||
}
|
||||
|
||||
final GlobalKey<FormState> _formKey;
|
||||
final TextEditingController _addressController;
|
||||
final TextEditingController _portController;
|
||||
final TextEditingController _loginController;
|
||||
@@ -80,6 +78,7 @@ class PowNodeCreateOrEditPage extends BasePage {
|
||||
final NodeCreateOrEditViewModel nodeCreateOrEditViewModel;
|
||||
final Node? editingNode;
|
||||
final bool? isSelected;
|
||||
final _formKey = GlobalKey<NodeFormState>();
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
@@ -118,9 +117,8 @@ class PowNodeCreateOrEditPage extends BasePage {
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: EdgeInsets.only(bottom: 24.0, top: 8),
|
||||
content: NodeForm(
|
||||
formKey: _formKey,
|
||||
key: _formKey,
|
||||
nodeViewModel: nodeCreateOrEditViewModel,
|
||||
editingNode: editingNode,
|
||||
),
|
||||
bottomSectionPadding: EdgeInsets.only(bottom: 24),
|
||||
bottomSection: Observer(
|
||||
|
||||
@@ -1,270 +1,210 @@
|
||||
import 'package:cake_wallet/core/node_address_validator.dart';
|
||||
import 'package:cake_wallet/core/node_port_validator.dart';
|
||||
import 'package:cake_wallet/core/socks_proxy_node_address_validator.dart';
|
||||
import 'package:cake_wallet/entities/new_ui_entities/list_item/list_Item_checkbox.dart';
|
||||
import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_text_field.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_checkbox.dart';
|
||||
import 'package:cake_wallet/src/widgets/new_list_row/new_list_section.dart';
|
||||
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
class NodeForm extends StatefulWidget {
|
||||
NodeForm({
|
||||
super.key,
|
||||
required this.nodeViewModel,
|
||||
required this.formKey,
|
||||
this.editingNode,
|
||||
this.type,
|
||||
});
|
||||
|
||||
final NodeCreateOrEditViewModel nodeViewModel;
|
||||
final GlobalKey<FormState> formKey;
|
||||
final Node? editingNode;
|
||||
final WalletType? type;
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() =>
|
||||
_NodeFormState(nodeViewModel: nodeViewModel, editingNode: this.editingNode);
|
||||
State<StatefulWidget> createState() => NodeFormState(vm: nodeViewModel);
|
||||
}
|
||||
|
||||
class _NodeFormState extends State<NodeForm> {
|
||||
_NodeFormState({
|
||||
required this.nodeViewModel,
|
||||
Node? editingNode,
|
||||
}) : _addressController = TextEditingController(text: editingNode?.uri.host.toString()),
|
||||
_pathController = TextEditingController(text: editingNode?.path.toString()),
|
||||
_portController = TextEditingController(
|
||||
text: (editingNode != null && editingNode.uri.hasPort)
|
||||
? editingNode.uri.port.toString()
|
||||
: ''),
|
||||
_loginController = TextEditingController(text: editingNode?.login),
|
||||
_passwordController = TextEditingController(text: editingNode?.password),
|
||||
_socksAddressController = TextEditingController(text: editingNode?.socksProxyAddress) {
|
||||
if (editingNode != null) {
|
||||
nodeViewModel
|
||||
..setAddress(editingNode.uri.host.toString())
|
||||
..setPath(editingNode.path.toString())
|
||||
..setPort(editingNode.uri.hasPort ? editingNode.uri.port.toString() : '')
|
||||
..setPassword(editingNode.password ?? '')
|
||||
..setLogin(editingNode.login ?? '')
|
||||
..setSSL(editingNode.isSSL)
|
||||
..setTrusted(editingNode.trusted)
|
||||
..setIsEnabledForAutoSwitching(editingNode.isEnabledForAutoSwitching)
|
||||
..setSocksProxy(editingNode.useSocksProxy)
|
||||
..setSocksProxyAddress(editingNode.socksProxyAddress ?? '');
|
||||
}
|
||||
class NodeFormState extends State<NodeForm> {
|
||||
NodeFormState({required this.vm});
|
||||
|
||||
if (nodeViewModel.hasAuthCredentials) {
|
||||
reaction((_) => nodeViewModel.login, (String login) {
|
||||
if (login != _loginController.text) _loginController.text = login;
|
||||
});
|
||||
|
||||
reaction((_) => nodeViewModel.password, (String password) {
|
||||
if (password != _passwordController.text) _passwordController.text = password;
|
||||
});
|
||||
}
|
||||
reaction((_) => nodeViewModel.address, (String address) {
|
||||
if (address != _addressController.text) {
|
||||
_addressController.text = address;
|
||||
}
|
||||
});
|
||||
|
||||
reaction((_) => nodeViewModel.port, (String port) {
|
||||
if (port != _portController.text) _portController.text = port;
|
||||
});
|
||||
|
||||
reaction((_) => nodeViewModel.path, (String path) {
|
||||
if (path != _pathController.text) _pathController.text = path;
|
||||
});
|
||||
|
||||
_addressController.addListener(() => nodeViewModel.address = _addressController.text);
|
||||
_pathController.addListener(() => nodeViewModel.path = _pathController.text);
|
||||
_portController.addListener(() => nodeViewModel.port = _portController.text);
|
||||
_loginController.addListener(() => nodeViewModel.login = _loginController.text);
|
||||
_passwordController.addListener(() => nodeViewModel.password = _passwordController.text);
|
||||
_socksAddressController
|
||||
.addListener(() => nodeViewModel.socksProxyAddress = _socksAddressController.text);
|
||||
}
|
||||
|
||||
final NodeCreateOrEditViewModel nodeViewModel;
|
||||
|
||||
final TextEditingController _addressController;
|
||||
final TextEditingController _pathController;
|
||||
final TextEditingController _portController;
|
||||
final TextEditingController _loginController;
|
||||
final TextEditingController _passwordController;
|
||||
final TextEditingController _socksAddressController;
|
||||
final Map<String, TextEditingController> _controllers = {};
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Form(
|
||||
key: widget.formKey,
|
||||
child: Column(children: <Widget>[
|
||||
Row(children: <Widget>[
|
||||
Expanded(
|
||||
child: BaseTextFormField(
|
||||
controller: _addressController,
|
||||
hintText: S.of(context).node_address,
|
||||
validator: widget.type == WalletType.decred
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
for(final key in vm.textFieldKeys) {
|
||||
_controllers[key] = TextEditingController();
|
||||
|
||||
_controllers[key]!.text = vm.getTextValue(key);
|
||||
_controllers[key]!.addListener(() {
|
||||
final text = _controllers[key]!.text;
|
||||
vm.updateViewModelFromText(key, text);
|
||||
});
|
||||
}
|
||||
|
||||
_setupReactions();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
for (final controller in _controllers.values) {
|
||||
controller.dispose();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
final NodeCreateOrEditViewModel vm;
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Observer(
|
||||
builder: (_)=>Form(
|
||||
key: _formKey,
|
||||
child: NewListSections(
|
||||
sections: {
|
||||
'main': [
|
||||
ListItemTextField(
|
||||
keyValue: vm.nodeLabelUIKey,
|
||||
label: 'Node label',
|
||||
initialValue: vm.label,
|
||||
),
|
||||
ListItemTextField(
|
||||
keyValue: vm.nodeAddressUIKey,
|
||||
label: S.current.node_address,
|
||||
initialValue: vm.address,
|
||||
validator: vm.walletType == WalletType.decred
|
||||
? NodeAddressValidatorDecredBlankException()
|
||||
: NodeAddressValidator(),
|
||||
),
|
||||
)
|
||||
]),
|
||||
const SizedBox(height: 10),
|
||||
if (nodeViewModel.hasPathSupport) ...[
|
||||
Row(children: <Widget>[
|
||||
Expanded(
|
||||
child: BaseTextFormField(
|
||||
controller: _pathController,
|
||||
hintText: "/path",
|
||||
if (vm.hasPathSupport)
|
||||
ListItemTextField(
|
||||
keyValue: vm.nodePathUIKey,
|
||||
label: '/path',
|
||||
initialValue: vm.path,
|
||||
validator: NodePathValidator(),
|
||||
),
|
||||
)
|
||||
]),
|
||||
const SizedBox(height: 10),
|
||||
],
|
||||
Row(children: <Widget>[
|
||||
Expanded(
|
||||
child: BaseTextFormField(
|
||||
controller: _portController,
|
||||
hintText: S.of(context).node_port,
|
||||
keyboardType: TextInputType.numberWithOptions(signed: false, decimal: false),
|
||||
ListItemTextField(
|
||||
keyValue: vm.nodePortUIKey,
|
||||
label: S.current.node_port,
|
||||
initialValue: '',
|
||||
//keyboardType: TextInputType.numberWithOptions(signed: false, decimal: false),
|
||||
validator: NodePortValidator(),
|
||||
),
|
||||
)
|
||||
]),
|
||||
const SizedBox(height: 10),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 20),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Observer(
|
||||
builder: (_) => StandardCheckbox(
|
||||
value: nodeViewModel.useSSL,
|
||||
borderColor: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
iconColor: Theme.of(context).colorScheme.primary,
|
||||
onChanged: (value) => nodeViewModel.useSSL = value,
|
||||
caption: S.of(context).use_ssl,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 20),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Observer(
|
||||
builder: (_) => StandardCheckbox(
|
||||
value: nodeViewModel.isEnabledForAutoSwitching,
|
||||
borderColor: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
iconColor: Theme.of(context).colorScheme.primary,
|
||||
onChanged: (value) => nodeViewModel.isEnabledForAutoSwitching = value,
|
||||
caption: S.current.enable_for_auto_switching,
|
||||
),
|
||||
if (vm.hasAuthCredentials) ...[
|
||||
ListItemTextField(
|
||||
keyValue: vm.nodeUsernameUIKey,
|
||||
label: S.current.login,
|
||||
initialValue: vm.login,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
if (nodeViewModel.hasAuthCredentials) ...[
|
||||
Row(children: <Widget>[
|
||||
Expanded(
|
||||
child: BaseTextFormField(
|
||||
controller: _loginController,
|
||||
hintText: S.of(context).login,
|
||||
ListItemTextField(
|
||||
keyValue: vm.nodePasswordUIKey,
|
||||
label: S.current.password,
|
||||
initialValue: vm.password,
|
||||
),
|
||||
)
|
||||
]),
|
||||
const SizedBox(height: 10),
|
||||
Row(children: <Widget>[
|
||||
Expanded(
|
||||
child: BaseTextFormField(
|
||||
controller: _passwordController,
|
||||
hintText: S.of(context).password,
|
||||
),
|
||||
)
|
||||
]),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 20),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Observer(
|
||||
builder: (_) => StandardCheckbox(
|
||||
value: nodeViewModel.trusted,
|
||||
borderColor: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
iconColor: Theme.of(context).colorScheme.primary,
|
||||
onChanged: (value) => nodeViewModel.trusted = value,
|
||||
caption: S.of(context).trusted,
|
||||
),
|
||||
),
|
||||
],
|
||||
]
|
||||
],
|
||||
'advanced': [
|
||||
ListItemCheckbox(
|
||||
keyValue: vm.useSSLUIKey,
|
||||
label: S.current.use_ssl,
|
||||
value: vm.useSSL,
|
||||
onChanged: (value) => vm.useSSL = value),
|
||||
ListItemCheckbox(
|
||||
keyValue: vm.nodeTrustedUIKey,
|
||||
label: S.current.trusted,
|
||||
value: vm.trusted,
|
||||
onChanged: (value) => vm.trusted = value,
|
||||
),
|
||||
),
|
||||
Observer(
|
||||
builder: (_) => Column(children: [
|
||||
if (nodeViewModel.usesEmbeddedProxy) ...[
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
StandardCheckbox(
|
||||
value: nodeViewModel.usesEmbeddedProxy,
|
||||
gradientBackground: false,
|
||||
borderColor: Theme.of(context).dividerColor,
|
||||
iconColor: Theme.of(context).colorScheme.primary,
|
||||
onChanged: null,
|
||||
caption: 'Embedded Tor SOCKS Proxy',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
StandardCheckbox(
|
||||
value: nodeViewModel.useSocksProxy,
|
||||
borderColor: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
iconColor: Theme.of(context).colorScheme.primary,
|
||||
onChanged: (value) {
|
||||
if (!value) _socksAddressController.text = '';
|
||||
nodeViewModel.useSocksProxy = value;
|
||||
},
|
||||
caption: 'SOCKS Proxy',
|
||||
),
|
||||
],
|
||||
),
|
||||
if (vm.usesEmbeddedProxy)
|
||||
ListItemCheckbox(
|
||||
keyValue: vm.nodeEmbeddedTorProxyUIKey,
|
||||
label: 'Embedded Tor SOCKS Proxy',
|
||||
value: vm.usesEmbeddedProxy,
|
||||
onChanged: (_) {},
|
||||
),
|
||||
if (nodeViewModel.useSocksProxy) ...[
|
||||
const SizedBox(height: 10),
|
||||
Row(children: <Widget>[
|
||||
Expanded(
|
||||
child: BaseTextFormField(
|
||||
controller: _socksAddressController,
|
||||
hintText: '[<ip>:]<port>',
|
||||
validator: SocksProxyNodeAddressValidator(),
|
||||
),
|
||||
)
|
||||
]),
|
||||
]
|
||||
]),
|
||||
),
|
||||
]
|
||||
]),
|
||||
ListItemCheckbox(
|
||||
keyValue: vm.useSocksProxyUIKey,
|
||||
label: 'Use SOCKS Proxy',
|
||||
value: vm.useSocksProxy,
|
||||
onChanged: (value) {
|
||||
vm.socksProxyAddress = '';
|
||||
vm.useSocksProxy = value;
|
||||
},
|
||||
),
|
||||
if (vm.useSocksProxy)
|
||||
ListItemTextField(
|
||||
keyValue: vm.socksProxyAddressUIKey,
|
||||
label: 'host:port',
|
||||
initialValue: vm.socksProxyAddress,
|
||||
validator: SocksProxyNodeAddressValidator(),
|
||||
),
|
||||
ListItemCheckbox(
|
||||
keyValue: vm.autoSwitchingUIKey,
|
||||
label: S.current.enable_for_auto_switching,
|
||||
value: vm.isEnabledForAutoSwitching,
|
||||
onChanged: (value) => vm.isEnabledForAutoSwitching = value),
|
||||
],
|
||||
},
|
||||
controllers: _controllers,
|
||||
getCheckboxValue: vm.getCheckboxValue,
|
||||
updateCheckboxValue: vm.updateCheckboxValue,
|
||||
tapHandlers: tapHandlers
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, VoidCallback> get tapHandlers => {
|
||||
'node_regular_with_drill_in_row_key': () => _showToast('Regular with drill-in row tapped'),
|
||||
'node_tall_row_key': () => _showToast('Tall row tapped'),
|
||||
'node_regular_with_trailing_row_key': () => _showToast('Regular with trailing row tapped'),
|
||||
'node_item_selector_row_key': () => _showToast('Item selector row tapped'),
|
||||
};
|
||||
|
||||
void _showToast(String msg) async {
|
||||
try {
|
||||
await Fluttertoast.showToast(
|
||||
msg: msg,
|
||||
backgroundColor: Color.fromRGBO(0, 0, 0, 0.85),
|
||||
);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
|
||||
void _setupReactions() {
|
||||
_bindController(() => vm.label, vm.nodeLabelUIKey);
|
||||
_bindController(() => vm.address, vm.nodeAddressUIKey);
|
||||
_bindController(() => vm.port, vm.nodePortUIKey);
|
||||
_bindController(() => vm.path, vm.nodePathUIKey);
|
||||
|
||||
if (vm.hasAuthCredentials) {
|
||||
_bindController(() => vm.login, vm.nodeUsernameUIKey);
|
||||
_bindController(() => vm.password, vm.nodePasswordUIKey);
|
||||
}
|
||||
|
||||
if (vm.hasPathSupport) {
|
||||
_bindController(() => vm.path, vm.nodePathUIKey);
|
||||
}
|
||||
|
||||
_bindController(
|
||||
() => vm.socksProxyAddress,
|
||||
vm.socksProxyAddressUIKey,
|
||||
);
|
||||
}
|
||||
|
||||
void _bindController<T>(
|
||||
T Function() observe,
|
||||
String uiKey,
|
||||
) {
|
||||
reaction<T>((_) => observe(), (value) {
|
||||
final controller = _controllers[uiKey];
|
||||
if (controller != null && controller.text != value.toString()) {
|
||||
controller.text = value.toString();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool validate() => _formKey.currentState?.validate() ?? false;
|
||||
}
|
||||
|
||||
@@ -21,24 +21,26 @@ class NodeListRow extends StandardListRow {
|
||||
final leading = buildLeading(context);
|
||||
final trailing = buildTrailing(context);
|
||||
return Container(
|
||||
height: 56,
|
||||
padding: EdgeInsets.only(left: 12, right: 12, top: 2, bottom: 2),
|
||||
padding: EdgeInsets.only(left: 18, right: 18, top: 2, bottom: 2),
|
||||
margin: EdgeInsets.only(top: 2, bottom: 2),
|
||||
child: FilledButton(
|
||||
onPressed: () => onTap?.call(context),
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).colorScheme.surfaceContainer,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||
borderRadius: BorderRadius.all(Radius.circular(20)),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
leading,
|
||||
buildCenter(context, hasLeftOffset: true),
|
||||
trailing,
|
||||
],
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
leading,
|
||||
buildCenter(context, hasLeftOffset: true),
|
||||
trailing,
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:cake_wallet/utils/device_info.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cake_wallet/utils/show_bar.dart';
|
||||
import 'package:another_flushbar/flushbar.dart';
|
||||
@@ -141,7 +142,7 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
|
||||
},
|
||||
child: Container(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
padding: EdgeInsets.only(left: 40.0, right: 40.0, bottom: 60.0),
|
||||
padding: EdgeInsets.only(left: 44.0, right: 44.0, bottom: 60.0),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Spacer(flex: 2),
|
||||
@@ -153,7 +154,7 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
Spacer(flex: 3),
|
||||
Spacer(flex: 2),
|
||||
Container(
|
||||
width: 180,
|
||||
child: Row(
|
||||
@@ -206,11 +207,11 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
|
||||
child: GridView.count(
|
||||
shrinkWrap: true,
|
||||
crossAxisCount: 3,
|
||||
childAspectRatio: _aspectRatio,
|
||||
childAspectRatio: DeviceInfo.instance.isDesktop ? _aspectRatio : 1.1,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
children: List.generate(12, (index) {
|
||||
const double marginRight = 8;
|
||||
const double marginLeft = 8;
|
||||
const double marginRight = 10;
|
||||
const double marginLeft = 10;
|
||||
|
||||
if (index == 9) {
|
||||
// Empty container
|
||||
@@ -228,7 +229,7 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
|
||||
button: true,
|
||||
onTap: () => _pop(),
|
||||
child: TextButton(
|
||||
onPressed: () => _pop(),
|
||||
onPressed: () => {_pop(), HapticFeedback.mediumImpact()},
|
||||
style: TextButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).colorScheme.surfaceContainer,
|
||||
shape: CircleBorder(),
|
||||
@@ -246,7 +247,7 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
|
||||
margin: EdgeInsets.only(left: marginLeft, right: marginRight),
|
||||
child: TextButton(
|
||||
key: ValueKey('pin_code_button_${index}_key'),
|
||||
onPressed: () => _push(index),
|
||||
onPressed: () => {_push(index), HapticFeedback.mediumImpact()},
|
||||
style: TextButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).colorScheme.surfaceContainer,
|
||||
shape: CircleBorder(),
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
|
||||
import 'package:cake_wallet/src/screens/receive/widgets/address_list.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||
|
||||
class AddressListPage extends BasePage {
|
||||
class AddressListPage extends StatelessWidget {
|
||||
AddressListPage(this.addressListViewModel);
|
||||
|
||||
final WalletAddressListViewModel addressListViewModel;
|
||||
@@ -14,18 +16,30 @@ class AddressListPage extends BasePage {
|
||||
String get title => S.current.accounts_subaddresses;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
AddressList(
|
||||
addressListViewModel: addressListViewModel,
|
||||
onSelect: (String address) async {
|
||||
Navigator.of(context).pop(address);
|
||||
},
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
ModalTopBar(
|
||||
title: S.of(context).accounts_subaddresses,
|
||||
leadingIcon: Icon(Icons.arrow_back_ios_new),
|
||||
onLeadingPressed: Navigator.of(context).pop,
|
||||
),
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
controller: ModalScrollController.of(context),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
AddressList(
|
||||
addressListViewModel: addressListViewModel,
|
||||
onSelect: (String address) async {
|
||||
Navigator.of(context).pop(address);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:cake_wallet/core/execution_state.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/entities/preferences_key.dart';
|
||||
import 'package:cake_wallet/src/screens/receive/anonpay_receive_page.dart';
|
||||
import 'package:cake_wallet/utils/feature_flag.dart';
|
||||
import 'package:cw_core/receive_page_option.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/present_receive_option_picker.dart';
|
||||
@@ -21,7 +22,7 @@ import 'package:cake_wallet/src/widgets/trail_button.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
@@ -53,10 +54,13 @@ class AnonPayInvoicePage extends BasePage {
|
||||
bool get extendBodyBehindAppBar => true;
|
||||
|
||||
@override
|
||||
AppBarStyle get appBarStyle => AppBarStyle.transparent;
|
||||
AppBarStyle get appBarStyle =>
|
||||
FeatureFlag.hasNewUiExtraPages ? AppBarStyle.completelyTransparent : AppBarStyle.transparent;
|
||||
|
||||
@override
|
||||
void onClose(BuildContext context) => Navigator.popUntil(context, (route) => route.isFirst);
|
||||
void onClose(BuildContext context) => FeatureFlag.hasNewUiExtraPages
|
||||
? Navigator.of(context, rootNavigator: true).pop()
|
||||
: Navigator.popUntil(context, (route) => route.isFirst);
|
||||
|
||||
@override
|
||||
Widget middle(BuildContext context) => PresentReceiveOptionPicker(
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/utils/share_util.dart';
|
||||
import 'package:cake_wallet/utils/show_bar.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
||||
class CopyLinkItem extends StatelessWidget {
|
||||
const CopyLinkItem({super.key, required this.url, required this.title});
|
||||
@@ -40,7 +40,7 @@ class CopyLinkItem extends StatelessWidget {
|
||||
highlightColor: Colors.transparent,
|
||||
splashColor: Colors.transparent,
|
||||
iconSize: 25,
|
||||
onPressed: () => Share.share(url),
|
||||
onPressed: () => ShareUtil.share(text: url, context: context),
|
||||
icon: Icon(
|
||||
Icons.share,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
|
||||
@@ -184,7 +184,7 @@ class CurrencyAmountTextField extends StatelessWidget {
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.deny(RegExp('[\\-|\\ ]')),
|
||||
],
|
||||
hintText: hintText ?? '0.0000',
|
||||
hintText: hintText ?? (selectedCurrencyDecimals == 0 ? '0' : '0.0000'),
|
||||
fillColor: fillColor,
|
||||
textStyle: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
fontSize: 16,
|
||||
@@ -198,8 +198,13 @@ class CurrencyAmountTextField extends StatelessWidget {
|
||||
),
|
||||
validator: isAmountEditable ? currencyValueValidator : null,
|
||||
onChanged: (value) {
|
||||
final sanitized =
|
||||
var sanitized =
|
||||
value.replaceAll(',', '.').withMaxDecimals(selectedCurrencyDecimals);
|
||||
|
||||
if (selectedCurrencyDecimals == 0) {
|
||||
sanitized = sanitized.replaceAll('.', '');
|
||||
}
|
||||
|
||||
if (sanitized != amountController.text) {
|
||||
// Update text while preserving a sane cursor position to avoid auto-selection
|
||||
amountController.value = amountController.value.copyWith(
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:cake_wallet/core/payment_uris.dart';
|
||||
import 'package:cake_wallet/entities/qr_view_data.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
@@ -15,6 +14,7 @@ import 'package:cake_wallet/utils/show_bar.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/payment_uris.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
@@ -108,6 +108,7 @@ class QRWidget extends StatelessWidget {
|
||||
),
|
||||
if (addressListViewModel.isPayjoinUnavailable &&
|
||||
!addressListViewModel.isSilentPayments &&
|
||||
!addressListViewModel.isLightning &&
|
||||
!addressListViewModel.isBitcoinViewOnly) ...[
|
||||
GestureDetector(
|
||||
onTap: () => _onPayjoinInactivePressed(context),
|
||||
@@ -155,8 +156,7 @@ class QRWidget extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
],
|
||||
if (addressListViewModel.payjoinEndpoint.isNotEmpty &&
|
||||
!addressListViewModel.isSilentPayments) ...[
|
||||
if (addressListViewModel.isPayjoinAvailable) ...[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
@@ -204,7 +204,7 @@ class QRWidget extends StatelessWidget {
|
||||
borderWidth: 0.0,
|
||||
selectedCurrency: _currencyName,
|
||||
selectedCurrencyDecimals:
|
||||
addressListViewModel.selectedCurrency.decimals,
|
||||
addressListViewModel.selectedCurrencyDecimals,
|
||||
amountFocusNode: amountTextFieldFocusNode,
|
||||
amountController: amountController,
|
||||
padding: EdgeInsets.only(top: 20, left: _width / 4),
|
||||
@@ -255,8 +255,7 @@ class QRWidget extends StatelessWidget {
|
||||
),
|
||||
Observer(
|
||||
builder: (_) => Offstage(
|
||||
offstage: addressListViewModel.payjoinEndpoint.isEmpty ||
|
||||
addressListViewModel.isSilentPayments,
|
||||
offstage: !addressListViewModel.isPayjoinAvailable,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(top: 12),
|
||||
child: PrimaryImageButton(
|
||||
@@ -283,6 +282,7 @@ class QRWidget extends StatelessWidget {
|
||||
|
||||
String get _currencyName {
|
||||
if (addressListViewModel.selectedCurrency is CryptoCurrency) {
|
||||
if (addressListViewModel.useSatoshi) return "SATS";
|
||||
return (addressListViewModel.selectedCurrency as CryptoCurrency).title.toUpperCase();
|
||||
}
|
||||
return addressListViewModel.selectedCurrency.name.toUpperCase();
|
||||
|
||||
@@ -1,66 +1,76 @@
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/main.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/keyboard_hide_overlay.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/view_model/rescan_view_model.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||
|
||||
class RescanPage extends BasePage {
|
||||
RescanPage(this._rescanViewModel)
|
||||
: _blockchainHeightWidgetKey = GlobalKey<BlockchainHeightState>();
|
||||
class RescanPage extends StatefulWidget {
|
||||
RescanPage(this._rescanViewModel);
|
||||
|
||||
@override
|
||||
String get title =>
|
||||
_rescanViewModel.isSilentPaymentsScan ? S.current.silent_payments_scanning : S.current.rescan;
|
||||
final GlobalKey<BlockchainHeightState> _blockchainHeightWidgetKey;
|
||||
final RescanViewModel _rescanViewModel;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
State<RescanPage> createState() => _RescanPageState();
|
||||
}
|
||||
|
||||
class _RescanPageState extends State<RescanPage> {
|
||||
final TextEditingController _heightController = TextEditingController();
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget child;
|
||||
if (_rescanViewModel.wallet.type != WalletType.decred) {
|
||||
if (widget._rescanViewModel.wallet.type != WalletType.decred) {
|
||||
child = Padding(
|
||||
padding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Observer(
|
||||
builder: (_) => BlockchainHeightWidget(
|
||||
key: _blockchainHeightWidgetKey,
|
||||
onHeightOrDateEntered: (value) => _rescanViewModel.isButtonEnabled = value,
|
||||
isSilentPaymentsScan: _rescanViewModel.isSilentPaymentsScan,
|
||||
isMwebScan: _rescanViewModel.isMwebScan,
|
||||
doSingleScan: _rescanViewModel.doSingleScan,
|
||||
hasDatePicker: !_rescanViewModel.isMwebScan, // disable date picker for mweb for now
|
||||
toggleSingleScan: () =>
|
||||
_rescanViewModel.doSingleScan = !_rescanViewModel.doSingleScan,
|
||||
walletType: _rescanViewModel.wallet.type,
|
||||
bitcoinMempoolAPIEnabled: _rescanViewModel.isBitcoinMempoolAPIEnabled,
|
||||
builder: (_) => SingleChildScrollView(
|
||||
controller: ModalScrollController.of(context),
|
||||
child: BlockchainHeightWidget(
|
||||
// key: _blockchainHeightWidgetKey,
|
||||
onHeightOrDateEntered: (value) => widget._rescanViewModel.isButtonEnabled = value,
|
||||
isSilentPaymentsScan: widget._rescanViewModel.isSilentPaymentsScan,
|
||||
isMwebScan: widget._rescanViewModel.isMwebScan,
|
||||
doSingleScan: widget._rescanViewModel.doSingleScan,
|
||||
hasDatePicker: !widget._rescanViewModel.isMwebScan,
|
||||
// disable date picker for mweb for now
|
||||
toggleSingleScan: () =>
|
||||
widget._rescanViewModel.doSingleScan = !widget._rescanViewModel.doSingleScan,
|
||||
walletType: widget._rescanViewModel.wallet.type,
|
||||
heightController: _heightController,
|
||||
bitcoinMempoolAPIEnabled: widget._rescanViewModel.isBitcoinMempoolAPIEnabled,
|
||||
),
|
||||
),
|
||||
),
|
||||
Observer(
|
||||
builder: (_) => LoadingPrimaryButton(
|
||||
isLoading: _rescanViewModel.state == RescanWalletState.rescaning,
|
||||
isLoading: widget._rescanViewModel.state == RescanWalletState.rescaning,
|
||||
text: S.of(context).rescan,
|
||||
onPressed: () async {
|
||||
if (_rescanViewModel.isSilentPaymentsScan) {
|
||||
if (widget._rescanViewModel.isSilentPaymentsScan) {
|
||||
return _toggleSilentPaymentsScanning(context);
|
||||
}
|
||||
|
||||
_rescanViewModel.rescanCurrentWallet(
|
||||
restoreHeight: _blockchainHeightWidgetKey.currentState!.height);
|
||||
widget._rescanViewModel.rescanCurrentWallet(
|
||||
restoreHeight: int.tryParse(_heightController.text) ?? 0);
|
||||
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
isDisabled: !_rescanViewModel.isButtonEnabled,
|
||||
isDisabled: !widget._rescanViewModel.isButtonEnabled,
|
||||
),
|
||||
)
|
||||
],
|
||||
@@ -76,10 +86,10 @@ class RescanPage extends BasePage {
|
||||
Spacer(),
|
||||
Observer(
|
||||
builder: (_) => LoadingPrimaryButton(
|
||||
isLoading: _rescanViewModel.state == RescanWalletState.rescaning,
|
||||
isLoading: widget._rescanViewModel.state == RescanWalletState.rescaning,
|
||||
text: S.of(context).rescan,
|
||||
onPressed: () async {
|
||||
await _rescanViewModel.rescanCurrentWallet(restoreHeight: 0);
|
||||
await widget._rescanViewModel.rescanCurrentWallet(restoreHeight: 0);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
@@ -93,18 +103,39 @@ class RescanPage extends BasePage {
|
||||
}
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () => FocusScope.of(context).unfocus(),
|
||||
child: child,
|
||||
// onTap: () => FocusScope.of(context).unfocus(),
|
||||
child: Material(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: KeyboardHideOverlay(
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
ModalTopBar(
|
||||
title: widget._rescanViewModel.isSilentPaymentsScan
|
||||
? S.current.silent_payments_scanning
|
||||
: S.current.rescan,
|
||||
leadingIcon: Icon(Icons.arrow_back_ios_new),
|
||||
onLeadingPressed: Navigator.of(context).pop),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
|
||||
child: child,
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _toggleSilentPaymentsScanning(BuildContext context) async {
|
||||
final height = _blockchainHeightWidgetKey.currentState!.height;
|
||||
final height = int.parse(_heightController.text);
|
||||
|
||||
Navigator.of(context).pop();
|
||||
|
||||
final needsToSwitch =
|
||||
await bitcoin!.getNodeIsElectrsSPEnabled(_rescanViewModel.wallet) == false;
|
||||
await bitcoin!.getNodeIsElectrsSPEnabled(widget._rescanViewModel.wallet) == false;
|
||||
|
||||
if (needsToSwitch) {
|
||||
return showPopUp<void>(
|
||||
@@ -117,13 +148,13 @@ class RescanPage extends BasePage {
|
||||
actionRightButton: () async {
|
||||
Navigator.of(_dialogContext).pop();
|
||||
|
||||
_rescanViewModel.rescanCurrentWallet(restoreHeight: height);
|
||||
widget._rescanViewModel.rescanCurrentWallet(restoreHeight: height);
|
||||
},
|
||||
actionLeftButton: () => Navigator.of(_dialogContext).pop(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_rescanViewModel.rescanCurrentWallet(restoreHeight: height);
|
||||
widget._rescanViewModel.rescanCurrentWallet(restoreHeight: height);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,9 +23,9 @@ class RestoreOptionsPage extends BasePage {
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) => _RestoreOptionsBody(
|
||||
isNewInstall: isNewInstall,
|
||||
themeType: currentTheme.type,
|
||||
);
|
||||
isNewInstall: isNewInstall,
|
||||
themeType: currentTheme.type,
|
||||
);
|
||||
}
|
||||
|
||||
class _RestoreOptionsBody extends StatefulWidget {
|
||||
@@ -93,7 +93,6 @@ class _RestoreOptionsBodyState extends State<_RestoreOptionsBody> {
|
||||
image: imageRestoreCupcake,
|
||||
title: S.of(context).restore_title_from_cupcake,
|
||||
description: S.of(context).restore_description_from_cupcake,
|
||||
tag: S.of(context).new_tag,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
@@ -144,21 +143,21 @@ class _RestoreOptionsBodyState extends State<_RestoreOptionsBody> {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (mounted) {
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.current.error,
|
||||
alertContent: error,
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop());
|
||||
});
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.current.error,
|
||||
alertContent: error,
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop());
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _onScanQRCode(BuildContext context) async {
|
||||
final isCameraPermissionGranted =
|
||||
await PermissionHandler.checkPermission(Permission.camera, context);
|
||||
await PermissionHandler.checkPermission(Permission.camera, context);
|
||||
|
||||
if (!isCameraPermissionGranted) return;
|
||||
try {
|
||||
@@ -181,4 +180,4 @@ class _RestoreOptionsBodyState extends State<_RestoreOptionsBody> {
|
||||
_showQRScanError(context, e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
import 'package:cake_wallet/core/wallet_name_validator.dart';
|
||||
import 'package:cake_wallet/entities/generate_name.dart';
|
||||
import 'package:cw_core/generate_name.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class WalletRestoreFromKeysForm extends StatefulWidget {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:cake_wallet/core/wallet_name_validator.dart';
|
||||
import 'package:cake_wallet/entities/generate_name.dart';
|
||||
import 'package:cw_core/generate_name.dart';
|
||||
import 'package:cake_wallet/entities/seed_type.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
@@ -171,7 +171,7 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
|
||||
icon: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(6.0),
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
width: 34,
|
||||
|
||||
@@ -490,12 +490,10 @@ class _WalletRestorePageBodyState extends State<_WalletRestorePageBody>
|
||||
controller: _tabController,
|
||||
children: [
|
||||
SingleChildScrollView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: _buildWalletRestoreFromSeedTab(),
|
||||
),
|
||||
if (_hasKeysTab)
|
||||
SingleChildScrollView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: _buildWalletRestoreFromKeysTab(),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -32,6 +32,7 @@ class SeedVerificationPage extends BasePage {
|
||||
walletSeedViewModel.verificationIndices.isEmpty
|
||||
? SeedVerificationSuccessView(
|
||||
key: ValueKey('seed_verification_success_view_page'),
|
||||
walletType: walletSeedViewModel.walletType,
|
||||
currentTheme: currentTheme,
|
||||
)
|
||||
: SeedVerificationStepView(
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/themes/core/material_base_theme.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SeedVerificationSuccessView extends StatelessWidget {
|
||||
const SeedVerificationSuccessView({required this.currentTheme, super.key});
|
||||
const SeedVerificationSuccessView(
|
||||
{required this.currentTheme, super.key, required this.walletType});
|
||||
|
||||
final MaterialThemeBase currentTheme;
|
||||
final WalletType walletType;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -18,7 +22,7 @@ class SeedVerificationSuccessView extends StatelessWidget {
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(maxHeight: MediaQuery.of(context).size.height * 0.3),
|
||||
child: CakeImageWidget(
|
||||
height: 200,
|
||||
height: 128,
|
||||
imageUrl: currentTheme.isDark
|
||||
? 'assets/images/seed_verified_dark.png'
|
||||
: 'assets/images/seed_verified_light.png',
|
||||
@@ -62,9 +66,13 @@ class SeedVerificationSuccessView extends StatelessWidget {
|
||||
PrimaryButton(
|
||||
key: ValueKey('wallet_seed_page_open_wallet_button_key'),
|
||||
onPressed: () {
|
||||
Navigator.of(context).popUntil((route) => route.isFirst);
|
||||
if (walletType == WalletType.bitcoin) {
|
||||
Navigator.of(context).pushNamed(Routes.lightningUsernamePage, arguments: true);
|
||||
} else {
|
||||
Navigator.of(context).popUntil((route) => route.isFirst);
|
||||
}
|
||||
},
|
||||
text: S.current.open_wallet,
|
||||
text: (walletType == WalletType.bitcoin) ? S.current.continue_text : S.current.open_wallet,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
),
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:cake_wallet/src/widgets/warning_box_widget.dart';
|
||||
import 'package:cake_wallet/utils/clipboard_util.dart';
|
||||
import 'package:cake_wallet/utils/share_util.dart';
|
||||
import 'package:cake_wallet/utils/show_bar.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@@ -20,7 +21,7 @@ class WalletSeedPage extends BasePage {
|
||||
WalletSeedPage(this.walletSeedViewModel, {required this.isNewWalletCreated});
|
||||
|
||||
@override
|
||||
String get title => '${walletSeedViewModel.walletType} ${S.current.seed_title}';
|
||||
String get title => '${walletTypeToString(walletSeedViewModel.walletType)} ${S.current.seed_title}';
|
||||
|
||||
final bool isNewWalletCreated;
|
||||
final WalletSeedViewModel walletSeedViewModel;
|
||||
@@ -57,7 +58,8 @@ class WalletSeedPage extends BasePage {
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return PopScope(
|
||||
canPop: false,
|
||||
//FIXME i can't figure out why this was here? it caused back gesture to break
|
||||
canPop: true,
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
child: Column(
|
||||
|
||||
@@ -2,7 +2,7 @@ import 'package:cake_wallet/core/selectable_option.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/provider_optoin_tile.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/themes/core/material_base_theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/core/address_validator.dart';
|
||||
import 'package:cake_wallet/core/auth_service.dart';
|
||||
import 'package:cake_wallet/entities/contact_record.dart';
|
||||
import 'package:cake_wallet/core/execution_state.dart';
|
||||
import 'package:cake_wallet/entities/contact_record.dart';
|
||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||
import 'package:cake_wallet/entities/template.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/connect_device/connect_device_page.dart';
|
||||
@@ -22,7 +22,7 @@ import 'package:cake_wallet/src/widgets/bottom_sheet/confirm_sending_bottom_shee
|
||||
import 'package:cake_wallet/src/widgets/bottom_sheet/info_bottom_sheet_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/simple_checkbox.dart';
|
||||
import 'package:cake_wallet/src/widgets/template_tile.dart';
|
||||
import 'package:cake_wallet/src/widgets/trail_button.dart';
|
||||
@@ -32,12 +32,12 @@ import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/payment/payment_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/send/output.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_switcher_view_model.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cake_wallet/view_model/send/send_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_switcher_view_model.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:keyboard_actions/keyboard_actions.dart';
|
||||
@@ -98,8 +98,7 @@ class SendPage extends BasePage {
|
||||
size: 16,
|
||||
);
|
||||
final _closeButton = currentTheme.isDark ? closeButtonImageDarkTheme : closeButtonImage;
|
||||
|
||||
bool isMobileView = responsiveLayoutUtil.shouldRenderMobileUI;
|
||||
final isMobileView = responsiveLayoutUtil.shouldRenderMobileUI;
|
||||
|
||||
return MergeSemantics(
|
||||
child: SizedBox(
|
||||
@@ -150,27 +149,25 @@ class SendPage extends BasePage {
|
||||
|
||||
@override
|
||||
Widget trailing(context) => Observer(
|
||||
builder: (_) {
|
||||
return sendViewModel.isBatchSending
|
||||
? TrailButton(
|
||||
caption: S.of(context).remove,
|
||||
onPressed: () {
|
||||
var pageToJump = (controller.page?.round() ?? 0) - 1;
|
||||
pageToJump = pageToJump > 0 ? pageToJump : 0;
|
||||
final output = _defineCurrentOutput();
|
||||
sendViewModel.removeOutput(output);
|
||||
controller.jumpToPage(pageToJump);
|
||||
},
|
||||
)
|
||||
: TrailButton(
|
||||
caption: S.of(context).clear,
|
||||
onPressed: () {
|
||||
final output = _defineCurrentOutput();
|
||||
_formKey.currentState?.reset();
|
||||
output.reset();
|
||||
},
|
||||
);
|
||||
},
|
||||
builder: (_) => sendViewModel.isBatchSending
|
||||
? TrailButton(
|
||||
caption: S.of(context).remove,
|
||||
onPressed: () {
|
||||
var pageToJump = (controller.page?.round() ?? 0) - 1;
|
||||
pageToJump = pageToJump > 0 ? pageToJump : 0;
|
||||
final output = _defineCurrentOutput();
|
||||
sendViewModel.removeOutput(output);
|
||||
controller.jumpToPage(pageToJump);
|
||||
},
|
||||
)
|
||||
: TrailButton(
|
||||
caption: S.of(context).clear,
|
||||
onPressed: () {
|
||||
final output = _defineCurrentOutput();
|
||||
_formKey.currentState?.reset();
|
||||
output.reset();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
@override
|
||||
@@ -183,7 +180,7 @@ class SendPage extends BasePage {
|
||||
return Observer(builder: (_) {
|
||||
List<Widget> sendCards = [];
|
||||
List<KeyboardActionsItem> keyboardActions = [];
|
||||
for (var output in sendViewModel.outputs) {
|
||||
for (final output in sendViewModel.outputs) {
|
||||
final isCurrent = sendViewModel.outputs.indexOf(output) == value;
|
||||
sendCards.add(
|
||||
SendCard(
|
||||
@@ -267,7 +264,7 @@ class SendPage extends BasePage {
|
||||
dotColor: Theme.of(context)
|
||||
.colorScheme
|
||||
.primary
|
||||
.withOpacity(0.4),
|
||||
.withAlpha(100),
|
||||
activeDotColor: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
@@ -418,12 +415,11 @@ class SendPage extends BasePage {
|
||||
return;
|
||||
}
|
||||
|
||||
final notValidItems = sendViewModel.outputs
|
||||
.where(
|
||||
(item) => item.address.isEmpty || item.cryptoAmount.isEmpty)
|
||||
.toList();
|
||||
final hasInvalidItems = sendViewModel.outputs.any((item) =>
|
||||
item.address.isEmpty ||
|
||||
(item.cryptoAmount.isEmpty && !item.sendAll));
|
||||
|
||||
if (notValidItems.isNotEmpty) {
|
||||
if (hasInvalidItems) {
|
||||
showErrorValidationAlert(context);
|
||||
return;
|
||||
}
|
||||
@@ -500,9 +496,7 @@ class SendPage extends BasePage {
|
||||
BuildContext? loadingBottomSheetContext;
|
||||
|
||||
void _setEffects(BuildContext context) {
|
||||
if (_effectsInstalled) {
|
||||
return;
|
||||
}
|
||||
if (_effectsInstalled) return;
|
||||
|
||||
if (sendViewModel.isElectrumWallet) {
|
||||
bitcoin!.updateFeeRates(sendViewModel.wallet);
|
||||
@@ -529,16 +523,14 @@ class SendPage extends BasePage {
|
||||
(_) {
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
key: ValueKey('send_page_send_failure_dialog_key'),
|
||||
buttonKey: ValueKey('send_page_send_failure_dialog_button_key'),
|
||||
alertTitle: S.of(context).error,
|
||||
alertContent: state.error,
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop(),
|
||||
);
|
||||
},
|
||||
builder: (context) => AlertWithOneAction(
|
||||
key: ValueKey('send_page_send_failure_dialog_key'),
|
||||
buttonKey: ValueKey('send_page_send_failure_dialog_button_key'),
|
||||
alertTitle: S.of(context).error,
|
||||
alertContent: state.error,
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop(),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -557,7 +549,7 @@ class SendPage extends BasePage {
|
||||
showModalBottomSheet<void>(
|
||||
context: context,
|
||||
isDismissible: false,
|
||||
builder: (BuildContext context) {
|
||||
builder: (context) {
|
||||
loadingBottomSheetContext = context;
|
||||
return LoadingBottomSheet(
|
||||
titleText: S.of(context).generating_transaction,
|
||||
@@ -577,34 +569,37 @@ class SendPage extends BasePage {
|
||||
isScrollControlled: true,
|
||||
builder: (BuildContext bottomSheetContext) {
|
||||
return Observer(
|
||||
builder: (_) => ConfirmSendingBottomSheet(
|
||||
key: ValueKey('send_page_confirm_sending_bottom_sheet_key'),
|
||||
titleText: S.of(bottomSheetContext).confirm_transaction,
|
||||
accessibleNavigationModeSlideActionButtonText:
|
||||
S.of(bottomSheetContext).send,
|
||||
footerType: FooterType.slideActionButton,
|
||||
isSlideActionEnabled: sendViewModel.isReadyForSend,
|
||||
walletType: sendViewModel.walletType,
|
||||
titleIconPath: sendViewModel.selectedCryptoCurrency.iconPath,
|
||||
currency: sendViewModel.selectedCryptoCurrency,
|
||||
amount: S.of(bottomSheetContext).send_amount,
|
||||
amountValue: sendViewModel.pendingTransaction!.amountFormatted,
|
||||
fiatAmountValue: sendViewModel.pendingTransactionFiatAmountFormatted,
|
||||
fee: isEVMCompatibleChain(sendViewModel.walletType)
|
||||
? S.of(bottomSheetContext).send_estimated_fee
|
||||
: S.of(bottomSheetContext).send_fee,
|
||||
feeValue: sendViewModel.pendingTransaction!.feeFormatted,
|
||||
feeFiatAmount:
|
||||
sendViewModel.pendingTransactionFeeFiatAmountFormatted,
|
||||
outputs: sendViewModel.outputs,
|
||||
onSlideActionComplete: () async {
|
||||
Navigator.of(bottomSheetContext).pop(true);
|
||||
sendViewModel.commitTransaction(context);
|
||||
},
|
||||
change: sendViewModel.pendingTransaction!.change,
|
||||
isOpenCryptoPay: sendViewModel.ocpRequest != null,
|
||||
),
|
||||
);
|
||||
builder: (_) => ConfirmSendingBottomSheet(
|
||||
key: ValueKey('send_page_confirm_sending_bottom_sheet_key'),
|
||||
titleText: S.of(bottomSheetContext).confirm_transaction,
|
||||
accessibleNavigationModeSlideActionButtonText: S.of(bottomSheetContext).send,
|
||||
footerType: FooterType.slideActionButton,
|
||||
isSlideActionEnabled: sendViewModel.isReadyForSend,
|
||||
walletType: sendViewModel.walletType,
|
||||
titleIconPath: sendViewModel.selectedCryptoCurrency.iconPath,
|
||||
currency: sendViewModel.selectedCryptoCurrency,
|
||||
amount: S.of(bottomSheetContext).send_amount,
|
||||
amountValue: sendViewModel.amountParsingProxy.getDisplayCryptoAmount(
|
||||
sendViewModel.pendingTransaction!.amountFormatted,
|
||||
sendViewModel.selectedCryptoCurrency),
|
||||
fiatAmountValue: sendViewModel.pendingTransactionFiatAmountFormatted,
|
||||
fee: isEVMCompatibleChain(sendViewModel.walletType)
|
||||
? S.of(bottomSheetContext).send_estimated_fee
|
||||
: S.of(bottomSheetContext).send_fee,
|
||||
feeValue: "${sendViewModel.amountParsingProxy.getDisplayCryptoAmount(
|
||||
sendViewModel.pendingTransaction!.feeFormattedValue,
|
||||
sendViewModel.selectedCryptoCurrency)} ${sendViewModel.amountParsingProxy
|
||||
.getCryptoSymbol(sendViewModel.wallet.currency)}",
|
||||
feeFiatAmount: sendViewModel.pendingTransactionFeeFiatAmountFormatted,
|
||||
outputs: sendViewModel.outputs,
|
||||
onSlideActionComplete: () async {
|
||||
Navigator.of(bottomSheetContext).pop(true);
|
||||
sendViewModel.commitTransaction(context);
|
||||
},
|
||||
change: sendViewModel.pendingTransaction!.change,
|
||||
isOpenCryptoPay: sendViewModel.ocpRequest != null,
|
||||
amountParsingProxy: sendViewModel.amountParsingProxy,
|
||||
),);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -615,9 +610,7 @@ class SendPage extends BasePage {
|
||||
|
||||
if (state is TransactionCommitted) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
if (!context.mounted) {
|
||||
return;
|
||||
}
|
||||
if (!context.mounted) return;
|
||||
|
||||
newContactAddress = newContactAddress ?? sendViewModel.newContactAddress();
|
||||
|
||||
@@ -788,37 +781,34 @@ class SendPage extends BasePage {
|
||||
}
|
||||
|
||||
Output _defineCurrentOutput() {
|
||||
if (controller.page == null) {
|
||||
throw Exception('Controller page is null');
|
||||
}
|
||||
if (controller.page == null) throw Exception('Controller page is null');
|
||||
final itemCount = controller.page!.round();
|
||||
return sendViewModel.outputs[itemCount];
|
||||
}
|
||||
|
||||
void showErrorValidationAlert(BuildContext context) async {
|
||||
await showPopUp<void>(
|
||||
void showErrorValidationAlert(BuildContext context) => showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.of(context).error,
|
||||
alertContent: 'Please, check receiver forms',
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop());
|
||||
});
|
||||
}
|
||||
builder: (context) => AlertWithOneAction(
|
||||
alertTitle: S.of(context).error,
|
||||
alertContent: 'Please, check receiver forms',
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop(),
|
||||
),
|
||||
);
|
||||
|
||||
bool isRegularElectrumAddress(String address) {
|
||||
final supportedTypes = [CryptoCurrency.btc, CryptoCurrency.ltc, CryptoCurrency.bch];
|
||||
final excludedPatterns = [
|
||||
RegExp(AddressValidator.silentPaymentAddressPatternMainnet),
|
||||
RegExp(AddressValidator.silentPaymentAddressPatternTestnet),
|
||||
RegExp(AddressValidator.mWebAddressPattern)
|
||||
RegExp(AddressValidator.mWebAddressPattern),
|
||||
RegExp(AddressValidator.bolt11InvoiceMatcher),
|
||||
];
|
||||
|
||||
final trimmed = address.trim();
|
||||
|
||||
bool isValid = false;
|
||||
for (var type in supportedTypes) {
|
||||
for (final type in supportedTypes) {
|
||||
final addressPattern = AddressValidator.getAddressFromStringPattern(type);
|
||||
if (addressPattern != null) {
|
||||
final regex = RegExp('^$addressPattern\$');
|
||||
@@ -829,23 +819,16 @@ class SendPage extends BasePage {
|
||||
}
|
||||
}
|
||||
|
||||
for (var pattern in excludedPatterns) {
|
||||
if (pattern.hasMatch(trimmed)) {
|
||||
return false;
|
||||
}
|
||||
for (final pattern in excludedPatterns) {
|
||||
if (pattern.hasMatch(trimmed)) return false;
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
String _sendButtonText(BuildContext context) {
|
||||
if (!sendViewModel.isReadyForSend) {
|
||||
return S.of(context).synchronizing;
|
||||
}
|
||||
if (sendViewModel.payjoinUri != null) {
|
||||
return S.of(context).send_payjoin;
|
||||
} else {
|
||||
return S.of(context).send;
|
||||
}
|
||||
if (!sendViewModel.isReadyForSend) return S.of(context).synchronizing;
|
||||
if (sendViewModel.payjoinUri != null) return S.of(context).send_payjoin;
|
||||
return S.of(context).send;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/view_model/send/send_template_view_model.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/screens/send/widgets/send_template_card.dart';
|
||||
import 'package:smooth_page_indicator/smooth_page_indicator.dart';
|
||||
|
||||
|
||||
@@ -6,9 +6,9 @@ import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'choose_yat_address_alert.dart';
|
||||
|
||||
Future<String> extractAddressFromParsed(
|
||||
BuildContext context,
|
||||
BuildContext? context,
|
||||
ParsedAddress parsedAddress) async {
|
||||
if (!context.mounted) return parsedAddress.addresses.first;
|
||||
if (context == null || !context.mounted) return parsedAddress.addresses.first;
|
||||
|
||||
var title = '';
|
||||
var content = '';
|
||||
@@ -83,6 +83,11 @@ Future<String> extractAddressFromParsed(
|
||||
content = S.of(context).extracted_address_content('${parsedAddress.name} (BIP-353)');
|
||||
address = parsedAddress.addresses.first;
|
||||
break;
|
||||
case ParseFrom.lnurlpay:
|
||||
title = S.of(context).address_detected;
|
||||
content = S.of(context).extracted_address_content('${parsedAddress.name} (Lightning)');
|
||||
address = parsedAddress.addresses.first;
|
||||
break;
|
||||
case ParseFrom.yatRecord:
|
||||
if (parsedAddress.name.isEmpty) {
|
||||
title = S.of(context).yat_error;
|
||||
|
||||
@@ -155,6 +155,11 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||
Future<void> _handlePaymentFlow(String uri, PaymentRequest paymentRequest) async {
|
||||
if (uri.contains('@') || paymentRequest.address.contains('@')) return;
|
||||
|
||||
if (OpenCryptoPayService.isOpenCryptoPayQR(uri)) {
|
||||
sendViewModel.createOpenCryptoPayTransaction(uri);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final result = await paymentViewModel.processAddress(uri);
|
||||
|
||||
@@ -588,15 +593,14 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||
focusNode: addressFocusNode,
|
||||
controller: addressController,
|
||||
onURIScanned: (uri) async {
|
||||
if (OpenCryptoPayService.isOpenCryptoPayQR(uri.toString())) {
|
||||
sendViewModel.createOpenCryptoPayTransaction(uri.toString());
|
||||
} else {
|
||||
// Process the payment through the new flow
|
||||
await _handlePaymentFlow(
|
||||
uri.toString(),
|
||||
PaymentRequest.fromUri(uri),
|
||||
);
|
||||
}
|
||||
output.resetParsedAddress();
|
||||
await output.fetchParsedAddress(context);
|
||||
|
||||
// Process the payment through the new flow
|
||||
await _handlePaymentFlow(
|
||||
uri.toString(),
|
||||
PaymentRequest.fromUri(uri),
|
||||
);
|
||||
},
|
||||
options: [
|
||||
AddressTextFieldOption.paste,
|
||||
@@ -670,8 +674,8 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||
sendAllButtonKey: ValueKey('send_page_send_all_button_key'),
|
||||
currencyAmountTextFieldWidgetKey:
|
||||
ValueKey('send_page_crypto_currency_amount_textfield_widget_key'),
|
||||
selectedCurrency: sendViewModel.selectedCryptoCurrency.title,
|
||||
selectedCurrencyDecimals: sendViewModel.selectedCryptoCurrency.decimals,
|
||||
selectedCurrency: output.useSatoshi ? "SATS" : sendViewModel.selectedCryptoCurrency.title,
|
||||
selectedCurrencyDecimals: output.useSatoshi ? 0 : sendViewModel.selectedCryptoCurrency.decimals,
|
||||
amountFocusNode: widget.cryptoAmountFocus,
|
||||
amountController: cryptoAmountController,
|
||||
isAmountEditable: true,
|
||||
@@ -774,7 +778,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||
),
|
||||
),
|
||||
),
|
||||
if (sendViewModel.feesViewModel.hasFees)
|
||||
if (sendViewModel.hasFees)
|
||||
Observer(
|
||||
builder: (_) => GestureDetector(
|
||||
key: ValueKey('send_page_select_fee_priority_button_key'),
|
||||
@@ -802,9 +806,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
output.estimatedFee.toString() +
|
||||
' ' +
|
||||
sendViewModel.currency.toString(),
|
||||
'${output.estimatedFee} ${sendViewModel.currencySymbol}',
|
||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
@@ -814,9 +816,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||
child: sendViewModel.isFiatDisabled
|
||||
? const SizedBox(height: 14)
|
||||
: Text(
|
||||
output.estimatedFeeFiatAmount +
|
||||
' ' +
|
||||
sendViewModel.fiat.title,
|
||||
'${output.estimatedFeeFiatAmount} ${sendViewModel.fiat.title}',
|
||||
style:
|
||||
Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
@@ -880,7 +880,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||
),
|
||||
),
|
||||
),
|
||||
if (sendViewModel.currency == CryptoCurrency.ltc && sendViewModel.isMwebEnabled)
|
||||
if (sendViewModel.isMwebAvailable)
|
||||
Observer(
|
||||
builder: (_) => Padding(
|
||||
padding: EdgeInsets.only(top: 14),
|
||||
@@ -942,8 +942,14 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||
output.sendAll = false;
|
||||
}
|
||||
|
||||
if (amount != output.cryptoAmount) {
|
||||
output.setCryptoAmount(amount);
|
||||
if (S.current.all.contains(amount)) return;
|
||||
|
||||
final cAmount = sendViewModel.amountParsingProxy
|
||||
.getDisplayCryptoAmount(output.cryptoAmount, sendViewModel.selectedCryptoCurrency);
|
||||
if (amount != cAmount) {
|
||||
final newAmount = sendViewModel.amountParsingProxy
|
||||
.getCanonicalCryptoAmount(amount, sendViewModel.selectedCryptoCurrency);
|
||||
output.setCryptoAmount(newAmount);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -973,7 +979,8 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||
output.setSendAll(await sendViewModel.sendingBalance);
|
||||
}
|
||||
|
||||
output.setCryptoAmount(cryptoAmountController.text);
|
||||
output.setCryptoAmount(sendViewModel.amountParsingProxy
|
||||
.getCanonicalCryptoAmount(cryptoAmountController.text, sendViewModel.selectedCryptoCurrency));
|
||||
});
|
||||
|
||||
reaction((_) => output.fiatAmount, (String amount) {
|
||||
@@ -987,8 +994,11 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||
output.sendAll = false;
|
||||
}
|
||||
|
||||
if (amount != cryptoAmountController.text) {
|
||||
cryptoAmountController.text = amount;
|
||||
final cryptoAmount = sendViewModel.amountParsingProxy
|
||||
.getCanonicalCryptoAmount(cryptoAmountController.text, sendViewModel.selectedCryptoCurrency);
|
||||
if (amount != cryptoAmount) {
|
||||
cryptoAmountController.text = sendViewModel.amountParsingProxy
|
||||
.getDisplayCryptoAmount(amount, sendViewModel.selectedCryptoCurrency);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1004,6 +1014,10 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||
if (output.address != address) {
|
||||
output.resetParsedAddress();
|
||||
output.address = address;
|
||||
|
||||
if (SendViewModelBase.isNonZeroAmountLightningInvoice(address)) {
|
||||
sendViewModel.createTransaction();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,178 +1,199 @@
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/entities/bitcoin_amount_display_mode.dart';
|
||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||
import 'package:cake_wallet/entities/language_service.dart';
|
||||
import 'package:cake_wallet/entities/sync_status_display_mode.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_choices_cell.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_theme_choice.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/utils/device_info.dart';
|
||||
import 'package:cake_wallet/utils/feature_flag.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/settings/choices_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||
|
||||
class DisplaySettingsPage extends BasePage {
|
||||
class DisplaySettingsPage extends StatelessWidget {
|
||||
DisplaySettingsPage(this._displaySettingsViewModel);
|
||||
|
||||
@override
|
||||
String get title => S.current.display_settings;
|
||||
|
||||
final DisplaySettingsViewModel _displaySettingsViewModel;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return SingleChildScrollView(
|
||||
child: Observer(builder: (_) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SettingsSwitcherCell(
|
||||
title: S.of(context).apps,
|
||||
value: _displaySettingsViewModel.shouldShowMarketPlaceInDashboard,
|
||||
onValueChange: (_, bool value) {
|
||||
_displaySettingsViewModel.setShouldShowMarketPlaceInDashbaord(value);
|
||||
},
|
||||
),
|
||||
SettingsSwitcherCell(
|
||||
title: S.of(context).show_address_book_popup,
|
||||
value: _displaySettingsViewModel.showAddressBookPopup,
|
||||
onValueChange: (_, bool value) {
|
||||
_displaySettingsViewModel.setShowAddressBookPopup(value);
|
||||
},
|
||||
),
|
||||
if (getIt.get<AppStore>().wallet?.type == WalletType.zcash)
|
||||
SettingsSwitcherCell(
|
||||
title: S.of(context).show_zcash_card,
|
||||
value: _displaySettingsViewModel.showZcashCard,
|
||||
onValueChange: (_, bool value) {
|
||||
_displaySettingsViewModel.setShowZcashCard(value);
|
||||
},
|
||||
),
|
||||
SettingsPickerCell<SyncStatusDisplayMode>(
|
||||
title: S.current.sync_status_display_mode,
|
||||
items: SyncStatusDisplayMode.values.toList(),
|
||||
selectedItem: _displaySettingsViewModel.syncStatusDisplayMode,
|
||||
onItemSelected: (SyncStatusDisplayMode mode) =>
|
||||
_displaySettingsViewModel.setSyncStatusDisplayMode(mode),
|
||||
displayItem: (SyncStatusDisplayMode mode) => mode.title,
|
||||
isGridView: false,
|
||||
),
|
||||
//if (!isHaven) it does not work correctly
|
||||
if (!_displaySettingsViewModel.disabledFiatApiMode)
|
||||
SettingsPickerCell<FiatCurrency>(
|
||||
title: S.of(context).settings_currency,
|
||||
searchHintText: S.of(context).search_currency,
|
||||
items: FiatCurrency.all,
|
||||
selectedItem: _displaySettingsViewModel.fiatCurrency,
|
||||
onItemSelected: (FiatCurrency currency) =>
|
||||
_displaySettingsViewModel.setFiatCurrency(currency),
|
||||
images: FiatCurrency.all
|
||||
.map((e) => Image.asset("assets/images/flags/${e.countryCode}.png"))
|
||||
.toList(),
|
||||
isGridView: true,
|
||||
matchingCriteria: (FiatCurrency currency, String searchText) {
|
||||
return currency.title.toLowerCase().contains(searchText) ||
|
||||
currency.fullName.toLowerCase().contains(searchText);
|
||||
},
|
||||
),
|
||||
SettingsPickerCell<String>(
|
||||
title: S.of(context).settings_change_language,
|
||||
searchHintText: S.of(context).search_language,
|
||||
items: LanguageService.list.keys.toList(),
|
||||
displayItem: (dynamic code) {
|
||||
return LanguageService.list[code] ?? '';
|
||||
},
|
||||
selectedItem: _displaySettingsViewModel.languageCode,
|
||||
onItemSelected: _displaySettingsViewModel.onLanguageSelected,
|
||||
images: LanguageService.list.keys
|
||||
.map((e) => Image.asset(
|
||||
"assets/images/flags/${LanguageService.localeCountryCode[e]}.png"))
|
||||
.toList(),
|
||||
matchingCriteria: (String code, String searchText) {
|
||||
return LanguageService.list[code]?.toLowerCase().contains(searchText) ?? false;
|
||||
},
|
||||
),
|
||||
|
||||
if (FeatureFlag.customBackgroundEnabled)
|
||||
StandardListRow(
|
||||
title: "Custom background",
|
||||
isSelected: false,
|
||||
onTap: (_) => _pickImage(context),
|
||||
),
|
||||
|
||||
if (responsiveLayoutUtil.shouldRenderMobileUI && DeviceInfo.instance.isMobile) ...[
|
||||
SizedBox(height: 24),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 24),
|
||||
child: Text(
|
||||
S.current.appearance,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 14,
|
||||
height: 22 / 14,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 20),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
SettingsChoicesCell(
|
||||
ChoicesListItem<ThemeMode>(
|
||||
title: "",
|
||||
items: ThemeMode.values,
|
||||
selectedItem: _displaySettingsViewModel.themeMode,
|
||||
onItemSelected: (ThemeMode themeMode) =>
|
||||
_displaySettingsViewModel.setThemeMode(themeMode),
|
||||
displayItem: (ThemeMode themeMode) {
|
||||
return themeMode.name[0].toUpperCase() +
|
||||
themeMode.name.substring(1).toLowerCase();
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: SafeArea(
|
||||
top: false,
|
||||
child: Column(
|
||||
children: [
|
||||
ModalTopBar(title: S.of(context).display_settings,leadingIcon: Icon(Icons.arrow_back_ios_new),onLeadingPressed: () => Navigator.of(context).pop()),
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
controller: ModalScrollController.of(context),
|
||||
physics: ClampingScrollPhysics(),
|
||||
child: Observer(builder: (_) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SettingsSwitcherCell(
|
||||
title: S.of(context).apps,
|
||||
value: _displaySettingsViewModel.shouldShowMarketPlaceInDashboard,
|
||||
onValueChange: (_, bool value) {
|
||||
_displaySettingsViewModel.setShouldShowMarketPlaceInDashbaord(value);
|
||||
},
|
||||
),
|
||||
useGenericColor: false,
|
||||
padding: EdgeInsets.all(12),
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
SettingsSwitcherCell(
|
||||
title: S.of(context).show_address_book_popup,
|
||||
value: _displaySettingsViewModel.showAddressBookPopup,
|
||||
onValueChange: (_, bool value) {
|
||||
_displaySettingsViewModel.setShowAddressBookPopup(value);
|
||||
},
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Semantics(
|
||||
label: S.of(context).color_theme,
|
||||
child: SettingsThemeChoicesCell(_displaySettingsViewModel),
|
||||
if (_displaySettingsViewModel.showZcashCardSetting)
|
||||
SettingsSwitcherCell(
|
||||
title: S.of(context).show_zcash_card,
|
||||
value: _displaySettingsViewModel.showZcashCard,
|
||||
onValueChange: (_, bool value) {
|
||||
_displaySettingsViewModel.setShowZcashCard(value);
|
||||
},
|
||||
),
|
||||
SettingsPickerCell<SyncStatusDisplayMode>(
|
||||
title: S.current.sync_status_display_mode,
|
||||
items: SyncStatusDisplayMode.values.toList(),
|
||||
selectedItem: _displaySettingsViewModel.syncStatusDisplayMode,
|
||||
onItemSelected: (SyncStatusDisplayMode mode) =>
|
||||
_displaySettingsViewModel.setSyncStatusDisplayMode(mode),
|
||||
displayItem: (SyncStatusDisplayMode mode) => mode.title,
|
||||
isGridView: false,
|
||||
),
|
||||
if (_displaySettingsViewModel.showDisplayAmountsInSatoshiSetting)
|
||||
SettingsPickerCell<BitcoinAmountDisplayMode>(
|
||||
title: S.of(context).bitcoin_amount_display,
|
||||
items: BitcoinAmountDisplayMode.all,
|
||||
selectedItem: _displaySettingsViewModel.displayAmountsInSatoshi,
|
||||
onItemSelected: _displaySettingsViewModel.setDisplayAmountsInSatoshi,
|
||||
displayItem: (mode) => mode.title,
|
||||
isGridView: false,
|
||||
),
|
||||
//if (!isHaven) it does not work correctly
|
||||
if (!_displaySettingsViewModel.disabledFiatApiMode)
|
||||
SettingsPickerCell<FiatCurrency>(
|
||||
title: S.of(context).settings_currency,
|
||||
searchHintText: S.of(context).search_currency,
|
||||
items: FiatCurrency.all,
|
||||
selectedItem: _displaySettingsViewModel.fiatCurrency,
|
||||
onItemSelected: (FiatCurrency currency) =>
|
||||
_displaySettingsViewModel.setFiatCurrency(currency),
|
||||
images: FiatCurrency.all
|
||||
.map((e) => Image.asset("assets/images/flags/${e.countryCode}.png"))
|
||||
.toList(),
|
||||
isGridView: true,
|
||||
matchingCriteria: (FiatCurrency currency, String searchText) {
|
||||
return currency.title.toLowerCase().contains(searchText) ||
|
||||
currency.fullName.toLowerCase().contains(searchText);
|
||||
},
|
||||
),
|
||||
SettingsPickerCell<String>(
|
||||
title: S.of(context).settings_change_language,
|
||||
searchHintText: S.of(context).search_language,
|
||||
items: LanguageService.list.keys.toList(),
|
||||
displayItem: (dynamic code) {
|
||||
return LanguageService.list[code] ?? '';
|
||||
},
|
||||
selectedItem: _displaySettingsViewModel.languageCode,
|
||||
onItemSelected: _displaySettingsViewModel.onLanguageSelected,
|
||||
images: LanguageService.list.keys
|
||||
.map((e) => Image.asset(
|
||||
"assets/images/flags/${LanguageService.localeCountryCode[e]}.png"))
|
||||
.toList(),
|
||||
matchingCriteria: (String code, String searchText) {
|
||||
return LanguageService.list[code]?.toLowerCase().contains(searchText) ?? false;
|
||||
},
|
||||
),
|
||||
|
||||
if (FeatureFlag.customBackgroundEnabled)
|
||||
StandardListRow(
|
||||
title: "Custom background",
|
||||
isSelected: false,
|
||||
onTap: (_) => _pickImage(context),
|
||||
),
|
||||
|
||||
if (responsiveLayoutUtil.shouldRenderMobileUI && DeviceInfo.instance.isMobile) ...[
|
||||
SizedBox(height: 24),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 24),
|
||||
child: Text(
|
||||
S.current.appearance,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 14,
|
||||
height: 22 / 14,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 20),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
SettingsChoicesCell(
|
||||
ChoicesListItem<ThemeMode>(
|
||||
title: "",
|
||||
items: ThemeMode.values,
|
||||
selectedItem: _displaySettingsViewModel.themeMode,
|
||||
onItemSelected: (ThemeMode themeMode) =>
|
||||
_displaySettingsViewModel.setThemeMode(themeMode),
|
||||
displayItem: (ThemeMode themeMode) {
|
||||
return themeMode.name[0].toUpperCase() +
|
||||
themeMode.name.substring(1).toLowerCase();
|
||||
},
|
||||
),
|
||||
useGenericColor: false,
|
||||
padding: EdgeInsets.all(12),
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Semantics(
|
||||
label: S.of(context).color_theme,
|
||||
child: SettingsThemeChoicesCell(_displaySettingsViewModel),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/node_list/pow_node_list_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||
|
||||
class ManageNodesPage extends BasePage {
|
||||
ManageNodesPage(this.isPow, {this.nodeListViewModel, this.powNodeListViewModel})
|
||||
@@ -60,6 +61,7 @@ class ManageNodesPage extends BasePage {
|
||||
nodeListViewModel?.nodes.length ?? powNodeListViewModel!.nodes.length;
|
||||
return Flexible(
|
||||
child: SectionStandardList(
|
||||
scrollController: ModalScrollController.of(context),
|
||||
sectionCount: 1,
|
||||
dividerPadding: EdgeInsets.symmetric(horizontal: 24),
|
||||
itemCounter: (int sectionIndex) => itemsCount,
|
||||
|
||||
@@ -23,13 +23,13 @@ class MwebSettingsPage extends BasePage {
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: Column(
|
||||
children: [
|
||||
SettingsSwitcherCell(
|
||||
title: S.current.litecoin_mweb_display_card,
|
||||
value: _mwebSettingsViewModel.mwebCardDisplay,
|
||||
onValueChange: (_, bool value) {
|
||||
_mwebSettingsViewModel.setMwebCardDisplay(value);
|
||||
},
|
||||
),
|
||||
// SettingsSwitcherCell(
|
||||
// title: S.current.litecoin_mweb_display,
|
||||
// value: _mwebSettingsViewModel.mwebCardDisplay,
|
||||
// onValueChange: (_, bool value) {
|
||||
// _mwebSettingsViewModel.setMwebCardDisplay(value);
|
||||
// },
|
||||
// ),
|
||||
SettingsSwitcherCell(
|
||||
title: S.current.litecoin_mweb_enable,
|
||||
value: _mwebSettingsViewModel.mwebEnabled,
|
||||
|
||||
@@ -9,17 +9,13 @@ import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arro
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_version_cell.dart';
|
||||
import 'package:cake_wallet/utils/feature_flag.dart';
|
||||
import 'package:cake_wallet/utils/share_util.dart';
|
||||
import 'package:cake_wallet/view_model/settings/other_settings_view_model.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:cw_core/pathForWallet.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cw_core/db/sqlite.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
class OtherSettingsPage extends BasePage {
|
||||
OtherSettingsPage(this._otherSettingsViewModel) {
|
||||
@@ -84,6 +80,16 @@ class OtherSettingsPage extends BasePage {
|
||||
},
|
||||
}),
|
||||
),
|
||||
if (_otherSettingsViewModel.walletType == WalletType.bitcoin) ...[
|
||||
SettingsCellWithArrow(
|
||||
title: S.of(context).export_lightning_logs,
|
||||
handler: onExportLNLog,
|
||||
),
|
||||
SettingsCellWithArrow(
|
||||
title: S.of(context).export_payjoin_logs,
|
||||
handler: onExportPJLog,
|
||||
),
|
||||
],
|
||||
SettingsCellWithArrow(
|
||||
title: S.current.settings_terms_and_conditions,
|
||||
handler: (context) => Navigator.of(context).pushNamed(Routes.readDisclaimer),
|
||||
@@ -176,4 +182,20 @@ class OtherSettingsPage extends BasePage {
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
Future<void> onExportLNLog(BuildContext context) async {
|
||||
final file = await _otherSettingsViewModel.getLightningLog();
|
||||
|
||||
if (file != null) {
|
||||
await ShareUtil.shareFile(filePath: file.path, fileName: "Lightning.log", context: context);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> onExportPJLog(BuildContext context) async {
|
||||
final file = await _otherSettingsViewModel.getPayjoinLog();
|
||||
|
||||
if (file != null) {
|
||||
await ShareUtil.shareFile(filePath: file.path, fileName: "Payjoin.log", context: context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,12 @@ class PrivacyPage extends BasePage {
|
||||
_privacySettingsViewModel.setExchangeApiMode(mode),
|
||||
),
|
||||
),
|
||||
if (_privacySettingsViewModel.canUseLightning)
|
||||
SettingsSwitcherCell(
|
||||
title: S.of(context).enable_lightning,
|
||||
value: _privacySettingsViewModel.useLightning,
|
||||
onValueChange: (_, value) => _privacySettingsViewModel.setUseLightning(value),
|
||||
),
|
||||
if (_privacySettingsViewModel.canUsePayjoin)
|
||||
SettingsSwitcherCell(
|
||||
title: S.of(context).use_payjoin,
|
||||
@@ -95,13 +101,13 @@ class PrivacyPage extends BasePage {
|
||||
_privacySettingsViewModel.setDisableAutomaticExchangeStatusUpdates(value);
|
||||
},
|
||||
),
|
||||
SettingsSwitcherCell(
|
||||
title: S.current.disable_bulletin,
|
||||
value: _privacySettingsViewModel.disableBulletin,
|
||||
onValueChange: (BuildContext _, bool value) {
|
||||
_privacySettingsViewModel.setDisableBulletin(value);
|
||||
},
|
||||
),
|
||||
// SettingsSwitcherCell(
|
||||
// title: S.current.disable_bulletin,
|
||||
// value: _privacySettingsViewModel.disableBulletin,
|
||||
// onValueChange: (BuildContext _, bool value) {
|
||||
// _privacySettingsViewModel.setDisableBulletin(value);
|
||||
// },
|
||||
// ),
|
||||
if (_privacySettingsViewModel.canUseBlinkProtection)
|
||||
SettingsSwitcherCell(
|
||||
title: S.current.use_blink_protection,
|
||||
@@ -189,10 +195,15 @@ class PrivacyPage extends BasePage {
|
||||
title: S.current.domain_looks_up,
|
||||
handler: (context) => Navigator.of(context).pushNamed(Routes.domainLookupsPage),
|
||||
),
|
||||
SettingsCellWithArrow(
|
||||
title: 'Trocador providers',
|
||||
handler: (context) => Navigator.of(context).pushNamed(Routes.trocadorProvidersPage),
|
||||
),
|
||||
if(_privacySettingsViewModel.hasCoinControl)
|
||||
SettingsCellWithArrow(
|
||||
title: "Coin Control",
|
||||
handler: (context) => Navigator.of(context).pushNamed(Routes.unspentCoinsList),
|
||||
),
|
||||
// SettingsCellWithArrow(
|
||||
// title: 'Trocador providers',
|
||||
// handler: (context) => Navigator.of(context).pushNamed(Routes.trocadorProvidersPage),
|
||||
// ),
|
||||
],
|
||||
);
|
||||
}),
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:cake_wallet/core/auth_service.dart';
|
||||
import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_regular_row.dart';
|
||||
import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_toggle.dart';
|
||||
import 'package:cake_wallet/entities/pin_code_required_duration.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/modal_header.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/src/widgets/new_list_row/new_list_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/picker.dart';
|
||||
import 'package:cake_wallet/utils/device_info.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/utils/feature_flag.dart';
|
||||
@@ -26,7 +28,7 @@ class SecurityBackupPage extends BasePage {
|
||||
final AuthService _authService;
|
||||
|
||||
@override
|
||||
String get title => S.current.security_and_backup;
|
||||
String get title => S.current.security;
|
||||
|
||||
final SecuritySettingsViewModel _securitySettingsViewModel;
|
||||
|
||||
@@ -36,144 +38,168 @@ class SecurityBackupPage extends BasePage {
|
||||
Widget body(BuildContext context) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (DeviceInfo.instance.isMobile || Platform.isMacOS || Platform.isLinux)
|
||||
Observer(builder: (_) {
|
||||
return SettingsSwitcherCell(
|
||||
key: ValueKey('security_backup_page_allow_biometrics_button_key'),
|
||||
title: S.current.settings_allow_biometrical_authentication,
|
||||
value: _securitySettingsViewModel.allowBiometricalAuthentication,
|
||||
onValueChange: (BuildContext context, bool value) {
|
||||
if (value) {
|
||||
_authService.authenticateAction(
|
||||
context,
|
||||
onAuthSuccess: (isAuthenticatedSuccessfully) async {
|
||||
if (isAuthenticatedSuccessfully) {
|
||||
if (await _securitySettingsViewModel.biometricAuthenticated()) {
|
||||
_securitySettingsViewModel
|
||||
.setAllowBiometricalAuthentication(isAuthenticatedSuccessfully);
|
||||
}
|
||||
} else {
|
||||
_securitySettingsViewModel
|
||||
.setAllowBiometricalAuthentication(isAuthenticatedSuccessfully);
|
||||
}
|
||||
},
|
||||
conditionToDetermineIfToUse2FA: _securitySettingsViewModel
|
||||
.shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
|
||||
);
|
||||
} else {
|
||||
_securitySettingsViewModel.setAllowBiometricalAuthentication(value);
|
||||
}
|
||||
});
|
||||
}),
|
||||
if (FeatureFlag.duressPinEnabled)
|
||||
Observer(builder: (_) {
|
||||
return SettingsSwitcherCell(
|
||||
key: ValueKey('security_backup_page_duress_pin_button_key'),
|
||||
title: 'Duress PIN',
|
||||
value: _securitySettingsViewModel.enableDuressPin,
|
||||
onValueChange: (BuildContext context, bool value) {
|
||||
_authService.authenticateAction(context, route: Routes.securityBackupDuressPin,
|
||||
onAuthSuccess: (isAuthenticatedSuccessfully) async {
|
||||
if (isAuthenticatedSuccessfully) {
|
||||
if (!value) {
|
||||
_securitySettingsViewModel.setEnableDuressPin(value);
|
||||
_securitySettingsViewModel.clearDuressPin();
|
||||
return;
|
||||
}
|
||||
final res = await _showDuressPinDescription(context);
|
||||
if (res) {
|
||||
final confirmation = await _showDuressPinConfirmation(context);
|
||||
|
||||
if (confirmation) {
|
||||
Navigator.of(context).pushNamed(
|
||||
Routes.setupDuressPin,
|
||||
arguments: (PinCodeState<PinCodeWidget> pinCtx, String _) async {
|
||||
pinCtx.close();
|
||||
_securitySettingsViewModel.setEnableDuressPin(true);
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18.0),
|
||||
child: Column(
|
||||
spacing: 16,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ModalHeader(
|
||||
iconPath: "assets/new-ui/settings_row_icons/security.svg",
|
||||
message: S.of(context).privacy_and_security_desc,
|
||||
title: S.of(context).security),
|
||||
Observer(
|
||||
builder: (_) => NewListSections(sections: {
|
||||
"": [
|
||||
if (DeviceInfo.instance.isMobile || Platform.isMacOS || Platform.isLinux)
|
||||
ListItemToggle(
|
||||
keyValue: "security_backup_page_allow_biometrics_button_key",
|
||||
label: S.current.settings_allow_biometrical_authentication,
|
||||
value: _securitySettingsViewModel.allowBiometricalAuthentication,
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
_authService.authenticateAction(
|
||||
context,
|
||||
onAuthSuccess: (isAuthenticatedSuccessfully) async {
|
||||
if (isAuthenticatedSuccessfully) {
|
||||
if (await _securitySettingsViewModel.biometricAuthenticated()) {
|
||||
_securitySettingsViewModel.setAllowBiometricalAuthentication(
|
||||
isAuthenticatedSuccessfully);
|
||||
}
|
||||
} else {
|
||||
_securitySettingsViewModel.setAllowBiometricalAuthentication(
|
||||
isAuthenticatedSuccessfully);
|
||||
}
|
||||
},
|
||||
conditionToDetermineIfToUse2FA: _securitySettingsViewModel
|
||||
.shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
|
||||
);
|
||||
} else {
|
||||
_securitySettingsViewModel.setAllowBiometricalAuthentication(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
conditionToDetermineIfToUse2FA: _securitySettingsViewModel
|
||||
.shouldRequireTOTP2FAForAllSecurityAndBackupSettings);
|
||||
});
|
||||
}),
|
||||
Observer(builder: (_) {
|
||||
return SettingsPickerCell<PinCodeRequiredDuration>(
|
||||
key: ValueKey('security_backup_page_require_pin_after_button_key'),
|
||||
title: S.current.require_pin_after,
|
||||
items: PinCodeRequiredDuration.values,
|
||||
selectedItem: _securitySettingsViewModel.pinCodeRequiredDuration,
|
||||
onItemSelected: (PinCodeRequiredDuration code) {
|
||||
_securitySettingsViewModel.setPinCodeRequiredDuration(code);
|
||||
},
|
||||
);
|
||||
}),
|
||||
if (!_isHardwareWallet)
|
||||
SettingsCellWithArrow(
|
||||
key: ValueKey('security_backup_page_show_keys_button_key'),
|
||||
title: S.current.show_keys,
|
||||
handler: (_) => _authService.authenticateAction(
|
||||
context,
|
||||
route: Routes.showKeys,
|
||||
conditionToDetermineIfToUse2FA:
|
||||
_securitySettingsViewModel.shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
|
||||
),
|
||||
}),
|
||||
if (FeatureFlag.duressPinEnabled)
|
||||
ListItemToggle(
|
||||
keyValue: "security_backup_page_duress_pin_button_key",
|
||||
label: "Duress PIN",
|
||||
value: _securitySettingsViewModel.enableDuressPin,
|
||||
onChanged: (bool value) {
|
||||
_authService
|
||||
.authenticateAction(context, route: Routes.securityBackupDuressPin,
|
||||
onAuthSuccess: (isAuthenticatedSuccessfully) async {
|
||||
if (isAuthenticatedSuccessfully) {
|
||||
if (!value) {
|
||||
_securitySettingsViewModel.setEnableDuressPin(value);
|
||||
_securitySettingsViewModel.clearDuressPin();
|
||||
return;
|
||||
}
|
||||
final res = await _showDuressPinDescription(context);
|
||||
if (res) {
|
||||
final confirmation = await _showDuressPinConfirmation(context);
|
||||
|
||||
if (confirmation) {
|
||||
Navigator.of(context).pushNamed(
|
||||
Routes.setupDuressPin,
|
||||
arguments:
|
||||
(PinCodeState<PinCodeWidget> pinCtx, String _) async {
|
||||
pinCtx.close();
|
||||
_securitySettingsViewModel.setEnableDuressPin(true);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
conditionToDetermineIfToUse2FA: _securitySettingsViewModel
|
||||
.shouldRequireTOTP2FAForAllSecurityAndBackupSettings);
|
||||
}),
|
||||
ListItemRegularRow(
|
||||
keyValue: "security_backup_page_require_pin_after_button_key",
|
||||
label: S.current.require_pin_after,
|
||||
trailingText: _securitySettingsViewModel.pinCodeRequiredDuration.toString(),
|
||||
onTap: () async {
|
||||
final items = PinCodeRequiredDuration.values;
|
||||
|
||||
final selectedAtIndex =
|
||||
items.indexOf(_securitySettingsViewModel.pinCodeRequiredDuration);
|
||||
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (_) => Picker(
|
||||
items: items,
|
||||
selectedAtIndex: selectedAtIndex,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
onItemSelected: (PinCodeRequiredDuration item) {
|
||||
_securitySettingsViewModel.setPinCodeRequiredDuration(item);
|
||||
},
|
||||
isSeparated: false,
|
||||
),
|
||||
);
|
||||
}),
|
||||
// if (!_isHardwareWallet)
|
||||
// ListItemRegularRow(
|
||||
// keyValue: "security_backup_page_show_keys_button_key",
|
||||
// label: S.current.show_keys,
|
||||
// onTap: () {
|
||||
// _authService.authenticateAction(
|
||||
// context,
|
||||
// route: Routes.showKeys,
|
||||
// conditionToDetermineIfToUse2FA: _securitySettingsViewModel
|
||||
// .shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
|
||||
// );
|
||||
// }),
|
||||
// if (!SettingsStoreBase.walletPasswordDirectInput)
|
||||
// ListItemRegularRow(
|
||||
// keyValue: "security_backup_page_change_password_button_key",
|
||||
// label: S.current.create_backup,
|
||||
// onTap: () {
|
||||
// _authService.authenticateAction(
|
||||
// context,
|
||||
// route: Routes.backup,
|
||||
// conditionToDetermineIfToUse2FA: _securitySettingsViewModel
|
||||
// .shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
|
||||
// );
|
||||
// }),
|
||||
ListItemRegularRow(
|
||||
keyValue: "security_backup_page_change_pin_button_key",
|
||||
label: S.current.settings_change_pin,
|
||||
onTap: () {
|
||||
_authService.authenticateAction(
|
||||
context,
|
||||
route: Routes.setupPin,
|
||||
arguments: (PinCodeState<PinCodeWidget> setupPinContext, String _) {
|
||||
setupPinContext.close();
|
||||
},
|
||||
conditionToDetermineIfToUse2FA: _securitySettingsViewModel
|
||||
.shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
|
||||
);
|
||||
}),
|
||||
ListItemRegularRow(
|
||||
keyValue: "security_backup_page_sign_and_verify",
|
||||
label: S.current.sign_verify_title,
|
||||
onTap: () {
|
||||
Navigator.of(context).pushNamed(Routes.signPage);
|
||||
}),
|
||||
ListItemRegularRow(
|
||||
keyValue: "security_backup_page_totp_2fa_button_key",
|
||||
label: _securitySettingsViewModel.useTotp2FA
|
||||
? S.current.modify_2fa
|
||||
: S.current.setup_2fa,
|
||||
onTap: () {
|
||||
_authService.authenticateAction(
|
||||
context,
|
||||
route: _securitySettingsViewModel.useTotp2FA
|
||||
? Routes.modify2FAPage
|
||||
: Routes.setup2faInfoPage,
|
||||
conditionToDetermineIfToUse2FA: _securitySettingsViewModel
|
||||
.shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
|
||||
);
|
||||
})
|
||||
]
|
||||
}),
|
||||
),
|
||||
if (!SettingsStoreBase.walletPasswordDirectInput)
|
||||
SettingsCellWithArrow(
|
||||
key: ValueKey('security_backup_page_create_backup_button_key'),
|
||||
title: S.current.create_backup,
|
||||
handler: (_) => _authService.authenticateAction(
|
||||
context,
|
||||
route: Routes.backup,
|
||||
conditionToDetermineIfToUse2FA:
|
||||
_securitySettingsViewModel.shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
|
||||
),
|
||||
),
|
||||
SettingsCellWithArrow(
|
||||
key: ValueKey('security_backup_page_change_pin_button_key'),
|
||||
title: S.current.settings_change_pin,
|
||||
handler: (_) => _authService.authenticateAction(
|
||||
context,
|
||||
route: Routes.setupPin,
|
||||
arguments: (PinCodeState<PinCodeWidget> setupPinContext, String _) {
|
||||
setupPinContext.close();
|
||||
},
|
||||
conditionToDetermineIfToUse2FA:
|
||||
_securitySettingsViewModel.shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
|
||||
),
|
||||
),
|
||||
SettingsCellWithArrow(
|
||||
key: ValueKey('security_backup_page_sign_and_verify'),
|
||||
title: S.current.sign_verify_title,
|
||||
handler: (_) => Navigator.of(context).pushNamed(Routes.signPage)
|
||||
//_securitySettingsViewModel.pinCodeRequiredDuration,
|
||||
),
|
||||
Observer(
|
||||
builder: (context) {
|
||||
return SettingsCellWithArrow(
|
||||
key: ValueKey('security_backup_page_totp_2fa_button_key'),
|
||||
title: _securitySettingsViewModel.useTotp2FA
|
||||
? S.current.modify_2fa
|
||||
: S.current.setup_2fa,
|
||||
handler: (_) => _authService.authenticateAction(
|
||||
context,
|
||||
route: _securitySettingsViewModel.useTotp2FA
|
||||
? Routes.modify2FAPage
|
||||
: Routes.setup2faInfoPage,
|
||||
conditionToDetermineIfToUse2FA: _securitySettingsViewModel
|
||||
.shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/settings/silent_payments_settings_view_model.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
class SilentPaymentsLogPage extends BasePage {
|
||||
@@ -33,6 +34,7 @@ class SilentPaymentsLogPage extends BasePage {
|
||||
return Center(child: Text('No logs found'));
|
||||
} else {
|
||||
return SingleChildScrollView(
|
||||
controller: ModalScrollController.of(context),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16.0),
|
||||
child: Text(
|
||||
|
||||
@@ -1,55 +1,64 @@
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
|
||||
import 'package:cake_wallet/utils/feature_flag.dart';
|
||||
import 'package:cake_wallet/view_model/settings/silent_payments_settings_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
|
||||
class SilentPaymentsSettingsPage extends BasePage {
|
||||
class SilentPaymentsSettingsPage extends StatelessWidget {
|
||||
SilentPaymentsSettingsPage(this._silentPaymentsSettingsViewModel);
|
||||
|
||||
@override
|
||||
String get title => S.current.silent_payments_settings;
|
||||
|
||||
final SilentPaymentsSettingsViewModel _silentPaymentsSettingsViewModel;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return SingleChildScrollView(
|
||||
child: Observer(builder: (_) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: Column(
|
||||
children: [
|
||||
SettingsSwitcherCell(
|
||||
title: S.current.silent_payments_display_card,
|
||||
value: _silentPaymentsSettingsViewModel.silentPaymentsCardDisplay,
|
||||
onValueChange: (_, bool value) {
|
||||
_silentPaymentsSettingsViewModel.setSilentPaymentsCardDisplay(value);
|
||||
},
|
||||
),
|
||||
SettingsSwitcherCell(
|
||||
title: S.current.silent_payments_always_scan,
|
||||
value: _silentPaymentsSettingsViewModel.silentPaymentsAlwaysScan,
|
||||
onValueChange: (_, bool value) {
|
||||
_silentPaymentsSettingsViewModel.setSilentPaymentsAlwaysScan(value);
|
||||
},
|
||||
),
|
||||
SettingsCellWithArrow(
|
||||
title: S.current.silent_payments_scanning,
|
||||
handler: (BuildContext context) => Navigator.of(context).pushNamed(Routes.rescan),
|
||||
),
|
||||
SettingsCellWithArrow(
|
||||
title: S.current.silent_payments_logs,
|
||||
handler: (BuildContext context) =>
|
||||
Navigator.of(context).pushNamed(Routes.silentPaymentsLogs),
|
||||
),
|
||||
],
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: Column(
|
||||
children: [
|
||||
ModalTopBar(title: S.current.silent_payments_settings,leadingIcon: Icon(Icons.arrow_back_ios_new),onLeadingPressed: Navigator.of(context).pop,),
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
child: Observer(builder: (_) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: Column(
|
||||
children: [
|
||||
if(!FeatureFlag.hasNewUi)
|
||||
SettingsSwitcherCell(
|
||||
title: S.current.silent_payments_display_card,
|
||||
value: _silentPaymentsSettingsViewModel.silentPaymentsCardDisplay,
|
||||
onValueChange: (_, bool value) {
|
||||
_silentPaymentsSettingsViewModel.setSilentPaymentsCardDisplay(value);
|
||||
},
|
||||
),
|
||||
SettingsSwitcherCell(
|
||||
title: S.current.silent_payments_always_scan,
|
||||
value: _silentPaymentsSettingsViewModel.silentPaymentsAlwaysScan,
|
||||
onValueChange: (_, bool value) {
|
||||
_silentPaymentsSettingsViewModel.setSilentPaymentsAlwaysScan(value);
|
||||
},
|
||||
),
|
||||
SettingsCellWithArrow(
|
||||
title: S.current.silent_payments_scanning,
|
||||
handler: (BuildContext context) => Navigator.of(context).pushNamed(Routes.rescan),
|
||||
),
|
||||
SettingsCellWithArrow(
|
||||
title: S.current.silent_payments_logs,
|
||||
handler: (BuildContext context) =>
|
||||
Navigator.of(context).pushNamed(Routes.silentPaymentsLogs),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arro
|
||||
import 'package:cake_wallet/view_model/set_up_2fa_viewmodel.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||
|
||||
import '../../../routes.dart';
|
||||
|
||||
@@ -25,6 +26,7 @@ class Modify2FAPage extends BasePage {
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return SingleChildScrollView(
|
||||
controller: ModalScrollController.of(context),
|
||||
child: _2FAControlsWidget(setup2FAViewModel: setup2FAViewModel),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:another_flushbar/flushbar.dart';
|
||||
import 'package:cake_wallet/core/execution_state.dart';
|
||||
import 'package:cake_wallet/core/totp_request_details.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
|
||||
import 'package:cake_wallet/utils/show_bar.dart';
|
||||
import 'package:cake_wallet/view_model/auth_state.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
@@ -92,7 +93,6 @@ class TotpAuthCodePageState extends State<TotpAuthCodePage> {
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
key: _key,
|
||||
resizeToAvoidBottomInset: false,
|
||||
body: TOTPEnterCode(
|
||||
setup2FAViewModel: widget.setup2FAViewModel,
|
||||
isForSetup: widget.totpArguments.isForSetup ?? false,
|
||||
@@ -108,99 +108,120 @@ class TotpAuthCodePageState extends State<TotpAuthCodePage> {
|
||||
}
|
||||
}
|
||||
|
||||
class TOTPEnterCode extends BasePage {
|
||||
TOTPEnterCode({
|
||||
class TOTPEnterCode extends StatefulWidget {
|
||||
const TOTPEnterCode({
|
||||
required this.setup2FAViewModel,
|
||||
required this.isForSetup,
|
||||
required this.isClosable,
|
||||
}) : totpController = TextEditingController() {
|
||||
totpController.addListener(() {
|
||||
setup2FAViewModel.enteredOTPCode = totpController.text;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@override
|
||||
String get title => isForSetup ? S.current.setup_2fa : S.current.verify_with_2fa;
|
||||
|
||||
Widget? leading(BuildContext context) {
|
||||
return isClosable ? super.leading(context) : null;
|
||||
}
|
||||
|
||||
final TextEditingController totpController;
|
||||
final Setup2FAViewModel setup2FAViewModel;
|
||||
final bool isForSetup;
|
||||
final bool isClosable;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 24,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
BaseTextFormField(
|
||||
textAlign: TextAlign.left,
|
||||
hintText: S.current.totp_code,
|
||||
controller: totpController,
|
||||
keyboardType: TextInputType.number,
|
||||
placeholderTextStyle: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
Text(
|
||||
S.current.please_fill_totp,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
height: 1.2,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Spacer(),
|
||||
Observer(
|
||||
builder: (context) {
|
||||
return PrimaryButton(
|
||||
isDisabled: setup2FAViewModel.enteredOTPCode.length != 8,
|
||||
onPressed: () async {
|
||||
final result =
|
||||
await setup2FAViewModel.totp2FAAuth(totpController.text, isForSetup);
|
||||
final bannedState = setup2FAViewModel.state is AuthenticationBanned;
|
||||
State<TOTPEnterCode> createState() => _TOTPEnterCodeState();
|
||||
}
|
||||
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return PopUpCancellableAlertDialog(
|
||||
contentText: _textDisplayedInPopupOnResult(result, bannedState, context),
|
||||
actionButtonText: S.of(context).ok,
|
||||
buttonAction: () {
|
||||
result ? setup2FAViewModel.success() : null;
|
||||
if (isForSetup && result) {
|
||||
Navigator.pop(context);
|
||||
// Navigator.of(context)
|
||||
// .popAndPushNamed(Routes.modify2FAPage);
|
||||
} else {
|
||||
Navigator.of(context).pop(result);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
if (isForSetup && result) {
|
||||
if (context.mounted) {
|
||||
Navigator.pushReplacementNamed(context, Routes.modify2FAPage);
|
||||
}
|
||||
}
|
||||
},
|
||||
text: S.of(context).continue_text,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
);
|
||||
},
|
||||
),
|
||||
SizedBox(height: 24),
|
||||
],
|
||||
class _TOTPEnterCodeState extends State<TOTPEnterCode> {
|
||||
final totpController = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
totpController.addListener(() {
|
||||
widget.setup2FAViewModel.enteredOTPCode = totpController.text;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
ModalTopBar(
|
||||
title: widget.isForSetup ? S.current.setup_2fa : S.current.verify_with_2fa,
|
||||
leadingIcon: Icon(Icons.arrow_back_ios_new),
|
||||
onLeadingPressed: Navigator.of(context).pop,
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 24,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
|
||||
BaseTextFormField(
|
||||
textAlign: TextAlign.left,
|
||||
hintText: S.current.totp_code,
|
||||
controller: totpController,
|
||||
keyboardType: TextInputType.number,
|
||||
placeholderTextStyle: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
Text(
|
||||
S.current.please_fill_totp,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
height: 1.2,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Spacer(),
|
||||
Observer(
|
||||
builder: (context) {
|
||||
return PrimaryButton(
|
||||
isDisabled: widget.setup2FAViewModel.enteredOTPCode.length != 8,
|
||||
onPressed: () async {
|
||||
final result =
|
||||
await widget.setup2FAViewModel.totp2FAAuth(totpController.text, widget.isForSetup);
|
||||
final bannedState = widget.setup2FAViewModel.state is AuthenticationBanned;
|
||||
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return PopUpCancellableAlertDialog(
|
||||
contentText: _textDisplayedInPopupOnResult(result, bannedState, context),
|
||||
actionButtonText: S.of(context).ok,
|
||||
buttonAction: () {
|
||||
result ? widget.setup2FAViewModel.success() : null;
|
||||
if (widget.isForSetup && result) {
|
||||
Navigator.pop(context);
|
||||
// Navigator.of(context)
|
||||
// .popAndPushNamed(Routes.modify2FAPage);
|
||||
} else {
|
||||
Navigator.of(context).pop(result);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
if (widget.isForSetup && result) {
|
||||
if (context.mounted) {
|
||||
Navigator.pushReplacementNamed(context, Routes.modify2FAPage);
|
||||
}
|
||||
}
|
||||
},
|
||||
text: S.of(context).continue_text,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
);
|
||||
},
|
||||
),
|
||||
SizedBox(height: 24),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -208,10 +229,10 @@ class TOTPEnterCode extends BasePage {
|
||||
String _textDisplayedInPopupOnResult(bool result, bool bannedState, BuildContext context) {
|
||||
switch (result) {
|
||||
case true:
|
||||
return isForSetup ? S.current.totp_2fa_success : S.current.totp_verification_success;
|
||||
return widget.isForSetup ? S.current.totp_2fa_success : S.current.totp_verification_success;
|
||||
case false:
|
||||
if (bannedState) {
|
||||
final state = setup2FAViewModel.state as AuthenticationBanned;
|
||||
final state = widget.setup2FAViewModel.state as AuthenticationBanned;
|
||||
return S.of(context).failed_authentication(state.error);
|
||||
} else {
|
||||
return S.current.totp_2fa_failure;
|
||||
|
||||
@@ -41,7 +41,7 @@ class Setup2FAQRPage extends BasePage {
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(maxHeight: MediaQuery.of(context).size.height * 0.4),
|
||||
constraints: BoxConstraints(maxHeight: MediaQuery.of(context).size.height * 0.3),
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1.0,
|
||||
child: Container(
|
||||
|
||||
@@ -34,35 +34,27 @@ class SupportPage extends BasePage {
|
||||
@override
|
||||
Widget body(BuildContext context) => Center(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.only(left: 24, right: 24),
|
||||
padding: const EdgeInsets.only(left: 16, right: 16, top: 16),
|
||||
child: Column(
|
||||
spacing: 16,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 20),
|
||||
child: OptionTile(
|
||||
image: Image.asset(_imageSupportChat, width: 55, height: 55),
|
||||
title: S.of(context).support_title_live_chat,
|
||||
description: S.of(context).support_description_live_chat,
|
||||
onPressed: () => _onPressedSupportChat(context),
|
||||
),
|
||||
OptionTile(
|
||||
image: Image.asset(_imageSupportChat, width: 55, height: 55),
|
||||
title: S.of(context).support_title_live_chat,
|
||||
description: S.of(context).support_description_live_chat,
|
||||
onPressed: () => _onPressedSupportChat(context),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 20),
|
||||
child: OptionTile(
|
||||
image: Image.asset(_imageSupportDocs, width: 55, height: 55),
|
||||
title: S.of(context).support_title_guides,
|
||||
description: S.of(context).support_description_guides,
|
||||
onPressed: () => _launchUrl(supportViewModel.docsUrl),
|
||||
),
|
||||
OptionTile(
|
||||
image: Image.asset(_imageSupportDocs, width: 55, height: 55),
|
||||
title: S.of(context).support_title_guides,
|
||||
description: S.of(context).support_description_guides,
|
||||
onPressed: () => _launchUrl(supportViewModel.docsUrl),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 20),
|
||||
child: OptionTile(
|
||||
image: Image.asset(_imageSupportLinks, width: 55, height: 55),
|
||||
title: S.of(context).support_title_other_links,
|
||||
description: S.of(context).support_description_other_links,
|
||||
onPressed: () => Navigator.pushNamed(context, Routes.supportOtherLinks),
|
||||
),
|
||||
OptionTile(
|
||||
image: Image.asset(_imageSupportLinks, width: 55, height: 55),
|
||||
title: S.of(context).support_title_other_links,
|
||||
description: S.of(context).support_description_other_links,
|
||||
onPressed: () => Navigator.pushNamed(context, Routes.supportOtherLinks),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -73,7 +65,7 @@ class SupportPage extends BasePage {
|
||||
if (DeviceInfo.instance.isDesktop) {
|
||||
_launchUrl(supportViewModel.fetchUrl());
|
||||
} else {
|
||||
Navigator.pushNamed(context, Routes.supportLiveChat);
|
||||
Navigator.of(context, rootNavigator: true).pushNamed(Routes.supportLiveChat);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,39 +1,52 @@
|
||||
import 'package:cake_wallet/core/secure_storage.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
|
||||
import 'package:cake_wallet/src/screens/support_chat/widgets/chatwoot_widget.dart';
|
||||
import 'package:cake_wallet/view_model/support_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SupportChatPage extends BasePage {
|
||||
class SupportChatPage extends StatelessWidget {
|
||||
SupportChatPage(this.supportViewModel, {required this.secureStorage});
|
||||
|
||||
final SupportViewModel supportViewModel;
|
||||
final SecureStorage secureStorage;
|
||||
|
||||
@override
|
||||
String get title => S.current.settings_support;
|
||||
|
||||
@override
|
||||
AppBarStyle get appBarStyle => AppBarStyle.regular;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) => FutureBuilder<String>(
|
||||
future: getCookie(),
|
||||
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
|
||||
if (snapshot.hasData)
|
||||
return ChatwootWidget(
|
||||
secureStorage,
|
||||
supportUrl: supportViewModel.fetchUrl(authToken: snapshot.data!),
|
||||
appVersion: supportViewModel.appVersion,
|
||||
fiatApiMode: supportViewModel.fiatApiMode,
|
||||
walletType: supportViewModel.walletType,
|
||||
walletSyncState: supportViewModel.walletSyncState,
|
||||
builtInTorState: supportViewModel.builtInTorState,
|
||||
);
|
||||
return Container();
|
||||
},
|
||||
);
|
||||
Widget build(BuildContext context) => Container(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: SafeArea(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
|
||||
child: Column(
|
||||
children: [
|
||||
ModalTopBar(
|
||||
title: S.current.settings_support,
|
||||
leadingIcon: Icon(Icons.arrow_back_ios_new),
|
||||
onLeadingPressed: Navigator.of(context).pop),
|
||||
FutureBuilder<String>(
|
||||
future: getCookie(),
|
||||
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
|
||||
if (snapshot.hasData)
|
||||
return Expanded(
|
||||
child: ChatwootWidget(
|
||||
secureStorage,
|
||||
supportUrl: supportViewModel.fetchUrl(authToken: snapshot.data!),
|
||||
appVersion: supportViewModel.appVersion,
|
||||
fiatApiMode: supportViewModel.fiatApiMode,
|
||||
walletType: supportViewModel.walletType,
|
||||
walletSyncState: supportViewModel.walletSyncState,
|
||||
builtInTorState: supportViewModel.builtInTorState,
|
||||
),
|
||||
);
|
||||
return Container();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
Future<String> getCookie() async =>
|
||||
await secureStorage.read(key: COOKIE_KEY) ?? "";
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:cake_wallet/view_model/support_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||
|
||||
class SupportOtherLinksPage extends BasePage {
|
||||
SupportOtherLinksPage(this.supportViewModel);
|
||||
@@ -29,6 +30,8 @@ class SupportOtherLinksPage extends BasePage {
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(maxWidth: 500),
|
||||
child: SectionStandardList(
|
||||
scrollController: ModalScrollController.of(context),
|
||||
physics: ClampingScrollPhysics(),
|
||||
sectionCount: 1,
|
||||
itemCounter: (int _) => supportViewModel.items.length,
|
||||
itemBuilder: (_, index) {
|
||||
|
||||
@@ -216,6 +216,7 @@ class RBFDetailsPage extends BasePage {
|
||||
} catch (_) {}
|
||||
},
|
||||
change: transactionDetailsViewModel.sendViewModel.pendingTransaction!.change,
|
||||
amountParsingProxy: transactionDetailsViewModel.sendViewModel.amountParsingProxy,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -57,7 +57,7 @@ class AnimatedURPage extends BasePage {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(32.0),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 32.0),
|
||||
child: URQR(
|
||||
urqr: urQr,
|
||||
walletType: animatedURmodel.wallet.type,
|
||||
@@ -66,7 +66,6 @@ class AnimatedURPage extends BasePage {
|
||||
),
|
||||
if (["ur:xmr-txunsigned", "ur:xmr-output", "ur:psbt", BBQR.header]
|
||||
.contains(urQrType)) ...{
|
||||
Spacer(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: SizedBox(
|
||||
|
||||
@@ -2,6 +2,6 @@ import 'dart:math';
|
||||
|
||||
extension StringParsing on String {
|
||||
String safeSubString(int start, int end) {
|
||||
return this.substring(0, min(this.toString().length, 12));
|
||||
return this.substring(start, min(this.toString().length, end));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import 'package:cake_wallet/view_model/wallet_keys_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
|
||||
class WalletKeysPage extends BasePage {
|
||||
@@ -214,6 +215,7 @@ class _WalletKeysPageBodyState extends State<WalletKeysPageBody>
|
||||
children: [
|
||||
Expanded(
|
||||
child: ListView.separated(
|
||||
controller: ModalScrollController.of(context),
|
||||
shrinkWrap: true,
|
||||
itemCount: items.length,
|
||||
itemBuilder: (context, index) {
|
||||
@@ -237,7 +239,7 @@ class _WalletKeysPageBodyState extends State<WalletKeysPageBody>
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 14),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
),
|
||||
child: Row(
|
||||
@@ -294,7 +296,7 @@ class _WalletKeysPageBodyState extends State<WalletKeysPageBody>
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 14),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
),
|
||||
child: Row(
|
||||
|
||||
@@ -17,9 +17,12 @@ import 'package:cake_wallet/src/screens/wallet_list/edit_wallet_button_widget.da
|
||||
import 'package:cake_wallet/src/screens/wallet_list/filtered_list.dart';
|
||||
import 'package:cake_wallet/src/screens/wallet_unlock/wallet_unlock_arguments.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/gradient_background.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/utils/exception_handler.dart';
|
||||
import 'package:cake_wallet/utils/feature_flag.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cake_wallet/utils/show_bar.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
@@ -28,6 +31,7 @@ import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
|
||||
import 'package:cake_wallet/wallet_type_utils.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -40,6 +44,9 @@ class WalletListPage extends BasePage {
|
||||
this.onWalletLoaded,
|
||||
});
|
||||
|
||||
@override
|
||||
bool get gradientBackground => true;
|
||||
|
||||
final WalletListViewModel walletListViewModel;
|
||||
final AuthService authService;
|
||||
final Future<void> Function(BuildContext)? onWalletLoaded;
|
||||
@@ -99,7 +106,7 @@ class WalletListPage extends BasePage {
|
||||
),
|
||||
child: Image.asset(
|
||||
'assets/images/filter_icon.png',
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -135,204 +142,207 @@ class WalletListBodyState extends State<WalletListBody> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: double.infinity,
|
||||
padding: EdgeInsets.only(top: 16),
|
||||
child: Stack(
|
||||
alignment: Alignment.bottomCenter,
|
||||
fit: StackFit.expand,
|
||||
children: <Widget>[
|
||||
SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (widget.walletListViewModel.multiWalletGroups.isNotEmpty) ...{
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 24),
|
||||
child: Text(
|
||||
S.current.shared_seed_wallet_groups,
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
Container(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: 20, right: 20),
|
||||
child: Observer(
|
||||
builder: (_) => FilteredList(
|
||||
shrinkWrap: true,
|
||||
list: widget.walletListViewModel.multiWalletGroups,
|
||||
updateFunction: widget.walletListViewModel.reorderAccordingToWalletList,
|
||||
itemBuilder: (context, index) {
|
||||
final group = widget.walletListViewModel.multiWalletGroups[index];
|
||||
final groupName =
|
||||
group.groupName ?? '${S.current.wallet_group} ${index + 1}';
|
||||
|
||||
widget.walletListViewModel.updateTileState(
|
||||
index,
|
||||
widget.walletListViewModel.expansionTileStateTrack[index] ?? false,
|
||||
);
|
||||
|
||||
return GroupedWalletExpansionTile(
|
||||
onExpansionChanged: (value) {
|
||||
widget.walletListViewModel.updateTileState(index, value);
|
||||
setState(() {});
|
||||
},
|
||||
shouldShowCurrentWalletPointer: true,
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
title: groupName,
|
||||
tileKey: ValueKey('group_wallets_expansion_tile_widget_$index'),
|
||||
leadingWidget: Icon(
|
||||
Icons.account_balance_wallet_outlined,
|
||||
size: 28,
|
||||
),
|
||||
trailingWidget: EditWalletButtonWidget(
|
||||
width: 88,
|
||||
isGroup: true,
|
||||
isExpanded:
|
||||
widget.walletListViewModel.expansionTileStateTrack[index]!,
|
||||
onTap: () {
|
||||
final wallet = widget.walletListViewModel
|
||||
.convertWalletInfoToWalletListItem(group.wallets.first);
|
||||
Navigator.of(context).pushNamed(
|
||||
Routes.walletEdit,
|
||||
arguments: WalletEditPageArguments(
|
||||
walletListViewModel: widget.walletListViewModel,
|
||||
editingWallet: wallet,
|
||||
isWalletGroup: true,
|
||||
groupName: groupName,
|
||||
walletGroupKey: group.groupKey,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
childWallets: group.wallets.map((walletInfo) {
|
||||
return widget.walletListViewModel
|
||||
.convertWalletInfoToWalletListItem(walletInfo);
|
||||
}).toList(),
|
||||
isSelected: false,
|
||||
onChildItemTapped: (wallet) =>
|
||||
wallet.isCurrent ? null : _loadWallet(wallet),
|
||||
childTrailingWidget: (item) {
|
||||
return item.isCurrent
|
||||
? SizedBox.shrink()
|
||||
: EditWalletButtonWidget(
|
||||
width: 64,
|
||||
onTap: () => Navigator.of(context).pushNamed(
|
||||
Routes.walletEdit,
|
||||
arguments: WalletEditPageArguments(
|
||||
walletListViewModel: widget.walletListViewModel,
|
||||
editingWallet: item,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
return GradientBackground(
|
||||
scaffold: Container(
|
||||
height: double.infinity,
|
||||
padding: EdgeInsets.only(top: 16),
|
||||
child: Stack(
|
||||
alignment: Alignment.bottomCenter,
|
||||
fit: StackFit.expand,
|
||||
children: <Widget>[
|
||||
SingleChildScrollView(
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (widget.walletListViewModel.multiWalletGroups.isNotEmpty) ...{
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 24),
|
||||
child: Text(
|
||||
S.current.shared_seed_wallets,
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 24),
|
||||
},
|
||||
if (widget.walletListViewModel.singleWalletsList.isNotEmpty) ...{
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 24),
|
||||
child: Text(
|
||||
S.current.single_seed_wallets_group,
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 20, right: 20),
|
||||
child: Container(
|
||||
child: Observer(
|
||||
builder: (_) => FilteredList(
|
||||
shrinkWrap: true,
|
||||
list: widget.walletListViewModel.singleWalletsList,
|
||||
updateFunction: widget.walletListViewModel.reorderAccordingToWalletList,
|
||||
itemBuilder: (context, index) {
|
||||
final wallet = widget.walletListViewModel.singleWalletsList[index];
|
||||
final currentColor = wallet.isCurrent
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: Theme.of(context).colorScheme.surface;
|
||||
return GroupedWalletExpansionTile(
|
||||
tileKey: ValueKey('single_wallets_expansion_tile_widget_$index'),
|
||||
isCurrentlySelectedWallet: wallet.isCurrent,
|
||||
leadingWidget: SizedBox(
|
||||
width: wallet.isCurrent ? 56 : 40,
|
||||
child: Row(
|
||||
children: [
|
||||
wallet.isCurrent
|
||||
? Container(
|
||||
height: 35,
|
||||
width: 6,
|
||||
margin: EdgeInsets.only(right: 16),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
topRight: Radius.circular(16),
|
||||
bottomRight: Radius.circular(16),
|
||||
),
|
||||
color: currentColor,
|
||||
),
|
||||
)
|
||||
: SizedBox(width: 6),
|
||||
Image.asset(
|
||||
getCryptoCurrencyForWalletListItem(
|
||||
wallet.type,
|
||||
).iconPath!,
|
||||
width: 32,
|
||||
height: 32,
|
||||
),
|
||||
],
|
||||
SizedBox(height: 16),
|
||||
Container(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: 20, right: 20),
|
||||
child: Observer(
|
||||
builder: (_) => FilteredList(
|
||||
shrinkWrap: true,
|
||||
list: widget.walletListViewModel.multiWalletGroups,
|
||||
updateFunction: widget.walletListViewModel.reorderAccordingToWalletList,
|
||||
itemBuilder: (context, index) {
|
||||
final group = widget.walletListViewModel.multiWalletGroups[index];
|
||||
final groupName =
|
||||
group.groupName ?? '${S.current.wallet_group} ${index + 1}';
|
||||
|
||||
widget.walletListViewModel.updateTileState(
|
||||
index,
|
||||
widget.walletListViewModel.expansionTileStateTrack[index] ?? false,
|
||||
);
|
||||
|
||||
return GroupedWalletExpansionTile(
|
||||
onExpansionChanged: (value) {
|
||||
widget.walletListViewModel.updateTileState(index, value);
|
||||
setState(() {});
|
||||
},
|
||||
shouldShowCurrentWalletPointer: true,
|
||||
borderRadius: BorderRadius.all(Radius.circular(18)),
|
||||
title: groupName,
|
||||
tileKey: ValueKey('group_wallets_expansion_tile_widget_$index'),
|
||||
leadingWidget: CakeImageWidget(
|
||||
imageUrl: "assets/new-ui/navbar/wallets.svg",
|
||||
width: 28,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
title: wallet.name,
|
||||
isSelected: false,
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
margin: EdgeInsets.only(left: 20, right: 20, bottom: 12),
|
||||
onTitleTapped: () => wallet.isCurrent ? null : _loadWallet(wallet),
|
||||
trailingWidget: wallet.isCurrent
|
||||
? null
|
||||
: EditWalletButtonWidget(
|
||||
width: 64,
|
||||
onTap: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
Routes.walletEdit,
|
||||
arguments: WalletEditPageArguments(
|
||||
walletListViewModel: widget.walletListViewModel,
|
||||
editingWallet: wallet,
|
||||
trailingWidget: EditWalletButtonWidget(
|
||||
width: 88,
|
||||
isGroup: true,
|
||||
isExpanded:
|
||||
widget.walletListViewModel.expansionTileStateTrack[index]!,
|
||||
onTap: () {
|
||||
final wallet = widget.walletListViewModel
|
||||
.convertWalletInfoToWalletListItem(group.wallets.first);
|
||||
Navigator.of(context).pushNamed(
|
||||
Routes.walletEdit,
|
||||
arguments: WalletEditPageArguments(
|
||||
walletListViewModel: widget.walletListViewModel,
|
||||
editingWallet: wallet,
|
||||
isWalletGroup: true,
|
||||
groupName: groupName,
|
||||
walletGroupKey: group.groupKey,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
childWallets: group.wallets.map((walletInfo) {
|
||||
return widget.walletListViewModel
|
||||
.convertWalletInfoToWalletListItem(walletInfo);
|
||||
}).toList(),
|
||||
isSelected: false,
|
||||
onChildItemTapped: (wallet) =>
|
||||
wallet.isCurrent ? null : _loadWallet(wallet),
|
||||
childTrailingWidget: (item) {
|
||||
return item.isCurrent
|
||||
? SizedBox.shrink()
|
||||
: EditWalletButtonWidget(
|
||||
width: 64,
|
||||
onTap: () => Navigator.of(context).pushNamed(
|
||||
Routes.walletEdit,
|
||||
arguments: WalletEditPageArguments(
|
||||
walletListViewModel: widget.walletListViewModel,
|
||||
editingWallet: item,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 150),
|
||||
},
|
||||
],
|
||||
SizedBox(height: 24),
|
||||
},
|
||||
if (widget.walletListViewModel.singleWalletsList.isNotEmpty) ...{
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 24),
|
||||
child: Text(
|
||||
S.current.single_seed_wallets_group,
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 20, right: 20),
|
||||
child: Container(
|
||||
child: Observer(
|
||||
builder: (_) => FilteredList(
|
||||
shrinkWrap: true,
|
||||
list: widget.walletListViewModel.singleWalletsList,
|
||||
updateFunction: widget.walletListViewModel.reorderAccordingToWalletList,
|
||||
itemBuilder: (context, index) {
|
||||
final wallet = widget.walletListViewModel.singleWalletsList[index];
|
||||
final currentColor = wallet.isCurrent
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: Theme.of(context).colorScheme.surface;
|
||||
return GroupedWalletExpansionTile(
|
||||
tileKey: ValueKey('single_wallets_expansion_tile_widget_$index'),
|
||||
isCurrentlySelectedWallet: wallet.isCurrent,
|
||||
leadingWidget: SizedBox(
|
||||
width: wallet.isCurrent ? 56 : 40,
|
||||
child: Row(
|
||||
children: [
|
||||
wallet.isCurrent
|
||||
? Container(
|
||||
height: 35,
|
||||
width: 6,
|
||||
margin: EdgeInsets.only(right: 16),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
topRight: Radius.circular(18),
|
||||
bottomRight: Radius.circular(18),
|
||||
),
|
||||
color: currentColor,
|
||||
),
|
||||
)
|
||||
: SizedBox(width: 6),
|
||||
Image.asset(
|
||||
getCryptoCurrencyIconForWalletListItem(
|
||||
wallet.type,
|
||||
),
|
||||
width: 32,
|
||||
height: 32,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
title: wallet.name,
|
||||
isSelected: false,
|
||||
borderRadius: BorderRadius.all(Radius.circular(18)),
|
||||
margin: EdgeInsets.only(left: 20, right: 20, bottom: 12),
|
||||
onTitleTapped: () => wallet.isCurrent ? null : _loadWallet(wallet),
|
||||
trailingWidget: wallet.isCurrent
|
||||
? null
|
||||
: EditWalletButtonWidget(
|
||||
width: 64,
|
||||
onTap: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
Routes.walletEdit,
|
||||
arguments: WalletEditPageArguments(
|
||||
walletListViewModel: widget.walletListViewModel,
|
||||
editingWallet: wallet,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
SizedBox(height: 250),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
child: Stack(
|
||||
Stack(
|
||||
alignment: Alignment.bottomCenter,
|
||||
children: [
|
||||
IgnorePointer(
|
||||
!FeatureFlag.hasNewUi
|
||||
? IgnorePointer(
|
||||
child: Container(
|
||||
alignment: Alignment.bottomCenter,
|
||||
alignment: Alignment.bottomCenter,
|
||||
height: 185,
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
@@ -347,9 +357,29 @@ class WalletListBodyState extends State<WalletListBody> {
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: IgnorePointer(
|
||||
child: Container(
|
||||
height: 275,
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: <Color>[
|
||||
Theme.of(context).colorScheme.surfaceDim.withAlpha(10),
|
||||
Theme.of(context).colorScheme.surfaceDim.withAlpha(150),
|
||||
Theme.of(context).colorScheme.surfaceDim.withAlpha(255),
|
||||
Theme.of(context).colorScheme.surfaceDim.withAlpha(255),
|
||||
Theme.of(context).colorScheme.surfaceDim.withAlpha(255),
|
||||
Theme.of(context).colorScheme.surfaceDim.withAlpha(255),
|
||||
Theme.of(context).colorScheme.surfaceDim.withAlpha(255)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 120,
|
||||
height: 240,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
margin: EdgeInsets.only(bottom: 24),
|
||||
padding: EdgeInsets.only(left: 16, right: 16),
|
||||
@@ -434,19 +464,24 @@ class WalletListBodyState extends State<WalletListBody> {
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
),
|
||||
if(FeatureFlag.hasNewUi)
|
||||
SizedBox(height:52.0)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _loadWallet(WalletListItem wallet) async {
|
||||
if (_loadingWallet) return;
|
||||
if (_loadingWallet) {
|
||||
printV("_loadWallet abandoned because _loadingWallet");
|
||||
return;
|
||||
}
|
||||
|
||||
_loadingWallet = true;
|
||||
|
||||
@@ -469,6 +504,7 @@ class WalletListBodyState extends State<WalletListBody> {
|
||||
context,
|
||||
onAuthSuccess: (isAuthenticatedSuccessfully) async {
|
||||
if (!isAuthenticatedSuccessfully) {
|
||||
printV("!isAuthenticatedSuccessfully");
|
||||
_loadingWallet = false;
|
||||
return;
|
||||
}
|
||||
@@ -510,16 +546,16 @@ class WalletListBodyState extends State<WalletListBody> {
|
||||
await widget.walletListViewModel.loadWallet(wallet);
|
||||
// only pop the wallets route in mobile as it will go back to dashboard page
|
||||
// in desktop platforms the navigation tree is different
|
||||
unawaited(hideProgressText());
|
||||
if (responsiveLayoutUtil.shouldRenderMobileUI) {
|
||||
// await Future.delayed(Duration(seconds: 1));
|
||||
// if (!this.mounted) return;
|
||||
if (!this.mounted) return;
|
||||
if (!context.mounted) return;
|
||||
if (requireHardwareWalletConnection) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
await widget.onWalletLoaded.call(context);
|
||||
}
|
||||
unawaited(hideProgressText());
|
||||
} catch (e) {
|
||||
await ExceptionHandler.resetLastPopupDate();
|
||||
final err = e.toString();
|
||||
@@ -534,16 +570,25 @@ class WalletListBodyState extends State<WalletListBody> {
|
||||
conditionToDetermineIfToUse2FA:
|
||||
widget.walletListViewModel.shouldRequireTOTP2FAForAccessingWallet,
|
||||
);
|
||||
_loadingWallet = false;
|
||||
}
|
||||
|
||||
void changeProcessText(String text) {
|
||||
_progressBar = createBar<void>(text, context, duration: null)..show(context);
|
||||
try {
|
||||
if (_progressBar != null) {
|
||||
_progressBar!.dismiss();
|
||||
}
|
||||
_progressBar = createBar<void>(text, context, duration: null)
|
||||
..show(context);
|
||||
}catch(e){}
|
||||
}
|
||||
|
||||
Future<void> hideProgressText() async {
|
||||
await Future.delayed(Duration(milliseconds: 50), () {
|
||||
_progressBar?.dismiss();
|
||||
_progressBar = null;
|
||||
try {
|
||||
_progressBar?.dismiss();
|
||||
_progressBar = null;
|
||||
} catch (e) {}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
@@ -86,150 +88,154 @@ class CreatePinWelcomePage extends BasePage {
|
||||
|
||||
return PopScope(
|
||||
canPop: false,
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
content: Container(
|
||||
alignment: Alignment.center,
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(maxWidth: imageMaxWidth),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
clipBehavior: Clip.none,
|
||||
children: <Widget>[
|
||||
AspectRatio(
|
||||
aspectRatio: imageAspectRatio,
|
||||
child: FittedBox(
|
||||
child: CakeImageWidget(
|
||||
imageUrl: welcomeImage,
|
||||
child: SafeArea(
|
||||
top: false,
|
||||
bottom: Platform.isAndroid,
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
content: Container(
|
||||
alignment: Alignment.center,
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(maxWidth: imageMaxWidth),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
clipBehavior: Clip.none,
|
||||
children: <Widget>[
|
||||
AspectRatio(
|
||||
aspectRatio: imageAspectRatio,
|
||||
child: FittedBox(
|
||||
child: CakeImageWidget(
|
||||
imageUrl: welcomeImage,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: (imageMaxWidth / imageAspectRatio) * 0.57,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Text(
|
||||
S.current.welcome,
|
||||
style: Theme.of(context).textTheme.displayLarge?.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 60,
|
||||
letterSpacing: 2.5,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
S.current.to.toLowerCase(),
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
if (!isMoneroOnly) ...[
|
||||
SizedBox(width: 8),
|
||||
CakeImageWidget(
|
||||
height: 40,
|
||||
imageUrl: cakeLogoThemed,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
],
|
||||
Text(
|
||||
appTitle(context),
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 48),
|
||||
if (isMoneroOnly)
|
||||
Positioned(
|
||||
top: (imageMaxWidth / imageAspectRatio) * 0.57,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Text(
|
||||
appDescription(context),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyLarge
|
||||
?.copyWith(fontWeight: FontWeight.w500),
|
||||
S.current.welcome,
|
||||
style: Theme.of(context).textTheme.displayLarge?.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 60,
|
||||
letterSpacing: 2.5,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
if (!isMoneroOnly)
|
||||
SizedBox(height: 16),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
AnimatedTypingText(
|
||||
words: [S.current.payments, S.current.privacy, S.current.security],
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
cursorColor: Theme.of(context).colorScheme.primary,
|
||||
cursorHeight: 28,
|
||||
cursorWidth: 4,
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
Text(
|
||||
S.current.made_easy,
|
||||
S.current.to.toLowerCase(),
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
if (!isMoneroOnly) ...[
|
||||
SizedBox(width: 8),
|
||||
CakeImageWidget(
|
||||
height: 40,
|
||||
imageUrl: cakeLogoThemed,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
],
|
||||
Text(
|
||||
appTitle(context),
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
bottomSection: Column(
|
||||
children: [
|
||||
RichText(
|
||||
textAlign: TextAlign.center,
|
||||
text: TextSpan(
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
SizedBox(height: 48),
|
||||
if (isMoneroOnly)
|
||||
Text(
|
||||
appDescription(context),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyLarge
|
||||
?.copyWith(fontWeight: FontWeight.w500),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
if (!isMoneroOnly)
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
AnimatedTypingText(
|
||||
words: [S.current.payments, S.current.privacy, S.current.security],
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
cursorColor: Theme.of(context).colorScheme.primary,
|
||||
cursorHeight: 28,
|
||||
cursorWidth: 4,
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
Text(
|
||||
S.current.made_easy,
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: 'By continuing, you agree to our ',
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: 'Terms of Service',
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
decoration: TextDecoration.underline,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () => Navigator.pushNamed(context, Routes.readDisclaimer),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 24),
|
||||
child: PrimaryButton(
|
||||
key: ValueKey('create_pin_welcome_page_create_a_pin_button_key'),
|
||||
onPressed: () => Navigator.pushNamed(context, Routes.welcomeWallet),
|
||||
text: isWalletPasswordDirectInput ? S.current.set_up_a_wallet : S.current.set_a_pin,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
),
|
||||
bottomSection: Column(
|
||||
children: [
|
||||
RichText(
|
||||
textAlign: TextAlign.center,
|
||||
text: TextSpan(
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: 'By continuing, you agree to our ',
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: 'Terms of Service',
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
decoration: TextDecoration.underline,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () => Navigator.pushNamed(context, Routes.readDisclaimer),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
],
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 24),
|
||||
child: PrimaryButton(
|
||||
key: ValueKey('create_pin_welcome_page_create_a_pin_button_key'),
|
||||
onPressed: () => Navigator.pushNamed(context, Routes.welcomeWallet),
|
||||
text: isWalletPasswordDirectInput ? S.current.set_up_a_wallet : S.current.set_a_pin,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:cake_wallet/src/screens/yat/widgets/yat_bar.dart';
|
||||
import 'package:cake_wallet/src/screens/yat/widgets/yat_page_indicator.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lottie/lottie.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:cake_wallet/src/screens/yat/widgets/yat_bar.dart';
|
||||
import 'package:cake_wallet/src/screens/yat/widgets/yat_page_indicator.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lottie/lottie.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:cake_wallet/src/screens/yat/widgets/yat_bar.dart';
|
||||
import 'package:cake_wallet/src/screens/yat/widgets/yat_page_indicator.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lottie/lottie.dart';
|
||||
|
||||
@@ -29,6 +29,7 @@ class AddressTextField<T extends Currency> extends StatelessWidget {
|
||||
this.onPushPasteButton,
|
||||
this.onPushAddressBookButton,
|
||||
this.onPushAddressPickerButton,
|
||||
this.onEditingComplete,
|
||||
this.onSelectedContact,
|
||||
this.selectedCurrency,
|
||||
this.addressKey,
|
||||
@@ -49,6 +50,7 @@ class AddressTextField<T extends Currency> extends StatelessWidget {
|
||||
final Function(Uri)? onURIScanned;
|
||||
final List<AddressTextFieldOption> options;
|
||||
final FormFieldValidator<String>? validator;
|
||||
final VoidCallback? onEditingComplete;
|
||||
|
||||
final Color? buttonColor;
|
||||
final Color? fillColor;
|
||||
@@ -77,6 +79,7 @@ class AddressTextField<T extends Currency> extends StatelessWidget {
|
||||
borderWidth: borderWidth,
|
||||
hasUnderlineBorder: hasUnderlineBorder,
|
||||
key: addressKey,
|
||||
onEditingComplete: onEditingComplete,
|
||||
enableIMEPersonalizedLearning: false,
|
||||
keyboardType: TextInputType.visiblePassword,
|
||||
onFieldSubmitted: (_) => FocusScope.of(context).unfocus(),
|
||||
@@ -242,7 +245,7 @@ class AddressTextField<T extends Currency> extends StatelessWidget {
|
||||
|
||||
Future<void> _presetAddressBookPicker(BuildContext context) async {
|
||||
final contact = await Navigator.of(context)
|
||||
.pushNamed(Routes.pickerAddressBook, arguments: selectedCurrency);
|
||||
.pushNamed(Routes.pickerAddressBook, arguments: [selectedCurrency, false]);
|
||||
|
||||
if (contact is ContactBase) {
|
||||
controller?.text = contact.address;
|
||||
|
||||
@@ -47,6 +47,7 @@ class BaseTextFormField extends StatelessWidget {
|
||||
this.suffixIconConstraints,
|
||||
super.key,
|
||||
this.suffixText,
|
||||
this.borderRadius = const BorderRadius.all(Radius.circular(18)), this.onEditingComplete,
|
||||
});
|
||||
|
||||
final TextEditingController? controller;
|
||||
@@ -82,6 +83,7 @@ class BaseTextFormField extends StatelessWidget {
|
||||
final bool? autocorrect;
|
||||
final bool? enableSuggestions;
|
||||
final void Function(String)? onChanged;
|
||||
final VoidCallback? onEditingComplete;
|
||||
final EdgeInsetsGeometry? contentPadding;
|
||||
final bool? alignLabelWithHint;
|
||||
final FloatingLabelBehavior? floatingLabelBehavior;
|
||||
@@ -92,6 +94,7 @@ class BaseTextFormField extends StatelessWidget {
|
||||
final void Function(String)? onFieldSubmitted;
|
||||
final bool hasUnderlineBorder;
|
||||
final double borderWidth;
|
||||
final BorderRadius borderRadius;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -110,6 +113,7 @@ class BaseTextFormField extends StatelessWidget {
|
||||
controller: controller,
|
||||
keyboardType: keyboardType,
|
||||
textInputAction: textInputAction,
|
||||
onEditingComplete: onEditingComplete,
|
||||
textAlign: textAlign,
|
||||
autovalidateMode: autovalidateMode,
|
||||
obscureText: obscureText,
|
||||
@@ -147,44 +151,49 @@ class BaseTextFormField extends StatelessWidget {
|
||||
labelStyle: !hasUnderlineBorder ? placeholderTextStyle : null,
|
||||
border: hasUnderlineBorder
|
||||
? UnderlineInputBorder(
|
||||
borderRadius: borderRadius,
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outlineVariant,
|
||||
style: borderWidth == 0.0 ? BorderStyle.none : BorderStyle.solid,
|
||||
width: borderWidth,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
: OutlineInputBorder(borderRadius: borderRadius, borderSide: BorderSide.none),
|
||||
focusedBorder: hasUnderlineBorder
|
||||
? UnderlineInputBorder(
|
||||
borderRadius: borderRadius,
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outlineVariant,
|
||||
style: borderWidth == 0.0 ? BorderStyle.none : BorderStyle.solid,
|
||||
width: borderWidth,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
: OutlineInputBorder(borderRadius: borderRadius, borderSide: BorderSide.none),
|
||||
disabledBorder: hasUnderlineBorder
|
||||
? UnderlineInputBorder(
|
||||
borderRadius: borderRadius,
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outlineVariant,
|
||||
width: borderWidth,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
: OutlineInputBorder(borderRadius: borderRadius, borderSide: BorderSide.none),
|
||||
enabledBorder: hasUnderlineBorder
|
||||
? UnderlineInputBorder(
|
||||
borderRadius: borderRadius,
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outlineVariant,
|
||||
style: borderWidth == 0.0 ? BorderStyle.none : BorderStyle.solid,
|
||||
width: borderWidth,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
: OutlineInputBorder(borderRadius: borderRadius, borderSide: BorderSide.none),
|
||||
errorBorder: hasUnderlineBorder
|
||||
? UnderlineInputBorder(
|
||||
borderRadius: borderRadius,
|
||||
borderSide: BorderSide(color: Theme.of(context).colorScheme.error),
|
||||
)
|
||||
: null,
|
||||
: OutlineInputBorder(borderRadius: borderRadius, borderSide: BorderSide.none),
|
||||
),
|
||||
validator: validator,
|
||||
);
|
||||
|
||||
@@ -25,6 +25,7 @@ class BlockchainHeightWidget extends StatefulWidget {
|
||||
this.bitcoinMempoolAPIEnabled,
|
||||
required this.walletType,
|
||||
this.blockHeightTextFieldKey,
|
||||
this.heightController,
|
||||
}) : super(key: key);
|
||||
|
||||
final Function(int)? onHeightChange;
|
||||
@@ -33,6 +34,7 @@ class BlockchainHeightWidget extends StatefulWidget {
|
||||
final bool hasDatePicker;
|
||||
final bool isSilentPaymentsScan;
|
||||
final bool isMwebScan;
|
||||
final TextEditingController? heightController;
|
||||
final bool doSingleScan;
|
||||
final Future<bool>? bitcoinMempoolAPIEnabled;
|
||||
final Function()? toggleSingleScan;
|
||||
@@ -44,13 +46,15 @@ class BlockchainHeightWidget extends StatefulWidget {
|
||||
|
||||
class BlockchainHeightState extends State<BlockchainHeightWidget> {
|
||||
final dateController = TextEditingController();
|
||||
final restoreHeightController = TextEditingController();
|
||||
late final TextEditingController restoreHeightController;
|
||||
|
||||
int get height => _height;
|
||||
int _height = 0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
restoreHeightController = widget.heightController ?? TextEditingController();
|
||||
|
||||
restoreHeightController.addListener(() {
|
||||
if (restoreHeightController.text.isNotEmpty) {
|
||||
widget.onHeightOrDateEntered?.call(true);
|
||||
@@ -71,100 +75,96 @@ class BlockchainHeightState extends State<BlockchainHeightWidget> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () => FocusScope.of(context).unfocus(),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(top: 20.0, bottom: 10.0),
|
||||
child: BaseTextFormField(
|
||||
key: widget.blockHeightTextFieldKey,
|
||||
focusNode: widget.focusNode,
|
||||
controller: restoreHeightController,
|
||||
keyboardType: TextInputType.numberWithOptions(
|
||||
signed: false,
|
||||
decimal: false,
|
||||
),
|
||||
hintText: widget.isSilentPaymentsScan
|
||||
? S.of(context).silent_payments_scan_from_height
|
||||
: S.of(context).widgets_restore_from_blockheight,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
if (widget.hasDatePicker) ...[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 15, bottom: 15),
|
||||
child: Text(
|
||||
S.of(context).widgets_or,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(top: 20.0, bottom: 10.0),
|
||||
child: BaseTextFormField(
|
||||
key: widget.blockHeightTextFieldKey,
|
||||
focusNode: widget.focusNode,
|
||||
controller: restoreHeightController,
|
||||
keyboardType: TextInputType.numberWithOptions(
|
||||
signed: false,
|
||||
decimal: false,
|
||||
child: Container(
|
||||
child: InkWell(
|
||||
onTap: () => _selectDate(context),
|
||||
child: IgnorePointer(
|
||||
child: BaseTextFormField(
|
||||
controller: dateController,
|
||||
hintText: widget.isSilentPaymentsScan
|
||||
? S.of(context).silent_payments_scan_from_date
|
||||
: S.of(context).widgets_restore_from_date,
|
||||
),
|
||||
hintText: widget.isSilentPaymentsScan
|
||||
? S.of(context).silent_payments_scan_from_height
|
||||
: S.of(context).widgets_restore_from_blockheight,
|
||||
),
|
||||
),
|
||||
)
|
||||
))
|
||||
],
|
||||
),
|
||||
if (widget.hasDatePicker) ...[
|
||||
if (widget.isSilentPaymentsScan)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 15, bottom: 15),
|
||||
child: Text(
|
||||
S.of(context).widgets_or,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: Container(
|
||||
child: InkWell(
|
||||
onTap: () => _selectDate(context),
|
||||
child: IgnorePointer(
|
||||
child: BaseTextFormField(
|
||||
controller: dateController,
|
||||
hintText: widget.isSilentPaymentsScan
|
||||
? S.of(context).silent_payments_scan_from_date
|
||||
: S.of(context).widgets_restore_from_date,
|
||||
),
|
||||
),
|
||||
padding: EdgeInsets.only(top: 24),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
S.of(context).scan_one_block,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
))
|
||||
],
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 8),
|
||||
child: StandardSwitch(
|
||||
value: widget.doSingleScan,
|
||||
onTapped: () => widget.toggleSingleScan?.call(),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
if (widget.isSilentPaymentsScan)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 24),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
S.of(context).scan_one_block,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 8),
|
||||
child: StandardSwitch(
|
||||
value: widget.doSingleScan,
|
||||
onTapped: () => widget.toggleSingleScan?.call(),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 40, right: 40, top: 24),
|
||||
child: Text(
|
||||
widget.isSilentPaymentsScan
|
||||
? S.of(context).silent_payments_scan_from_date_or_blockheight
|
||||
: S.of(context).restore_from_date_or_blockheight,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
)
|
||||
]
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 40, right: 40, top: 24),
|
||||
child: Text(
|
||||
widget.isSilentPaymentsScan
|
||||
? S.of(context).silent_payments_scan_from_date_or_blockheight
|
||||
: S.of(context).restore_from_date_or_blockheight,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
)
|
||||
]
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'package:cake_wallet/core/amount_parsing_proxy.dart';
|
||||
import 'package:cake_wallet/core/address_validator.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/widgets/rounded_icon_button.dart';
|
||||
import 'package:cake_wallet/themes/core/theme_extension.dart';
|
||||
@@ -35,6 +37,7 @@ class ConfirmSendingBottomSheet extends BaseBottomSheet {
|
||||
required this.feeFiatAmount,
|
||||
required this.outputs,
|
||||
required this.walletType,
|
||||
this.amountParsingProxy,
|
||||
this.change,
|
||||
this.explanation,
|
||||
this.isOpenCryptoPay = false,
|
||||
@@ -71,10 +74,14 @@ class ConfirmSendingBottomSheet extends BaseBottomSheet {
|
||||
final CakePayBuyCardViewModel? cakePayBuyCardViewModel;
|
||||
final String? quantity;
|
||||
final String? explanation;
|
||||
final AmountParsingProxy? amountParsingProxy;
|
||||
|
||||
final bool showScrollbar;
|
||||
final ScrollController scrollController = ScrollController();
|
||||
|
||||
bool get showAddress => !outputs
|
||||
.any((e) => RegExp(AddressValidator.bolt11InvoiceMatcher).hasMatch(e.address.toLowerCase()));
|
||||
|
||||
@override
|
||||
Widget contentWidget(BuildContext context) {
|
||||
final itemTitleTextStyle = Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
@@ -113,7 +120,7 @@ class ConfirmSendingBottomSheet extends BaseBottomSheet {
|
||||
itemSubTitleTextStyle: itemSubTitleTextStyle,
|
||||
tileBackgroundColor: tileBackgroundColor,
|
||||
applyAddressFormatting: false,
|
||||
copyButton: true,
|
||||
copyButton: true,
|
||||
)),
|
||||
),
|
||||
if (explanation != null)
|
||||
@@ -127,7 +134,7 @@ class ConfirmSendingBottomSheet extends BaseBottomSheet {
|
||||
),
|
||||
StandardTile(
|
||||
itemTitle: amount,
|
||||
itemValue: '$amountValue ${currency.title}',
|
||||
itemValue: '$amountValue ${amountParsingProxy?.getCryptoSymbol(currency) ?? currency.title}',
|
||||
itemTitleTextStyle: itemTitleTextStyle,
|
||||
itemSubTitle: fiatAmountValue,
|
||||
itemSubTitleTextStyle: itemSubTitleTextStyle,
|
||||
@@ -145,48 +152,49 @@ class ConfirmSendingBottomSheet extends BaseBottomSheet {
|
||||
const SizedBox(height: 8),
|
||||
Column(
|
||||
children: [
|
||||
ListView.separated(
|
||||
padding: const EdgeInsets.only(top: 0),
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: outputs.length,
|
||||
separatorBuilder: (context, index) => const SizedBox(height: 8),
|
||||
itemBuilder: (context, index) {
|
||||
final bool isBatchSending = outputs.length > 1;
|
||||
final item = outputs[index];
|
||||
final contactName = item.parsedAddress.name;
|
||||
final isCakePayName = contactName == 'Cake Pay';
|
||||
final batchContactTitle =
|
||||
'${index + 1}/${outputs.length} - ${contactName.isEmpty ? 'Address' : contactName}';
|
||||
final _address = item.isParsedAddress ? item.extractedAddress : item.address;
|
||||
final _amount = item.cryptoAmount.replaceAll(',', '.') + ' ${currency.title}';
|
||||
return isBatchSending || (contactName.isNotEmpty && !isCakePayName)
|
||||
? ExpansionAddressTile(
|
||||
contactType: isOpenCryptoPay ? 'Open CryptoPay' : S.of(context).contact,
|
||||
name: isBatchSending ? batchContactTitle : contactName,
|
||||
address: _address,
|
||||
amount: _amount,
|
||||
walletType: walletType,
|
||||
isBatchSending: isBatchSending,
|
||||
itemTitleTextStyle: itemTitleTextStyle,
|
||||
itemSubTitleTextStyle: itemSubTitleTextStyle,
|
||||
tileBackgroundColor: tileBackgroundColor,
|
||||
)
|
||||
: AddressTile(
|
||||
itemTitle: isCakePayName
|
||||
? item.parsedAddress.profileName
|
||||
: S.of(context).address,
|
||||
imagePath: isCakePayName ? item.parsedAddress.profileImageUrl : null,
|
||||
itemTitleTextStyle: itemTitleTextStyle,
|
||||
walletType: walletType,
|
||||
amount: isCakePayName ? item.fiatAmount : _amount,
|
||||
address: _address,
|
||||
itemSubTitle: isCakePayName ? quantity : null,
|
||||
itemSubTitleTextStyle: itemSubTitleTextStyle,
|
||||
tileBackgroundColor: tileBackgroundColor,
|
||||
);
|
||||
},
|
||||
),
|
||||
if (showAddress)
|
||||
ListView.separated(
|
||||
padding: const EdgeInsets.only(top: 0),
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: outputs.length,
|
||||
separatorBuilder: (_, __) => const SizedBox(height: 8),
|
||||
itemBuilder: (context, index) {
|
||||
final isBatchSending = outputs.length > 1;
|
||||
final item = outputs[index];
|
||||
final contactName = item.parsedAddress.name;
|
||||
final isCakePayName = contactName == 'Cake Pay';
|
||||
final batchContactTitle =
|
||||
'${index + 1}/${outputs.length} - ${contactName.isEmpty ? 'Address' : contactName}';
|
||||
final _address = item.isParsedAddress ? item.extractedAddress : item.address;
|
||||
final _amount = '${item.cryptoAmount.replaceAll(',', '.')} ${amountParsingProxy?.getCryptoSymbol(currency) ?? currency.title}';
|
||||
return isBatchSending || (contactName.isNotEmpty && !isCakePayName)
|
||||
? ExpansionAddressTile(
|
||||
contactType: isOpenCryptoPay ? 'Open CryptoPay' : S.of(context).contact,
|
||||
name: isBatchSending ? batchContactTitle : contactName,
|
||||
address: _address,
|
||||
amount: _amount,
|
||||
walletType: walletType,
|
||||
isBatchSending: isBatchSending,
|
||||
itemTitleTextStyle: itemTitleTextStyle,
|
||||
itemSubTitleTextStyle: itemSubTitleTextStyle,
|
||||
tileBackgroundColor: tileBackgroundColor,
|
||||
)
|
||||
: AddressTile(
|
||||
itemTitle: isCakePayName
|
||||
? item.parsedAddress.profileName
|
||||
: S.of(context).address,
|
||||
imagePath: isCakePayName ? item.parsedAddress.profileImageUrl : null,
|
||||
itemTitleTextStyle: itemTitleTextStyle,
|
||||
walletType: walletType,
|
||||
amount: isCakePayName ? item.fiatAmount : _amount,
|
||||
address: _address,
|
||||
itemSubTitle: isCakePayName ? quantity : null,
|
||||
itemSubTitleTextStyle: itemSubTitleTextStyle,
|
||||
tileBackgroundColor: tileBackgroundColor,
|
||||
);
|
||||
},
|
||||
),
|
||||
if (change != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
@@ -194,7 +202,8 @@ class ConfirmSendingBottomSheet extends BaseBottomSheet {
|
||||
contactType: 'Change',
|
||||
name: S.of(context).send_change_to_you,
|
||||
address: change!.address,
|
||||
amount: change!.amount + ' ${currency.title}',
|
||||
amount:
|
||||
'${amountParsingProxy?.getDisplayCryptoString(change!.amount.toInt(), currency)} ${amountParsingProxy?.getCryptoSymbol(currency) ?? currency.title}',
|
||||
isBatchSending: true,
|
||||
walletType: walletType,
|
||||
itemTitleTextStyle: itemTitleTextStyle,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:cake_wallet/entities/generate_name.dart';
|
||||
import 'package:cake_wallet/evm/evm.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/utils/payment_request.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/generate_name.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/src/widgets/bottom_sheet/base_bottom_sheet_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
@@ -153,10 +153,10 @@ class _PaymentConfirmationContent extends StatelessWidget {
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
child: Image.asset(
|
||||
getCryptoCurrencyForWalletListItem(
|
||||
getCryptoCurrencyIconForWalletListItem(
|
||||
paymentViewModel.detectedWalletType!,
|
||||
chainId: paymentViewModel.detectedChainId,
|
||||
).iconPath!,
|
||||
),
|
||||
width: 32,
|
||||
height: 32,
|
||||
errorBuilder: (context, error, stackTrace) => Icon(
|
||||
@@ -192,10 +192,10 @@ class _PaymentConfirmationContent extends StatelessWidget {
|
||||
),
|
||||
] else ...[
|
||||
Image.asset(
|
||||
getCryptoCurrencyForWalletListItem(
|
||||
getCryptoCurrencyIconForWalletListItem(
|
||||
paymentViewModel.detectedWalletType!,
|
||||
chainId: paymentViewModel.detectedChainId,
|
||||
).iconPath!,
|
||||
),
|
||||
width: 118,
|
||||
height: 118,
|
||||
),
|
||||
|
||||
@@ -172,10 +172,10 @@ class SwapConfirmationContentState extends State<SwapConfirmationContent> {
|
||||
bottom: -4,
|
||||
right: -4,
|
||||
child: CakeImageWidget(
|
||||
imageUrl: getCryptoCurrencyForWalletListItem(
|
||||
imageUrl: getCryptoCurrencyIconForWalletListItem(
|
||||
widget.paymentFlowResult.walletType!,
|
||||
chainId: widget.paymentFlowResult.chainId,
|
||||
).iconPath!,
|
||||
),
|
||||
width: 16,
|
||||
height: 16,
|
||||
),
|
||||
@@ -212,6 +212,7 @@ class SwapConfirmationContentState extends State<SwapConfirmationContent> {
|
||||
return AmountValidator(
|
||||
isAutovalidate: true,
|
||||
currency: widget.exchangeViewModel.receiveCurrency,
|
||||
amountParsingProxy: widget.exchangeViewModel.amountParsingProxy,
|
||||
minValue: widget.exchangeViewModel.limits.min.toString(),
|
||||
maxValue: widget.exchangeViewModel.limits.max.toString(),
|
||||
).call(value);
|
||||
@@ -286,7 +287,7 @@ class SwapConfirmationContentState extends State<SwapConfirmationContent> {
|
||||
SwapConfirmationTextfield(
|
||||
key: ValueKey('swap_confirmation_bottomsheet_address_textfield_key'),
|
||||
isAddress: true,
|
||||
walletType: cryptoCurrencyToWalletType(widget.exchangeViewModel.receiveCurrency),
|
||||
walletType: cryptoCurrencyOrTokenToWalletType(widget.exchangeViewModel.receiveCurrency),
|
||||
hintText: 'Destination Address',
|
||||
focusNode: _addressFocus,
|
||||
controller: _addressController,
|
||||
@@ -439,7 +440,7 @@ class SwapConfirmationContentState extends State<SwapConfirmationContent> {
|
||||
|
||||
exchangeViewModel.receiveAddress = _addressController.text;
|
||||
exchangeViewModel.depositAddress = exchangeViewModel.wallet.walletAddresses.addressForExchange;
|
||||
exchangeViewModel.receiveAmount = _amountController.text;
|
||||
exchangeViewModel.setCanonicalReceiveAmount(_amountController.text);
|
||||
_amountFiatController.text = exchangeViewModel.receiveAmountFiatFormatted;
|
||||
exchangeViewModel.isReceiveAmountEntered = true;
|
||||
exchangeViewModel.isFixedRateMode = true;
|
||||
|
||||
@@ -112,6 +112,9 @@ class _SwapDetailsBottomSheetState extends State<SwapDetailsBottomSheet> {
|
||||
buttonAction: () {
|
||||
_showingFailureDialog = false;
|
||||
Navigator.of(popupContext).pop();
|
||||
if(mounted) {
|
||||
Navigator.of(context, rootNavigator: true).pop();
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
@@ -172,7 +175,7 @@ class _SwapDetailsBottomSheetState extends State<SwapDetailsBottomSheet> {
|
||||
onSingleActionButtonPressed: () {
|
||||
Navigator.of(bottomSheetContext).pop();
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
Navigator.of(context, rootNavigator: true).pop();
|
||||
}
|
||||
;
|
||||
},
|
||||
@@ -348,7 +351,7 @@ class _SwapDetailsContent extends StatelessWidget {
|
||||
const SizedBox(height: 4),
|
||||
AddressFormatter.buildSegmentedAddress(
|
||||
address: trade.payoutAddress ?? '',
|
||||
walletType: trade.to != null ? cryptoCurrencyToWalletType(trade.to!) : null,
|
||||
walletType: trade.to != null ? cryptoCurrencyOrTokenToWalletType(trade.to!) : null,
|
||||
evenTextStyle: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium!
|
||||
|
||||
@@ -97,9 +97,9 @@ class _WalletSwitcherContent extends StatelessWidget {
|
||||
child: Row(
|
||||
children: [
|
||||
Image.asset(
|
||||
getCryptoCurrencyForWalletListItem(
|
||||
getCryptoCurrencyIconForWalletListItem(
|
||||
wallet.type,
|
||||
).iconPath!,
|
||||
),
|
||||
width: 32,
|
||||
height: 32,
|
||||
),
|
||||
|
||||
@@ -12,6 +12,7 @@ class CakeImageWidget extends StatelessWidget {
|
||||
this.errorWidget,
|
||||
this.color,
|
||||
this.borderRadius = 24.0,
|
||||
this.colorFilter,
|
||||
});
|
||||
|
||||
final String? imageUrl;
|
||||
@@ -22,6 +23,7 @@ class CakeImageWidget extends StatelessWidget {
|
||||
final Widget? errorWidget;
|
||||
final Color? color;
|
||||
final double borderRadius;
|
||||
final ColorFilter? colorFilter;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -31,6 +33,8 @@ class CakeImageWidget extends StatelessWidget {
|
||||
|
||||
final isSvg = imageUrl!.toLowerCase().endsWith('.svg');
|
||||
final isAsset = imageUrl!.startsWith('assets/');
|
||||
final effectiveColorFilter = colorFilter ??
|
||||
(color != null ? ColorFilter.mode(color!, BlendMode.srcIn) : null);
|
||||
|
||||
Widget imageWidget;
|
||||
if (isAsset) {
|
||||
@@ -39,7 +43,7 @@ class CakeImageWidget extends StatelessWidget {
|
||||
imageUrl!,
|
||||
height: height,
|
||||
width: width,
|
||||
color: color,
|
||||
colorFilter: effectiveColorFilter,
|
||||
fit: fit ?? BoxFit.contain,
|
||||
)
|
||||
: Image.asset(
|
||||
@@ -55,7 +59,7 @@ class CakeImageWidget extends StatelessWidget {
|
||||
imageUrl!,
|
||||
height: height,
|
||||
width: width,
|
||||
color: color,
|
||||
colorFilter: effectiveColorFilter,
|
||||
fit: fit ?? BoxFit.contain,
|
||||
placeholderBuilder: (_) {
|
||||
return loadingWidget ?? const Center(child: CircularProgressIndicator());
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user