Compare commits

..
Author SHA1 Message Date
Volodymyr Kozieiev 6ac5f77678 component tests fixes 2024-08-23 16:59:36 +01:00
Volodymyr Kozieiev 895a8b9588 fix preview 2024-08-22 15:15:46 +01:00
Volodymyr Kozieiev 7076f365ee lint fixes 2024-08-22 15:15:45 +01:00
Volodymyr Kozieiev 94ab92d931 wip 2024-08-22 15:15:45 +01:00
Volodymyr Kozieiev 91cb714ea9 limits are not used externally, we rely on controlled input limit 2024-08-22 15:15:45 +01:00
Volodymyr Kozieiev c7bd607c5a Unnecessary duplication removed 2024-08-22 15:15:45 +01:00
Volodymyr Kozieiev f62afdac16 add conversion to controlled input 2024-08-22 15:15:44 +01:00
Volodymyr Kozieiev 41997d9851 incorrectly used available-limit property removed from send screen 2024-08-22 15:15:44 +01:00
Volodymyr Kozieiev 2ced6095b9 Remove unnecessary fields from token-input 2024-08-22 15:15:44 +01:00
Volodymyr Kozieiev ea1b01a352 token input basic refactoring 2024-08-22 15:15:43 +01:00
Volodymyr Kozieiev b7d89aa185 fixing review notes 2024-08-22 14:14:00 +01:00
Volodymyr Kozieiev fb25ccfc6a Fix conversion rounding on send page 2024-08-22 14:13:51 +01:00
Parvesh Monu 449b2808e6 fix: Not visible status bar on light theme (#21105)
The issue occurred because we updated the theme value after setting the root
(not before), which caused the root to be initialized with the old theme value.
2024-08-22 08:54:14 -03:00
Parvesh Monu 6bf1aecb6d Improve app theme naming and doc strings (#21107) 2024-08-22 10:55:05 +05:30
Ulises Manuel 4d26934949 feat(settings): Add Privacy Policy and metrics modal for the sharing usage data toggle (#21087)
* Open metrics modal when changing metrics in settings

* Move privacy policy & terms of use components under `status-im.common`

* Add a link to the Privacy Policy in the metrics modal

* Update privacy policy and terms of use content

* Remove extra function wrapper
2024-08-21 12:04:37 -06:00
Mohamed Javid 0f6aa8d401 Fix: app upgrade issues in wallet (#21104)
This commit:

- hides keypairs with empty origin in selection while creating an account
- hides remove account button for accounts with empty origin


Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2024-08-21 19:29:21 +05:30
Parvesh Monu 32ddf81a5e Rename utils.navigation to events-helper and add docstring to clarify namespace use case (#21093) 2024-08-21 14:26:48 +05:30
frankandAndrea Maria Piana 7b34a4fb1b fix_:parse route response (#21091)
Co-authored-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
2024-08-20 21:50:12 +08:00
Lungu Cristian 215e516b2b fix: dapp tx get-standard-crypto-format arg order (#21086) 2024-08-20 11:38:27 +03:00
Mohsen eecd1d50e4 [#19926] feat: allow for disabling testnet mode by pressing the banner (#20935) 2024-08-19 15:00:03 +03:00
Parvesh Monu 5a9279b232 Remove redundant visibility status update call (#21068) 2024-08-19 17:02:59 +05:30
Yevheniia Berdnyk 6a5e718cd3 e2e: fixes for communities tests 2024-08-18 23:13:00 +03:00
Siddarth Kumar 70009f80ab chore_: fix APK selection for run-android script (#21075)
This commit fixes the APK selection process when executing `make run-android`
2024-08-17 14:41:20 +05:30
Icaro Motta 8e3031f106 feat(wallet): Make wallet behave well when device is offline (#21067)
We make the wallet closer to being offline-first, that is, once data is loaded,
going offline won’t cause unnecessary data re-fetches which currently cause all
balances and data to stay loading forever or eventually balances end up zeroed.

Areas that may be impacted: read-only data displayed in the wallet and editing
accounts.

Fixes https://github.com/status-im/status-mobile/issues/21066
2024-08-17 00:48:01 -03:00
Andrea Maria Piana 3e0c31af9a Add caching of token amounts
https://github.com/status-im/status-go/compare/9cdfd6fb...9e7d1303
2024-08-16 20:01:29 +01:00
Brian Sztamfater e1bbefa60b feat(swap): setup swap ui (#20853)
Signed-off-by: Brian Sztamfater <brian@status.im>
2024-08-16 13:45:36 -03:00
frank 4575f83f49 fix_: scheduler panic after quick stop (#21064) 2024-08-16 22:19:25 +08:00
Shivek Khurana 37232eee7e 👓 Hide chain down toasts (#21055) 2024-08-15 19:00:31 +05:30
Sean HagstromandYevheniia Berdnyk 373ca6ec2d feature: require manual acceptance of terms-of-use and privacy-statement (#21010)
This change changes the initial onboarding screen to require users to manually accept the terms-of-use and privacy-statement before continuing to onboard into the app.

---------

Co-authored-by: Yevheniia Berdnyk <ie.berdnyk@gmail.com>
2024-08-15 10:41:12 +01:00
frankandIgor Sirotin fe28f44d4b chore_: remove passing node config when sync (#20949)
* chore_: remove passing node config when sync

https://github.com/status-im/status-go/compare/4a43b2b2...01f7787c

* chore_: remove passing deviceType

* chore_: update status-go

* fix_: remove calculating keystorePath and rootDataDir

* chore_: update status-go

* chore_: update status-go to v0.184.43

---------

Co-authored-by: Igor Sirotin <sirotin@status.im>
2024-08-15 14:14:54 +08:00
Andrea Maria Piana cd81519e9a drive stage from config
https://github.com/status-im/status-go/compare/a93c8572...1f7816dd
2024-08-14 20:10:19 +01:00
Sean Hagstrom d4a58ac2fb chore: add beta tags to communities and chat home screen titles (#21018)
* chore: add english translation for "Beta"

* tweak: add beta tags to communities and chat home screen titles

* fix: use smaller size for beta tags on chat and communities home screens
2024-08-14 16:47:15 +05:30
frank 75280c4db6 fix_:enable light client after sync (#21041) 2024-08-14 17:02:37 +08:00
Jamie Caprani 9a52339ea7 wallet(account): disable send and bridge options for assets with a 0 balance (#21033) 2024-08-12 17:26:40 +01:00
Mohsen d53bef385a [#20971] fix: app crash when collectible max limit is nan (#20992) 2024-08-09 21:39:07 +03:30
Parvesh Monu 35e71289dc Improve theme switch (#20984) 2024-08-09 16:42:49 +02:00
frank 0dfc8384e6 chore_: ignore sensitive fields of WalletConfig when logging (#20986) 2024-08-09 17:20:43 +08:00
Yevheniia Berdnyk 605e3a727d e2e: fixes for welcome button and more 2024-08-09 00:20:50 +03:00
Ulises Manuel 41e18993be fix(wallet): Remove redirection to send flow when the user scans an address QR code (#20979) 2024-08-08 08:19:41 -06:00
flexsurfer ce3f2b5895 Token-gated permissions are not displayed on community's homepage #20965 (#20976) 2024-08-08 12:16:38 +02:00
frank 3b4cbfa4bc fix_: enable light client and disable store confirmation for mobile v1 by default (#20953)
https://github.com/status-im/status-go/compare/cba3ac57...6581f75c
2024-08-07 21:48:10 +08:00
Yevheniia Berdnyk 9a3cdc4f8b e2e: fixes for community link and welcome button error 2024-08-06 18:58:31 +03:00
frankandSiddarth Kumar 1d84c30de3 chore:enable status proxy (#20931)
* feat_: enable status proxy

* chore: update jenkins lib

* chore: add missing env reference

---------

Co-authored-by: Siddarth Kumar <siddarthkay@gmail.com>
2024-08-06 07:19:36 -07:00
Jamie CapraniandYevheniia Berdnyk 8133c4c5f0 chore(wallet): feature flag adding watch only accounts (#20955)
* chore(wallet): feature flag adding watch only accounts

* e2e: disabled test for watch-only accounts

---------

Co-authored-by: Yevheniia Berdnyk <ie.berdnyk@gmail.com>
2024-08-05 10:08:05 -07:00
Yevheniia Berdnyk e21ece0f8d e2e: update for deep links tests 2024-08-05 17:57:35 +03:00
Sean Hagstrom 89828ba7fd chore: allow for overriding metro server port (#20202)
This change will now allow for customizing the port number when running the metro server. The environment variable `RCT_METRO_PORT` can now be set when executing commands like `make run-ios`, `make run ios-device`, and `make run-android`. Though, it should be noted that `make clean` may need to be ran before attempting to set or change `RCT_METRO_PORT` since the react-native app will have statically built code that references the value of RCT_METRO_PORT from compile time and not runtime.
2024-08-05 12:48:35 +01:00
Shivek Khurana 8215498ddb 📈 Capture onboarding funnel (#20926)
* 📈 Capture onboarding funnel

*  Faster lookup

*  Capture how many people enabled metrics

- It technically captures disabled too
- But we'll never know if someone disabled
- Because that info won't be transmitted

* ✏️ Fix tests

* 🧯 Fix lint
2024-08-02 22:19:58 +05:30
Ulises Manuel ae6205c874 fix(wallet): Fix input amount step skipped while sending a token (#20852)
* fix(wallet): Fix input amount step skipped while sending a token

* Add comment about ^:flush-dom metadata
2024-08-02 09:30:00 -06:00
Lungu Cristian 328f9686c9 Fix typed-data chainid check (#20943)
* fix: typed-data chainid check & toast on sign fail

* fix: parsing the chain-id and error-handling
2024-08-02 14:55:46 +03:00
Sean Hagstrom 9f412577d7 fix: remove mobile-data sync setting inside legacy settings (#20942) 2024-08-02 11:59:09 +01:00
Parvesh Monu 11bd340a94 fix 'Usage data' bottom sheet reappears after reopening the app if you swipe it down on onboarding (#20945) 2024-08-02 15:17:18 +05:30
Parvesh Monu 3c102b9797 fix Mobile app doesn't allow taking screenshots when the 'Block Screenshots' toggle is turned off (#20941) 2024-08-01 17:06:14 +05:30
Ulises Manuel bd4b7fc49d fix(wallet): Error while bridging tokens due to bonderFees (#20929) 2024-07-31 15:40:24 -06:00
Brian Sztamfater 29252336cc chore: bump react-native-camera-link version to Status fork to remove usage of ML Kit (#20891) 2024-07-31 17:54:21 -03:00
Brian Sztamfater 4b5b4f9099 fix(wallet): inverted values of receiver and sender when displaying bridging routes (#20893)
Signed-off-by: Brian Sztamfater <brian@status.im>
2024-07-31 17:02:03 -03:00
Yevheniia Berdnyk 28da6f8bfc e2e: fixes for wallet, community urls and more 2024-07-31 19:27:28 +03:00
Mohamed Javid e0fe0d2dbd fix(android): send and bridge ERC-20 tokens (#20924)
This commit adds the missing "encodeFunctionCall" method in Android

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2024-07-31 18:33:46 +05:30
frank 07cf34ad60 feat_: add fallback pairing support (#20921)
Creating this PR for regression testing the comparability of feature sync
between different versions(mainly current PR build vs v2.29) after adding
backend fallback pairing support. Because frontend haven't added the invocations
relate to fallback pairing API yet, so fallback pairing won't support with
current build.

relate status-go PR https://github.com/status-im/status-go/pull/5614

Testing notes expected: sync should work as before between different versions
like following: v2.29 <-> PR build PR build <-> recent desktop build

NOTE: It breaks with v1 of pairing, but that's ancient (last year I believe), so
safe to break.
2024-07-30 16:08:20 -03:00
Mohamed Javid 7c4c9a9fc0 fix_: use usd as default currency (#20916)
This commit uses "usd" currency as default for the fiat price calculation for the tokens. 

Every currency has a different format - decimal which we need to rely on a separate RPC to fetch currency format and do the calculation. So, this PR will change to use usd as the default for v2.30.

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2024-07-30 21:03:26 +05:30
Brian Sztamfater 59ceddbaad fix(wallet): fix bridge transactions (#20902)
* fix(wallet): fix bridge transactions

Signed-off-by: Brian Sztamfater <brian@status.im>

* add support for approve transactions

Signed-off-by: Brian Sztamfater <brian@status.im>

---------

Signed-off-by: Brian Sztamfater <brian@status.im>
2024-07-30 11:02:11 -03:00
mmilad75 99ccbc3383 Cover wallet send events with tests Part 2 #20411 #20533 (#20721) 2024-07-30 14:14:36 +02:00
Lungu Cristian 8c2d5398b0 Enabling WalletConnect feature flag (#20906)
* feat: removed wallet connect feature flag

* fix: show pending requests when logging out and in

* fix: don't show requests across (test/main)nets

* format: added env newlines

* fix: network state reset on network type change

* fix: reject typed-data if wrong chain-id

* chore: added logs for future debugging
2024-07-29 19:55:54 +03:00
mmilad75andJamie Caprani 67c83b13e3 fix(wallet): remove edit routes button in bridging (#20874)
Co-authored-by: Jamie Caprani <jamiecaprani@gmail.com>
2024-07-29 18:10:32 +02:00
Brian Sztamfater 11a84ba145 feat(wallet): disable complex routing (#20901) 2024-07-29 16:47:35 +01:00
Jamie Caprani 1f5bb579c9 chore(wallet): disable bridging on unsupported tokens (#20846) 2024-07-29 03:02:10 -07:00
Parvesh MonuandAndrea Maria Piana 4586f8007c Add toggle in advanced settings for mobile data
https://github.com/status-im/status-go/compare/4a43b2b2...9755b3de
Co-authored-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
2024-07-29 14:27:28 +05:30
yqrashawn 55c620e59d fix: create password for small screen (#20645)
* fix: create password for small screen

* feat: use floating button page

Signed-off-by: yqrashawn <namy.19@gmail.com>

* fix: use keyboard will show for ios

Signed-off-by: yqrashawn <namy.19@gmail.com>

* fix: safe area bottom on devices without physical home button

Signed-off-by: yqrashawn <namy.19@gmail.com>

* feat(create-password): tips and checkbox stick with confirm button

* fix: floating button blur after rebase

* fix(floating-button): absolute content avoid keyboard view

---------

Signed-off-by: yqrashawn <namy.19@gmail.com>
2024-07-29 13:41:15 +08:00
mmilad75 525609f0af Wallet Activity: transactions are not sorted by time #20808 (#20862) 2024-07-27 17:22:01 +02:00
Icaro Motta 90653955a6 chore(settings): Disable telemetry option (#20881) 2024-07-26 16:51:49 -03:00
John Ngei d27ab756d0 fix_:display group message using the new ui (#20787) 2024-07-26 21:14:04 +02:00
Siddarth Kumar c6a1db6334 ci: enable split apks & build only for arm64-v8a (#20683)
In this commit:
- we set `ANDROID_ABI_SPLIT` to `true`
- we set `ANDROID_ABI_INCLUDE` to `arm64-v8a` for debug & PR android builds
- release builds would still contain `armeabi-v7a;arm64-v8a` and there is no change for E2E android builds
- we point to relevant changes in `status-jenkins-lib` which also introduces a size check for this `apk`.
The agreed threshold is 100 MB.
2024-07-27 00:02:44 +05:30
flexsurfer 73777e052e Ensure keycard account can send transaction after upgrading from v1 to v2 #20552 (#20845) 2024-07-26 17:45:45 +02:00
Mohsen a6d3fc3742 [#20524] fix: the missed keypairs are shown in the key pair list screen (#20888) 2024-07-26 19:11:01 +03:30
Parvesh Monu a671c70838 fix broken screen and navigation when syncing fails (#20887) 2024-07-26 19:31:42 +05:30
Shivek Khurana a45991b6dc 🥅 Filter connected dapps based on testnet mode, reject proposals and requests gracefully (#20799)
* 🥅 Filter connected dapps based on testnet mode

- Fixes #20794

* 🥅 Remove map, just filter

* 💿 Rebase

*  Remove greedy fetch

* 🙅‍♀️ Properly reject proposals and requests

* 🎗️ Remove newline and move `set`

- `set` was applied at the wrong place here

* ✏️ Address review comments

* 👀 Read proposal to reject from state

* ◀️ Bring back network filtering

* 🧹 Cleanup

* ✏️ Move comment around

* 🎣 Use filter operable accounts helper

*  Add back events deleted during rebase

* 🧰 Fix Issue 2, Testnet sessions not visible

* 🖊️ Fix lint

* 🔗 Make testnet filtering more explicit

* 🥢 Use union instead of two subsets call

* ✏️ Fix lint

* 🔇 Undo changes that creeped in an unrelated ns
2024-07-26 18:24:53 +05:30
BalogunofAfrica 2e9fa22e4f feat: wallet router v2 (#20631)
* add v2 method

* rename v2 to get-suggested-route

* remove timestamp check on success

* handle async signal for suggestion

* fix stop get suggested routes

* address feedback

* rename get-suggest-route

* prefer lazy seq

* fix formatting

* update suggested routes success

* refactor get-in calls in start-get-suggested-routes

* move transformations to data store

* clean suggested routes immediately

* fix lint

* pass precision as ar

* change test name

* fix big number division error (issues 1,2)

* only trigger router fetch when there address (to/from)

* check response data for error response when routes received via signal

* update status-go

* fix: test failure

* update status go

* handle error message for generic errors
2024-07-26 12:23:09 +01:00
John Ngei 737d8c4d53 rename sub to fix error when requesting to join community (#20868) 2024-07-26 11:38:49 +02:00
Parvesh Monu 3aa7e103f7 Sync process is blocked on Enabled notifications screen (#20883) 2024-07-26 13:48:38 +05:30
Icaro Motta c1d2d44da4 perf: Fix app freeze after login (#20729)
We do a few things to reduce the initial load and make the app more responsive
after login. The scenario we are covering is a user who joined communities with
a large number of members and/or which contain token-gated channels with many
members.

- Related to https://github.com/status-im/status-mobile/issues/20283
- Related to https://github.com/status-im/status-mobile/issues/20285

- Optimize how we convert a community from JS to CLJS. Community members and
  chat members are no longer transformed to CLJS, they are kept as JS. Read more
  details below.
- Delay processing lower-priority events by creating a third login phase. The
  goal is to not put on the same queue we process communities less important
  events, like fetching the count of unread notifications. Around 15 events
  could be delayed without causing trouble (and this further prevent a big chain
  of more events to be dispatched right after login).
- Tried to use re-frame's flush-dom metadata, but removed due to uncertainty,
  check out the discussion:
  https://github.com/status-im/status-mobile/pull/20729#discussion_r1683047969
  Use re-frame’s support for the flush-dom metadata whenever a signal arrives.
  According to the official documentation, this should tell re-frame to only
  process the event after the UI has been updated. It’s hard to say if this
  makes any difference, but the theory is sound.
- Reduce the amount of data returned to the subscription that renders a list of
  communities. We were returning too much, like all members, chats, token
  permissions, etc.

Other things I fixed or improved along the way:

- Because members are now stored as JS, I took the opportunity to fix how
  members are sorted when they are listed.
- Removed a few unused subs.
- Configured oops to not throw during development (in production the behavior is
  to never throw). This means oops is now safe to be used instead of interop
  that can mysteriously fail in advanced compilation.
- Show compressed key instead of public key in member list for the account
  currently logged in.

Technical details

The number one reason affecting the freeze after login was coming from
converting thousands of members inside communities and also because we were
doing it in an inefficient way using clojure.walk/stringify-keys. We shouldn't
also transform that much data on the client as the parent issue created by
flexsurfer correctly recommends. Ever since PR
https://github.com/status-im/status-mobile/pull/20414 was merged, status-go
doesn't return members in open channels, which greatly helps, for example, to
load the Status community. The problem still exists for communities with
token-gated channels with many members.

The current code in develop does something quite inefficient: it fetches the
communities, then transforms them recursively with js->clj and keywordizes keys,
then transforms again all the potentially thousands of member IDs back to
strings. This PR changes this. We now shallowly convert a community and ignore
members because they can grow too fast. From artificial benchmarks simulating
many members in token-gated channels, or communities with thousands of members,
the improvement is noticeable.

You will only really notice improvements if you have spectated or joined a
community with 1000+ members and/or a community with many token-gated channels,
each containing perhaps hundreds of members.

What's the ideal solution?

We should consider removing community members and channel members from the
community entity returned by status-go entirely. The members should be a
separate resource and paginated so that the client doesn't need to worry
about the number of members, for the most part.
2024-07-25 21:23:08 -03:00
Yevheniia Berdnyk 0fed8113d1 e2e: updated testnet switching and added one test into smoke 2024-07-25 23:46:13 +03:00
Ulises Manuel 53c35cb554 fix(wallet): Linear gradient exception on invalid colors for watched account cards (#20854)
* Create a linear-gradient wrapper to avoid crashes and notify color errors

* Fix references to the flamingo color

* Add customization color fallback
2024-07-25 13:35:46 -06:00
Mohamed Javid be82365542 chore(settings)_: Remove testnet toggle from legacy advanced settings (#20875)
This commit removes two toggles from legacy advanced settings

- "Testnet mode" - duplicate toggle as the user can switch to testnet from wallet settings
- "Enable Goerli as test network" - Goerli is depreciated and wallet services use Sepolia testnet as default. 

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2024-07-25 21:41:57 +05:30
Mohamed Javid eae8a65597 feat(wallet)_: Add beta info box in activity tab (#20873)
This commit adds the beta info box with links to chain explorers to the activity tab in the wallet.

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2024-07-25 21:28:30 +05:30
Lungu Cristian fe54a25a3a fix: not clearing network & web3-wallet on logout (#20886) 2024-07-25 18:37:26 +03:00
Alexander 15a4219ef4 Reject wallet-connect request by dragging the modal down (#20763) (#20836)
* Reject wallet-connect request by dragging the modal down (#20763)

* Fix
2024-07-25 16:51:10 +02:00
Alexander 2ffbdac89e WalletConnect show expired toast (#20857)
* WalletConnect show expired toast

* Fixes

* Fix

* Post-rebase fix
2024-07-25 16:11:31 +02:00
Parvesh Monu 402eb8397d fix Issue with scrolling WalletConnect transaction on Android (#20867) 2024-07-25 18:22:17 +05:30
Lungu Cristian ff88049a08 Fix WalletConnect header alignment on Android (#20860)
* fix: header align and max width for summary-tag

* fix: standard-title show ellipse when text is long

* fix: summary-tag long name overflowing
2024-07-25 14:21:19 +03:00
Lungu Cristian cee21241d4 WalletConnect no internet edge-cases (#20826)
* feat: only initialize wc if internet online

* feat: no internet toast for session establishment

* feat: no internet banner on session requests

* feat: reloading walletconnect on connection change

* fix: re-initialize only when previously failed to

* fix: removed legacy net-info ns

* ref: renamed :network-status to :network/status

* ref: moved network subs to own "category"

* fix: device network fx args

* fix: tests & showing persisted dapps when offline

* fix: addressed review comments

* fix: rebase issues

* fix: linting

* fix: usage of web3-wallet (#20864)

* fix: moved networks to contextx and renaming

* ref: moved building supported namespaces into fx
2024-07-25 11:21:31 +03:00
Icaro Motta 60ad7c8a29 chore(tests): New match-strict? cljs.test directive (#20825)
Equality checks in tests using = give a bad experience by default on test
failures containing nested data structures. We use the cljs.test directive
match? from matcher-combinators library to help compare nested structures. The
problem with match? is that its default matcher for maps (embeds) can be too
permissive, and this causes surprises.

Here we upgrade matcher-combinators to latest, where a new matcher called
nested-equals is available. This matcher won't allow extra keys in maps. This
matcher eliminates the need for manually adding nested equals matchers as we
have to do currently.

- Upgrades matcher-combinators from 3.8.8 to 3.9.1 (latest as of 2024-07-19)

What changes?

When asserting in tests, we now have the option to use match-strict? or match?.
Both directives are available by integrating with cljs.test. The code
implementing the new match-strict? directive was 100% copied from the library
matcher-combinators because we need to wrap the expected value ourselves with
matcher-combinators.matchers/nested-equals. It's ugly code, but it's how we can
integrate with cljs.test/assert-expr.
2024-07-24 23:06:41 -03:00
Mohamed Javid 4989c92780 fix_: Adding own address as saved addresses (#20839)
This commit:

- prevents the user from saving their wallet address as the saved address
- fixes button not capturing taps when the keyboard is open in saved address flows

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2024-07-24 19:46:59 +05:30
89 changed files with 1219 additions and 918 deletions
+3 -2
View File
@@ -436,10 +436,11 @@ android-clean: ##@prepare Clean Gradle state
rm -rf ~/.gradle rm -rf ~/.gradle
android-ports: export FLOWSTORM_PORT := 7722 android-ports: export FLOWSTORM_PORT ?= 7722
android-ports: export TARGET := android-sdk android-ports: export TARGET := android-sdk
android-ports: export RCT_METRO_PORT ?= 8081
android-ports: ##@other Add proxies to Android Device/Simulator android-ports: ##@other Add proxies to Android Device/Simulator
adb reverse tcp:8081 tcp:8081 && \ adb reverse tcp:$(RCT_METRO_PORT) tcp:$(RCT_METRO_PORT) && \
adb reverse tcp:3449 tcp:3449 && \ adb reverse tcp:3449 tcp:3449 && \
adb reverse tcp:4567 tcp:4567 && \ adb reverse tcp:4567 tcp:4567 && \
adb reverse tcp:$(FLOWSTORM_PORT) tcp:$(FLOWSTORM_PORT) && \ adb reverse tcp:$(FLOWSTORM_PORT) tcp:$(FLOWSTORM_PORT) && \
+1 -1
View File
@@ -1 +1 @@
2.30.0 2.29.0
+3 -3
View File
@@ -1115,7 +1115,7 @@ PODS:
- React-perflogger (= 0.73.5) - React-perflogger (= 0.73.5)
- ReactNativeAudioToolkit (2.0.3): - ReactNativeAudioToolkit (2.0.3):
- React - React
- ReactNativeCameraKit (14.0.0-beta13): - ReactNativeCameraKit (14.0.0-beta15):
- React-Core - React-Core
- ReactNativeNavigation (7.39.0): - ReactNativeNavigation (7.39.0):
- HMSegmentedControl - HMSegmentedControl
@@ -1485,7 +1485,7 @@ SPEC CHECKSUMS:
FBLazyVector: 56e0e498dbb513b96c40bac6284729ba4e62672d FBLazyVector: 56e0e498dbb513b96c40bac6284729ba4e62672d
FBReactNativeSpec: 146c741a3f40361f6bc13a4ba284678cbedb5881 FBReactNativeSpec: 146c741a3f40361f6bc13a4ba284678cbedb5881
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
glog: 530710e7949eb12c82670bd09e946becf50a3c2a glog: 36c15c01d70ed395c6e4f3619f98a23ee415b07a
hermes-engine: 1d1835b2cc54c381909d94d1b3c8e0a2f1a94a0e hermes-engine: 1d1835b2cc54c381909d94d1b3c8e0a2f1a94a0e
HMSegmentedControl: 34c1f54d822d8308e7b24f5d901ec674dfa31352 HMSegmentedControl: 34c1f54d822d8308e7b24f5d901ec674dfa31352
Keycard: ac6df4d91525c3c82635ac24d4ddd9a80aca5fc8 Keycard: ac6df4d91525c3c82635ac24d4ddd9a80aca5fc8
@@ -1551,7 +1551,7 @@ SPEC CHECKSUMS:
React-utils: 987a4526a2fc0acdfaf87888adfe0bf9d0452066 React-utils: 987a4526a2fc0acdfaf87888adfe0bf9d0452066
ReactCommon: 2947b0bffd82ea0e58ca7928881152d4c6dae9af ReactCommon: 2947b0bffd82ea0e58ca7928881152d4c6dae9af
ReactNativeAudioToolkit: de9610f323e855ac6574be8c99621f3d57c5df06 ReactNativeAudioToolkit: de9610f323e855ac6574be8c99621f3d57c5df06
ReactNativeCameraKit: d95d3e19c514526a234d9f93c6db7e7f10eef9ea ReactNativeCameraKit: 71343efc1256720184ce980f164c7eedb78d5c16
ReactNativeNavigation: 679d8c8bf3b8ae98d285bce810964d10da85dbdb ReactNativeNavigation: 679d8c8bf3b8ae98d285bce810964d10da85dbdb
RNCAsyncStorage: c913ede1fa163a71cea118ed4670bbaaa4b511bb RNCAsyncStorage: c913ede1fa163a71cea118ed4670bbaaa4b511bb
RNCClipboard: 60fed4b71560d7bfe40e9d35dea9762b024da86d RNCClipboard: 60fed4b71560d7bfe40e9d35dea9762b024da86d
@@ -36,11 +36,6 @@ class NetworkManager(private val reactContext: ReactApplicationContext) : ReactC
@ReactMethod @ReactMethod
fun inputConnectionStringForBootstrapping(connectionString: String, configJSON: String, callback: Callback) { fun inputConnectionStringForBootstrapping(connectionString: String, configJSON: String, callback: Callback) {
val jsonConfig = JSONObject(configJSON) val jsonConfig = JSONObject(configJSON)
val receiverConfig = jsonConfig.getJSONObject("receiverConfig")
val keyStorePath = utils.pathCombine(utils.getNoBackupDirectory(), "/keystore")
receiverConfig.put("keystorePath", keyStorePath)
receiverConfig.getJSONObject("nodeConfig").put("rootDataDir", utils.getNoBackupDirectory())
utils.executeRunnableStatusGoMethod( utils.executeRunnableStatusGoMethod(
{ Statusgo.inputConnectionStringForBootstrapping(connectionString, jsonConfig.toString()) }, { Statusgo.inputConnectionStringForBootstrapping(connectionString, jsonConfig.toString()) },
callback callback
@@ -35,21 +35,7 @@ RCT_EXPORT_METHOD(inputConnectionStringForBootstrapping:(NSString *)cs
configJSON:(NSString *)configJSON configJSON:(NSString *)configJSON
callback:(RCTResponseSenderBlock)callback) { callback:(RCTResponseSenderBlock)callback) {
NSData *configData = [configJSON dataUsingEncoding:NSUTF8StringEncoding]; NSString *result = StatusgoInputConnectionStringForBootstrapping(cs, configJSON);
NSError *error;
NSMutableDictionary *configDict = [NSJSONSerialization JSONObjectWithData:configData options:NSJSONReadingMutableContainers error:&error];
NSMutableDictionary *receiverConfig = configDict[@"receiverConfig"];
NSMutableDictionary *nodeConfig = receiverConfig[@"nodeConfig"];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *rootUrl =[[fileManager URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask] lastObject];
NSURL *multiaccountKeystoreDir = [rootUrl URLByAppendingPathComponent:@"keystore"];
NSString *keystoreDir = multiaccountKeystoreDir.path;
NSString *rootDataDir = rootUrl.path;
[receiverConfig setValue:keystoreDir forKey:@"keystorePath"];
[nodeConfig setValue:rootDataDir forKey:@"rootDataDir"];
NSString *modifiedConfigJSON = [Utils jsonStringWithPrettyPrint:NO fromDictionary:configDict];
NSString *result = StatusgoInputConnectionStringForBootstrapping(cs, modifiedConfigJSON);
callback(@[result]); callback(@[result]);
} }
+2
View File
@@ -17,6 +17,7 @@
hermesEnabled ? lib.getEnvWithDefault "ORG_GRADLE_PROJECT_hermesEnabled" "true", hermesEnabled ? lib.getEnvWithDefault "ORG_GRADLE_PROJECT_hermesEnabled" "true",
buildUrl ? lib.getEnvWithDefault "ORG_GRADLE_PROJECT_buildUrl" null, buildUrl ? lib.getEnvWithDefault "ORG_GRADLE_PROJECT_buildUrl" null,
statusGoSrcOverride ? lib.getEnvWithDefault "STATUS_GO_SRC_OVERRIDE" null, statusGoSrcOverride ? lib.getEnvWithDefault "STATUS_GO_SRC_OVERRIDE" null,
reactMetroPort ? lib.getEnvWithDefault "RCT_METRO_PORT" 8081,
# If APKs should be split based on architectures # If APKs should be split based on architectures
androidAbiSplit ? lib.getEnvWithDefault "ANDROID_ABI_SPLIT" "true", androidAbiSplit ? lib.getEnvWithDefault "ANDROID_ABI_SPLIT" "true",
# Android architectures to build for # Android architectures to build for
@@ -158,6 +159,7 @@ in stdenv.mkDerivation rec {
--no-build-cache \ --no-build-cache \
--parallel \ --parallel \
-Dmaven.repo.local='${deps.gradle}' \ -Dmaven.repo.local='${deps.gradle}' \
-PreactNativeDevServerPort=${toString reactMetroPort} \
assemble${gradleBuildType} assemble${gradleBuildType}
''; '';
in in
+62 -38
View File
@@ -1,54 +1,78 @@
Last updated: 2023-09-22 Status Software - Privacy Policy
Last update: 16 August 2024
This Status Software - Privacy Policy (“Privacy Policy”) is intended to inform users of Status approach to privacy in respect of Status Software. In this regard, if you are using Status Software, this Privacy Policy applies to you.
Under the relevant data protection legislation, we are under certain obligations if we process any personal data when you use Status Software. Personal data means all information by which a person can be directly or indirectly identified, in line with the definitions of the General Data Protection Regulation (GDPR), the Swiss Federal Act on Data Protection of June 19, 1992 (DPA) (as amended from time to time) and its ordinances, and other relevant legislation on the protection of personal data. When we refer to privacy legislation in this Privacy Policy we mean GDPR and all such relevant legislation.
This Privacy Policy describes how we process the (limited) amount of personal data that we collect when you use Status Software. We also inform you about your rights and choices that you have in respect of any personal data we process.
If you do not agree with this Privacy Policy or any part of it, please do not use Status Software. If you are already using Status Software, we ask that you immediately stop using Status Software.
Who we are Who we are
Status is developing a set of open source projects that use peer-to-peer technologies to help people transact securely, communicate freely, and organise with confidence. Anyone participating in these projects helps to build technology and tools that empowers people to advance their own sovereign communities. Status is developing a set of open source projects that use peer-to-peer technologies to help people transact securely, communicate freely, and organise with confidence. Anyone participating in these projects helps to build technology and tools that empowers people to advance their own sovereign communities.
Whenever Status or we” or “us” or any similar variation, are used in these terms, were referring to Status Research & Development GmbH, a Swiss company with its registered office at Baarerstrasse 10, Zug, Switzerland, and includes its “representatives”, which means Status affiliates, directors, officers, employees, agents and any other representatives of Status. For the purposes of these terms, “representatives” also includes Status core contributors without prejudice to the other legal categories mentioned. Whenever "Status" or "we" is used in this Privacy Policy, we're referring to Status Research & Development GmbH, a Swiss company with its registered offices at Baarerstrasse 10, 6302 Zug Switzerland. Our contact information can be found on our website and at the end of this Privacy Policy.
Status does not provide any services, such as financial services, to users of Status Software or any third party. Status is not an intermediary, agent, advisor, or custodian, and does not owe you any fiduciary duty. Status does not provide any services, such as financial services, to users of Status Software or any third party. Status is not an intermediary, agent, advisor, or custodian, and does not owe you any fiduciary duty.
Status Software Status Software
At Status, we strive to develop open source software that can serve as a secure communication tool that helps to uphold human rights. Status Software is specifically designed to facilitate the free flow of information, protect the right to private, secure communications and promote the sovereignty of individuals. At Status, we strive to develop open source software that can serve as a secure communication tool that helps to uphold human rights. Status Software is specifically designed to facilitate the free flow of information, protect the right to private, secure communications and promote the sovereignty of individuals.
Status Software is composed of a secure messaging tool, a crypto wallet, and a Web 3 browser integrated together. The software developed by Status in this regard is simply called “Status Software” and you can find more details about its development on Status GitHub page. Status Software is composed of a secure messaging tool, a crypto wallet, and a Web 3 browser that are integrated together. Any software developed by Status in this regard is simply called “Status Software” and you can find more details about its development here.
Status limits its processing of personal data from your use of Status Software
Status uses an open source, peer-to-peer protocol with end-to-end encryption and metadata suppression, which by design, means that Status (and any third party) is unable to and does not collect, store, own, control or have any visibility or means of access to your identity, Wallet, browsing information, any user private keys, Digital Assets, messages, content, history of interactions, user accounts or any other user information.
Notwithstanding the above, Status processes personal data on a limited basis from users of Status Software and the specific types of information we process will depend on how you use Status Software. As such, we only collect and process personal data in the following instances:
Usage Data: Where you opt-in to share usage data, we collect and/or briefly process certain data about your interactions with Status Software. Adhering to the principle of privacy by design, this option is off by default. The situations where we collect usage data and the type of personal data we process are as follows:
Network Behaviour: Status Software utilises a private, censorship resistant, peer-to-peer messaging protocol called Waku (https://waku.org/). To allow us to understand the performance, usage patterns, and reliability of the Waku protocol, we collect non-personally identifiable information such as the number of messages sent to you, connected and discovered peers, the rate of successfully sent messages, type of connection to peers, and details about your OS, Status Software application version and bandwidth usage. Such usage data is linked to a randomly generated peer ID associated with your instance of Status Software, which is a unique identifier used for the duration of your interaction with Status Software and generated with each restart of the Status Software. Such usage data will be kept for only as long as necessary to fulfil the aforementioned purposes and in any event, no longer than thirty (30) days and it will be deleted thereafter.
Analytics: Status also uses privacy-focused analytics to collect trends and insights about Status Software users. The usage data consists of personal data which we briefly process, that includes your IP address, universally unique identifiers of your device (UUID), and logs of actions, including button presses and screen visits, during your interactions with Status Software. Such usage data will be kept for only as long as necessary to fulfil the aforementioned purposes and it will be deleted thereafter.
We process any personal data collected in the context of usage data based on your consent when you choose to opt-in. If you no longer wish to provide us any further usage data, you can opt-out at any time by disabling these functions.
Proxy Server: When you use Status Software, it will automatically interact with a server (“Proxy Server”) that we control and has been implemented to improve the performance of Status Software. The Proxy Server is hosted on a number of reputable third party cloud providers (see section 4 of this Privacy Policy). As part of the implementation of the Proxy Server, certain information, including personal data will be shared from the users instance of Status Software and incidentally processed by us by way of the Proxy Server, which include the following:
the users IP address: your IP address will be processed when third party requests are run through the Proxy Server. This however removes your individual device details and IP address through the external requests made from the Proxy Server to the third party service provider (e.g. Infura); and
the users public address (also known as a wallet address): your public address is temporarily processed in the Proxy Server by us when making API calls to third party service providers in order to facilitate certain user activities on Status Software (for instance, transactions conducted using the Wallet).
Apart from your IP address, we do not retain any of the above personal data (or any other information) in line with the principle of data minimisation. The IP address will be kept for only as long as necessary to fulfil the aforementioned purposes and in any event, no longer than fourteen (14) days and it will be deleted thereafter.
We process the above personal data for the purposes of facilitating the technical operation of Status Software and optimising the functionality and users experience of Status Software. We have a legitimate interest in processing this personal data for these purposes.
Personal data sharing with third party service providers
We share personal data with third party service providers in the context of fulfilling the above purposes in which we collect and process personal data. We have contracted such third party service providers to provide their services and act as data processors on our behalf and are only permitted to process personal data in accordance with our instructions.
Third party service providers we engage and services we utilise from them include:
DigitalOcean: Providing hosting services in relation to the Proxy Server;
Alibaba Cloud: Providing hosting services in relation to the Proxy Server;
Google Cloud: Providing hosting services in relation to the Proxy Server;and
MixPanel: Providing analytics services in relation to the Usage Data.
Our role in your privacy Third party collection and processing of personal data
In addition to our limited collection of personal data, third parties might collect or process personal data as a result of Status Software making use of certain features or to provide certain content. To the extent you interact with such third party content or features, their respective privacy policies will apply.
This Privacy Statement applies to you as a user of Status Software. We do note however the following:
Cookies: We do not set any cookies for the use of Status Software. However, the Web 3 browser embedded within Status Software technically supports the use of cookies set by third party websites. Status is not responsible for nor is it able to influence whether such cookies are set by such third party websites and you should consult the relevant privacy policies of such third party websites.
Under the relevant data protection legislation, we would be under certain obligations if we process any personal data when you use Status Software. Personal data means all information by which a person can be directly or indirectly identified, in line with the definitions of the General Data Protection Regulation (GDPR), the Swiss Federal Act on Data Protection of June 19, 1992 (DPA) (as amended from time to time) and its ordinances, and other relevant legislation on the protection of personal data. When we refer to privacy legislation in this Privacy Statement, we mean GDPR and all such relevant legislation. Phone number and social media handles: When generating an account on Status Software, you will have the option to utilise your phone number and certain social media handles such as your X (formerly known as Twitter) account, to generate an account. While this information is not shared with Status and Status has no access to it, third party service providers might receive it to authenticate your ownership of such accounts. Using your phone number or social media handle, such as on X, will allow you to find your contacts who are also using Status Software and it will help your contacts to find you. This will allow them to discover and potentially interact with you, if you accept their request to connect, so that you can start building up your network of contacts on Status Software. Please note that this is not required for you to access or utilise Status Software, and you can always choose to generate an account anonymously without utilising your phone number or social media handle.
Web 3: An inherent feature of Web 3 is its transparency, particularly in the context of blockchain networks. This means that your public key and wallet address will be visible to others when you engage in transactions on such networks and that third parties may be able to (and for the avoidance of doubt, not through the use of Status Software) connect your public key and wallet address to your identity and determine the Digital Assets you own in your Wallet. You should also be aware that entries on blockchain networks are practically immutable, which means that they generally cannot be deleted or modified by anyone, including Status, even if the transaction turns out to have been made in error or otherwise.
However, since we do not collect or process any of your personal data when you use Status Software, we have decided to inform you of the following instead:
- Ethereum is a public blockchain:
Ethereum is the community-run technology powering the cryptocurrency ether (ETH) and thousands of decentralized applications (DApps). Status Software provides a mobile portal and streamlined access to Ethereums growing ecosystem of decentralized applications.
The Ethereum public network is accessible to anyone in the world with an internet connection. Anyone can read or create transactions on a public blockchain and validate the transactions being executed. Therefore, information you share on the Ethereum blockchain is public, whether or not you use Status Software.
You will also be able to create (and join) open and public communities on Status Software that are accessible to other users. We note that content in these open and public communities will be accessible to all other users of Status Software. Nonetheless, all content will still be protected by the same end-to-end encryption utilised in Status Software.
Ethereum Name Service (ENS) allows Ethereum addresses to be replaced by custom text-based names. Anyone can register a stateofus.eth username by staking the required Status Network Tokens (SNT) from within the profile tab on Status Software. When you register an ENS name with a given wallet address, that address becomes associated with the username, reducing the privacy of that account.
- Offering secure, private messaging:
When generating an account, a randomisation process is started on your own device that generates a key pair. Status does not have visibility of or access to your private keys. You will then be given display name options to choose from (derived from this key pair). Your display name will only be shared if you choose to share it by chatting with Status or other users.
Within Status Software, you can create a list of trusted contacts based on the chat keys you choose to trust. Your trusted contacts are also stored locally on your own device. This means that Status has no access to your contact list and does not collect or process any personal data in this respect.
On Status Software, you can exchange messages with other users, including photos and audio messages. Only the recipient of a message can decrypt the message by opening it on their own device. This means that Status can never access any user messages in private chats.
Messaging on Status Software uses an open source, peer-to-peer protocol with end-to-end encryption and metadata suppression, to protect your messages from third parties (including Status). End-to-end encryption means that only the sender and recipient of a message can read its contents, and no one else. This protocol, known as Waku, relies on a distributed network of nodes instead of a centralised server which means that there is no central party or server from which messages can be intercepted, modified or blocked. Learn more about Waku and how it works on Wakus website.
- A quick bite on cookies:
We do not set any cookies for the use of Status Software. However, the Web 3.0 browser embedded within Status Software technically supports the use of cookies set by third party websites. Status is not responsible for nor is it able to influence whether such cookies are set by such third party websites and you should consult the relevant privacy policies of such third party websites.
Changes to Status Privacy Statement Security measures we take in respect of Status Software
It is unlikely that this Privacy Statement will change because we do not intend to collect or process your personal data, ever. That said, we reserve the right to modify or replace any part of this Privacy Statement at any time and in our sole discretion. As a general approach, Status takes data security seriously and the measures we take as an organisation. In respect of Status Software, we implement a variety of security measures that are reasonably designed to maintain the safety of your personal data when it is shared with us.
Exporting personal data outside the European Union and Switzerland
While it is not intended that Status will export your personal data outside the European Union or Switzerland, Status is obliged to protect the privacy of such personal data if it is exported outside these areas and it will only be processed in countries or by parties that provide an adequate level of protection as deemed by Switzerland or the European Commission. Otherwise, Status will use specific forms of contractual clauses to ensure such personal data is provided the same protection as required in Switzerland or Europe. The transmission of personal data outside the European Union and Switzerland will always occur in conformity with applicable privacy legislation.
Your choices and rights
As explained in this Privacy Policy, Status limits its collection and processing of your personal data. Nonetheless, you still have certain choices and rights in respect of the personal data which we do collect and process. As laid out in relevant privacy legislation, you have the right to:
Ask us to correct or update your personal data (where possible);
Ask us to remove your personal data from our systems;
Ask us for a copy of your personal data, which may also be transferred to another data controller at your request;
Withdraw your consent to process your personal data (only if consent was asked for a processing activity), which only affects processing activities that are based on your consent and doesn't affect the validity of such processing activities before you have withdrawn your consent;
Object to the processing of your personal data; and
File a complaint with the Federal Data Protection and Information Commissioner (FDPIC), if you believe that your personal data has been processed unlawfully.
If you have any questions about the Status Software Privacy Statement, please contact us at legal@status.im or through the appropriate means of communication indicated on Status website. Once we receive your request, we might require you to verify your identity such that we can appropriately respond to your request and we will do so in line with any applicable mandatory deadlines. Please contact us with the relevant request at: legal@status.im.
Third party websites
Within Status Software, you might come across links to third party websites. These third party sites will often have separate and independent privacy policies. We therefore have no responsibility or liability for the content and activities of these linked sites. Please refer to these privacy policies of these third party websites when you use such websites.
The Privacy Policy might change
We may modify or replace any part of this Privacy Policy. Please check on Status Software periodically for any changes. The new Privacy Policy will be effective immediately upon it being placed in Status Software .
This document is CC-BY-SA. Contact Information
To the extent that you have any questions about the Privacy Policy, please email us at legal@status.im.
This document is licensed under CC-BY-SA.
+8 -11
View File
@@ -1,4 +1,6 @@
Last updated: 2023-09-22 Status Terms of Use
Last updated: 14 August 2024
1. Introduction 1. Introduction
@@ -81,20 +83,15 @@ Status Software also includes a Web 3 browser through which you may access and i
3. Privacy 3. Privacy
Status uses an open source, peer-to-peer protocol with end-to-end encryption and metadata suppression, which by design, means that Status (and any third party) is unable to and does not collect, store, own, control or have any visibility or means of access to your identity, Wallet, browsing information, any user private keys, Digital Assets, messages, content, history of interactions, transactions, user accounts or any other user information. Status uses an open source, peer-to-peer protocol with end-to-end encryption and metadata suppression, which by design, means that Status (and any third party) is unable to and does not collect, store, own, control or have any visibility or means of access to your identity, Wallet, browsing information, any user private keys, Digital Assets, messages, content, history of interactions, user accounts or any other user information.
Therefore, Status does not (and cannot) monetise any users data or content, such as messages or browser information and Status does have any interest in doing so. Therefore, Status does not (and cannot) monetise any users data or content, such as messages or browser information and Status does not have any interest in doing so.
When generating an account on Status Software, you will have the option to utilise your phone number and certain social media handles such as your Twitter account, to generate an account. While this information is not shared with Status and Status has no access to it, third party service providers may receive it to authenticate your ownership of such accounts. Using your phone number or social media handle, such as on X (formerly known as Twitter), will allow you to find your contacts who are also using Status Software and it will help your contacts to find you. This will allow them to discover and potentially interact with you, if you accept their request to connect, so that you can start building up your network of contacts on Status Software. Please note that this is not required for you to access or utilise Status Software, and you can always choose to generate an account anonymously without utilising your phone number or social media handle. Status however processes a limited amount of user data to ensure the technical operation of the Status Software, enhance its functionality, and improve the user experience. For more details on this limited process of user data, please refer to our Privacy Policy.
In addition, you may opt in to submit certain technical information (also referred to as telemetry) to assist Status with the development of the Status Software, particularly to measure message reliability and bandwidth usage. In any event this technical information is anonymised by Status Software and also protected by end-to-end encryption. We note that even anonymised data can be vulnerable to being potentially compromised, such as by correlation attacks. You should opt in to submit this technical information only if you are willing to accept these risks.
You acknowledge that an inherent feature of Web 3 is its transparency, particularly in the context of blockchain networks. This means that your public key and wallet address will be visible to others when you engage in transactions on such networks and that third parties may be able to (and for the avoidance of doubt, not through the use of Status Software) connect your public key and wallet address to your identity and determine the Digital Assets you own in your Wallet. You should also be aware that entries on blockchain networks are practically immutable, which means that they generally cannot be deleted or modified by anyone, including Status, even if the transaction turns out to have been made in error or otherwise.
If you use third party services offered through Status Software, your privacy may not be protected and you will be subject to the privacy terms and conditions of such third parties. Please refer to our full terms regarding third party services below. If you use third party services offered through Status Software, your privacy may not be protected and you will be subject to the privacy terms and conditions of such third parties. Please refer to our full terms regarding third party services below.
Learn more about how we protect your data on Status Software by reading the Status Privacy Statement and relevant security page on the Status website.
4. Using third party services and interacting with Web 3 4. Using third party services and interacting with Web 3
@@ -103,9 +100,9 @@ When using Status Software, you may access or use third party products, services
Your interactions with Web 3 could include many aspects, such as interacting with smart contracts that reside on blockchain networks, for example to swap Digital Assets or use (crypto) bridges, or deploying your own smart contracts through Status Community Spaces. You may also interact with and participate in peer-to-peer networks which could vary in their level of decentralisation, ranging from more centralised networks controlled by a single party to networks governed by decentralised communities. Your interactions with Web 3 could include many aspects, such as interacting with smart contracts that reside on blockchain networks, for example to swap Digital Assets or use (crypto) bridges, or deploying your own smart contracts through Status Community Spaces. You may also interact with and participate in peer-to-peer networks which could vary in their level of decentralisation, ranging from more centralised networks controlled by a single party to networks governed by decentralised communities.
You acknowledge when you use Third Party Services or interact with Web 3, that Status does not control or operate any Third Party Service or Web 3. These are products, services or tools that are respectively provided by others or may be operating autonomously, and not by Status. When you use Third Party Services or interact with Web 3, you acknowledge that Status does not control or operate any Third Party Service or Web 3. These products, services, or tools are either controlled, operated, owned, or licensed by the third party providing them, or they may operate autonomously, independently of Status or any other party.
Please note that when you use Third Party Services or interact with Web 3 that there may be applicable terms and conditions, including privacy policies which govern your use of Third Party Services or interaction with Web 3. In case of interactions with Web 3, such as decentralised exchanges, there may be in some cases no applicable terms and conditions, and the relevant software code will govern your interactions. Please note that using Third Party Services or interacting with Web 3 might be subject to specific terms and conditions, including privacy policies, set by the third-party providers. These providers might exclude and disclaim all liability regarding their Third Party Services and are not responsible or liable for your use of Status Software. It is crucial that you read and understand the applicable terms and conditions, including privacy policies, of these Third Party Services to be aware of the implications of their use. In case of interactions with Web 3, such as decentralised exchanges, there might be in some cases no applicable terms and conditions, and the relevant software code will govern your interactions.
Please also note that bridges are used to move tokens from one blockchain network to another. When you use a bridge, for example in the course of sending or bridging tokens, Status utilises an algorithm that aims to determine the lowest service fee which the user may incur at the time of a bridges use. This algorithm relies on information extracted directly from the relevant blockchain networks. Status is not responsible for the accuracy and correctness of any information that these algorithms may extract from such networks Please also note that bridges are used to move tokens from one blockchain network to another. When you use a bridge, for example in the course of sending or bridging tokens, Status utilises an algorithm that aims to determine the lowest service fee which the user may incur at the time of a bridges use. This algorithm relies on information extracted directly from the relevant blockchain networks. Status is not responsible for the accuracy and correctness of any information that these algorithms may extract from such networks
+1 -1
View File
@@ -6,7 +6,7 @@ if pgrep -f 'shadow-cljs watch mobile' > /dev/null; then
exit 1 exit 1
fi fi
if pgrep -f 'react-native start' > /dev/null; then if pgrep -f "react-native start --port=${RCT_METRO_PORT:-8081}" > /dev/null; then
echo "Error: make run-metro is already running in another terminal" >&2 echo "Error: make run-metro is already running in another terminal" >&2
echo "Please close that terminal before running this command." >&2 echo "Please close that terminal before running this command." >&2
exit 1 exit 1
+1 -1
View File
@@ -13,7 +13,7 @@ export BUILD_TYPE=debug
# Install the APK on running emulator or android device. # Install the APK on running emulator or android device.
installAndLaunchApp() { installAndLaunchApp() {
adb install -r ./result/app-arm64-v8a-debug.apk > "${ADB_INSTALL_LOG_FILE}" 2>&1 adb install -r "./result/app-${ANDROID_ABI_INCLUDE}-debug.apk" > "${ADB_INSTALL_LOG_FILE}" 2>&1
"${GIT_ROOT}/scripts/wait-for-metro-port.sh" 2>&1 "${GIT_ROOT}/scripts/wait-for-metro-port.sh" 2>&1
# connected android devices need this port to be exposed for metro # connected android devices need this port to be exposed for metro
adb reverse "tcp:${RCT_METRO_PORT}" "tcp:${RCT_METRO_PORT}" adb reverse "tcp:${RCT_METRO_PORT}" "tcp:${RCT_METRO_PORT}"
+2 -2
View File
@@ -1,8 +1,8 @@
#!/usr/bin/env bash #!/usr/bin/env bash
if pgrep -f 'react-native start' > /dev/null; then if pgrep -f "react-native start --port=${RCT_METRO_PORT:-8081}" > /dev/null; then
echo "Info: metro is already running in another terminal" echo "Info: metro is already running in another terminal"
else else
echo "Info: starting a new metro terminal" echo "Info: starting a new metro terminal"
react-native start --reset-cache react-native start --port=${RCT_METRO_PORT:-8081} --reset-cache
fi fi
+1 -1
View File
@@ -4,7 +4,7 @@ set -euo pipefail
TIMEOUT=10 # Metro should not take this long to start. TIMEOUT=10 # Metro should not take this long to start.
while [ "${TIMEOUT}" -gt 0 ]; do while [ "${TIMEOUT}" -gt 0 ]; do
if ! lsof -i:8081 &> /dev/null; then if ! lsof -i:"${RCT_METRO_PORT:-8081}" &> /dev/null; then
echo "." echo "."
sleep 1 sleep 1
((TIMEOUT--)) ((TIMEOUT--))
+2 -2
View File
@@ -99,9 +99,9 @@
(rf/defn system-theme-mode-changed (rf/defn system-theme-mode-changed
{:events [:system-theme-mode-changed]} {:events [:system-theme-mode-changed]}
[{:keys [db] :as cofx} _] [{:keys [db] :as cofx} _]
(let [current-theme-type (get-in cofx [:db :profile/profile :appearance])] (let [appearance-type (get-in cofx [:db :profile/profile :appearance])]
(when (and (multiaccounts.model/logged-in? db) (when (and (multiaccounts.model/logged-in? db)
(= current-theme-type status-im.constants/theme-type-system)) (= appearance-type status-im.constants/appearance-type-system))
{:dispatch [:theme/switch]}))) {:dispatch [:theme/switch]})))
(defn- on-biometric-auth-fail (defn- on-biometric-auth-fail
@@ -30,7 +30,7 @@
[{:keys [db] :as cofx} {:keys [auth-method logout?]}] [{:keys [db] :as cofx} {:keys [auth-method logout?]}]
(let [key-uid (get-in db [:profile/profile :key-uid])] (let [key-uid (get-in db [:profile/profile :key-uid])]
(rf/merge cofx (rf/merge cofx
{:dispatch [:init-root :progress] {:dispatch [:update-theme-and-init-root :progress]
:effects.shell/reset-state nil :effects.shell/reset-state nil
:hide-popover nil :hide-popover nil
::logout nil ::logout nil
@@ -107,11 +107,12 @@
(assoc network-tag-props (assoc network-tag-props
:status :status
(if error? :error :default))]]) (if error? :error :default))]])
[text/text (when fiat-value
{:size :paragraph-2 [text/text
:style style/fiat-amount {:size :paragraph-2
:weight :medium} :style style/fiat-amount
(str currency-symbol fiat-value)]]) :weight :medium}
(str currency-symbol fiat-value)])])
(when loading? (when loading?
[rn/view {:style (style/row-2-loader theme)}])]] [rn/view {:style (style/row-2-loader theme)}])]]
(when (and (not= status :loading) (= type :pay) show-approval-label?) (when (and (not= status :loading) (= type :pay) show-approval-label?)
@@ -6,16 +6,20 @@
(h/describe "Wallet: Token Input" (h/describe "Wallet: Token Input"
(h/test "Token label renders" (h/test "Token label renders"
(h/render-with-theme-provider [token-input/view (h/render-with-theme-provider [token-input/view
{:token :snt {:token-symbol :snt
:currency :eur :currency-symbol :snt
:currency-symbol "€" :on-swap nil
:conversion 1}]) :error? false
:value "1"
:converted-value "10"}])
(h/is-truthy (h/get-by-text "SNT"))) (h/is-truthy (h/get-by-text "SNT")))
(h/test "Amount renders" (h/test "Amount renders"
(h/render-with-theme-provider [token-input/view (h/render-with-theme-provider [token-input/view
{:token :snt {:token-symbol :snt
:currency :eur :currency-symbol :snt
:currency-symbol "€" :on-swap nil
:conversion 1}]) :error? false
:value "1"
:converted-value "€0.00"}])
(h/is-truthy (h/get-by-text "€0.00")))) (h/is-truthy (h/get-by-text "€0.00"))))
@@ -4,15 +4,14 @@
[:=> [:=>
[:catn [:catn
[:props [:props
[:map [:map {:closed true}
[:token {:optional true} [:maybe [:or :string :keyword]]] [:token-symbol [:maybe [:or :string :keyword]]]
[:currency {:optional true} [:maybe [:or :string :keyword]]] [:currency-symbol [:maybe [:or :string :keyword]]]
[:error? {:optional true} [:maybe :boolean]] [:hint-component {:optional true} [:maybe :schema.common/hiccup]]
[:title {:optional true} [:maybe :string]] [:on-token-press {:optional true} [:maybe fn?]]
[:conversion {:optional true} [:maybe :double]] [:on-swap [:maybe fn?]]
[:show-keyboard? {:optional true} [:maybe :boolean]] [:container-style {:optional true} [:maybe :map]]
[:networks {:optional true} [:error? [:maybe :boolean]]
[:maybe [:sequential [:map [:source [:maybe :schema.common/image-source]]]]]] [:value [:maybe :string]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]] [:converted-value [:maybe :string]]]]]
[:value {:optional true} [:maybe :string]]]]]
:any]) :any])
@@ -25,9 +25,11 @@
(defn input-container (defn input-container
[window-width] [window-width]
{:width (- window-width 120) {:width (- window-width 120)
:margin-left 8 :margin-left 8
:margin-right 8}) :margin-right 8
:flex-direction :row
:align-items :flex-end})
(def text-input-dimensions (def text-input-dimensions
(-> typography/heading-1 (-> typography/heading-1
@@ -65,6 +67,6 @@
:justify-content :space-between :justify-content :space-between
:align-items :center}) :align-items :center})
(defn fiat-amount (defn converted-amount
[theme] [theme]
{:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)}) {:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)})
+53 -123
View File
@@ -1,57 +1,15 @@
(ns quo.components.wallet.token-input.view (ns quo.components.wallet.token-input.view
(:require (:require
[clojure.string :as string] [clojure.string :as string]
[oops.core :as oops]
[quo.components.buttons.button.view :as button] [quo.components.buttons.button.view :as button]
[quo.components.dividers.divider-line.view :as divider-line] [quo.components.dividers.divider-line.view :as divider-line]
[quo.components.markdown.text :as text] [quo.components.markdown.text :as text]
[quo.components.tags.network-tags.view :as network-tag]
[quo.components.utilities.token.view :as token] [quo.components.utilities.token.view :as token]
[quo.components.wallet.token-input.schema :as component-schema] [quo.components.wallet.token-input.schema :as component-schema]
[quo.components.wallet.token-input.style :as style] [quo.components.wallet.token-input.style :as style]
[quo.theme :as quo.theme] [quo.theme :as quo.theme]
[react-native.core :as rn] [react-native.core :as rn]
[schema.core :as schema] [schema.core :as schema]))
[utils.number :as number]))
(defn fiat-format
[currency-symbol num-value conversion]
(str currency-symbol (.toFixed (* num-value conversion) 2)))
(defn crypto-format
[num-value conversion crypto-decimals token]
(str (number/remove-trailing-zeroes
(.toFixed (/ num-value conversion) (or crypto-decimals 2)))
" "
(string/upper-case (or (clj->js token) ""))))
(defn calc-value
[{:keys [crypto? currency-symbol token value conversion crypto-decimals]}]
(let [num-value (if (string? value)
(or (parse-double value) 0)
value)]
(if crypto?
(fiat-format currency-symbol num-value conversion)
(crypto-format num-value conversion crypto-decimals token))))
(defn- data-info
[{:keys [theme token crypto-decimals conversion networks title crypto? currency-symbol amount
error?]}]
[rn/view {:style style/data-container}
[network-tag/view
{:networks networks
:title title
:status (when error? :error)}]
[text/text
{:size :paragraph-2
:weight :medium
:style (style/fiat-amount theme)}
(calc-value {:crypto? crypto?
:currency-symbol currency-symbol
:token token
:value amount
:conversion conversion
:crypto-decimals crypto-decimals})]])
(defn- token-name-text (defn- token-name-text
[theme text] [theme text]
@@ -62,91 +20,63 @@
(string/upper-case (or (clj->js text) ""))]) (string/upper-case (or (clj->js text) ""))])
(defn input-section (defn input-section
[{:keys [on-change-text value value-internal set-value-internal on-selection-change [{:keys [token-symbol on-token-press value error? on-swap currency-symbol]}]
on-token-press]}] (let [theme (quo.theme/use-theme)
(let [input-ref (atom nil) window-width (:width (rn/get-window))]
set-ref #(reset! input-ref %) [rn/pressable
focus-input #(when-let [ref ^js @input-ref] {:style {:width "100%"
(.focus ref)) :flex-direction :row}
controlled-input? (some? value) :on-press on-token-press}
handle-on-change-text (fn [v] [token/view
(when-not controlled-input? {:token token-symbol
(set-value-internal v)) :size :size-32}]
(when on-change-text [rn/view {:style (style/input-container window-width)}
(on-change-text v))) [rn/text-input
handle-selection-change (fn [^js e] {:style (style/text-input theme error?)
(when on-selection-change :placeholder-text-color (style/placeholder-text theme)
(-> e :placeholder "0"
(oops/oget "nativeEvent.selection") :keyboard-type :numeric
(js->clj :keywordize-keys true) :editable false
(on-selection-change))))] :value value}]
(fn [{:keys [token customization-color show-keyboard? crypto? currency value error? selection [token-name-text theme currency-symbol]]
handle-on-swap allow-selection?] [button/button
:or {show-keyboard? true {:icon true
allow-selection? true}}] :icon-only? true
(let [theme (quo.theme/use-theme) :size 32
window-width (:width (rn/get-window))] :on-press #(when on-swap (on-swap))
[rn/pressable :type :outline
{:style {:width "100%" :accessibility-label :reorder}
:flex-direction :row} :i/reorder]]))
:on-press on-token-press}
[token/view
{:token token
:size :size-32}]
[rn/view {:style (style/input-container window-width)}
[rn/pressable
{:style style/text-input-container
:on-press focus-input}
[rn/text-input
(cond-> {:style (style/text-input theme error?)
:placeholder-text-color (style/placeholder-text theme)
:auto-focus true
:ref set-ref
:placeholder "0"
:keyboard-type :numeric
:on-change-text handle-on-change-text
:selection-color customization-color
:show-soft-input-on-focus show-keyboard?
:on-selection-change handle-selection-change
:selection (clj->js selection)
:editable allow-selection?}
controlled-input? (assoc :value value)
(not controlled-input?) (assoc :default-value value-internal))]
[token-name-text theme (if crypto? token currency)]]]
[button/button
{:icon true
:icon-only? true
:size 32
:on-press handle-on-swap
:type :outline
:accessibility-label :reorder}
:i/reorder]]))))
(defn- view-internal (defn- view-internal
[{:keys [container-style value on-swap] :as props}] [{:keys [token-symbol
(let [theme (quo.theme/use-theme) value
width (:width (rn/get-window)) on-token-press
[value-internal set-value-internal] (rn/use-state nil) error?
[crypto? set-crypto] (rn/use-state true) container-style
handle-on-swap (rn/use-callback on-swap
(fn [] converted-value
(set-crypto (not crypto?)) hint-component
(when on-swap (on-swap (not crypto?)))) currency-symbol]}]
[crypto? on-swap])] (let [theme (quo.theme/use-theme)
width (:width (rn/get-window))]
[rn/view {:style (merge (style/main-container width) container-style)} [rn/view {:style (merge (style/main-container width) container-style)}
[rn/view {:style style/amount-container} [rn/view {:style style/amount-container}
[input-section [input-section
(assoc props {:theme theme
:value-internal value-internal :token-symbol token-symbol
:theme theme :on-token-press on-token-press
:set-value-internal set-value-internal :value value
:handle-on-swap handle-on-swap :error? error?
:crypto? crypto?)]] :on-swap on-swap
:currency-symbol currency-symbol}]]
[divider-line/view {:container-style (style/divider theme)}] [divider-line/view {:container-style (style/divider theme)}]
[data-info [rn/view {:style style/data-container}
(assoc props hint-component
:theme theme [text/text
:crypto? crypto? {:size :paragraph-2
:amount (or value value-internal))]])) :weight :medium
:style (style/converted-amount theme)}
converted-value]]]))
(def view (schema/instrument #'view-internal component-schema/?schema)) (def view (schema/instrument #'view-internal component-schema/?schema))
+4 -2
View File
@@ -19,8 +19,10 @@
:text-color (colors/resolve-color :danger theme)}}) :text-color (colors/resolve-color :danger theme)}})
(defn- banner (defn- banner
[{:keys [text type second-banner? colors-map]}] [{:keys [text type second-banner? colors-map on-press]}]
[rn/view (when second-banner? {:style style/second-banner-wrapper}) [rn/pressable
{:on-press #(when on-press (on-press))
:style (when second-banner? style/second-banner-wrapper)}
[hole-view/hole-view [hole-view/hole-view
{:style (style/hole-view (get-in colors-map [type :background-color])) {:style (style/hole-view (get-in colors-map [type :background-color]))
:holes (if second-banner? :holes (if second-banner?
+3 -2
View File
@@ -126,7 +126,8 @@
[reanimated/view [reanimated/view
{:style (reanimated/apply-animations-to-style {:style (reanimated/apply-animations-to-style
{:opacity bg-opacity} {:opacity bg-opacity}
{:flex 1 :background-color colors/neutral-100-opa-70})}]] {:flex 1
:background-color (if shell? colors/neutral-100-opa-60 colors/neutral-100-opa-70)})}]]
;; sheet ;; sheet
[gesture/gesture-detector {:gesture sheet-gesture} [gesture/gesture-detector {:gesture sheet-gesture}
[reanimated/view [reanimated/view
@@ -138,7 +139,7 @@
[quo/blur [quo/blur
{:style style/shell-bg {:style style/shell-bg
:blur-radius (or blur-radius 20) :blur-radius (or blur-radius 20)
:blur-amount 32 :blur-amount 15
:blur-type :transparent :blur-type :transparent
:overlay-color :transparent}]]) :overlay-color :transparent}]])
(when selected-item (when selected-item
@@ -1,6 +1,8 @@
(ns status-im.common.controlled-input.utils (ns status-im.common.controlled-input.utils
(:require (:require
[clojure.string :as string])) [clojure.string :as string]
[status-im.contexts.wallet.common.utils :as wallet-utils]
[utils.money :as money]))
(def init-state (def init-state
{:value "" {:value ""
@@ -14,7 +16,7 @@
(defn numeric-value (defn numeric-value
[state] [state]
(or (parse-double (input-value state)) 0)) (money/bignumber (input-value state)))
(defn input-error (defn input-error
[state] [state]
@@ -26,23 +28,29 @@
(defn upper-limit (defn upper-limit
[state] [state]
(:upper-limit state)) (money/bignumber (:upper-limit state)))
(defn lower-limit (defn lower-limit
[state] [state]
(:lower-limit state)) (money/bignumber (:lower-limit state)))
(defn upper-limit-exceeded? (defn upper-limit-exceeded?
[state] [state]
(and (let [num-value (numeric-value state)]
(upper-limit state) (and
(> (numeric-value state) (upper-limit state)))) (upper-limit state)
(if (money/bignumber? num-value)
(money/greater-than (numeric-value state) (upper-limit state))
false))))
(defn lower-limit-exceeded? (defn- lower-limit-exceeded?
[state] [state]
(and (let [num-value (numeric-value state)]
(lower-limit state) (and
(< (numeric-value state) (lower-limit state)))) (lower-limit state)
(if (money/bignumber? num-value)
(money/less-than (numeric-value state) (lower-limit state))
false))))
(defn- recheck-errorness (defn- recheck-errorness
[state] [state]
@@ -75,13 +83,11 @@
(defn increase (defn increase
[state] [state]
(let [new-val (inc (numeric-value state))] (set-input-value state (str (money/add (numeric-value state) 1))))
(set-input-value state (str new-val))))
(defn decrease (defn decrease
[state] [state]
(let [new-val (dec (numeric-value state))] (set-input-value state (str (money/add (numeric-value state) -1))))
(set-input-value state (str new-val))))
(def ^:private not-digits-or-dot-pattern (def ^:private not-digits-or-dot-pattern
#"[^0-9+\.]") #"[^0-9+\.]")
@@ -130,3 +136,29 @@
(defn empty-value? (defn empty-value?
[state] [state]
(string/blank? (:value state))) (string/blank? (:value state)))
(defn- fiat->crypto
[value conversion-rate]
(-> value
(money/fiat->crypto conversion-rate)
(wallet-utils/cut-crypto-decimals-to-fit-usd-cents conversion-rate)))
(defn- crypto->fiat
[value conversion-rate]
(-> value
(money/crypto->fiat conversion-rate)
(wallet-utils/cut-fiat-balance-to-two-decimals)))
(defn ->crypto
[state conversion-rate]
(-> state
(set-input-value (fiat->crypto (input-value state) conversion-rate))
(set-upper-limit (fiat->crypto (upper-limit state) conversion-rate))
(set-lower-limit (fiat->crypto (lower-limit state) conversion-rate))))
(defn ->fiat
[state conversion-rate]
(-> state
(set-input-value (crypto->fiat (input-value state) conversion-rate))
(set-upper-limit (crypto->fiat (upper-limit state) conversion-rate))
(set-lower-limit (crypto->fiat (lower-limit state) conversion-rate))))
@@ -3,14 +3,14 @@
[quo.core :as quo] [quo.core :as quo]
[react-native.core :as rn] [react-native.core :as rn]
[status-im.common.data-confirmation-sheet.style :as style] [status-im.common.data-confirmation-sheet.style :as style]
[status-im.common.events-helper :as events-helper]
[utils.i18n :as i18n] [utils.i18n :as i18n]
[utils.navigation :as navigation]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
(defn on-choice-callback (defn on-choice-callback
[syncing-on-mobile-network?] [syncing-on-mobile-network?]
(rf/dispatch [:network/set-syncing-on-mobile-network syncing-on-mobile-network?]) (rf/dispatch [:network/set-syncing-on-mobile-network syncing-on-mobile-network?])
(navigation/hide-bottom-sheet)) (events-helper/hide-bottom-sheet))
(def on-wifi-only (partial on-choice-callback false)) (def on-wifi-only (partial on-choice-callback false))
(def on-mobile-and-wifi (partial on-choice-callback true)) (def on-mobile-and-wifi (partial on-choice-callback true))
@@ -19,7 +19,7 @@
[] []
(let [settings-drawer? (= (rf/sub [:view-id]) :screen/settings.syncing) (let [settings-drawer? (= (rf/sub [:view-id]) :screen/settings.syncing)
syncing-on-mobile-network? (rf/sub [:profile/syncing-on-mobile-network?])] syncing-on-mobile-network? (rf/sub [:profile/syncing-on-mobile-network?])]
(rn/use-mount navigation/dismiss-keyboard) (rn/use-mount events-helper/dismiss-keyboard)
[:<> [:<>
[quo/text [quo/text
{:weight :semi-bold {:weight :semi-bold
@@ -1,6 +1,6 @@
(ns utils.navigation (ns status-im.common.events-helper
"Prefer to declare in this namespace only reusable navigation functions that "Provides highly reusable dispatch functions to ensure consistency and avoid duplication in managing various events,
aren't better suited in a particular bounded context." including navigation and other actions that affect application behavior."
(:require [utils.re-frame :as rf])) (:require [utils.re-frame :as rf]))
(defn navigate-back (defn navigate-back
@@ -21,7 +21,7 @@
:padding-bottom 2}} :padding-bottom 2}}
[quo/tag [quo/tag
{:accessibility-label :communities-chat-beta-tag {:accessibility-label :communities-chat-beta-tag
:size 32 :size 24
:type :label :type :label
:label (i18n/label :t/beta) :label (i18n/label :t/beta)
:labelled? true :labelled? true
@@ -1,8 +1,10 @@
(ns status-im.common.metrics-confirmation-modal.view (ns status-im.common.metrics-confirmation-modal.view
(:require (:require
[quo.core :as quo] [quo.core :as quo]
[quo.theme]
[react-native.core :as rn] [react-native.core :as rn]
[status-im.common.metrics-confirmation-modal.style :as style] [status-im.common.metrics-confirmation-modal.style :as style]
[status-im.common.privacy.view :as privacy]
[utils.i18n :as i18n] [utils.i18n :as i18n]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
@@ -50,6 +52,15 @@
(toggle-metrics false) (toggle-metrics false)
(hide-bottom-sheet)) (hide-bottom-sheet))
(defn- on-privacy-policy-press
[]
(rf/dispatch
[:show-bottom-sheet
{:content (fn []
[quo.theme/provider :dark
[privacy/privacy-statement]])
:shell? true}]))
(defn view (defn view
[{:keys [settings?]}] [{:keys [settings?]}]
(rn/use-mount #(dismiss-keyboard)) (rn/use-mount #(dismiss-keyboard))
@@ -64,7 +75,17 @@
[bullet-points [bullet-points
{:title (i18n/label :t/what-we-wont-receive) {:title (i18n/label :t/what-we-wont-receive)
:points not-receive-points}] :points not-receive-points}]
(when-not settings? (if settings?
[quo/text
{:size :paragraph-2
:style style/info-text}
(i18n/label :t/usage-data-shared-from-all-profiles)
(i18n/label :t/more-details-in-privacy-policy-1)
[quo/text
{:size :paragraph-2
:weight :bold
:on-press on-privacy-policy-press}
(i18n/label :t/more-details-in-privacy-policy-2)]]
[quo/text [quo/text
{:size :paragraph-2 {:size :paragraph-2
:style style/info-text} :style style/info-text}
@@ -1,4 +1,4 @@
(ns status-im.contexts.onboarding.privacy.view (ns status-im.common.privacy.view
(:require-macros [legacy.status-im.utils.slurp :refer [slurp]]) (:require-macros [legacy.status-im.utils.slurp :refer [slurp]])
(:require [quo.core :as quo] (:require [quo.core :as quo]
[react-native.gesture :as gesture])) [react-native.gesture :as gesture]))
@@ -1,4 +1,4 @@
(ns status-im.contexts.onboarding.terms.view (ns status-im.common.terms.view
(:require-macros [legacy.status-im.utils.slurp :refer [slurp]]) (:require-macros [legacy.status-im.utils.slurp :refer [slurp]])
(:require [quo.core :as quo] (:require [quo.core :as quo]
[react-native.gesture :as gesture])) [react-native.gesture :as gesture]))
+28 -9
View File
@@ -2,23 +2,42 @@
(:require (:require
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.common.theme.core :as theme] [status-im.common.theme.core :as theme]
[status-im.common.theme.utils :as utils])) [status-im.constants :as constants]))
(re-frame/reg-fx (re-frame/reg-fx
:theme/init-theme :theme/init-theme
(fn [] (fn []
(theme/add-device-theme-change-listener))) (theme/add-device-theme-change-listener)))
(defn- appearance-type->theme
"Converts appearance type identifier to a theme keyword.
Returns `:light` or `:dark` based on `appearance-type`:
- `appearance-type-light` (1): Light theme.
- `appearance-type-dark` (2): Dark theme.
- `appearance-type-system` (0): Uses system preference."
[appearance-type]
(condp = appearance-type
constants/appearance-type-dark :dark
constants/appearance-type-light :light
constants/appearance-type-system (if (theme/device-theme-dark?) :dark :light)
:dark))
;; Switches the theme and triggers related effects.
;;
;; Parameters:
;; - `theme`: Optional theme keyword to apply. If not provided, defaults to the theme determined by
;; `appearance-type`.
;; - `appearance-type`: Optional appearance type to determine the theme. If not provided, defaults to
;; the appearance type from the user's profile.
;; - `view-id`: Optional view ID for updating status and navigation color. If not provided, uses the
;; current view ID from the database. `view-id` is required because status and navigation bar colors
;; depends on the screen. For example, on the home screen, the navigation color should always be dark,
;; regardless of the theme, due to the bottom tabs.
(re-frame/reg-event-fx (re-frame/reg-event-fx
:theme/switch :theme/switch
(fn [{db :db} [{:keys [theme view-id theme-type]}]] (fn [{db :db} [{:keys [theme view-id appearance-type]}]]
"Switches the theme and triggers related effects. (let [appearance-type (or appearance-type (get-in db [:profile/profile :appearance]))
Parameters: theme (or theme (appearance-type->theme appearance-type))]
- theme: Optional theme keyword to apply. If not provided, defaults to the theme determined by theme-type.
- theme-type: Optional theme type to determine the theme. If not provided, defaults to the theme type from the user's profile.
- view-id: Optional view ID for updating status color. If not provided, uses the current view ID from the database."
(let [theme-type (or theme-type (get-in db [:profile/profile :appearance]))
theme (or theme (utils/theme-type->theme-value theme-type))]
{:db (assoc db :theme theme) {:db (assoc db :theme theme)
:fx [[:theme/legacy-theme-fx theme] :fx [[:theme/legacy-theme-fx theme]
[:dispatch [:dispatch
-17
View File
@@ -1,17 +0,0 @@
(ns status-im.common.theme.utils
(:require
[status-im.common.theme.core :as theme]
[status-im.constants :as constants]))
(defn theme-type->theme-value
"Converts theme type identifier to a theme keyword.
Returns `:light` or `:dark` based on `theme-type`:
- `theme-type-light` (1): Light theme.
- `theme-type-dark` (2): Dark theme.
- `theme-type-system` (0): Uses system preference."
[theme-type]
(condp = theme-type
constants/theme-type-dark :dark
constants/theme-type-light :light
constants/theme-type-system (if (theme/device-theme-dark?) :dark :light)
:dark))
-2
View File
@@ -123,8 +123,6 @@
:waku.test :waku.test
["enrtree://AOGYWMBYOUIMOENHXCHILPKY3ZRFEULMFI4DOM442QSZ73TT2A7VI@test.waku.nodes.status.im"]}) ["enrtree://AOGYWMBYOUIMOENHXCHILPKY3ZRFEULMFI4DOM442QSZ73TT2A7VI@test.waku.nodes.status.im"]})
(def default-kdf-iterations 3200)
(def community-accounts-selection-enabled? true) (def community-accounts-selection-enabled? true)
(def fetch-messages-enabled? (enabled? (get-config :FETCH_MESSAGES_ENABLED "1"))) (def fetch-messages-enabled? (enabled? (get-config :FETCH_MESSAGES_ENABLED "1")))
(def test-networks-enabled? (enabled? (get-config :TEST_NETWORKS_ENABLED "0"))) (def test-networks-enabled? (enabled? (get-config :TEST_NETWORKS_ENABLED "0")))
+3 -3
View File
@@ -425,9 +425,9 @@
"We prefix our keys with 0xe701 prior to serialisation them" "We prefix our keys with 0xe701 prior to serialisation them"
"0xe701") "0xe701")
(def ^:const theme-type-system 0) (def ^:const appearance-type-system 0)
(def ^:const theme-type-light 1) (def ^:const appearance-type-light 1)
(def ^:const theme-type-dark 2) (def ^:const appearance-type-dark 2)
(def ^:const bottom-sheet-animation-delay 450) (def ^:const bottom-sheet-animation-delay 450)
(def ^:const local-pair-event-process-success "process-success") (def ^:const local-pair-event-process-success "process-success")
@@ -0,0 +1,5 @@
(ns status-im.contexts.network.data-store)
(defn online?
[{:network/keys [status]}]
(= :online status))
@@ -91,7 +91,7 @@
(merge (merge
{:db (assoc db :profile/profiles-overview multiaccounts)} {:db (assoc db :profile/profiles-overview multiaccounts)}
(when-not (seq multiaccounts) (when-not (seq multiaccounts)
{:dispatch [:init-root :screen/onboarding.intro]})))) {:dispatch [:update-theme-and-init-root :screen/onboarding.intro]}))))
(rf/defn password-set (rf/defn password-set
{:events [:onboarding/password-set]} {:events [:onboarding/password-set]}
@@ -3,11 +3,11 @@
[quo.core :as quo] [quo.core :as quo]
[react-native.core :as rn] [react-native.core :as rn]
[react-native.safe-area :as safe-area] [react-native.safe-area :as safe-area]
[status-im.common.privacy.view :as privacy]
[status-im.common.terms.view :as terms]
[status-im.contexts.onboarding.common.background.view :as background] [status-im.contexts.onboarding.common.background.view :as background]
[status-im.contexts.onboarding.common.overlay.view :as overlay] [status-im.contexts.onboarding.common.overlay.view :as overlay]
[status-im.contexts.onboarding.intro.style :as style] [status-im.contexts.onboarding.intro.style :as style]
[status-im.contexts.onboarding.privacy.view :as privacy]
[status-im.contexts.onboarding.terms.view :as terms]
[utils.i18n :as i18n] [utils.i18n :as i18n]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
@@ -38,7 +38,7 @@
(str (i18n/label :t/accept-status-tos-prefix) " ")] (str (i18n/label :t/accept-status-tos-prefix) " ")]
[quo/text [quo/text
{:on-press #(rf/dispatch [:show-bottom-sheet {:on-press #(rf/dispatch [:show-bottom-sheet
{:content (fn [] [terms/terms-of-use]) {:content terms/terms-of-use
:shell? true}]) :shell? true}])
:style style/highlighted-text :style style/highlighted-text
:size :paragraph-2 :size :paragraph-2
@@ -50,7 +50,7 @@
" & "] " & "]
[quo/text [quo/text
{:on-press #(rf/dispatch [:show-bottom-sheet {:on-press #(rf/dispatch [:show-bottom-sheet
{:content (fn [] [privacy/privacy-statement]) {:content privacy/privacy-statement
:shell? true}]) :shell? true}])
:style style/highlighted-text :style style/highlighted-text
:size :paragraph-2 :size :paragraph-2
@@ -2,12 +2,10 @@
(:require (:require
[quo.core :as quo] [quo.core :as quo]
[quo.foundations.colors :as colors] [quo.foundations.colors :as colors]
[re-frame.core :as re-frame]
[react-native.core :as rn] [react-native.core :as rn]
[react-native.linear-gradient :as linear-gradient] [react-native.linear-gradient :as linear-gradient]
[react-native.safe-area :as safe-area] [react-native.safe-area :as safe-area]
[status-im.common.resources :as resources] [status-im.common.resources :as resources]
[status-im.constants :as constants]
[status-im.contexts.onboarding.welcome.style :as style] [status-im.contexts.onboarding.welcome.style :as style]
[utils.i18n :as i18n] [utils.i18n :as i18n]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
@@ -24,20 +22,12 @@
:description (i18n/label :t/welcome-to-web3-sub-title) :description (i18n/label :t/welcome-to-web3-sub-title)
:description-accessibility-label :welcome-sub-title}])) :description-accessibility-label :welcome-sub-title}]))
(defn dispatch-visibility-status-update
[status-type]
(re-frame/dispatch
[:visibility-status-updates/delayed-visibility-status-update status-type]))
(defn view (defn view
[] []
(let [profile-color (rf/sub [:onboarding/customization-color]) (let [profile-color (rf/sub [:onboarding/customization-color])
{:keys [status-type]} (rf/sub [:multiaccount/current-user-visibility-status]) window (rf/sub [:dimensions/window])
window (rf/sub [:dimensions/window]) insets (safe-area/get-insets)]
insets (safe-area/get-insets)]
[rn/view {:style (style/page-container insets)} [rn/view {:style (style/page-container insets)}
(when (nil? status-type)
(dispatch-visibility-status-update constants/visibility-status-automatic))
[quo/page-nav [quo/page-nav
{:type :no-title {:type :no-title
:background :blur :background :blur
@@ -55,7 +45,7 @@
:colors [colors/neutral-100-opa-0 colors/neutral-100-opa-80]}]) :colors [colors/neutral-100-opa-0 colors/neutral-100-opa-80]}])
[quo/button [quo/button
{:on-press (fn [] {:on-press (fn []
(rf/dispatch [:init-root :shell-stack]) (rf/dispatch [:update-theme-and-init-root :shell-stack])
(rf/dispatch [:profile/show-testnet-mode-banner-if-enabled]) (rf/dispatch [:profile/show-testnet-mode-banner-if-enabled])
(rf/dispatch [:universal-links/process-stored-event])) (rf/dispatch [:universal-links/process-stored-event]))
:type :primary :type :primary
@@ -25,4 +25,4 @@
(rf/dispatch [:navigate-back]) (rf/dispatch [:navigate-back])
(do (do
(rf/dispatch [:theme/switch {:theme :dark}]) (rf/dispatch [:theme/switch {:theme :dark}])
(rf/dispatch [:init-root root])))}])) (rf/dispatch [:update-theme-and-init-root root])))}]))
@@ -2,9 +2,13 @@
(:require (:require
[quo.core :as quo] [quo.core :as quo]
[quo.foundations.resources :as resources] [quo.foundations.resources :as resources]
[react-native.core :as rn]
[react-native.safe-area :as safe-area] [react-native.safe-area :as safe-area]
[reagent.core :as reagent] [reagent.core :as reagent]
[status-im.contexts.preview.quo.preview :as preview])) [status-im.common.controlled-input.utils :as controlled-input]
[status-im.contexts.preview.quo.preview :as preview]
[status-im.contexts.wallet.common.utils :as utils]
[utils.money :as money]))
(def networks (def networks
[{:source (resources/get-network :arbitrum)} [{:source (resources/get-network :arbitrum)}
@@ -12,47 +16,75 @@
{:source (resources/get-network :ethereum)}]) {:source (resources/get-network :ethereum)}])
(def title "Max: 200 SNT") (def title "Max: 200 SNT")
(def conversion-rate 3450.28)
(def descriptor (def descriptor
[{:key :token [{:key :token-symbol
:type :select :type :select
:options [{:key :eth} :options [{:key :eth}
{:key :snt}]} {:key :snt}]}
{:key :currency {:key :currency
:type :select :type :select
:options [{:key :usd} :options [{:key "$"}
{:key :eur}]} {:key "€"}]}
{:key :error? {:key :error?
:type :boolean}
{:key :allow-selection?
:type :boolean}]) :type :boolean}])
(defn view (defn view
[] []
(let [state (reagent/atom {:token :eth (let [state (reagent/atom {:token-symbol :eth
:currency :usd :currency "$"
:conversion 0.02 :crypto? true
:networks networks :error? false})]
:title title
:customization-color :blue
:show-keyboard? false
:allow-selection? true})
value (reagent/atom "")
set-value (fn [v]
(swap! value str v))
delete (fn [_]
(swap! value #(subs % 0 (dec (count %)))))]
(fn [] (fn []
[preview/preview-container (let [{:keys [currency token-symbol crypto? error?]} @state
{:state state [input-state set-input-state] (rn/use-state controlled-input/init-state)
:descriptor descriptor input-amount (controlled-input/input-value input-state)
:full-screen? true swap-between-fiat-and-crypto (fn []
:component-container-style {:flex 1 (if crypto?
:justify-content :space-between}} (set-input-state #(controlled-input/->fiat
[quo/token-input (assoc @state :value @value)] %
[quo/numbered-keyboard conversion-rate))
{:container-style {:padding-bottom (safe-area/get-top)} (set-input-state
:left-action :dot #(controlled-input/->crypto
:delete-key? true %
:on-press set-value conversion-rate))))
:on-delete delete}]]))) converted-value (if crypto?
(utils/prettify-balance currency
(money/crypto->fiat
input-amount
conversion-rate))
(utils/prettify-crypto-balance
(or (clj->js token-symbol) "")
(money/fiat->crypto input-amount
conversion-rate)
conversion-rate))]
[preview/preview-container
{:state state
:descriptor descriptor
:full-screen? true
:component-container-style {:flex 1
:justify-content :space-between}}
[quo/token-input
{:token-symbol token-symbol
:currency-symbol (if crypto? token-symbol currency)
:error? error?
:value input-amount
:converted-value converted-value
:on-swap (fn []
(swap! state assoc :crypto? (not crypto?))
(swap-between-fiat-and-crypto))
:hint-component [quo/network-tags
{:networks networks
:title title
:status (when (:error? @state) :error)}]}]
[quo/numbered-keyboard
{:container-style {:padding-bottom (safe-area/get-top)}
:left-action :dot
:delete-key? true
:on-press (fn [c]
(set-input-state #(controlled-input/add-character % c)))
:on-delete (fn []
(set-input-state controlled-input/delete-last))}]]))))
+4 -4
View File
@@ -12,10 +12,10 @@
:wakuV2Nameserver "8.8.8.8" :wakuV2Nameserver "8.8.8.8"
:statusProxyEnabled config/status-proxy-enabled? :statusProxyEnabled config/status-proxy-enabled?
:statusProxyStageName config/status-proxy-stage-name :statusProxyStageName config/status-proxy-stage-name
:statusProxyMarketUser "test" :statusProxyMarketUser config/STATUS_BUILD_PROXY_USER
:statusProxyMarketPassword "test" :statusProxyMarketPassword config/STATUS_BUILD_PROXY_PASSWORD
:statusProxyBlockchainUser "test" :statusProxyBlockchainUser config/STATUS_BUILD_PROXY_USER
:statusProxyBlockchainPassword "test" :statusProxyBlockchainPassword config/STATUS_BUILD_PROXY_PASSWORD
:openseaAPIKey config/opensea-api-key :openseaAPIKey config/opensea-api-key
:poktToken config/POKT_TOKEN :poktToken config/POKT_TOKEN
:infuraToken config/INFURA_TOKEN :infuraToken config/INFURA_TOKEN
+2 -2
View File
@@ -53,7 +53,7 @@
(assoc :profile/profiles-overview profiles) (assoc :profile/profiles-overview profiles)
(update :profile/login #(select-profile % key-uid))) (update :profile/login #(select-profile % key-uid)))
db-with-settings) db-with-settings)
:fx [[:dispatch [:init-root :screen/profile.profiles]] :fx [[:dispatch [:update-theme-and-init-root :screen/profile.profiles]]
(when (and key-uid userConfirmed) (when (and key-uid userConfirmed)
[:effects.biometric/check-if-available [:effects.biometric/check-if-available
{:key-uid key-uid {:key-uid key-uid
@@ -61,7 +61,7 @@
(rf/dispatch [:profile.login/check-biometric-success key-uid (rf/dispatch [:profile.login/check-biometric-success key-uid
auth-method]))}])]}) auth-method]))}])]})
{:db db-with-settings {:db db-with-settings
:fx [[:dispatch [:init-root :screen/onboarding.intro]]]})))) :fx [[:dispatch [:update-theme-and-init-root :screen/onboarding.intro]]]}))))
(rf/reg-event-fx (rf/reg-event-fx
:profile/update-setting-from-backup :profile/update-setting-from-backup
@@ -73,13 +73,13 @@
(cond (cond
pairing-completed? pairing-completed?
[[:dispatch [:init-root :screen/onboarding.syncing-results]]] [[:dispatch [:update-theme-and-init-root :screen/onboarding.syncing-results]]]
(get db :onboarding/new-account?) (get db :onboarding/new-account?)
[[:dispatch [:onboarding/finalize-setup]]] [[:dispatch [:onboarding/finalize-setup]]]
:else :else
[[:dispatch [:init-root :shell-stack]] [[:dispatch [:update-theme-and-init-root :shell-stack]]
[:dispatch [:profile/show-testnet-mode-banner-if-enabled]]]))}))) [:dispatch [:profile/show-testnet-mode-banner-if-enabled]]]))})))
;; login phase 2: we want to load and show chats faster, so we split login into 2 phases ;; login phase 2: we want to load and show chats faster, so we split login into 2 phases
@@ -185,7 +185,7 @@
{:db (-> db {:db (-> db
(assoc-in [:profile/login :password] password) (assoc-in [:profile/login :password] password)
(assoc-in [:profile/login :processing] true)) (assoc-in [:profile/login :processing] true))
:fx [[:dispatch [:init-root :progress]] :fx [[:dispatch [:update-theme-and-init-root :progress]]
[:effects.profile/login [:effects.profile/login
[(get-in db [:profile/login :key-uid]) [(get-in db [:profile/login :key-uid])
(security/safe-unmask-data password)]]]}))) (security/safe-unmask-data password)]]]})))
@@ -251,5 +251,6 @@
config/enable-alert-banner?) config/enable-alert-banner?)
{:fx [[:dispatch {:fx [[:dispatch
[:alert-banners/add [:alert-banners/add
{:type :alert {:type :alert
:text (i18n/label :t/testnet-mode-enabled)}]]]}))) :text (i18n/label :t/testnet-mode-enabled)
:on-press #(rf/dispatch [:wallet/show-disable-testnet-mode-confirmation])}]]]})))
@@ -95,7 +95,7 @@
(rf/reg-event-fx :profile.settings/change-appearance (rf/reg-event-fx :profile.settings/change-appearance
(fn [_ [theme]] (fn [_ [theme]]
{:fx [[:dispatch [:profile.settings/profile-update :appearance theme]] {:fx [[:dispatch [:profile.settings/profile-update :appearance theme]]
[:dispatch [:theme/switch {:theme-type theme}]]]})) [:dispatch [:theme/switch {:appearance-type theme}]]]}))
(rf/reg-fx :profile.settings/get-profile-picture (rf/reg-fx :profile.settings/get-profile-picture
(fn [key-uid] (fn [key-uid]
@@ -2,8 +2,8 @@
(:require (:require
[quo.core :as quo] [quo.core :as quo]
[react-native.core :as rn] [react-native.core :as rn]
[status-im.common.events-helper :as events-helper]
[utils.i18n :as i18n] [utils.i18n :as i18n]
[utils.navigation :as navigation]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
(defn- open-blocked-users (defn- open-blocked-users
@@ -21,7 +21,7 @@
[quo/page-nav [quo/page-nav
{:background :blur {:background :blur
:icon-name :i/arrow-left :icon-name :i/arrow-left
:on-press navigation/navigate-back}] :on-press events-helper/navigate-back}]
[quo/page-top {:title (i18n/label :t/messages)}] [quo/page-top {:title (i18n/label :t/messages)}]
[quo/category [quo/category
{:label (i18n/label :t/contacts) {:label (i18n/label :t/contacts)
@@ -2,8 +2,8 @@
(:require (:require
[quo.core :as quo] [quo.core :as quo]
[status-im.common.data-confirmation-sheet.view :as data-confirmation-sheet] [status-im.common.data-confirmation-sheet.view :as data-confirmation-sheet]
[status-im.common.events-helper :as events-helper]
[utils.i18n :as i18n] [utils.i18n :as i18n]
[utils.navigation :as navigation]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
(defn- open-paired-devices-list (defn- open-paired-devices-list
@@ -26,7 +26,7 @@
[quo/page-nav [quo/page-nav
{:background :blur {:background :blur
:icon-name :i/arrow-left :icon-name :i/arrow-left
:on-press navigation/navigate-back}] :on-press events-helper/navigate-back}]
[quo/page-top {:title (i18n/label :t/syncing)}] [quo/page-top {:title (i18n/label :t/syncing)}]
[quo/category [quo/category
{:data [{:title (i18n/label :t/sync-and-backup) {:data [{:title (i18n/label :t/sync-and-backup)
@@ -5,11 +5,12 @@
[react-native.core :as rn] [react-native.core :as rn]
[react-native.platform :as platform] [react-native.platform :as platform]
[react-native.safe-area :as safe-area] [react-native.safe-area :as safe-area]
[status-im.common.events-helper :as events-helper]
[status-im.common.metrics-confirmation-modal.view :as metrics-modal]
[status-im.contexts.settings.privacy-and-security.profile-picture.view :as profile-picture.view] [status-im.contexts.settings.privacy-and-security.profile-picture.view :as profile-picture.view]
[status-im.contexts.settings.privacy-and-security.style :as style] [status-im.contexts.settings.privacy-and-security.style :as style]
[status-im.feature-flags :as ff] [status-im.feature-flags :as ff]
[utils.i18n :as i18n] [utils.i18n :as i18n]
[utils.navigation :as navigation]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
(defn- setting-preview-privacy (defn- setting-preview-privacy
@@ -24,6 +25,15 @@
:id :preview-privacy :id :preview-privacy
:customization-color customization-color}}) :customization-color customization-color}})
(defn- on-share-usage-data-press
[]
(rf/dispatch
[:show-bottom-sheet
{:content (fn []
[quo.theme/provider :dark
[metrics-modal/view {:settings? true}]])
:shell? true}]))
(defn view (defn view
[] []
(let [insets (safe-area/get-insets) (let [insets (safe-area/get-insets)
@@ -68,7 +78,7 @@
{:key :header {:key :header
:background :blur :background :blur
:icon-name :i/arrow-left :icon-name :i/arrow-left
:on-press navigation/navigate-back}] :on-press events-helper/navigate-back}]
[quo/standard-title [quo/standard-title
{:title (i18n/label :t/privacy-and-security) {:title (i18n/label :t/privacy-and-security)
:container-style style/title-container :container-style style/title-container
@@ -85,13 +95,13 @@
show-profile-pictures-to show-profile-pictures-to
open-show-profile-pictures-to-options)) open-show-profile-pictures-to-options))
(setting-preview-privacy preview-privacy? customization-color toggle-preview-privacy) (setting-preview-privacy preview-privacy? customization-color toggle-preview-privacy)
{:title (i18n/label :t/share-usage-data) {:title (i18n/label :t/share-usage-data)
:blur? true :description :text
:action :selector :description-props {:text (i18n/label :t/from-all-profiles-on-device)}
:action-props {:on-change #(rf/dispatch :blur? true
[:centralized-metrics/toggle-centralized-metrics :action :selector
(not centralized-metrics-enabled?)]) :action-props {:on-change on-share-usage-data-press
:customization-color customization-color :customization-color customization-color
:checked? centralized-metrics-enabled?}}] :checked? centralized-metrics-enabled?}}]
:blur? true :blur? true
:list-type :settings}]])) :list-type :settings}]]))
@@ -2,7 +2,9 @@
(:require (:require
[clojure.string :as string] [clojure.string :as string]
[native-module.core :as native-module] [native-module.core :as native-module]
[react-native.platform :as platform]
[status-im.contexts.settings.wallet.data-store :as data-store] [status-im.contexts.settings.wallet.data-store :as data-store]
[status-im.contexts.settings.wallet.network-settings.testnet-mode.view :as testnet]
[taoensso.timbre :as log] [taoensso.timbre :as log]
[utils.i18n :as i18n] [utils.i18n :as i18n]
[utils.re-frame :as rf] [utils.re-frame :as rf]
@@ -292,3 +294,15 @@
(rf/reg-event-fx :wallet/make-partially-operable-accounts-fully-operable (rf/reg-event-fx :wallet/make-partially-operable-accounts-fully-operable
make-partially-operable-accounts-fully-operable) make-partially-operable-accounts-fully-operable)
(rf/reg-event-fx :wallet/show-disable-testnet-mode-confirmation
(fn [{:keys [db]} _]
(let [theme (:theme db)
blur? (and (not platform/android?) (= theme :dark))]
{:fx [[:dispatch
[:show-bottom-sheet
{:content (fn [] [testnet/view
{:enable? false
:blur? blur?}])
:shell? blur?
:theme (when-not platform/android? theme)}]]]})))
+10 -39
View File
@@ -3,8 +3,8 @@
[clojure.string :as string] [clojure.string :as string]
[native-module.core :as native-module] [native-module.core :as native-module]
[react-native.platform :as platform] [react-native.platform :as platform]
[status-im.config :as config]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.contexts.profile.config :as profile.config]
[status-im.contexts.syncing.utils :as sync-utils] [status-im.contexts.syncing.utils :as sync-utils]
[taoensso.timbre :as log] [taoensso.timbre :as log]
[utils.i18n :as i18n] [utils.i18n :as i18n]
@@ -21,26 +21,6 @@
{:events [:syncing/clear-states]} {:events [:syncing/clear-states]}
[{:keys [db]} role] [{:keys [db]} role]
{:db (dissoc db :syncing)}) {:db (dissoc db :syncing)})
(defn- get-default-node-config
[]
(let [log-level config/log-level
log-config (if (empty? log-level)
{:LogLevel "ERROR" :LogEnabled false}
{:LogLevel log-level :LogEnabled true})]
(merge {:WalletConfig (cond-> {:Enabled true}
(not= config/opensea-api-key "")
(assoc :OpenseaAPIKey config/opensea-api-key))
:WakuV2Config {;; Temporary fix until https://github.com/status-im/status-go/issues/3024
;; is resolved
:Nameserver "8.8.8.8"
:LightClient true}
:ShhextConfig {:VerifyTransactionURL config/verify-transaction-url
:VerifyENSURL config/verify-ens-url
:VerifyENSContractAddress config/verify-ens-contract-address
:VerifyTransactionChainID config/verify-transaction-chain-id}}
log-config)))
(defn- input-connection-string-callback (defn- input-connection-string-callback
[res] [res]
(log/info "[local-pairing] input-connection-string-for-bootstrapping callback" (log/info "[local-pairing] input-connection-string-for-bootstrapping callback"
@@ -69,24 +49,15 @@
(rf/defn initiate-local-pairing-with-connection-string (rf/defn initiate-local-pairing-with-connection-string
{:events [:syncing/input-connection-string-for-bootstrapping]} {:events [:syncing/input-connection-string-for-bootstrapping]}
[{:keys [db]} connection-string] [{:keys [db]} connection-string]
(let [default-node-config-string (.stringify js/JSON (clj->js (get-default-node-config))) (let [config-map (.stringify js/JSON
callback (clj->js
(fn [final-node-config] {:receiverConfig
(let [config-map (.stringify js/JSON {:createAccount (profile.config/create)}}))]
(clj->js (rf/dispatch [:syncing/update-role constants/local-pairing-role-receiver])
{:receiverConfig (native-module/input-connection-string-for-bootstrapping
{:kdfIterations config/default-kdf-iterations connection-string
:nodeConfig final-node-config config-map
:settingCurrentNetwork config/default-network input-connection-string-callback)))
:deviceType platform/os
:deviceName
(native-module/get-installation-name)}}))]
(rf/dispatch [:syncing/update-role constants/local-pairing-role-receiver])
(native-module/input-connection-string-for-bootstrapping
connection-string
config-map
input-connection-string-callback)))]
(native-module/prepare-dir-and-update-config "" default-node-config-string callback)))
(rf/defn preparations-for-connection-string (rf/defn preparations-for-connection-string
{:events [:syncing/get-connection-string]} {:events [:syncing/get-connection-string]}
@@ -2,6 +2,7 @@
(:require [camel-snake-kebab.extras :as cske] (:require [camel-snake-kebab.extras :as cske]
[clojure.string :as string] [clojure.string :as string]
[react-native.platform :as platform] [react-native.platform :as platform]
[status-im.contexts.network.data-store :as network.data-store]
[status-im.contexts.wallet.collectible.utils :as collectible-utils] [status-im.contexts.wallet.collectible.utils :as collectible-utils]
[taoensso.timbre :as log] [taoensso.timbre :as log]
[utils.ethereum.chain :as chain] [utils.ethereum.chain :as chain]
@@ -122,14 +123,15 @@
(rf/reg-event-fx (rf/reg-event-fx
:wallet/request-collectibles-for-current-viewing-account :wallet/request-collectibles-for-current-viewing-account
(fn [{:keys [db]} _] (fn [{:keys [db]} _]
(let [current-viewing-account (-> db :wallet :current-viewing-account-address) (when (network.data-store/online? db)
[request-id] (get-unique-collectible-request-id 1)] (let [current-viewing-account (-> db :wallet :current-viewing-account-address)
{:db (assoc-in db [:wallet :ui :collectibles :pending-requests] 1) [request-id] (get-unique-collectible-request-id 1)]
:fx [[:dispatch {:db (assoc-in db [:wallet :ui :collectibles :pending-requests] 1)
[:wallet/request-new-collectibles-for-account :fx [[:dispatch
{:request-id request-id [:wallet/request-new-collectibles-for-account
:account current-viewing-account {:request-id request-id
:amount collectibles-request-batch-size}]]]}))) :account current-viewing-account
:amount collectibles-request-batch-size}]]]}))))
(defn- update-fetched-collectibles-progress (defn- update-fetched-collectibles-progress
[db owner-address collectibles offset has-more?] [db owner-address collectibles offset has-more?]
+58 -36
View File
@@ -12,14 +12,17 @@
[full-name] [full-name]
(first (string/split full-name #" "))) (first (string/split full-name #" ")))
(defn prettify-balance (defn cut-fiat-balance-to-two-decimals
[currency-symbol balance] [balance]
(let [valid-balance? (and balance (let [valid-balance? (and balance
(or (number? balance) (.-toFixed balance)))] (or (number? balance) (.-toFixed balance)))]
(as-> balance $ (as-> balance $
(if valid-balance? $ 0) (if valid-balance? $ 0)
(.toFixed $ 2) (.toFixed $ 2))))
(str currency-symbol $))))
(defn prettify-balance
[currency-symbol balance]
(str currency-symbol (cut-fiat-balance-to-two-decimals balance)))
(defn get-derivation-path (defn get-derivation-path
[number-of-accounts] [number-of-accounts]
@@ -47,8 +50,8 @@
nil)) nil))
(defn calc-max-crypto-decimals (defn calc-max-crypto-decimals
[value] [one-cent-value]
(let [str-representation (str value) (let [str-representation (str one-cent-value)
decimal-part (second (clojure.string/split str-representation #"\.")) decimal-part (second (clojure.string/split str-representation #"\."))
exponent (extract-exponent str-representation) exponent (extract-exponent str-representation)
zeroes-count (count (take-while #(= \0 %) decimal-part)) zeroes-count (count (take-while #(= \0 %) decimal-part))
@@ -58,29 +61,60 @@
(inc max-decimals) (inc max-decimals)
max-decimals))) max-decimals)))
(defn get-crypto-decimals-count (defn token-usd-price
[{:keys [market-values-per-currency]}] [token]
(let [price (get-in market-values-per-currency [:usd :price]) (get-in token [:market-values-per-currency :usd :price]))
one-cent-value (if (pos? price) (/ 0.01 price) 0)]
(calc-max-crypto-decimals one-cent-value))) (defn one-cent-value
[token-price-in-usd]
(if (pos? token-price-in-usd)
(/ 0.01 token-price-in-usd)
0))
(defn analyze-token-amount-for-price
"For full details: https://github.com/status-im/status-mobile/issues/18225"
[token-price-in-usd token-units]
(if (or (nil? token-units)
(not (money/bignumber? token-units))
(money/equal-to token-units 0))
{:zero-value? true}
(let [cent-value (one-cent-value token-price-in-usd)]
{:usd-cent-value cent-value
:standardized-decimals-count (if (nil? token-price-in-usd)
missing-price-decimals
(calc-max-crypto-decimals cent-value))})))
(defn cut-crypto-decimals-to-fit-usd-cents
[token-units token-price-in-usd]
(let [{:keys [zero-value? usd-cent-value standardized-decimals-count]}
(analyze-token-amount-for-price token-price-in-usd token-units)]
(cond
zero-value? "0"
(< token-units usd-cent-value) "0"
:else (number/remove-trailing-zeroes
(.toFixed token-units standardized-decimals-count)))))
(defn prettify-crypto-balance
[token-symbol crypto-balance conversion-rate]
(str (cut-crypto-decimals-to-fit-usd-cents crypto-balance conversion-rate)
" "
(string/upper-case token-symbol)))
(defn get-standard-crypto-format (defn get-standard-crypto-format
"For full details: https://github.com/status-im/status-mobile/issues/18225" "For full details: https://github.com/status-im/status-mobile/issues/18225"
[{:keys [market-values-per-currency]} token-units] [token token-units]
(cond (or (nil? token-units) (let [token-price-in-usd (token-usd-price token)
(money/equal-to token-units 0)) {:keys [zero-value? usd-cent-value standardized-decimals-count]}
"0" (analyze-token-amount-for-price token-price-in-usd token-units)]
(cond
zero-value?
"0"
(nil? (-> market-values-per-currency :usd :price)) (< token-units usd-cent-value)
(number/remove-trailing-zeroes (.toFixed token-units missing-price-decimals)) (str "<" (number/remove-trailing-zeroes (.toFixed usd-cent-value standardized-decimals-count)))
:else :else
(let [price (-> market-values-per-currency :usd :price) (number/remove-trailing-zeroes (.toFixed token-units standardized-decimals-count)))))
one-cent-value (if (pos? price) (/ 0.01 price) 0)
decimals-count (calc-max-crypto-decimals one-cent-value)]
(if (< token-units one-cent-value)
(str "<" (number/remove-trailing-zeroes (.toFixed one-cent-value decimals-count)))
(number/remove-trailing-zeroes (.toFixed token-units decimals-count))))))
(defn get-market-value (defn get-market-value
[currency {:keys [market-values-per-currency]}] [currency {:keys [market-values-per-currency]}]
@@ -286,18 +320,6 @@
(let [counter (count address)] (let [counter (count address)]
(str (subs address 0 6) "\u2026" (subs address (- counter 3) counter))))) (str (subs address 0 6) "\u2026" (subs address (- counter 3) counter)))))
(defn make-limit-label-crypto
[amount currency]
(str amount
" "
(some-> currency
name
string/upper-case)))
(defn make-limit-label-fiat
[amount currency-symbol]
(str currency-symbol amount))
(defn get-account-name-error (defn get-account-name-error
[s existing-account-names] [s existing-account-names]
(cond (cond
@@ -73,7 +73,12 @@
token-units (money/bignumber 0.01)] token-units (money/bignumber 0.01)]
(is (= (utils/get-standard-crypto-format {:market-values-per-currency market-values-per-currency} (is (= (utils/get-standard-crypto-format {:market-values-per-currency market-values-per-currency}
token-units) token-units)
"<2"))))) "<2")))
(let [market-values-per-currency {:usd {:price 0.005}}
token-units "0.01"]
(is (= (utils/get-standard-crypto-format {:market-values-per-currency market-values-per-currency}
token-units)
"0")))))
(deftest calculate-total-token-balance-test (deftest calculate-total-token-balance-test
(testing "calculate-total-token-balance function" (testing "calculate-total-token-balance function"
@@ -163,5 +168,3 @@
expected-order ["DAI" "ETH" "SNT"]] expected-order ["DAI" "ETH" "SNT"]]
(is (= expected-order sorted-tokens))))) (is (= expected-order sorted-tokens)))))
@@ -262,3 +262,7 @@
;; :cost () ;; tbd not used on desktop ;; :cost () ;; tbd not used on desktop
:token-fees token-fees :token-fees token-fees
:gas-amount (:tx-gas-amount new-path)})) :gas-amount (:tx-gas-amount new-path)}))
(defn tokens-never-loaded?
[db]
(nil? (get-in db [:wallet :ui :tokens-loading])))
+4 -1
View File
@@ -7,4 +7,7 @@
(def defaults (def defaults
{:ui {:network-filter network-filter-defaults {:ui {:network-filter network-filter-defaults
:tokens-loading {}}}) ;; Note: we set it to nil by default to differentiate when the user logs
;; in and the device is offline, versus re-fetching when offline and
;; tokens already exist in the app-db.
:tokens-loading nil}})
+32 -10
View File
@@ -5,6 +5,7 @@
[clojure.string :as string] [clojure.string :as string]
[react-native.platform :as platform] [react-native.platform :as platform]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.contexts.network.data-store :as network.data-store]
[status-im.contexts.settings.wallet.effects] [status-im.contexts.settings.wallet.effects]
[status-im.contexts.settings.wallet.events] [status-im.contexts.settings.wallet.events]
[status-im.contexts.wallet.common.activity-tab.events] [status-im.contexts.wallet.common.activity-tab.events]
@@ -107,23 +108,44 @@
[:dispatch [:wallet/request-new-collectibles-for-account-from-signal address]] [:dispatch [:wallet/request-new-collectibles-for-account-from-signal address]]
[:dispatch [:wallet/check-recent-history-for-account address]]]})) [:dispatch [:wallet/check-recent-history-for-account address]]]}))
(rf/reg-event-fx (defn- reconcile-accounts
:wallet/get-accounts-success [db-accounts-by-address new-accounts]
(reduce
(fn [res {:keys [address] :as account}]
;; Because we add extra fields (tokens and collectibles) into the RPC
;; response from accounts_getAccounts, if we are offline we want to keep
;; the old balances in the accounts, thus we merge the up-to-date account
;; from status-go into the cached accounts. We also merge when online
;; because we will re-fetch balances anyway.
;;
;; Refactor improvement: don't augment entities from status-go, store
;; tokens and collectibles in separate keys in the app-db indexed by
;; account address.
(assoc res
address
(-> (get db-accounts-by-address address)
(merge account)
;; These should not be cached, otherwise when going
;; offline->online collectibles won't be fetched.
(dissoc :current-collectible-idx :has-more-collectibles?))))
{}
new-accounts))
(rf/reg-event-fx :wallet/get-accounts-success
(fn [{:keys [db]} [accounts]] (fn [{:keys [db]} [accounts]]
(let [wallet-accounts (data-store/rpc->accounts accounts) (let [wallet-accounts (data-store/rpc->accounts accounts)
wallet-db (get db :wallet) wallet-db (get db :wallet)
new-account? (:new-account? wallet-db) new-account? (:new-account? wallet-db)
navigate-to-account (:navigate-to-account wallet-db)] navigate-to-account (:navigate-to-account wallet-db)]
{:db (assoc-in db {:db (update-in db [:wallet :accounts] reconcile-accounts wallet-accounts)
[:wallet :accounts] :fx (concat (when (or (data-store/tokens-never-loaded? db)
(utils.collection/index-by :address wallet-accounts)) (network.data-store/online? db))
:fx (concat refresh-accounts-fx-dispatches refresh-accounts-fx-dispatches)
[(when new-account? [(when new-account?
[:dispatch [:wallet/navigate-to-new-account navigate-to-account]])])}))) [:dispatch [:wallet/navigate-to-new-account navigate-to-account]])])})))
(rf/reg-event-fx (rf/reg-event-fx :wallet/get-accounts
:wallet/get-accounts (fn []
(fn [_]
{:fx [[:json-rpc/call {:fx [[:json-rpc/call
[{:method "accounts_getAccounts" [{:method "accounts_getAccounts"
:on-success [:wallet/get-accounts-success] :on-success [:wallet/get-accounts-success]
@@ -486,7 +508,7 @@
{:test-networks-enabled? test-networks-enabled? {:test-networks-enabled? test-networks-enabled?
:is-goerli-enabled? is-goerli-enabled?}) :is-goerli-enabled? is-goerli-enabled?})
chains-filtered-by-mode (remove #(not (contains? chain-ids-by-mode %)) down-chain-ids) chains-filtered-by-mode (remove #(not (contains? chain-ids-by-mode %)) down-chain-ids)
chains-down? (seq chains-filtered-by-mode) chains-down? (and (network.data-store/online? db) (seq chains-filtered-by-mode))
chain-names (when chains-down? chain-names (when chains-down?
(->> (map #(-> (network-utils/id->network %) (->> (map #(-> (network-utils/id->network %)
name name
@@ -518,8 +518,8 @@
(rf/reg-event-fx (rf/reg-event-fx
:wallet/handle-suggested-routes :wallet/handle-suggested-routes
(fn [_ data] (fn [_ [data]]
(if-let [{:keys [code details]} (-> data :ErrorResponse first)] (if-let [{:keys [code details]} (-> data :ErrorResponse)]
(let [error-message (if (= code "0") "An error occurred" details)] (let [error-message (if (= code "0") "An error occurred" details)]
(log/error "failed to get suggested routes (async)" (log/error "failed to get suggested routes (async)"
{:event :wallet/handle-suggested-routes {:event :wallet/handle-suggested-routes
@@ -531,7 +531,6 @@
candidates-fix (comp ->old-route-paths candidates-fix (comp ->old-route-paths
remove-invalid-bonder-fees-routes) remove-invalid-bonder-fees-routes)
routes (-> data routes (-> data
(first)
(data-store/rpc->suggested-routes) (data-store/rpc->suggested-routes)
(update :best best-routes-fix) (update :best best-routes-fix)
(update :candidates candidates-fix))] (update :candidates candidates-fix))]
@@ -110,46 +110,23 @@
(h/setup-subs sub-mocks) (h/setup-subs sub-mocks)
(h/render-with-theme-provider [input-amount/view (h/render-with-theme-provider [input-amount/view
{:crypto-decimals 2 {:crypto-decimals 2
:limit-crypto 250 :limit-crypto (money/bignumber 250)
:initial-crypto-currency? false}]) :initial-crypto-currency? false}])
(h/is-truthy (h/get-by-text "0")) (h/is-truthy (h/get-by-text "0"))
(h/is-truthy (h/get-by-text "ETH")) (h/is-truthy (h/get-by-text "USD"))
(h/is-truthy (h/get-by-text "$0.00")) (h/is-truthy (h/get-by-text "0 ETH"))
(h/is-truthy (h/get-by-label-text :container)) (h/is-truthy (h/get-by-label-text :container))
(h/is-disabled (h/get-by-label-text :button-one))) (h/is-disabled (h/get-by-label-text :button-one)))
(h/test "Fill token input and confirm"
(h/setup-subs sub-mocks)
(let [on-confirm (h/mock-fn)]
(h/render-with-theme-provider [input-amount/view
{:on-confirm on-confirm
:crypto-decimals 10
:limit-crypto 1000
:initial-crypto-currency? false}])
(h/fire-event :press (h/query-by-label-text :keyboard-key-1))
(h/fire-event :press (h/query-by-label-text :keyboard-key-2))
(h/fire-event :press (h/query-by-label-text :keyboard-key-3))
(h/fire-event :press (h/query-by-label-text :keyboard-key-.))
(h/fire-event :press (h/query-by-label-text :keyboard-key-4))
(h/fire-event :press (h/query-by-label-text :keyboard-key-5))
(-> (h/wait-for #(h/get-by-text "$1234.50"))
(.then (fn []
(h/is-truthy (h/get-by-label-text :button-one))
(h/is-truthy (h/get-by-label-text :container))
(h/fire-event :press (h/get-by-label-text :button-one))
(h/was-called on-confirm))))))
(h/test "Fill token input and confirm" (h/test "Fill token input and confirm"
(h/setup-subs sub-mocks) (h/setup-subs sub-mocks)
(let [on-confirm (h/mock-fn)] (let [on-confirm (h/mock-fn)]
(h/render-with-theme-provider [input-amount/view (h/render-with-theme-provider [input-amount/view
{:crypto-decimals 10 {:crypto-decimals 10
:limit-crypto 1000 :limit-crypto (money/bignumber 1000)
:on-confirm on-confirm :on-confirm on-confirm
:initial-crypto-currency? false}]) :initial-crypto-currency? true}])
(h/fire-event :press (h/query-by-label-text :keyboard-key-1)) (h/fire-event :press (h/query-by-label-text :keyboard-key-1))
(h/fire-event :press (h/query-by-label-text :keyboard-key-2)) (h/fire-event :press (h/query-by-label-text :keyboard-key-2))
@@ -158,18 +135,19 @@
(h/fire-event :press (h/query-by-label-text :keyboard-key-4)) (h/fire-event :press (h/query-by-label-text :keyboard-key-4))
(h/fire-event :press (h/query-by-label-text :keyboard-key-5)) (h/fire-event :press (h/query-by-label-text :keyboard-key-5))
(-> (h/wait-for #(h/get-by-text "$1234.50")) (-> (h/wait-for #(h/get-by-text "$1234.50"))
(.then (fn [] (.then (fn []
(h/is-truthy (h/get-by-label-text :button-one)) (let [btn (h/get-by-label-text :button-one)]
(h/is-truthy (h/get-by-label-text :container)) (h/is-truthy btn)
(h/fire-event :press (h/get-by-label-text :button-one)) (h/fire-event :press btn)
(h/was-called on-confirm)))))) (h/was-called on-confirm)))))))
(h/test "Try to fill more than limit" (h/test "Try to fill more than limit"
(h/setup-subs sub-mocks) (h/setup-subs sub-mocks)
(h/render-with-theme-provider [input-amount/view (h/render-with-theme-provider [input-amount/view
{:crypto-decimals 1 {:crypto-decimals 1
:limit-crypto 1}]) :limit-crypto (money/bignumber 1)}])
(h/fire-event :press (h/query-by-label-text :keyboard-key-2)) (h/fire-event :press (h/query-by-label-text :keyboard-key-2))
(h/fire-event :press (h/query-by-label-text :keyboard-key-9)) (h/fire-event :press (h/query-by-label-text :keyboard-key-9))
@@ -181,13 +159,12 @@
(h/setup-subs sub-mocks) (h/setup-subs sub-mocks)
(h/render-with-theme-provider [input-amount/view (h/render-with-theme-provider [input-amount/view
{:crypto-decimals 1 {:crypto-decimals 1
:limit-crypto 1 :limit-crypto (money/bignumber 10)
:on-confirm #()}]) :on-confirm #()}])
(h/fire-event :press (h/query-by-label-text :keyboard-key-9))
(h/fire-event :press (h/query-by-label-text :keyboard-key-9)) (h/fire-event :press (h/query-by-label-text :keyboard-key-9))
(h/is-truthy (h/get-by-label-text :container-error)) (h/is-truthy (h/get-by-label-text :container-error))
(h/fire-event :press (h/query-by-label-text :reorder)) (h/fire-event :press (h/query-by-label-text :reorder))
(h/is-truthy #(h/get-by-text "Max: $100.00"))))
(-> (h/wait-for #(h/get-by-text "Max: $1000.00"))
(.then (fn []
(h/wait-for #(h/is-truthy (h/get-by-label-text :container))))))))
@@ -153,9 +153,8 @@
bottom (safe-area/get-bottom) bottom (safe-area/get-bottom)
[crypto-currency? set-crypto-currency] (rn/use-state initial-crypto-currency?) [crypto-currency? set-crypto-currency] (rn/use-state initial-crypto-currency?)
on-navigate-back on-navigate-back on-navigate-back on-navigate-back
[input-state set-input-state] (rn/use-state controlled-input/init-state)
[just-toggled-mode? set-just-toggled-mode?] (rn/use-state false) [just-toggled-mode? set-just-toggled-mode?] (rn/use-state false)
clear-input! #(set-input-state controlled-input/delete-all)
handle-on-confirm (fn [amount] handle-on-confirm (fn [amount]
(rf/dispatch [:wallet/set-token-amount-to-send (rf/dispatch [:wallet/set-token-amount-to-send
{:amount amount {:amount amount
@@ -167,59 +166,39 @@
:as :as
token} (rf/sub [:wallet/wallet-send-token]) token} (rf/sub [:wallet/wallet-send-token])
send-from-locked-amounts (rf/sub [:wallet/wallet-send-from-locked-amounts]) send-from-locked-amounts (rf/sub [:wallet/wallet-send-from-locked-amounts])
{token-balance :total-balance {token-balance :total-balance
available-balance :available-balance :as token-by-symbol} (rf/sub [:wallet/token-by-symbol
:as token-by-symbol} (rf/sub [:wallet/token-by-symbol
(str token-symbol) (str token-symbol)
enabled-from-chain-ids]) enabled-from-chain-ids])
currency-symbol (rf/sub [:profile/currency-symbol])
currency (rf/sub [:profile/currency]) currency (rf/sub [:profile/currency])
conversion-rate (-> token conversion-rate (-> token
:market-values-per-currency :market-values-per-currency
currency currency
:price) :price)
[input-state set-input-state] (rn/use-state
(-> controlled-input/init-state
(controlled-input/set-upper-limit
(utils/cut-crypto-decimals-to-fit-usd-cents
(or default-limit-crypto token-balance)
conversion-rate))))
clear-input! #(set-input-state controlled-input/delete-all)
currency-symbol (rf/sub [:profile/currency-symbol])
loading-routes? (rf/sub loading-routes? (rf/sub
[:wallet/wallet-send-loading-suggested-routes?]) [:wallet/wallet-send-loading-suggested-routes?])
route (rf/sub [:wallet/wallet-send-route]) route (rf/sub [:wallet/wallet-send-route])
on-confirm (or default-on-confirm handle-on-confirm) on-confirm (or default-on-confirm handle-on-confirm)
crypto-decimals (or token-decimals default-crypto-decimals) crypto-decimals (or token-decimals default-crypto-decimals)
current-crypto-limit (or default-limit-crypto input-value (controlled-input/input-value input-state)
(utils/get-standard-crypto-format valid-input? (not (or (controlled-input/empty-value? input-state)
token (controlled-input/input-error input-state)))
token-balance))
available-crypto-limit (or default-limit-crypto
(utils/get-standard-crypto-format
token
available-balance))
current-fiat-limit (.toFixed (* token-balance conversion-rate) 2)
available-fiat-limit (.toFixed (* available-balance conversion-rate) 2)
current-limit (if crypto-currency?
current-crypto-limit
current-fiat-limit)
available-limit (if crypto-currency?
available-crypto-limit
available-fiat-limit)
valid-input? (not (or (string/blank?
(controlled-input/input-value
input-state))
(<= (controlled-input/numeric-value
input-state)
0)
(> (controlled-input/numeric-value
input-state)
available-limit)))
input-num-value (controlled-input/numeric-value input-state)
input-amount (controlled-input/input-value input-state)
confirm-disabled? (or (nil? route) confirm-disabled? (or (nil? route)
(empty? route) (empty? route)
(string/blank? (controlled-input/input-value (not valid-input?))
input-state))
(<= input-num-value 0)
(> input-num-value current-limit))
amount-in-crypto (if crypto-currency? amount-in-crypto (if crypto-currency?
input-amount input-value
(number/remove-trailing-zeroes (number/remove-trailing-zeroes
(.toFixed (/ input-amount conversion-rate) (.toFixed (/ input-value conversion-rate)
crypto-decimals))) crypto-decimals)))
total-amount-receiver (rf/sub [:wallet/total-amount true]) total-amount-receiver (rf/sub [:wallet/total-amount true])
amount-text (str (number/remove-trailing-zeroes amount-text (str (number/remove-trailing-zeroes
@@ -231,11 +210,9 @@
native-currency-symbol (when-not confirm-disabled? native-currency-symbol (when-not confirm-disabled?
(get-in first-route (get-in first-route
[:from :native-currency-symbol])) [:from :native-currency-symbol]))
native-token (when native-currency-symbol fee-formatted (when native-currency-symbol
(rf/sub [:wallet/token-by-symbol (rf/sub [:wallet/wallet-send-fee-fiat-formatted
native-currency-symbol])) native-currency-symbol]))
fee-formatted (rf/sub [:wallet/wallet-send-fee-fiat-formatted
native-token])
show-select-asset-sheet #(rf/dispatch show-select-asset-sheet #(rf/dispatch
[:show-bottom-sheet [:show-bottom-sheet
{:content (fn [] {:content (fn []
@@ -269,8 +246,7 @@
receiver-selected-network)) receiver-selected-network))
receiver-networks)) receiver-networks))
input-error (controlled-input/input-error input-state) input-error (controlled-input/input-error input-state)
limit-insufficient? (> (controlled-input/numeric-value input-state) limit-insufficient? (controlled-input/upper-limit-exceeded? input-state)
current-limit)
should-try-again? (and (not limit-insufficient?) no-routes-found?) should-try-again? (and (not limit-insufficient?) no-routes-found?)
current-address (rf/sub [:wallet/current-viewing-account-address]) current-address (rf/sub [:wallet/current-viewing-account-address])
owned-eth-token (rf/sub [:wallet/token-by-symbol owned-eth-token (rf/sub [:wallet/token-by-symbol
@@ -283,7 +259,9 @@
(if (= token-symbol (if (= token-symbol
(string/upper-case (string/upper-case
constants/mainnet-short-name)) constants/mainnet-short-name))
(= current-limit input-amount) (money/equal-to
(controlled-input/numeric-value input-state)
(controlled-input/upper-limit input-state))
(money/equal-to (:total-balance (money/equal-to (:total-balance
owned-eth-token) owned-eth-token)
0))) 0)))
@@ -297,34 +275,20 @@
:valid-input? valid-input? :valid-input? valid-input?
:bounce-duration-ms bounce-duration-ms :bounce-duration-ms bounce-duration-ms
:token token})) :token token}))
swap-between-fiat-and-crypto (fn [swap-to-crypto-currency?] swap-between-fiat-and-crypto (fn []
(set-just-toggled-mode? true) (set-just-toggled-mode? true)
(set-crypto-currency swap-to-crypto-currency?) (if crypto-currency?
(set-input-state (set-input-state
(fn [input-state] #(controlled-input/->fiat % conversion-rate))
(controlled-input/set-input-value (set-input-state
input-state #(controlled-input/->crypto % conversion-rate)))
(let [value (controlled-input/input-value (set-crypto-currency (not crypto-currency?)))]
input-state)
new-value (if swap-to-crypto-currency?
(.toFixed (/ value
conversion-rate)
crypto-decimals)
(.toFixed (* value
conversion-rate)
12))]
(number/remove-trailing-zeroes
new-value))))))]
(rn/use-mount (rn/use-mount
(fn [] (fn []
(let [dismiss-keyboard-fn #(when (= % "active") (rn/dismiss-keyboard!)) (let [dismiss-keyboard-fn #(when (= % "active") (rn/dismiss-keyboard!))
app-keyboard-listener (.addEventListener rn/app-state "change" dismiss-keyboard-fn)] app-keyboard-listener (.addEventListener rn/app-state "change" dismiss-keyboard-fn)]
#(.remove app-keyboard-listener)))) #(.remove app-keyboard-listener))))
(hot-reload/use-safe-unmount on-navigate-back) (hot-reload/use-safe-unmount on-navigate-back)
(rn/use-effect
(fn []
(set-input-state #(controlled-input/set-upper-limit % current-limit)))
[current-limit])
(rn/use-effect (rn/use-effect
(fn [] (fn []
(when input-error (debounce/clear-all)) (when input-error (debounce/clear-all))
@@ -348,24 +312,36 @@
:on-press #(rf/dispatch [:navigate-back]) :on-press #(rf/dispatch [:navigate-back])
:switcher-type :select-account}] :switcher-type :select-account}]
[quo/token-input [quo/token-input
{:container-style style/input-container {:container-style style/input-container
:token token-symbol :token-symbol token-symbol
:currency fiat-currency :value input-value
:currency-symbol currency-symbol :on-swap swap-between-fiat-and-crypto
:crypto-decimals (min token-decimals 6) :on-token-press show-select-asset-sheet
:error? (controlled-input/input-error input-state) :error? (controlled-input/input-error input-state)
:networks (seq from-enabled-networks) :currency-symbol (if crypto-currency? token-symbol fiat-currency)
:title (i18n/label :converted-value (if crypto-currency?
:t/send-limit (utils/prettify-balance
{:limit (if crypto-currency? currency-symbol
(utils/make-limit-label-crypto current-limit token-symbol) (money/crypto->fiat input-value
(utils/make-limit-label-fiat current-limit currency-symbol))}) conversion-rate))
:conversion conversion-rate (utils/prettify-crypto-balance
:show-keyboard? false (or (clj->js token-symbol) "")
:value input-amount (money/fiat->crypto input-value
:on-swap swap-between-fiat-and-crypto conversion-rate)
:on-token-press show-select-asset-sheet conversion-rate))
:allow-selection? false}] :hint-component [quo/network-tags
{:networks (seq from-enabled-networks)
:title (i18n/label
:t/send-limit
{:limit (if crypto-currency?
(utils/prettify-crypto-balance
(or (clj->js token-symbol) "")
(controlled-input/upper-limit input-state)
conversion-rate)
(utils/prettify-balance currency-symbol
(controlled-input/upper-limit
input-state)))})
:status (when (controlled-input/input-error input-state) :error)}]}]
[routes/view [routes/view
{:token token-by-symbol {:token token-by-symbol
:send-amount-in-crypto amount-in-crypto :send-amount-in-crypto amount-in-crypto
@@ -410,7 +386,7 @@
:left-action :dot :left-action :dot
:delete-key? true :delete-key? true
:on-press (fn [c] :on-press (fn [c]
(let [new-text (str input-amount c) (let [new-text (str input-value c)
max-decimals (if crypto-currency? crypto-decimals 2) max-decimals (if crypto-currency? crypto-decimals 2)
regex-pattern (str "^\\d*\\.?\\d{0," max-decimals "}$") regex-pattern (str "^\\d*\\.?\\d{0," max-decimals "}$")
regex (re-pattern regex-pattern)] regex (re-pattern regex-pattern)]
@@ -1,19 +1,12 @@
(ns status-im.contexts.wallet.send.routes.view (ns status-im.contexts.wallet.send.routes.view
(:require (:require
[clojure.string :as string]
[quo.core :as quo] [quo.core :as quo]
[react-native.core :as rn] [react-native.core :as rn]
[react-native.safe-area :as safe-area]
[status-im.common.controlled-input.utils :as controlled-input]
[status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.common.utils.networks :as network-utils] [status-im.contexts.wallet.common.utils.networks :as network-utils]
[status-im.contexts.wallet.send.routes.style :as style] [status-im.contexts.wallet.send.routes.style :as style]
[status-im.contexts.wallet.send.utils :as send-utils] [status-im.contexts.wallet.send.utils :as send-utils]
[status-im.contexts.wallet.sheets.network-preferences.view :as network-preferences] [status-im.contexts.wallet.sheets.network-preferences.view :as network-preferences]
[status-im.feature-flags :as ff]
[utils.i18n :as i18n] [utils.i18n :as i18n]
[utils.money :as money]
[utils.number :as number]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
@@ -87,160 +80,164 @@
(rf/dispatch [:wallet/update-receiver-networks (rf/dispatch [:wallet/update-receiver-networks
chain-ids]))}]))}])) chain-ids]))}]))}]))
(defn- edit-amount #_(defn- edit-amount
[{:keys [chain-id token-symbol send-amount-in-crypto init-amount]}] [{:keys [chain-id token-symbol send-amount-in-crypto init-amount]}]
(rf/dispatch (rf/dispatch
[:show-bottom-sheet [:show-bottom-sheet
{:content {:content
(fn [] (fn []
(let [{:keys [network-name] :as network-details} (rf/sub [:wallet/network-details-by-chain-id (let [{:keys [network-name] :as network-details} (rf/sub [:wallet/network-details-by-chain-id
chain-id]) chain-id])
{fiat-currency :currency} (rf/sub [:profile/profile]) {fiat-currency :currency} (rf/sub [:profile/profile])
{token-decimals :decimals {token-decimals :decimals
:as :as
token} (rf/sub [:wallet/wallet-send-token]) token} (rf/sub [:wallet/wallet-send-token])
currency (rf/sub [:profile/currency]) currency (rf/sub [:profile/currency])
currency-symbol (rf/sub [:profile/currency-symbol]) currency-symbol (rf/sub [:profile/currency-symbol])
send-from-locked-amounts (rf/sub send-from-locked-amounts (rf/sub
[:wallet/wallet-send-from-locked-amounts]) [:wallet/wallet-send-from-locked-amounts])
{account-color :color} (rf/sub [:wallet/current-viewing-account]) {account-color :color} (rf/sub [:wallet/current-viewing-account])
locked-amount (get send-from-locked-amounts chain-id) locked-amount (get send-from-locked-amounts chain-id)
network-name-str (string/capitalize (name network-name)) network-name-str (string/capitalize (name network-name))
[input-state set-input-state] (rn/use-state (cond-> controlled-input/init-state [input-state set-input-state] (rn/use-state
init-amount (cond-> controlled-input/init-state
(controlled-input/set-input-value init-amount
(money/to-string init-amount))
locked-amount
(controlled-input/set-input-value
locked-amount)))
[crypto-currency? set-crypto-currency] (rn/use-state true)
conversion-rate (-> token
:market-values-per-currency
currency
:price)
{token-balance :total-balance} (rf/sub [:wallet/token-by-symbol
(str token-symbol)
[chain-id]])
current-crypto-limit (utils/get-standard-crypto-format
token
token-balance)
current-fiat-limit (.toFixed (* token-balance conversion-rate) 2)
current-limit (if crypto-currency?
current-crypto-limit
current-fiat-limit)
crypto-decimals token-decimals
input-amount (controlled-input/input-value input-state)
[is-amount-locked? set-is-amount-locked] (rn/use-state (some? locked-amount))
bottom (safe-area/get-bottom)
amount-in-crypto (if crypto-currency?
input-amount
(number/remove-trailing-zeroes
(.toFixed (/ input-amount
conversion-rate)
crypto-decimals)))
locked-greater-then-send-amount? (let [amount (money/bignumber
amount-in-crypto)
send-amount (money/bignumber
send-amount-in-crypto)]
(and (money/bignumber? amount)
(money/bignumber? send-amount)
(money/greater-than amount send-amount)))
swap-between-fiat-and-crypto (fn [swap-to-crypto-currency?]
(set-crypto-currency swap-to-crypto-currency?)
(set-input-state
(fn [input-state]
(controlled-input/set-input-value (controlled-input/set-input-value
input-state (money/to-string init-amount))
(let [value (controlled-input/input-value locked-amount
input-state) (controlled-input/set-input-value
new-value (if locked-amount)))
swap-to-crypto-currency? [crypto-currency? set-crypto-currency] (rn/use-state true)
(.toFixed conversion-rate (-> token
(/ value :market-values-per-currency
conversion-rate) currency
crypto-decimals) :price)
(.toFixed {token-balance :total-balance} (rf/sub [:wallet/token-by-symbol
(* value (str token-symbol)
conversion-rate) [chain-id]])
12))] current-crypto-limit (utils/get-standard-crypto-format
(number/remove-trailing-zeroes token
new-value)))))) token-balance)
lock-or-unlock-amount (fn [] current-fiat-limit (.toFixed (* token-balance conversion-rate) 2)
(if is-amount-locked? current-limit (if crypto-currency?
(rf/dispatch [:wallet/lock-from-amount current-crypto-limit
chain-id current-fiat-limit)
amount-in-crypto]) crypto-decimals token-decimals
(rf/dispatch [:wallet/unlock-from-amount input-amount (controlled-input/input-value input-state)
chain-id])) [is-amount-locked? set-is-amount-locked] (rn/use-state (some? locked-amount))
(rf/dispatch [:hide-bottom-sheet]))] bottom (safe-area/get-bottom)
(rn/use-effect amount-in-crypto (if crypto-currency?
(fn [] input-amount
(set-input-state #(controlled-input/set-upper-limit % current-limit))) (number/remove-trailing-zeroes
[current-limit]) (.toFixed (/ input-amount
[:<> conversion-rate)
[quo/drawer-top crypto-decimals)))
{:title (i18n/label :t/send-from-network {:network network-name-str}) locked-greater-then-send-amount? (let [amount (money/bignumber
:description (i18n/label :t/define-amount-sent-from-network {:network network-name-str})}] amount-in-crypto)
[quo/token-input send-amount (money/bignumber
{:container-style style/input-container send-amount-in-crypto)]
:token token-symbol (and (money/bignumber? amount)
:currency fiat-currency (money/bignumber? send-amount)
:currency-symbol currency-symbol (money/greater-than amount
:crypto-decimals (min token-decimals 6) send-amount)))
:error? (controlled-input/input-error input-state) swap-between-fiat-and-crypto (fn [swap-to-crypto-currency?]
:networks [network-details] (set-crypto-currency
:title (i18n/label swap-to-crypto-currency?)
:t/send-limit (set-input-state
{:limit (if crypto-currency? (fn [input-state]
(utils/make-limit-label-crypto current-limit token-symbol) (controlled-input/set-input-value
(utils/make-limit-label-fiat current-limit currency-symbol))}) input-state
:conversion conversion-rate (let [value (controlled-input/input-value
:show-keyboard? false input-state)
:value (controlled-input/input-value input-state) new-value
:on-swap swap-between-fiat-and-crypto (if
:allow-selection? false}] swap-to-crypto-currency?
(when locked-greater-then-send-amount? (.toFixed
[quo/information-box (/ value
{:type :error conversion-rate)
:icon :i/info crypto-decimals)
:style style/error-box} (.toFixed
(i18n/label :t/value-higher-than-send-amount)]) (* value
[quo/disclaimer conversion-rate)
{:on-change (fn [checked?] 12))]
(set-is-amount-locked checked?)) (number/remove-trailing-zeroes
:checked? is-amount-locked? new-value))))))
:container-style style/disclaimer lock-or-unlock-amount (fn []
:icon (if is-amount-locked? (if is-amount-locked?
:i/locked (rf/dispatch [:wallet/lock-from-amount
:i/unlocked) chain-id
:customization-color account-color} amount-in-crypto])
(i18n/label :t/dont-auto-recalculate-network {:network network-name-str})] (rf/dispatch [:wallet/unlock-from-amount
[quo/bottom-actions chain-id]))
{:actions :one-action (rf/dispatch [:hide-bottom-sheet]))]
:button-one-label (i18n/label :t/update) (rn/use-effect
:button-one-props {:on-press lock-or-unlock-amount (fn []
:customization-color account-color (set-input-state #(controlled-input/set-upper-limit % current-limit)))
:disabled? (or (controlled-input/empty-value? input-state) [current-limit])
(controlled-input/input-error input-state) [:<>
locked-greater-then-send-amount?)}}] [quo/drawer-top
[quo/numbered-keyboard {:title (i18n/label :t/send-from-network {:network network-name-str})
{:container-style (style/keyboard-container bottom) :description (i18n/label :t/define-amount-sent-from-network {:network network-name-str})}]
:left-action :dot [quo/token-input
:delete-key? true {:container-style style/input-container
:on-press (fn [c] :token token-symbol
(let [new-text (str input-amount c) :currency fiat-currency
max-decimals (if crypto-currency? crypto-decimals 2) :currency-symbol currency-symbol
regex-pattern (str "^\\d*\\.?\\d{0," max-decimals "}$") :crypto-decimals (min token-decimals 6)
regex (re-pattern regex-pattern)] :error? (controlled-input/input-error input-state)
(when (re-matches regex new-text) :networks [network-details]
(set-is-amount-locked true) :title (i18n/label
(set-input-state #(controlled-input/add-character % c))))) :t/send-limit
:on-delete (fn [] {:limit (if crypto-currency?
(set-is-amount-locked true) (utils/make-limit-label-crypto current-limit token-symbol)
(set-input-state controlled-input/delete-last)) (utils/make-limit-label-fiat current-limit currency-symbol))})
:on-long-press-delete (fn [] :conversion conversion-rate
(set-is-amount-locked true) :show-keyboard? false
(set-input-state controlled-input/delete-all))}]]))}])) :value (controlled-input/input-value input-state)
:on-swap swap-between-fiat-and-crypto
:allow-selection? false}]
(when locked-greater-then-send-amount?
[quo/information-box
{:type :error
:icon :i/info
:style style/error-box}
(i18n/label :t/value-higher-than-send-amount)])
[quo/disclaimer
{:on-change (fn [checked?]
(set-is-amount-locked checked?))
:checked? is-amount-locked?
:container-style style/disclaimer
:icon (if is-amount-locked?
:i/locked
:i/unlocked)
:customization-color account-color}
(i18n/label :t/dont-auto-recalculate-network {:network network-name-str})]
[quo/bottom-actions
{:actions :one-action
:button-one-label (i18n/label :t/update)
:button-one-props {:on-press lock-or-unlock-amount
:customization-color account-color
:disabled? (or (controlled-input/empty-value? input-state)
(controlled-input/input-error input-state)
locked-greater-then-send-amount?)}}]
[quo/numbered-keyboard
{:container-style (style/keyboard-container bottom)
:left-action :dot
:delete-key? true
:on-press (fn [c]
(let [new-text (str input-amount c)
max-decimals (if crypto-currency? crypto-decimals 2)
regex-pattern (str "^\\d*\\.?\\d{0," max-decimals "}$")
regex (re-pattern regex-pattern)]
(when (re-matches regex new-text)
(set-is-amount-locked true)
(set-input-state #(controlled-input/add-character % c)))))
:on-delete (fn []
(set-is-amount-locked true)
(set-input-state controlled-input/delete-last))
:on-long-press-delete (fn []
(set-is-amount-locked true)
(set-input-state controlled-input/delete-all))}]]))}]))
(defn render-network-values (defn render-network-values
[{:keys [network-values token-symbol on-press on-long-press receiver? loading-routes? [{:keys [network-values token-symbol on-press on-long-press receiver? loading-routes?
@@ -388,13 +385,6 @@
chain-id-to-disable chain-id-to-disable
disabled-from-chain-ids disabled-from-chain-ids
token-available-networks-for-suggested-routes)) token-available-networks-for-suggested-routes))
(when (ff/enabled? ::ff/wallet.custom-network-amounts)
:on-long-press) (fn [chain-id amount-calculated-for-chain]
(edit-amount
{:chain-id chain-id
:token-symbol token-symbol
:send-amount-in-crypto send-amount-in-crypto
:init-amount amount-calculated-for-chain}))
:receiver? false :receiver? false
:theme theme :theme theme
:loading-routes? loading-routes? :loading-routes? loading-routes?
@@ -213,11 +213,8 @@
first-route (first route) first-route (first route)
native-currency-symbol (get-in first-route native-currency-symbol (get-in first-route
[:from :native-currency-symbol]) [:from :native-currency-symbol])
native-token (when native-currency-symbol
(rf/sub [:wallet/token-by-symbol
native-currency-symbol]))
fee-formatted (rf/sub [:wallet/wallet-send-fee-fiat-formatted fee-formatted (rf/sub [:wallet/wallet-send-fee-fiat-formatted
native-token]) native-currency-symbol])
account (rf/sub [:wallet/current-viewing-account]) account (rf/sub [:wallet/current-viewing-account])
account-color (:color account) account-color (:color account)
bridge-to-network (when bridge-to-chain-id bridge-to-network (when bridge-to-chain-id
@@ -1,5 +1,6 @@
(ns status-im.contexts.wallet.sheets.account-options.view (ns status-im.contexts.wallet.sheets.account-options.view
(:require [oops.core :as oops] (:require [clojure.string :as string]
[oops.core :as oops]
[quo.core :as quo] [quo.core :as quo]
[quo.foundations.colors :as colors] [quo.foundations.colors :as colors]
quo.theme quo.theme
@@ -33,6 +34,7 @@
[{:keys [theme show-account-selector? options-height]}] [{:keys [theme show-account-selector? options-height]}]
(let [{:keys [name color emoji address watch-only? (let [{:keys [name color emoji address watch-only?
default-account?]} (rf/sub [:wallet/current-viewing-account]) default-account?]} (rf/sub [:wallet/current-viewing-account])
{:keys [derived-from]} (rf/sub [:wallet/current-viewing-account-keypair])
network-preference-details (rf/sub [:wallet/network-preference-details]) network-preference-details (rf/sub [:wallet/network-preference-details])
multichain-address (utils/get-multichain-address multichain-address (utils/get-multichain-address
network-preference-details network-preference-details
@@ -89,7 +91,7 @@
#(rf/dispatch [:wallet/share-account #(rf/dispatch [:wallet/share-account
{:title share-title :content multichain-address}]) {:title share-title :content multichain-address}])
600))} 600))}
(when-not default-account? (when-not (or default-account? (string/blank? derived-from))
{:add-divider? (not show-account-selector?) {:add-divider? (not show-account-selector?)
:icon :i/delete :icon :i/delete
:accessibility-label :remove-account :accessibility-label :remove-account
+9 -3
View File
@@ -43,9 +43,15 @@
{:fx [[:dispatch [:wallet/reload]]]} {:fx [[:dispatch [:wallet/reload]]]}
"wallet-blockchain-status-changed" "wallet-blockchain-status-changed"
{:fx [[:dispatch {:fx [[:dispatch-later
[:wallet/blockchain-status-changed ;; Don't dispatch immediately because the signal may arrive as
(transforms/js->clj event-js)]]]} ;; soon as the device goes offline. We need to give some time for
;; RN to dispatch the network status update, otherwise when going
;; offline the user will immediately see a toast saying "provider
;; X is down".
[{:ms 500
:dispatch [:wallet/blockchain-status-changed
(transforms/js->clj event-js)]}]]]}
"wallet-activity-filtering-done" "wallet-activity-filtering-done"
{:fx {:fx
@@ -28,20 +28,23 @@
:max-priority-fee-per-gas "0.011000001" :max-priority-fee-per-gas "0.011000001"
:eip1559-enabled true} :eip1559-enabled true}
:estimated-time 3 :estimated-time 3
:receive-amount 99.98 :receive-amount "99.98"
:receive-token {:symbol "SNT" :pay-token {:symbol "SNT"
:address "0x432492384728934239789"}
:receive-token {:symbol "USDT"
:address "0x432492384728934239789"}}) :address "0x432492384728934239789"}})
(defn- assets-view (defn- assets-view
[search-text on-change-text] [search-text on-change-text]
(let [on-token-press (fn [token] (let [on-token-press (fn [token]
(let [token-networks (:networks token)] (let [token-networks (:networks token)
asset-to-receive (rf/sub [:wallet/token-by-symbol "SNT"])]
(rf/dispatch [:wallet.swap/select-asset-to-pay (rf/dispatch [:wallet.swap/select-asset-to-pay
{:token token {:token token
:network (when (= (count token-networks) 1) :network (when (= (count token-networks) 1)
(first token-networks)) (first token-networks))
:stack-id :screen/wallet.swap-select-asset-to-pay}]) :stack-id :screen/wallet.swap-select-asset-to-pay}])
(rf/dispatch [:wallet.swap/select-asset-to-receive {:token token}]) (rf/dispatch [:wallet.swap/select-asset-to-receive {:token asset-to-receive}])
(rf/dispatch [:wallet.swap/set-pay-amount 100]) (rf/dispatch [:wallet.swap/set-pay-amount 100])
(rf/dispatch [:wallet.swap/set-swap-proposal dummy-swap-proposal]) (rf/dispatch [:wallet.swap/set-swap-proposal dummy-swap-proposal])
(rf/dispatch [:wallet.swap/set-provider])))] (rf/dispatch [:wallet.swap/set-provider])))]
@@ -4,13 +4,13 @@
[quo.foundations.resources :as resources] [quo.foundations.resources :as resources]
[quo.theme :as quo.theme] [quo.theme :as quo.theme]
[react-native.core :as rn] [react-native.core :as rn]
[status-im.common.events-helper :as events-helper]
[status-im.common.floating-button-page.view :as floating-button-page] [status-im.common.floating-button-page.view :as floating-button-page]
[status-im.common.standard-authentication.core :as standard-auth] [status-im.common.standard-authentication.core :as standard-auth]
[status-im.contexts.wallet.common.utils.external-links :as external-links] [status-im.contexts.wallet.common.utils.external-links :as external-links]
[status-im.contexts.wallet.swap.set-spending-cap.style :as style] [status-im.contexts.wallet.swap.set-spending-cap.style :as style]
[utils.address :as address-utils] [utils.address :as address-utils]
[utils.i18n :as i18n] [utils.i18n :as i18n]
[utils.navigation :as navigation]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
(defn- swap-title (defn- swap-title
@@ -172,11 +172,7 @@
(defn footer (defn footer
[{:keys [estimated-time-min native-currency-symbol network theme account-color loading-fees?]}] [{:keys [estimated-time-min native-currency-symbol network theme account-color loading-fees?]}]
(let [native-token (when native-currency-symbol (let [fee-formatted (rf/sub [:wallet/wallet-send-fee-fiat-formatted native-currency-symbol])
(rf/sub [:wallet/token-by-symbol
native-currency-symbol]))
fee-formatted (rf/sub [:wallet/wallet-send-fee-fiat-formatted
native-token])
on-auth-success (rn/use-callback #(js/alert "Not implemented yet"))] on-auth-success (rn/use-callback #(js/alert "Not implemented yet"))]
[rn/view {:style {:margin-bottom -10}} [rn/view {:style {:margin-bottom -10}}
[transaction-details [transaction-details
@@ -213,7 +209,7 @@
{:footer-container-padding 0 {:footer-container-padding 0
:header [quo/page-nav :header [quo/page-nav
{:icon-name :i/close {:icon-name :i/close
:on-press navigation/navigate-back :on-press events-helper/navigate-back
:margin-top 8 :margin-top 8
:background :blur :background :blur
:accessibility-label :top-bar}] :accessibility-label :top-bar}]
@@ -0,0 +1,35 @@
(ns status-im.contexts.wallet.swap.setup-swap.style)
(def container
{:flex 1})
(def keyboard-container
{:align-self :flex-end
:width "100%"})
(def inputs-container
{:padding-top 12
:padding-horizontal 20})
(def details-container
{:flex-direction :row
:justify-content :space-between
:padding-top 7
:padding-horizontal 20})
(def detail-item
{:flex 1
:height 36
:background-color :transparent})
(def swap-order-button
{:margin-top -9
:z-index 2
:align-self :center})
(def receive-token-swap-input-container
{:margin-top -9})
(def footer-container
{:flex 1
:justify-content :flex-end})
@@ -0,0 +1,132 @@
(ns status-im.contexts.wallet.swap.setup-swap.view
(:require [quo.core :as quo]
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[status-im.common.events-helper :as events-helper]
[status-im.contexts.wallet.common.account-switcher.view :as account-switcher]
[status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.swap.setup-swap.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- data-item
[{:keys [title subtitle size subtitle-icon loading?]}]
[quo/data-item
{:container-style style/detail-item
:blur? false
:card? false
:subtitle-type (if subtitle-icon :editable :default)
:status (if loading? :loading :default)
:title title
:subtitle subtitle
:size size
:icon subtitle-icon}])
(defn- transaction-details
[{:keys [max-slippage native-currency-symbol loading-fees?]}]
(let [max-fees (rf/sub [:wallet/wallet-send-fee-fiat-formatted native-currency-symbol])]
[rn/view {:style style/details-container}
[data-item
{:title (i18n/label :t/max-fees)
:subtitle max-fees
:loading? loading-fees?
:size :small}]
[data-item
{:title (i18n/label :t/max-slippage)
:subtitle max-slippage
:subtitle-icon :i/edit
:loading? loading-fees?}]]))
(defn view
[]
(let [[pay-value set-pay-value] (rn/use-state "")
{:keys [color]} (rf/sub [:wallet/current-viewing-account])
{:keys [max-slippage swap-proposal loading-fees?
receive-amount network]} (rf/sub [:wallet/swap])
currency (rf/sub [:profile/currency])
currency-symbol (rf/sub [:profile/currency-symbol])
asset-to-pay (rf/sub [:wallet/swap-asset-to-pay])
asset-to-receive (rf/sub [:wallet/swap-asset-to-receive])
pay-token-fiat-value (utils/calculate-token-fiat-value
{:currency currency
:balance (or pay-value 0)
:token asset-to-pay})
receive-token-fiat-value (utils/calculate-token-fiat-value
{:currency currency
:balance (or receive-amount 0)
:token asset-to-receive})
native-currency-symbol (get-in swap-proposal
[:from :native-currency-symbol])
pay-token-symbol (:symbol asset-to-pay)
receive-token-symbol (:symbol asset-to-receive)
on-press (fn [v] (set-pay-value (str pay-value v)))
delete (fn []
(set-pay-value #(subs % 0 (dec (count %)))))]
[rn/view {:style style/container}
[account-switcher/view
{:on-press events-helper/navigate-back
:icon-name :i/arrow-left
:margin-top (safe-area/get-top)
:switcher-type :select-account}]
[rn/view {:style style/inputs-container}
[quo/swap-input
{:type :pay
:error? false
:token pay-token-symbol
:customization-color :blue
:show-approval-label? false
:status :default
:currency-symbol currency-symbol
:on-swap-press #(js/alert "Swap Pressed")
:on-token-press #(js/alert "Token Pressed")
:on-max-press #(js/alert "Max Pressed")
:value pay-value
:fiat-value pay-token-fiat-value
:network-tag-props {:title (i18n/label :t/max-token
{:number 200
:token-symbol pay-token-symbol})
:networks [{:source (:source network)}]}
:approval-label-props {:status :approve
:token-value pay-value
:button-props {:on-press
#(js/alert "Approve Pressed")}
:customization-color color
:token-symbol pay-token-symbol}}]
[quo/swap-order-button
{:container-style style/swap-order-button
:on-press #(js/alert "Pressed")}]
[quo/swap-input
{:type :receive
:error? false
:token receive-token-symbol
:customization-color color
:show-approval-label? false
:enable-swap? true
:status :default
:currency-symbol currency-symbol
:on-swap-press #(js/alert "Swap Pressed")
:on-token-press #(js/alert "Token Pressed")
:on-max-press #(js/alert "Max Pressed")
:value receive-amount
:fiat-value receive-token-fiat-value
:container-style style/receive-token-swap-input-container}]]
[rn/view {:style style/footer-container}
(when swap-proposal
[transaction-details
{:native-currency-symbol native-currency-symbol
:max-slippage max-slippage
:loading-fees? loading-fees?}])
[quo/bottom-actions
{:actions :one-action
:button-one-label (i18n/label :t/review-swap)
:button-one-props {:disabled? (or (not swap-proposal)
loading-fees?)
:customization-color color
:on-press #(js/alert "Review swap pressed")}}]]
[quo/numbered-keyboard
{:container-style style/keyboard-container
:left-action :dot
:delete-key? true
:on-press on-press
:on-delete delete}]]))
@@ -103,11 +103,7 @@
(defn footer (defn footer
[{:keys [estimated-time-min native-currency-symbol max-slippage theme account-color provider [{:keys [estimated-time-min native-currency-symbol max-slippage theme account-color provider
loading-fees?]}] loading-fees?]}]
(let [native-token (when native-currency-symbol (let [fee-formatted (rf/sub [:wallet/wallet-send-fee-fiat-formatted native-currency-symbol])]
(rf/sub [:wallet/token-by-symbol
native-currency-symbol]))
fee-formatted (rf/sub [:wallet/wallet-send-fee-fiat-formatted
native-token])]
[:<> [:<>
[transaction-details [transaction-details
{:estimated-time-min estimated-time-min {:estimated-time-min estimated-time-min
@@ -19,4 +19,8 @@
"Swap confirmation"] "Swap confirmation"]
[quo/button [quo/button
{:on-press #(rf/dispatch [:open-modal :screen/wallet.swap-set-spending-cap])} {:on-press #(rf/dispatch [:open-modal :screen/wallet.swap-set-spending-cap])}
"Set spending cap"]])) "Set spending cap"]
[quo/button
{:on-press #(rf/dispatch [:navigate-to-within-stack
[:screen/wallet.setup-swap :screen/wallet.swap-propasal]])}
"Setup swap"]]))
+1 -1
View File
@@ -29,7 +29,7 @@
(re-frame/dispatch [:bottom-sheet-hidden]) (re-frame/dispatch [:bottom-sheet-hidden])
(when @state/root-id (when @state/root-id
(reset! theme/device-theme (rn/get-color-scheme)) (reset! theme/device-theme (rn/get-color-scheme))
(re-frame/dispatch [:init-root @state/root-id]) (re-frame/dispatch [:update-theme-and-init-root @state/root-id])
(re-frame/dispatch [:chat/check-last-chat]) (re-frame/dispatch [:chat/check-last-chat])
(re-frame/dispatch [:alert-banners/restore-alert-banners])) (re-frame/dispatch [:alert-banners/restore-alert-banners]))
(rn/hide-splash-screen))) (rn/hide-splash-screen)))
-1
View File
@@ -74,7 +74,6 @@
(let [[status-bar-theme] (get-status-nav-color root-id theme) (let [[status-bar-theme] (get-status-nav-color root-id theme)
root (get (roots/roots status-bar-theme) root-id)] root (get (roots/roots status-bar-theme) root-id)]
(dismiss-all-modals) (dismiss-all-modals)
(rf/dispatch [:theme/switch {:view-id root-id}])
(reset! state/root-id (or (get-in root [:root :stack :id]) root-id)) (reset! state/root-id (or (get-in root [:root :stack :id]) root-id))
(navigation/set-root root) (navigation/set-root root)
(state/navigation-state-reset [{:id root-id (state/navigation-state-reset [{:id root-id
+7
View File
@@ -70,10 +70,17 @@
(assoc :dispatch [:chat/close (:current-chat-id db)]))) (assoc :dispatch [:chat/close (:current-chat-id db)])))
(rf/defn init-root (rf/defn init-root
"WARNING: Use `:update-theme-and-init-root` instead. `:init-root` should not be used directly."
{:events [:init-root]} {:events [:init-root]}
[{:keys [db]} root-id] [{:keys [db]} root-id]
{:set-root [root-id (:theme db)]}) {:set-root [root-id (:theme db)]})
(rf/defn update-theme-and-init-root
{:events [:update-theme-and-init-root]}
[_ root-id]
{:fx [[:dispatch [:theme/switch {:view-id root-id}]]
[:dispatch [:init-root root-id]]]})
(rf/defn change-tab (rf/defn change-tab
{:events [:navigate-change-tab]} {:events [:navigate-change-tab]}
[{:keys [db]} stack-id] [{:keys [db]} stack-id]
+5
View File
@@ -120,6 +120,7 @@
[status-im.contexts.wallet.send.transaction-progress.view :as wallet-transaction-progress] [status-im.contexts.wallet.send.transaction-progress.view :as wallet-transaction-progress]
[status-im.contexts.wallet.swap.select-asset-to-pay.view :as wallet-swap-select-asset-to-pay] [status-im.contexts.wallet.swap.select-asset-to-pay.view :as wallet-swap-select-asset-to-pay]
[status-im.contexts.wallet.swap.set-spending-cap.view :as wallet-swap-set-spending-cap] [status-im.contexts.wallet.swap.set-spending-cap.view :as wallet-swap-set-spending-cap]
[status-im.contexts.wallet.swap.setup-swap.view :as wallet-swap-setup-swap]
[status-im.contexts.wallet.swap.swap-confirmation.view :as wallet-swap-confirmation] [status-im.contexts.wallet.swap.swap-confirmation.view :as wallet-swap-confirmation]
[status-im.contexts.wallet.swap.swap-proposal.view :as wallet-swap-propasal] [status-im.contexts.wallet.swap.swap-proposal.view :as wallet-swap-propasal]
[status-im.contexts.wallet.wallet-connect.modals.send-transaction.view :as [status-im.contexts.wallet.wallet-connect.modals.send-transaction.view :as
@@ -522,6 +523,10 @@
:insets {:top? true}} :insets {:top? true}}
:component wallet-swap-select-asset-to-pay/view} :component wallet-swap-select-asset-to-pay/view}
{:name :screen/wallet.setup-swap
:options {:insets {:bottom? true}}
:component wallet-swap-setup-swap/view}
{:name :screen/wallet.swap-propasal {:name :screen/wallet.swap-propasal
:options {:insets {:top? true}} :options {:insets {:top? true}}
:component wallet-swap-propasal/view} :component wallet-swap-propasal/view}
+6 -2
View File
@@ -152,8 +152,12 @@
(fn [toasts [_ toast-id & cursor]] (fn [toasts [_ toast-id & cursor]]
(get-in toasts (into [:toasts toast-id] cursor)))) (get-in toasts (into [:toasts toast-id] cursor))))
(re-frame/reg-sub (re-frame/reg-sub :network/offline?
:network/offline?
:<- [:network/status] :<- [:network/status]
(fn [status] (fn [status]
(= status :offline))) (= status :offline)))
(re-frame/reg-sub :network/online?
:<- [:network/status]
(fn [status]
(= status :online)))
+5
View File
@@ -14,6 +14,11 @@
:<- [:wallet/swap] :<- [:wallet/swap]
:-> :asset-to-pay) :-> :asset-to-pay)
(rf/reg-sub
:wallet/swap-asset-to-receive
:<- [:wallet/swap]
:-> :asset-to-receive)
(rf/reg-sub (rf/reg-sub
:wallet/swap-asset-to-pay-token-symbol :wallet/swap-asset-to-pay-token-symbol
:<- [:wallet/swap-asset-to-pay] :<- [:wallet/swap-asset-to-pay]
+45
View File
@@ -64,6 +64,43 @@
:token-list-id "" :token-list-id ""
:built-on "ETH" :built-on "ETH"
:verified true} :verified true}
:asset-to-receive
{:description "Dai Stablecoin"
:decimals 18
:symbol "DAI"
:name "Dai Stablecoin"
:total-balance 1
:balances-per-chain
{1
{:raw-balance 1000000000000000000
:balance "1"
:chain-id 1}
10
{:raw-balance 0
:balance "0"
:chain-id 10}
42161
{:raw-balance 0
:balance "0"
:chain-id 42161}}
:networks (concat [(networks :mainnet-network)] (networks :layer-2-networks))
:chain-id 0
:market-values-per-currency
{:usd
{:change-24hour -0.00109422754667007
:change-pct-day -5.57352274163899
:change-pct-24hour -4.177805426737527
:high-day 0.0271858672171352
:market-cap 170783296.1155821
:has-error false
:change-pct-hour -0.0160462113709363
:low-day 0.02473516779550377
:price 0.0251}}
:asset-website-url "https://status.im/"
:available-balance 1
:token-list-id ""
:built-on "ETH"
:verified true}
:network nil}) :network nil})
(h/deftest-sub :wallet/swap (h/deftest-sub :wallet/swap
@@ -82,6 +119,14 @@
swap-data) swap-data)
(is (match? (swap-data :asset-to-pay) (rf/sub [sub-name]))))) (is (match? (swap-data :asset-to-pay) (rf/sub [sub-name])))))
(h/deftest-sub :wallet/swap-asset-to-receive
[sub-name]
(testing "Return swap asset-to-receive"
(swap! rf-db/app-db assoc-in
[:wallet :ui :swap]
swap-data)
(is (match? (swap-data :asset-to-receive) (rf/sub [sub-name])))))
(h/deftest-sub :wallet/swap-asset-to-pay-token-symbol (h/deftest-sub :wallet/swap-asset-to-pay-token-symbol
[sub-name] [sub-name]
(testing "Return asset-to-pay token symbol" (testing "Return asset-to-pay token symbol"
+30 -23
View File
@@ -48,11 +48,10 @@
:wallet/home-tokens-loading? :wallet/home-tokens-loading?
:<- [:wallet/tokens-loading] :<- [:wallet/tokens-loading]
(fn [tokens-loading] (fn [tokens-loading]
(or (empty? tokens-loading) (->> tokens-loading
(->> tokens-loading vals
vals (some true?)
(some true?) boolean)))
boolean))))
(rf/reg-sub (rf/reg-sub
:wallet/current-viewing-account-tokens-loading? :wallet/current-viewing-account-tokens-loading?
@@ -211,7 +210,7 @@
:wallet/fully-operable-keypairs-list :wallet/fully-operable-keypairs-list
:<- [:wallet/keypairs-list] :<- [:wallet/keypairs-list]
(fn [keypairs] (fn [keypairs]
(filter #(= :fully (:lowest-operability %)) keypairs))) (filter #(and (= :fully (:lowest-operability %)) (not-empty (:derived-from %))) keypairs)))
(rf/reg-sub (rf/reg-sub
:wallet/keypair-names :wallet/keypair-names
@@ -351,9 +350,10 @@
:<- [:wallet/accounts] :<- [:wallet/accounts]
:<- [:wallet/balances-in-selected-networks] :<- [:wallet/balances-in-selected-networks]
:<- [:wallet/tokens-loading] :<- [:wallet/tokens-loading]
:<- [:network/online?]
:<- [:profile/currency-symbol] :<- [:profile/currency-symbol]
:<- [:wallet/keypairs] :<- [:wallet/keypairs]
(fn [[accounts balances tokens-loading currency-symbol keypairs]] (fn [[accounts balances tokens-loading online? currency-symbol keypairs]]
(mapv (fn [{:keys [color address watch-only? key-uid operable] :as account}] (mapv (fn [{:keys [color address watch-only? key-uid operable] :as account}]
(let [account-type (cond (let [account-type (cond
(= operable :no) :missing-keypair (= operable :no) :missing-keypair
@@ -373,8 +373,9 @@
account account
keypair)}])) keypair)}]))
#(rf/dispatch [:wallet/navigate-to-account address])) #(rf/dispatch [:wallet/navigate-to-account address]))
:loading? (or (get tokens-loading address) :loading? (and online?
(not (contains? tokens-loading address))) (or (get tokens-loading address)
(not (contains? tokens-loading address))))
:balance (utils/prettify-balance currency-symbol :balance (utils/prettify-balance currency-symbol
(get balances address))))) (get balances address)))))
accounts))) accounts)))
@@ -690,23 +691,29 @@
(rf/reg-sub (rf/reg-sub
:wallet/wallet-send-fee-fiat-formatted :wallet/wallet-send-fee-fiat-formatted
:<- [:wallet/current-viewing-account]
:<- [:wallet/wallet-send-route] :<- [:wallet/wallet-send-route]
:<- [:profile/currency] :<- [:profile/currency]
:<- [:profile/currency-symbol] :<- [:profile/currency-symbol]
(fn [[route currency currency-symbol] [_ token-for-fees]] (fn [[account route currency currency-symbol] [_ token-symbol-for-fees]]
(let [fee-in-native-token (send-utils/calculate-full-route-gas-fee route) (when token-symbol-for-fees
fee-in-crypto-formatted (utils/get-standard-crypto-format (let [tokens (:tokens account)
token-for-fees token-for-fees (first (filter #(= (string/lower-case (:symbol %))
fee-in-native-token) (string/lower-case token-symbol-for-fees))
fee-in-fiat (utils/calculate-token-fiat-value tokens))
{:currency currency fee-in-native-token (send-utils/calculate-full-route-gas-fee route)
:balance fee-in-native-token fee-in-crypto-formatted (utils/get-standard-crypto-format
:token token-for-fees}) token-for-fees
fee-formatted (utils/get-standard-fiat-format fee-in-native-token)
fee-in-crypto-formatted fee-in-fiat (utils/calculate-token-fiat-value
currency-symbol {:currency currency
fee-in-fiat)] :balance fee-in-native-token
fee-formatted))) :token token-for-fees})
fee-formatted (utils/get-standard-fiat-format
fee-in-crypto-formatted
currency-symbol
fee-in-fiat)]
fee-formatted))))
(rf/reg-sub (rf/reg-sub
:wallet/has-partially-operable-accounts? :wallet/has-partially-operable-accounts?
@@ -133,8 +133,7 @@
max-fees-fiat (wallet-utils/calculate-token-fiat-value {:currency currency max-fees-fiat (wallet-utils/calculate-token-fiat-value {:currency currency
:balance max-fees-ether :balance max-fees-ether
:token eth-token}) :token eth-token})
max-fees-fiat-formatted (-> max-fees-ether max-fees-fiat-formatted (-> (wallet-utils/get-standard-crypto-format eth-token max-fees-ether)
(wallet-utils/get-standard-crypto-format eth-token)
(wallet-utils/get-standard-fiat-format currency-symbol (wallet-utils/get-standard-fiat-format currency-symbol
max-fees-fiat)) max-fees-fiat))
balance (-> eth-token balance (-> eth-token
+25 -19
View File
@@ -13,17 +13,20 @@
{:before #(reset! rf-db/app-db {})}) {:before #(reset! rf-db/app-db {})})
(def ^:private accounts-with-tokens (def ^:private accounts-with-tokens
{:0x1 {:tokens [{:symbol "ETH" {:0x1 {:tokens [{:symbol "ETH"
:balances-per-chain {1 {:raw-balance "100"}}} :balances-per-chain {1 {:raw-balance "100"}}
{:symbol "SNT" :market-values-per-currency {:usd {:price 10000}}}
:balances-per-chain {1 {:raw-balance "100"}}}] {:symbol "SNT"
:balances-per-chain {1 {:raw-balance "100"}}
:market-values-per-currency {:usd {:price 10000}}}]
:network-preferences-names #{} :network-preferences-names #{}
:customization-color nil :customization-color nil
:operable? true :operable? true
:operable :fully :operable :fully
:address "0x1"} :address "0x1"}
:0x2 {:tokens [{:symbol "SNT" :0x2 {:tokens [{:symbol "SNT"
:balances-per-chain {1 {:raw-balance "200"}}}] :balances-per-chain {1 {:raw-balance "200"}}
:market-values-per-currency {:usd {:price 10000}}}]
:network-preferences-names #{} :network-preferences-names #{}
:customization-color nil :customization-color nil
:operable? true :operable? true
@@ -501,10 +504,12 @@
(assoc-in [:wallet :ui :send :token-symbol] "ETH"))) (assoc-in [:wallet :ui :send :token-symbol] "ETH")))
(let [result (rf/sub [sub-name])] (let [result (rf/sub [sub-name])]
(is (match? result (is (match? result
[{:tokens [{:symbol "ETH" [{:tokens [{:symbol "ETH"
:balances-per-chain {1 {:raw-balance "100"}}} :balances-per-chain {1 {:raw-balance "100"}}
{:symbol "SNT" :market-values-per-currency {:usd {:price 10000}}}
:balances-per-chain {1 {:raw-balance "100"}}}] {:symbol "SNT"
:balances-per-chain {1 {:raw-balance "100"}}
:market-values-per-currency {:usd {:price 10000}}}]
:network-preferences-names #{} :network-preferences-names #{}
:customization-color nil :customization-color nil
:operable? true :operable? true
@@ -518,10 +523,12 @@
(assoc-in [:wallet :ui :send :token] {:symbol "ETH"}))) (assoc-in [:wallet :ui :send :token] {:symbol "ETH"})))
(let [result (rf/sub [sub-name])] (let [result (rf/sub [sub-name])]
(is (match? result (is (match? result
[{:tokens [{:symbol "ETH" [{:tokens [{:symbol "ETH"
:balances-per-chain {1 {:raw-balance "100"}}} :balances-per-chain {1 {:raw-balance "100"}}
{:symbol "SNT" :market-values-per-currency {:usd {:price 10000}}}
:balances-per-chain {1 {:raw-balance "100"}}}] {:symbol "SNT"
:balances-per-chain {1 {:raw-balance "100"}}
:market-values-per-currency {:usd {:price 10000}}}]
:network-preferences-names #{} :network-preferences-names #{}
:customization-color nil :customization-color nil
:operable? true :operable? true
@@ -959,15 +966,14 @@
(testing "wallet send fee calculated and formatted in fiat" (testing "wallet send fee calculated and formatted in fiat"
(swap! rf-db/app-db (swap! rf-db/app-db
#(-> % #(-> %
(assoc-in [:wallet :accounts] accounts-with-tokens)
(assoc-in [:wallet :current-viewing-account-address] "0x1")
(assoc-in [:wallet :ui :send :route] route-data) (assoc-in [:wallet :ui :send :route] route-data)
(assoc-in [:profile/profile :currency] :usd) (assoc-in [:profile/profile :currency] :usd)
(assoc-in [:profile/profile :currency-symbol] "$"))) (assoc-in [:profile/profile :currency-symbol] "$")))
(let [token-for-fees {:decimals 18 (let [token-symbol-for-fees "ETH"
:symbol "ETH" result (rf/sub [sub-name token-symbol-for-fees])]
:name "Ether"
:market-values-per-currency {:usd {:price 10000}}}
result (rf/sub [sub-name token-for-fees])]
(is (match? result "$1.00"))))) (is (match? result "$1.00")))))
(h/deftest-sub :wallet/has-partially-operable-accounts? (h/deftest-sub :wallet/has-partially-operable-accounts?
+2
View File
@@ -6,6 +6,8 @@
prefer to use it for more general purpose concepts, such as the re-frame event prefer to use it for more general purpose concepts, such as the re-frame event
layer." layer."
(:require-macros test-helpers.unit) (:require-macros test-helpers.unit)
;; We must require test-helpers.matchers namespace to register the custom cljs.test directive
;; `match-strict?`
(:require (:require
[re-frame.core :as rf] [re-frame.core :as rf]
[re-frame.db :as rf-db] [re-frame.db :as rf-db]
+9 -2
View File
@@ -55,8 +55,9 @@
(.lessThan ^js bn1 bn2)) (.lessThan ^js bn1 bn2))
(defn equal-to (defn equal-to
[bn1 bn2] [n1 n2]
(.eq ^js bn1 bn2)) (when-let [^js bn1 (bignumber n1)]
(.eq ^js bn1 n2)))
(extend-type BigNumber (extend-type BigNumber
IEquiv IEquiv
@@ -262,6 +263,12 @@
[bn1 bn2] [bn1 bn2]
(.round (.dividedBy ^js bn1 bn2) 0)) (.round (.dividedBy ^js bn1 bn2) 0))
(defn fiat->crypto
[crypto fiat-price]
(when-let [crypto-bn (bignumber crypto)]
(div crypto-bn
(bignumber fiat-price))))
(defn absolute-value (defn absolute-value
[bn] [bn]
(when bn (when bn
+3 -3
View File
@@ -3,7 +3,7 @@
"_comment": "Instead use: scripts/update-status-go.sh <rev>", "_comment": "Instead use: scripts/update-status-go.sh <rev>",
"owner": "status-im", "owner": "status-im",
"repo": "status-go", "repo": "status-go",
"version": "feat/close-hystrix-on-going-online", "version": "v0.184.47",
"commit-sha1": "10a12762c21e9124d623a6619e8cac90116dc1b9", "commit-sha1": "83aa01c7daf69d518e624fc584ea22ca27b34b73",
"src-sha256": "0r1zxnfkbf16fry1nysxpl00d88r9fr59b6h07fnzp5nkpzzyxjb" "src-sha256": "07gqijx652ap0lf0kvcpd0lxx5x58air19pxgjawwymq2b8kly16"
} }
+18 -10
View File
@@ -1,11 +1,13 @@
import logging import logging
import time import time
from datetime import datetime
from decimal import Decimal from decimal import Decimal
from json import JSONDecodeError from json import JSONDecodeError
from os import environ from os import environ
from typing import List from typing import List
import pytest import pytest
import pytz
import requests import requests
from selenium.common import TimeoutException from selenium.common import TimeoutException
@@ -38,7 +40,7 @@ class NetworkApi:
return self.send_etherscan_request(params) return self.send_etherscan_request(params)
def get_transactions(self, address: str) -> List[dict]: def get_transactions(self, address: str) -> List[dict]:
params = {'module': 'account', 'action': 'txlist', 'address': address, 'sort': 'desc'} params = {'module': 'account', 'action': 'txlist', 'address': address, 'page': 1, 'offset': 10, 'sort': 'desc'}
return self.send_etherscan_request(params) return self.send_etherscan_request(params)
def is_transaction_successful(self, transaction_hash: str) -> int: def is_transaction_successful(self, transaction_hash: str) -> int:
@@ -106,22 +108,28 @@ class NetworkApi:
self.log("Failed iterate transactions(Etherscan unexpected error): " + str(e)) self.log("Failed iterate transactions(Etherscan unexpected error): " + str(e))
continue continue
def wait_for_confirmation_of_transaction(self, address, amount, confirmations=6, token=False): def wait_for_confirmation_of_transaction(self, address, tx_time, confirmations=7, token=False):
expected_tx_timestamp = datetime.strptime(tx_time, "%Y-%m-%dT%H:%M:%S%z").replace(tzinfo=pytz.UTC)
start_time = time.time() start_time = time.time()
if token: if token:
token_info = "token transaction" token_info = "token transaction"
else: else:
token_info = "ETH transaction" token_info = "ETH transaction"
self.log('Waiting %s %s for %s to have %s confirmations' % (amount, token_info, address, confirmations)) self.log('Waiting for %s of %s to have %s confirmations' % (token_info, address, confirmations))
while round(time.time() - start_time, ndigits=2) < 600: # should be < idleTimeout capability while round(time.time() - start_time, ndigits=2) < 600: # should be < idleTimeout capability
transaction = self.find_transaction_by_unique_amount(address, amount, token) if token:
transaction = self.get_token_transactions(address)[0]
else:
transaction = self.get_transactions(address)[0]
tx_timestamp = datetime.fromtimestamp(int(transaction['timeStamp'])).replace(tzinfo=pytz.UTC)
if tx_timestamp > expected_tx_timestamp:
if int(transaction['confirmations']) >= confirmations:
return
time.sleep(20)
self.log( self.log(
'Expected amount of confirmations is %s, in fact %s' % (confirmations, transaction['confirmations'])) 'Expected amount of confirmations is %s, in fact %s' % (confirmations, transaction['confirmations']))
if int(transaction['confirmations']) >= confirmations: pytest.fail('The last transaction was not confirmed, address is %s, still has %s confirmations' % (
return address, int(transaction['confirmations'])))
time.sleep(20)
pytest.fail('Transaction with amount %s was not confirmed, address is %s, still has %s confirmations' % (
amount, address, int(transaction['confirmations'])))
def verify_balance_is_updated(self, initial_balance, recipient_address, wait_time=360): def verify_balance_is_updated(self, initial_balance, recipient_address, wait_time=360):
counter = 0 counter = 0
@@ -142,7 +150,7 @@ class NetworkApi:
balance = round(self.get_balance(address), 4) balance = round(self.get_balance(address), 4)
if balance == expected_balance: if balance == expected_balance:
return return
time.sleep(10) time.sleep(20)
raise TimeoutException( raise TimeoutException(
'balance is not updated on Etherscan, it is %s but expected to be %s' % (balance, expected_balance)) 'balance is not updated on Etherscan, it is %s but expected to be %s' % (balance, expected_balance))
@@ -264,15 +264,15 @@ class TestCommunityOneDeviceMerged(MultipleSharedDeviceTestCase):
expected_communities = { expected_communities = {
# ' 0xUX': ['Design', 'Ethereum', 'Collaboration'], # ' 0xUX': ['Design', 'Ethereum', 'Collaboration'],
'Status': ['Web3', 'Blockchain', 'Ethereum'], 'Status': ['Web3', 'Blockchain', 'Ethereum'],
'Status Inu': ['News', 'Social', 'Web3'], # 'Status Inu': ['News', 'Social', 'Web3'],
} }
for community_name, tags in expected_communities.items(): for community_name, tags in expected_communities.items():
self.home.just_fyi("Check %s community tags in the Discover communities screen" % community_name) self.home.just_fyi("Check %s community tags in the Discover communities screen" % community_name)
card = self.home.get_discover_community_card_by_name(community_name=community_name) card = self.home.get_discover_community_card_by_name(community_name=community_name)
try: try:
card.wait_for_visibility_of_element(30) card.wait_for_visibility_of_element(30)
if community_name == 'Status': # if community_name == 'Status':
card.swipe_to_web_element() # card.swipe_to_web_element()
missing_tags = list() missing_tags = list()
for text in tags: for text in tags:
try: try:
@@ -281,18 +281,17 @@ class TestCommunityOneDeviceMerged(MultipleSharedDeviceTestCase):
missing_tags.append(text) missing_tags.append(text)
if missing_tags: if missing_tags:
self.errors.append("Community '%s' is missing tag(s) %s." % (community_name, ','.join(tags))) self.errors.append("Community '%s' is missing tag(s) %s." % (community_name, ','.join(tags)))
# if community_name == 'Status':
self.home.just_fyi("Check Status community screen")
card.click()
if self.community_view.join_button.is_element_differs_from_template(
'status_community_join_button.png'):
self.errors.append("Status community Join button is different from expected template.")
if self.community_view.community_logo.is_element_differs_from_template('status_community_logo.png'):
self.errors.append("Status community logo is different from expected template.")
if community_name == 'Status': # self.community_view.close_community_view_button.click()
self.home.just_fyi("Check Status community screen") # self.home.swipe_up()
card.click()
if self.community_view.join_button.is_element_differs_from_template(
'status_community_join_button.png'):
self.errors.append("Status community Join button is different from expected template.")
if self.community_view.community_logo.is_element_differs_from_template('status_community_logo.png'):
self.errors.append("Status community logo is different from expected template.")
self.community_view.close_community_view_button.click()
self.home.swipe_up()
except TimeoutException: except TimeoutException:
self.errors.append("Community '%s' is not in the Discover Communities list." % community_name) self.errors.append("Community '%s' is not in the Discover Communities list." % community_name)
@@ -17,13 +17,14 @@ class TestDeepLinksOneDevice(MultipleSharedDeviceTestCase):
self.home = self.sign_in.create_user(username=self.username) self.home = self.sign_in.create_user(username=self.username)
self.home.communities_tab.click_until_presence_of_element(self.home.plus_community_button) self.home.communities_tab.click_until_presence_of_element(self.home.plus_community_button)
self.community_name = "open community" self.open_community_name = "open community"
self.channel_name = "general" self.channel_name = "general"
self.community = self.home.create_community(community_type="open") self.community = self.home.create_community(community_type="open")
self.profile_view = self.home.get_profile_view() self.profile_view = self.home.get_profile_view()
self.browser_view = self.home.get_dapp_view() self.browser_view = self.home.get_dapp_view()
self.home.get_chat(self.community_name, community=True).click() self.home.get_chat(self.open_community_name, community=True).click()
self.community_view = self.home.get_community_view() self.community_view = self.home.get_community_view()
self.open_community_url = self.community_view.copy_community_link()
self.channel = self.community_view.get_channel(self.channel_name).click() self.channel = self.community_view.get_channel(self.channel_name).click()
@marks.testrail_id(704613) @marks.testrail_id(704613)
@@ -39,7 +40,6 @@ class TestDeepLinksOneDevice(MultipleSharedDeviceTestCase):
self.channel.send_message(url) self.channel.send_message(url)
self.channel.chat_element_by_text(url).click_on_link_inside_message_body() self.channel.chat_element_by_text(url).click_on_link_inside_message_body()
if self.channel.profile_send_contact_request_button.is_element_displayed(10): if self.channel.profile_send_contact_request_button.is_element_displayed(10):
# username_text = self.profile_view.default_username_text.text
username_text = self.profile_view.contact_name_text.text username_text = self.profile_view.contact_name_text.text
if not (username_text.endswith(url[-6:]) or username_text == text): if not (username_text.endswith(url[-6:]) or username_text == text):
self.errors.append("Incorrect username is shown for profile url %s" % url) self.errors.append("Incorrect username is shown for profile url %s" % url)
@@ -47,34 +47,10 @@ class TestDeepLinksOneDevice(MultipleSharedDeviceTestCase):
self.errors.append("Profile was not opened by the profile url %s" % url) self.errors.append("Profile was not opened by the profile url %s" % url)
self.profile_view.close_button.click() self.profile_view.close_button.click()
community_urls = {
"https://status.app/c/ixyACjgKDVNOVCBjb21tdW5pdHkSHHJlcXVpcmUgMTAgU05UIEdvZXJsaSB0byB1c2UYASIHI2VhYjcwMAM=#zQ3shUeGnhM33QW4g9JfYfeLFAH9ZwbDboNYn5exCR7S3ii1y":
"SNT community",
"https://status.app/c/G0UAAMTyNsn2QZDEG0EXftOl8pOEfwEBOOSA_YTfIk85xmADDgINGmxpUHAXzK36bN0fK42Xf4YD2yjPk1z2pbFwFw==#zQ3sheoNX5kiuM393TJ6xDnL57aQoiwFWEuJnazJ6W2eNuh9u":
"open community",
"https://status.app/c/G00AAGS9TbI9mSR-ZNmFrhRjNuEeXAAbcAIUaLLJyjMOG3ACJQ12oIHD78QhzO9s_T5bUeU7rnATWJg3mGgTUemrAg==#zQ3shp9f5M3uyMpwTi3rFpFrP6WCWmNsW9pgK9cjXVTaf2vgj":
"closed community"
}
for url, text in community_urls.items():
self.channel.just_fyi("Opening community '%s' by the url %s" % (text, url))
self.channel.chat_message_input.clear()
self.channel.send_message(url)
self.channel.chat_element_by_text(url).click_on_link_inside_message_body()
if text == 'SNT community':
if self.community_view.community_title.text != text:
self.errors.append("Community '%s' was not requested to join by the url %s" % (text, url))
else:
if not self.community_view.join_button.is_element_displayed(
10) or self.community_view.community_title.text != text:
self.errors.append("Community '%s' was not requested to join by the url %s" % (text, url))
if text != "closed community": # the last one
self.home.navigate_back_to_home_view()
self.home.get_to_community_channel_from_home(self.community_name)
self.errors.verify_no_errors() self.errors.verify_no_errors()
@marks.testrail_id(702775) @marks.testrail_id(702775)
def test_links_deep_links(self): def test_links_deep_links_profile(self):
self.home.navigate_back_to_home_view() self.home.navigate_back_to_home_view()
self.home.browser_tab.click() self.home.browser_tab.click()
@@ -87,7 +63,6 @@ class TestDeepLinksOneDevice(MultipleSharedDeviceTestCase):
for link, text in profile_links.items(): for link, text in profile_links.items():
self.channel.just_fyi("Opening profile link %s" % link) self.channel.just_fyi("Opening profile link %s" % link)
self.browser_view.open_url(link) self.browser_view.open_url(link)
# shown_name_text = self.profile_view.default_username_text.text
shown_name_text = self.profile_view.contact_name_text.text shown_name_text = self.profile_view.contact_name_text.text
if text: if text:
name_is_shown = shown_name_text == text or shown_name_text.endswith(link[-6:]) name_is_shown = shown_name_text == text or shown_name_text.endswith(link[-6:])
@@ -97,25 +72,43 @@ class TestDeepLinksOneDevice(MultipleSharedDeviceTestCase):
self.errors.append("Profile was not opened by the profile deep link %s" % link) self.errors.append("Profile was not opened by the profile deep link %s" % link)
self.browser_view.click_system_back_button() self.browser_view.click_system_back_button()
self.errors.verify_no_errors()
@marks.testrail_id(739307)
def test_deep_links_communities(self):
closed_community_name, snt_community_name = "closed community", "SNT community"
self.home.navigate_back_to_home_view()
self.home.communities_tab.click()
self.home.create_community(community_type="closed")
if not self.community_view.community_options_button.is_element_displayed():
self.home.get_chat(closed_community_name, community=True).click()
closed_community_url = self.community_view.copy_community_link()
self.home.navigate_back_to_home_view()
self.home.create_community(community_type="token-gated")
if not self.community_view.community_options_button.is_element_displayed():
self.home.get_chat(snt_community_name, community=True).click()
snt_community_url = self.community_view.copy_community_link()
self.home.reopen_app(sign_in=False)
self.sign_in.create_user(username="second user", first_user=False)
self.home.browser_tab.click()
old, new = "https://status.app/", "status-app://"
community_links = { community_links = {
"status.app://c/ixyACjgKDVNOVCBjb21tdW5pdHkSHHJlcXVpcmUgMTAgU05UIEdvZXJsaSB0byB1c2UYASIHI2VhYjcwMAM=#zQ3shUeGnhM33QW4g9JfYfeLFAH9ZwbDboNYn5exCR7S3ii1y": snt_community_name: snt_community_url.replace(old, new),
"SNT community", self.open_community_name: self.open_community_url.replace(old, new),
"status.app://c/G0UAAMTyNsn2QZDEG0EXftOl8pOEfwEBOOSA_YTfIk85xmADDgINGmxpUHAXzK36bN0fK42Xf4YD2yjPk1z2pbFwFw==#zQ3sheoNX5kiuM393TJ6xDnL57aQoiwFWEuJnazJ6W2eNuh9u": closed_community_name: closed_community_url.replace(old, new)
"open community",
"status.app://c/G00AAGS9TbI9mSR-ZNmFrhRjNuEeXAAbcAIUaLLJyjMOG3ACJQ12oIHD78QhzO9s_T5bUeU7rnATWJg3mGgTUemrAg==#zQ3shp9f5M3uyMpwTi3rFpFrP6WCWmNsW9pgK9cjXVTaf2vgj":
"closed community"
} }
for link, text in community_links.items(): for text, link in community_links.items():
self.channel.just_fyi("Opening community '%s' by the link %s" % (text, link)) self.channel.just_fyi("Opening community '%s' by the link %s" % (text, link))
self.browser_view.open_url(link) self.browser_view.open_url(link)
if text == 'SNT community': if text == snt_community_name:
if self.community_view.community_title.text != text: if self.community_view.community_title.text != text:
self.errors.append("Community '%s' was not requested to join by the deep link %s" % (text, link)) self.errors.append("Community '%s' was not requested to join by the deep link %s" % (text, link))
else: else:
if not self.community_view.join_button.is_element_displayed( if not self.community_view.join_button.is_element_displayed(
10) or self.community_view.community_title.text != text: 10) or self.community_view.community_title.text != text:
self.errors.append("Community '%s' was not requested to join by the deep link %s" % (text, link)) self.errors.append("Community '%s' was not requested to join by the deep link %s" % (text, link))
if text != "closed community": # the last one if text != closed_community_name: # the last one
self.home.navigate_back_to_home_view() self.home.navigate_back_to_home_view()
self.home.browser_tab.click() self.home.browser_tab.click()
+8 -2
View File
@@ -135,11 +135,14 @@ class TestWalletMultipleDevice(MultipleSharedDeviceTestCase):
self.wallet_1.just_fyi("Sending funds from wallet") self.wallet_1.just_fyi("Sending funds from wallet")
amount_to_send = 0.0001 amount_to_send = 0.0001
device_time_before_sending = self.wallet_1.driver.device_time
self.wallet_1.send_asset(address='arb1:' + self.receiver['wallet_address'], self.wallet_1.send_asset(address='arb1:' + self.receiver['wallet_address'],
asset_name='Ether', asset_name='Ether',
amount=amount_to_send) amount=amount_to_send)
self.network_api.wait_for_confirmation_of_transaction(address=self.sender['wallet_address'],
tx_time=device_time_before_sending)
device_time = self.wallet_1.driver.device_time device_time_after_sending = self.wallet_1.driver.device_time
self._check_balances_after_tx(amount_to_send, sender_balance, receiver_balance, eth_amount_sender, self._check_balances_after_tx(amount_to_send, sender_balance, receiver_balance, eth_amount_sender,
eth_amount_receiver) eth_amount_receiver)
@@ -164,10 +167,13 @@ class TestWalletMultipleDevice(MultipleSharedDeviceTestCase):
self.wallet_1.just_fyi("Sending asset from drawer") self.wallet_1.just_fyi("Sending asset from drawer")
amount_to_send = 0.0001 amount_to_send = 0.0001
device_time_before_sending = self.wallet_1.driver.device_time
self.wallet_1.send_asset_from_drawer(address='arb1:' + self.receiver['wallet_address'], self.wallet_1.send_asset_from_drawer(address='arb1:' + self.receiver['wallet_address'],
asset_name='Ether', asset_name='Ether',
amount=amount_to_send) amount=amount_to_send)
device_time = self.wallet_1.driver.device_time self.network_api.wait_for_confirmation_of_transaction(address=self.sender['wallet_address'],
tx_time=device_time_before_sending)
device_time_after_sending = self.wallet_1.driver.device_time
self._check_balances_after_tx(amount_to_send, sender_balance, receiver_balance, eth_amount_sender, self._check_balances_after_tx(amount_to_send, sender_balance, receiver_balance, eth_amount_sender,
eth_amount_receiver) eth_amount_receiver)
+5 -6
View File
@@ -372,7 +372,7 @@ class CommunityView(HomeView):
self.community_create_a_channel_button = Button(self.driver, accessibility_id="community-create-channel") self.community_create_a_channel_button = Button(self.driver, accessibility_id="community-create-channel")
self.channel_name_edit_box = EditBox(self.driver, translation_id="name-your-channel-placeholder") self.channel_name_edit_box = EditBox(self.driver, translation_id="name-your-channel-placeholder")
self.channel_descripton = ChatView(self.driver).community_description_edit_box self.channel_descripton = ChatView(self.driver).community_description_edit_box
self.community_options_button = Button(self.driver, accessibility_id="community-menu-button") self.community_options_button = Button(self.driver, accessibility_id="community-options-for-community")
self.view_members_button = Button(self.driver, accessibility_id="view-members") self.view_members_button = Button(self.driver, accessibility_id="view-members")
self.community_info_button = Button(self.driver, translation_id="community-info") self.community_info_button = Button(self.driver, translation_id="community-info")
self.invite_button = Button(self.driver, accessibility_id="community-invite-people") self.invite_button = Button(self.driver, accessibility_id="community-invite-people")
@@ -455,11 +455,10 @@ class CommunityView(HomeView):
def copy_community_link(self): def copy_community_link(self):
self.driver.info("Copy community link") self.driver.info("Copy community link")
self.community_options_button.click() self.community_options_button.click()
self.community_info_button.click() self.share_community_button.click()
self.element_starts_with_text('join.status.im/c/').click() text = self.sharing_text_native.text
community_link_text = self.element_starts_with_text('join.status.im/c/').text self.click_system_back_button(times=2)
self.home_button.double_click() return text
return 'https://%s' % community_link_text
def handle_membership_request(self, username: str, approve=True): def handle_membership_request(self, username: str, approve=True):
self.driver.info("Handling membership request of user '%s', approve='%s'" % (username, str(approve))) self.driver.info("Handling membership request of user '%s', approve='%s'" % (username, str(approve)))
Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 KiB

After

Width:  |  Height:  |  Size: 172 KiB

+6 -2
View File
@@ -290,8 +290,11 @@ class ProfileView(BaseView):
self.advertise_device_button = Button(self.driver, accessibility_id="advertise-device") self.advertise_device_button = Button(self.driver, accessibility_id="advertise-device")
self.sync_all_button = Button(self.driver, translation_id="sync-all-devices") self.sync_all_button = Button(self.driver, translation_id="sync-all-devices")
self.syncing_button = Button(self.driver, accessibility_id="icon, Syncing, label-component, icon") self.syncing_button = Button(self.driver, accessibility_id="icon, Syncing, label-component, icon")
self.sync_plus_button = Button(self.driver, self.paired_devices_button = Button(self.driver,
xpath="//*[@text='Syncing']/following-sibling::android.view.ViewGroup[1]") accessibility_id="icon, Paired devices, 0 devices, label-component, icon")
self.sync_plus_button = Button(
self.driver,
xpath="//*[@text='Paired devices']/following-sibling::android.view.ViewGroup[@content-desc='icon']")
# Keycard # Keycard
self.keycard_button = Button(self.driver, accessibility_id="keycard-button") self.keycard_button = Button(self.driver, accessibility_id="keycard-button")
@@ -539,6 +542,7 @@ class ProfileView(BaseView):
def get_sync_code(self): def get_sync_code(self):
self.syncing_button.scroll_and_click() self.syncing_button.scroll_and_click()
self.paired_devices_button.click()
self.sync_plus_button.click() self.sync_plus_button.click()
for checkbox in Button( for checkbox in Button(
self.driver, self.driver,
+8 -3
View File
@@ -263,11 +263,15 @@ class SignInView(BaseView):
# self.next_button.click() # self.next_button.click()
# self.identifiers_button.wait_and_click(30) # self.identifiers_button.wait_and_click(30)
if enable_notifications: if enable_notifications:
self.enable_notifications_button.click_until_presence_of_element(self.start_button) self.enable_notifications_button.wait_and_click()
if self.allow_button.is_element_displayed(): for _ in range(3):
self.allow_button.click() self.allow_button.click_if_shown(sec=10)
self.enable_notifications_button.click_if_shown()
if self.start_button.is_element_displayed():
break
else: else:
self.maybe_later_button.click_until_presence_of_element(self.start_button) self.maybe_later_button.click_until_presence_of_element(self.start_button)
self.cancel_button.click_if_shown() # TODO: remove when issue 20806 is fixed
self.start_button.click() self.start_button.click()
self.chats_tab.wait_for_visibility_of_element(30) self.chats_tab.wait_for_visibility_of_element(30)
self.driver.info("## New multiaccount is created successfully!", device=False) self.driver.info("## New multiaccount is created successfully!", device=False)
@@ -297,6 +301,7 @@ class SignInView(BaseView):
self.enable_notifications_button.click_until_presence_of_element(self.start_button) self.enable_notifications_button.click_until_presence_of_element(self.start_button)
else: else:
self.maybe_later_button.click_until_presence_of_element(self.start_button) self.maybe_later_button.click_until_presence_of_element(self.start_button)
self.cancel_button.click_if_shown() # TODO: remove when issue 20806 is fixed
self.start_button.click() self.start_button.click()
self.chats_tab.wait_for_visibility_of_element(30) self.chats_tab.wait_for_visibility_of_element(30)
self.driver.info("## Multiaccount is recovered successfully!", device=False) self.driver.info("## Multiaccount is recovered successfully!", device=False)
+8 -2
View File
@@ -1,3 +1,5 @@
import time
import pytest import pytest
from tests import common_password from tests import common_password
@@ -120,7 +122,12 @@ class WalletView(BaseView):
self.login_button.click() self.login_button.click()
def confirm_transaction(self): def confirm_transaction(self):
self.confirm_button.click_until_presence_of_element(self.slide_button_track) self.confirm_button.click()
for _ in range(3):
if self.slide_button_track.is_element_displayed():
break
time.sleep(1)
self.confirm_button.click()
self.slide_and_confirm_with_password() self.slide_and_confirm_with_password()
def set_amount(self, amount: float): def set_amount(self, amount: float):
@@ -160,7 +167,6 @@ class WalletView(BaseView):
self.add_account_button.click() self.add_account_button.click()
self.add_account_to_watch.click() self.add_account_to_watch.click()
self.address_to_watch_input.send_keys(address) self.address_to_watch_input.send_keys(address)
self.account_has_activity_label.wait_for_visibility_of_element()
self.add_account_continue_button.click() self.add_account_continue_button.click()
SignInView(self.driver).profile_title_input.send_keys(account_name) SignInView(self.driver).profile_title_input.send_keys(account_name)
self.add_watched_address_button.click() self.add_watched_address_button.click()
+6
View File
@@ -1038,6 +1038,7 @@
"free": "↓ Free", "free": "↓ Free",
"fri": "Fri", "fri": "Fri",
"from": "from", "from": "from",
"from-all-profiles-on-device": "From all profiles on device",
"from-capitalized": "From", "from-capitalized": "From",
"from-label": "From", "from-label": "From",
"gas-amount-limit": "Gas amount limit", "gas-amount-limit": "Gas amount limit",
@@ -1478,6 +1479,7 @@
"max-fees": "Max fees", "max-fees": "Max fees",
"max-priority-fee": "Max priority fee", "max-priority-fee": "Max priority fee",
"max-slippage": "Max slippage", "max-slippage": "Max slippage",
"max-token": "Max: {{number}} {{token-symbol}}",
"maximum-fee": "Maximum fee", "maximum-fee": "Maximum fee",
"maximum-fee-desc": "Maximum overall price for the transaction. If the current block base fee exceeds this, your transaction will be included in a following block with a lower base fee.", "maximum-fee-desc": "Maximum overall price for the transaction. If the current block base fee exceeds this, your transaction will be included in a following block with a lower base fee.",
"may": "May", "may": "May",
@@ -1569,6 +1571,8 @@
"mobile-syncing-sheet-title": "Sync using mobile data?", "mobile-syncing-sheet-title": "Sync using mobile data?",
"mon": "Mon", "mon": "Mon",
"more": "more", "more": "more",
"more-details-in-privacy-policy-1": "For more details refer to our ",
"more-details-in-privacy-policy-2": "Privacy Policy",
"move-and-reset": "Move and Reset", "move-and-reset": "Move and Reset",
"move-keystore-file": "Move keystore file", "move-keystore-file": "Move keystore file",
"move-keystore-file-to-keycard": "Move keystore file to keycard?", "move-keystore-file-to-keycard": "Move keystore file to keycard?",
@@ -2070,6 +2074,7 @@
"reveal-sync-code": "Reveal sync code", "reveal-sync-code": "Reveal sync code",
"review-bridge": "Review bridge", "review-bridge": "Review bridge",
"review-send": "Review send", "review-send": "Review send",
"review-swap": "Review swap",
"revoke-access": "Revoke access", "revoke-access": "Revoke access",
"ropsten-testnet": "Ropsten Testnet", "ropsten-testnet": "Ropsten Testnet",
"rpc-usage-copy": "Copy", "rpc-usage-copy": "Copy",
@@ -2565,6 +2570,7 @@
"updates-to-tos": "Updates to Terms of Use", "updates-to-tos": "Updates to Terms of Use",
"updates-to-tos-desc": "Before you continue, please review the Terms of Use and confirm you take full responsibility for how you use the app.", "updates-to-tos-desc": "Before you continue, please review the Terms of Use and confirm you take full responsibility for how you use the app.",
"url": "URL", "url": "URL",
"usage-data-shared-from-all-profiles": "Usage data will be shared from all profiles added to device. ",
"usd-currency": "USD", "usd-currency": "USD",
"use-as-profile-picture": "Use as profile picture", "use-as-profile-picture": "Use as profile picture",
"use-biometrics": "Use biometrics to fill in your password", "use-biometrics": "Use biometrics to fill in your password",