Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ff4b8d8b6 | ||
|
|
f775010691 |
@@ -74,8 +74,7 @@
|
||||
;; https://github.com/borkdude/clj-kondo/issues/867
|
||||
:unresolved-symbol {:exclude [PersistentPriorityMap.EMPTY
|
||||
number
|
||||
legacy.status-im.test-helpers/restore-app-db
|
||||
(cljs.test/is [match-strict?])]}
|
||||
legacy.status-im.test-helpers/restore-app-db]}
|
||||
:unresolved-var {:level :error}
|
||||
:unsorted-required-namespaces {:level :error}
|
||||
:unused-alias {:level :warning}
|
||||
|
||||
@@ -353,17 +353,13 @@ 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 --config-merge '{:output-to \"$(SHADOW_OUTPUT_TO)\" :ns-regexp \"$(SHADOW_NS_REGEXP)\"}'" \
|
||||
'yarn shadow-cljs watch test --verbose' \
|
||||
"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
|
||||
|
||||
+51
-27
@@ -1,43 +1,67 @@
|
||||
## Getting Started
|
||||
## Getting Started
|
||||
|
||||
[Starting Guide](starting-guide.md)
|
||||
|
||||
[IDE Setup](ide-setup.md)
|
||||
|
||||
|
||||
- [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
|
||||
|
||||
- [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)
|
||||
[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)
|
||||
|
||||
|
||||
## 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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
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:
|
||||

|
||||

|
||||
|
||||
|
||||
## 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.
|
||||

|
||||
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.
|
||||

|
||||
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 it’s 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 it’s 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
|
||||

|
||||

|
||||
|
||||
|
||||
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.
|
||||
@@ -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/tests/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/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:
|
||||
|
||||
@@ -1,167 +0,0 @@
|
||||
# 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).
|
||||
@@ -711,11 +711,11 @@
|
||||
},
|
||||
|
||||
{
|
||||
"path": "nubank/matcher-combinators/3.9.1/matcher-combinators-3.9.1",
|
||||
"path": "nubank/matcher-combinators/3.8.8/matcher-combinators-3.8.8",
|
||||
"host": "https://repo.clojars.org",
|
||||
"jar": {
|
||||
"sha1": "f0830a112cae8ee931a90d9f39214a0ed4d44150",
|
||||
"sha256": "1rjcgqhms84xmnhs7sqcd3b2dpa37mmzgz2yz33yz2rdb9skl96g"
|
||||
"sha1": "4c94bd510f0c18a20191e46dd6becedebc640bbd",
|
||||
"sha256": "0wpla2hx0s4mda58ndyd8938zmnz1gyhgfr6pzfphy27xfbvsssl"
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ net/cgrand/macrovich/0.2.1/macrovich-0.2.1.jar
|
||||
net/java/dev/jna/jna/5.12.1/jna-5.12.1.jar
|
||||
nrepl/bencode/1.1.0/bencode-1.1.0.jar
|
||||
nrepl/nrepl/1.0.0/nrepl-1.0.0.jar
|
||||
nubank/matcher-combinators/3.9.1/matcher-combinators-3.9.1.jar
|
||||
nubank/matcher-combinators/3.8.8/matcher-combinators-3.8.8.jar
|
||||
org/apache/ant/ant/1.10.11/ant-1.10.11.jar
|
||||
org/apache/ant/ant-launcher/1.10.11/ant-launcher-1.10.11.jar
|
||||
org/apache/commons/commons-lang3/3.12.0/commons-lang3-3.12.0.jar
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@
|
||||
[cider/piggieback "0.4.1"]
|
||||
[org.slf4j/slf4j-nop "2.0.9"]
|
||||
[re-frisk-remote "1.6.0"]
|
||||
[nubank/matcher-combinators "3.9.1"]
|
||||
[nubank/matcher-combinators "3.8.8"]
|
||||
|
||||
;; Use the same version specified in the Nix dependency.
|
||||
[clj-kondo/clj-kondo "2024.03.13"]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
(ns legacy.status-im.data-store.communities
|
||||
(:require
|
||||
[clojure.set :as set]
|
||||
[status-im.constants :as constants]
|
||||
[utils.transforms :as transforms]))
|
||||
[clojure.walk :as walk]
|
||||
[status-im.constants :as constants]))
|
||||
|
||||
(defn <-revealed-accounts-rpc
|
||||
[accounts]
|
||||
@@ -22,26 +22,19 @@
|
||||
(reduce #(assoc %1 (key-fn %2) (<-request-to-join-community-rpc %2)) {} requests))
|
||||
|
||||
(defn <-chats-rpc
|
||||
"This transformation from RPC is optimized differently because there can be
|
||||
thousands of members in all chats and we don't want to transform them from JS
|
||||
to CLJS because they will only be used to list community members or community
|
||||
chat members."
|
||||
[chats-js]
|
||||
(let [chat-key-fn (fn [k]
|
||||
(case k
|
||||
"tokenGated" :token-gated?
|
||||
"canPost" :can-post?
|
||||
"can-view" :can-view?
|
||||
"hideIfPermissionsNotMet" :hide-if-permissions-not-met?
|
||||
(keyword k)))
|
||||
chat-val-fn (fn [k v]
|
||||
(if (= "members" k)
|
||||
v
|
||||
(transforms/js->clj v)))]
|
||||
(transforms/<-js-map
|
||||
chats-js
|
||||
{:val-fn (fn [_ v]
|
||||
(transforms/<-js-map v {:key-fn chat-key-fn :val-fn chat-val-fn}))})))
|
||||
[chats]
|
||||
(reduce-kv (fn [acc k v]
|
||||
(assoc acc
|
||||
(name k)
|
||||
(-> v
|
||||
(assoc :token-gated? (:tokenGated v)
|
||||
:can-post? (:canPost v)
|
||||
:can-view? (:canView v)
|
||||
:hide-if-permissions-not-met? (:hideIfPermissionsNotMet v))
|
||||
(dissoc :canPost :tokenGated :canView :hideIfPermissionsNotMet)
|
||||
(update :members walk/stringify-keys))))
|
||||
{}
|
||||
chats))
|
||||
|
||||
(defn <-categories-rpc
|
||||
[categ]
|
||||
@@ -55,59 +48,49 @@
|
||||
[token-permission]
|
||||
(= (:type token-permission) constants/community-token-permission-become-member))
|
||||
|
||||
(defn- rename-community-key
|
||||
[k]
|
||||
(case k
|
||||
"canRequestAccess" :can-request-access?
|
||||
"canManageUsers" :can-manage-users?
|
||||
"canDeleteMessageForEveryone" :can-delete-message-for-everyone?
|
||||
;; This flag is misleading based on its name alone
|
||||
;; because it should not be used to decide if the user
|
||||
;; is *allowed* to join. Allowance is based on token
|
||||
;; permissions. Still, the flag can be used to know
|
||||
;; whether or not the user will have to wait until an
|
||||
;; admin approves a join request.
|
||||
"canJoin" :can-join?
|
||||
"requestedToJoinAt" :requested-to-join-at
|
||||
"isMember" :is-member?
|
||||
"outroMessage" :outro-message
|
||||
"adminSettings" :admin-settings
|
||||
"tokenPermissions" :token-permissions
|
||||
"communityTokensMetadata" :tokens-metadata
|
||||
"introMessage" :intro-message
|
||||
"muteTill" :muted-till
|
||||
"lastOpenedAt" :last-opened-at
|
||||
"joinedAt" :joined-at
|
||||
(keyword k)))
|
||||
|
||||
(defn <-rpc
|
||||
[c-js]
|
||||
(let [community (transforms/<-js-map
|
||||
c-js
|
||||
{:key-fn rename-community-key
|
||||
:val-fn (fn [k v]
|
||||
(case k
|
||||
"members" v
|
||||
"chats" (<-chats-rpc v)
|
||||
(transforms/js->clj v)))})]
|
||||
(-> community
|
||||
(update :admin-settings
|
||||
set/rename-keys
|
||||
{:pinMessageAllMembersEnabled :pin-message-all-members-enabled?})
|
||||
(update :token-permissions seq)
|
||||
(update :categories <-categories-rpc)
|
||||
(assoc :role-permissions?
|
||||
(->> community
|
||||
:tokenPermissions
|
||||
vals
|
||||
(some role-permission?)))
|
||||
(assoc :membership-permissions?
|
||||
(->> community
|
||||
:tokenPermissions
|
||||
vals
|
||||
(some membership-permission?)))
|
||||
(assoc :token-images
|
||||
(reduce (fn [acc {sym :symbol image :image}]
|
||||
(assoc acc sym image))
|
||||
{}
|
||||
(:communityTokensMetadata community))))))
|
||||
[c]
|
||||
(-> c
|
||||
(set/rename-keys
|
||||
{:canRequestAccess :can-request-access?
|
||||
:canManageUsers :can-manage-users?
|
||||
:canDeleteMessageForEveryone :can-delete-message-for-everyone?
|
||||
;; This flag is misleading based on its name alone
|
||||
;; because it should not be used to decide if the user
|
||||
;; is *allowed* to join. Allowance is based on token
|
||||
;; permissions. Still, the flag can be used to know
|
||||
;; whether or not the user will have to wait until an
|
||||
;; admin approves a join request.
|
||||
:canJoin :can-join?
|
||||
:requestedToJoinAt :requested-to-join-at
|
||||
:isMember :is-member?
|
||||
:outroMessage :outro-message
|
||||
:adminSettings :admin-settings
|
||||
:tokenPermissions :token-permissions
|
||||
:communityTokensMetadata :tokens-metadata
|
||||
:introMessage :intro-message
|
||||
:muteTill :muted-till
|
||||
:lastOpenedAt :last-opened-at
|
||||
:joinedAt :joined-at})
|
||||
(update :admin-settings
|
||||
set/rename-keys
|
||||
{:pinMessageAllMembersEnabled :pin-message-all-members-enabled?})
|
||||
(update :members walk/stringify-keys)
|
||||
(update :chats <-chats-rpc)
|
||||
(update :token-permissions seq)
|
||||
(update :categories <-categories-rpc)
|
||||
(assoc :role-permissions?
|
||||
(->> c
|
||||
:tokenPermissions
|
||||
vals
|
||||
(some role-permission?)))
|
||||
(assoc :membership-permissions?
|
||||
(->> c
|
||||
:tokenPermissions
|
||||
vals
|
||||
(some membership-permission?)))
|
||||
(assoc :token-images
|
||||
(reduce (fn [acc {sym :symbol image :image}]
|
||||
(assoc acc sym image))
|
||||
{}
|
||||
(:communityTokensMetadata c)))))
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
legacy.status-im.multiaccounts.logout.core
|
||||
[legacy.status-im.multiaccounts.model :as multiaccounts.model]
|
||||
legacy.status-im.multiaccounts.update.core
|
||||
legacy.status-im.network.net-info
|
||||
legacy.status-im.pairing.core
|
||||
legacy.status-im.profile.core
|
||||
legacy.status-im.search.core
|
||||
|
||||
@@ -13,18 +13,15 @@
|
||||
(native-module/logout)))
|
||||
|
||||
(rf/defn initialize-app-db
|
||||
[{{:keys [keycard initials-avatar-font-file biometrics]
|
||||
:network/keys [type status]
|
||||
:wallet-connect/keys [web3-wallet]}
|
||||
[{{:keys [keycard initials-avatar-font-file biometrics]
|
||||
:network/keys [type]}
|
||||
:db}]
|
||||
{:db (assoc db/app-db
|
||||
:network/type type
|
||||
:network/status status
|
||||
:initials-avatar-font-file initials-avatar-font-file
|
||||
:keycard (dissoc keycard :secrets :pin :application-info)
|
||||
:biometrics biometrics
|
||||
:syncing nil
|
||||
:wallet-connect/web3-wallet web3-wallet)})
|
||||
:network/type type
|
||||
:initials-avatar-font-file initials-avatar-font-file
|
||||
:keycard (dissoc keycard :secrets :pin :application-info)
|
||||
:biometrics biometrics
|
||||
:syncing nil)})
|
||||
|
||||
(rf/defn logout-method
|
||||
{:events [::logout-method]}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
(ns legacy.status-im.network.net-info
|
||||
(:require
|
||||
["@react-native-community/netinfo" :default net-info]
|
||||
[native-module.core :as native-module]
|
||||
[re-frame.core :as re-frame]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(rf/defn change-network-status
|
||||
[{:keys [db] :as cofx} is-connected?]
|
||||
(rf/merge cofx
|
||||
{:db (assoc db :network-status (if is-connected? :online :offline))}))
|
||||
|
||||
(rf/defn change-network-type
|
||||
[{:keys [db] :as cofx} network-type expensive?]
|
||||
(rf/merge cofx
|
||||
{:db (assoc db :network/type network-type)
|
||||
:network/notify-status-go [network-type expensive?]
|
||||
:dispatch [:mobile-network/on-network-status-change]}))
|
||||
|
||||
(rf/defn handle-network-info-change
|
||||
{:events [::network-info-changed]}
|
||||
[{:keys [db] :as cofx} {:keys [isConnected type details] :as state}]
|
||||
(let [old-network-status (:network-status db)
|
||||
old-network-type (:network/type db)
|
||||
connectivity-status (if isConnected :online :offline)
|
||||
status-changed? (= connectivity-status old-network-status)
|
||||
type-changed? (= type old-network-type)]
|
||||
(log/debug "[net-info]"
|
||||
"old-network-status" old-network-status
|
||||
"old-network-type" old-network-type
|
||||
"connectivity-status" connectivity-status
|
||||
"type" type
|
||||
"details" details)
|
||||
(rf/merge cofx
|
||||
(when-not status-changed?
|
||||
(change-network-status isConnected))
|
||||
(when-not type-changed?
|
||||
(change-network-type type (:is-connection-expensive details))))))
|
||||
|
||||
(defn add-net-info-listener
|
||||
[]
|
||||
(when net-info
|
||||
(.addEventListener ^js net-info
|
||||
#(re-frame/dispatch [::network-info-changed
|
||||
(js->clj % :keywordize-keys true)]))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:network/listen-to-network-info
|
||||
(fn []
|
||||
(add-net-info-listener)))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:network/notify-status-go
|
||||
(fn [[network-type expensive?]]
|
||||
(native-module/connection-change network-type expensive?)))
|
||||
@@ -15,6 +15,8 @@
|
||||
(reg-root-key-sub :visibility-status-updates :visibility-status-updates)
|
||||
(reg-root-key-sub :fleets/custom-fleets :custom-fleets)
|
||||
(reg-root-key-sub :ui/search :ui/search)
|
||||
(reg-root-key-sub :network/type :network/type)
|
||||
(reg-root-key-sub :network-status :network-status)
|
||||
(reg-root-key-sub :peer-stats/count :peer-stats/count)
|
||||
(reg-root-key-sub :peers-summary :peers-summary)
|
||||
(reg-root-key-sub :web3-node-version :web3-node-version)
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
light-client-enabled?
|
||||
store-confirmations-enabled?
|
||||
current-fleet
|
||||
test-networks-enabled?
|
||||
is-goerli-enabled?
|
||||
peer-syncing-enabled?]}]
|
||||
(keep
|
||||
identity
|
||||
@@ -85,6 +87,22 @@
|
||||
[: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
|
||||
:container-margin-bottom 8
|
||||
:on-press
|
||||
#(re-frame/dispatch [:profile.settings/toggle-test-networks])
|
||||
:accessory :switch
|
||||
:active test-networks-enabled?}
|
||||
{:size :small
|
||||
:title "Enable Goerli as test network"
|
||||
:accessibility-label :enable-sepolia-as-test-network
|
||||
:container-margin-bottom 8
|
||||
:on-press
|
||||
#(re-frame/dispatch [:profile.settings/toggle-goerli-test-network])
|
||||
:accessory :switch
|
||||
:active is-goerli-enabled?}
|
||||
{:size :small
|
||||
:title "Peer syncing"
|
||||
:accessibility-label :peer-syncing
|
||||
@@ -106,7 +124,9 @@
|
||||
|
||||
(views/defview advanced-settings
|
||||
[]
|
||||
(views/letsubs [light-client-enabled? [:profile/light-client-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]
|
||||
@@ -127,6 +147,8 @@
|
||||
: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}]]))
|
||||
|
||||
@@ -127,7 +127,7 @@
|
||||
(when (and can-manage-users? (= constants/community-on-request-access (:access permissions)))
|
||||
[requests-to-join community-id])
|
||||
[rn/flat-list
|
||||
{:data sorted-members
|
||||
{:data (keys sorted-members)
|
||||
:render-data {:community-id community-id
|
||||
:my-public-key my-public-key
|
||||
:can-kick-users? (and can-manage-users?
|
||||
|
||||
@@ -127,7 +127,7 @@
|
||||
[:app-state
|
||||
:current-chat-id
|
||||
:network
|
||||
:network/status
|
||||
:network-status
|
||||
:peers-summary
|
||||
:sync-state
|
||||
:view-id
|
||||
|
||||
@@ -171,7 +171,7 @@
|
||||
#js
|
||||
{:getEnforcing {}})
|
||||
|
||||
(def net-info #js {:addEventListener identity})
|
||||
(def net-info #js {})
|
||||
(def react-native-biometrics #js {:default {}})
|
||||
(def react-native-static-safe-area-insets #js {:default {}})
|
||||
|
||||
|
||||
@@ -332,9 +332,7 @@
|
||||
(defn set-blank-preview-flag
|
||||
[flag]
|
||||
(log/debug "[native-module] set-blank-preview-flag")
|
||||
;; Sometimes the app crashes during logout because `flag` is nil.
|
||||
(when flag
|
||||
(.setBlankPreviewFlag ^js (encryption) flag)))
|
||||
(.setBlankPreviewFlag ^js (encryption) flag))
|
||||
|
||||
(defn get-device-model-info
|
||||
[]
|
||||
|
||||
@@ -65,8 +65,9 @@
|
||||
:style (style/container loading? theme size)}
|
||||
[loading-view theme]]
|
||||
[linear-gradient/linear-gradient
|
||||
{:style (style/container loading? theme size)
|
||||
:colors (linear-gradient-props theme customization-color)}
|
||||
(assoc {:style (style/container loading? theme size)}
|
||||
:colors
|
||||
(linear-gradient-props theme customization-color))
|
||||
[rn/pressable
|
||||
{:accessibility-label :internal-link-card
|
||||
:on-press on-press}
|
||||
|
||||
@@ -47,12 +47,3 @@
|
||||
(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})
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
(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]
|
||||
@@ -21,15 +20,9 @@
|
||||
:on-press-in on-press-in
|
||||
:on-press-out on-press-out}
|
||||
[rn/view {:style style/container-info}
|
||||
(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)}]])
|
||||
[fast-image/fast-image
|
||||
{:source (:avatar dapp)
|
||||
:style {:width 32 :height 32}}]
|
||||
[rn/view {:style style/user-info}
|
||||
[text/text
|
||||
{:weight :semi-bold
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
(defn main
|
||||
[{:keys [type customization-color]} theme]
|
||||
{:justify-content :flex-start
|
||||
:align-items :center
|
||||
{:align-items :center
|
||||
:height 32
|
||||
:padding-left 4
|
||||
:padding-right 10
|
||||
@@ -16,7 +15,6 @@
|
||||
(defn label
|
||||
[type theme]
|
||||
{:color (colors/theme-colors colors/neutral-100 colors/white theme)
|
||||
:flex-shrink 1
|
||||
:margin-left (if (= type :address) 6 4)})
|
||||
|
||||
(def collectible-image
|
||||
|
||||
@@ -58,19 +58,18 @@
|
||||
- :emoji - string - emoji used for displaying account avatar
|
||||
- :image-source - resource - image to display on :network, :collectible and :user
|
||||
- :theme - :light / :dark"
|
||||
[{:keys [label customization-color type container-style]
|
||||
[{:keys [label customization-color type]
|
||||
:as props
|
||||
:or {customization-color colors/neutral-80-opa-5}}]
|
||||
(let [theme (quo.theme/use-theme)]
|
||||
[rn/view
|
||||
{:accessibility-label :container
|
||||
:style (merge (style/main (assoc props :customization-color customization-color)
|
||||
theme)
|
||||
container-style)}
|
||||
:style (style/main (assoc props :customization-color customization-color) theme)}
|
||||
[left-view props]
|
||||
[text/text
|
||||
{:style (style/label type theme)
|
||||
:weight :semi-bold
|
||||
:ellipsize-mode :tail
|
||||
:number-of-lines 1
|
||||
:size :heading-1}
|
||||
label]]))
|
||||
|
||||
@@ -21,8 +21,3 @@
|
||||
(if blur?
|
||||
(colors/theme-colors colors/neutral-80-opa-70 colors/white-opa-70 theme)
|
||||
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme)))
|
||||
|
||||
(def text
|
||||
{:flex 1
|
||||
;; NOTE: assures the ellipses are not cut off when text is too long
|
||||
:padding-right 2})
|
||||
|
||||
@@ -59,9 +59,7 @@
|
||||
[rn/view {:style (merge style/container container-style)}
|
||||
[text/text
|
||||
{:size :heading-1
|
||||
:number-of-lines 1
|
||||
:weight :semi-bold
|
||||
:style style/text
|
||||
:accessibility-label accessibility-label}
|
||||
title]
|
||||
(case right
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
[react-native.platform :as platform]))
|
||||
|
||||
(def account-colors
|
||||
[:blue :yellow :purple :turquoise :magenta :sky :orange :army :pink :camel :copper])
|
||||
[:blue :yellow :purple :turquoise :magenta :sky :orange :army :flamingo :camel :copper])
|
||||
|
||||
(defn alpha
|
||||
[value opacity]
|
||||
@@ -44,13 +44,6 @@
|
||||
(alpha light-color light-opacity)
|
||||
(alpha dark-color dark-opacity))))))
|
||||
|
||||
(defn valid-color?
|
||||
[color]
|
||||
(or (keyword? color)
|
||||
(and (string? color)
|
||||
(or (string/starts-with? color "#")
|
||||
(string/starts-with? color "rgb")))))
|
||||
|
||||
|
||||
;;;;Neutral
|
||||
|
||||
@@ -246,7 +239,7 @@
|
||||
60 "#CC6438"}
|
||||
:army {50 "#216266"
|
||||
60 "#1A4E52"}
|
||||
:pink {50 "#F66F8F"
|
||||
:flamingo {50 "#F66F8F"
|
||||
60 "#C55972"}
|
||||
:purple {50 "#7140FD"
|
||||
60 "#5A33CA"}
|
||||
@@ -323,14 +316,13 @@
|
||||
[s]
|
||||
(and (string? s) (string/starts-with? s "#")))
|
||||
|
||||
(def fallback-color (customization :blue))
|
||||
|
||||
(defn- get-from-colors-map
|
||||
[color suffix]
|
||||
(let [color-without-suffix (get colors-map color fallback-color)]
|
||||
(if (hex-string? color-without-suffix)
|
||||
(let [color-without-suffix (get colors-map color)
|
||||
resolved-color? (hex-string? color-without-suffix)]
|
||||
(if resolved-color?
|
||||
color-without-suffix
|
||||
(get color-without-suffix suffix))))
|
||||
(get-in colors-map [color suffix]))))
|
||||
|
||||
(defn- resolve-color*
|
||||
([color theme]
|
||||
@@ -364,15 +356,11 @@
|
||||
([color suffix]
|
||||
(custom-color color suffix nil))
|
||||
([color suffix opacity]
|
||||
(let [resolved-color (cond
|
||||
(not (keyword? color))
|
||||
color
|
||||
|
||||
(hex-string? (get colors-map color))
|
||||
(get colors-map color fallback-color)
|
||||
|
||||
:else
|
||||
(get-in colors-map [color suffix] (get fallback-color suffix)))]
|
||||
(let [hex? (not (keyword? color))
|
||||
resolved-color (cond hex? color
|
||||
(hex-string? (get colors-map color)) (get colors-map color)
|
||||
:else (get-in colors-map
|
||||
[color suffix]))]
|
||||
(if opacity
|
||||
(alpha resolved-color (/ opacity 100))
|
||||
resolved-color))))))
|
||||
|
||||
@@ -1,39 +1,6 @@
|
||||
(ns react-native.linear-gradient
|
||||
(:require
|
||||
["react-native-linear-gradient" :default LinearGradient]
|
||||
[quo.foundations.colors :as colors]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[taoensso.timbre :as log]))
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
(def ^:private linear-gradient* (reagent/adapt-react-class LinearGradient))
|
||||
|
||||
(defn- split-valid-colors
|
||||
[acc idx color]
|
||||
(let [color? (colors/valid-color? color)]
|
||||
(cond-> acc
|
||||
:always (update :safe-colors conj (if color? color "transparent"))
|
||||
(not color?) (update :wrong-colors conj [idx color]))))
|
||||
|
||||
(defn- wrong-colors-str
|
||||
[colors]
|
||||
(reduce-kv (fn [s idx color]
|
||||
(str s "Index: " idx ", color: " (prn-str color)))
|
||||
"Invalid color values in vector passed to Linear Gradient:\n"
|
||||
colors))
|
||||
|
||||
(defn linear-gradient
|
||||
[props & children]
|
||||
(when ^boolean js/goog.DEBUG
|
||||
(assert (vector? (:colors props))))
|
||||
(let [{:keys [wrong-colors safe-colors]} (rn/use-memo
|
||||
(fn []
|
||||
(reduce-kv split-valid-colors
|
||||
{:safe-colors []
|
||||
:wrong-colors {}}
|
||||
(:colors props)))
|
||||
[(:colors props)])]
|
||||
(when (seq wrong-colors)
|
||||
(log/error (wrong-colors-str wrong-colors)))
|
||||
(into [linear-gradient* (assoc props :colors safe-colors)]
|
||||
children)))
|
||||
(def linear-gradient (reagent/adapt-react-class LinearGradient))
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
(oops/ocall wc-utils
|
||||
"buildApprovedNamespaces"
|
||||
(bean/->js {:proposal proposal
|
||||
:supportedNamespaces (clj->js supported-namespaces)})))
|
||||
:supportedNamespaces supported-namespaces})))
|
||||
|
||||
;; Get an error from this list:
|
||||
;; https://github.com/WalletConnect/walletconnect-monorepo/blob/c6e9529418a0c81d4efcc6ac4e61f242a50b56c5/packages/utils/src/errors.ts
|
||||
@@ -51,10 +51,9 @@
|
||||
|
||||
(defn reject-session
|
||||
[{:keys [web3-wallet id reason]}]
|
||||
(oops/ocall web3-wallet
|
||||
"rejectSession"
|
||||
(bean/->js {:id id
|
||||
:reason reason})))
|
||||
(.rejectSession web3-wallet
|
||||
(clj->js {:id id
|
||||
:reason reason})))
|
||||
|
||||
(defn approve-session
|
||||
[{:keys [web3-wallet id approved-namespaces]}]
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
(ns status-im.common.raw-data-block.view
|
||||
(:require [quo.core :as quo]
|
||||
[react-native.gesture :as gesture]
|
||||
[react-native.core :as rn]
|
||||
[status-im.common.raw-data-block.style :as style]))
|
||||
|
||||
(defn view
|
||||
[data]
|
||||
[gesture/scroll-view
|
||||
{:style style/container}
|
||||
[rn/scroll-view
|
||||
{:style style/container
|
||||
:content-container-style style/content}
|
||||
[quo/text
|
||||
{:size :paragraph-2
|
||||
:style style/content
|
||||
:weight :code}
|
||||
data]])
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
(ns status-im.contexts.chat.messenger.messages.link-preview.events
|
||||
(:require [camel-snake-kebab.core :as csk]
|
||||
[status-im.common.json-rpc.events :as json-rpc]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.collection]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
@@ -33,6 +35,17 @@
|
||||
{:fx [[:dispatch
|
||||
[:profile.settings/profile-update :link-preview-request-enabled (boolean enabled?)]]]}))
|
||||
|
||||
(rf/reg-event-fx :chat.ui/link-preview-whitelist-received
|
||||
(fn [{:keys [db]} [whitelist]]
|
||||
{:db (assoc db :link-previews-whitelist whitelist)}))
|
||||
|
||||
(rf/reg-fx :chat.ui/request-link-preview-whitelist
|
||||
(fn []
|
||||
(json-rpc/call {:method "wakuext_getLinkPreviewWhitelist"
|
||||
:params []
|
||||
:on-success [:chat.ui/link-preview-whitelist-received]
|
||||
:on-error #(log/error "Failed to get link preview whitelist")})))
|
||||
|
||||
(rf/reg-event-fx :chat.ui/enable-link-previews
|
||||
(fn [{{:profile/keys [profile]} :db} [site enabled?]]
|
||||
(let [enabled-sites (if enabled?
|
||||
|
||||
@@ -103,11 +103,11 @@
|
||||
(models.contact/process-js-contacts cofx response-js)
|
||||
|
||||
(seq communities)
|
||||
(do
|
||||
(let [communities-clj (types/js->clj communities)]
|
||||
(js-delete response-js "communities")
|
||||
(rf/merge cofx
|
||||
(process-next response-js sync-handler)
|
||||
(communities/handle-communities communities)))
|
||||
(communities/handle-communities communities-clj)))
|
||||
|
||||
(seq bookmarks)
|
||||
(let [bookmarks-clj (types/js->clj bookmarks)]
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"0xD" {:address "0xD"
|
||||
:operable? false
|
||||
:position 3
|
||||
:color :pink
|
||||
:color :flamingo
|
||||
:emoji "🦩"}})
|
||||
|
||||
(def permissioned-accounts
|
||||
|
||||
@@ -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/operable-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
|
||||
|
||||
@@ -262,7 +262,7 @@
|
||||
|
||||
can-edit-addresses? (rf/sub [:communities/can-edit-shared-addresses? id])
|
||||
|
||||
wallet-accounts (rf/sub [:wallet/operable-accounts])
|
||||
wallet-accounts (rf/sub [:wallet/operable-accounts-without-watched-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)
|
||||
|
||||
@@ -57,24 +57,19 @@
|
||||
:index index})
|
||||
|
||||
(defn- members
|
||||
[community-id chat-id theme]
|
||||
(let [online-members (rf/sub [:communities/chat-members-sorted community-id chat-id :online])
|
||||
offline-members (rf/sub [:communities/chat-members-sorted community-id chat-id :offline])]
|
||||
[rn/section-list
|
||||
{:key-fn :public-key
|
||||
:content-container-style {:padding-bottom 20}
|
||||
:get-item-layout get-item-layout
|
||||
:content-inset-adjustment-behavior :never
|
||||
:sections [{:title (i18n/label :t/online)
|
||||
:data online-members}
|
||||
{:title (i18n/label :t/offline)
|
||||
:data offline-members}]
|
||||
:sticky-section-headers-enabled false
|
||||
:render-section-header-fn contact-list/contacts-section-header
|
||||
:render-section-footer-fn footer
|
||||
:render-data {:theme theme}
|
||||
:render-fn contact-item
|
||||
:scroll-event-throttle 32}]))
|
||||
[items theme]
|
||||
[rn/section-list
|
||||
{:key-fn :public-key
|
||||
:content-container-style {:padding-bottom 20}
|
||||
:get-item-layout get-item-layout
|
||||
:content-inset-adjustment-behavior :never
|
||||
:sections items
|
||||
:sticky-section-headers-enabled false
|
||||
:render-section-header-fn contact-list/contacts-section-header
|
||||
:render-section-footer-fn footer
|
||||
:render-data {:theme theme}
|
||||
:render-fn contact-item
|
||||
:scroll-event-throttle 32}])
|
||||
|
||||
(defn view
|
||||
[]
|
||||
@@ -83,6 +78,8 @@
|
||||
{:keys [description chat-name emoji muted chat-type color]
|
||||
:as chat} (rf/sub [:chats/chat-by-id chat-id])
|
||||
pins-count (rf/sub [:chats/pin-messages-count chat-id])
|
||||
items (rf/sub [:communities/sorted-community-members-section-list
|
||||
community-id chat-id])
|
||||
theme (quo.theme/use-theme)]
|
||||
(rn/use-mount (fn []
|
||||
(rf/dispatch [:pin-message/load-pin-messages chat-id])))
|
||||
@@ -136,4 +133,4 @@
|
||||
(if muted
|
||||
(home.actions/unmute-chat-action chat-id)
|
||||
(home.actions/mute-chat-action chat-id chat-type muted)))}]}]]]
|
||||
[members community-id chat-id theme]]))
|
||||
[members items theme]]))
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
:as item}]
|
||||
(let [user-selected? (rf/sub [:is-contact-selected? public-key])
|
||||
{:keys [id]} (rf/sub [:get-screen-params])
|
||||
community-members-keys (set (rf/sub [:communities/community-members id]))
|
||||
community-members-keys (set (keys (rf/sub [:communities/community-members id])))
|
||||
community-member? (boolean (community-members-keys public-key))
|
||||
on-toggle (fn []
|
||||
(when-not community-member?
|
||||
|
||||
@@ -39,6 +39,17 @@
|
||||
|
||||
(rf/reg-event-fx :communities/handle-community handle-community)
|
||||
|
||||
(schema/=> handle-community
|
||||
[:=>
|
||||
[:catn
|
||||
[:cofx :schema.re-frame/cofx]
|
||||
[:args
|
||||
[:schema [:catn [:community-js map?]]]]]
|
||||
[:maybe
|
||||
[:map
|
||||
[:db [:map [:communities map?]]]
|
||||
[:fx vector?]]]])
|
||||
|
||||
(rf/defn handle-removed-chats
|
||||
[{:keys [db]} chat-ids]
|
||||
{:db (reduce (fn [db chat-id]
|
||||
@@ -73,9 +84,10 @@
|
||||
|
||||
(rf/defn handle-communities
|
||||
{:events [:community/fetch-success]}
|
||||
[{:keys [db]} communities-js]
|
||||
{:fx (map (fn [c] [:dispatch [:communities/handle-community c]])
|
||||
communities-js)})
|
||||
[{:keys [db]} communities]
|
||||
{:fx
|
||||
(->> communities
|
||||
(map #(vector :dispatch [:communities/handle-community %])))})
|
||||
|
||||
(rf/reg-event-fx :communities/request-to-join-result
|
||||
(fn [{:keys [db]} [community-id request-id response-js]]
|
||||
@@ -124,30 +136,21 @@
|
||||
{}
|
||||
categories))}))
|
||||
|
||||
(rf/reg-event-fx :community/fetch-low-priority
|
||||
(fn []
|
||||
{:fx [[:json-rpc/call
|
||||
[{:method "wakuext_checkAndDeletePendingRequestToJoinCommunity"
|
||||
:params []
|
||||
:js-response true
|
||||
:on-success [:sanitize-messages-and-process-response]
|
||||
:on-error #(log/info "failed to fetch communities" %)}
|
||||
{:method "wakuext_collapsedCommunityCategories"
|
||||
:params []
|
||||
:on-success [:communities/fetched-collapsed-categories-success]
|
||||
:on-error #(log/error "failed to fetch collapsed community categories" %)}]]]}))
|
||||
|
||||
(rf/reg-event-fx :community/fetch
|
||||
(fn [_]
|
||||
{:fx [[:json-rpc/call
|
||||
[{:method "wakuext_serializedCommunities"
|
||||
:params []
|
||||
:on-success [:community/fetch-success]
|
||||
:js-response true
|
||||
:on-error #(log/error "failed to fetch communities" %)}]]
|
||||
;; Dispatch a little after 1000ms because other higher-priority events
|
||||
;; after login are being processed at the 1000ms mark.
|
||||
[:dispatch-later [{:ms 1200 :dispatch [:community/fetch-low-priority]}]]]}))
|
||||
{:json-rpc/call [{:method "wakuext_serializedCommunities"
|
||||
:params []
|
||||
:on-success #(rf/dispatch [:community/fetch-success %])
|
||||
:on-error #(log/error "failed to fetch communities" %)}
|
||||
{:method "wakuext_checkAndDeletePendingRequestToJoinCommunity"
|
||||
:params []
|
||||
:js-response true
|
||||
:on-success #(rf/dispatch [:sanitize-messages-and-process-response %])
|
||||
:on-error #(log/info "failed to fetch communities" %)}
|
||||
{:method "wakuext_collapsedCommunityCategories"
|
||||
:params []
|
||||
:on-success #(rf/dispatch [:communities/fetched-collapsed-categories-success %])
|
||||
:on-error #(log/error "failed to fetch collapsed community categories" %)}]}))
|
||||
|
||||
(defn update-previous-permission-addresses
|
||||
[{:keys [db]} [community-id]]
|
||||
|
||||
@@ -151,7 +151,7 @@
|
||||
(-> effects :json-rpc/call first (select-keys [:method :params]))))))))
|
||||
|
||||
(deftest handle-community-test
|
||||
(let [community #js {:id community-id :clock 2}]
|
||||
(let [community {:id community-id :clock 2}]
|
||||
(testing "given a unjoined community"
|
||||
(let [effects (events/handle-community {} [community])]
|
||||
(is (match? community-id
|
||||
@@ -163,7 +163,7 @@
|
||||
(filter some? (:fx effects))))))
|
||||
|
||||
(testing "given a joined community"
|
||||
(let [community #js {:id community-id :clock 2 :joined true}
|
||||
(let [community (assoc community :joined true)
|
||||
effects (events/handle-community {} [community])]
|
||||
(is (match?
|
||||
[[:dispatch
|
||||
@@ -172,19 +172,16 @@
|
||||
(filter some? (:fx effects))))))
|
||||
|
||||
(testing "given a community with token-permissions-check"
|
||||
(let [community #js
|
||||
{:id community-id :clock 2 :token-permissions-check :fake-token-permissions-check}
|
||||
(let [community (assoc community :token-permissions-check :fake-token-permissions-check)
|
||||
effects (events/handle-community {} [community])]
|
||||
(is (match?
|
||||
[[:dispatch
|
||||
[:communities/check-permissions-to-join-community-with-all-addresses community-id]]]
|
||||
(filter some? (:fx effects))))))
|
||||
|
||||
(testing "given a community with lower clock"
|
||||
(let [effects (events/handle-community {:db {:communities {community-id {:clock 3}}}} [community])]
|
||||
(is (nil? effects))))
|
||||
|
||||
(testing "given a community without clock"
|
||||
(let [community #js {:id community-id}
|
||||
(let [community (dissoc community :clock)
|
||||
effects (events/handle-community {} [community])]
|
||||
(is (nil? effects))))))
|
||||
|
||||
@@ -75,51 +75,46 @@
|
||||
:banner (resources/get-image :discover)
|
||||
:accessibility-label :communities-home-discover-card}})
|
||||
|
||||
(defn on-tab-change
|
||||
[tab]
|
||||
(rf/dispatch [:communities/select-tab tab]))
|
||||
|
||||
(defn view
|
||||
[]
|
||||
(let [flat-list-ref (rn/use-ref-atom nil)
|
||||
set-flat-list-ref (rn/use-callback #(reset! flat-list-ref %))
|
||||
theme (quo.theme/use-theme)
|
||||
customization-color (rf/sub [:profile/customization-color])
|
||||
selected-tab (or (rf/sub [:communities/selected-tab]) :joined)
|
||||
{:keys [joined pending opened]} (rf/sub [:communities/grouped-by-status])
|
||||
selected-items (case selected-tab
|
||||
:joined joined
|
||||
:pending pending
|
||||
:opened opened)
|
||||
scroll-shared-value (reanimated/use-shared-value 0)
|
||||
on-scroll (rn/use-callback
|
||||
(fn [event]
|
||||
(common.banner/set-scroll-shared-value
|
||||
{:scroll-input (oops/oget event
|
||||
"nativeEvent.contentOffset.y")
|
||||
:shared-value scroll-shared-value})))]
|
||||
[:<>
|
||||
(if (empty? selected-items)
|
||||
[common.empty-state/view
|
||||
{:selected-tab selected-tab
|
||||
:tab->content (empty-state-content theme)}]
|
||||
[reanimated/flat-list
|
||||
{:ref set-flat-list-ref
|
||||
:key-fn :id
|
||||
:content-inset-adjustment-behavior :never
|
||||
:header [common.header-spacing/view]
|
||||
:render-fn item-render
|
||||
:style {:margin-top -1}
|
||||
:data selected-items
|
||||
:scroll-event-throttle 8
|
||||
:content-container-style {:padding-bottom
|
||||
jump-to.constants/floating-shell-button-height}
|
||||
:on-scroll on-scroll}])
|
||||
[common.banner/animated-banner
|
||||
{:content banner-data
|
||||
:customization-color customization-color
|
||||
:scroll-ref flat-list-ref
|
||||
:tabs tabs-data
|
||||
:selected-tab selected-tab
|
||||
:on-tab-change on-tab-change
|
||||
:scroll-shared-value scroll-shared-value}]]))
|
||||
(let [flat-list-ref (atom nil)
|
||||
set-flat-list-ref #(reset! flat-list-ref %)]
|
||||
(fn []
|
||||
(let [theme (quo.theme/use-theme)
|
||||
customization-color (rf/sub [:profile/customization-color])
|
||||
selected-tab (or (rf/sub [:communities/selected-tab]) :joined)
|
||||
{:keys [joined pending opened]} (rf/sub [:communities/grouped-by-status])
|
||||
selected-items (case selected-tab
|
||||
:joined joined
|
||||
:pending pending
|
||||
:opened opened)
|
||||
scroll-shared-value (reanimated/use-shared-value 0)]
|
||||
[:<>
|
||||
(if (empty? selected-items)
|
||||
[common.empty-state/view
|
||||
{:selected-tab selected-tab
|
||||
:tab->content (empty-state-content theme)}]
|
||||
[reanimated/flat-list
|
||||
{:ref set-flat-list-ref
|
||||
:key-fn :id
|
||||
:content-inset-adjustment-behavior :never
|
||||
:header [common.header-spacing/view]
|
||||
:render-fn item-render
|
||||
:style {:margin-top -1}
|
||||
:data selected-items
|
||||
:scroll-event-throttle 8
|
||||
:content-container-style {:padding-bottom
|
||||
jump-to.constants/floating-shell-button-height}
|
||||
:on-scroll #(common.banner/set-scroll-shared-value
|
||||
{:scroll-input (oops/oget
|
||||
%
|
||||
"nativeEvent.contentOffset.y")
|
||||
:shared-value scroll-shared-value})}])
|
||||
[:f> common.banner/animated-banner
|
||||
{:content banner-data
|
||||
:customization-color customization-color
|
||||
:scroll-ref flat-list-ref
|
||||
:tabs tabs-data
|
||||
:selected-tab selected-tab
|
||||
:on-tab-change (fn [tab] (rf/dispatch [:communities/select-tab tab]))
|
||||
:scroll-shared-value scroll-shared-value}]]))))
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
(ns status-im.contexts.networks.events
|
||||
(:require
|
||||
["@react-native-community/netinfo" :default net-info]
|
||||
[native-module.core :as native-module]
|
||||
[status-im.feature-flags :as ff]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(rf/reg-fx
|
||||
:effects.network/listen-to-network-info
|
||||
(fn []
|
||||
(when net-info
|
||||
(.addEventListener ^js net-info
|
||||
#(rf/dispatch [:network/on-state-change
|
||||
(js->clj % :keywordize-keys true)])))))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:network/on-state-change
|
||||
(fn [{:keys [db]} [{:keys [isConnected type details]}]]
|
||||
(let [old-network-status (:network/status db)
|
||||
old-network-type (:network/type db)
|
||||
connectivity-status (if isConnected :online :offline)
|
||||
status-changed? (not= connectivity-status old-network-status)
|
||||
type-changed? (not= type old-network-type)
|
||||
is-connection-expensive? (:is-connection-expensive details)]
|
||||
(log/debug "[net-info]"
|
||||
"old-network-status" old-network-status
|
||||
"old-network-type" old-network-type
|
||||
"connectivity-status" connectivity-status
|
||||
"type" type
|
||||
"details" details)
|
||||
{:fx [(when status-changed?
|
||||
[:dispatch [:network/on-network-status-change isConnected]])
|
||||
(when type-changed?
|
||||
[:dispatch [:network/on-network-type-change type is-connection-expensive?]])]})))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:network/on-network-type-change
|
||||
(fn [{:keys [db]} [network-type expensive?]]
|
||||
{:db (assoc db :network/type network-type)
|
||||
:fx [[:effects.network/notify-status-go network-type expensive?]
|
||||
[:dispatch [:mobile-network/on-network-status-change]]]}))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:network/on-network-status-change
|
||||
(fn [{:keys [db]} [is-connected?]]
|
||||
(let [network-status (if is-connected? :online :offline)]
|
||||
{:db (assoc db :network/status network-status)
|
||||
:fx [(when (ff/enabled? ::ff/wallet.wallet-connect)
|
||||
[:dispatch [:wallet-connect/reload-on-network-change is-connected?]])]})))
|
||||
|
||||
(rf/reg-fx
|
||||
:effects.network/notify-status-go
|
||||
(fn [network-type expensive?]
|
||||
(native-module/connection-change network-type expensive?)))
|
||||
@@ -73,9 +73,7 @@
|
||||
:onboarding/navigated-to-enter-seed-phrase-from-screen
|
||||
:screen/onboarding.new-to-status)]]
|
||||
:dispatch-later [{:ms constants/onboarding-generating-keys-animation-duration-ms
|
||||
:dispatch [:navigate-to-within-stack
|
||||
[:screen/onboarding.enable-notifications
|
||||
:screen/onboarding.generating-keys]]}]
|
||||
:dispatch [:init-root :screen/onboarding.enable-notifications]}]
|
||||
:db (-> db
|
||||
(dissoc :profile/login)
|
||||
(dissoc :auth-method)
|
||||
@@ -189,3 +187,14 @@
|
||||
{:key-uid key-uid
|
||||
:error %})}))))
|
||||
|
||||
(rf/defn navigate-to-identifiers
|
||||
{:events [:onboarding/navigate-to-identifiers]}
|
||||
[{:keys [db]}]
|
||||
(if (:onboarding/generated-keys? db)
|
||||
{:dispatch [:navigate-to-within-stack
|
||||
[:screen/onboarding.identifiers
|
||||
(get db
|
||||
:onboarding/navigated-to-enter-seed-phrase-from-screen
|
||||
:screen/onboarding.new-to-status)]]}
|
||||
{:dispatch-later [{:ms constants/onboarding-generating-keys-navigation-retry-ms
|
||||
:dispatch [:onboarding/navigate-to-identifiers]}]}))
|
||||
|
||||
@@ -2,13 +2,6 @@
|
||||
(:require
|
||||
[quo.foundations.colors :as colors]))
|
||||
|
||||
(def absolute-fill
|
||||
{:position :absolute
|
||||
:top 0
|
||||
:bottom 0
|
||||
:left 0
|
||||
:right 0})
|
||||
|
||||
(defn page-container
|
||||
[in-onboarding?]
|
||||
{:flex 1
|
||||
|
||||
@@ -52,9 +52,7 @@
|
||||
profile-color (:color (rf/sub [:onboarding/profile]))
|
||||
logged-in? (rf/sub [:multiaccount/logged-in?])]
|
||||
[rn/view {:style (style/page-container in-onboarding?)}
|
||||
(when-not in-onboarding?
|
||||
[rn/view {:style style/absolute-fill}
|
||||
[background/view true]])
|
||||
(when-not in-onboarding? [background/view true])
|
||||
[quo/page-nav {:type :no-title :background :blur}]
|
||||
[page-title (pairing-progress pairing-status)]
|
||||
(if config/show-not-implemented-features?
|
||||
|
||||
@@ -3,13 +3,6 @@
|
||||
[quo.foundations.colors :as colors]
|
||||
[react-native.reanimated :as reanimated]))
|
||||
|
||||
(def absolute-fill
|
||||
{:position :absolute
|
||||
:top 0
|
||||
:bottom 0
|
||||
:left 0
|
||||
:right 0})
|
||||
|
||||
(defn page-container
|
||||
[top]
|
||||
{:flex 1
|
||||
|
||||
@@ -66,8 +66,7 @@
|
||||
translate-x (reanimated/use-shared-value 0)
|
||||
window-width (:width (rn/get-window))]
|
||||
[rn/view {:style (style/page-container top)}
|
||||
[rn/view {:style style/absolute-fill}
|
||||
[background/view true]]
|
||||
[background/view true]
|
||||
[reanimated/view
|
||||
{:style (style/content translate-x)}
|
||||
[page-title]
|
||||
|
||||
@@ -13,17 +13,14 @@
|
||||
:value "Default"}
|
||||
{:key :active
|
||||
:value "Active"}]}
|
||||
{:key :dapp
|
||||
{:key :action
|
||||
:type :select
|
||||
:options [{:value "With icon"
|
||||
:key {:avatar (resources/get-dapp :coingecko)
|
||||
:name "Coingecko"
|
||||
:value "coingecko.com"}}
|
||||
{:value "Without icon"
|
||||
:key {:avatar nil
|
||||
:name "Coingecko"
|
||||
:value "coingecko.com"
|
||||
:customization-color :blue}}]}])
|
||||
:options [{:key :none
|
||||
:value "None"}
|
||||
{:key :icon
|
||||
:value "Icon"}]}
|
||||
{:key :blur?
|
||||
:type :boolean}])
|
||||
|
||||
(defn preview
|
||||
[]
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
[]
|
||||
(let [state (reagent/atom {:type :default
|
||||
:customization-color :blue
|
||||
:account-color :pink
|
||||
:account-color :flamingo
|
||||
:title "Alisher Yakupov"
|
||||
:address "0x21a...49e"
|
||||
:on-options-press #(js/alert "Options button pressed!")})]
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
{:image-source (quo.resources/get-network :ethereum)
|
||||
:label "Mainnet"}
|
||||
:saved-address
|
||||
{:customization-color :pink
|
||||
{:customization-color :flamingo
|
||||
:label "Peter Lambo"}
|
||||
:account
|
||||
{:label "Account"
|
||||
|
||||
@@ -57,15 +57,14 @@
|
||||
[{:method "wakuext_startMessenger"
|
||||
:on-success [:profile.login/messenger-started]
|
||||
:on-error #(log/error "failed to start messenger" %)}]]
|
||||
[:dispatch [:universal-links/generate-profile-url]]
|
||||
[:dispatch [:community/fetch]]
|
||||
|
||||
;; Wallet initialization can be delayed a little bit because we
|
||||
;; need to free the queue for heavier events first, such as
|
||||
;; loading chats and communities. This globally helps alleviate
|
||||
;; stuttering immediately after login.
|
||||
[:dispatch-later [{:ms 500 :dispatch [:wallet/initialize]}]]
|
||||
|
||||
[:push-notifications/load-preferences]
|
||||
[:profile.config/get-node-config]
|
||||
[:logs/set-level log-level]
|
||||
[:activity-center.notifications/fetch-pending-contact-requests-fx]
|
||||
[:activity-center/update-seen-state]
|
||||
[:activity-center.notifications/fetch-unread-count]
|
||||
|
||||
;; Immediately try to open last chat. We can't wait until the
|
||||
;; messenger has started and has processed all chats because
|
||||
@@ -91,45 +90,24 @@
|
||||
;; login phase 2: we want to load and show chats faster, so we split login into 2 phases
|
||||
(rf/reg-event-fx :profile.login/get-chats-callback
|
||||
(fn [{:keys [db]}]
|
||||
(let [{:keys [notifications-enabled? key-uid]} (:profile/profile db)]
|
||||
(let [{:keys [notifications-enabled? key-uid
|
||||
preview-privacy?]} (:profile/profile db)]
|
||||
{:db db
|
||||
:fx [[:effects.profile/enable-local-notifications]
|
||||
[:contacts/initialize-contacts]
|
||||
;; The delay is arbitrary. We just want to give some time for the
|
||||
;; thread to process more important events first, but we can't delay
|
||||
;; too much otherwise the UX may degrade due to stale data.
|
||||
[:dispatch-later [{:ms 1500 :dispatch [:profile.login/non-critical-initialization]}]]
|
||||
[:browser/initialize-browser]
|
||||
[:dispatch [:mobile-network/on-network-status-change]]
|
||||
[:group-chats/get-group-chat-invitations]
|
||||
[:profile.settings/get-profile-picture key-uid]
|
||||
[:profile.settings/blank-preview-flag-changed preview-privacy?]
|
||||
[:chat.ui/request-link-preview-whitelist]
|
||||
[:visibility-status-updates/fetch]
|
||||
[:switcher-cards/fetch]
|
||||
(when (ff/enabled? ::ff/wallet.wallet-connect)
|
||||
[:dispatch [:wallet-connect/init]])
|
||||
(when notifications-enabled?
|
||||
[:effects/push-notifications-enable])]})))
|
||||
|
||||
;; Login phase 3: events at this phase can wait a bit longer to be processed in
|
||||
;; order to leave room for higher-priority or heavy weight events.
|
||||
(rf/reg-event-fx :profile.login/non-critical-initialization
|
||||
(fn [{:keys [db]}]
|
||||
(let [{:keys [preview-privacy?]} (:profile/profile db)]
|
||||
{:fx [[:browser/initialize-browser]
|
||||
[:logging/initialize-web3-client-version]
|
||||
[:group-chats/get-group-chat-invitations]
|
||||
[:profile.settings/blank-preview-flag-changed preview-privacy?]
|
||||
(when (ff/enabled? ::ff/shell.jump-to)
|
||||
[:switcher-cards/fetch])
|
||||
[:visibility-status-updates/fetch]
|
||||
[:dispatch [:universal-links/generate-profile-url]]
|
||||
[:push-notifications/load-preferences]
|
||||
[:profile.config/get-node-config]
|
||||
[:activity-center.notifications/fetch-pending-contact-requests-fx]
|
||||
[:activity-center/update-seen-state]
|
||||
[:activity-center.notifications/fetch-unread-count]
|
||||
[:pairing/get-our-installations]
|
||||
[:json-rpc/call
|
||||
[{:method "admin_nodeInfo"
|
||||
:on-success [:profile.login/node-info-fetched]
|
||||
:on-error #(log/error "node-info: failed error" %)}]]]})))
|
||||
|
||||
(rf/reg-event-fx :profile.login/messenger-started
|
||||
(fn [{:keys [db]} [{:keys [mailservers]}]]
|
||||
(let [new-account? (get db :onboarding/new-account?)]
|
||||
@@ -141,6 +119,11 @@
|
||||
(rf/dispatch [:chats-list/load-success result])
|
||||
(rf/dispatch [:communities/get-user-requests-to-join])
|
||||
(rf/dispatch [:profile.login/get-chats-callback]))}]
|
||||
[:json-rpc/call
|
||||
[{:method "admin_nodeInfo"
|
||||
:on-success [:profile.login/node-info-fetched]
|
||||
:on-error #(log/error "node-info: failed error" %)}]]
|
||||
[:pairing/get-our-installations]
|
||||
(when-not new-account?
|
||||
[:dispatch [:universal-links/process-stored-event]])]})))
|
||||
|
||||
@@ -156,9 +139,11 @@
|
||||
(if error
|
||||
{:db (update db :profile/login #(-> % (dissoc :processing) (assoc :error error)))}
|
||||
{:db (dissoc db :profile/login)
|
||||
:fx [(when (and new-account? (not recovered-account?))
|
||||
[:dispatch-later [{:ms 1000 :dispatch [:wallet-legacy/set-initial-blocks-range]}]])
|
||||
[:dispatch-later [{:ms 2000 :dispatch [:ens/update-usernames ensUsernames]}]]
|
||||
:fx [[:logging/initialize-web3-client-version]
|
||||
(when (and new-account? (not recovered-account?))
|
||||
[:dispatch [:wallet-legacy/set-initial-blocks-range]])
|
||||
[:dispatch [:ens/update-usernames ensUsernames]]
|
||||
[:dispatch [:wallet/initialize]]
|
||||
[:dispatch [:profile.login/login-existing-profile settings account]]]})))
|
||||
|
||||
(rf/reg-event-fx
|
||||
|
||||
+28
-32
@@ -7,7 +7,6 @@
|
||||
[react-native.safe-area :as safe-area]
|
||||
[status-im.common.floating-button-page.view :as floating-button-page]
|
||||
[status-im.contexts.settings.wallet.saved-addresses.add-address-to-save.style :as style]
|
||||
[status-im.contexts.wallet.common.utils :as utils]
|
||||
[status-im.contexts.wallet.common.validation :as validation]
|
||||
[utils.debounce :as debounce]
|
||||
[utils.i18n :as i18n]
|
||||
@@ -19,21 +18,20 @@
|
||||
|
||||
(defn- validate-input
|
||||
[account-addresses saved-addresses user-input]
|
||||
(let [[_ address-without-prefix] (utils/split-prefix-and-address user-input)]
|
||||
(cond
|
||||
(string/blank? user-input)
|
||||
nil
|
||||
(cond
|
||||
(string/blank? user-input)
|
||||
nil
|
||||
|
||||
(contains? saved-addresses address-without-prefix)
|
||||
:existing-saved-address
|
||||
(contains? saved-addresses user-input)
|
||||
:existing-saved-address
|
||||
|
||||
(contains? account-addresses address-without-prefix)
|
||||
:own-account
|
||||
(contains? account-addresses user-input)
|
||||
:own-account
|
||||
|
||||
(not
|
||||
(or (validation/eth-address? user-input)
|
||||
(validation/ens-name? user-input)))
|
||||
:invalid-address-or-ens)))
|
||||
(not
|
||||
(or (validation/eth-address? user-input)
|
||||
(validation/ens-name? user-input)))
|
||||
:invalid-address-or-ens))
|
||||
|
||||
(defn- address-input
|
||||
[{:keys [input-value on-change-text paste-into-input clear-input]}]
|
||||
@@ -95,9 +93,8 @@
|
||||
|
||||
(defn- existing-saved-address
|
||||
[{:keys [address]}]
|
||||
(let [[_ address-without-prefix] (utils/split-prefix-and-address address)
|
||||
{:keys [name customization-color chain-short-names ens ens?]}
|
||||
(rf/sub [:wallet/saved-address-by-address address-without-prefix])]
|
||||
(let [{:keys [name customization-color chain-short-names ens ens?]}
|
||||
(rf/sub [:wallet/saved-address-by-address address])]
|
||||
[rn/view {:style style/existing-saved-address-container}
|
||||
[quo/text
|
||||
{:size :paragraph-1
|
||||
@@ -108,7 +105,7 @@
|
||||
{:blur? true
|
||||
:active-state? true
|
||||
:user-props {:name name
|
||||
:address (str chain-short-names address-without-prefix)
|
||||
:address (str chain-short-names address)
|
||||
:ens (when ens? ens)
|
||||
:customization-color customization-color
|
||||
:blur? true}
|
||||
@@ -171,21 +168,20 @@
|
||||
(rn/use-mount #(rf/dispatch [:wallet/clear-address-to-save]))
|
||||
[quo/overlay {:type :shell}
|
||||
[floating-button-page/view
|
||||
{:footer-container-padding 0
|
||||
:keyboard-should-persist-taps :handled
|
||||
:header [quo/page-nav
|
||||
{:type :no-title
|
||||
:icon-name :i/close
|
||||
:behind-overlay? true
|
||||
:on-press navigate-back
|
||||
:margin-top (safe-area/get-top)
|
||||
:accessibility-label :add-address-to-save-page-nav}]
|
||||
:footer (when (= view-id :screen/settings.add-address-to-save)
|
||||
[quo/button
|
||||
{:customization-color profile-color
|
||||
:disabled? button-disabled?
|
||||
:on-press on-press-continue}
|
||||
(i18n/label :t/continue)])}
|
||||
{:footer-container-padding 0
|
||||
:header [quo/page-nav
|
||||
{:type :no-title
|
||||
:icon-name :i/close
|
||||
:behind-overlay? true
|
||||
:on-press navigate-back
|
||||
:margin-top (safe-area/get-top)
|
||||
:accessibility-label :add-address-to-save-page-nav}]
|
||||
:footer (when (= view-id :screen/settings.add-address-to-save)
|
||||
[quo/button
|
||||
{:customization-color profile-color
|
||||
:disabled? button-disabled?
|
||||
:on-press on-press-continue}
|
||||
(i18n/label :t/continue)])}
|
||||
[quo/page-top
|
||||
{:container-style style/header-container
|
||||
:blur? true
|
||||
|
||||
@@ -105,25 +105,24 @@
|
||||
[ens ens? open-network-preferences address-text])]
|
||||
[quo/overlay {:type :shell}
|
||||
[floating-button-page/view
|
||||
{:footer-container-padding (if edit? (+ (safe-area/get-bottom) 12) 0)
|
||||
:keyboard-should-persist-taps :handled
|
||||
:header [quo/page-nav
|
||||
{:type :no-title
|
||||
:background :blur
|
||||
:icon-name (if edit? :i/close :i/arrow-left)
|
||||
:on-press navigate-back
|
||||
:margin-top (when-not edit? (safe-area/get-top))
|
||||
:accessibility-label :save-address-page-nav}]
|
||||
:footer [quo/button
|
||||
{:accessibility-label :save-address-button
|
||||
:type :primary
|
||||
:customization-color address-color
|
||||
:disabled? (string/blank? address-label)
|
||||
:on-press on-press-save}
|
||||
(i18n/label :t/save-address)]
|
||||
:customization-color address-color
|
||||
:gradient-cover? true
|
||||
:shell-overlay? true}
|
||||
{:footer-container-padding (if edit? (+ (safe-area/get-bottom) 12) 0)
|
||||
:header [quo/page-nav
|
||||
{:type :no-title
|
||||
:background :blur
|
||||
:icon-name (if edit? :i/close :i/arrow-left)
|
||||
:on-press navigate-back
|
||||
:margin-top (when-not edit? (safe-area/get-top))
|
||||
:accessibility-label :save-address-page-nav}]
|
||||
:footer [quo/button
|
||||
{:accessibility-label :save-address-button
|
||||
:type :primary
|
||||
:customization-color address-color
|
||||
:disabled? (string/blank? address-label)
|
||||
:on-press on-press-save}
|
||||
(i18n/label :t/save-address)]
|
||||
:customization-color address-color
|
||||
:gradient-cover? true
|
||||
:shell-overlay? true}
|
||||
[quo/wallet-user-avatar
|
||||
{:full-name (if (string/blank? address-label)
|
||||
placeholder
|
||||
|
||||
@@ -346,8 +346,7 @@
|
||||
#(reset! camera-permission-granted? %)
|
||||
#(reset! camera-permission-granted? false)))))
|
||||
[:<>
|
||||
[rn/view {:style style/absolute-fill}
|
||||
background]
|
||||
background
|
||||
(when camera-ready-to-scan?
|
||||
[render-camera
|
||||
{:torch-mode torch-mode
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
(ns status-im.contexts.wallet.account.tabs.dapps.style
|
||||
(:require
|
||||
[quo.foundations.colors :as colors]))
|
||||
|
||||
(def dapps-container
|
||||
{:padding-horizontal 20
|
||||
:padding-vertical 8})
|
||||
|
||||
(defn dapps-list
|
||||
[theme]
|
||||
{:border-radius 16
|
||||
:border-width 1
|
||||
:border-color (colors/theme-colors colors/neutral-10 colors/neutral-80 theme)})
|
||||
|
||||
(defn separator
|
||||
[theme]
|
||||
{:height 1
|
||||
:background-color (colors/theme-colors colors/neutral-10 colors/neutral-80 theme)})
|
||||
@@ -0,0 +1,37 @@
|
||||
(ns status-im.contexts.wallet.account.tabs.dapps.view
|
||||
(:require
|
||||
[quo.core :as quo]
|
||||
[quo.theme :as quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[status-im.common.resources :as resources]
|
||||
[status-im.contexts.wallet.account.tabs.dapps.style :as style]
|
||||
[status-im.contexts.wallet.common.empty-tab.view :as empty-tab]
|
||||
[utils.i18n :as i18n]))
|
||||
|
||||
(defn dapp-options
|
||||
[]
|
||||
[quo/action-drawer
|
||||
[[{:icon :i/browser
|
||||
:accessibility-label :visit-dapp
|
||||
:label (i18n/label :t/visit-dapp)}
|
||||
{:icon :i/disconnect
|
||||
:accessibility-label :disconnect-dapp
|
||||
:label (i18n/label :t/disconnect-dapp)
|
||||
:add-divider? true
|
||||
:danger? true}]]])
|
||||
|
||||
(defn view
|
||||
[]
|
||||
(let [theme (quo.theme/use-theme)
|
||||
dapps-list []]
|
||||
(if (empty? dapps-list)
|
||||
[empty-tab/view
|
||||
{:title (i18n/label :t/no-dapps)
|
||||
:description (i18n/label :t/no-dapps-description)
|
||||
:image (resources/get-themed-image :no-dapps theme)}]
|
||||
[rn/view {:style style/dapps-container}
|
||||
[rn/flat-list
|
||||
{:data dapps-list
|
||||
:style (style/dapps-list theme)
|
||||
:render-fn (fn [item] [quo/dapp item])
|
||||
:separator [rn/view {:style (style/separator theme)}]}]])))
|
||||
@@ -3,6 +3,7 @@
|
||||
[react-native.core :as rn]
|
||||
[status-im.contexts.wallet.account.tabs.about.view :as about]
|
||||
[status-im.contexts.wallet.account.tabs.assets.view :as assets]
|
||||
[status-im.contexts.wallet.account.tabs.dapps.view :as dapps]
|
||||
[status-im.contexts.wallet.collectible.options.view :as options-drawer]
|
||||
[status-im.contexts.wallet.common.activity-tab.view :as activity]
|
||||
[status-im.contexts.wallet.common.collectibles-tab.view :as collectibles]
|
||||
@@ -44,4 +45,5 @@
|
||||
{:title (i18n/label :t/no-permissions)
|
||||
:description (i18n/label :t/no-collectibles-description)
|
||||
:placeholder? true}]
|
||||
:dapps [dapps/view]
|
||||
[about/view])]))
|
||||
|
||||
@@ -13,11 +13,13 @@
|
||||
|
||||
(def first-tab-id :assets)
|
||||
|
||||
(def tabs-data
|
||||
[{:id :assets :label (i18n/label :t/assets) :accessibility-label :assets-tab}
|
||||
{:id :collectibles :label (i18n/label :t/collectibles) :accessibility-label :collectibles-tab}
|
||||
{:id :activity :label (i18n/label :t/activity) :accessibility-label :activity-tab}
|
||||
{:id :about :label (i18n/label :t/about) :accessibility-label :about}])
|
||||
(defn tabs-data
|
||||
[watch-only?]
|
||||
(cond-> [{:id :assets :label (i18n/label :t/assets) :accessibility-label :assets-tab}
|
||||
{:id :collectibles :label (i18n/label :t/collectibles) :accessibility-label :collectibles-tab}
|
||||
{:id :activity :label (i18n/label :t/activity) :accessibility-label :activity-tab}]
|
||||
(not watch-only?) (conj {:id :dapps :label (i18n/label :t/dapps) :accessibility-label :dapps})
|
||||
:always (conj {:id :about :label (i18n/label :t/about) :accessibility-label :about})))
|
||||
|
||||
(defn- change-tab [id] (rf/dispatch [:wallet/select-account-tab id]))
|
||||
|
||||
@@ -67,7 +69,7 @@
|
||||
{:style style/tabs
|
||||
:size 32
|
||||
:active-tab-id selected-tab
|
||||
:data tabs-data
|
||||
:data (tabs-data watch-only?)
|
||||
:on-change change-tab
|
||||
:scrollable? true
|
||||
:scroll-on-press? true}]
|
||||
|
||||
@@ -12,16 +12,13 @@
|
||||
(hot-reload/use-safe-unmount #(rf/dispatch [:wallet/clean-routes-calculation]))
|
||||
[rn/view {:style style/bridge-send-wrapper}
|
||||
[input-amount/view
|
||||
{:current-screen-id :screen/wallet.bridge-input-amount
|
||||
:button-one-label (i18n/label :t/review-bridge)
|
||||
:button-one-props {:icon-left :i/bridge}
|
||||
:enabled-from-chain-ids (rf/sub
|
||||
[:wallet/bridge-from-chain-ids])
|
||||
:from-enabled-networks (rf/sub [:wallet/bridge-from-networks])
|
||||
:on-confirm (fn [amount]
|
||||
(rf/dispatch [:wallet/set-token-amount-to-bridge
|
||||
{:amount amount
|
||||
:stack-id :screen/wallet.bridge-input-amount}]))
|
||||
:on-navigate-back (fn []
|
||||
(rf/dispatch [:wallet/clean-disabled-from-networks])
|
||||
(rf/dispatch [:wallet/clean-send-amount]))}]])
|
||||
{:current-screen-id :screen/wallet.bridge-input-amount
|
||||
:button-one-label (i18n/label :t/review-bridge)
|
||||
:button-one-props {:icon-left :i/bridge}
|
||||
:on-confirm (fn [amount]
|
||||
(rf/dispatch [:wallet/set-token-amount-to-bridge
|
||||
{:amount amount
|
||||
:stack-id :screen/wallet.bridge-input-amount}]))
|
||||
:on-navigate-back (fn []
|
||||
(rf/dispatch [:wallet/clean-disabled-from-networks])
|
||||
(rf/dispatch [:wallet/clean-send-amount]))}]])
|
||||
|
||||
@@ -28,6 +28,17 @@
|
||||
(log/debug "unsupported collectible file type:" (or collectible-type "Unknown type"))
|
||||
false)))
|
||||
|
||||
(defn total-owned-collectible
|
||||
([ownership]
|
||||
(total-owned-collectible ownership false))
|
||||
([ownership address]
|
||||
(reduce (fn [acc item]
|
||||
(if (or (not address) (= (:address item) address))
|
||||
(+ acc (js/parseInt (:balance item)))
|
||||
acc))
|
||||
0
|
||||
ownership)))
|
||||
|
||||
(defn collectible-owned-counter
|
||||
[total]
|
||||
(when (> total 1) (str "x" total)))
|
||||
|
||||
@@ -160,9 +160,6 @@
|
||||
collectible-owner (rf/sub [:wallet/collectible-details-owner collectible])
|
||||
aspect-ratio (rf/sub [:wallet/collectible-aspect-ratio])
|
||||
gradient-color (rf/sub [:wallet/collectible-gradient-color])
|
||||
total-owned (rf/sub [:wallet/total-owned-collectible
|
||||
(:ownership collectible)
|
||||
(:address collectible-owner)])
|
||||
{:keys [id
|
||||
preview-url
|
||||
collection-data
|
||||
@@ -185,7 +182,10 @@
|
||||
:image-height 300
|
||||
:id token-id
|
||||
:header collectible-name
|
||||
:description collection-name}]
|
||||
:description collection-name}
|
||||
total-owned (utils/total-owned-collectible
|
||||
(:ownership collectible)
|
||||
(:address collectible-owner))]
|
||||
[rn/view {:style style/container}
|
||||
[rn/view
|
||||
[gradient-layer preview-uri]
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
(ns status-im.contexts.wallet.common.activity-tab.view
|
||||
(:require
|
||||
[clojure.string :as string]
|
||||
[quo.core :as quo]
|
||||
[quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[status-im.common.resources :as resources]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.contexts.shell.jump-to.constants :as jump-to.constants]
|
||||
[status-im.contexts.wallet.common.empty-tab.view :as empty-tab]
|
||||
[utils.i18n :as i18n]
|
||||
@@ -82,10 +80,6 @@
|
||||
; :network-logo network-logo}
|
||||
; :blur? false}])
|
||||
|
||||
(defn- section-header
|
||||
[{:keys [title]}]
|
||||
[quo/divider-date title])
|
||||
|
||||
(defn activity-item
|
||||
[{:keys [transaction] :as activity}]
|
||||
(case transaction
|
||||
@@ -94,63 +88,20 @@
|
||||
;; :mint [mint-activity activity]
|
||||
nil))
|
||||
|
||||
(defn- pressable-text
|
||||
[{:keys [on-press text]}]
|
||||
[rn/text
|
||||
{:style {:text-decoration-line :underline}
|
||||
:on-press on-press}
|
||||
text])
|
||||
|
||||
(defn view
|
||||
[]
|
||||
(let [theme (quo.theme/use-theme)
|
||||
address (rf/sub [:wallet/current-viewing-account-address])
|
||||
activity-list (rf/sub [:wallet/activities-for-current-viewing-account])
|
||||
open-eth-chain-explorer (rn/use-callback
|
||||
#(rf/dispatch [:wallet/navigate-to-chain-explorer
|
||||
{:address address
|
||||
:network constants/mainnet-network-name}])
|
||||
[address])
|
||||
open-oeth-chain-explorer (rn/use-callback
|
||||
#(rf/dispatch [:wallet/navigate-to-chain-explorer
|
||||
{:address address
|
||||
:network constants/optimism-network-name}])
|
||||
[address])
|
||||
open-arb-chain-explorer (rn/use-callback
|
||||
#(rf/dispatch [:wallet/navigate-to-chain-explorer
|
||||
{:address address
|
||||
:network constants/arbitrum-network-name}])
|
||||
[address])]
|
||||
[:<>
|
||||
[quo/information-box
|
||||
{:type :informative
|
||||
:icon :i/info
|
||||
:closable? false
|
||||
:style {:margin-horizontal 20 :margin-vertical 8}}
|
||||
[:<>
|
||||
(str (i18n/label :t/wallet-activity-beta-message) " ")
|
||||
[pressable-text
|
||||
{:on-press open-eth-chain-explorer
|
||||
:text (i18n/label :t/etherscan)}]
|
||||
", "
|
||||
[pressable-text
|
||||
{:on-press open-oeth-chain-explorer
|
||||
:text (i18n/label :t/op-explorer)}]
|
||||
(str ", " (string/lower-case (i18n/label :t/or)) " ")
|
||||
[pressable-text
|
||||
{:on-press open-arb-chain-explorer
|
||||
:text (i18n/label :t/arbiscan)}]
|
||||
"."]]
|
||||
(if (empty? activity-list)
|
||||
[empty-tab/view
|
||||
{:title (i18n/label :t/no-activity)
|
||||
:description (i18n/label :t/empty-tab-description)
|
||||
:image (resources/get-themed-image :no-activity theme)}]
|
||||
[rn/section-list
|
||||
{:sections activity-list
|
||||
:sticky-section-headers-enabled false
|
||||
:style {:flex 1
|
||||
:padding-horizontal 8}
|
||||
:content-container-style {:padding-bottom jump-to.constants/floating-shell-button-height}
|
||||
:render-fn activity-item
|
||||
:render-section-header-fn section-header}])]))
|
||||
(let [theme (quo.theme/use-theme)
|
||||
activity-list (rf/sub [:wallet/activities-for-current-viewing-account])]
|
||||
(if (empty? activity-list)
|
||||
[empty-tab/view
|
||||
{:title (i18n/label :t/no-activity)
|
||||
:description (i18n/label :t/empty-tab-description)
|
||||
:image (resources/get-themed-image :no-activity theme)}]
|
||||
[rn/section-list
|
||||
{:sections activity-list
|
||||
:sticky-section-headers-enabled false
|
||||
:style {:flex 1
|
||||
:padding-horizontal 8}
|
||||
:content-container-style {:padding-bottom jump-to.constants/floating-shell-button-height}
|
||||
:render-fn activity-item
|
||||
:render-section-header-fn (fn [{:keys [title]}] [quo/divider-date title])}])))
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
[status-im.contexts.wallet.collectible.utils :as utils]
|
||||
[status-im.contexts.wallet.common.collectibles-tab.style :as style]
|
||||
[status-im.contexts.wallet.common.empty-tab.view :as empty-tab]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
[utils.i18n :as i18n]))
|
||||
|
||||
(defn- collectible-item
|
||||
[{:keys [preview-url collection-data collectible-data total-owned on-press on-long-press]
|
||||
@@ -62,16 +61,15 @@
|
||||
;; TODO: https://github.com/status-im/status-mobile/issues/20137
|
||||
;; 1. If possible, move `collectibles-data` calculation to a subscription
|
||||
;; 2. Optimization: do not recalculate all the collectibles, process only the new ones
|
||||
(let [collectibles-data (map-indexed
|
||||
(fn [index {:keys [ownership] :as collectible}]
|
||||
(let [total-owned (rf/sub [:wallet/total-owned-collectible ownership
|
||||
current-account-address])]
|
||||
(assoc collectible
|
||||
:total-owned total-owned
|
||||
:on-long-press on-collectible-long-press
|
||||
:on-press on-collectible-press
|
||||
:collectible-index index)))
|
||||
collectibles)]
|
||||
(let [collectibles-data (map-indexed (fn [index {:keys [ownership] :as collectible}]
|
||||
(assoc collectible
|
||||
:total-owned (utils/total-owned-collectible
|
||||
ownership
|
||||
current-account-address)
|
||||
:on-long-press on-collectible-long-press
|
||||
:on-press on-collectible-press
|
||||
:collectible-index index))
|
||||
collectibles)]
|
||||
[rn/flat-list
|
||||
{:data collectibles-data
|
||||
:style {:flex 1}
|
||||
|
||||
@@ -16,14 +16,14 @@
|
||||
:right-icon :i/external})
|
||||
|
||||
(defn- action-send
|
||||
[send-params entry-point]
|
||||
[send-params]
|
||||
{:icon :i/send
|
||||
:accessibility-label :send
|
||||
:label (i18n/label :t/send)
|
||||
:on-press (fn []
|
||||
(rf/dispatch [:hide-bottom-sheet])
|
||||
(rf/dispatch [:wallet/clean-send-data])
|
||||
(rf/dispatch [:wallet/set-token-to-send send-params entry-point]))})
|
||||
(rf/dispatch [:wallet/set-token-to-send send-params]))})
|
||||
|
||||
(defn- action-receive
|
||||
[selected-account?]
|
||||
@@ -66,41 +66,36 @@
|
||||
:on-press #(js/alert "to be implemented")})
|
||||
|
||||
(defn token-value-drawer
|
||||
[token watch-only? entry-point]
|
||||
[token watch-only?]
|
||||
(let [token-symbol (:token token)
|
||||
token-data (first (rf/sub [:wallet/current-viewing-account-tokens-filtered
|
||||
token-symbol]))
|
||||
selected-account? (rf/sub [:wallet/current-viewing-account-address])
|
||||
token-owners (rf/sub [:wallet/operable-addresses-with-token-symbol token-symbol])
|
||||
send-or-bridge-params (if selected-account?
|
||||
{:token token-data
|
||||
:stack-id :screen/wallet.accounts
|
||||
:start-flow? true
|
||||
:owners token-owners}
|
||||
:start-flow? true}
|
||||
{:token-symbol token-symbol
|
||||
:stack-id :wallet-stack
|
||||
:start-flow? true
|
||||
:owners token-owners})]
|
||||
:start-flow? true})]
|
||||
[quo/action-drawer
|
||||
[(cond->> [(when (ff/enabled? ::ff/wallet.assets-modal-manage-tokens)
|
||||
(action-manage-tokens watch-only?))
|
||||
(when (ff/enabled? ::ff/wallet.assets-modal-hide)
|
||||
(action-hide))]
|
||||
(not watch-only?) (concat [(action-buy)
|
||||
(when (seq token-owners)
|
||||
(action-send send-or-bridge-params entry-point))
|
||||
(action-send send-or-bridge-params)
|
||||
(action-receive selected-account?)
|
||||
(when (ff/enabled? ::ff/wallet.swap) (action-swap))
|
||||
(when (seq (seq token-owners))
|
||||
(action-bridge send-or-bridge-params))]))]]))
|
||||
(action-bridge send-or-bridge-params)]))]]))
|
||||
|
||||
(defn view
|
||||
[item _ _ {:keys [watch-only? entry-point]}]
|
||||
[item _ _ {:keys [watch-only?]}]
|
||||
[quo/token-value
|
||||
(cond-> item
|
||||
(or (not watch-only?) (ff/enabled? ::ff/wallet.long-press-watch-only-asset))
|
||||
(assoc :on-long-press
|
||||
#(rf/dispatch
|
||||
[:show-bottom-sheet
|
||||
{:content (fn [] [token-value-drawer item watch-only? entry-point])
|
||||
{:content (fn [] [token-value-drawer item watch-only?])
|
||||
:selected-item (fn [] [quo/token-value item])}])))])
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
(ns status-im.contexts.wallet.connected-dapps.view
|
||||
(:require
|
||||
[quo.core :as quo]
|
||||
[quo.foundations.colors :as colors]
|
||||
[quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[status-im.common.plus-button.view :as plus-button]
|
||||
[status-im.common.resources :as resources]
|
||||
[status-im.contexts.wallet.connected-dapps.disconnect-dapp.view :as disconnect-dapp]
|
||||
[status-im.contexts.wallet.connected-dapps.style :as style]
|
||||
[status-im.contexts.wallet.wallet-connect.core :as core]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]
|
||||
[utils.string]))
|
||||
[quo.core :as quo]
|
||||
[quo.foundations.colors :as colors]
|
||||
[quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[status-im.common.plus-button.view :as plus-button]
|
||||
[status-im.common.resources :as resources]
|
||||
[status-im.contexts.wallet.connected-dapps.disconnect-dapp.view :as disconnect-dapp]
|
||||
[status-im.contexts.wallet.connected-dapps.style :as style]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn- on-disconnect
|
||||
[wallet-account {:keys [name topic]}]
|
||||
@@ -86,8 +84,7 @@
|
||||
{:keys [color] :as wallet-account} (rf/sub [:wallet/current-viewing-account])
|
||||
sessions (rf/sub
|
||||
[:wallet-connect/sessions-for-current-account])
|
||||
theme (quo.theme/use-theme)
|
||||
customization-color (rf/sub [:profile/customization-color])]
|
||||
theme (quo.theme/use-theme)]
|
||||
[rn/view {:flex 1}
|
||||
[header
|
||||
{:title (i18n/label :t/connected-dapps)
|
||||
@@ -107,13 +104,11 @@
|
||||
:content-container-style (style/dapps-list theme)
|
||||
:render-fn (fn [{:keys [topic pairingTopic name url iconUrl]}]
|
||||
[quo/dapp
|
||||
{:dapp {:avatar (core/compute-dapp-icon-path iconUrl
|
||||
url)
|
||||
:name (core/compute-dapp-name name url)
|
||||
:value url
|
||||
:topic topic
|
||||
:pairing-topic pairingTopic
|
||||
:customization-color customization-color}
|
||||
{:dapp {:avatar iconUrl
|
||||
:name name
|
||||
:value url
|
||||
:topic topic
|
||||
:pairing-topic pairingTopic}
|
||||
:accessibility-label (str "dapp-" topic)
|
||||
:state :default
|
||||
:action :icon
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
(ns status-im.contexts.wallet.data-store-test
|
||||
(:require
|
||||
[cljs.test :refer-macros [deftest is testing]]
|
||||
[matcher-combinators.matchers :as matchers]
|
||||
matcher-combinators.test
|
||||
[status-im.contexts.wallet.data-store :as sut]))
|
||||
|
||||
@@ -161,73 +162,82 @@
|
||||
(deftest reconcile-keypairs-test
|
||||
(testing "reconcile-keypairs represents updated key pairs and accounts"
|
||||
(is
|
||||
(match-strict?
|
||||
{:removed-keypair-ids #{}
|
||||
:removed-account-addresses #{}
|
||||
:updated-accounts-by-address {"1x123" (merge account
|
||||
{:key-uid "0x123"
|
||||
:address "1x123"})
|
||||
"1x456" (merge account
|
||||
{:key-uid "0x456"
|
||||
:address "1x456"
|
||||
:operable? false
|
||||
:operable :no})}
|
||||
:updated-keypairs-by-id {"0x123" {:key-uid "0x123"
|
||||
:type :seed
|
||||
:lowest-operability :fully
|
||||
:accounts [(merge account
|
||||
{:key-uid "0x123"
|
||||
:address "1x123"})]}
|
||||
"0x456" {:key-uid "0x456"
|
||||
:type :key
|
||||
:lowest-operability :no
|
||||
:accounts [(merge account
|
||||
{:key-uid "0x456"
|
||||
:address "1x456"
|
||||
:operable? false
|
||||
:operable :no})]}}}
|
||||
(match?
|
||||
(matchers/match-with
|
||||
[set? matchers/set-equals
|
||||
map? matchers/equals]
|
||||
{:removed-keypair-ids #{}
|
||||
:removed-account-addresses #{}
|
||||
:updated-accounts-by-address {"1x123" (merge account
|
||||
{:key-uid "0x123"
|
||||
:address "1x123"})
|
||||
"1x456" (merge account
|
||||
{:key-uid "0x456"
|
||||
:address "1x456"
|
||||
:operable? false
|
||||
:operable :no})}
|
||||
:updated-keypairs-by-id {"0x123" {:key-uid "0x123"
|
||||
:type :seed
|
||||
:lowest-operability :fully
|
||||
:accounts [(merge account
|
||||
{:key-uid "0x123"
|
||||
:address "1x123"})]}
|
||||
"0x456" {:key-uid "0x456"
|
||||
:type :key
|
||||
:lowest-operability :no
|
||||
:accounts [(merge account
|
||||
{:key-uid "0x456"
|
||||
:address "1x456"
|
||||
:operable? false
|
||||
:operable :no})]}}})
|
||||
(sut/reconcile-keypairs [raw-keypair-seed-phrase
|
||||
raw-keypair-private-key]))))
|
||||
(testing "reconcile-keypairs represents removed key pairs and accounts"
|
||||
(is
|
||||
(match-strict?
|
||||
{:removed-keypair-ids #{"0x456"}
|
||||
:removed-account-addresses #{"1x456"}
|
||||
:updated-accounts-by-address {"1x123" (merge account
|
||||
{:key-uid "0x123"
|
||||
:address "1x123"})}
|
||||
:updated-keypairs-by-id {"0x123" {:key-uid "0x123"
|
||||
:type :seed
|
||||
:lowest-operability :fully
|
||||
:accounts [(merge account
|
||||
{:key-uid "0x123"
|
||||
:address "1x123"})]}}}
|
||||
(match?
|
||||
(matchers/match-with
|
||||
[set? matchers/set-equals
|
||||
map? matchers/equals]
|
||||
{:removed-keypair-ids #{"0x456"}
|
||||
:removed-account-addresses #{"1x456"}
|
||||
:updated-accounts-by-address {"1x123" (merge account
|
||||
{:key-uid "0x123"
|
||||
:address "1x123"})}
|
||||
:updated-keypairs-by-id {"0x123" {:key-uid "0x123"
|
||||
:type :seed
|
||||
:lowest-operability :fully
|
||||
:accounts [(merge account
|
||||
{:key-uid "0x123"
|
||||
:address "1x123"})]}}})
|
||||
(sut/reconcile-keypairs [raw-keypair-seed-phrase
|
||||
(assoc raw-keypair-private-key :removed true)]))))
|
||||
(testing "reconcile-keypairs ignores chat accounts inside updated accounts"
|
||||
(is
|
||||
(match-strict?
|
||||
{:removed-keypair-ids #{}
|
||||
:removed-account-addresses #{}
|
||||
:updated-accounts-by-address {"2x000" (merge account
|
||||
{:key-uid "0x000"
|
||||
:address "2x000"
|
||||
:chat false
|
||||
:wallet true
|
||||
:default-account? true})}
|
||||
:updated-keypairs-by-id {"0x000" {:key-uid "0x000"
|
||||
:type :profile
|
||||
:lowest-operability :fully
|
||||
:accounts [(merge account
|
||||
{:key-uid "0x000"
|
||||
:address "1x000"
|
||||
:chat true
|
||||
:wallet false
|
||||
:default-account? false})
|
||||
(merge account
|
||||
{:key-uid "0x000"
|
||||
:address "2x000"
|
||||
:chat false
|
||||
:wallet true
|
||||
:default-account? true})]}}}
|
||||
(match?
|
||||
(matchers/match-with
|
||||
[set? matchers/set-equals
|
||||
map? matchers/equals]
|
||||
{:removed-keypair-ids #{}
|
||||
:removed-account-addresses #{}
|
||||
:updated-accounts-by-address {"2x000" (merge account
|
||||
{:key-uid "0x000"
|
||||
:address "2x000"
|
||||
:chat false
|
||||
:wallet true
|
||||
:default-account? true})}
|
||||
:updated-keypairs-by-id {"0x000" {:key-uid "0x000"
|
||||
:type :profile
|
||||
:lowest-operability :fully
|
||||
:accounts [(merge account
|
||||
{:key-uid "0x000"
|
||||
:address "1x000"
|
||||
:chat true
|
||||
:wallet false
|
||||
:default-account? false})
|
||||
(merge account
|
||||
{:key-uid "0x000"
|
||||
:address "2x000"
|
||||
:chat false
|
||||
:wallet true
|
||||
:default-account? true})]}}})
|
||||
(sut/reconcile-keypairs [raw-keypair-profile])))))
|
||||
|
||||
@@ -65,21 +65,10 @@
|
||||
{:db (assoc-in db [:wallet :current-viewing-account-address] address)}))
|
||||
|
||||
(rf/reg-event-fx :wallet/clean-current-viewing-account
|
||||
(fn [{:keys [db]} [ignore-just-completed-transaction?]]
|
||||
(let [{:keys [entry-point just-completed-transaction?]} (-> db :wallet :ui :send)
|
||||
entry-point-wallet-home? (= entry-point :wallet-stack)]
|
||||
{:db (cond-> db
|
||||
(and (not entry-point)
|
||||
(not ignore-just-completed-transaction?)
|
||||
(not just-completed-transaction?))
|
||||
(update :wallet dissoc :current-viewing-account-address)
|
||||
|
||||
entry-point-wallet-home?
|
||||
(update-in [:wallet :ui :send] dissoc :entry-point)
|
||||
|
||||
(and entry-point-wallet-home?
|
||||
(not just-completed-transaction?))
|
||||
(update :wallet dissoc :current-viewing-account-address))})))
|
||||
(fn [{:keys [db]}]
|
||||
(let [just-completed-transaction? (get-in db [:wallet :ui :send :just-completed-transaction?])]
|
||||
(when-not just-completed-transaction?
|
||||
{:db (update db :wallet dissoc :current-viewing-account-address)}))))
|
||||
|
||||
(rf/reg-event-fx :wallet/close-account-page
|
||||
(fn [{:keys [db]}]
|
||||
|
||||
@@ -18,5 +18,4 @@
|
||||
[rn/flat-list
|
||||
{:render-fn token-value/view
|
||||
:data tokens
|
||||
:render-data {:entry-point :wallet-stack}
|
||||
:content-container-style style/list-container}])))
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
(defn view
|
||||
[{:keys [selected-tab]}]
|
||||
(let [collectible-list (rf/sub [:wallet/owned-collectibles-list-in-selected-networks])
|
||||
(let [collectible-list (rf/sub [:wallet/all-collectibles-list-in-selected-networks])
|
||||
request-collectibles #(rf/dispatch
|
||||
[:wallet/request-collectibles-for-all-accounts {}])]
|
||||
[rn/view {:style style/container}
|
||||
|
||||
@@ -193,35 +193,25 @@
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet/set-token-to-send
|
||||
(fn [{:keys [db]} [{:keys [token-symbol token stack-id start-flow? owners]} entry-point]]
|
||||
(fn [{:keys [db]} [{:keys [token-symbol token stack-id start-flow?]}]]
|
||||
;; `token` is a map extracted from the sender, but in the wallet home page we don't know the
|
||||
;; sender yet, so we only provide the `token-symbol`, later in
|
||||
;; `:wallet/select-from-account` the `token` key will be set.
|
||||
(let [{:keys [networks]} token
|
||||
receiver-networks (get-in db [:wallet :ui :send :receiver-networks])
|
||||
token-networks-ids (map :chain-id networks)
|
||||
unsupported-token? (not-any? (set receiver-networks) token-networks-ids)
|
||||
unique-owner (when (= (count owners) 1)
|
||||
(first owners))
|
||||
unique-owner-tokens (get-in db [:wallet :accounts unique-owner :tokens])
|
||||
token-data (or token
|
||||
(when (and token-symbol unique-owner)
|
||||
(some #(when (= (:symbol %) token-symbol) %)
|
||||
unique-owner-tokens)))]
|
||||
(when (or token-data token-symbol)
|
||||
(let [{token-networks :networks} token
|
||||
receiver-networks (get-in db [:wallet :ui :send :receiver-networks])
|
||||
token-networks-ids (mapv #(:chain-id %) token-networks)
|
||||
token-not-supported-in-receiver-networks? (not-any? (set receiver-networks)
|
||||
token-networks-ids)]
|
||||
(when (or token token-symbol)
|
||||
{:db (cond-> db
|
||||
:always (update-in [:wallet :ui :send]
|
||||
#(-> %
|
||||
(dissoc :collectible)
|
||||
(assoc :token-not-supported-in-receiver-networks?
|
||||
unsupported-token?)))
|
||||
token-symbol (assoc-in [:wallet :ui :send :token-symbol] token-symbol)
|
||||
token-data (update-in [:wallet :ui :send]
|
||||
#(assoc %
|
||||
:token token-data
|
||||
:token-display-name (:symbol token-data)))
|
||||
unique-owner (assoc-in [:wallet :current-viewing-account-address] unique-owner)
|
||||
entry-point (assoc-in [:wallet :ui :send :entry-point] entry-point))
|
||||
:always (update-in [:wallet :ui :send] dissoc :collectible)
|
||||
:always (assoc-in
|
||||
[:wallet :ui :send :token-not-supported-in-receiver-networks?]
|
||||
token-not-supported-in-receiver-networks?)
|
||||
token (assoc-in [:wallet :ui :send :token] token)
|
||||
token (assoc-in [:wallet :ui :send :token-display-name]
|
||||
(:symbol token))
|
||||
token-symbol (assoc-in [:wallet :ui :send :token-symbol] token-symbol))
|
||||
:fx [[:dispatch [:wallet/clean-suggested-routes]]
|
||||
[:dispatch
|
||||
[:wallet/wizard-navigate-forward
|
||||
@@ -235,9 +225,9 @@
|
||||
(let [{token-networks :networks
|
||||
token-symbol :symbol} token
|
||||
receiver-networks (get-in db [:wallet :ui :send :receiver-networks])
|
||||
token-networks-ids (map :chain-id token-networks)
|
||||
token-not-supported-in-receiver-networks? (not-any? (set receiver-networks)
|
||||
token-networks-ids)]
|
||||
token-networks-ids (mapv #(:chain-id %) token-networks)
|
||||
token-not-supported-in-receiver-networks? (not (some (set receiver-networks)
|
||||
token-networks-ids))]
|
||||
{:db (-> db
|
||||
(assoc-in [:wallet :ui :send :token] token)
|
||||
(assoc-in [:wallet :ui :send :token-display-name] token-symbol)
|
||||
@@ -266,9 +256,7 @@
|
||||
(rf/reg-event-fx
|
||||
:wallet/set-collectible-to-send
|
||||
(fn [{db :db} [{:keys [collectible current-screen start-flow?]}]]
|
||||
(let [viewing-account? (some? (-> db :wallet :current-viewing-account-address))
|
||||
entry-point (when-not viewing-account? :wallet-stack)
|
||||
collection-data (:collection-data collectible)
|
||||
(let [collection-data (:collection-data collectible)
|
||||
collectible-data (:collectible-data collectible)
|
||||
contract-type (:contract-type collectible)
|
||||
tx-type (if (= contract-type constants/wallet-contract-type-erc-1155)
|
||||
@@ -283,7 +271,6 @@
|
||||
|
||||
collectible
|
||||
(str (:name collection-data) " #" collectible-id))
|
||||
owner-address (-> collectible :ownership first :address)
|
||||
collectible-tx (-> db
|
||||
(update-in [:wallet :ui :send] dissoc :token)
|
||||
(assoc-in [:wallet :ui :send :collectible] collectible)
|
||||
@@ -291,14 +278,7 @@
|
||||
(assoc-in [:wallet :ui :send :tx-type] tx-type))
|
||||
recipient-set? (-> db :wallet :ui :send :recipient)]
|
||||
{:db (cond-> collectible-tx
|
||||
:always
|
||||
(assoc-in [:wallet :ui :send :entry-point] entry-point)
|
||||
|
||||
(not viewing-account?)
|
||||
(assoc-in [:wallet :current-viewing-account-address] owner-address)
|
||||
|
||||
one-collectible?
|
||||
(assoc-in [:wallet :ui :send :amount] 1))
|
||||
one-collectible? (assoc-in [:wallet :ui :send :amount] 1))
|
||||
:fx [(when (and one-collectible? recipient-set?)
|
||||
[:dispatch [:wallet/get-suggested-routes {:amount 1}]])
|
||||
[:dispatch
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
{:on-press #(rf/dispatch [:navigate-back])
|
||||
:margin-top (safe-area/get-top)
|
||||
:switcher-type :select-account}]}
|
||||
|
||||
[quo/page-top
|
||||
{:title (i18n/label :t/from-label)
|
||||
:title-accessibility-label :title-label}]
|
||||
|
||||
@@ -146,8 +146,6 @@
|
||||
button-one-props :button-one-props
|
||||
current-screen-id :current-screen-id
|
||||
initial-crypto-currency? :initial-crypto-currency?
|
||||
enabled-from-chain-ids :enabled-from-chain-ids
|
||||
from-enabled-networks :from-enabled-networks
|
||||
:or {initial-crypto-currency? true}}]
|
||||
(let [_ (rn/dismiss-keyboard!)
|
||||
bottom (safe-area/get-bottom)
|
||||
@@ -166,6 +164,9 @@
|
||||
token-decimals :decimals
|
||||
:as
|
||||
token} (rf/sub [:wallet/wallet-send-token])
|
||||
send-enabled-networks (rf/sub [:wallet/wallet-send-enabled-networks])
|
||||
enabled-from-chain-ids (rf/sub
|
||||
[:wallet/wallet-send-enabled-from-chain-ids])
|
||||
send-from-locked-amounts (rf/sub [:wallet/wallet-send-from-locked-amounts])
|
||||
{token-balance :total-balance
|
||||
available-balance :available-balance
|
||||
@@ -354,7 +355,7 @@
|
||||
:currency-symbol currency-symbol
|
||||
:crypto-decimals (min token-decimals 6)
|
||||
:error? (controlled-input/input-error input-state)
|
||||
:networks (seq from-enabled-networks)
|
||||
:networks (seq send-enabled-networks)
|
||||
:title (i18n/label
|
||||
:t/send-limit
|
||||
{:limit (if crypto-currency?
|
||||
@@ -428,4 +429,3 @@
|
||||
(set-just-toggled-mode? false)
|
||||
(set-input-state controlled-input/delete-all)
|
||||
(rf/dispatch [:wallet/clean-suggested-routes]))}]]))
|
||||
|
||||
|
||||
@@ -177,9 +177,7 @@
|
||||
(rf/dispatch [:wallet/clean-selected-collectible])
|
||||
(rf/dispatch [:wallet/clean-send-address])
|
||||
(rf/dispatch [:wallet/clean-disabled-from-networks])
|
||||
(rf/dispatch [:wallet/select-address-tab nil])
|
||||
(rf/dispatch [:wallet/clean-current-viewing-account
|
||||
:ignore-just-complete-transaction]))
|
||||
(rf/dispatch [:wallet/select-address-tab nil]))
|
||||
on-change-tab #(rf/dispatch [:wallet/select-address-tab %])
|
||||
input-value (reagent/atom "")
|
||||
input-focused? (reagent/atom false)]
|
||||
|
||||
@@ -8,12 +8,9 @@
|
||||
(defn view
|
||||
[]
|
||||
[input-amount/view
|
||||
{:current-screen-id :screen/wallet.send-input-amount
|
||||
:button-one-label (i18n/label :t/review-send)
|
||||
:enabled-from-chain-ids (rf/sub
|
||||
[:wallet/wallet-send-enabled-from-chain-ids])
|
||||
:from-enabled-networks (rf/sub [:wallet/wallet-send-enabled-networks])
|
||||
:on-navigate-back (fn []
|
||||
(rf/dispatch [:wallet/clean-disabled-from-networks])
|
||||
(rf/dispatch [:wallet/clean-from-locked-amounts])
|
||||
(rf/dispatch [:wallet/clean-send-amount]))}])
|
||||
{:current-screen-id :screen/wallet.send-input-amount
|
||||
:button-one-label (i18n/label :t/review-send)
|
||||
:on-navigate-back (fn []
|
||||
(rf/dispatch [:wallet/clean-disabled-from-networks])
|
||||
(rf/dispatch [:wallet/clean-from-locked-amounts])
|
||||
(rf/dispatch [:wallet/clean-send-amount]))}])
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
(defn view
|
||||
[]
|
||||
(let [selected-account-address (rf/sub [:wallet/current-viewing-account-address])
|
||||
accounts (rf/sub [:wallet/operable-accounts])]
|
||||
accounts (rf/sub [:wallet/operable-accounts-without-watched-accounts])]
|
||||
[:<>
|
||||
[quo/drawer-top {:title (i18n/label :t/select-account)}]
|
||||
[gesture/flat-list
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
[clojure.string :as string]
|
||||
[native-module.core :as native-module]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.contexts.wallet.common.utils.networks :as networks]
|
||||
[utils.security.core :as security]
|
||||
[utils.string]
|
||||
[utils.transforms :as transforms]))
|
||||
|
||||
(def method-to-screen
|
||||
@@ -102,7 +100,7 @@
|
||||
networks (get-in db [:wallet :networks (if test-mode? :test :prod)])]
|
||||
(mapv #(-> % :chain-id) networks)))
|
||||
|
||||
(defn- add-full-testnet-name
|
||||
(defn add-full-testnet-name
|
||||
"Updates the `:full-name` key with the full testnet name if using testnet `:chain-id`.\n
|
||||
e.g. `{:full-name \"Mainnet\"}` -> `{:full-name \"Mainnet Sepolia\"`}`"
|
||||
[network]
|
||||
@@ -113,12 +111,6 @@
|
||||
constants/goerli-chain-ids (add-testnet-name constants/goerli-full-name)
|
||||
network)))
|
||||
|
||||
(defn chain-id->network-details
|
||||
[chain-id]
|
||||
(-> chain-id
|
||||
(networks/get-network-details)
|
||||
(add-full-testnet-name)))
|
||||
|
||||
(defn event-should-be-handled?
|
||||
[db {:keys [topic]}]
|
||||
(some #(= topic %)
|
||||
@@ -151,23 +143,3 @@
|
||||
account-addresses))
|
||||
accounts))
|
||||
sessions))
|
||||
|
||||
(defn compute-dapp-name
|
||||
"Sometimes dapps have no name or an empty name. Return url as name in that case"
|
||||
[name url]
|
||||
(if (seq name)
|
||||
name
|
||||
(when (seq url)
|
||||
(-> url
|
||||
utils.string/remove-trailing-slash
|
||||
utils.string/remove-http-prefix
|
||||
string/capitalize))))
|
||||
|
||||
(defn compute-dapp-icon-path
|
||||
"Some dapps have icons with relative paths, make paths absolute in those cases, send nil if icon is missing"
|
||||
[icon-path url]
|
||||
(when (and (seq icon-path)
|
||||
(seq url))
|
||||
(if (string/starts-with? icon-path "http")
|
||||
icon-path
|
||||
(str (utils.string/remove-trailing-slash url) icon-path))))
|
||||
|
||||
@@ -50,15 +50,11 @@
|
||||
|
||||
(rf/reg-fx
|
||||
:effects.wallet-connect/approve-session
|
||||
(fn [{:keys [web3-wallet proposal networks accounts on-success on-fail]}]
|
||||
(fn [{:keys [web3-wallet proposal supported-namespaces on-success on-fail]}]
|
||||
(let [{:keys [params id]} proposal
|
||||
approved-namespaces (->> {:eip155
|
||||
{:chains networks
|
||||
:accounts accounts
|
||||
:methods constants/wallet-connect-supported-methods
|
||||
:events constants/wallet-connect-supported-events}}
|
||||
(wallet-connect/build-approved-namespaces
|
||||
params))]
|
||||
approved-namespaces (wallet-connect/build-approved-namespaces
|
||||
params
|
||||
supported-namespaces)]
|
||||
(-> (wallet-connect/approve-session
|
||||
{:web3-wallet web3-wallet
|
||||
:id id
|
||||
|
||||
@@ -13,33 +13,18 @@
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/init
|
||||
(fn [{:keys [db]}]
|
||||
(let [network-status (:network/status db)
|
||||
web3-wallet-missing? (-> db :wallet-connect/web3-wallet boolean not)]
|
||||
(if (and (= network-status :online) web3-wallet-missing?)
|
||||
(do (log/info "Initialising WalletConnect SDK")
|
||||
{:fx [[:effects.wallet-connect/init
|
||||
{:on-success #(rf/dispatch [:wallet-connect/on-init-success %])
|
||||
:on-fail #(rf/dispatch [:wallet-connect/on-init-fail %])}]]})
|
||||
;; NOTE: when offline, fetching persistent sessions only
|
||||
{:fx [[:dispatch [:wallet-connect/fetch-persisted-sessions]]]}))))
|
||||
(fn []
|
||||
{:fx [[:effects.wallet-connect/init
|
||||
{:on-success #(rf/dispatch [:wallet-connect/on-init-success %])
|
||||
:on-fail #(rf/dispatch [:wallet-connect/on-init-fail %])}]]}))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/on-init-success
|
||||
(fn [{:keys [db]} [web3-wallet]]
|
||||
(log/info "WalletConnect SDK initialisation successful")
|
||||
{:db (assoc db :wallet-connect/web3-wallet web3-wallet)
|
||||
:fx [[:dispatch [:wallet-connect/register-event-listeners]]
|
||||
[:dispatch [:wallet-connect/fetch-persisted-sessions]]]}))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/reload-on-network-change
|
||||
(fn [{:keys [db]} [is-connected?]]
|
||||
(let [logged-in? (-> db :profile/profile boolean)]
|
||||
(when (and is-connected? logged-in?)
|
||||
(log/info "Re-Initialising WalletConnect SDK due to network change")
|
||||
{:fx [[:dispatch [:wallet-connect/init]]]}))))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/register-event-listeners
|
||||
(fn [{:keys [db]}]
|
||||
@@ -79,7 +64,6 @@
|
||||
(if (and (not-empty session-networks) required-networks-supported?)
|
||||
{:db (update db
|
||||
:wallet-connect/current-proposal assoc
|
||||
:response-sent? false
|
||||
:request proposal
|
||||
:session-networks session-networks
|
||||
:address (or current-viewing-address
|
||||
@@ -93,12 +77,12 @@
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/session-networks-unsupported
|
||||
(fn [{:keys [db]} [proposal]]
|
||||
(fn [_ [proposal]]
|
||||
(let [{:keys [name]} (wallet-connect-core/get-session-dapp-metadata proposal)]
|
||||
{:fx [[:dispatch
|
||||
[:toasts/upsert
|
||||
{:type :negative
|
||||
:theme (:theme db)
|
||||
:theme :dark
|
||||
:text (i18n/label :t/wallet-connect-networks-not-supported {:dapp name})}]]]})))
|
||||
|
||||
(rf/reg-event-fx
|
||||
@@ -132,20 +116,17 @@
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/disconnect-dapp
|
||||
(fn [{:keys [db]} [{:keys [topic on-success on-fail]}]]
|
||||
(let [web3-wallet (get db :wallet-connect/web3-wallet)
|
||||
network-status (:network/status db)]
|
||||
(if (= network-status :online)
|
||||
{:fx [[:effects.wallet-connect/disconnect
|
||||
{:web3-wallet web3-wallet
|
||||
:topic topic
|
||||
:reason (wallet-connect/get-sdk-error
|
||||
constants/wallet-connect-user-disconnected-reason-key)
|
||||
:on-fail on-fail
|
||||
:on-success (fn []
|
||||
(rf/dispatch [:wallet-connect/disconnect-session topic])
|
||||
(when on-success
|
||||
(on-success)))}]]}
|
||||
{:fx [[:dispatch [:wallet-connect/no-internet-toast]]]}))))
|
||||
(let [web3-wallet (get db :wallet-connect/web3-wallet)]
|
||||
{:fx [[:effects.wallet-connect/disconnect
|
||||
{:web3-wallet web3-wallet
|
||||
:topic topic
|
||||
:reason (wallet-connect/get-sdk-error
|
||||
constants/wallet-connect-user-disconnected-reason-key)
|
||||
:on-fail on-fail
|
||||
:on-success (fn []
|
||||
(rf/dispatch [:wallet-connect/disconnect-session topic])
|
||||
(when on-success
|
||||
(on-success)))}]]})))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/pair
|
||||
@@ -160,66 +141,51 @@
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/approve-session
|
||||
(fn [{:keys [db]}]
|
||||
(let [web3-wallet (get db :wallet-connect/web3-wallet)
|
||||
current-proposal (get-in db [:wallet-connect/current-proposal :request])
|
||||
session-networks (->> (get-in db [:wallet-connect/current-proposal :session-networks])
|
||||
(map wallet-connect-core/chain-id->eip155)
|
||||
vec)
|
||||
current-address (get-in db [:wallet-connect/current-proposal :address])
|
||||
accounts (-> (partial wallet-connect-core/format-eip155-address current-address)
|
||||
(map session-networks))
|
||||
network-status (:network/status db)
|
||||
expiry (get-in current-proposal [:params :expiryTimestamp])]
|
||||
(if (= network-status :online)
|
||||
{:db (assoc-in db [:wallet-connect/current-proposal :response-sent?] true)
|
||||
:fx [(if (wc-utils/timestamp-expired? expiry)
|
||||
[:dispatch
|
||||
[:toasts/upsert
|
||||
{:id :wallet-connect-proposal-expired
|
||||
:type :negative
|
||||
:text (i18n/label :t/wallet-connect-proposal-expired)}]]
|
||||
[:effects.wallet-connect/approve-session
|
||||
{:web3-wallet web3-wallet
|
||||
:proposal current-proposal
|
||||
:networks session-networks
|
||||
:accounts accounts
|
||||
:on-success (fn [approved-session]
|
||||
(log/info "Wallet Connect session approved")
|
||||
(rf/dispatch [:wallet-connect/reset-current-session-proposal])
|
||||
(rf/dispatch [:wallet-connect/persist-session
|
||||
approved-session]))
|
||||
:on-fail (fn [error]
|
||||
(log/error "Wallet Connect session approval failed"
|
||||
{:error error
|
||||
:event :wallet-connect/approve-session})
|
||||
(rf/dispatch
|
||||
[:wallet-connect/reset-current-session-proposal]))}])
|
||||
[:dispatch [:dismiss-modal :screen/wallet.wallet-connect-session-proposal]]]}
|
||||
{:fx [[:dispatch [:wallet-connect/no-internet-toast]]]}))))
|
||||
(let [web3-wallet (get db :wallet-connect/web3-wallet)
|
||||
current-proposal (get-in db [:wallet-connect/current-proposal :request])
|
||||
session-networks (->> (get-in db [:wallet-connect/current-proposal :session-networks])
|
||||
(map wallet-connect-core/chain-id->eip155)
|
||||
vec)
|
||||
current-address (get-in db [:wallet-connect/current-proposal :address])
|
||||
accounts (-> (partial wallet-connect-core/format-eip155-address current-address)
|
||||
(map session-networks))
|
||||
supported-namespaces (clj->js {:eip155
|
||||
{:chains session-networks
|
||||
:methods constants/wallet-connect-supported-methods
|
||||
:events constants/wallet-connect-supported-events
|
||||
:accounts accounts}})]
|
||||
{:fx [[:effects.wallet-connect/approve-session
|
||||
{:web3-wallet web3-wallet
|
||||
:proposal current-proposal
|
||||
:supported-namespaces supported-namespaces
|
||||
:on-success (fn [approved-session]
|
||||
(log/info "Wallet Connect session approved")
|
||||
(rf/dispatch [:wallet-connect/reset-current-session-proposal])
|
||||
(rf/dispatch [:wallet-connect/persist-session approved-session]))
|
||||
:on-fail (fn [error]
|
||||
(log/error "Wallet Connect session approval failed"
|
||||
{:error error
|
||||
:event :wallet-connect/approve-session})
|
||||
(rf/dispatch
|
||||
[:wallet-connect/reset-current-session-proposal]))}]
|
||||
[:dispatch [:dismiss-modal :screen/wallet.wallet-connect-session-proposal]]]})))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/on-scan-connection
|
||||
(fn [{:keys [db]} [scanned-text]]
|
||||
(let [network-status (:network/status db)
|
||||
parsed-uri (wallet-connect/parse-uri scanned-text)
|
||||
(fn [_ [scanned-text]]
|
||||
(let [parsed-uri (wallet-connect/parse-uri scanned-text)
|
||||
version (:version parsed-uri)
|
||||
valid-wc-uri? (wc-utils/valid-wc-uri? parsed-uri)
|
||||
expired? (-> parsed-uri
|
||||
:expiryTimestamp
|
||||
wc-utils/timestamp-expired?)
|
||||
version-supported? (wc-utils/version-supported? version)]
|
||||
(if (or (not valid-wc-uri?)
|
||||
(not version-supported?)
|
||||
(= network-status :offline)
|
||||
expired?)
|
||||
(if (or (not valid-wc-uri?) expired? (not version-supported?))
|
||||
{:fx [[:dispatch
|
||||
[:toasts/upsert
|
||||
{:type :negative
|
||||
:theme :dark
|
||||
:text (cond (= network-status :offline)
|
||||
(i18n/label :t/wallet-connect-no-internet-warning)
|
||||
|
||||
(not valid-wc-uri?)
|
||||
:text (cond (not valid-wc-uri?)
|
||||
(i18n/label :t/wallet-connect-wrong-qr)
|
||||
|
||||
expired?
|
||||
@@ -227,10 +193,7 @@
|
||||
|
||||
(not version-supported?)
|
||||
(i18n/label :t/wallet-connect-version-not-supported
|
||||
{:version version})
|
||||
|
||||
:else
|
||||
(i18n/label :t/something-went-wrong))}]]]}
|
||||
{:version version}))}]]]}
|
||||
{:fx [[:dispatch [:wallet-connect/pair scanned-text]]]}))))
|
||||
|
||||
;; We first load sessions from database, then we initiate a call to Wallet Connect SDK and
|
||||
@@ -273,18 +236,16 @@
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/fetch-persisted-sessions-success
|
||||
(fn [{:keys [db]} [sessions]]
|
||||
(let [network-status (:network/status db)
|
||||
sessions' (mapv (fn [{:keys [sessionJson] :as session}]
|
||||
(assoc session
|
||||
:accounts
|
||||
(-> sessionJson
|
||||
types/json->clj
|
||||
:namespaces
|
||||
:eip155
|
||||
:accounts)))
|
||||
sessions)]
|
||||
{:fx [(when (= network-status :online)
|
||||
[:dispatch [:wallet-connect/fetch-active-sessions]])]
|
||||
(let [sessions' (mapv (fn [{:keys [sessionJson] :as session}]
|
||||
(assoc session
|
||||
:accounts
|
||||
(-> sessionJson
|
||||
types/json->clj
|
||||
:namespaces
|
||||
:eip155
|
||||
:accounts)))
|
||||
sessions)]
|
||||
{:fx [[:dispatch [:wallet-connect/fetch-active-sessions]]]
|
||||
:db (assoc db :wallet-connect/sessions sessions')})))
|
||||
|
||||
(rf/reg-event-fx
|
||||
@@ -295,14 +256,14 @@
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/fetch-persisted-sessions
|
||||
(fn [{:keys [now]} _]
|
||||
(let [current-timestamp (quot now 1000)]
|
||||
{:fx [[:json-rpc/call
|
||||
[{:method "wallet_getWalletConnectActiveSessions"
|
||||
;; NOTE: This is the activeSince timestamp to avoid expired sessions
|
||||
:params [current-timestamp]
|
||||
:on-success [:wallet-connect/fetch-persisted-sessions-success]
|
||||
:on-error [:wallet-connect/fetch-persisted-sessions-fail]}]]]})))
|
||||
(fn [_ _]
|
||||
{:fx [[:json-rpc/call
|
||||
[{:method "wallet_getWalletConnectActiveSessions"
|
||||
;; This is the activeSince timestamp to avoid expired sessions
|
||||
;; 0 means, return everything
|
||||
:params [0]
|
||||
:on-success [:wallet-connect/fetch-persisted-sessions-success]
|
||||
:on-error [:wallet-connect/fetch-persisted-sessions-fail]}]]]}))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/persist-session
|
||||
@@ -329,12 +290,3 @@
|
||||
:params [topic]
|
||||
:on-success #(log/info "Wallet Connect session disconnected")
|
||||
:on-error #(log/info "Wallet Connect session persistence failed" %)}]]]}))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/no-internet-toast
|
||||
(fn [{:keys [db]}]
|
||||
{:fx [[:dispatch
|
||||
[:toasts/upsert
|
||||
{:type :negative
|
||||
:theme (:theme db)
|
||||
:text (i18n/label :t/wallet-connect-no-internet-warning)}]]]}))
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
(ns status-im.contexts.wallet.wallet-connect.modals.common.footer.view
|
||||
(:require [quo.core :as quo]
|
||||
[quo.foundations.colors :as colors]
|
||||
[quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[status-im.common.standard-authentication.core :as standard-authentication]
|
||||
[status-im.contexts.wallet.wallet-connect.modals.common.footer.style :as style]
|
||||
@@ -14,34 +13,23 @@
|
||||
(rf/dispatch [:wallet-connect/respond-current-session password]))
|
||||
|
||||
(defn view
|
||||
[{:keys [warning-label slide-button-text error-text]} & children]
|
||||
(let [{:keys [customization-color]} (rf/sub [:wallet-connect/current-request-account-details])
|
||||
offline? (rf/sub [:network/offline?])
|
||||
theme (quo.theme/use-theme)]
|
||||
[:<>
|
||||
(when (or offline? error-text)
|
||||
[quo/alert-banner
|
||||
{:action? false
|
||||
:text (if offline?
|
||||
(i18n/label :t/wallet-connect-no-internet-warning)
|
||||
error-text)}])
|
||||
[rn/view {:style style/content-container}
|
||||
(into [rn/view
|
||||
{:style style/data-items-container}]
|
||||
children)
|
||||
[rn/view {:style style/auth-container}
|
||||
[standard-authentication/slide-button
|
||||
{:size :size-48
|
||||
:track-text slide-button-text
|
||||
:disabled? (or offline? (seq error-text))
|
||||
:customization-color customization-color
|
||||
:on-auth-success on-auth-success
|
||||
:auth-button-label (i18n/label :t/confirm)}]]
|
||||
[rn/view {:style style/warning-container}
|
||||
[quo/text
|
||||
{:size :paragraph-2
|
||||
:style {:color (if (= theme :dark)
|
||||
colors/white-opa-70
|
||||
colors/neutral-80-opa-70)}
|
||||
:weight :medium}
|
||||
warning-label]]]]))
|
||||
[{:keys [warning-label slide-button-text disabled?]} & children]
|
||||
(let [{:keys [customization-color]} (rf/sub [:wallet-connect/current-request-account-details])]
|
||||
[rn/view {:style style/content-container}
|
||||
(into [rn/view
|
||||
{:style style/data-items-container}]
|
||||
children)
|
||||
[rn/view {:style style/auth-container}
|
||||
[standard-authentication/slide-button
|
||||
{:size :size-48
|
||||
:track-text slide-button-text
|
||||
:disabled? disabled?
|
||||
:customization-color customization-color
|
||||
:on-auth-success on-auth-success
|
||||
:auth-button-label (i18n/label :t/confirm)}]]
|
||||
[rn/view {:style style/warning-container}
|
||||
[quo/text
|
||||
{:size :paragraph-2
|
||||
:style {:color colors/neutral-80-opa-70}
|
||||
:weight :medium}
|
||||
warning-label]]]))
|
||||
|
||||
@@ -1,23 +1,15 @@
|
||||
(ns status-im.contexts.wallet.wallet-connect.modals.common.header.style
|
||||
(:require [quo.foundations.typography :as typography]))
|
||||
|
||||
(def ^:private line-height (:line-height typography/heading-1))
|
||||
(ns status-im.contexts.wallet.wallet-connect.modals.common.header.style)
|
||||
|
||||
(def header-container
|
||||
{:padding-vertical 12
|
||||
:justify-content :flex-start
|
||||
:align-items :center
|
||||
:flex-direction :row
|
||||
:flex-wrap :wrap
|
||||
:row-gap 2})
|
||||
:flex-direction :column
|
||||
:align-items :stretch})
|
||||
|
||||
(def word-container
|
||||
{:height line-height
|
||||
:justify-content :center})
|
||||
(def header-dapp-name
|
||||
{:flex-direction :row})
|
||||
|
||||
(def dapp-container
|
||||
{:margin-top 0
|
||||
:height line-height})
|
||||
(def header-text
|
||||
{:margin-vertical 4})
|
||||
|
||||
(def account-container
|
||||
{:height line-height})
|
||||
(def header-account-name
|
||||
{:align-items :flex-start})
|
||||
|
||||
@@ -1,29 +1,25 @@
|
||||
(ns status-im.contexts.wallet.wallet-connect.modals.common.header.view
|
||||
(:require [clojure.string :as string]
|
||||
[quo.core :as quo]
|
||||
(:require [quo.core :as quo]
|
||||
[react-native.core :as rn]
|
||||
[status-im.contexts.wallet.wallet-connect.core :as core]
|
||||
[status-im.contexts.wallet.wallet-connect.modals.common.header.style :as style]))
|
||||
|
||||
(defn view
|
||||
[{:keys [label dapp account]}]
|
||||
[rn/view {:style style/header-container}
|
||||
(let [{:keys [name iconUrl url]} dapp
|
||||
image-source (core/compute-dapp-icon-path iconUrl url)]
|
||||
[rn/view {:style style/dapp-container}
|
||||
[quo/summary-tag
|
||||
{:type :dapp
|
||||
:label name
|
||||
:image-source image-source}]])
|
||||
(for [word (string/split label #" ")]
|
||||
^{:key word}
|
||||
[rn/view {:style style/word-container}
|
||||
[quo/text
|
||||
{:size :heading-1
|
||||
:weight :semi-bold}
|
||||
(str " " word)]])
|
||||
(let [{:keys [name iconUrl]} dapp]
|
||||
[rn/view {:style style/header-dapp-name}
|
||||
[rn/view {:flex 1}
|
||||
[quo/summary-tag
|
||||
{:type :dapp
|
||||
:label name
|
||||
:image-source iconUrl}]]])
|
||||
[quo/text
|
||||
{:size :heading-1
|
||||
:weight :semi-bold
|
||||
:style style/header-text}
|
||||
label]
|
||||
(let [{:keys [emoji customization-color name]} account]
|
||||
[rn/view {:style style/account-container}
|
||||
[rn/view {:style style/header-account-name}
|
||||
[quo/summary-tag
|
||||
{:type :account
|
||||
:emoji emoji
|
||||
|
||||
@@ -7,5 +7,6 @@
|
||||
[quo/page-nav
|
||||
{:icon-name :i/close
|
||||
:background :blur
|
||||
:on-press #(rf/dispatch [:wallet-connect/dismiss-request-modal])
|
||||
:on-press #(do (rf/dispatch [:navigate-back])
|
||||
(rf/dispatch [:wallet-connect/reject-session-request]))
|
||||
:accessibility-label accessibility-label}])
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
network (rf/sub [:wallet-connect/current-request-network])
|
||||
{:keys [max-fees-fiat-formatted
|
||||
error-state]} (rf/sub [:wallet-connect/current-request-transaction-information])]
|
||||
(rn/use-unmount #(rf/dispatch [:wallet-connect/on-request-modal-dismissed]))
|
||||
[rn/view {:style (style/container bottom)}
|
||||
[quo/gradient-cover {:customization-color customization-color}]
|
||||
[page-nav/view
|
||||
@@ -34,16 +33,19 @@
|
||||
:dapp dapp
|
||||
:account account}]
|
||||
[data-block/view]]
|
||||
(when error-state
|
||||
[quo/alert-banner
|
||||
{:action? false
|
||||
:text (i18n/label (condp = error-state
|
||||
:not-enough-assets-to-pay-gas-fees
|
||||
:t/not-enough-assets-to-pay-gas-fees
|
||||
|
||||
:not-enough-assets
|
||||
:t/not-enough-assets))}])
|
||||
[footer/view
|
||||
{:warning-label (i18n/label :t/wallet-connect-sign-warning)
|
||||
:slide-button-text (i18n/label :t/slide-to-send)
|
||||
:error-text (when error-state
|
||||
(i18n/label (condp = error-state
|
||||
:not-enough-assets-to-pay-gas-fees
|
||||
:t/not-enough-assets-to-pay-gas-fees
|
||||
|
||||
:not-enough-assets
|
||||
:t/not-enough-assets)))}
|
||||
:disabled? error-state}
|
||||
[quo/data-item
|
||||
{:status :default
|
||||
:card? false
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
{:keys [customization-color]
|
||||
:as account} (rf/sub [:wallet-connect/current-request-account-details])
|
||||
dapp (rf/sub [:wallet-connect/current-request-dapp])]
|
||||
(rn/use-unmount #(rf/dispatch [:wallet-connect/on-request-modal-dismissed]))
|
||||
[rn/view {:style (style/container bottom)}
|
||||
[quo/gradient-cover {:customization-color customization-color}]
|
||||
[page-nav/view
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
network (rf/sub [:wallet-connect/current-request-network])
|
||||
{:keys [max-fees-fiat-formatted
|
||||
error-state]} (rf/sub [:wallet-connect/current-request-transaction-information])]
|
||||
(rn/use-unmount #(rf/dispatch [:wallet-connect/on-request-modal-dismissed]))
|
||||
[rn/view {:style (style/container bottom)}
|
||||
[quo/gradient-cover {:customization-color customization-color}]
|
||||
[page-nav/view
|
||||
@@ -33,16 +32,19 @@
|
||||
:dapp dapp
|
||||
:account account}]
|
||||
[data-block/view]]
|
||||
(when error-state
|
||||
[quo/alert-banner
|
||||
{:action? false
|
||||
:text (i18n/label (condp = error-state
|
||||
:not-enough-assets-to-pay-gas-fees
|
||||
:t/not-enough-assets-to-pay-gas-fees
|
||||
|
||||
:not-enough-assets
|
||||
:t/not-enough-assets))}])
|
||||
[footer/view
|
||||
{:warning-label (i18n/label :t/wallet-connect-sign-warning)
|
||||
:slide-button-text (i18n/label :t/slide-to-sign)
|
||||
:error-text (when error-state
|
||||
(i18n/label (condp = error-state
|
||||
:not-enough-assets-to-pay-gas-fees
|
||||
:t/not-enough-assets-to-pay-gas-fees
|
||||
|
||||
:not-enough-assets
|
||||
:t/not-enough-assets)))}
|
||||
:disabled? error-state}
|
||||
[quo/data-item
|
||||
{:status :default
|
||||
:card? false
|
||||
|
||||
@@ -5,10 +5,8 @@
|
||||
[re-frame.core :as rf]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.contexts.wallet.wallet-connect.core :as wallet-connect-core]
|
||||
[status-im.contexts.wallet.wallet-connect.signing :as signing]
|
||||
[status-im.contexts.wallet.wallet-connect.transactions :as transactions]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.transforms :as transforms]))
|
||||
|
||||
(rf/reg-event-fx
|
||||
@@ -29,9 +27,7 @@
|
||||
existing-event (get-in db [:wallet-connect/current-request :event])]
|
||||
;; NOTE: make sure we don't show two requests at the same time
|
||||
(when-not existing-event
|
||||
{:db (-> db
|
||||
(assoc-in [:wallet-connect/current-request :event] event)
|
||||
(assoc-in [:wallet-connect/current-request :response-sent?] false))
|
||||
{:db (assoc-in db [:wallet-connect/current-request :event] event)
|
||||
:fx [(condp = method
|
||||
constants/wallet-connect-eth-send-transaction-method
|
||||
[:dispatch [:wallet-connect/process-eth-send-transaction]]
|
||||
@@ -126,30 +122,15 @@
|
||||
:wallet-connect/process-sign-typed
|
||||
(fn [{:keys [db]}]
|
||||
(let [[address raw-data] (wallet-connect-core/get-db-current-request-params db)
|
||||
parsed-raw-data (transforms/js-parse raw-data)
|
||||
session-chain-id (-> (wallet-connect-core/get-db-current-request-event db)
|
||||
(get-in [:params :chainId])
|
||||
wallet-connect-core/eip155->chain-id)
|
||||
data-chain-id (-> parsed-raw-data
|
||||
transforms/js->clj
|
||||
signing/typed-data-chain-id)
|
||||
parsed-data (try (-> parsed-raw-data
|
||||
parsed-data (try (-> raw-data
|
||||
transforms/js-parse
|
||||
(transforms/js-dissoc :types :primaryType)
|
||||
(transforms/js-stringify 2))
|
||||
(catch js/Error _ nil))]
|
||||
(cond
|
||||
(nil? parsed-data)
|
||||
(if (nil? parsed-data)
|
||||
{:fx [[:dispatch
|
||||
[:wallet-connect/on-processing-error
|
||||
(ex-info "Failed to parse JSON typed data" {:data raw-data})]]]}
|
||||
|
||||
(not= session-chain-id data-chain-id)
|
||||
{:fx [[:dispatch
|
||||
[:wallet-connect/wrong-typed-data-chain-id
|
||||
{:expected-chain-id session-chain-id
|
||||
:wrong-chain-id data-chain-id}]]]}
|
||||
|
||||
:else
|
||||
{:db (update-in db
|
||||
[:wallet-connect/current-request]
|
||||
assoc
|
||||
@@ -158,39 +139,19 @@
|
||||
:raw-data raw-data)
|
||||
:fx [[:dispatch [:wallet-connect/show-request-modal]]]}))))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/wrong-typed-data-chain-id
|
||||
(fn [_ [{:keys [expected-chain-id wrong-chain-id]}]]
|
||||
(let [wrong-network-name (-> wrong-chain-id
|
||||
wallet-connect-core/chain-id->network-details
|
||||
:full-name)
|
||||
expected-network-name (-> expected-chain-id
|
||||
wallet-connect-core/chain-id->network-details
|
||||
:full-name)
|
||||
toast-message (i18n/label :t/wallet-connect-typed-data-wrong-chain-id-warning
|
||||
{:wrong-chain wrong-network-name
|
||||
:expected-chain expected-network-name})]
|
||||
{:fx [[:dispatch
|
||||
[:toasts/upsert
|
||||
{:type :negative
|
||||
:theme :dark
|
||||
:text toast-message}]]
|
||||
[:dispatch
|
||||
[:wallet-connect/on-processing-error
|
||||
(ex-info "Can't proceed signing typed data due to wrong chain-id included in the data"
|
||||
{:expected-chain-id expected-chain-id
|
||||
:wrong-chain-id wrong-chain-id})]]]})))
|
||||
|
||||
;; TODO: we should reject a request if processing fails
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/on-processing-error
|
||||
(fn [{:keys [db]} [error]]
|
||||
(let [{:keys [address event]} (get db :wallet-connect/current-request)
|
||||
method (wallet-connect-core/get-request-method event)]
|
||||
method (wallet-connect-core/get-request-method event)
|
||||
screen (wallet-connect-core/method-to-screen method)]
|
||||
(log/error "Failed to process Wallet Connect request"
|
||||
{:error error
|
||||
:address address
|
||||
:method method
|
||||
:wallet-connect-event event
|
||||
:event :wallet-connect/on-processing-error})
|
||||
{:fx [[:dispatch [:wallet-connect/dismiss-request-modal]]]})))
|
||||
|
||||
{:fx [[:dispatch [:dismiss-modal screen]]
|
||||
[:dispatch [:wallet-connect/reset-current-request]]]})))
|
||||
|
||||
@@ -3,42 +3,31 @@
|
||||
[react-native.wallet-connect :as wallet-connect]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.contexts.wallet.wallet-connect.core :as wallet-connect-core]
|
||||
[status-im.contexts.wallet.wallet-connect.utils :as wc-utils]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.i18n :as i18n]))
|
||||
[taoensso.timbre :as log]))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/respond-current-session
|
||||
(fn [{:keys [db]} [password]]
|
||||
(let [event (get-in db [:wallet-connect/current-request :event])
|
||||
method (wallet-connect-core/get-request-method event)
|
||||
screen (wallet-connect-core/method-to-screen method)
|
||||
expiry (get-in event [:params :request :expiryTimestamp])]
|
||||
(if (wc-utils/timestamp-expired? expiry)
|
||||
{:fx [[:dispatch
|
||||
[:toasts/upsert
|
||||
{:id :new-wallet-account-created
|
||||
:type :negative
|
||||
:text (i18n/label :t/wallet-connect-request-expired)}]]
|
||||
[:dispatch [:dismiss-modal screen]]]}
|
||||
{:fx [(condp = method
|
||||
constants/wallet-connect-personal-sign-method
|
||||
[:dispatch [:wallet-connect/respond-sign-message password :personal-sign]]
|
||||
method (wallet-connect-core/get-request-method event)]
|
||||
{:fx [(condp = method
|
||||
constants/wallet-connect-personal-sign-method
|
||||
[:dispatch [:wallet-connect/respond-sign-message password :personal-sign]]
|
||||
|
||||
constants/wallet-connect-eth-sign-method
|
||||
[:dispatch [:wallet-connect/respond-sign-message password :eth-sign]]
|
||||
constants/wallet-connect-eth-sign-method
|
||||
[:dispatch [:wallet-connect/respond-sign-message password :eth-sign]]
|
||||
|
||||
constants/wallet-connect-eth-send-transaction-method
|
||||
[:dispatch [:wallet-connect/respond-send-transaction-data password]]
|
||||
constants/wallet-connect-eth-send-transaction-method
|
||||
[:dispatch [:wallet-connect/respond-send-transaction-data password]]
|
||||
|
||||
constants/wallet-connect-eth-sign-transaction-method
|
||||
[:dispatch [:wallet-connect/respond-sign-transaction-data password]]
|
||||
constants/wallet-connect-eth-sign-transaction-method
|
||||
[:dispatch [:wallet-connect/respond-sign-transaction-data password]]
|
||||
|
||||
constants/wallet-connect-eth-sign-typed-method
|
||||
[:dispatch [:wallet-connect/respond-sign-typed-data password :v1]]
|
||||
constants/wallet-connect-eth-sign-typed-method
|
||||
[:dispatch [:wallet-connect/respond-sign-typed-data password :v1]]
|
||||
|
||||
constants/wallet-connect-eth-sign-typed-v4-method
|
||||
[:dispatch [:wallet-connect/respond-sign-typed-data password :v4]])]}))))
|
||||
constants/wallet-connect-eth-sign-typed-v4-method
|
||||
[:dispatch [:wallet-connect/respond-sign-typed-data password :v4]])]})))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/respond-sign-message
|
||||
@@ -50,7 +39,7 @@
|
||||
:data raw-data
|
||||
:rpc-method rpc-method
|
||||
:on-error #(rf/dispatch [:wallet-connect/on-sign-error %])
|
||||
:on-success #(rf/dispatch [:wallet-connect/finish-session-request %])}]]})))
|
||||
:on-success #(rf/dispatch [:wallet-connect/send-response {:result %}])}]]})))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/respond-sign-typed-data
|
||||
@@ -64,7 +53,7 @@
|
||||
:chain-id chain-id
|
||||
:version typed-data-version
|
||||
:on-error #(rf/dispatch [:wallet-connect/on-sign-error %])
|
||||
:on-success #(rf/dispatch [:wallet-connect/finish-session-request %])}]]})))
|
||||
:on-success #(rf/dispatch [:wallet-connect/send-response {:result %}])}]]})))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/respond-send-transaction-data
|
||||
@@ -78,7 +67,7 @@
|
||||
:tx-hash tx-hash
|
||||
:tx-args tx-args
|
||||
:on-error #(rf/dispatch [:wallet-connect/on-sign-error %])
|
||||
:on-success #(rf/dispatch [:wallet-connect/finish-session-request %])}]]})))
|
||||
:on-success #(rf/dispatch [:wallet-connect/send-response {:result %}])}]]})))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/respond-sign-transaction-data
|
||||
@@ -92,13 +81,15 @@
|
||||
:tx-hash tx-hash
|
||||
:tx-params tx-args
|
||||
:on-error #(rf/dispatch [:wallet-connect/on-sign-error %])
|
||||
:on-success #(rf/dispatch [:wallet-connect/finish-session-request %])}]]})))
|
||||
:on-success #(rf/dispatch [:wallet-connect/send-response {:result %}])}]]})))
|
||||
|
||||
;; TODO: should reject if "signing" fails
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/on-sign-error
|
||||
(fn [{:keys [db]} [error]]
|
||||
(let [{:keys [raw-data address event]} (get db :wallet-connect/current-request)
|
||||
method (wallet-connect-core/get-request-method event)]
|
||||
method (wallet-connect-core/get-request-method event)
|
||||
screen (wallet-connect-core/method-to-screen method)]
|
||||
(log/error "Failed to sign Wallet Connect request"
|
||||
{:error error
|
||||
:address address
|
||||
@@ -106,72 +97,56 @@
|
||||
:method method
|
||||
:wallet-connect-event event
|
||||
:event :wallet-connect/on-sign-error})
|
||||
{:fx [[:dispatch [:wallet-connect/dismiss-request-modal]]]})))
|
||||
{:fx [[:dispatch [:dismiss-modal screen]]
|
||||
[:dispatch [:wallet-connect/reset-current-request]]]})))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/send-response
|
||||
(fn [{:keys [db]} [{:keys [result error]}]]
|
||||
(when-let [{:keys [id topic] :as event} (get-in db [:wallet-connect/current-request :event])]
|
||||
(let [method (wallet-connect-core/get-request-method event)
|
||||
web3-wallet (get db :wallet-connect/web3-wallet)]
|
||||
{:db (assoc-in db [:wallet-connect/current-request :response-sent?] true)
|
||||
:fx [[:effects.wallet-connect/respond-session-request
|
||||
{:web3-wallet web3-wallet
|
||||
:topic topic
|
||||
:id id
|
||||
:result result
|
||||
:error error
|
||||
:on-error (fn [error]
|
||||
(log/error "Failed to send Wallet Connect response"
|
||||
{:error error
|
||||
:method method
|
||||
:event :wallet-connect/send-response
|
||||
:wallet-connect-event event}))
|
||||
:on-success (fn []
|
||||
(log/info "Successfully sent Wallet Connect response to dApp"))}]]}))))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/dismiss-request-modal
|
||||
(fn [{:keys [db]} _]
|
||||
(let [screen (-> db
|
||||
(get-in [:wallet-connect/current-request :event])
|
||||
wallet-connect-core/get-request-method
|
||||
wallet-connect-core/method-to-screen)]
|
||||
{:fx [[:dispatch [:dismiss-modal screen]]]})))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/finish-session-request
|
||||
(fn [_ [result]]
|
||||
{:fx [[:dispatch [:wallet-connect/send-response {:result result}]]
|
||||
[:dispatch [:wallet-connect/dismiss-request-modal]]]}))
|
||||
(let [{:keys [id topic] :as event} (get-in db [:wallet-connect/current-request :event])
|
||||
method (wallet-connect-core/get-request-method event)
|
||||
screen (wallet-connect-core/method-to-screen method)
|
||||
web3-wallet (get db :wallet-connect/web3-wallet)]
|
||||
{:fx [[:effects.wallet-connect/respond-session-request
|
||||
{:web3-wallet web3-wallet
|
||||
:topic topic
|
||||
:id id
|
||||
:result result
|
||||
:error error
|
||||
:on-error (fn [error]
|
||||
(log/error "Failed to send Wallet Connect response"
|
||||
{:error error
|
||||
:method method
|
||||
:event :wallet-connect/send-response
|
||||
:wallet-connect-event event})
|
||||
(rf/dispatch [:dismiss-modal screen])
|
||||
(rf/dispatch [:wallet-connect/reset-current-request]))
|
||||
:on-success (fn []
|
||||
(log/info "Successfully sent Wallet Connect response to dApp")
|
||||
(rf/dispatch [:dismiss-modal screen])
|
||||
(rf/dispatch [:wallet-connect/reset-current-request]))}]]})))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/reject-session-proposal
|
||||
(fn [{:keys [db]} _]
|
||||
(let [web3-wallet (get db :wallet-connect/web3-wallet)
|
||||
{:keys [request response-sent?]} (:wallet-connect/current-proposal db)]
|
||||
{:fx [(when-not response-sent?
|
||||
[:effects.wallet-connect/reject-session-proposal
|
||||
{:web3-wallet web3-wallet
|
||||
:proposal request
|
||||
:on-success #(log/info "Wallet Connect session proposal rejected")
|
||||
:on-error #(log/error "Wallet Connect unable to reject session proposal")}])
|
||||
(let [web3-wallet (get db :wallet-connect/web3-wallet)
|
||||
current-proposal (get-in db [:wallet-connect/current-proposal :request])]
|
||||
{:fx [[:effects.wallet-connect/reject-session-proposal
|
||||
{:web3-wallet web3-wallet
|
||||
:proposal current-proposal
|
||||
:on-success #(log/info "Wallet Connect session proposal rejected")
|
||||
:on-error #(log/error "Wallet Connect unable to reject session proposal")}]
|
||||
[:dispatch [:wallet-connect/reset-current-session-proposal]]]})))
|
||||
|
||||
;; NOTE: Currently we only reject a session if the user dismissed a modal
|
||||
;; without accepting the session first.
|
||||
;; NOTE: Currently we only reject a session if the user rejected it
|
||||
;; But this needs to be solidified to ensure other cases:
|
||||
;; - Unsupported WC version
|
||||
;; - Invalid params from dapps
|
||||
;; - Unsupported method
|
||||
;; - Failed processing of request
|
||||
;; - Failed "responding" (signing or sending message/transaction)
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/on-request-modal-dismissed
|
||||
(fn [{:keys [db]}]
|
||||
{:fx [(when-not (get-in db [:wallet-connect/current-request :response-sent?])
|
||||
[:dispatch
|
||||
[:wallet-connect/send-response
|
||||
{:error (wallet-connect/get-sdk-error
|
||||
constants/wallet-connect-user-rejected-error-key)}]])
|
||||
[:dispatch [:wallet-connect/reset-current-request]]]}))
|
||||
:wallet-connect/reject-session-request
|
||||
(fn [_ _]
|
||||
{:fx [[:dispatch
|
||||
[:wallet-connect/send-response
|
||||
{:error (wallet-connect/get-sdk-error
|
||||
constants/wallet-connect-user-rejected-error-key)}]]]}))
|
||||
|
||||
@@ -5,15 +5,14 @@
|
||||
{:padding-horizontal 20
|
||||
:padding-top 12})
|
||||
|
||||
(defn approval-note-container
|
||||
[theme]
|
||||
(def approval-note-container
|
||||
{:margin-horizontal 20
|
||||
:padding-horizontal 16
|
||||
:padding-vertical 12
|
||||
:border-radius 16
|
||||
:border-width 1
|
||||
:border-color (colors/theme-colors colors/neutral-10 colors/black-opa-30 theme)
|
||||
:background-color (colors/theme-colors colors/neutral-2_5 colors/black-opa-30 theme)})
|
||||
:border-color colors/neutral-10
|
||||
:background-color colors/neutral-2_5})
|
||||
|
||||
(def approval-note-title
|
||||
{:color colors/neutral-50
|
||||
|
||||
@@ -6,27 +6,21 @@
|
||||
[quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[status-im.common.floating-button-page.view :as floating-button-page]
|
||||
[status-im.contexts.wallet.wallet-connect.core :as wallet-connect-core]
|
||||
[status-im.contexts.wallet.wallet-connect.session-proposal.style :as style]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]
|
||||
[utils.string]))
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn- dapp-metadata
|
||||
[]
|
||||
(let [proposer (rf/sub [:wallet-connect/session-proposer])
|
||||
{:keys [icons name url]} (:metadata proposer)
|
||||
first-icon (first icons)
|
||||
dapp-name (wallet-connect-core/compute-dapp-name name url)
|
||||
profile-picture (wallet-connect-core/compute-dapp-icon-path first-icon url)]
|
||||
{:keys [icons name url]} (:metadata proposer)]
|
||||
[:<>
|
||||
[rn/view {:style style/dapp-avatar}
|
||||
[quo/user-avatar
|
||||
{:profile-picture profile-picture
|
||||
:size :big
|
||||
:full-name dapp-name}]]
|
||||
{:profile-picture (first icons)
|
||||
:size :big}]]
|
||||
[quo/page-top
|
||||
{:title dapp-name
|
||||
{:title name
|
||||
:description :context-tag
|
||||
:context-tag {:type :icon
|
||||
:size 32
|
||||
@@ -37,9 +31,8 @@
|
||||
[]
|
||||
(let [dapp-name (rf/sub [:wallet-connect/session-proposer-name])
|
||||
labels [(i18n/label :t/check-your-account-balance-and-activity)
|
||||
(i18n/label :t/request-txns-and-message-signing)]
|
||||
theme (quo.theme/use-theme)]
|
||||
[rn/view {:style (style/approval-note-container theme)}
|
||||
(i18n/label :t/request-txns-and-message-signing)]]
|
||||
[rn/view {:style style/approval-note-container}
|
||||
[quo/text
|
||||
{:style style/approval-note-title
|
||||
:weight :regular
|
||||
@@ -53,8 +46,7 @@
|
||||
{:color colors/neutral-40}]
|
||||
[quo/text
|
||||
{:weight :regular
|
||||
:size :paragraph-2
|
||||
:color colors/neutral-40}
|
||||
:size :paragraph-2}
|
||||
label]])
|
||||
labels)]))
|
||||
|
||||
@@ -70,7 +62,7 @@
|
||||
|
||||
(defn- accounts-list
|
||||
[]
|
||||
(let [accounts (rf/sub [:wallet/operable-accounts])
|
||||
(let [accounts (rf/sub [:wallet/operable-accounts-without-watched-accounts])
|
||||
selected-address (rf/sub [:wallet-connect/current-proposal-address])]
|
||||
[rn/view {:style style/account-switcher-list}
|
||||
(for [{:keys [address] :as account} accounts]
|
||||
@@ -148,9 +140,10 @@
|
||||
:button-two-label (i18n/label :t/decline)
|
||||
:button-two-props {:type :grey
|
||||
:accessibility-label :wc-deny-connection
|
||||
:on-press #(rf/dispatch
|
||||
[:dismiss-modal
|
||||
:screen/wallet.wallet-connect-session-proposal])}
|
||||
:on-press (fn []
|
||||
(rf/dispatch [:navigate-back])
|
||||
(rf/dispatch
|
||||
[:wallet-connect/reject-session-proposal]))}
|
||||
:button-one-label (i18n/label :t/connect)
|
||||
:button-one-props {:customization-color customization-color
|
||||
:type :primary
|
||||
@@ -164,13 +157,11 @@
|
||||
{:type :no-title
|
||||
:background :blur
|
||||
:icon-name :i/close
|
||||
:on-press (rn/use-callback
|
||||
#(rf/dispatch [:dismiss-modal :screen/wallet.wallet-connect-session-proposal]))
|
||||
:on-press (rn/use-callback #(rf/dispatch [:navigate-back]))
|
||||
:accessibility-label :wc-session-proposal-top-bar}])
|
||||
|
||||
(defn view
|
||||
[]
|
||||
(rn/use-unmount #(rf/dispatch [:wallet-connect/reject-session-proposal]))
|
||||
[floating-button-page/view
|
||||
{:footer-container-padding 0
|
||||
:header [header]
|
||||
|
||||
@@ -6,20 +6,6 @@
|
||||
[utils.hex :as hex]
|
||||
[utils.transforms :as transforms]))
|
||||
|
||||
(defn typed-data-chain-id
|
||||
"Returns the `:chain-id` from typed data if it's present and if the EIP712 domain defines it. Without
|
||||
the `:chain-id` in the domain type, it will not be signed as part of the typed-data."
|
||||
[typed-data]
|
||||
(let [chain-id-type? (->> typed-data
|
||||
:types
|
||||
:EIP712Domain
|
||||
(some #(= "chainId" (:name %))))
|
||||
data-chain-id (-> typed-data
|
||||
:domain
|
||||
:chainId)]
|
||||
(when chain-id-type?
|
||||
data-chain-id)))
|
||||
|
||||
(defn eth-sign
|
||||
[password address data]
|
||||
(-> {:data data
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
status-im.contexts.contact.blocking.events
|
||||
status-im.contexts.keycard.effects
|
||||
status-im.contexts.keycard.events
|
||||
status-im.contexts.networks.events
|
||||
status-im.contexts.onboarding.common.overlay.events
|
||||
status-im.contexts.onboarding.events
|
||||
status-im.contexts.profile.events
|
||||
@@ -58,7 +57,7 @@
|
||||
cofx
|
||||
{:db db/app-db
|
||||
:theme/init-theme nil
|
||||
:effects.network/listen-to-network-info nil
|
||||
:network/listen-to-network-info nil
|
||||
:effects.biometric/get-supported-type nil
|
||||
:effects.keycard/register-card-events nil
|
||||
:effects.keycard/check-nfc-enabled nil
|
||||
|
||||
@@ -37,6 +37,15 @@
|
||||
:id :screen/profile.profiles
|
||||
:options (options/dark-root-options)}}]}}}
|
||||
|
||||
:screen/onboarding.enable-notifications
|
||||
{:root {:stack {:children [{:component {:name :screen/onboarding.enable-notifications
|
||||
:id :screen/onboarding.enable-notifications
|
||||
:options (options/dark-root-options)}}]}}}
|
||||
|
||||
:screen/onboarding.welcome
|
||||
{:root {:stack {:children [{:component {:name :screen/onboarding.welcome
|
||||
:id :screen/onboarding.welcome
|
||||
:options (options/dark-root-options)}}]}}}
|
||||
:screen/onboarding.syncing-results
|
||||
{:root {:stack {:children [{:component {:name :screen/onboarding.syncing-results
|
||||
:id :screen/onboarding.syncing-results
|
||||
|
||||
@@ -348,15 +348,11 @@
|
||||
:component enter-seed-phrase/view}
|
||||
|
||||
{:name :screen/onboarding.enable-notifications
|
||||
:options {:theme :dark
|
||||
:layout options/onboarding-transparent-layout
|
||||
:animations (merge
|
||||
transitions/new-to-status-modal-animations
|
||||
transitions/push-animations-for-transparent-background)
|
||||
:popGesture false
|
||||
:modalPresentationStyle :overCurrentContext
|
||||
:hardwareBackButton {:dismissModalOnPress false
|
||||
:popStackOnPress false}}
|
||||
:options {:theme :dark
|
||||
:layout options/onboarding-transparent-layout
|
||||
:animations (merge
|
||||
transitions/new-to-status-modal-animations
|
||||
transitions/push-animations-for-transparent-background)}
|
||||
:component enable-notifications/view}
|
||||
|
||||
{:name :screen/onboarding.identifiers
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
(:require
|
||||
["react-native" :refer (DevSettings LogBox NativeModules)]
|
||||
[react-native.platform :as platform]
|
||||
[status-im.setup.oops :as setup.oops]
|
||||
[status-im.setup.schema :as schema]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
@@ -48,7 +47,6 @@
|
||||
:utils/dispatch-later
|
||||
:json-rpc/call})
|
||||
(when ^:boolean js/goog.DEBUG
|
||||
(setup.oops/setup!)
|
||||
(schema/setup!)
|
||||
(when platform/ios?
|
||||
;; on Android this method doesn't work
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
(ns status-im.setup.oops
|
||||
(:require [oops.config]))
|
||||
|
||||
(defn setup!
|
||||
"Change oops defaults to warn and print instead of throwing exceptions during
|
||||
development."
|
||||
[]
|
||||
(oops.config/update-current-runtime-config!
|
||||
merge
|
||||
{:error-reporting :console
|
||||
:expected-function-value :warn
|
||||
:invalid-selector :warn
|
||||
:missing-object-key :warn
|
||||
:object-is-frozen :warn
|
||||
:object-is-sealed :warn
|
||||
:object-key-not-writable :warn
|
||||
:unexpected-empty-selector :warn
|
||||
:unexpected-object-value :warn
|
||||
:unexpected-punching-selector :warn
|
||||
:unexpected-soft-selector :warn}))
|
||||
@@ -2,8 +2,7 @@
|
||||
(:require
|
||||
["bignumber.js" :as BigNumber]
|
||||
[matcher-combinators.core :as matcher-combinators]
|
||||
[matcher-combinators.model :as matcher.model]
|
||||
[status-im.setup.oops :as setup.oops]))
|
||||
[matcher-combinators.model :as matcher.model]))
|
||||
|
||||
;; We must implement Matcher in order for tests to work with the `match?`
|
||||
;; directive.
|
||||
@@ -17,5 +16,3 @@
|
||||
:matcher-combinators.result/value actual}
|
||||
{:matcher-combinators.result/type :mismatch
|
||||
:matcher-combinators.result/value (matcher.model/->Mismatch this actual)})))
|
||||
|
||||
(setup.oops/setup!)
|
||||
|
||||
@@ -6,9 +6,7 @@
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.contexts.communities.utils :as utils]
|
||||
[status-im.contexts.profile.utils :as profile.utils]
|
||||
[status-im.subs.chat.utils :as subs.utils]
|
||||
[status-im.subs.contact.utils :as contact.utils]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.money :as money]))
|
||||
|
||||
@@ -71,7 +69,7 @@
|
||||
(fn [[_ community-id]]
|
||||
[(re-frame/subscribe [:communities/community community-id])])
|
||||
(fn [[{:keys [members]}] _]
|
||||
(js-keys members)))
|
||||
members))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:communities/community-chat-members
|
||||
@@ -94,80 +92,51 @@
|
||||
{}
|
||||
public-keys))
|
||||
|
||||
(defn- sort-members-by-name-old
|
||||
(defn- sort-members-by-name
|
||||
[names descending? members]
|
||||
(if descending?
|
||||
(sort-by #(get names %) #(compare %2 %1) members)
|
||||
(sort-by #(get names %) members)))
|
||||
(sort-by #(get names (first %)) #(compare %2 %1) members)
|
||||
(sort-by #(get names (first %)) members)))
|
||||
|
||||
(defn- sort-members-by-name
|
||||
[names members-keys]
|
||||
(let [forced-last-key "zzzzzz"
|
||||
sort-keyfn (fn [k]
|
||||
(if-let [[primary-name secondary-name] (get names k)]
|
||||
(or (some-> primary-name
|
||||
string/lower-case)
|
||||
(some-> secondary-name
|
||||
string/lower-case))
|
||||
;; Sort unknown keys at the end.
|
||||
forced-last-key))]
|
||||
(sort-by sort-keyfn members-keys)))
|
||||
|
||||
;; This implementation is wrong, but since it's only used in a legacy view, we
|
||||
;; can ignore it for now.
|
||||
(re-frame/reg-sub :communities/sorted-community-members
|
||||
(re-frame/reg-sub
|
||||
:communities/sorted-community-members
|
||||
(fn [[_ community-id]]
|
||||
[(re-frame/subscribe [:profile/profile])
|
||||
(re-frame/subscribe [:communities/community-members community-id])])
|
||||
(let [profile (re-frame/subscribe [:profile/profile])
|
||||
members (re-frame/subscribe [:communities/community-members community-id])]
|
||||
[profile members]))
|
||||
(fn [[profile members] _]
|
||||
(let [names (keys->names members profile)]
|
||||
(let [names (keys->names (keys members) profile)]
|
||||
(->> members
|
||||
(sort-members-by-name-old names false)
|
||||
(sort-members-by-name names false)
|
||||
(sort-by #(visibility-status-utils/visibility-status-order (get % 0)))))))
|
||||
|
||||
(re-frame/reg-sub :communities/chat-members
|
||||
(re-frame/reg-sub
|
||||
:communities/sorted-community-members-section-list
|
||||
(fn [[_ community-id chat-id]]
|
||||
[(re-frame/subscribe [:profile/public-key])
|
||||
(re-frame/subscribe [:communities/community-chat-members community-id chat-id])
|
||||
(re-frame/subscribe [:visibility-status-updates])
|
||||
(re-frame/subscribe [:multiaccount/current-user-visibility-status])])
|
||||
(fn [[profile-pub-key members-js visibility-status-updates my-status-update] [_ _ _ visibility-status]]
|
||||
(let [members-keys (js-keys members-js)
|
||||
online? (fn [public-key]
|
||||
(let [{visibility-status-type :status-type}
|
||||
(if (or (string/blank? profile-pub-key)
|
||||
(= profile-pub-key public-key))
|
||||
my-status-update
|
||||
(get visibility-status-updates public-key))]
|
||||
(subs.utils/online? visibility-status-type)))]
|
||||
(filter (if (= :online visibility-status)
|
||||
online?
|
||||
(complement online?))
|
||||
members-keys))))
|
||||
|
||||
(defn- names-by-key
|
||||
[contacts profile public-keys]
|
||||
(let [names (reduce (fn [acc k]
|
||||
(if-let [contact (get contacts k)]
|
||||
(assoc acc k (contact.utils/contact-two-names contact profile))
|
||||
acc))
|
||||
{}
|
||||
public-keys)]
|
||||
(assoc names
|
||||
(:public-key profile)
|
||||
[(profile.utils/displayed-name profile) nil])))
|
||||
|
||||
;; This is a potentially expensive subscription because we don't control how
|
||||
;; many members and contacts exist in the app-db. Future improvements include
|
||||
;; removing members from the payload and paginating them from status-go.
|
||||
(re-frame/reg-sub :communities/chat-members-sorted
|
||||
(fn [[_ community-id chat-id visibility-status]]
|
||||
[(re-frame/subscribe [:profile/profile])
|
||||
(re-frame/subscribe [:contacts/contacts-raw])
|
||||
(re-frame/subscribe [:communities/chat-members community-id chat-id visibility-status])])
|
||||
(fn [[profile contacts ^js members-keys]]
|
||||
(sort-members-by-name (names-by-key contacts profile members-keys)
|
||||
members-keys)))
|
||||
(let [profile (re-frame/subscribe [:profile/profile])
|
||||
members (re-frame/subscribe [:communities/community-chat-members
|
||||
community-id chat-id])
|
||||
visibility-status-updates (re-frame/subscribe
|
||||
[:visibility-status-updates])
|
||||
my-status-update (re-frame/subscribe
|
||||
[:multiaccount/current-user-visibility-status])]
|
||||
[profile members visibility-status-updates my-status-update]))
|
||||
(fn [[profile members visibility-status-updates my-status-update] _]
|
||||
(let [online? (fn [public-key]
|
||||
(let [{visibility-status-type :status-type}
|
||||
(if (or (string/blank? (:public-key profile))
|
||||
(= (:public-key profile) public-key))
|
||||
my-status-update
|
||||
(get visibility-status-updates public-key))]
|
||||
(subs.utils/online? visibility-status-type)))
|
||||
names (keys->names (keys members) profile)]
|
||||
(->> members
|
||||
(sort-members-by-name names true)
|
||||
keys
|
||||
(group-by online?)
|
||||
(map (fn [[k v]]
|
||||
{:title (if k (i18n/label :t/online) (i18n/label :t/offline))
|
||||
:data v}))))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:communities/featured-contract-communities
|
||||
@@ -208,13 +177,6 @@
|
||||
(if (or (empty? @memo-communities-stack-items) (= view-id :communities-stack))
|
||||
(let [grouped-communities (->> communities
|
||||
vals
|
||||
;; Remove data that can grow fast or is
|
||||
;; reliably not needed to list communities.
|
||||
;; We could use an allowlist of keys for
|
||||
;; optimal performance of this sub, but
|
||||
;; that's harder to maintain in case we miss
|
||||
;; any key.
|
||||
(map #(dissoc % :members :chats :token-permissions :tokens-metadata))
|
||||
(group-by #(group-communities-by-status requests %))
|
||||
merge-opened-communities
|
||||
(map (fn [[k v]]
|
||||
|
||||
@@ -11,8 +11,6 @@
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(def community-id "0x02b5bdaf5a25fcfe2ee14c501fab1836b8de57f61621080c3d52073d16de0d98d6")
|
||||
(def channel-id "0x1-channel-id")
|
||||
(def chat-id (str community-id channel-id))
|
||||
|
||||
(h/deftest-sub :communities
|
||||
[sub-name]
|
||||
@@ -459,117 +457,65 @@
|
||||
(match? []
|
||||
(rf/sub [sub-name community-id]))))))
|
||||
|
||||
(h/deftest-sub :communities/chat-members-sorted
|
||||
(h/deftest-sub :communities/sorted-community-members-section-list
|
||||
[sub-name]
|
||||
(let [token-image-eth "data:image/jpeg;base64,/9j/2w"
|
||||
channel-id-1 "89f98a1e-6776-4e5f-8626-8ab9f855253f"
|
||||
channel-id-2 "a076358e-4638-470e-a3fb-584d0a542ce6"
|
||||
chat-id-2 (str community-id channel-id-2)
|
||||
|
||||
member-id-1 "0x01"
|
||||
member-id-2 "0x02"
|
||||
|
||||
visibility-status-updates
|
||||
{member-id-1 {:status-type constants/visibility-status-always-online}
|
||||
member-id-2 {:status-type constants/visibility-status-always-online}}
|
||||
|
||||
contacts
|
||||
{member-id-1 {:display-name "John Marston"}
|
||||
member-id-2 {:display-name "Arthur Morgan"}}
|
||||
|
||||
community {:id community-id
|
||||
:permissions {:access 3}
|
||||
:token-images {"ETH" token-image-eth}
|
||||
:name "Community super name"
|
||||
:chats {channel-id-1
|
||||
{:description "x"
|
||||
:emoji "🎲"
|
||||
:permissions {:access 1}
|
||||
:color "#88B0FF"
|
||||
:name "random"
|
||||
:categoryID "0c3c64e7-d56e-439b-a3fb-a946d83cb056"
|
||||
:id channel-id-1
|
||||
:position 4
|
||||
:can-post? false
|
||||
:members nil}
|
||||
channel-id-2
|
||||
{:description "General channel for the community"
|
||||
:emoji "🥔"
|
||||
:permissions {:access 1}
|
||||
:color "#4360DF"
|
||||
:name "general"
|
||||
:categoryID "0c3c64e7-d56e-439b-a3fb-a946d83cb056"
|
||||
:id channel-id-2
|
||||
:position 0
|
||||
:token-gated? true
|
||||
:can-post? false
|
||||
:members (clj->js {member-id-1 {"roles" [1]}
|
||||
member-id-2 {"roles" [1]}
|
||||
"0x05" {"roles" [1]}})}}
|
||||
:members (js->clj {member-id-1 {"roles" [1]}
|
||||
member-id-2 {"roles" [1]}
|
||||
"0x03" {"roles" [1]}
|
||||
"0x04" {"roles" [1]}})}]
|
||||
(testing "returns sorted community members who are online"
|
||||
(swap! rf-db/app-db assoc :contacts/contacts contacts)
|
||||
(testing "returns sorted community members per online status"
|
||||
(let [token-image-eth "data:image/jpeg;base64,/9j/2w"
|
||||
channel-id-1 "89f98a1e-6776-4e5f-8626-8ab9f855253f"
|
||||
channel-id-2 "a076358e-4638-470e-a3fb-584d0a542ce6"
|
||||
chat-id-1 (str community-id channel-id-1)
|
||||
chat-id-2 (str community-id channel-id-2)
|
||||
community {:id community-id
|
||||
:permissions {:access 3}
|
||||
:token-images {"ETH" token-image-eth}
|
||||
:name "Community super name"
|
||||
:chats {channel-id-1
|
||||
{:description "x"
|
||||
:emoji "🎲"
|
||||
:permissions {:access 1}
|
||||
:color "#88B0FF"
|
||||
:name "random"
|
||||
:categoryID "0c3c64e7-d56e-439b-a3fb-a946d83cb056"
|
||||
:id channel-id-1
|
||||
:position 4
|
||||
:can-post? false
|
||||
:members nil}
|
||||
channel-id-2
|
||||
{:description "General channel for the community"
|
||||
:emoji "🥔"
|
||||
:permissions {:access 1}
|
||||
:color "#4360DF"
|
||||
:name "general"
|
||||
:categoryID "0c3c64e7-d56e-439b-a3fb-a946d83cb056"
|
||||
:id channel-id-2
|
||||
:position 0
|
||||
:token-gated? true
|
||||
:can-post? false
|
||||
:members {"0x01" {"roles" [1]}
|
||||
"0x02" {"roles" [1]}
|
||||
"0x05" {"roles" [1]}}}}
|
||||
:members {"0x01" {"roles" [1]}
|
||||
"0x02" {"roles" [1]}
|
||||
"0x03" {"roles" [1]}
|
||||
"0x04" {"roles" [1]}}
|
||||
:can-request-access? false
|
||||
:outroMessage "bla"
|
||||
:verified false}]
|
||||
(swap! rf-db/app-db assoc-in [:communities community-id] community)
|
||||
(swap! rf-db/app-db assoc :profile/profile profile-test/sample-profile)
|
||||
(swap! rf-db/app-db assoc :visibility-status-updates visibility-status-updates)
|
||||
(is (= [member-id-2 member-id-1]
|
||||
(rf/sub [sub-name community-id chat-id-2 :online]))))
|
||||
|
||||
(testing "returns sorted community members per offline status"
|
||||
(swap! rf-db/app-db assoc-in [:communities community-id] community)
|
||||
(swap! rf-db/app-db assoc :profile/profile profile-test/sample-profile)
|
||||
(swap! rf-db/app-db assoc :visibility-status-updates visibility-status-updates)
|
||||
(is (= ["0x05"] (rf/sub [sub-name community-id chat-id-2 :offline]))))))
|
||||
|
||||
(h/deftest-sub :communities/chat-members
|
||||
[sub-name]
|
||||
(let [member-1-id "0x1-member"
|
||||
member-2-id "0x2-member"
|
||||
|
||||
visibility-status-updates
|
||||
{member-1-id {:status-type constants/visibility-status-always-online}
|
||||
member-2-id {:status-type constants/visibility-status-always-online}}
|
||||
|
||||
communities
|
||||
{community-id {:id community-id
|
||||
:chats {channel-id {:token-gated? false
|
||||
:members (clj->js {member-2-id {}})}}
|
||||
:members (clj->js {member-1-id {}
|
||||
member-2-id {}})}}]
|
||||
(testing "members from non token-gated channels and online"
|
||||
(swap! rf-db/app-db assoc :visibility-status-updates visibility-status-updates)
|
||||
(swap! rf-db/app-db assoc :profile/profile profile-test/sample-profile)
|
||||
(swap! rf-db/app-db assoc :communities communities)
|
||||
|
||||
;; When channel is not token-gated, all community members are considered.
|
||||
(is (= [member-1-id member-2-id]
|
||||
(rf/sub [sub-name community-id chat-id :online]))))
|
||||
|
||||
(testing "members from token-gated channels and online"
|
||||
(swap! rf-db/app-db assoc :visibility-status-updates visibility-status-updates)
|
||||
(swap! rf-db/app-db assoc :profile/profile profile-test/sample-profile)
|
||||
(swap! rf-db/app-db assoc
|
||||
:communities
|
||||
(assoc-in communities [community-id :chats channel-id :token-gated?] true))
|
||||
|
||||
;; When channel is token-gated, only its members are considered.
|
||||
(is (= [member-2-id]
|
||||
(rf/sub [sub-name community-id chat-id :online]))))
|
||||
|
||||
(testing "members from token-gated channels and offline"
|
||||
(swap! rf-db/app-db assoc :profile/profile profile-test/sample-profile)
|
||||
(swap! rf-db/app-db assoc
|
||||
:communities
|
||||
(assoc-in communities [community-id :chats channel-id :token-gated?] true))
|
||||
|
||||
(is (= [member-2-id] (rf/sub [sub-name community-id chat-id :offline]))))
|
||||
|
||||
(testing "members from non token-gated channels and offline"
|
||||
(swap! rf-db/app-db assoc :profile/profile profile-test/sample-profile)
|
||||
(swap! rf-db/app-db assoc :communities communities)
|
||||
|
||||
(is (= [member-1-id member-2-id]
|
||||
(rf/sub [sub-name community-id chat-id :offline]))))))
|
||||
:visibility-status-updates
|
||||
{"0x01" {:status-type constants/visibility-status-always-online}
|
||||
"0x02" {:status-type constants/visibility-status-always-online}})
|
||||
(testing "a non-token gated community should look at all members of a community"
|
||||
(is (= [{:title (i18n/label :t/online)
|
||||
:data ["0x01" "0x02"]}
|
||||
{:title (i18n/label :t/offline)
|
||||
:data ["0x03" "0x04"]}]
|
||||
(rf/sub [sub-name community-id chat-id-1]))))
|
||||
(testing "a token gated community should use the members option in the channel"
|
||||
(is (= [{:title (i18n/label :t/online)
|
||||
:data ["0x01" "0x02"]}
|
||||
{:title (i18n/label :t/offline)
|
||||
:data ["0x05"]}]
|
||||
(rf/sub [sub-name community-id chat-id-2])))))))
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
|
||||
(re-frame/reg-sub :communities/accounts-to-reveal
|
||||
(fn [[_ community-id]]
|
||||
[(re-frame/subscribe [:wallet/operable-accounts])
|
||||
[(re-frame/subscribe [:wallet/operable-accounts-without-watched-accounts])
|
||||
(re-frame/subscribe [:communities/addresses-to-reveal community-id])])
|
||||
(fn [[accounts addresses] _]
|
||||
(filter #(contains? addresses (:address %))
|
||||
@@ -61,7 +61,7 @@
|
||||
|
||||
(re-frame/reg-sub :communities/airdrop-account
|
||||
(fn [[_ community-id]]
|
||||
[(re-frame/subscribe [:wallet/operable-accounts])
|
||||
[(re-frame/subscribe [:wallet/operable-accounts-without-watched-accounts])
|
||||
(re-frame/subscribe [:communities/airdrop-address community-id])])
|
||||
(fn [[accounts airdrop-address] _]
|
||||
(->> accounts
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user