Compare commits

..
Author SHA1 Message Date
alwx 823a683dab Attempt to fix "Undefined is not a function" when declining connection request 2024-07-24 10:32:50 +02:00
Icaro Motta cef1308b3a chore(tests): Allow test-watch-for-repl target to run while the app is running (#20827)
Fix a long standing problem where we can't run in parallel the make target
run-clojure and test-watch-for-repl, or in other words, we can't run tests via
the REPL while the app is running.

Details:

I found out that shadow-cljs fails to build because it does not expand the
environment variables SHADOW_OUTPUT_TO and SHADOW_NS_REGEXP if and only if the
run-clojure target was executed.

This is the top of the stacktrace, and it happens because it doesn't know where
to output the test build because the env var wasn't expanded:

    [build] NullPointerException:
    [build]         shadow.build.node/configure (node.clj:59)
    [build]         shadow.build.node/configure (node.clj:45)
    [build]         shadow.build.targets.node-script/configure (node_script.clj:37)

The solution is to pass the option --config-merge to shadow-cljs and override
both :ns-regexp and :output-to because CLI args override options from env vars
in shadow-cljs.
2024-07-24 00:18:27 -03:00
Icaro Motta 4b8a612df4 chore(docs): Document some of our existing testing practices (#20691)
Document some of our current testing practices in hopes of helping reduce
friction in PRs and communication in general. In theory, nothing in the text
should be a surprise because these are things we have been discussing over many
months (some things for almost 1.5 years) and are already present in the code.
2024-07-23 23:45:14 -03:00
Ulises Manuel e4639c153f feat(wallet): Skip From page while sending an asset only owned by one account (#20773)
- Update tests

- Omit from page while sending a token in home page

- Hide send and bridge option for not owned tokens

- Fix subscription to return accounts owning an asset
2024-07-23 16:08:33 -06:00
Alexander 8597b899bd Remove dApps tab from Wallet (#20818) 2024-07-23 18:57:27 +03:00
John Ngei a5c8182461 fix onboarding navigation to enable notification screen and blur issues (#20725) 2024-07-23 15:18:43 +02:00
mmilad75 89bb3ea5c3 Watch-only collectibles should not be included on the main wallet page by default #20735 (#20761) 2024-07-23 14:45:02 +02:00
Shivek Khurana 3bb7c308e3 🦁 Fix issues with missing dapp images and dapp names (#20811)
* 🦁 Beast mode

* 🗑️ Remove `prn` calls

* 🌎 Add utils to remove http:// prefix

* ⏭️ Extracted name and avatar computation

* 🧹 Cleanup

* ✏️ Fix lint and remove REPL flow

*  Remove broken test

* 🖊️ Fix lint
2024-07-23 17:04:03 +05:30
Parvesh Monu 1a4987478c fix UI elements are misplaced on the 'Sign in by syncing' screen when adding a new profile (#20837) 2024-07-23 16:09:04 +05:30
Parvesh Monu 540094c229 fix Missing 'Mark as read' option on community channels (#20838) 2024-07-22 22:55:50 +05:30
Alexander 08b65cbcb7 Proper handling of disconnection, both from the dapp itself and from the list of connected dapps (#20817) 2024-07-22 15:20:52 +02:00
AlexanderandLungu Cristian d623cc8444 Only show the Wallet Connect requests for the logged in user (#20815)
Co-authored-by: Lungu Cristian <lungucristian95@gmail.com>
2024-07-22 14:57:45 +02:00
Brian Sztamfater 4b7c906df2 feat(swap): set spending cap screen (#20727)
Signed-off-by: Brian Sztamfater <brian@status.im>
2024-07-22 08:50:29 -03:00
Omar Basem 3b446963ef fix: assets sort order on bridge and assets screen (#20813)
* fix: assets sort order on bridge and assets screen (#20813)
2024-07-22 14:16:40 +04:00
Icaro Motta cec46f985a chore(style): Sort JSON keys in translations/en.json (#20785)
Sorts keys (non-recursively) in translations/en.json. The solution uses
the prettier plugin https://github.com/Gudahtt/prettier-plugin-sort-json.

We only format translations/en.json because it’s the only file we need to
manually change.

- Dependency added: prettier-plugin-sort-json version 4.0.0
- Dependency upgraded: prettier, from version 2.8.8 to 3.3.3.
2024-07-21 22:39:50 -03:00
Volodymyr Kozieiev 06b2996167 Do not update disabled networks when receiver network updated (#20756) 2024-07-19 15:10:13 +01:00
Lungu Cristian 02e24208db Wallet Connect transactions (#20755)
* feat: updated signing endpoints and refactor

https://github.com/status-im/status-go/compare/6e056348...e8aec741

* fix: using the generic warning at the bottom

* fix: show parsed transaction parameters

* feat: adding fees to transactions

https://github.com/status-im/status-go/compare/6e056348...b2e5e7a8

* feat: added fees

* feat: added eip-1559 fee estimation & tx priority

* feat: added fees and failed processing handling

* fix: show testnet name in the request

* fix: address review comments

* feat: added max-fee color when not enough balance

* ref: broke down tx fees subscription

* fix: handle gas estimation on status-go

https://github.com/status-im/status-go/compare/1ef2434b...5389f281

* fix: don't overwrite dynamic fees if already there

* fix: malli schema and review comments

* fix: addressed review comments 1

* fix: addressed comments 2

* fix: removed unused require

* fix: addressed QA review

https://github.com/status-im/status-go/compare/484b8aca...d07f9b5b

* fix: requests being shown simultaneously

* fix: removed support for eth_signTransaction
2024-07-19 14:16:51 +03:00
frank 84b8943fd4 fix_:still return the image when image server is unable to get public key (#20538)
https://github.com/status-im/status-go/compare/cba3ac57...4a43b2b2
2024-07-19 17:17:53 +08:00
Ajay Sivan c52b3e457e Wallet/Swap Input Component (#20318) 2024-07-19 01:00:38 -07:00
Omar Basem 21cc8a199a fix: button width (#20792)
* fix: button width (#20792)
2024-07-19 08:13:50 +04:00
Yevheniia Berdnyk 59374543ef e2e: disabled activity verification in wallet tests 2024-07-18 19:45:20 +03:00
Alexander 9f84a7b912 A pack of bugfixes for the user story "Connect to dApps" (#20711) 2024-07-18 14:57:53 +02:00
Vit∀ly Vlasov 2b0847ef76 fix_: ui part for share-all-future-addresses storage (PR #20549) (#20549)
Related status-go PR: https://github.com/status-im/status-go/pull/5354

https://github.com/status-im/status-go/compare/6e056348...8458cafe

Signed-off-by: Vitaly Vlasov <mail@vitv.ly>
2024-07-18 15:36:38 +03:00
Yevheniia Berdnyk 97b19c5440 e2e: fixes for wallet and deep/universal links tests 2024-07-18 00:52:06 +03:00
Icaro Motta b3e88508ac chore(activity-center): Various small refactors (#20690)
Potentially a solution to https://github.com/status-im/status-mobile/issues/15706

- [x] Fixes swipe button on Android and iOS.
- [x] Performance: we now subscribe only to the minimum from each community.
      This could be the reason the AC would lag as described in the parent
      issue.
- [x] Performance: was able to use flex and removed swipe button height
      calculation that was using `onLayout` and was causing a re-render.
- [x] Performance: reduced the initial number of items to render in the flatlist
      from 10 to 7.
- [x] Performance: delay rendering the heavy list of notification components.
      See in the video below how slow it is to open the AC with just 6
      notifications and that the opening animation is never displayed. And then
      check the improved version with the artificial delay provided by
      `rn/delay-render`. By opening the AC first and animating, this gives the
      user something to look for, and hopefully a few milliseconds more to think
      the app is not stuck, which will be preciously used to render
      notifications.

We refactor all views in the AC to:

- [x] Follow our newest standards with React hooks.
- [x] Removed prop-drilling by creating a separate React context to store the
      current swipeable item (because we need to call `.close` on a `Gesture
      Swipeable` instance whenever a new swipeable opens.
2024-07-17 12:16:06 -03:00
Mohsen 7b09402fcb [#20742] fix: blue background behind the "Save address" button (#20754) 2024-07-17 16:04:40 +03:30
mmilad75 a912125ba9 Token value shows fiat value on the transaction confirmation page #20734 (#20770) 2024-07-17 10:47:27 +02:00
Ulises Manuel 2d0437dd5e fix(wallet): Improvements on activity tab (#20703)
* Fix schema for networks in context-tags

* Fix wallet-activity component overflowing the activity tab

* Improve robustness of the activity tab fetching mechanism

* Handle `wallet-activity-filtering-entries-updated` signal

* Improve processing of data received for the activity tab
2024-07-16 15:07:33 -06:00
Parvesh Monu 43651ef0d0 Adjust on-boarding illustrations position and scale (#20692) 2024-07-16 20:43:19 +05:30
Yevheniia Berdnyk 13232dc7b7 e2e: updated create profile flow 2024-07-16 15:46:23 +01:00
Andrea Maria Piana 93b5f7a918 Add confirmation to centralized metrics
This commit add a confirmation for centralized metrics.
It is added in 3 places:

1) Onboarding -> Create new account
2) Onboarding -> Sync profile
3) On the accounts view if the user is upgrading

To test 1 & 2, you should just be able to do that on a newly installed
device.

To test 3, you will have to upgrade from a PR without this feature that
has at least an account. It should show the confirmation modal until you
either click on Not now or Share usage data.

The modal should also be added in settings, but I will do that as a
separate PR.
2024-07-16 15:46:23 +01:00
Mohsen 4898d32243 [#20664] feat: add share usage data confirmation modal 2024-07-16 15:46:23 +01:00
Lungu Cristian 588692e0eb Wallet Connect message signing (#20693)
* feat: updated signing endpoints and refactor

https://github.com/status-im/status-go/compare/6e056348...e8aec741

* fix: address review comments
2024-07-16 14:36:16 +03:00
Nikolay bd9e440839 [18942] Slideshow: slider-bar component (#20615) 2024-07-16 12:42:38 +02:00
Ulises Manuel 940edc2e53 fix(wallet): Some collectibles were displayed as "can iOS can't fetch collectible error (#20714) 2024-07-15 12:34:02 -06:00
mmilad75 5a93cd755e Cover wallet send events with tests #20411 (#20533) 2024-07-15 14:56:50 +02:00
Yevheniia Berdnyk 942c130d23 e2e: separated tests to run in PRs 2024-07-15 12:46:18 +03:00
Andrea Maria Piana bb8aad3be8 set fleet to status.staging
https://github.com/status-im/status-go/compare/1ef2434b...484b8aca
2024-07-15 10:30:01 +01:00
Andrea Maria Piana ba76b9fd51 Allow setting store confirmations
This commit adds the ability to enable store confirmations in the UI,
under advanced settings.

https://github.com/status-im/status-go/compare/1ef2434b...0d1e5aae
2024-07-15 07:46:13 +01:00
Brian Sztamfater 5f085e9cea feat(swap): swap confirmation screen (#20669)
Signed-off-by: Brian Sztamfater <brian@status.im>
2024-07-14 00:51:02 -03:00
Mohamed Javid 12b8dcf1e7 fix(saved-addresses): grouping and sorting of saved addresses (#20689)
This commit fixes the grouping and sorting of saved addresses.

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2024-07-12 19:25:39 +05:30
Omar Basem 0e6d59b312 fix: hardware induced back actions (#20183)
* fix: hardware induced back actions (#20183)
2024-07-12 17:09:42 +04:00
Omar Basem 087fcb1010 Fix(wallet): navigation after transaction from settings (#20676)
* fix(wallet): navigation after transaction from settings (#20676)
2024-07-12 16:07:45 +04:00
Ajay Sivan 0de4c8cabb List-Items/Approval-Info Component (#20317) 2024-07-12 04:55:36 -07:00
Parvesh Monu 6553d5ca44 fix peer count invisible in Light mode (#20733) 2024-07-12 16:59:44 +05:30
Ajay Sivan 20b256b0d0 Settings/Data Item - editable variant (#20656) 2024-07-12 04:15:37 -07:00
Mohamed Javid 1c85b292f8 fix(community): remove non operable accounts in the selection of sharing addresses with the community (#20636)
This commit removes the non-operable accounts in the "Addresses for permission" and "Airdrop address" selections while joining a community

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2024-07-12 16:29:43 +05:30
flexsurfer d2e975f6d9 [#20434] Allow users from v1 to login with keycard (#20558) 2024-07-12 12:02:17 +02:00
Shivek KhuranaandLungu Cristian 721aa51d8d 🎣 Fix wallet connect init issue in release bundles (#20665)
* 👀 Added alerts so I peek into build

* 🆙 Update logs

* 🅰️ Annotate

* 📤 Don't refer

* 🖊️ Use default import

* added ^js

*  Added ocall

* 🎣 Isolate WC js calls to rn ns

* 🧯Fix on-scan-connection issue

---------

Co-authored-by: Lungu Cristian <lungucristian95@gmail.com>
2024-07-12 14:47:25 +05:30
Parvesh Monu 23c25d7715 Fix missing avatar and name in members of the community channels and group chats (#20619) 2024-07-12 11:12:49 +05:30
257 changed files with 8861 additions and 5423 deletions
-1
View File
@@ -4,7 +4,6 @@ DEFAULT_NETWORK=mainnet_rpc
DEV_BUILD=1
ETHEREUM_DEV_CLUSTER=1
EXTENSIONS=0
FLEET=status.prod
GROUP_CHATS_ENABLED=1
LOG_LEVEL=info
MAILSERVER_CONFIRMATIONS_ENABLED=1
+1 -1
View File
@@ -2,7 +2,7 @@ DEBUG_WEBVIEW=1
DEFAULT_NETWORK=goerli_rpc
ETHEREUM_DEV_CLUSTER=1
EXTENSIONS=0
FLEET=status.prod
FLEET=status.staging
GROUP_CHATS_ENABLED=1
LOG_LEVEL=debug
MAILSERVER_CONFIRMATIONS_ENABLED=0
-1
View File
@@ -3,7 +3,6 @@ DEBUG_WEBVIEW=1
DEFAULT_NETWORK=goerli_rpc
ETHEREUM_DEV_CLUSTER=1
EXTENSIONS=0
FLEET=status.prod
GROUP_CHATS_ENABLED=1
LOG_LEVEL=debug
MAILSERVER_CONFIRMATIONS_ENABLED=1
-1
View File
@@ -2,7 +2,6 @@ DEBUG_WEBVIEW=1
DEFAULT_NETWORK=mainnet_rpc
ETHEREUM_DEV_CLUSTER=1
EXTENSIONS=0
FLEET=status.prod
GROUP_CHATS_ENABLED=1
LOG_LEVEL=info
MAILSERVER_CONFIRMATIONS_ENABLED=1
-1
View File
@@ -2,7 +2,6 @@ DEBUG_WEBVIEW=0
DEFAULT_NETWORK=mainnet_rpc
ETHEREUM_DEV_CLUSTER=0
EXTENSIONS=0
FLEET=status.prod
GROUP_CHATS_ENABLED=1
LOG_LEVEL=
MAILSERVER_CONFIRMATIONS_ENABLED=1
+14 -9
View File
@@ -1,10 +1,15 @@
*
/*
# Format top-level js files.
!*.js
!*/
*.clj-kondo
*.shadow-cljs
modules
result
target
component-spec
/app
# Ignore all except src/js/**/*.js
!/src/
/src/*
!/src/js
!/src/js/**/*.js
# Ignore all except translations/en.json
!/translations/
/translations/*
!/translations/en.json
+4
View File
@@ -6,4 +6,8 @@ module.exports = {
tabWidth: 2,
trailingComma: 'all',
useTabs: false,
// JSON sorting
jsonSortOrder: '{ "/.*/": "caseInsensitiveLexical" } ',
plugins: ['prettier-plugin-sort-json'],
};
+6 -2
View File
@@ -317,7 +317,7 @@ lint: ##@test Run code style checks
scripts/lint/translations.clj && \
zprint '{:search-config? true}' -sfc $$ALL_CLOJURE_FILES && \
sh scripts/lint/trailing-newline.sh && \
node_modules/.bin/prettier --write .
node_modules/.bin/prettier --check .
# NOTE: We run the linter twice because of https://github.com/kkinnear/zprint/issues/271
lint-fix: export TARGET := clojure
@@ -353,13 +353,17 @@ test: export SHADOW_NS_REGEXP := .*-test$$
test: ##@test Run all Clojure tests
test: _test-clojure
# Note: we need to override the :output-to and :ns-regexp options because
# shadow-cljs has a bug where it will not read from the env vars to expand the
# configuration when the shadow-cljs mobile target is already running.
test-watch-for-repl: export TARGET := default
test-watch-for-repl: export SHADOW_OUTPUT_TO := target/test/test.js
test-watch-for-repl: export SHADOW_NS_REGEXP := .*-test$$
test-watch-for-repl: ##@test Watch all Clojure tests and support REPL connections
rm -f "$$SHADOW_OUTPUT_TO" && \
yarn install && shadow-cljs compile mocks && \
concurrently --kill-others --prefix-colors 'auto' --names 'build,repl' \
'yarn shadow-cljs watch test --verbose' \
"yarn shadow-cljs watch test --verbose --config-merge '{:output-to \"$(SHADOW_OUTPUT_TO)\" :ns-regexp \"$(SHADOW_NS_REGEXP)\"}'" \
"until [ -f $$SHADOW_OUTPUT_TO ] ; do sleep 1 ; done ; node --require ./test-resources/override.js $$SHADOW_OUTPUT_TO --repl"
test-unit: export SHADOW_OUTPUT_TO := target/unit_test/test.js
+1 -1
View File
@@ -99,7 +99,7 @@ pipeline {
--rerun_count=2 \
--testrail_report=True \
-m testrail_id \
-m \"new_ui_critical or new_ui_medium\" \
-m \"nightly\" \
-k \"${params.KEYWORD_EXPRESSION}\" \
--apk=${params.APK_URL ?: apk_path}
"""
+1 -3
View File
@@ -35,13 +35,11 @@ pipeline {
description: 'OBSOLETE ARGUMENT TO BE REMOVED',
defaultValue: 'DUMMY',
)
/* Commented to use TEST_MARKERS values from job params
string(
name: 'TEST_MARKERS',
description: 'Marker expression for matching tests to run.',
defaultValue: 'new_ui_critical',
defaultValue: 'smoke',
)
*/
}
options {
+27 -51
View File
@@ -1,67 +1,43 @@
## Getting Started
[Starting Guide](starting-guide.md)
[IDE Setup](ide-setup.md)
## Getting Started
- [Starting Guide](starting-guide.md)
- [IDE Setup](ide-setup.md)
## Development Process
[Coding guidelines](new-guidelines.md)
[UI components coding guidelines](ui-guidelines.md)
[Release Checklist](release-checklist.md)
[Release Guide](release-guide.md)
[Merging PR process](merging-pr-process.md)
[PR Review Policy](pr-review-policy.md)
[Working on PR together with QA team](pipeline_process.md)
[Debugging](debugging.md)
[Patching](patching.md)
[Creating a pixel perfect UI](pixel-perfection.md)
[Contributing to status-go](status-go-changes.md)
[Malli schemas (recorded demo)](https://www.youtube.com/watch?v=SlRio70aYVI) ([slides](files/forging-code-with-schemas-sep-2023-slides.pdf))
- [Coding guidelines](new-guidelines.md)
- [UI components coding guidelines](ui-guidelines.md)
- [Release Checklist](release-checklist.md)
- [Release Guide](release-guide.md)
- [Merging PR process](merging-pr-process.md)
- [PR Review Policy](pr-review-policy.md)
- [Working on PR together with QA team](pipeline_process.md)
- [Debugging](debugging.md)
- [Patching](patching.md)
- [Creating a pixel perfect UI](pixel-perfection.md)
- [Contributing to status-go](status-go-changes.md)
- [Malli schemas (recorded demo)](https://www.youtube.com/watch?v=SlRio70aYVI) ([slides](files/forging-code-with-schemas-sep-2023-slides.pdf))
## Testing
[How to run local tests](testing.md)
[End-to-end tests (e2e) overview](how-to-launch-e2e.md)
[Component tests (jest) overview](component-tests-overview.md)
- [Overview](tests/tests-overview.md)
- [How to run local tests](tests/how-to-run-local-tests.md)
- [End-to-end tests (e2e) overview](tests/how-to-launch-e2e.md)
- [Component tests (jest) overview](tests/component-tests-overview.md)
## Project details
[status-go introduction (recorded meeting)](https://drive.google.com/file/d/1B7TljmTZ8fHkqJH8ChU1Cp4FGDFM03gq/view)
[re-frame usage (recorded meeting)](https://drive.google.com/file/d/1qv_E0CEGzQpu_zGXD0gCTU5EvhC2k8Jy/view)
[status app functionality demo](https://drive.google.com/file/u/0/d/1PmwsMLTLDNNIdv5_6wvMOwoj2PfT50c6/view)
- [status-go introduction (recorded meeting)](https://drive.google.com/file/d/1B7TljmTZ8fHkqJH8ChU1Cp4FGDFM03gq/view)
- [re-frame usage (recorded meeting)](https://drive.google.com/file/d/1qv_E0CEGzQpu_zGXD0gCTU5EvhC2k8Jy/view)
- [status app functionality demo](https://drive.google.com/file/u/0/d/1PmwsMLTLDNNIdv5_6wvMOwoj2PfT50c6/view)
## Misc
[Importing icons from Figma into project](export-icons.md)
[Updating Status APK builds for the F-Droid Android application catalogue](fdroid.md)
[Troubleshooting for known errors](troubleshooting.md)
- [Importing icons from Figma into project](export-icons.md)
- [Updating Status APK builds for the F-Droid Android application catalogue](fdroid.md)
- [Troubleshooting for known errors](troubleshooting.md)
## Outdated:
[Old guidelines](codebase-structure-and-guidelines.md)
[Post mortem analysis](post-mortem.md)
- [Old guidelines](codebase-structure-and-guidelines.md)
- [Post mortem analysis](post-mortem.md)
+9 -9
View File
@@ -32,9 +32,9 @@ Ready for testing, a PR should meet the following criteria:
### E2E tests and analyzing the results
The PR **MUST** be moved to the E2E column when it is ready for testing (**mandatory for all PRs**).
That will also trigger e2e tests run. QAs are monitoring PRs from E2E column and take it into test.
That will also trigger e2e tests run. QAs are monitoring PRs from E2E column and take it into test.
This step cannot be skipped. So, at least one comment from the `status-im-auto` bot with results is a prerequisite for moving forward.
Information on how to analyze tests can be found [here](https://github.com/status-im/status-mobile/blob/develop/doc/how-to-launch-e2e.md).
Information on how to analyze tests can be found [here](https://github.com/status-im/status-mobile/blob/develop/doc/tests/how-to-launch-e2e.md).
Tests might be flaky, as they depend on infrastructure - SauceLabs and Waku.
If there are `Failed tests` and you are not sure about the reason, you can always ping the mobile QAs for help (preferably in PRs by `@status-im/mobile-qa`).
@@ -46,14 +46,14 @@ Please, respect this rule.**
### Adding `skip-manual-qa`
**Do not hesitate to use a `skip-manual-qa`** if you're sure that it is a simple flow and you checked it.
**Do not hesitate to use a `skip-manual-qa`** if you're sure that it is a simple flow and you checked it.
- Please ask another team member before adding the `skip-manual-qa` label (PR/Status community/DMs) so that there's a second opinion.
- The PR MUST have a proper reasoning why manual QA is skipped.
- The PR MUST include the steps of testing that has been done by the developer prior to moving it forward.
**NOTE:** Make sure that QAs are OK with that;
Before merging PRs, please make sure that information is added about how you tested the PRs, that e2s have been passed and their results have been reviewed.
Before merging PRs, please make sure that information is added about how you tested the PRs, that e2s have been passed and their results have been reviewed.
The QA team appreciates your help!
@@ -72,7 +72,7 @@ The QA team appreciates your help!
- QA engineer picks up one of PRs with the ```request-manual-qa``` label, drags the item to the ```IN TESTING``` column and assigns it to themselves.
- During testing, QA will add comments describing the issues found, and also review automation tests results.
Usually found issues are numbered as "Issue 1, Issue 2", etc.
When the first round of testing is completed and all issues for this stage are found, the QA can add the ```Tested - Issues``` label and drag the card to the ```CONTRIBUTOR``` column. These two actions are optional.
When the first round of testing is completed and all issues for this stage are found, the QA can add the ```Tested - Issues``` label and drag the card to the ```CONTRIBUTOR``` column. These two actions are optional.
**IMPORTANT NOTE:** when the issues are fixed, developer **MUST** notify the QA that it is ready to be re-tested again by mention them in the PR.
@@ -86,7 +86,7 @@ After that the developer merges PR into develop.
_**How do I know if a design review is needed?**_
There are three cases here depending on the changes in the PR:
1. **Functional PRs with UI changes:** after the ```Tested - OK``` label is added, the QA moves the PR to the ```Design review``` column + mentions ```@Francesca-G``` in comments.
1. **Functional PRs with UI changes:** after the ```Tested - OK``` label is added, the QA moves the PR to the ```Design review``` column + mentions ```@Francesca-G``` in comments.
2. **Component PRs:** once the PR has received a review from developers and e2e tests results, it can be moved directly to the ```Design review``` column by the developer (manual testing step can be skipped) + the developer mentions ```@Francesca-G``` in comments.
3. **Functional PRs changes in which are not related to UI (e.g. a crash fix):** skip the ```Design review``` step (the PR should only be manually tested by QA).
@@ -99,7 +99,7 @@ There are three possible scenarios when the design review is completed:
**Notes:**
- If your PR has a long story and started from `develop` branch several days ago, please rebase it to current develop before adding label
- if PR can be tested by developer (in case of small changes) and/or developer is sure that the changes made cannot introduce a regression, then PR can be merged without manual testing. Also, currently, PRs are not manually tested if the changes relate only the design (creation of components, etc.) and do not affect the functionality (see `skip-manual-qa` label)
---
---
#### Why my PR is in `Contributor` column?
PR can be moved to this column by the ```status-github-bot``` or by QA engineer with label `Tested-issues` or if one of the requirements for manual QA was not met.
@@ -120,6 +120,6 @@ In the second case - after fixing of all found issues, the developer should ping
6. In case of manual testing - the label ```Tested - OK``` from QA
7. In case of design review - the approval from the designer
You can merge your PR into develop - some useful clues you can find [here](https://notes.status.im/setup-e2e#3-Merging-PR)
You can merge your PR into develop - some useful clues you can find [here](https://notes.status.im/setup-e2e#3-Merging-PR)
HAPPY DEVELOPMENT! :tada:
HAPPY DEVELOPMENT! :tada:
@@ -8,7 +8,7 @@ As a part of CI for Status mobile app and in order to ensure there are no regres
- Automated tests written on Python 3.9 and pytest.
- Appium (server) and Selenium WebDriver (protocol) are the base of test automation framework.
TestRail is a test case management system tool where we have test cases.
TestRail is a test case management system tool where we have test cases.
Each of the test case gets a priority (Critical/High/Medium)
@@ -20,21 +20,21 @@ For now we support e2e for Android only.
Whenever we need to push set of test scripts we create 16 parallel sessions (max, but depending on amount of cases that are included in job) and each thread: 1) uploads Android .apk file to SauceLabs -> 2) runs through the test steps -> 3) receives results whether test failed on particular step or succeeded with no errors -> 3) Parse test results and push them as a Github comment (if the suite ran against respective PR) and into TestRail.
We push **whole automation test suite (currently 155, amount is changing)** against each nightly build (if the nightly builds job succeeded). Results of the test run are saved in TestRail.
And also we push set of autotests whenever PR with successful builds got moved in to `E2E Tests` column from [Pipeline for QA dashboard ](https://github.com/status-im/status-react/projects/7).
And also we push set of autotests whenever PR with successful builds got moved in to `E2E Tests` column from [Pipeline for QA dashboard ](https://github.com/status-im/status-react/projects/7).
In that case we save results in TestRail as well and push a comment with test results in a respective PR.
For example: https://github.com/status-im/status-react/pull/9147#issuecomment-540008770
![](images/how-to-launch-e2e/how-to-launch-e2e-1.png)
![](../images/how-to-launch-e2e/how-to-launch-e2e-1.png)
The test_send_stt_from_wallet opens link in TestRail https://ethstatus.testrail.net/index.php?/tests/view/890885 where performed steps could be found
List of all runs performed by test jobs could be found here https://ethstatus.testrail.net/index.php?/runs/overview/14
List of all runs performed by test jobs could be found here https://ethstatus.testrail.net/index.php?/runs/overview/14
**For credentials for TestRail to see results ping Chu in DM**:
Opening any test run navigates you to list of test cases with results:
![](images/how-to-launch-e2e/how-to-launch-e2e-2.png)
![](../images/how-to-launch-e2e/how-to-launch-e2e-2.png)
## What about launching e2e manually
@@ -53,12 +53,12 @@ Params to specify:
- test_marks: tests by priorities (by default: `critical or high or medium`, which corresponds the whole suite; to launch the same suite as in PRs, use `critical or high`)
- testrail_case_id: here is the list of test cases which you may find in test rail (4-digit value)
For easier access you can hit `Rerun tests` in GH comment and testrail_case_id/ apk_name/ pr_id will be filled automatically. For making sure that tests are being rerun on most recent e2e build it is recommended to paste link to the last e2e build in apk_name field. The list of PR builds can be found in Jenkins Builds block on PR page.
![](images/how-to-launch-e2e/how-to-launch-e2e-3.png)
For easier access you can hit `Rerun tests` in GH comment and testrail_case_id/ apk_name/ pr_id will be filled automatically. For making sure that tests are being rerun on most recent e2e build it is recommended to paste link to the last e2e build in apk_name field. The list of PR builds can be found in Jenkins Builds block on PR page.
![](../images/how-to-launch-e2e/how-to-launch-e2e-3.png)
And then hit Build.
Once the job starts it picks up specified tests, runs them against provided apk and sends results to pull request.
Even we have 16 parallel sessions for testing its a time consuming operation (whole test suite we have automated at the moment takes ~140 minutes to finish).
Even we have 16 parallel sessions for testing its a time consuming operation (whole test suite we have automated at the moment takes ~140 minutes to finish).
So for PRs we pick only set of `critical or high` (you can also use this in TEST_MARKS param for job)
tests (otherwise some PRs could wait their turn of the scheduled Jenkins job till the next day).
@@ -78,9 +78,9 @@ Several examples of when test fails to succeed:
- **Valid issue in the automated test scripts** - that's what we're looking for
Example: here is the test results https://github.com/status-im/status-react/pull/13015#issuecomment-1016495043 where one test failed.
Example: here is the test results https://github.com/status-im/status-react/pull/13015#issuecomment-1016495043 where one test failed.
1. Open the test in TestRail and open session recorded for this test in SauceLabs
![](images/how-to-launch-e2e/how-to-launch-e2e-4.png)
![](../images/how-to-launch-e2e/how-to-launch-e2e-4.png)
In TestRail you may find all the steps performed by the test.
@@ -98,6 +98,6 @@ Not all features of the app could be covered by e2e at the moment:
## Brief flow for test to be automated
Whenever there is a need to have a new test:
1) Create a test scenario in TestRail.
1) Create a test scenario in TestRail.
2) If certain item could be checked in scope of existing test case we update existing one (otherwise we may have thousands of test cases which is overkill to manage in TestRail as well as in automated test scripts). And also complex autotests increase probability to not catch regressions by stopping test execution (due to valid bug or changed feature) keeping the rest test steps uncovered. So here we need to balance when it makes sense to update existing test case with more checks.
3) Then we create test script based on the test case, ensure test passes for the build and pushing the changes to repo.
+167
View File
@@ -0,0 +1,167 @@
# Tests
## Introduction
This document provides a general overview of the types of tests we use and when
to use them. It is not meant to be a tutorial or a detailed documentation about
testing in software development.
## Types of tests
Tests in `status-mobile` are comprised of:
- Unit tests
- Subscription tests
- Event tests
- Tests for various utilities
- [Component tests](./component-tests-overview.md)
- Integration/contract tests
- [End-to-end tests](./how-to-launch-e2e.md)
We apply the [test
pyramid](https://en.wikipedia.org/wiki/Test_automation#Testing_at_different_levels)
strategy, which means we want the majority of tests at the bottom of the
pyramid. Those should be fast and deterministic and support REPL-Driven
development (RDD). Slightly above them, we have component tests, then
integration/contract tests and finally end-to-end tests. The closer to the top
of the pyramid, the more valuable a test can be, but also more difficult to
pinpoint why it failed and harder to make it dependable.
*Note*: there are literally dozens of [types of
tests](https://en.wikipedia.org/wiki/Software_testing), each with its strengths
and weaknesses.
We tend not to stub or mock implementations in our tests, which means our tests
are [sociable](https://martinfowler.com/bliki/UnitTest.html).
## What to test?
The UI is driven by global & local state changes caused by events. Global state
is managed by re-frame and local state by Reagent atoms or React hooks. Except
for component and end-to-end tests, we test only non-UI code in `status-mobile`.
Given that the UI is greatly derived from global state, by guaranteeing the
state is correct we can prevent bugs and, more importantly, reduce the [cost of
change](https://www.pmi.org/disciplined-agile/agile/costofchange).
We strive to minimize the amount of _business logic_ in views (UI code). We
achieve this by moving capabilities to status-go and also by adhering to
re-frame's architecture.
Whenever appropriate (see section `When to test?`), we _may_ test:
- Re-frame events.
- Re-frame subscriptions.
- Utility functions.
- User journeys through integration/contract tests.
Interestingly, we don't test re-frame _effects_ in isolation.
### What are status-mobile integration and contract tests?
The mobile _integration tests_ can be used to "simulate" user interactions and
make actual calls to status-go via the RPC layer and actually receive signals.
We can also use these tests to verify the app-db and multiple subscriptions are
correct. We use the word _simulate_ because there is no UI. Basically, any flow
that can be driven by re-frame events is possible to automatically test. There
is no way to change or inspect local state managed by React.
A _contract test_ has the same capabilities as an integration test, but we want
to draw the line that they should focus more on a particupar RPC endpoint or
signal, and not on a user journey (e.g. create a wallet account). In the future,
we may consider running them automatically in status-go.
**Note:** integration tests and contract tests are currently overlapping in
their responsibilities and still require a clearer distinction.
## When to test?
(Automated) tests basically exist to support rapid software changes, but not
every piece of code should be tested. The following are general recommendations,
not rules.
- What would be the consequences to the user of a bug in the implementation you
are working on?
- Can a QA exercise all the branches in the code you changed? Not surprisingly,
usually QAs can't test many code paths (it may be nearly impossible), and
because PRs are not often tested by reviewers, many PRs can get into `develop`
without the necessary quality assurance.
- How costly was it for you to verify a function/event/etc was correct? Now
consider that this cost will be dispersed to every developer who needs to
change the implementation if there are no tests.
- Check the number of conditionals, and if they nest as well. Every conditional
may require two different assertions, and the number of assertions can grow
exponentially.
- How complicated are the arguments to the function? If they contain nested maps
or data that went through a few transformations, it may be tricky to decipher
what they are, unless you are familiar with the code. A test would be able to
capture the data, however complex they are.
### When to unit-test subscriptions?
Only test [layer-3
subscriptions](https://day8.github.io/re-frame/subscriptions/#the-four-layers),
i.e. don't bother testing extractor subscriptions (check the related
[guideline](https://github.com/status-im/status-mobile/blob/7774c4eac16fdee950a17bf5d07630c45a980f41/doc/new-guidelines.md#subscription-tests)).
Some layer-3 subscriptions can still be straightforward and may not be worth
testing.
- Check the number of _inputs_ to the sub (from the graph). The higher this
number, the greater the chance the subscription can break if any of the
input's implementation changes.
**Note**: if a tested subscription changes inadvertently, even if its own tests
still pass, other subscriptions that depend on it and have tests may still fail.
This is why we don't directly test the subscription handler, but instead, use
the macro `test-helpers.unit/deftest-sub`.
### When to unit-test events?
A good hint is to ask if you and other CCs need to rely on re-frisk, UI, REPL,
or FlowStorm to understand the event. If the answer is yes or probably, then a
test would be prudent.
- Many events only receive arguments and pass them along without much or any
transformation to an RPC call. These are straightforward and usually don't
need tests ([example](https://github.com/status-im/status-mobile/blob/7774c4eac16fdee950a17bf5d07630c45a980f41/src/status_im/contexts/contact/blocking/events.cljs#L79-L85)).
- Overall, every event basically returns two effects at most, `:fx` and/or
`:db`. Usually, the complicated part lies in the computation to return the new
app-db. If the event doesn't perform transformations in the app-db or just
does a trivial `assoc`, for example, it may not be worth testing.
For reference, the re-frame author particularly [suggests testing events and
subscriptions](https://github.com/day8/re-frame/blob/09e2d7132c479aa43f2a64164e54e42bf8511902/docs/Testing.md#what-to-test).
### When to unit-test utility functions?
Most utility functions in `status-mobile` are pure and can be readily and
cheaply tested.
- If the utility is used in an event/subscription and if the event/subscription
has tests, you may prefer to test the event/subscription and not the utility,
or the other way around sometimes.
- If the utility is tricky to verify, such as functions manipulating time, write
tests ([example](https://github.com/status-im/status-mobile/blob/7774c4eac16fdee950a17bf5d07630c45a980f41/src/utils/datetime.cljs#L1)).
- Utilities can be particularly hard to verify by QAs because they can be lower
level and require very particular inputs. In such cases, consider writing
tests.
### When to write integration/contract tests?
- You want to make real calls to status-go because you think the unit tests are
not enough (test pyramid strategy).
- You constantly need to retest the same things on the UI, sometimes over
multiple screens.
- The flow is too important to rely only on manual QA, which can't always be
done due to resource limits, so an integration/contract test fills this gap.
- You want to rely less on end-to-end tests, which can be more unreliable and
slower to change.
- You want automatic verifications for some area of the mobile app whenever
status-go is upgraded.
**Note**: the feedback cycle to write integration tests is longer than unit
tests because they are slower and harder to debug. Using the REPL with them is
difficult due to their stateful nature.
### When to test Quo components?
This is covered in [quo/README.md#component-tests](https://github.com/status-im/status-mobile/blob/7774c4eac16fdee950a17bf5d07630c45a980f41/src/quo/README.md#component-tests).
+2 -2
View File
@@ -915,7 +915,7 @@ PODS:
- glog
- RCT-Folly (= 2022.05.16.00)
- React-Core
- react-native-compat (2.12.2):
- react-native-compat (2.11.2):
- glog
- RCT-Folly (= 2022.05.16.00)
- React-Core
@@ -1517,7 +1517,7 @@ SPEC CHECKSUMS:
react-native-blob-util: 600972b1782380a5a7d5db61a3817ea32349dae9
react-native-blur: 799045500f56146afc46245148080e7b7623cb75
react-native-cameraroll: af8eec1e585d053ff485d98ec837f9a8a11b5745
react-native-compat: 84e00e8dcff9251278c0d48f2bce81f4502e3925
react-native-compat: 3af9add14d349701306d3d052638435f6795ac2c
react-native-config: 5330c8258265c1e5fdb8c009d2cabd6badd96727
react-native-get-random-values: 21325b2244dfa6b58878f51f9aa42821e7ba3d06
react-native-hole-view: 6935448993bac79f2b5a4ad7e9741094cf810679
@@ -92,6 +92,11 @@ public class EncryptionUtils extends ReactContextBaseJavaModule {
return Statusgo.hexToUtf8(str);
}
@ReactMethod(isBlockingSynchronousMethod = true)
public String serializeLegacyKey(final String publicKey) {
return Statusgo.serializeLegacyKey(publicKey);
}
@ReactMethod
public void setBlankPreviewFlag(final Boolean blankPreview) {
final SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this.reactContext);
@@ -94,6 +94,10 @@ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(hexToUtf8:(NSString *)str) {
return StatusgoHexToUtf8(str);
}
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(serializeLegacyKey:(NSString *)str) {
return StatusgoSerializeLegacyKey(str);
}
RCT_EXPORT_METHOD(setBlankPreviewFlag:(BOOL *)newValue)
{
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
@@ -672,6 +672,38 @@ void _Sha3(const FunctionCallbackInfo<Value>& args) {
}
void _SerializeLegacyKey(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
if (args.Length() != 1) {
// Throw an Error that is passed back to JavaScript
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8Literal(isolate, "Wrong number of arguments for SerializeLegacyKey")));
return;
}
// Check the argument types
if (!args[0]->IsString()) {
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8Literal(isolate, "Wrong argument type for 'str'")));
return;
}
String::Utf8Value arg0Obj(isolate, args[0]->ToString(context).ToLocalChecked());
char *arg0 = *arg0Obj;
// Call exported Go function, which returns a C string
char *c = SerializeLegacyKey(arg0);
Local<String> ret = String::NewFromUtf8(isolate, c).ToLocalChecked();
args.GetReturnValue().Set(ret);
delete c;
}
void _ToChecksumAddress(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
@@ -1976,6 +2008,7 @@ void init(Local<Object> exports) {
NODE_SET_METHOD(exports, "checkAddressChecksum", _CheckAddressChecksum);
NODE_SET_METHOD(exports, "isAddress", _IsAddress);
NODE_SET_METHOD(exports, "sha3", _Sha3);
NODE_SET_METHOD(exports, "serializeLegacyKey", _SerializeLegacyKey);
NODE_SET_METHOD(exports, "toChecksumAddress", _ToChecksumAddress);
NODE_SET_METHOD(exports, "logout", _Logout);
NODE_SET_METHOD(exports, "hashMessage", _HashMessage);
+2 -1
View File
@@ -97,7 +97,8 @@
"jest-silent-reporter": "^0.5.0",
"nodemon": "^2.0.16",
"nyc": "^14.1.1",
"prettier": "^2.8.8",
"prettier": "^3.3.3",
"prettier-plugin-sort-json": "^4.0.0",
"process": "0.11.10",
"react-test-renderer": "18.1.0",
"shadow-cljs": "2.26.2",
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

+276 -37
View File
@@ -1,38 +1,277 @@
(ns keycard.keycard)
(ns keycard.keycard
(:require
["react-native" :as rn]
["react-native-status-keycard" :default status-keycard]
[react-native.platform :as platform]
[taoensso.timbre :as log]
[utils.address :as address]))
(defprotocol Keycard
(start-nfc [this args])
(stop-nfc [this args])
(set-nfc-message [this args])
(check-nfc-support [this args])
(check-nfc-enabled [this args])
(open-nfc-settings [this])
(register-card-events [this args])
(set-pairings [this args])
(on-card-disconnected [this callback])
(on-card-connected [this callback])
(remove-event-listener [this event])
(remove-event-listeners [this])
(get-application-info [this args])
(factory-reset [this args])
(install-applet [this args])
(install-cash-applet [this args])
(init-card [this args])
(install-applet-and-init-card [this args])
(pair [this args])
(generate-and-load-key [this args])
(unblock-pin [this args])
(verify-pin [this args])
(change-pin [this args])
(change-puk [this args])
(change-pairing [this args])
(unpair [this args])
(delete [this args])
(remove-key [this args])
(remove-key-with-unpair [this args])
(export-key [this args])
(unpair-and-delete [this args])
(import-keys [this args])
(get-keys [this args])
(sign [this args])
(sign-typed-data [this args]))
(defonce event-emitter
(if platform/ios?
(new (.-NativeEventEmitter rn) status-keycard)
(.-DeviceEventEmitter rn)))
(defn start-nfc
[{:keys [on-success on-failure prompt-message]}]
(log/debug "start-nfc")
(.. status-keycard
(startNFC (str prompt-message))
(then on-success)
(catch on-failure)))
(defn stop-nfc
[{:keys [on-success on-failure error-message]}]
(log/debug "stop-nfc")
(.. status-keycard
(stopNFC (str error-message))
(then on-success)
(catch on-failure)))
(defn set-nfc-message
[{:keys [on-success on-failure status-message]}]
(log/debug "set-nfc-message")
(.. status-keycard
(setNFCMessage (str status-message))
(then on-success)
(catch on-failure)))
(defn check-nfc-support
[{:keys [on-success]}]
(.. status-keycard
nfcIsSupported
(then on-success)))
(defn check-nfc-enabled
[{:keys [on-success]}]
(.. status-keycard
nfcIsEnabled
(then on-success)))
(defn open-nfc-settings
[]
(.openNfcSettings status-keycard))
(defn remove-event-listeners
[]
(doseq [event ["keyCardOnConnected" "keyCardOnDisconnected" "keyCardOnNFCUserCancelled"
"keyCardOnNFCTimeout"]]
(.removeAllListeners ^js event-emitter event)))
(defn remove-event-listener
[^js event]
(when event
(.remove event)))
(defn on-card-connected
[callback]
(.addListener ^js event-emitter "keyCardOnConnected" callback))
(defn on-card-disconnected
[callback]
(.addListener ^js event-emitter "keyCardOnDisconnected" callback))
(defn on-nfc-user-cancelled
[callback]
(.addListener ^js event-emitter "keyCardOnNFCUserCancelled" callback))
(defn on-nfc-timeout
[callback]
(.addListener ^js event-emitter "keyCardOnNFCTimeout" callback))
(defn on-nfc-enabled
[callback]
(.addListener ^js event-emitter "keyCardOnNFCEnabled" callback))
(defn on-nfc-disabled
[callback]
(.addListener ^js event-emitter "keyCardOnNFCDisabled" callback))
(defn set-pairings
[pairings]
(.. status-keycard (setPairings (clj->js (or pairings {})))))
(defn get-application-info
[{:keys [on-success on-failure]}]
(.. status-keycard
(getApplicationInfo)
(then (fn [response]
(let [info (-> response
(js->clj :keywordize-keys true)
(update :key-uid address/normalized-hex))]
(on-success info))))
(catch on-failure)))
(defn factory-reset
[{:keys [on-success on-failure]}]
(.. status-keycard
(factoryReset)
(then (fn [response]
(let [info (-> response
(js->clj :keywordize-keys true)
(update :key-uid address/normalized-hex))]
(on-success info))))
(catch on-failure)))
(defn install-applet
[{:keys [on-success on-failure]}]
(.. status-keycard
installApplet
(then on-success)
(catch on-failure)))
(defn install-cash-applet
[{:keys [on-success on-failure]}]
(.. status-keycard
installCashApplet
(then on-success)
(catch on-failure)))
(defn init-card
[{:keys [pin on-success on-failure]}]
(.. status-keycard
(init pin)
(then on-success)
(catch on-failure)))
(defn install-applet-and-init-card
[{:keys [pin on-success on-failure]}]
(.. status-keycard
(installAppletAndInitCard pin)
(then on-success)
(catch on-failure)))
(defn pair
[{:keys [password on-success on-failure]}]
(when password
(.. status-keycard
(pair password)
(then on-success)
(catch on-failure))))
(defn generate-and-load-key
[{:keys [mnemonic pin on-success on-failure]}]
(.. status-keycard
(generateAndLoadKey mnemonic pin)
(then on-success)
(catch on-failure)))
(defn unblock-pin
[{:keys [puk new-pin on-success on-failure]}]
(when (and new-pin puk)
(.. status-keycard
(unblockPin puk new-pin)
(then on-success)
(catch on-failure))))
(defn verify-pin
[{:keys [pin on-success on-failure]}]
(when (not-empty pin)
(.. status-keycard
(verifyPin pin)
(then on-success)
(catch on-failure))))
(defn change-pin
[{:keys [current-pin new-pin on-success on-failure]}]
(when (and current-pin new-pin)
(.. status-keycard
(changePin current-pin new-pin)
(then on-success)
(catch on-failure))))
(defn change-puk
[{:keys [pin puk on-success on-failure]}]
(when (and pin puk)
(.. status-keycard
(changePUK pin puk)
(then on-success)
(catch on-failure))))
(defn change-pairing
[{:keys [pin pairing on-success on-failure]}]
(when (and pin pairing)
(.. status-keycard
(changePairingPassword pin pairing)
(then on-success)
(catch on-failure))))
(defn unpair
[{:keys [pin on-success on-failure]}]
(when pin
(.. status-keycard
(unpair pin)
(then on-success)
(catch on-failure))))
(defn delete
[{:keys [on-success on-failure]}]
(.. status-keycard
(delete)
(then on-success)
(catch on-failure)))
(defn remove-key
[{:keys [pin on-success on-failure]}]
(.. status-keycard
(removeKey pin)
(then on-success)
(catch on-failure)))
(defn remove-key-with-unpair
[{:keys [pin on-success on-failure]}]
(.. status-keycard
(removeKeyWithUnpair pin)
(then on-success)
(catch on-failure)))
(defn export-key
[{:keys [pin path on-success on-failure]}]
(.. status-keycard
(exportKeyWithPath pin path)
(then on-success)
(catch on-failure)))
(defn unpair-and-delete
[{:keys [pin on-success on-failure]}]
(when (not-empty pin)
(.. status-keycard
(unpairAndDelete pin)
(then on-success)
(catch on-failure))))
(defn import-keys
[{:keys [pin on-success on-failure]}]
(when (not-empty pin)
(.. status-keycard
(importKeys pin)
(then on-success)
(catch on-failure))))
(defn get-keys
[{:keys [pin on-success on-failure]}]
(when (not-empty pin)
(.. status-keycard
(getKeys pin)
(then on-success)
(catch on-failure))))
(defn sign
[{pin :pin path :path card-hash :hash on-success :on-success on-failure :on-failure}]
(when (and pin card-hash)
(if path
(.. status-keycard
(signWithPath pin path card-hash)
(then on-success)
(catch on-failure))
(.. status-keycard
(sign pin card-hash)
(then on-success)
(catch on-failure)))))
(defn sign-typed-data
[{card-hash :hash on-success :on-success on-failure :on-failure}]
(when card-hash
(.. status-keycard
(signPinless card-hash)
(then on-success)
(catch on-failure))))
-365
View File
@@ -1,365 +0,0 @@
(ns keycard.real-keycard
(:require
["react-native" :as rn]
["react-native-status-keycard" :default status-keycard]
[keycard.keycard :as keycard]
[react-native.platform :as platform]
[taoensso.timbre :as log]
[utils.address :as address]))
(defonce event-emitter
(if platform/ios?
(new (.-NativeEventEmitter rn) status-keycard)
(.-DeviceEventEmitter rn)))
(defonce active-listeners (atom []))
(defn start-nfc
[{:keys [on-success on-failure prompt-message]}]
(log/debug "start-nfc")
(.. status-keycard
(startNFC (str prompt-message))
(then on-success)
(catch on-failure)))
(defn stop-nfc
[{:keys [on-success on-failure error-message]}]
(log/debug "stop-nfc")
(.. status-keycard
(stopNFC (str error-message))
(then on-success)
(catch on-failure)))
(defn set-nfc-message
[{:keys [on-success on-failure status-message]}]
(log/debug "set-nfc-message")
(.. status-keycard
(setNFCMessage (str status-message))
(then on-success)
(catch on-failure)))
(defn check-nfc-support
[{:keys [on-success]}]
(.. status-keycard
nfcIsSupported
(then on-success)))
(defn check-nfc-enabled
[{:keys [on-success]}]
(.. status-keycard
nfcIsEnabled
(then on-success)))
(defn open-nfc-settings
[]
(.openNfcSettings status-keycard))
(defn remove-event-listeners
[]
(doseq [event ["keyCardOnConnected" "keyCardOnDisconnected" "keyCardOnNFCUserCancelled"
"keyCardOnNFCTimeout"]]
(.removeAllListeners ^js event-emitter event)))
(defn remove-event-listener
[^js event]
(.remove event))
(defn on-card-connected
[callback]
(.addListener ^js event-emitter "keyCardOnConnected" callback))
(defn on-card-disconnected
[callback]
(.addListener ^js event-emitter "keyCardOnDisconnected" callback))
(defn on-nfc-user-cancelled
[callback]
(.addListener ^js event-emitter "keyCardOnNFCUserCancelled" callback))
(defn on-nfc-timeout
[callback]
(.addListener ^js event-emitter "keyCardOnNFCTimeout" callback))
(defn on-nfc-enabled
[callback]
(.addListener ^js event-emitter "keyCardOnNFCEnabled" callback))
(defn on-nfc-disabled
[callback]
(.addListener ^js event-emitter "keyCardOnNFCDisabled" callback))
(defn set-pairings
[{:keys [pairings]}]
(.. status-keycard (setPairings (clj->js (or pairings {})))))
(defn register-card-events
[args]
(doseq [listener @active-listeners]
(remove-event-listener listener))
(reset! active-listeners
[(on-card-connected (:on-card-connected args))
(on-card-disconnected (:on-card-disconnected args))
(on-nfc-user-cancelled (:on-nfc-user-cancelled args))
(on-nfc-timeout (:on-nfc-timeout args))
(on-nfc-enabled (:on-nfc-enabled args))
(on-nfc-disabled (:on-nfc-disabled args))]))
(defn get-application-info
[{:keys [on-success on-failure]}]
(.. status-keycard
(getApplicationInfo)
(then (fn [response]
(let [info (-> response
(js->clj :keywordize-keys true)
(update :key-uid address/normalized-hex))]
(on-success info))))
(catch on-failure)))
(defn factory-reset
[{:keys [on-success on-failure]}]
(.. status-keycard
(factoryReset)
(then (fn [response]
(let [info (-> response
(js->clj :keywordize-keys true)
(update :key-uid address/normalized-hex))]
(on-success info))))
(catch on-failure)))
(defn install-applet
[{:keys [on-success on-failure]}]
(.. status-keycard
installApplet
(then on-success)
(catch on-failure)))
(defn install-cash-applet
[{:keys [on-success on-failure]}]
(.. status-keycard
installCashApplet
(then on-success)
(catch on-failure)))
(defn init-card
[{:keys [pin on-success on-failure]}]
(.. status-keycard
(init pin)
(then on-success)
(catch on-failure)))
(defn install-applet-and-init-card
[{:keys [pin on-success on-failure]}]
(.. status-keycard
(installAppletAndInitCard pin)
(then on-success)
(catch on-failure)))
(defn pair
[{:keys [password on-success on-failure]}]
(when password
(.. status-keycard
(pair password)
(then on-success)
(catch on-failure))))
(defn generate-and-load-key
[{:keys [mnemonic pin on-success on-failure]}]
(.. status-keycard
(generateAndLoadKey mnemonic pin)
(then on-success)
(catch on-failure)))
(defn unblock-pin
[{:keys [puk new-pin on-success on-failure]}]
(when (and new-pin puk)
(.. status-keycard
(unblockPin puk new-pin)
(then on-success)
(catch on-failure))))
(defn verify-pin
[{:keys [pin on-success on-failure]}]
(when (not-empty pin)
(.. status-keycard
(verifyPin pin)
(then on-success)
(catch on-failure))))
(defn change-pin
[{:keys [current-pin new-pin on-success on-failure]}]
(when (and current-pin new-pin)
(.. status-keycard
(changePin current-pin new-pin)
(then on-success)
(catch on-failure))))
(defn change-puk
[{:keys [pin puk on-success on-failure]}]
(when (and pin puk)
(.. status-keycard
(changePUK pin puk)
(then on-success)
(catch on-failure))))
(defn change-pairing
[{:keys [pin pairing on-success on-failure]}]
(when (and pin pairing)
(.. status-keycard
(changePairingPassword pin pairing)
(then on-success)
(catch on-failure))))
(defn unpair
[{:keys [pin on-success on-failure]}]
(when pin
(.. status-keycard
(unpair pin)
(then on-success)
(catch on-failure))))
(defn delete
[{:keys [on-success on-failure]}]
(.. status-keycard
(delete)
(then on-success)
(catch on-failure)))
(defn remove-key
[{:keys [pin on-success on-failure]}]
(.. status-keycard
(removeKey pin)
(then on-success)
(catch on-failure)))
(defn remove-key-with-unpair
[{:keys [pin on-success on-failure]}]
(.. status-keycard
(removeKeyWithUnpair pin)
(then on-success)
(catch on-failure)))
(defn export-key
[{:keys [pin path on-success on-failure]}]
(.. status-keycard
(exportKeyWithPath pin path)
(then on-success)
(catch on-failure)))
(defn unpair-and-delete
[{:keys [pin on-success on-failure]}]
(when (not-empty pin)
(.. status-keycard
(unpairAndDelete pin)
(then on-success)
(catch on-failure))))
(defn import-keys
[{:keys [pin on-success on-failure]}]
(when (not-empty pin)
(.. status-keycard
(importKeys pin)
(then on-success)
(catch on-failure))))
(defn get-keys
[{:keys [pin on-success on-failure]}]
(when (not-empty pin)
(.. status-keycard
(getKeys pin)
(then on-success)
(catch on-failure))))
(defn sign
[{pin :pin path :path card-hash :hash on-success :on-success on-failure :on-failure}]
(when (and pin card-hash)
(if path
(.. status-keycard
(signWithPath pin path card-hash)
(then on-success)
(catch on-failure))
(.. status-keycard
(sign pin card-hash)
(then on-success)
(catch on-failure)))))
(defn sign-typed-data
[{card-hash :hash on-success :on-success on-failure :on-failure}]
(when card-hash
(.. status-keycard
(signPinless card-hash)
(then on-success)
(catch on-failure))))
(defrecord RealKeycard []
keycard/Keycard
(keycard/start-nfc [_this args]
(start-nfc args))
(keycard/stop-nfc [_this args]
(stop-nfc args))
(keycard/set-nfc-message [_this args]
(set-nfc-message args))
(keycard/check-nfc-support [_this args]
(check-nfc-support args))
(keycard/check-nfc-enabled [_this args]
(check-nfc-enabled args))
(keycard/open-nfc-settings [_this]
(open-nfc-settings))
(keycard/register-card-events [_this args]
(register-card-events args))
(keycard/on-card-connected [_this callback]
(on-card-connected callback))
(keycard/on-card-disconnected [_this callback]
(on-card-disconnected callback))
(keycard/remove-event-listener [_this event]
(remove-event-listener event))
(keycard/remove-event-listeners [_this]
(remove-event-listeners))
(keycard/set-pairings [_this args]
(set-pairings args))
(keycard/get-application-info [_this args]
(get-application-info args))
(keycard/factory-reset [_this args]
(factory-reset args))
(keycard/install-applet [_this args]
(install-applet args))
(keycard/install-cash-applet [_this args]
(install-cash-applet args))
(keycard/init-card [_this args]
(init-card args))
(keycard/install-applet-and-init-card [_this args]
(install-applet-and-init-card args))
(keycard/pair [_this args]
(pair args))
(keycard/generate-and-load-key [_this args]
(generate-and-load-key args))
(keycard/unblock-pin [_this args]
(unblock-pin args))
(keycard/verify-pin [_this args]
(verify-pin args))
(keycard/change-pin [_this args]
(change-pin args))
(keycard/change-puk [_this args]
(change-puk args))
(keycard/change-pairing [_this args]
(change-pairing args))
(keycard/unpair [_this args]
(unpair args))
(keycard/delete [_this args]
(delete args))
(keycard/remove-key [_this args]
(remove-key args))
(keycard/remove-key-with-unpair [_this args]
(remove-key-with-unpair args))
(keycard/export-key [_this args]
(export-key args))
(keycard/unpair-and-delete [_this args]
(unpair-and-delete args))
(keycard/import-keys [_this args]
(import-keys args))
(keycard/get-keys [_this args]
(get-keys args))
(keycard/sign [_this args]
(sign args))
(keycard/sign-typed-data [_this args]
(sign-typed-data args)))
@@ -18,6 +18,7 @@
[{:keys [current-log-level
telemetry-enabled?
light-client-enabled?
store-confirmations-enabled?
current-fleet
test-networks-enabled?
is-goerli-enabled?
@@ -77,6 +78,15 @@
[:wakuv2.ui/toggle-light-client (not light-client-enabled?)])
:accessory :switch
:active light-client-enabled?}
{:size :small
:title (i18n/label :t/store-confirmations)
:accessibility-label :store-confirmations
:container-margin-bottom 8
:on-press
#(re-frame/dispatch
[:wakuv2.ui/toggle-store-confirmations (not store-confirmations-enabled?)])
:accessory :switch
:active store-confirmations-enabled?}
{:size :small
:title "Testnet mode"
:accessibility-label :test-networks-enabled
@@ -114,13 +124,14 @@
(views/defview advanced-settings
[]
(views/letsubs [test-networks-enabled? [:profile/test-networks-enabled?]
is-goerli-enabled? [:profile/is-goerli-enabled?]
light-client-enabled? [:profile/light-client-enabled?]
telemetry-enabled? [:profile/telemetry-enabled?]
current-log-level [:log-level/current-log-level]
current-fleet [:fleets/current-fleet]
peer-syncing-enabled? [:profile/peer-syncing-enabled?]]
(views/letsubs [test-networks-enabled? [:profile/test-networks-enabled?]
is-goerli-enabled? [:profile/is-goerli-enabled?]
light-client-enabled? [:profile/light-client-enabled?]
store-confirmations-enabled? [:profile/store-confirmations-enabled?]
telemetry-enabled? [:profile/telemetry-enabled?]
current-log-level [:log-level/current-log-level]
current-fleet [:fleets/current-fleet]
peer-syncing-enabled? [:profile/peer-syncing-enabled?]]
[:<>
[quo/page-nav
{:type :title
@@ -130,13 +141,14 @@
:on-press #(rf/dispatch [:navigate-back])}]
[list/flat-list
{:data (flat-list-data
{:current-log-level current-log-level
:telemetry-enabled? telemetry-enabled?
:light-client-enabled? light-client-enabled?
:current-fleet current-fleet
:dev-mode? false
:test-networks-enabled? test-networks-enabled?
:is-goerli-enabled? is-goerli-enabled?
:peer-syncing-enabled? peer-syncing-enabled?})
{:current-log-level current-log-level
:telemetry-enabled? telemetry-enabled?
:light-client-enabled? light-client-enabled?
:store-confirmations-enabled? store-confirmations-enabled?
:current-fleet current-fleet
:dev-mode? false
:test-networks-enabled? test-networks-enabled?
:is-goerli-enabled? is-goerli-enabled?
:peer-syncing-enabled? peer-syncing-enabled?})
:key-fn (fn [_ i] (str i))
:render-fn render-item}]]))
@@ -1,12 +1,14 @@
(ns legacy.status-im.ui.screens.peers-stats
(:require
[quo.foundations.colors :as colors]
[react-native.core :as rn]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn peers-stats
[]
(let [peers-count (rf/sub [:peer-stats/count])]
(let [peers-count (rf/sub [:peer-stats/count])
theme (rf/sub [:theme])]
(rn/use-mount
(fn []
(rf/dispatch [:peer-stats/get-count])))
@@ -18,4 +20,5 @@
{:style {:flex-direction :row
:margin-vertical 8
:justify-content :space-between}}
[rn/text (str (i18n/label :t/peers-count) ": " peers-count)]]]))
[rn/text {:style {:color (colors/theme-colors colors/neutral-100 colors/white theme)}}
(str (i18n/label :t/peers-count) ": " peers-count)]]]))
-16
View File
@@ -1,16 +0,0 @@
(ns legacy.status-im.utils.hex
(:require
[clojure.string :as string]))
(defn normalize-hex
[hex]
(when hex
(string/lower-case (if (string/starts-with? hex "0x")
(subs hex 2)
hex))))
(defn valid-hex?
[hex]
(let [hex (normalize-hex hex)]
(and (re-matches #"^[0-9a-fA-F]+$" hex)
(not= (js/parseInt hex 16) 0))))
@@ -1,58 +0,0 @@
(ns legacy.status-im.utils.keychain.core
(:require
[oops.core :as oops]
[re-frame.core :as re-frame]
[react-native.keychain :as keychain]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
(defn- whisper-key-name
[address]
(str address "-whisper"))
(re-frame/reg-fx
:keychain/get-keycard-keys
(fn [[key-uid callback]]
(keychain/get-credentials
key-uid
(fn [encryption-key-data]
(if encryption-key-data
(keychain/get-credentials
(whisper-key-name key-uid)
(fn [whisper-key-data]
(if whisper-key-data
(callback [(oops/oget encryption-key-data "password")
(oops/oget whisper-key-data "password")])
(callback nil))))
(callback nil))))))
(re-frame/reg-fx
:keychain/save-keycard-keys
(fn [[key-uid encryption-public-key whisper-private-key]]
(keychain/save-credentials
key-uid
key-uid
encryption-public-key
#(when-not %
(log/error
(str "Error while saving encryption-public-key"))))
(keychain/save-credentials
(whisper-key-name key-uid)
key-uid
whisper-private-key
#(when-not %
(log/error
(str "Error while saving whisper-private-key"))))))
(rf/defn get-keycard-keys
[_ key-uid]
{:keychain/get-keycard-keys
[key-uid
#(re-frame/dispatch
[:multiaccounts.login.callback/get-keycard-keys-success key-uid %])]})
(rf/defn save-keycard-keys
[_ key-uid encryption-public-key whisper-private-key]
{:keychain/save-keycard-keys [key-uid
encryption-public-key
whisper-private-key]})
+16
View File
@@ -137,3 +137,19 @@
:on-error #(log/error "failed to set light client"
{:error %
:enabled? enabled?})}]})
(rf/defn toggle-store-confirmations
{:events [:wakuv2.ui/toggle-store-confirmations]}
[{:keys [db]} enabled?]
{:db (assoc-in db
[:profile/profile :wakuv2-config :EnableStoreConfirmationForMessagesSent]
enabled?)
:json-rpc/call [{:method "wakuext_setStoreConfirmationForMessagesSent"
:params [{:enabled enabled?}]
:on-success (fn []
(log/info "store confirmation set successfully" enabled?)
(re-frame/dispatch [:logout]))
:on-error #(log/error "failed to set store confirmation"
{:error %
:enabled? enabled?})}]})
+5 -2
View File
@@ -110,8 +110,11 @@
(def status-keycard
#js
{:default #js
{:nfcIsSupported (fn [] #js {:then identity})
:nfcIsEnabled (fn [] #js {:then identity})}})
{:nfcIsSupported (fn [] #js {:then identity})
:nfcIsEnabled (fn [] #js {:then identity})
:getApplicationInfo (fn [] #js {:then identity})
:getKeys (fn [] #js {:then identity})
:setPairings (fn [] #js {:then identity})}})
(def snoopy #js {:default #js {}})
(def snoopy-filter #js {:default #js {}})
+5 -95
View File
@@ -87,31 +87,6 @@
config
#(callback (types/json->clj %))))
(defn save-multiaccount-and-login-with-keycard
"NOTE: chat-key is a whisper private key sent from keycard"
[key-uid multiaccount-data password settings config accounts-data chat-key]
(log/debug "[native-module] save-account-and-login-with-keycard")
(init-keystore
key-uid
#(.saveAccountAndLoginWithKeycard
^js (account-manager)
multiaccount-data
password
settings
config
accounts-data
chat-key)))
(defn login-with-config
"NOTE: beware, the password has to be sha3 hashed"
[key-uid account-data hashed-password config]
(log/debug "[native-module] loginWithConfig")
(clear-web-data)
(let [config (if config (types/clj->json config) "")]
(init-keystore
key-uid
#(.loginWithConfig ^js (account-manager) account-data hashed-password config))))
(defn login-account
"NOTE: beware, the password has to be sha3 hashed"
[{:keys [keyUid] :as request}]
@@ -153,19 +128,6 @@
(clear-web-data)
(.logout ^js (account-manager)))
(defn multiaccount-load-account
"NOTE: beware, the password has to be sha3 hashed
this function is used after storing an account when you still want to
derive accounts from it, because saving an account flushes the loaded keys
from memory"
[address hashed-password callback]
(log/debug "[native-module] multiaccount-load-account")
(.multiAccountLoadAccount ^js (account-manager)
(types/clj->json {:address address
:password hashed-password})
callback))
(defn multiaccount-derive-addresses
"NOTE: this should be named derive-accounts
this only derive addresses, they still need to be stored
@@ -179,38 +141,6 @@
:paths paths})
callback)))
(defn multiaccount-store-account
"NOTE: beware, the password has to be sha3 hashed
this stores the account and flush keys in memory so
in order to also store derived accounts like initial wallet
and chat accounts, you need to load the account again with
`multiaccount-load-account` before using `multiaccount-store-derived`
and the id of the account stored will have changed"
[account-id key-uid hashed-password callback]
(log/debug "[native-module] multiaccount-store-account")
(when (status)
(init-keystore
key-uid
#(.multiAccountStoreAccount ^js (account-manager)
(types/clj->json {:accountID account-id
:password hashed-password})
callback))))
(defn multiaccount-store-derived
"NOTE: beware, the password has to be sha3 hashed"
[account-id key-uid paths hashed-password callback]
(log/debug "[native-module] multiaccount-store-derived"
"account-id"
account-id)
(init-keystore
key-uid
#(.multiAccountStoreDerived ^js (account-manager)
(types/clj->json {:accountID account-id
:paths paths
:password hashed-password})
callback)))
(defn multiaccount-generate-and-derive-addresses
"used to generate multiple multiaccounts for onboarding
NOTE: nothing is saved so you will need to use
@@ -234,37 +164,12 @@
:Bip39Passphrase password})
callback))
(defn multiaccount-import-private-key
[private-key callback]
(log/debug "[native-module] multiaccount-import-private-key")
(.multiAccountImportPrivateKey ^js (account-manager)
(types/clj->json {:privateKey private-key})
callback))
(defn verify
"NOTE: beware, the password has to be sha3 hashed"
[address hashed-password callback]
(log/debug "[native-module] verify")
(.verify ^js (account-manager) address hashed-password callback))
(defn verify-database-password
"NOTE: beware, the password has to be sha3 hashed"
[key-uid hashed-password callback]
(log/debug "[native-module] verify-database-password")
(.verifyDatabasePassword ^js (account-manager) key-uid hashed-password callback))
(defn login-with-keycard
[{:keys [key-uid multiaccount-data password chat-key node-config]}]
(log/debug "[native-module] login-with-keycard")
(clear-web-data)
(init-keystore
key-uid
#(.loginWithKeycard ^js (account-manager)
multiaccount-data
password
chat-key
(types/clj->json node-config))))
(defn set-soft-input-mode
[mode]
(log/debug "[native-module] set-soft-input-mode")
@@ -333,6 +238,11 @@
:key input-key})
(.deserializeAndCompressKey ^js (encryption) input-key callback))
(defn serialize-legacy-key
"Compresses an old format public key (0x04...) to the new one zQ..."
[public-key]
(.serializeLegacyKey ^js (encryption) public-key))
(defn compressed-key->public-key
"Provides compressed key to status-go and gets back the uncompressed public key via deserialization"
[public-key deserialization-key callback]
@@ -5,6 +5,7 @@
(defn- get-dimensions
[size]
(case size
:size-32 32
:size-24 24
:size-20 20
nil))
@@ -1,5 +1,6 @@
(ns quo.components.avatars.token-avatar.view
(:require [quo.components.avatars.token-avatar.style :as style]
[quo.components.utilities.token.view :as token]
[react-native.core :as rn]
[react-native.hole-view :as hole-view]
[react-native.platform :as platform]
@@ -12,13 +13,14 @@
[:map {:closed true}
[:type {:optional true} [:enum :asset :collectible]]
[:context? {:optional true} [:maybe :boolean]]
[:image :schema.common/image-source]
[:image {:optional true} [:maybe :schema.common/image-source]]
[:token {:optional true} [:maybe [:or :keyword :string]]]
[:network-image {:optional true} [:maybe :schema.common/image-source]]
[:container-style {:optional true} [:maybe :map]]]]]
:any])
(defn- view-internal
[{:keys [type context? image network-image container-style]}]
[{:keys [type context? image token network-image container-style]}]
[rn/view
{:style (merge style/container container-style)
:accessibility-label :token-avatar}
@@ -32,9 +34,11 @@
[])
:style style/hole-view}
platform/android? (assoc :key context?))
[rn/image
{:source image
:style (style/image type)}]]
[token/view
{:size :size-32
:token token
:style (style/image type)
:image-source image}]]
(when context?
[rn/image
{:source network-image
@@ -21,15 +21,17 @@
(colors/theme-colors colors/white colors/neutral-95 theme))})
(defn buttons-container
[actions]
{:flex-direction (if (= actions :two-vertical-actions) :column :row)
:justify-content :space-around
:padding-vertical 12
:gap 12
:padding-horizontal 20})
[actions container-style]
(merge
{:flex-direction (if (= actions :two-vertical-actions) :column :row)
:justify-content :space-around
:padding-vertical 12
:gap 12
:padding-horizontal 20}
container-style))
(def button-container
{:flex 1})
{:flex-grow 1})
(def description-top
{:flex-direction :row
@@ -30,7 +30,8 @@
[:button-two-props {:optional true} [:maybe :map]]
[:scroll? {:optional true} [:maybe :boolean]]
[:blur? {:optional true} [:maybe :boolean]]
[:container-style {:optional true} [:maybe :map]]]]]
[:container-style {:optional true} [:maybe :map]]
[:buttons-container-style {:optional true} [:maybe :map]]]]]
:any])
(def ^:private role-icon
@@ -42,7 +43,7 @@
(defn- view-internal
[{:keys [actions description description-text description-top-text error-message role button-one-label
button-two-label blur? button-one-props button-two-props scroll? container-style
context-tag-props]}]
buttons-container-style context-tag-props]}]
(let [theme (quo.theme/use-theme)]
[rn/view
{:style (merge (style/container scroll? blur? theme) container-style)}
@@ -72,14 +73,14 @@
:context (i18n/label (keyword "t" role))}
context-tag-props)]])
[rn/view {:style (style/buttons-container actions)}
[rn/view {:style (style/buttons-container actions buttons-container-style)}
(when (or (= actions :two-actions)
(= actions :two-vertical-actions))
[button/button
(merge
{:size 40
:background (when (or blur? scroll?) :blur)
:container-style style/button-container
:background (when (or blur? scroll?) :blur)
:theme theme
:accessibility-label :button-two}
button-two-props)
@@ -0,0 +1,58 @@
(ns quo.components.list-items.approval-info.component-spec
(:require [quo.components.list-items.approval-info.view :as approval-info]
[test-helpers.component :as h]))
(h/describe "List Items: Approval Info"
(h/test "should render correctly with basic props"
(h/render-with-theme-provider
[approval-info/view
{:type :spending-cap
:label "Spending Cap"
:avatar-props {:image "image"}}])
(h/is-truthy (h/get-by-label-text :approval-info)))
(h/test "should render correctly with label & description"
(h/render-with-theme-provider
[approval-info/view
{:type :spending-cap
:label "Spending Cap"
:description "Description"
:avatar-props {:image "image"}}])
(h/is-truthy (h/get-by-text "Spending Cap"))
(h/is-truthy (h/get-by-text "Description")))
(h/test "on-button-press event is called when button is pressed"
(let [on-button-press (h/mock-fn)]
(h/render-with-theme-provider
[approval-info/view
{:type :spending-cap
:label "Spending Cap"
:button-label "Edit"
:button-icon :i/options
:on-button-press on-button-press
:avatar-props {:image "image"}}])
(h/fire-event :press (h/get-by-text "Edit"))
(h/was-called on-button-press)))
(h/test "on-option-press event is called when option icon is pressed"
(let [on-option-press (h/mock-fn)]
(h/render-with-theme-provider
[approval-info/view
{:type :spending-cap
:label "Spending Cap"
:option-icon :i/options
:on-option-press on-option-press
:avatar-props {:image "image"}}])
(h/fire-event :press (h/get-by-label-text :icon))
(h/was-called on-option-press)))
(h/test "on-avatar-press event is called when avatar is pressed"
(let [on-avatar-press (h/mock-fn)]
(h/render-with-theme-provider
[approval-info/view
{:type :spending-cap
:label "Spending Cap"
:on-avatar-press on-avatar-press
:avatar-props {:image "image"}}])
(h/fire-event :press (h/get-by-label-text :token-avatar))
(h/was-called on-avatar-press))))
@@ -0,0 +1,36 @@
(ns quo.components.list-items.approval-info.style
(:require [quo.foundations.colors :as colors]))
(defn icon-description-color
[blur? theme]
(if blur?
colors/white-opa-40
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme)))
(defn container
[description? blur? theme]
{:flex-direction :row
:padding-horizontal 12
:padding-vertical (if description? 8 12)
:border-radius 16
:gap 8
:align-items :center
:border-width 1
:border-color (if blur?
colors/white-opa-5
(colors/theme-colors colors/neutral-10 colors/neutral-80 theme))})
(def labels
{:flex 1
:justify-content :center
:padding-right 4})
(defn label
[blur? theme]
{:color (if blur?
colors/white
(colors/theme-colors colors/neutral-100 colors/white theme))})
(defn description
[blur? theme]
{:color (icon-description-color blur? theme)})
@@ -0,0 +1,124 @@
(ns quo.components.list-items.approval-info.view
(:require [clojure.string :as string]
[quo.components.avatars.account-avatar.view :as account-avatar]
[quo.components.avatars.collection-avatar.view :as collection-avatar]
[quo.components.avatars.community-avatar.view :as community-avatar]
[quo.components.avatars.dapp-avatar.view :as dapp-avatar]
[quo.components.avatars.icon-avatar :as icon-avatar]
[quo.components.avatars.token-avatar.view :as token-avatar]
[quo.components.avatars.wallet-user-avatar.view :as wallet-user-avatar]
[quo.components.buttons.button.view :as button]
[quo.components.icon :as icon]
[quo.components.list-items.approval-info.style :as style]
[quo.components.markdown.text :as text]
[quo.components.tags.tiny-tag.view :as tiny-tag]
[quo.foundations.colors :as colors]
quo.theme
[react-native.core :as rn]
[schema.core :as schema]))
(def ?schema
[:=>
[:catn
[:props
[:map {:closed true}
[:type
[:enum :spending-cap :token-contract :account :spending-contract :network :date-signed
:collectible :address :community :collectible-contract]]
[:avatar-props :map]
[:label :string]
[:description {:optional true} [:maybe :string]]
[:button-label {:optional true} [:maybe :string]]
[:button-icon {:optional true} [:maybe :keyword]]
[:blur? {:optional true} [:maybe :boolean]]
[:unlimited-icon? {:optional true} [:maybe :boolean]]
[:option-icon {:optional true} [:maybe :keyword]]
[:tag-label {:optional true} [:maybe :string]]
[:on-button-press {:optional true} [:maybe fn?]]
[:on-avatar-press {:optional true} [:maybe fn?]]
[:on-option-press {:optional true} [:maybe fn?]]
[:container-style {:optional true} [:maybe :any]]]]]
:any])
(defn- avatar
[{:keys [type avatar-props blur? theme on-press]}]
[rn/pressable {:on-press on-press}
(case type
:account [account-avatar/view (assoc avatar-props :size 32)]
:collectible-contract [collection-avatar/view (assoc avatar-props :size :size-32)]
:spending-contract [dapp-avatar/view avatar-props]
:date-signed [icon-avatar/icon-avatar
(assoc avatar-props
:size :size-32
:opacity 10
:color (if blur?
colors/white
(colors/theme-colors colors/neutral-50
colors/neutral-40
theme)))]
:address [wallet-user-avatar/wallet-user-avatar
(assoc avatar-props
:size :size-32
:monospace? true
:lowercase? true
:neutral? true)]
:community [community-avatar/view avatar-props]
[token-avatar/view
(assoc avatar-props
:type
(if (= type :collectible) :collectible :asset))])])
(defn- view-internal
[{:keys [type avatar-props label description blur? unlimited-icon? container-style
on-option-press on-avatar-press on-button-press button-label button-icon tag-label
option-icon]}]
(let [theme (quo.theme/use-theme)
description? (not (string/blank? description))]
[rn/view
{:style (merge (style/container description? blur? theme) container-style)
:accessibility-label :approval-info}
[avatar
{:type type
:blur? blur?
:theme theme
:on-press on-avatar-press
:avatar-props avatar-props}]
[rn/view
{:style style/labels}
[rn/view
{:style {:flex-direction :row :align-items :center}}
[text/text
{:size :paragraph-1
:weight (if (= type :address) :monospace :semi-bold)
:style (style/label blur? theme)}
label]
(when unlimited-icon?
[icon/icon :i/alert
{:container-style {:margin-left 4}
:size 16
:color (style/icon-description-color blur? theme)}])]
(when description?
[text/text
{:size :paragraph-2
:weight (if (contains? #{:collectible-contract :spending-contract :account :token-contract}
type)
:monospace
:regular)
:style (style/description blur? theme)}
description])]
(when (= type :account) [tiny-tag/view {:label tag-label}])
(when (and (= type :spending-cap) button-icon)
[button/button
{:type :outline
:size 24
:blur? blur?
:icon-left button-icon
:on-press on-button-press}
button-label])
(when option-icon
[rn/pressable {:on-press on-option-press}
[icon/icon option-icon
{:color (style/icon-description-color blur? theme)
:size 20}]])]))
(def view (schema/instrument #'view-internal ?schema))
@@ -47,3 +47,12 @@
(defn style-text-value
[theme]
{:color (colors/theme-colors colors/neutral-50 colors/white theme)})
(def initials-avatar-container
{:width 32
:height 32})
(def image-avatar
{:width 32
:height 32
:border-radius 32})
+10 -3
View File
@@ -1,5 +1,6 @@
(ns quo.components.list-items.dapp.view
(:require
[quo.components.avatars.user-avatar.view :as user-avatar]
[quo.components.list-items.dapp.style :as style]
[quo.components.markdown.text :as text]
[quo.theme :as quo.theme]
@@ -20,9 +21,15 @@
:on-press-in on-press-in
:on-press-out on-press-out}
[rn/view {:style style/container-info}
[fast-image/fast-image
{:source (:avatar dapp)
:style {:width 32 :height 32}}]
(if (:avatar dapp)
[fast-image/fast-image
{:source (:avatar dapp)
:style style/image-avatar}]
[rn/view {:style style/initials-avatar-container}
[user-avatar/initials-avatar
{:full-name (:name dapp)
:size :small
:customization-color (:customization-color dapp)}]])
[rn/view {:style style/user-info}
[text/text
{:weight :semi-bold
@@ -0,0 +1,43 @@
(ns quo.components.pin-input.pin.view
(:require [quo.foundations.colors :as colors]
quo.theme
[react-native.core :as rn]))
(defn view
[{:keys [theme state blur?]}]
(let [app-theme (quo.theme/use-theme)
theme (or theme app-theme)]
[rn/view {:style {:width 36 :height 36 :align-items :center :justify-content :center}}
(case state
:active
[rn/view
{:style {:width 16
:height 16
:border-radius 8
:background-color (if blur?
colors/white-opa-20
(colors/theme-colors colors/neutral-40 colors/neutral-50 theme))}}]
:filled
[rn/view
{:style {:width 16
:height 16
:border-radius 8
:background-color (if blur?
colors/white
(colors/theme-colors colors/neutral-100 colors/white theme))}}]
:error
[rn/view
{:style {:width 16
:height 16
:border-radius 8
:background-color (if blur?
colors/danger-60
(colors/theme-colors colors/danger-50 colors/danger-60 theme))}}]
[rn/view
{:style
{:width 12
:height 12
:border-radius 6
:background-color (if blur?
colors/white-opa-20
(colors/theme-colors colors/neutral-40 colors/neutral-50 theme))}}])]))
+27
View File
@@ -0,0 +1,27 @@
(ns quo.components.pin-input.view
(:require [quo.components.markdown.text :as text]
[quo.components.pin-input.pin.view :as pin]
[quo.foundations.colors :as colors]
quo.theme
[react-native.core :as rn]))
(defn view
[{:keys [number-of-pins number-of-filled-pins error? info]
:or {number-of-pins 6 number-of-filled-pins 0}}]
(let [theme (quo.theme/use-theme)]
[rn/view {:style {:align-items :center}}
[rn/view {:style {:flex-direction :row}}
(for [i (range 1 (inc number-of-pins))]
^{:key i}
[pin/view
{:state (cond
error? :error
(<= i number-of-filled-pins) :filled
(= i (inc number-of-filled-pins)) :active)}])]
(when info
[text/text
{:style {:color (if error?
(colors/theme-colors colors/danger-50 colors/danger-60 theme)
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme))}
:size :paragraph-2}
info])]))
@@ -79,14 +79,20 @@
[counter-view counter])
[rn/view {:style (style/collectible-border theme)}]]]))
(defn invalid-image?
[image-src]
(or (nil? image-src)
(string/blank? image-src)))
(defn view-internal
[{:keys [container-style square? on-press counter image-src native-ID supported-file?
on-collectible-load aspect-ratio gradient-color-index]
:or {gradient-color-index :gradient-1
on-collectible-load (fn [])}}]
(let [theme (quo.theme/use-theme)
[error? set-error] (rn/use-state (or (nil? image-src)
(string/blank? image-src)))]
[error? set-error] (rn/use-state (invalid-image? image-src))]
(rn/use-effect #(set-error (invalid-image? image-src))
[image-src])
[rn/pressable
{:style (merge container-style (style/container aspect-ratio))
:accessibility-label :expanded-collectible
@@ -159,4 +159,19 @@
:icon :i/placeholder
:emoji "🎮"
:customization-color :yellow}])
(h/is-truthy (h/query-by-label-text :account-emoji))))
(h/is-truthy (h/query-by-label-text :account-emoji)))
(h/test "edit icon is visible when subtitle type is editable"
(h/render [quo/data-item
{:on-press (h/mock-fn)
:blur? false
:card? true
:status :default
:size :default
:title "Label"
:subtitle "Subtitle"
:subtitle-type :editable
:icon :i/placeholder
:emoji "🎮"
:customization-color :yellow}])
(h/is-truthy (h/query-by-label-text :edit-icon))))
@@ -54,6 +54,7 @@
(def subtitle-container
{:flex-direction :row
:align-items :center
:margin-bottom 1})
(def right-container
@@ -62,7 +63,7 @@
(defn subtitle-icon-container
[subtitle-type]
{:margin-right (when (not= :default subtitle-type) 4)
{:margin-right (when-not (contains? #{:editable :default} subtitle-type) 4)
:justify-content :center})
(defn title
@@ -7,7 +7,8 @@
[quo.components.settings.data-item.style :as style]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]))
[react-native.core :as rn]
[schema.core :as schema]))
(defn- left-loading
[{:keys [size blur?]}]
@@ -40,7 +41,17 @@
{:weight :medium
:size :paragraph-2
:style (style/description blur? theme)}
subtitle]]))
subtitle]
(when (= subtitle-type :editable)
[icons/icon :i/edit
{:accessibility-label :edit-icon
:size 12
:container-style {:margin-left 2}
:color (if blur?
colors/neutral-40
(colors/theme-colors colors/neutral-50
colors/neutral-40
theme))}])]))
(defn- left-title
[{:keys [title blur?]}]
@@ -100,7 +111,32 @@
:color icon-color
:size 20}]])]))
(defn view
(def ?schema
[:=>
[:catn
[:props
[:map {:closed true}
[:blur? {:optional true} [:maybe :boolean]]
[:card? {:optional true} [:maybe :boolean]]
[:right-icon {:optional true} [:maybe :keyword]]
[:right-content {:optional true} [:maybe :map]]
[:icon-color {:optional true} [:maybe :schema.common/customization-color]]
[:status {:optional true} [:maybe [:enum :default :loading]]]
[:subtitle-type {:optional true} [:maybe [:enum :default :icon :network :account :editable]]]
[:size {:optional true} [:maybe [:enum :default :small :large]]]
[:title :string]
[:subtitle {:optional true} [:maybe :string]]
[:custom-subtitle {:optional true} [:maybe fn?]]
[:icon {:optional true} [:maybe :keyword]]
[:emoji {:optional true} [:maybe :string]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]
[:network-image {:optional true} [:maybe :schema.common/image-source]]
[:on-press {:optional true} [:maybe fn?]]
[:container-style {:optional true} [:maybe :map]]]]]
:any])
(defn view-internal
[{:keys [blur? card? right-icon right-content status size on-press container-style]
:as props}]
(let [theme (quo.theme/use-theme)
@@ -123,3 +159,5 @@
{:right-icon right-icon
:right-content right-content
:icon-color icon-color}])]))
(def view (schema/instrument #'view-internal ?schema))
@@ -0,0 +1,25 @@
(ns quo.components.slideshow.slider-bar.component-spec
(:require
[quo.components.slideshow.slider-bar.view :as slider-bar]
[test-helpers.component :as h]))
(h/describe "Slideshow: slider-bar"
(h/test "default render"
(h/render-with-theme-provider [slider-bar/view {:accessibility-label :slider-bar}])
(h/is-truthy (h/query-by-label-text :slider-bar)))
(h/test "render with total-amount and active-index"
(h/render-with-theme-provider [slider-bar/view
{:total-amount 4
:active-index 1
:accessibility-label :slider-bar}])
(h/is-truthy (h/query-by-label-text :slider-bar))
(h/is-equal 4 (count (h/query-all-by-label-text :slide-bar-item))))
(h/test "render with total-amount active-index and possible scroll"
(h/render-with-theme-provider [slider-bar/view
{:total-amount 10
:active-index 7
:accessibility-label :slider-bar}])
(h/is-truthy (h/query-by-label-text :slider-bar))
(h/is-equal 10 (count (h/query-all-by-label-text :slide-bar-item)))))
@@ -0,0 +1,14 @@
(ns quo.components.slideshow.slider-bar.schema)
(def ?schema
[:=>
[:cat
[:map {:closed true}
[:total-amount {:optional true} [:maybe :int]]
[:active-index {:optional true} :int]
[:customization-color {:optional true}
[:maybe :schema.common/customization-color]]
[:blur? {:optional true} [:maybe :boolean]]
[:accessibility-label {:optional true} [:maybe :keyword]]
[:container-style {:optional true} [:maybe :map]]]]
:any])
@@ -0,0 +1,31 @@
(ns quo.components.slideshow.slider-bar.style
(:require
[quo.foundations.colors :as colors]))
(def list-wrapper
{:align-items :center})
(defn list-bar
[bar-width]
{:width bar-width})
(defn item-wrapper
[{:keys [size spacing]}]
{:width size
:height size
:margin-left spacing
:flex 1
:align-items :center
:justify-content :center})
(defn item
[{:keys [size spacing active? customization-color theme blur?]}]
{:width size
:height size
:border-radius spacing
:background-color
(cond
(and blur? active?) colors/white
(and blur? (not active?)) colors/white-opa-10
(and (not blur?) active?) (colors/resolve-color customization-color theme)
:else (colors/theme-colors colors/neutral-20 colors/neutral-80 theme))})
@@ -0,0 +1,150 @@
(ns quo.components.slideshow.slider-bar.view
(:require
[quo.components.slideshow.slider-bar.schema :as component-schema]
[quo.components.slideshow.slider-bar.style :as style]
[quo.theme]
[react-native.core :as rn]
[react-native.reanimated :as reanimated]
[schema.core :as schema]))
(def item-size 8)
(def item-spacing item-size)
(def max-length 6)
(def bar-width (* (+ item-spacing item-size) max-length))
(def micro-scale 0.25)
(def small-scale 0.5)
(def medium-scale 0.75)
(def default-scale 1)
(def items-before-scroll
(/ max-length 2))
(defn- calc-relative-index
"Calculates item index in visible area"
[index active-index total-length]
(let [last-index (dec total-length)]
(cond
(< active-index items-before-scroll) index
(> active-index
(- last-index items-before-scroll)) (+ (- index total-length) max-length)
:else (+ (- index active-index) items-before-scroll))))
(defn- calc-first-item-scale
"Calculates scale for first item in visible area"
[active-index]
(cond
(= active-index items-before-scroll) medium-scale
(> active-index items-before-scroll) small-scale
:else default-scale))
(defn- calc-second-item-scale
"Calculates scale for second item in visible area"
[active-index]
(if (> active-index items-before-scroll)
medium-scale
default-scale))
(defn- calc-last-item-scale
"Calculates scale for last item in visible area"
[active-index total-length]
(let [last-index (dec total-length)]
(if (> active-index (- last-index items-before-scroll))
medium-scale
small-scale)))
(defn- calc-last-but-one-item-scale
"Calculates scale for before last item in visible area"
[active-index total-length]
(if (> active-index (- (dec total-length) items-before-scroll))
default-scale
medium-scale))
(defn- get-scale
[index active-index total-length]
(let [relative-index (calc-relative-index index active-index total-length)]
(if (or (= index active-index)
(< total-length max-length))
default-scale
(cond
(< relative-index 0) micro-scale
(= relative-index 0) (calc-first-item-scale active-index)
(= relative-index 1) (calc-second-item-scale active-index)
(= relative-index 4) (calc-last-but-one-item-scale active-index total-length)
(= relative-index 5) (calc-last-item-scale active-index total-length)
(> relative-index 5) micro-scale
:else
default-scale))))
(defn- bar-item
[index _ _ {:keys [active-index total-length customization-color theme blur?]}]
(let [active? (= index active-index)
shared-scale (reanimated/use-shared-value (get-scale index active-index total-length))]
(rn/use-effect (fn []
(let [new-scale-value (get-scale index active-index total-length)]
(reanimated/animate shared-scale new-scale-value)))
[index active-index total-length])
[rn/view
{:key index
:style (style/item-wrapper {:size item-size
:spacing item-spacing})}
[reanimated/view
{:accessibility-label :slide-bar-item
:style
(reanimated/apply-animations-to-style
{:transform [{:scale shared-scale}]}
(style/item {:size item-size
:spacing item-spacing
:active? active?
:customization-color customization-color
:theme theme
:blur? blur?}))}]]))
(defn- get-item-layout
[_ index]
(let [length (+ item-size item-spacing)]
#js {:length length
:index index
:offset (* index length)}))
(defn- view-internal
[{:keys [customization-color blur? accessibility-label container-style]
:as props}]
(let [active-index (or (:active-index props) 0)
total-amount (or (:total-amount props) 1)
theme (quo.theme/use-theme)
flat-list-ref (rn/use-ref-atom nil)
set-flat-list-ref (rn/use-callback #(reset! flat-list-ref %))
center-position 0.5
data (range total-amount)
scroll-to-index (rn/use-callback
(fn []
(some-> ^js @flat-list-ref
(.scrollToIndex #js {:animated true
:index active-index
:viewOffset item-spacing
:viewPosition center-position})))
[flat-list-ref active-index])]
(rn/use-effect scroll-to-index [active-index])
[rn/view
{:style (merge style/list-wrapper container-style)
:accessibility-label accessibility-label}
[reanimated/flat-list
{:style (style/list-bar bar-width)
:data data
:scroll-enabled false
:ref set-flat-list-ref
:shows-horizontal-scroll-indicator false
:bounces false
:horizontal true
:extra-data (str active-index)
:render-data {:active-index active-index
:total-length total-amount
:customization-color customization-color
:theme theme
:blur? blur?}
:get-item-layout get-item-layout
:render-fn bar-item
:key-fn identity}]]))
(def view (schema/instrument #'view-internal component-schema/?schema))
@@ -53,7 +53,7 @@
(def ^:private ?network
[:map
[:network-logo {:optional true} [:maybe :schema.common/image-source]]
[:network-name {:optional true} [:maybe :string]]])
[:network-name {:optional true} [:maybe [:or :string :keyword]]]])
(def ^:private ?multinetwork
[:map
@@ -41,7 +41,8 @@
:align-items :center
:height size
:background-color background-color
:border-radius border-radius}
:border-radius border-radius
:flex-shrink 1}
(= state :selected) (assoc :height (+ size 2)
:border-color border-color
:border-width 1))))
@@ -50,11 +51,13 @@
[size]
{:margin-right (if (= size 24) 6 10)
:flex-direction :row
:flex-shrink 1
:align-items :center})
(defn tag-spacing
[size]
{:margin-left (if (= size 24) 4 8)})
[size shrinkable?]
(cond-> {:margin-left (if (= size 24) 4 8)}
shrinkable? (assoc :flex-shrink 1)))
(defn text
[theme]
+12 -9
View File
@@ -17,17 +17,19 @@
[schema.core :as schema]))
(defn- tag-skeleton
[{:keys [size text theme]
[{:keys [size text theme shrinkable?]
:or {size 24
theme (quo.theme/use-theme)}}
logo-component]
[rn/view {:style (style/tag-container size)}
logo-component
[rn/view {:style (style/tag-spacing size)}
[rn/view {:style (style/tag-spacing size shrinkable?)}
[text/text
{:style (style/text theme)
:weight :medium
:size (if (= size 24) :paragraph-2 :paragraph-1)}
{:style (style/text theme)
:weight :medium
:size (if (= size 24) :paragraph-2 :paragraph-1)
:number-of-lines 1
:ellipsize-mode :middle}
text]]])
(defn- communities-tag
@@ -37,7 +39,7 @@
icon-size (if (= size 24) 16 20)]
[rn/view {:style (style/tag-container size)}
[fast-image/fast-image {:style (style/circle-logo size) :source community-logo}]
[rn/view {:style (style/tag-spacing size)}
[rn/view {:style (style/tag-spacing size false)}
[text/text
{:style (style/text theme)
:weight :medium
@@ -148,9 +150,10 @@
:collectible
[tag-skeleton
{:theme theme
:size size
:text (str collectible-name " #" collectible-number)}
{:theme theme
:size size
:text (str collectible-name " #" collectible-number)
:shrinkable? true}
[rn/image {:style (style/rounded-logo size) :source collectible}]]
:account
@@ -5,8 +5,9 @@
[:catn
[:props
[:map
[:type [:enum :status-account :saved-account :account :user]]
[:type [:enum :status-account :saved-account :account :user :token]]
[:account-props {:optional true} [:maybe :map]]
[:token-props {:optional true} [:maybe :map]]
[:networks? {:optional true} [:maybe :boolean]]
[:values {:optional true} [:maybe :map]]]]]
:any])
@@ -4,6 +4,7 @@
[quo.components.avatars.user-avatar.view :as user-avatar]
[quo.components.avatars.wallet-user-avatar.view :as wallet-user-avatar]
[quo.components.markdown.text :as text]
[quo.components.utilities.token.view :as token]
[quo.components.wallet.summary-info.schema :as summary-info-schema]
[quo.components.wallet.summary-info.style :as style]
[quo.foundations.colors :as colors]
@@ -58,13 +59,14 @@
:theme theme}])]))
(defn- view-internal
[{:keys [type account-props networks? values]}]
[{:keys [type account-props token-props networks? values]}]
(let [theme (quo.theme/use-theme)]
[rn/view
{:style (style/container networks? theme)}
[rn/view
{:style style/info-container}
(case type
:token [token/view (select-keys token-props #{:token :size})]
:status-account [account-avatar/view account-props]
:saved-account [wallet-user-avatar/wallet-user-avatar (assoc account-props :size :size-32)]
:account [wallet-user-avatar/wallet-user-avatar
@@ -73,7 +75,8 @@
:neutral? true)]
[user-avatar/user-avatar account-props])
[rn/view {:style {:margin-left 8}}
(when (not= type :account) [text/text {:weight :semi-bold} (:name account-props)])
(when (not= type :account)
[text/text {:weight :semi-bold} (or (:name account-props) (:label token-props))])
[rn/view
{:style {:flex-direction :row
:align-items :center}}
@@ -91,7 +94,7 @@
:weight (when (= type :account) :semi-bold)
:style {:color (when (not= type :account)
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme))}}
(:address account-props)]]]]
(or (:address account-props) (:address token-props))]]]]
(when networks?
[:<>
[rn/view
@@ -0,0 +1,31 @@
(ns quo.components.wallet.swap-input.component-spec
(:require [quo.components.wallet.swap-input.view :as swap-input]
[test-helpers.component :as h]))
(h/describe "Wallet: Swap Input"
(h/test "should render correctly with props"
(h/render-with-theme-provider
[swap-input/view
{:type :pay
:error? false
:token "SNT"
:status :default
:currency-symbol "€"
:value "5"
:fiat-value "1.50"
:network-tag-props {:title "Max: 200 SNT"}}])
(h/is-truthy (h/get-by-label-text :swap-input))
(h/is-truthy (h/get-by-text "SNT"))
(h/is-truthy (h/get-by-text "€1.50"))
(h/is-truthy (h/get-by-text "Max: 200 SNT")))
(h/test "should render correctly with approval label"
(h/render-with-theme-provider
[swap-input/view
{:type :pay
:show-approval-label? true
:approval-label-props
{:status :approve
:token-value "10"
:token-symbol "SNT"}}])
(h/is-truthy (h/get-by-text "Approve 10 SNT"))))
@@ -0,0 +1,74 @@
(ns quo.components.wallet.swap-input.style
(:require [quo.foundations.colors :as colors]
[quo.foundations.typography :as typography]))
(defn- border-color
[theme]
(colors/theme-colors colors/neutral-10 colors/neutral-80 theme))
(defn- loader-color
[theme]
(colors/theme-colors colors/neutral-5 colors/neutral-90 theme))
(defn content
[theme]
{:border-width 1
:border-radius 16
:border-color (border-color theme)
:background-color (colors/theme-colors colors/white colors/neutral-95 theme)})
(defn row-1
[loading?]
{:padding 12
:gap 8
:align-items (if loading? :center :flex-end)
:flex-direction :row})
(defn row-1-loader
[theme]
{:width 74
:height 14
:border-radius 6
:background-color (loader-color theme)})
(def input-container
{:flex 1
:flex-direction :row
:gap 5
:height 32
:align-items :flex-end})
(defn input
[disabled? error? theme]
(assoc typography/font-semi-bold
:font-size 27
:flex-shrink 1
:padding 0
:color (cond
error? (colors/resolve-color :danger theme)
disabled? (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)
:else (colors/theme-colors colors/neutral-100 colors/white theme))
:line-height 32))
(defn token-symbol
[theme]
{:padding-bottom 3
:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)})
(defn row-2
[align-right?]
{:flex-direction :row
:justify-content (if align-right? :flex-end :space-between)
:align-items :center
:padding 12})
(defn row-2-loader
[theme]
{:width 80
:height 10
:margin-vertical 7
:border-radius 6
:background-color (loader-color theme)})
(def fiat-amount
{:color colors/neutral-50})
@@ -0,0 +1,121 @@
(ns quo.components.wallet.swap-input.view
(:require [oops.core :as oops]
[quo.components.avatars.token-avatar.view :as token-avatar]
[quo.components.buttons.button.view :as buttons]
[quo.components.dividers.divider-line.view :as divider-line]
[quo.components.markdown.text :as text]
[quo.components.tags.network-tags.view :as network-tag]
[quo.components.wallet.approval-label.schema :as approval-label.schema]
[quo.components.wallet.approval-label.view :as approval-label]
[quo.components.wallet.swap-input.style :as style]
[quo.foundations.colors :as colors]
quo.theme
[react-native.core :as rn]
[schema.core :as schema]))
(def ?schema
[:=>
[:catn
[:props
[:map {:closed true}
[:type {:optional true} [:maybe [:enum :pay :receive]]]
[:status {:optional true} [:maybe [:enum :default :disabled :loading]]]
[:token {:optional true} [:maybe :string]]
[:value {:optional true} [:maybe :string]]
[:default-value {:optional true} [:maybe :string]]
[:currency-symbol {:optional true} [:maybe :string]]
[:fiat-value {:optional true} [:maybe :string]]
[:show-approval-label? {:optional true} [:maybe :boolean]]
[:error? {:optional true} [:maybe :boolean]]
[:show-keyboard? {:optional true} [:maybe :boolean]]
[:approval-label-props {:optional true} [:maybe approval-label.schema/?schema]]
[:network-tag-props {:optional true} [:maybe :map]]
[:on-change-text {:optional true} [:maybe fn?]]
[:enable-swap? {:optional true} [:maybe :boolean]]
[:on-swap-press {:optional true} [:maybe fn?]]
[:on-token-press {:optional true} [:maybe fn?]]
[:on-max-press {:optional true} [:maybe fn?]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]
[:container-style {:optional true} [:maybe :map]]]]]
:any])
(defn view-internal
[{:keys [type status token value fiat-value show-approval-label? error? network-tag-props
approval-label-props default-value enable-swap?
currency-symbol on-change-text show-keyboard?
container-style on-swap-press on-token-press on-max-press]}]
(let [theme (quo.theme/use-theme)
pay? (= type :pay)
disabled? (= status :disabled)
loading? (= status :loading)
controlled-input? (some? value)
input-ref (rn/use-ref-atom nil)
set-input-ref (rn/use-callback (fn [ref] (reset! input-ref ref)) [])
focus-input (rn/use-callback (fn []
(some-> @input-ref
(oops/ocall "focus")))
[input-ref])]
[rn/view
{:style container-style
:accessibility-label :swap-input}
[rn/view {:style (style/content theme)}
[rn/view
{:style (style/row-1 loading?)}
[rn/pressable {:on-press on-token-press}
[token-avatar/view
{:type :asset
:token token}]]
(if loading?
[rn/view {:style (style/row-1-loader theme)}]
[:<>
[rn/pressable
{:style style/input-container
:on-press focus-input}
[rn/text-input
(cond-> {:ref set-input-ref
:style (style/input disabled? error? theme)
:placeholder-text-color (colors/theme-colors colors/neutral-40
colors/neutral-50
theme)
:keyboard-type :numeric
:auto-focus true
:on-change-text on-change-text
:show-soft-input-on-focus show-keyboard?
:default-value default-value
:placeholder "0"}
controlled-input? (assoc :value value))]
[text/text
{:size :paragraph-2
:weight :semi-bold
:style (style/token-symbol theme)}
token]]
(when (and pay? enable-swap?)
[buttons/button
{:type :outline
:size 32
:on-press on-swap-press
:icon-only? true}
:i/reorder])])]
[divider-line/view]
[rn/view
{:style (style/row-2 (or (not pay?) loading?))}
(when-not loading?
[:<>
(when pay?
[rn/pressable {:on-press on-max-press}
[network-tag/view
(assoc network-tag-props
:status
(if error? :error :default))]])
[text/text
{:size :paragraph-2
:style style/fiat-amount
:weight :medium}
(str currency-symbol fiat-value)]])
(when loading?
[rn/view {:style (style/row-2-loader theme)}])]]
(when (and (not= status :loading) (= type :pay) show-approval-label?)
[approval-label/view
approval-label-props])]))
(def view (schema/instrument #'view-internal ?schema))
@@ -51,8 +51,7 @@
(defn prop-text
[theme]
{:margin-right 4
:color (colors/theme-colors colors/neutral-100 colors/white theme)})
{:color (colors/theme-colors colors/neutral-100 colors/white theme)})
(def icon-container
{:width 32
@@ -61,12 +60,15 @@
(def container
{:flex-direction :row
:flex 1
:column-gap 8})
(def content-line
{:flex-direction :row
:margin-top 2
:align-items :center})
{:flex-direction :row
:margin-top 2
:align-items :center
:column-gap 4
:justify-content :flex-start})
(def icon-hole-view
{:width 32
@@ -98,8 +98,11 @@
(defn prop-tag
[props blur?]
[rn/view {:style {:margin-right 4}}
[context-tag/view (merge props {:size 24 :blur? blur?})]])
[context-tag/view
(assoc props
:size 24
:blur? blur?
:container-style {:flex-shrink 1})])
(defn- view-internal
[{:keys [state blur? first-tag second-tag third-tag fourth-tag on-press
@@ -121,7 +124,7 @@
:on-press-out on-press-out}
[rn/view {:style style/container}
[transaction-icon-view props theme]
[rn/view
[rn/view {:style {:flex 1}}
[transaction-header props theme]
[rn/view {:style style/content-line}
(when first-tag [prop-tag first-tag blur?])
+12
View File
@@ -86,6 +86,7 @@
quo.components.list-items.account-list-card.view
quo.components.list-items.account.view
quo.components.list-items.address.view
quo.components.list-items.approval-info.view
quo.components.list-items.channel.view
quo.components.list-items.community.view
quo.components.list-items.dapp.view
@@ -120,6 +121,7 @@
quo.components.overlay.view
quo.components.password.password-tips.view
quo.components.password.tips.view
quo.components.pin-input.view
quo.components.profile.collectible-list-item.view
quo.components.profile.collectible.view
quo.components.profile.expanded-collectible.view
@@ -145,6 +147,7 @@
quo.components.settings.settings-item.view
quo.components.share.qr-code.view
quo.components.share.share-qr-code.view
quo.components.slideshow.slider-bar.view
quo.components.switchers.group-messaging-card.view
quo.components.tabs.account-selector
quo.components.tabs.segmented-tab
@@ -183,6 +186,7 @@
quo.components.wallet.progress-bar.view
quo.components.wallet.required-tokens.view
quo.components.wallet.summary-info.view
quo.components.wallet.swap-input.view
quo.components.wallet.token-input.view
quo.components.wallet.transaction-progress.view
quo.components.wallet.transaction-summary.view
@@ -320,6 +324,9 @@
(def keyboard-key quo.components.numbered-keyboard.keyboard-key.view/view)
(def numbered-keyboard quo.components.numbered-keyboard.numbered-keyboard.view/view)
;;;; PIN input
(def pin-input quo.components.pin-input.view/view)
;;;; Links
(def internal-link-card quo.components.links.internal-link-card.view/view)
(def link-preview quo.components.links.link-preview.view/view)
@@ -330,6 +337,7 @@
(def account-item quo.components.list-items.account.view/view)
(def account-list-card quo.components.list-items.account-list-card.view/view)
(def address quo.components.list-items.address.view/view)
(def approval-info quo.components.list-items.approval-info.view/view)
(def channel quo.components.list-items.channel.view/view)
(def community-list quo.components.list-items.community.view/view)
(def dapp quo.components.list-items.dapp.view/view)
@@ -412,6 +420,9 @@
(def qr-code quo.components.share.qr-code.view/view)
(def share-qr-code quo.components.share.share-qr-code.view/view)
;;;; Slideshow
(def slider-bar quo.components.slideshow.slider-bar.view/view)
;;;; SWITCHER
(def group-messaging-card quo.components.switchers.group-messaging-card.view/view)
@@ -462,6 +473,7 @@
(def progress-bar quo.components.wallet.progress-bar.view/view)
(def required-tokens quo.components.wallet.required-tokens.view/view)
(def summary-info quo.components.wallet.summary-info.view/view)
(def swap-input quo.components.wallet.swap-input.view/view)
(def network-link quo.components.wallet.network-link.view/view)
(def token-input quo.components.wallet.token-input.view/view)
(def wallet-overview quo.components.wallet.wallet-overview.view/view)
+3
View File
@@ -55,6 +55,7 @@
quo.components.links.url-preview.component-spec
quo.components.list-items.account.component-spec
quo.components.list-items.address.component-spec
quo.components.list-items.approval-info.component-spec
quo.components.list-items.channel.component-spec
quo.components.list-items.community.component-spec
quo.components.list-items.dapp.component-spec
@@ -86,6 +87,7 @@
quo.components.settings.reorder-item.component-spec
quo.components.settings.settings-item.component-spec
quo.components.share.share-qr-code.component-spec
quo.components.slideshow.slider-bar.component-spec
quo.components.switchers.base-card.component-spec
quo.components.switchers.group-messaging-card.component-spec
quo.components.tags.collectible-tag.component-spec
@@ -110,6 +112,7 @@
quo.components.wallet.progress-bar.component-spec
quo.components.wallet.required-tokens.component-spec
quo.components.wallet.summary-info.component-spec
quo.components.wallet.swap-input.component-spec
quo.components.wallet.token-input.component-spec
quo.components.wallet.transaction-progress.component-spec
quo.components.wallet.transaction-summary.component-spec
+1
View File
@@ -43,6 +43,7 @@
:gnosis (js/require "../resources/images/networks/Gnosis.png")
:hermez (js/require "../resources/images/networks/Hermez.png")
:optimism (js/require "../resources/images/networks/Optimism.png")
:paraswap (js/require "../resources/images/networks/Paraswap.png")
:polygon (js/require "../resources/images/networks/Polygon.png")
:scroll (js/require "../resources/images/networks/Scroll.png")
:taiko (js/require "../resources/images/networks/Taiko.png")
+72 -15
View File
@@ -1,35 +1,92 @@
(ns react-native.wallet-connect
(:require
["@walletconnect/core" :refer [Core]]
["@walletconnect/utils" :refer
[buildApprovedNamespaces getSdkError parseUri]]
["@walletconnect/web3wallet" :refer [Web3Wallet]]))
["@walletconnect/core" :as wc-core]
["@walletconnect/utils" :as wc-utils]
["@walletconnect/web3wallet$default" :as Web3Wallet]
[cljs-bean.core :as bean]
[oops.core :as oops]))
(defn- wallet-connect-core
[project-id]
(Core. #js {:projectId project-id}))
(new ^js wc-core/Core (clj->js {:projectId project-id})))
(defn init
[project-id metadata]
(let [core (wallet-connect-core project-id)]
(Web3Wallet.init
(clj->js {:core core
:metadata metadata}))))
(oops/ocall Web3Wallet
"init"
(bean/->js {:core core
:metadata metadata}))))
(defn build-approved-namespaces
[proposal supported-namespaces]
(buildApprovedNamespaces
(clj->js {:proposal proposal
:supportedNamespaces supported-namespaces})))
(oops/ocall wc-utils
"buildApprovedNamespaces"
(bean/->js {:proposal proposal
:supportedNamespaces supported-namespaces})))
;; Get an error from this list:
;; https://github.com/WalletConnect/walletconnect-monorepo/blob/c6e9529418a0c81d4efcc6ac4e61f242a50b56c5/packages/utils/src/errors.ts
(defn get-sdk-error
[error-key]
(getSdkError error-key))
(oops/ocall wc-utils "getSdkError" error-key))
(defn parse-uri
[uri]
(-> uri
parseUri
(js->clj :keywordize-keys true)))
(-> (oops/ocall wc-utils "parseUri" uri)
(bean/->clj)))
(defn respond-session-request
[{:keys [web3-wallet topic id result error]}]
(oops/ocall web3-wallet
"respondSessionRequest"
(bean/->js {:topic topic
:response
(merge {:id id
:jsonrpc "2.0"}
(when result
{:result result})
(when error
{:error error}))})))
(defn reject-session
[{:keys [web3-wallet id reason]}]
(.rejectSession web3-wallet
(clj->js {:id id
:reason reason})))
(defn approve-session
[{:keys [web3-wallet id approved-namespaces]}]
(oops/ocall web3-wallet
"approveSession"
(bean/->js {:id id
:namespaces approved-namespaces})))
(defn disconnect-session
[{:keys [web3-wallet reason topic]}]
(oops/ocall web3-wallet
"disconnectSession"
(bean/->js {:topic topic
:reason reason})))
(defn get-active-sessions
[web3-wallet]
(oops/ocall web3-wallet "getActiveSessions"))
(defn core-pairing-pair
[web3-wallet url]
(oops/ocall web3-wallet
"core.pairing.pair"
(bean/->js {:uri url})))
(defn get-pairings
[web3-wallet]
(oops/ocall web3-wallet "core.pairing.getPairings"))
(defn register-handler
[{:keys [web3-wallet event handler]}]
(oops/ocall web3-wallet
"on"
event
#(-> (bean/->clj %)
handler)))
+5 -3
View File
@@ -63,8 +63,10 @@
(defn view
[{:keys [hide? insets]}
{:keys [content selected-item padding-bottom-override border-radius on-close shell?
gradient-cover? customization-color hide-handle? blur-radius]
:or {border-radius 12}}]
gradient-cover? customization-color hide-handle? blur-radius
hide-on-background-press?]
:or {border-radius 12
hide-on-background-press? true}}]
(let [theme (quo.theme/use-theme)
{window-height :height} (rn/get-window)
[sheet-height set-sheet-height] (rn/use-state 0)
@@ -119,7 +121,7 @@
:on-layout handle-layout-height}
;; backdrop
[rn/pressable
{:on-press #(rf/dispatch [:hide-bottom-sheet])
{:on-press #(when hide-on-background-press? (rf/dispatch [:hide-bottom-sheet]))
:style {:flex 1}}
[reanimated/view
{:style (reanimated/apply-animations-to-style
@@ -1,5 +1,6 @@
(ns status-im.common.floating-button-page.floating-container.style
(:require [react-native.safe-area :as safe-area]))
(:require [quo.foundations.colors :as colors]
[react-native.safe-area :as safe-area]))
(defn content-container
[blur? keyboard-shown?]
@@ -11,7 +12,12 @@
:padding-horizontal 20}
blur? (dissoc :padding-vertical :padding-horizontal))))
(def blur-inner-container
{:background-color :transparent ; required, otherwise blur-view will shrink
(defn blur-inner-container
[theme shell-overlay?]
{:background-color (colors/theme-colors colors/white-70-blur
(if shell-overlay?
colors/neutral-80-opa-80-blur
colors/neutral-95-opa-70-blur)
theme)
:padding-vertical 12
:padding-horizontal 20})
@@ -1,27 +1,25 @@
(ns status-im.common.floating-button-page.floating-container.view
(:require
[quo.core :as quo]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[status-im.common.floating-button-page.floating-container.style :as style]))
(defn- blur-container
[child]
[child shell-overlay?]
(let [theme (quo.theme/use-theme)]
[quo/blur
{:blur-amount 52
:blur-radius 20
:blur-type :transparent
:blur-overlay-color (colors/theme-colors colors/white-70-blur colors/neutral-95-opa-70-blur theme)}
[rn/view {:style style/blur-inner-container}
{:blur-amount 20
:blur-type :transparent
:overlay-color :transparent}
[rn/view {:style (style/blur-inner-container theme shell-overlay?)}
child]]))
(defn view
[{:keys [on-layout keyboard-shown? blur?]} child]
[{:keys [on-layout keyboard-shown? blur? shell-overlay?]} child]
[rn/view
{:style (style/content-container blur? keyboard-shown?)
:on-layout on-layout}
(if blur?
[blur-container child]
[blur-container child shell-overlay?]
child)])
@@ -58,7 +58,7 @@
(defn view
[{:keys [header footer customization-color footer-container-padding header-container-style
content-container-style gradient-cover? keyboard-should-persist-taps]
content-container-style gradient-cover? keyboard-should-persist-taps shell-overlay?]
:or {footer-container-padding (safe-area/get-top)}}
& children]
(reagent/with-let [scroll-view-ref (atom nil)
@@ -121,7 +121,8 @@
[floating-container/view
{:on-layout set-footer-container-height
:keyboard-shown? keyboard-shown?
:blur? show-background?}
:blur? show-background?
:shell-overlay? shell-overlay?}
footer]]]])
(finally
(remove-listeners))))
+39 -14
View File
@@ -144,6 +144,10 @@
(fn [_ [opts]]
{:keychain/save-password-and-auth-method opts}))
(defn- whisper-key-name
[address]
(str address "-whisper"))
;; NOTE: migrating the plaintext password in the keychain
;; with the hashed one. Added due to the sync onboarding
;; flow, where the password arrives already hashed.
@@ -151,17 +155,38 @@
:keychain/password-hash-migration
(fn [{:keys [key-uid callback]
:or {callback identity}}]
(-> (get-password-migration! key-uid identity)
(.then (fn [migrated?]
(if migrated?
(callback)
(-> (get-user-password! key-uid identity)
(.then security/hash-masked-password)
(.then #(save-user-password! key-uid %))
(.then #(save-password-migration! key-uid))
(.then callback)))))
(.catch (fn [err]
(log/error "Failed to migrate the keychain password"
{:error err
:key-uid key-uid
:event :keychain/password-hash-migration}))))))
(keychain/get-credentials
(whisper-key-name key-uid)
(fn [whisper-key-data]
(if whisper-key-data
(callback) ;; we don't need to migrate keycard password
(-> (get-password-migration! key-uid identity)
(.then (fn [migrated?]
(if migrated?
(callback)
(-> (get-user-password! key-uid identity)
(.then security/hash-masked-password)
(.then #(save-user-password! key-uid %))
(.then #(save-password-migration! key-uid))
(.then callback)))))
(.catch (fn [err]
(log/error "Failed to migrate the keychain password"
{:error err
:key-uid key-uid
:event :keychain/password-hash-migration})))))))))
(re-frame/reg-fx
:keychain/get-keycard-keys
(fn [[key-uid callback]]
(keychain/get-credentials
key-uid
(fn [encryption-key-data]
(if encryption-key-data
(keychain/get-credentials
(whisper-key-name key-uid)
(fn [whisper-key-data]
(if whisper-key-data
(callback [(oops/oget encryption-key-data "password")
(oops/oget whisper-key-data "password")])
(callback nil))))
(callback nil))))))
@@ -0,0 +1,14 @@
(ns status-im.common.metrics-confirmation-modal.style)
(def points-wrapper
{:margin-top 11
:margin-horizontal 20
:gap 21
:margin-bottom 15})
(def item-text
{:margin-top 10
:margin-left -4})
(def info-text
{:margin-top -5})
@@ -0,0 +1,79 @@
(ns status-im.common.metrics-confirmation-modal.view
(:require
[quo.core :as quo]
[react-native.core :as rn]
[status-im.common.metrics-confirmation-modal.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- dismiss-keyboard
[]
(rf/dispatch [:dismiss-keyboard]))
(defn- hide-bottom-sheet
[]
(rf/dispatch [:hide-bottom-sheet]))
(defn- toggle-metrics
[enabled?]
(rf/dispatch [:centralized-metrics/toggle-centralized-metrics enabled?]))
(def ^:private will-receive-points
[:t/ip-address
:t/universally-unique-identifiers-of-device
:t/logs-of-actions-withing-the-app])
(def ^:private not-receive-points
[:t/your-profile-information
:t/your-addresses
:t/information-you-input-and-send])
(defn- bullet-points
[{:keys [title points]}]
[rn/view
[quo/text {:weight :semi-bold}
title]
(for [label points]
^{:key label}
[quo/markdown-list
{:description (i18n/label label)
:blur? true
:container-style style/item-text}])])
(defn- on-share-usage
[]
(toggle-metrics true)
(hide-bottom-sheet))
(defn- on-do-not-share
[]
(toggle-metrics false)
(hide-bottom-sheet))
(defn view
[{:keys [settings?]}]
(rn/use-mount #(dismiss-keyboard))
[:<>
[quo/drawer-top
{:title (i18n/label :t/help-us-improve-status)
:description (i18n/label :t/collecting-usage-data)}]
[rn/view {:style style/points-wrapper}
[bullet-points
{:title (i18n/label :t/what-we-will-receive)
:points will-receive-points}]
[bullet-points
{:title (i18n/label :t/what-we-wont-receive)
:points not-receive-points}]
(when-not settings?
[quo/text
{:size :paragraph-2
:style style/info-text}
(i18n/label :t/sharing-usage-data-can-be-turned-off)])]
[quo/bottom-actions
{:actions :two-actions
:blur? true
:button-one-label (i18n/label :t/share-usage-data)
:button-one-props {:on-press on-share-usage}
:button-two-label (i18n/label (if settings? :t/do-not-share :t/not-now))
:button-two-props {:type :grey
:on-press on-do-not-share}}]])
+3 -1
View File
@@ -25,7 +25,9 @@
:invite-friends (js/require "../resources/images/ui2/invite-friends.png")
:transaction-progress (js/require "../resources/images/ui2/transaction-progress.png")
:welcome-illustration (js/require "../resources/images/ui2/welcome_illustration.png")
:notifications (js/require "../resources/images/ui2/notifications.png")})
:notifications (js/require "../resources/images/ui2/notifications.png")
:nfc-prompt (js/require "../resources/images/ui2/nfc-prompt.png")
:nfc-success (js/require "../resources/images/ui2/nfc-success.png")})
(def ui-themed
{:angry-man
@@ -10,11 +10,14 @@
(defn authorize
[{:keys [db]} [args]]
(let [key-uid (get-in db [:profile/profile :key-uid])]
(let [key-uid (get-in db [:profile/profile :key-uid])
keycard? (get-in db [:profile/profile :keycard-pairing])]
{:fx [[:effects.biometric/check-if-available
{:key-uid key-uid
:on-success #(rf/dispatch [:standard-auth/authorize-with-biometric args])
:on-fail #(rf/dispatch [:standard-auth/authorize-with-password args])}]]}))
:on-fail (if keycard?
#(rf/dispatch [:standard-auth/authorize-with-keycard args])
#(rf/dispatch [:standard-auth/authorize-with-password args]))}]]}))
(schema/=> authorize events-schema/?authorize)
(rf/reg-event-fx :standard-auth/authorize authorize)
@@ -45,8 +48,11 @@
(defn on-biometric-success
[{:keys [db]} [on-auth-success]]
(let [key-uid (get-in db [:profile/profile :key-uid])]
{:fx [[:keychain/get-user-password [key-uid on-auth-success]]
(let [key-uid (get-in db [:profile/profile :key-uid])
keycard? (get-in db [:profile/profile :keycard-pairing])]
{:fx [(if keycard?
[:keychain/get-keycard-keys [key-uid on-auth-success]]
[:keychain/get-user-password [key-uid on-auth-success]])
[:dispatch [:standard-auth/set-success true]]
[:dispatch [:standard-auth/reset-login-password]]]}))
+1 -1
View File
@@ -72,7 +72,7 @@
;; CONFIG VALUES
(def log-level (string/upper-case (get-config :LOG_LEVEL "")))
(def fleet (get-config :FLEET "waku.sandbox"))
(def fleet (get-config :FLEET ""))
(def apn-topic (get-config :APN_TOPIC "im.status.ethereum"))
(def default-network (get-config :DEFAULT_NETWORK "goerli_rpc"))
(def max-installations 2)
+21 -1
View File
@@ -45,6 +45,11 @@
(def ^:const activity-center-membership-status-accepted 2)
(def ^:const activity-center-membership-status-declined 3)
;; Choose the maximum number of notifications that *usually/safely* fit on
;; most screens, so that the UI doesn't have to needlessly render
;; notifications.
(def ^:const notifications-per-page 7)
(def ^:const mute-for-15-mins-type 1)
(def ^:const mute-for-1-hour-type 2)
(def ^:const mute-for-8-hours-type 3)
@@ -126,6 +131,7 @@
(def ^:const profile-pictures-visibility-none 3)
(def ^:const min-password-length 6)
(def ^:const pincode-length 6)
(def ^:const new-password-min-length 10)
(def ^:const max-group-chat-participants 20)
(def ^:const max-group-chat-name-length 24)
@@ -275,16 +281,21 @@
#{wallet-connect-personal-sign-method
wallet-connect-eth-sign-method
wallet-connect-eth-send-transaction-method
wallet-connect-eth-sign-transaction-method
;; NOTE: disabled, as we have no clear use cases for it and other wallets don't support it
;; wallet-connect-eth-sign-transaction-method
wallet-connect-eth-sign-typed-method
wallet-connect-eth-sign-typed-v4-method})
(def ^:const wallet-connect-supported-events #{"accountsChanged" "chainChanged"})
(def ^:const wallet-connect-session-proposal-event "session_proposal")
(def ^:const wallet-connect-session-request-event "session_request")
(def ^:const wallet-connect-session-delete-event "session_delete")
(def ^:const wallet-connect-user-rejected-error-key "USER_REJECTED")
(def ^:const wallet-connect-user-disconnected-reason-key "USER_DISCONNECTED")
(def ^:const transaction-pending-type-wallet-connect-transfer "WalletConnectTransfer")
(def ^:const token-sort-priority {"SNT" 1 "STT" 1 "ETH" 2 "DAI" 3})
(def ^:const dapp-permission-contact-code "contact-code")
(def ^:const dapp-permission-web3 "web3")
(def ^:const dapp-permission-qr-code "qr-code")
@@ -505,6 +516,9 @@
(def ^:const optimism-full-name "Optimism")
(def ^:const arbitrum-full-name "Arbitrum")
(def ^:const sepolia-full-name "Sepolia")
(def ^:const goerli-full-name "Goerli")
(def ^:const mainnet-network-name :mainnet)
(def ^:const ethereum-network-name :ethereum)
(def ^:const optimism-network-name :optimism)
@@ -565,3 +579,9 @@
(def ^:const default-slippage 0.5)
(def ^:const max-recommended-slippage 5)
(def ^:const max-slippage-decimal-places 2)
(def ^:const swap-default-provider
{:name :paraswap
:full-name "Paraswap"
:color :blue
:contract-address "0xdef171fe48cf0115b1d80b88dc8eab59176fee57"
:terms-and-conditions-url "https://files.paraswap.io/tos_v4.pdf"})
@@ -7,10 +7,17 @@
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
(def ^:const user-confirmed-key :centralized-metrics/user-confirmed?)
(def ^:const enabled-key :centralized-metrics/enabled?)
(defn show-confirmation-modal?
[db]
(not (user-confirmed-key db)))
(defn push-event?
[db]
(or (not (:centralized-metrics/user-confirmed? db))
(:centralized-metrics/enabled? db)))
(or (not (user-confirmed-key db))
(enabled-key db)))
(defn centralized-metrics-interceptor
[context]
@@ -29,5 +36,16 @@
(fn [{:keys [db]} [enabled?]]
{:fx [[:effects.centralized-metrics/toggle-metrics enabled?]]
:db (assoc db
:centralized-metrics/user-confirmed? true
:centralized-metrics/enabled? enabled?)}))
user-confirmed-key
true
enabled-key
enabled?)}))
(rf/reg-event-fx :centralized-metrics/check-modal
(fn [{:keys [db]} [modal-view]]
(when (show-confirmation-modal? db)
{:fx [[:dispatch
[:show-bottom-sheet
{:content (fn [] [modal-view])
:shell? true}]]]})))
@@ -6,6 +6,11 @@
[status-im.contexts.centralized-metrics.tracking :as tracking]
[test-helpers.unit :as h]))
(deftest show-confirmation-modal-test
(testing "returns true if the user confirmed"
(is (false? (events/show-confirmation-modal? {events/user-confirmed-key true})))
(is (true? (events/show-confirmation-modal? {})))))
(deftest push-event-test
(testing "returns correct boolean value"
(is (true? (events/push-event? {:centralized-metrics/user-confirmed? false})))
@@ -27,7 +27,7 @@
:on-error #(p-reject (str "failed to sign data\n" %))}))))
(defn- edit-shared-addresses-for-community
[community-id signatures addresses-to-reveal airdrop-address]
[community-id signatures addresses-to-reveal airdrop-address _share-future-addresses?]
(promesa/create
(fn [p-resolve p-reject]
(rpc/call
@@ -41,15 +41,16 @@
:on-error p-reject}))))
(defn- request-to-join
[community-id signatures addresses-to-reveal airdrop-address]
[community-id signatures addresses-to-reveal airdrop-address share-future-addresses?]
(promesa/create
(fn [p-resolve p-reject]
(rpc/call
{:method :wakuext_requestToJoinCommunity
:params [{:communityId community-id
:signatures signatures
:addressesToReveal addresses-to-reveal
:airdropAddress airdrop-address}]
:params [{:communityId community-id
:signatures signatures
:addressesToReveal addresses-to-reveal
:airdropAddress airdrop-address
:shareFutureAddresses share-future-addresses?}]
:js-response true
:on-success p-resolve
:on-error p-reject}))))
@@ -64,15 +65,18 @@
(defn- sign-and-call-endpoint
[{:keys [community-id password pub-key
addresses-to-reveal airdrop-address
addresses-to-reveal airdrop-address share-future-addresses?
on-success on-error
callback]}]
(-> (promesa/let [sign-params (generate-requests-for-signing pub-key community-id addresses-to-reveal)
(-> (promesa/let [sign-params (generate-requests-for-signing pub-key
community-id
addresses-to-reveal)
signatures (sign-data sign-params password)
result (callback community-id
signatures
addresses-to-reveal
airdrop-address)]
airdrop-address
share-future-addresses?)]
(run-callback-or-event on-success result))
(promesa/catch #(run-callback-or-event on-error %))))
@@ -87,6 +91,7 @@
[:or [:set string?]
[:sequential string?]]]
[:airdrop-address string?]
[:share-future-addresses? boolean?]
[:on-success [:or fn? :schema.re-frame/event]]
[:on-error [:or fn? :schema.re-frame/event]]
[:callback fn?]]]
@@ -17,13 +17,12 @@
(rf/reg-event-fx :communities/initialize-permission-addresses initialize-permission-addresses)
(defn do-init-permission-addresses
[{:keys [db]} [community-id revealed-accounts]]
(let [wallet-accounts (utils/sorted-non-watch-only-accounts db)
[{:keys [db]} [community-id revealed-accounts share-future-addresses?]]
(let [wallet-accounts (utils/sorted-operable-non-watch-only-accounts db)
addresses-to-reveal (if (seq revealed-accounts)
(set (keys revealed-accounts))
;; Reveal all addresses as fallback.
(set (map :address wallet-accounts)))
;; When there are no revealed addresses, such as when joining a
;; community, use first address for airdrops.
airdrop-address (or (->> revealed-accounts
@@ -35,10 +34,7 @@
first
:address))]
{:db (-> db
;; Set to false by default while we don't persist the user's choice
;; in status-go, otherwise whenever the view is mounted, the choice
;; of selected addresses won't be respected.
(assoc-in [:communities/selected-share-all-addresses community-id] false)
(assoc-in [:communities/selected-share-all-addresses community-id] share-future-addresses?)
(assoc-in [:communities/all-addresses-to-reveal community-id] addresses-to-reveal)
(assoc-in [:communities/all-airdrop-addresses community-id] airdrop-address))
:fx [[:dispatch
@@ -60,36 +56,44 @@
is selecting an airdrop address, we must submit to status-go the current
choice of addresses to share, and vice-versa. If we omit addresses to share,
status-go will default to all available."
[{:keys [db]} [{:keys [community-id password on-success addresses airdrop-address]}]]
(let [pub-key (get-in db [:profile/profile :public-key])
wallet-accounts (utils/sorted-non-watch-only-accounts db)
addresses-to-reveal (if (seq addresses)
(set addresses)
(get-in db [:communities/all-addresses-to-reveal community-id]))
new-airdrop-address (if (contains? addresses-to-reveal airdrop-address)
airdrop-address
(->> wallet-accounts
(filter #(contains? addresses-to-reveal (:address %)))
first
:address))]
[{:keys [db]}
[{:keys [community-id password on-success addresses airdrop-address share-future-addresses?]}]]
(let [pub-key (get-in db [:profile/profile :public-key])
wallet-accounts (utils/sorted-operable-non-watch-only-accounts db)
addresses-to-reveal (if (seq addresses)
(set addresses)
(get-in db [:communities/all-addresses-to-reveal community-id]))
new-airdrop-address (if (contains? addresses-to-reveal airdrop-address)
airdrop-address
(->> wallet-accounts
(filter #(contains? addresses-to-reveal (:address %)))
first
:address))
share-future-addresses? (if (nil? share-future-addresses?)
(get-in db [:communities/selected-share-all-addresses community-id])
share-future-addresses?)]
{:fx [[:effects.community/edit-shared-addresses
{:community-id community-id
:password password
:pub-key pub-key
:addresses-to-reveal addresses-to-reveal
:airdrop-address new-airdrop-address
:on-success (fn []
(when (fn? on-success)
(on-success addresses-to-reveal new-airdrop-address))
(rf/dispatch [:communities/edit-shared-addresses-success
community-id addresses-to-reveal airdrop-address]))
:on-error [:communities/edit-shared-addresses-failure community-id]}]]}))
{:community-id community-id
:password password
:pub-key pub-key
:addresses-to-reveal addresses-to-reveal
:share-future-addresses? share-future-addresses?
:airdrop-address new-airdrop-address
:on-success (fn []
(when (fn? on-success)
(on-success addresses-to-reveal
new-airdrop-address
share-future-addresses?))
(rf/dispatch [:communities/edit-shared-addresses-success
community-id addresses-to-reveal airdrop-address]))
:on-error [:communities/edit-shared-addresses-failure community-id]}]]}))
(rf/reg-event-fx :communities/edit-shared-addresses edit-shared-addresses)
(rf/reg-event-fx :communities/edit-shared-addresses-success
(fn [_ [community-id addresses-to-reveal airdrop-address]]
(fn [_ [community-id addresses-to-reveal airdrop-address share-future-addresses?]]
{:fx [[:dispatch [:communities/set-airdrop-address community-id airdrop-address]]
[:dispatch [:communities/set-share-all-addresses community-id share-future-addresses?]]
[:dispatch [:communities/set-addresses-to-reveal community-id addresses-to-reveal]]]}))
(rf/reg-event-fx :communities/edit-shared-addresses-failure
@@ -9,17 +9,25 @@
(def wallet-accounts
{"0xA" {:address "0xA"
:watch-only? true
:operable? true
:position 2
:color :red
:emoji "🦇"}
"0xB" {:address "0xB"
:position 0
:color :blue
:emoji "🐈"}
"0xC" {:address "0xC"
:position 1
:color :orange
:emoji "🛏️"}})
"0xB" {:address "0xB"
:operable? true
:position 0
:color :blue
:emoji "🐈"}
"0xC" {:address "0xC"
:operable? true
:position 1
:color :orange
:emoji "🛏️"}
"0xD" {:address "0xD"
:operable? false
:position 3
:color :flamingo
:emoji "🦩"}})
(def permissioned-accounts
[{:address "0xB"
@@ -62,7 +70,7 @@
(is
(match?
{:db (-> (:db cofx)
(assoc-in [:communities/selected-share-all-addresses community-id] false)
(assoc-in [:communities/selected-share-all-addresses community-id] true)
(assoc-in [:communities/all-addresses-to-reveal community-id] addresses-to-reveal)
(assoc-in [:communities/all-airdrop-addresses community-id] airdrop-address))
:fx [[:dispatch
@@ -74,7 +82,7 @@
[:dispatch
[:communities/check-permissions-to-join-during-selection community-id
addresses-to-reveal]]]}
(sut/do-init-permission-addresses cofx [community-id revealed-accounts])))))
(sut/do-init-permission-addresses cofx [community-id revealed-accounts true])))))
;; Expect to mark all addresses to be revealed and first one to receive
;; airdrops when no addresses were previously revealed.
@@ -85,7 +93,7 @@
(is
(match?
{:db (-> (:db cofx)
(assoc-in [:communities/selected-share-all-addresses community-id] false)
(assoc-in [:communities/selected-share-all-addresses community-id] true)
(assoc-in [:communities/all-addresses-to-reveal community-id] addresses-to-reveal))
:fx [[:dispatch
[:communities/check-permissions-to-join-community
@@ -93,7 +101,7 @@
[:dispatch
[:communities/check-permissions-to-join-during-selection community-id
addresses-to-reveal]]]}
(sut/do-init-permission-addresses cofx [community-id revealed-accounts]))))))
(sut/do-init-permission-addresses cofx [community-id revealed-accounts true]))))))
(deftest edit-shared-addresses-test
(testing
@@ -101,6 +109,7 @@
fallback to all wallet addresses"
(let [pub-key "abcdef"
revealed-addresses #{"0xB" "0xC"}
share-future-addresses? true
cofx {:db {:profile/profile {:public-key pub-key}
:communities/all-addresses-to-reveal {community-id revealed-addresses}}}
airdrop-address "0xB"
@@ -108,22 +117,24 @@
actual
(sut/edit-shared-addresses
cofx
[{:community-id community-id
:password password
:airdrop-address airdrop-address
:on-success (fn [new-addresses-to-reveal]
(is (match? revealed-addresses new-addresses-to-reveal)))}])
[{:community-id community-id
:password password
:airdrop-address airdrop-address
:share-future-addresses? share-future-addresses?
:on-success (fn [new-addresses-to-reveal]
(is (match? revealed-addresses new-addresses-to-reveal)))}])
on-success-wrapper (-> actual :fx first second :on-success)]
(is (match?
{:fx [[:effects.community/edit-shared-addresses
{:community-id community-id
:password password
:pub-key pub-key
:addresses-to-reveal revealed-addresses
:airdrop-address airdrop-address
:on-success fn?
:on-error [:communities/edit-shared-addresses-failure community-id]}]]}
{:community-id community-id
:password password
:pub-key pub-key
:addresses-to-reveal revealed-addresses
:share-future-addresses? share-future-addresses?
:airdrop-address airdrop-address
:on-success fn?
:on-error [:communities/edit-shared-addresses-failure community-id]}]]}
actual))
(on-success-wrapper)))
@@ -22,7 +22,7 @@
airdrop-account (rf/sub [:communities/airdrop-account id])
revealed-accounts (rf/sub [:communities/accounts-to-reveal id])
revealed-accounts-count (count revealed-accounts)
wallet-accounts-count (count (rf/sub [:wallet/accounts-without-watched-accounts]))
wallet-accounts-count (count (rf/sub [:wallet/operable-accounts-without-watched-accounts]))
addresses-shared-text (if (= revealed-accounts-count wallet-accounts-count)
(i18n/label :t/all-addresses)
(i18n/label-pluralize
@@ -120,7 +120,7 @@
(defn set-permissioned-accounts
[{:keys [db]} [community-id addresses-to-reveal]]
(let [addresses-to-reveal (set addresses-to-reveal)
wallet-accounts (utils/sorted-non-watch-only-accounts db)
wallet-accounts (utils/sorted-operable-non-watch-only-accounts db)
current-airdrop-address (get-in db [:communities/all-airdrop-addresses community-id])
new-airdrop-address (if (contains? addresses-to-reveal current-airdrop-address)
current-airdrop-address
@@ -142,7 +142,7 @@
[{:keys [db]} [community-id new-value]]
(let [current-addresses (get-in db [:communities/all-addresses-to-reveal community-id])
addresses-to-reveal (if new-value
(->> (utils/sorted-non-watch-only-accounts db)
(->> (utils/sorted-operable-non-watch-only-accounts db)
(map :address)
set)
current-addresses)]
@@ -33,9 +33,15 @@
(let [cofx
{:db {:communities/all-addresses-to-reveal {community-id #{"0xA" "0xB" "0xC"}}
:communities/all-airdrop-addresses {community-id "0xB"}
:wallet {:accounts {"0xB" {:address "0xB" :position 0}
"0xA" {:address "0xA" :position 1}
"0xC" {:address "0xC" :position 2}}}}}
:wallet {:accounts {"0xB" {:address "0xB"
:operable? true
:position 0}
"0xA" {:address "0xA"
:operable? true
:position 1}
"0xC" {:address "0xC"
:operable? true
:position 2}}}}}
addresses-to-reveal ["0xA" "0xC"]]
(is (match?
{:db {:communities/all-addresses-to-reveal
@@ -52,9 +58,9 @@
(testing "sets flag from false -> true will mark all addresses to be revealed"
(let [cofx {:db
{:wallet
{:accounts {"0xB" {:address "0xB" :position 0}
"0xA" {:address "0xA" :position 1}
"0xC" {:address "0xC" :position 2}}}
{:accounts {"0xB" {:address "0xB" :operable? true :position 0}
"0xA" {:address "0xA" :operable? true :position 1}
"0xC" {:address "0xC" :operable? true :position 2}}}
:communities/all-addresses-to-reveal {community-id #{"0xA"}}
:communities/selected-share-all-addresses {community-id false}}}
addresses-to-reveal #{"0xA" "0xB" "0xC"}]
@@ -203,7 +203,9 @@
:addresses-for-permissions])
(rf/dispatch [:hide-bottom-sheet]))}]))}])
(rf/dispatch [:communities/set-share-all-addresses id flag-share-all-addresses]))
(rf/dispatch [:communities/set-addresses-to-reveal id addresses-to-reveal])))
(do
(rf/dispatch [:communities/set-share-all-addresses id flag-share-all-addresses])
(rf/dispatch [:communities/set-addresses-to-reveal id addresses-to-reveal]))))
highest-role (rf/sub [:communities/highest-role-for-selection id])
[unmodified-role _] (rn/use-state highest-role)]
@@ -260,7 +262,8 @@
can-edit-addresses? (rf/sub [:communities/can-edit-shared-addresses? id])
wallet-accounts (rf/sub [:wallet/accounts-without-watched-accounts])
wallet-accounts (rf/sub [:wallet/operable-accounts])
joined (rf/sub [:communities/community-joined id])
unmodified-addresses-to-reveal (rf/sub [:communities/addresses-to-reveal id])
[addresses-to-reveal set-addresses-to-reveal] (rn/use-state unmodified-addresses-to-reveal)
@@ -285,7 +288,6 @@
(set-flag-share-all-addresses new-value)
(when new-value
(set-addresses-to-reveal (set (map :address wallet-accounts))))))]
(rn/use-mount
(fn []
(when-not flag-share-all-addresses
@@ -310,6 +312,7 @@
flag-share-all-addresses]
:header [quo/page-setting
{:checked? flag-share-all-addresses
:disabled? joined
:customization-color color
:on-change toggle-flag-share-all-addresses
:setting-text (i18n/label
@@ -47,12 +47,11 @@
:label (i18n/label :t/view-token-gating)}))
(defn- action-mark-as-read
[]
(when config/show-not-implemented-features?
{:icon :i/mark-as-read
:accessibility-label :chat-mark-as-read
:on-press not-implemented/alert
:label (i18n/label :t/mark-as-read)}))
[chat-id]
{:icon :i/mark-as-read
:accessibility-label :chat-mark-as-read
:on-press #(hide-sheet-and-dispatch [:chat.ui/mark-all-read-pressed chat-id])
:label (i18n/label :t/mark-as-read)})
(defn- action-toggle-muted
[id muted? muted-till chat-type]
@@ -130,7 +129,7 @@
(and (not inside-chat?) (not locked?))
[quo/action-drawer
[[(when-not hide-view-members? (action-view-members-and-details community-id chat-id))
(action-mark-as-read)
(action-mark-as-read chat-id)
(action-toggle-muted chat-id muted muted-till chat-type)
(action-notification-settings)
(action-pinned-messages chat-id)
@@ -143,7 +142,7 @@
[[(action-view-members-and-details community-id chat-id)
(when token-gated?
(action-token-requirements))
(action-mark-as-read)
(action-mark-as-read chat-id)
(action-toggle-muted chat-id muted muted-till chat-type)
(action-notification-settings)
(when config/fetch-messages-enabled?
+16 -20
View File
@@ -16,6 +16,7 @@
[status-im.navigation.events :as navigation]
[status-im.navigation.transitions :as transitions]
[taoensso.timbre :as log]
[utils.collection :as collection-utils]
[utils.re-frame :as rf]))
(defn handle-community
@@ -154,7 +155,7 @@
(defn update-previous-permission-addresses
[{:keys [db]} [community-id]]
(when community-id
(let [accounts (utils/sorted-non-watch-only-accounts db)
(let [accounts (utils/sorted-operable-non-watch-only-accounts db)
selected-permission-addresses (get-in db
[:communities community-id
:selected-permission-addresses])
@@ -198,7 +199,7 @@
[{:keys [db]} [community-id]]
(let [share-all-addresses? (get-in db [:communities community-id :share-all-addresses?])
next-share-all-addresses? (not share-all-addresses?)
accounts (utils/sorted-non-watch-only-accounts db)
accounts (utils/sorted-operable-non-watch-only-accounts db)
addresses (set (map :address accounts))]
{:db (update-in db
[:communities community-id]
@@ -372,15 +373,14 @@
(when (and community joined (not fetching-revealed-accounts))
{:db (assoc-in db [:communities community-id :fetching-revealed-accounts] true)
:json-rpc/call
[{:method "wakuext_getRevealedAccounts"
:params [community-id (get-in db [:profile/profile :public-key])]
:js-response true
:on-success [:communities/get-revealed-accounts-success community-id on-success]
:on-error (fn [err]
(log/error {:message "failed to fetch revealed accounts"
:community-id community-id
:err err})
(rf/dispatch [:communities/get-revealed-accounts-failed community-id]))}]})))
[{:method "wakuext_latestRequestToJoinForCommunity"
:params [community-id]
:on-success [:communities/get-revealed-accounts-success community-id on-success]
:on-error (fn [err]
(log/error {:message "failed to fetch revealed accounts"
:community-id community-id
:err err})
(rf/dispatch [:communities/get-revealed-accounts-failed community-id]))}]})))
(rf/reg-event-fx :communities/get-revealed-accounts get-revealed-accounts)
@@ -399,22 +399,18 @@
[:json-rpc/call :schema.common/rpc-call]]]])
(rf/reg-event-fx :communities/get-revealed-accounts-success
(fn [{:keys [db]} [community-id on-success revealed-accounts-js]]
(fn [{:keys [db]} [community-id on-success request-to-join]]
(when-let [community (get-in db [:communities community-id])]
(let [revealed-accounts
(reduce
(fn [acc {:keys [address] :as revealed-account}]
(assoc acc address revealed-account))
{}
(data-store.communities/<-revealed-accounts-rpc revealed-accounts-js))
(let [revealed-accounts (collection-utils/index-by :address (:revealedAccounts request-to-join))
share-future-addresses? (:shareFutureAddresses request-to-join)
community-with-revealed-accounts
(-> community
(assoc :revealed-accounts revealed-accounts)
(assoc :share-future-addresses? share-future-addresses?)
(dissoc :fetching-revealed-accounts))]
{:db (assoc-in db [:communities community-id] community-with-revealed-accounts)
:fx [(when (vector? on-success)
[:dispatch (conj on-success revealed-accounts)])]}))))
[:dispatch (conj on-success revealed-accounts share-future-addresses?)])]}))))
(rf/reg-event-fx :communities/get-revealed-accounts-failed
(fn [{:keys [db]} [community-id]]
@@ -146,8 +146,8 @@
effects (events/get-revealed-accounts {:db db} [community-id])]
(is (match? (assoc-in db [:communities community-id :fetching-revealed-accounts] true)
(:db effects)))
(is (match? {:method "wakuext_getRevealedAccounts"
:params [community-id "profile-public-key"]}
(is (match? {:method "wakuext_latestRequestToJoinForCommunity"
:params [community-id]}
(-> effects :json-rpc/call first (select-keys [:method :params]))))))))
(deftest handle-community-test
@@ -51,7 +51,7 @@
(rf/reg-event-fx :communities/check-permissions-to-join-community-with-all-addresses
(fn [{:keys [db]} [community-id]]
(let [accounts (utils/sorted-non-watch-only-accounts db)
(let [accounts (utils/sorted-operable-non-watch-only-accounts db)
addresses (set (map :address accounts))]
{:db (assoc-in db [:communities/permissions-check community-id :checking?] true)
:json-rpc/call [{:method "wakuext_checkPermissionsToJoinCommunity"
@@ -117,13 +117,14 @@
{:community community-name})}]]]})))
(defn request-to-join-with-signatures
[_ [community-id addresses-to-reveal signatures]]
[_ [community-id addresses-to-reveal signatures share-future-addresses?]]
{:fx [[:json-rpc/call
[{:method "wakuext_requestToJoinCommunity"
:params [{:communityId community-id
:signatures signatures
:addressesToReveal addresses-to-reveal
:airdropAddress (first addresses-to-reveal)}]
:params [{:communityId community-id
:signatures signatures
:addressesToReveal addresses-to-reveal
:shareFutureAddresses share-future-addresses?
:airdropAddress (first addresses-to-reveal)}]
:js-response true
:on-success [:communities/requested-to-join]
:on-error [:communities/requested-to-join-error community-id]}]]]})
@@ -153,16 +154,18 @@
(defn request-to-join-with-addresses
[{:keys [db]}
[{:keys [community-id password]}]]
(let [pub-key (get-in db [:profile/profile :public-key])
addresses-to-reveal (get-in db [:communities/all-addresses-to-reveal community-id])
airdrop-address (get-in db [:communities/all-airdrop-addresses community-id])]
(let [pub-key (get-in db [:profile/profile :public-key])
addresses-to-reveal (get-in db [:communities/all-addresses-to-reveal community-id])
share-future-addresses? (get-in db [:communities/selected-share-all-addresses community-id])
airdrop-address (get-in db [:communities/all-airdrop-addresses community-id])]
{:fx [[:effects.community/request-to-join
{:community-id community-id
:password password
:pub-key pub-key
:addresses-to-reveal addresses-to-reveal
:airdrop-address airdrop-address
:on-success [:communities/requested-to-join]
:on-error [:communities/requested-to-join-error community-id]}]]}))
{:community-id community-id
:password password
:pub-key pub-key
:addresses-to-reveal addresses-to-reveal
:airdrop-address airdrop-address
:share-future-addresses? share-future-addresses?
:on-success [:communities/requested-to-join]
:on-error [:communities/requested-to-join-error community-id]}]]}))
(rf/reg-event-fx :communities/request-to-join-with-addresses request-to-join-with-addresses)
@@ -37,19 +37,22 @@
(sut/sign-data cofx [community-id password sign-params])))))
(deftest request-to-join-with-signatures-test
(let [cofx {:db {}}
addresses-to-reveal [account-pub-key "0x2"]
signatures ["11111" "222222"]
expected {:fx [[:json-rpc/call
[{:method "wakuext_requestToJoinCommunity"
:params [{:communityId community-id
:signatures signatures
:addressesToReveal addresses-to-reveal
:airdropAddress "0x1"}]
:js-response true
:on-success [:communities/requested-to-join]
:on-error [:communities/requested-to-join-error
community-id]}]]]}]
(let [cofx {:db {}}
addresses-to-reveal [account-pub-key "0x2"]
share-future-addresses? true
signatures ["11111" "222222"]
expected {:fx [[:json-rpc/call
[{:method "wakuext_requestToJoinCommunity"
:params [{:communityId community-id
:signatures signatures
:addressesToReveal addresses-to-reveal
:shareFutureAddresses share-future-addresses?
:airdropAddress "0x1"}]
:js-response true
:on-success [:communities/requested-to-join]
:on-error [:communities/requested-to-join-error
community-id]}]]]}]
(is (match? expected
(sut/request-to-join-with-signatures cofx
[community-id addresses-to-reveal signatures])))))
[community-id addresses-to-reveal signatures
share-future-addresses?])))))
@@ -12,9 +12,10 @@
constants/community-token-permission-become-member :t/member
fallback-to)))
(defn sorted-non-watch-only-accounts
(defn sorted-operable-non-watch-only-accounts
[db]
(->> (get-in db [:wallet :accounts])
(vals)
(remove :watch-only?)
(filter :operable?)
(sort-by :position)))
+111
View File
@@ -0,0 +1,111 @@
(ns status-im.contexts.keycard.effects
(:require [keycard.keycard :as keycard]
[native-module.core :as native-module]
[react-native.async-storage :as async-storage]
[react-native.platform :as platform]
[status-im.contexts.profile.config :as profile.config]
[taoensso.timbre :as log]
[utils.re-frame :as rf]
[utils.transforms :as transforms]))
(defonce ^:private active-listeners (atom []))
(defn register-card-events
[]
(doseq [listener @active-listeners]
(keycard/remove-event-listener listener))
(reset! active-listeners
[(keycard/on-card-connected #(rf/dispatch [:keycard/on-card-connected]))
(keycard/on-card-disconnected #(rf/dispatch [:keycard/on-card-disconnected]))
(when platform/ios?
(keycard/on-nfc-user-cancelled #(rf/dispatch [:keycard.ios/on-nfc-user-cancelled])))
(when platform/ios?
(keycard/on-nfc-timeout #(rf/dispatch [:keycard.ios/on-nfc-timeout])))
(keycard/on-nfc-enabled #(rf/dispatch [:keycard/on-check-nfc-enabled-success true]))
(keycard/on-nfc-disabled #(rf/dispatch [:keycard/on-check-nfc-enabled-success false]))]))
(rf/reg-fx :effects.keycard/register-card-events register-card-events)
(defn check-nfc-enabled
[]
(log/debug "[keycard] check-nfc-enabled")
(keycard/check-nfc-enabled
{:on-success
(fn [response]
(log/debug "[keycard response] check-nfc-enabled")
(rf/dispatch [:keycard/on-check-nfc-enabled-success response]))}))
(rf/reg-fx :effects.keycard/check-nfc-enabled check-nfc-enabled)
(rf/reg-fx
:effects.keycard.ios/start-nfc
(fn [args]
(log/debug "fx start-nfc")
(keycard/start-nfc args)))
(rf/reg-fx
:effects.keycard.ios/stop-nfc
(fn [args]
(log/debug "fx stop-nfc")
(keycard/stop-nfc args)))
(defn- error-object->map
[^js object]
{:code (.-code object)
:error (.-message object)})
(defn get-application-info
[{:keys [on-success on-failure] :as args}]
(log/debug "[keycard] get-application-info")
(keycard/get-application-info
(assoc
args
:on-success
(fn [response]
(log/debug "[keycard response succ] get-application-info")
(when on-success
(on-success response)))
:on-failure
(fn [response]
(log/error "[keycard response fail] get-application-info")
(when on-failure
(on-failure (error-object->map response)))))))
(rf/reg-fx :effects.keycard/get-application-info get-application-info)
(defn get-keys
[{:keys [on-success on-failure] :as args}]
(log/debug "[keycard] get-keys")
(keycard/get-keys
(assoc
args
:on-success
(fn [response]
(log/debug "[keycard response succ] get-keys")
(when on-success
(on-success (transforms/js->clj response))))
:on-failure
(fn [response]
(log/warn "[keycard response fail] get-keys"
(error-object->map response))
(when on-failure
(on-failure (error-object->map response)))))))
(rf/reg-fx :effects.keycard/get-keys get-keys)
(defn login
[{:keys [key-uid password whisper-private-key]}]
(native-module/login-account
(assoc (profile.config/login)
:keyUid key-uid
:password password
:keycardWhisperPrivateKey whisper-private-key)))
(rf/reg-fx :effects.keycard/login-with-keycard login)
(defn retrieve-pairings
[]
(async-storage/get-item
"status-keycard-pairings"
#(rf/dispatch [:keycard/on-retrieve-pairings-success %])))
(rf/reg-fx :effects.keycard/retrieve-pairings retrieve-pairings)
(defn set-pairing-to-keycard
[pairings]
(keycard/set-pairings pairings))
(rf/reg-fx :effects.keycard/set-pairing-to-keycard set-pairing-to-keycard)
@@ -0,0 +1,57 @@
(ns status-im.contexts.keycard.events
(:require [re-frame.core :as rf]
status-im.contexts.keycard.login.events
status-im.contexts.keycard.pin.events
status-im.contexts.keycard.sheet.events
[taoensso.timbre :as log]))
(rf/reg-event-fx :keycard/on-check-nfc-enabled-success
(fn [{:keys [db]} [nfc-enabled?]]
{:db (assoc-in db [:keycard :nfc-enabled?] nfc-enabled?)}))
(rf/reg-event-fx :keycard.ios/on-nfc-user-cancelled
(fn [{:keys [db]}]
(log/debug "[keycard] nfc user cancelled")
{:db (assoc-in db [:keycard :pin :status] nil)
:fx [(when-let [on-nfc-cancelled-event-vector (get-in db [:keycard :on-nfc-cancelled-event-vector])]
[:dispatch on-nfc-cancelled-event-vector])]}))
(rf/reg-event-fx :keycard/on-card-connected
(fn [{:keys [db]} _]
(log/debug "[keycard] card globally connected")
{:db (assoc-in db [:keycard :card-connected?] true)
:fx [(when-let [event (get-in db [:keycard :on-card-connected-event-vector])]
[:dispatch event])]}))
(rf/reg-event-fx :keycard/on-card-disconnected
(fn [{:keys [db]} _]
(log/debug "[keycard] card disconnected")
{:db (assoc-in db [:keycard :card-connected?] false)
:fx [(when-let [event (get-in db [:keycard :on-card-disconnected-event-vector])]
[:dispatch event])]}))
(rf/reg-event-fx :keycard.ios/start-nfc
(fn [_]
{:effects.keycard.ios/start-nfc nil}))
(rf/reg-event-fx :keycard.ios/on-nfc-timeout
(fn [{:keys [db]} _]
(log/debug "[keycard] nfc timeout")
{:db (assoc-in db [:keycard :card-connected?] false)
:fx [[:dispatch-later [{:ms 500 :dispatch [:keycard.ios/start-nfc]}]]]}))
(rf/reg-event-fx :keycard/get-application-info
(fn [_ [{:keys [on-success on-failure]}]]
(log/debug "[keycard] get-application-info")
{:effects.keycard/get-application-info {:on-success on-success
:on-failure on-failure}}))
(rf/reg-event-fx :keycard/on-retrieve-pairings-success
(fn [{:keys [db]} [pairings]]
{:db (assoc-in db [:keycard :pairings] pairings)
:fx [[:effects.keycard/set-pairing-to-keycard pairings]]}))
(rf/reg-event-fx :keycard.ios/on-start-nfc-success
(fn [{:keys [db]} [{:keys [on-cancel-event-vector]}]]
(log/debug "[keycard] nfc started success")
{:db (assoc-in db [:keycard :on-nfc-cancelled-event-vector] on-cancel-event-vector)}))
@@ -0,0 +1,87 @@
(ns status-im.contexts.keycard.login.events
(:require [status-im.contexts.keycard.utils :as keycard.utils]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
(rf/reg-event-fx :keycard.login/on-get-keys-error
(fn [{:keys [db]} [error]]
(log/debug "[keycard] get keys error: " error)
(let [tag-was-lost? (keycard.utils/tag-lost? (:error error))
pin-retries-count (keycard.utils/pin-retries (:error error))]
(if tag-was-lost?
{:db (assoc-in db [:keycard :pin :status] nil)}
(if (nil? pin-retries-count)
{:effects.utils/show-popup {:title "wrong-keycard"}}
{:db (-> db
(assoc-in [:keycard :application-info :pin-retry-counter] pin-retries-count)
(update-in [:keycard :pin] assoc :status :error))
:fx [[:dispatch [:keycard/hide-connection-sheet]]
(when (zero? pin-retries-count)
[:effects.utils/show-popup {:title "frozen-keycard"}])]})))))
(rf/reg-event-fx :keycard.login/on-get-keys-success
(fn [{:keys [db]} [data]]
(let [{:keys [key-uid encryption-public-key
whisper-private-key]} data
key-uid (str "0x" key-uid)
profile (get-in db [:profile/profiles-overview key-uid])]
{:db
(-> db
(dissoc :keycard)
(update :profile/login assoc
:password encryption-public-key
:key-uid key-uid
:name (:name profile)))
:fx [[:dispatch [:keycard/hide-connection-sheet]]
[:effects.keycard/login-with-keycard
{:password encryption-public-key
:whisper-private-key whisper-private-key
:key-uid key-uid}]]})))
(rf/reg-event-fx :keycard.login/on-get-keys-from-keychain-success
(fn [{:keys [db]} [key-uid [encryption-public-key whisper-private-key]]]
(when (and encryption-public-key whisper-private-key)
(let [profile (get-in db [:profile/profiles-overview key-uid])]
{:db
(-> db
(dissoc :keycard)
(update :profile/login assoc
:password encryption-public-key
:key-uid key-uid
:name (:name profile)))
:fx [[:dispatch [:keycard/hide-connection-sheet]]
[:effects.keycard/login-with-keycard
{:password encryption-public-key
:whisper-private-key whisper-private-key
:key-uid key-uid}]]}))))
(rf/reg-event-fx :keycard.login/on-get-application-info-success
(fn [{:keys [db]} [application-info]]
(let [profile (get-in db [:profile/profiles-overview (get-in db [:profile/login :key-uid])])
pin (get-in db [:keycard :pin :text])
error (keycard.utils/validate-application-info profile application-info)]
(if error
{:effects.utils/show-popup {:title (str error)}}
{:db (-> db
(assoc-in [:keycard :application-info] application-info)
(assoc-in [:keycard :pin :status] :verifying))
:effects.keycard/get-keys {:pin pin
:on-success #(rf/dispatch [:keycard.login/on-get-keys-success %])
:on-failure #(rf/dispatch [:keycard.login/on-get-keys-error %])}}))))
(rf/reg-event-fx :keycard.login/cancel-reading-card
(fn [{:keys [db]}]
{:db (assoc-in db [:keycard :on-card-connected-event-vector] nil)}))
(rf/reg-event-fx :keycard/read-card-and-login
(fn [{:keys [db]}]
(let [connected? (get-in db [:keycard :card-connected?])
event-vector [:keycard/get-application-info
{:on-success #(rf/dispatch [:keycard.login/on-get-application-info-success %])}]]
(log/debug "[keycard] proceed-to-login")
{:db (assoc-in db [:keycard :on-card-connected-event-vector] event-vector)
:fx [[:dispatch
[:keycard/show-connection-sheet
{:on-cancel-event-vector [:keycard.login/cancel-reading-card]}]]
(when connected?
[:dispatch event-vector])]})))
@@ -0,0 +1,21 @@
(ns status-im.contexts.keycard.pin.events
(:require [utils.re-frame :as rf]))
(rf/reg-event-fx :keycard.pin/delete-pressed
(fn [{:keys [db]}]
(let [pin (get-in db [:keycard :pin :text])]
(when (and pin (pos? (count pin)))
{:db (-> db
(assoc-in [:keycard :pin :text] (.slice pin 0 -1))
(assoc-in [:keycard :pin :status] nil))}))))
(rf/reg-event-fx :keycard.pin/number-pressed
(fn [{:keys [db]} [number max-numbers on-complete-event]]
(let [pin (get-in db [:keycard :pin :text])
new-pin (str pin number)]
(when (<= (count new-pin) max-numbers)
{:db (-> db
(assoc-in [:keycard :pin :text] new-pin)
(assoc-in [:keycard :pin :status] nil))
:fx [(when (= (dec max-numbers) (count pin))
[:dispatch [on-complete-event]])]}))))
@@ -0,0 +1,26 @@
(ns status-im.contexts.keycard.pin.view
(:require [quo.core :as quo]
[react-native.core :as rn]
[status-im.constants :as constants]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn auth
[callback-event-key]
(let [{:keys [text status]} (rf/sub [:keycard/pin])
pin-retry-counter (rf/sub [:keycard/pin-retry-counter])
error? (= status :error)]
[rn/view {:padding-bottom 12 :flex 1}
[rn/view {:flex 1 :justify-content :center :align-items :center :padding 34}
[quo/pin-input
{:blur? false
:number-of-pins constants/pincode-length
:number-of-filled-pins (count text)
:error? error?
:info (when error?
(i18n/label :t/pin-retries-left {:number pin-retry-counter}))}]]
[quo/numbered-keyboard
{:delete-key? true
:on-delete #(rf/dispatch [:keycard.pin/delete-pressed])
:on-press #(rf/dispatch [:keycard.pin/number-pressed % constants/pincode-length
callback-event-key])}]]))

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