Compare commits

...
Author SHA1 Message Date
Ibrahem Khalil 9abb794785 Merge branch 'develop' into 18877 2024-02-18 14:02:46 +02:00
Flavio Fraschetti bfed36d19f [Communities] Account selection: сan't see the entire text of the community rules (#18761)
Resolved the scrolling issue pertaining to community rules. Additionally, will ensure compatibility with iOS devices lacking safe-area-view in the follow-up.
2024-02-18 08:03:52 -03:00
ibrkhalil aefb0338ec Use placeholder value if no wallet name is supplied 2024-02-17 20:14:36 +02:00
Yevheniia Berdnyk 456ccb4c69 e2e: temp fix for image selection 2024-02-17 19:03:20 +02:00
Andrea Maria Piana be2c697b7c set light client 2024-02-16 16:30:44 +00:00
Ajay Sivan b27f323dd7 FIX: Addresses for permissions info button bg color (#18855) 2024-02-16 06:08:05 -08:00
Mohsen a33e7f2e90 [#18600] feat: implement set bio screen (#18765) 2024-02-16 16:19:44 +03:00
Ajay Sivan 54d9e5ebeb Share all current and future addresses - UI (#18843)
Fixes
- 'Confirm changes' button unexpected enabled state
- The Confirm button remains enabled even after deselecting all addresses when the user has watch-only wallet accounts.
- Disable the Confirm button when the user deselects the accounts with the token required to join.
- Use community color for address checkbox
2024-02-16 04:11:22 -08:00
Alexander 49567596da General QR code scanner flow (#18677)
* QR scanner

* More options for QR

* Router fixes

* Updates for `on-qr-code-scanned`

* Update for communities

* Updates

* Scan profile path

* More fixes

* Last fixes for scanning

* Fixes

* Fixes

* Fixes

* Test fixes

* Fixes

* Updated Utils.m

* Test fix
2024-02-16 12:06:28 +01:00
Nikolay 2e23dc7ad7 chore(wallet): update default placeholder name for account and watched address when adding it #18416 (#18683) 2024-02-15 20:30:38 +00:00
Mohsen dc53bddb66 [#18754] fix: scrollable addresses permission list in android (#18808) 2024-02-15 18:00:21 +03:00
Mohamed Javid 10f9fe11db [Fix] Broken wallet account emoji when syncing devices (#18828)
This commit fixes the broken wallet account emoji when syncing devices by adding a sanitization method for the wallet account emoji. The desktop can handle both variants (raw emoji and img tag); this PR adds the same for mobile.

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2024-02-15 18:52:59 +05:30
Flavio Fraschetti 2e13cfc47f [Refactor#18748] - Collectible Tag (#18804)
[Refactor Collectible Tag] Improve component schema and add component_spec
2024-02-15 09:52:24 -03:00
Siddarth Kumar e6ea8ae9a9 fix: iOS build stage to install app on simulator (#18839)
fixes #18836

## Summary

While introducing https://github.com/status-im/status-mobile/pull/18784 I missed out on adding an install step to `run-ios.sh` script.

`make run-ios` would work fine for simulators that already have the app installed but would fail for new emulators.
@flexsurfer  discovered this problem.

This commit fixes that by adding an install step to the script.

## Review notes
- `make run-clojure`
- `make run-ios` 
should just work.

## Platforms
- iOS
2024-02-15 17:55:06 +05:30
Alexander 834c0a3800 Design review: create profile password (#18607)
* Design review: create profile password

* Fixes

* Fixes

* Fixes

* Fixes

* Code style fix

* Fixes
2024-02-15 10:42:54 +01:00
Siddarth Kumar 2c96c38339 chore: disable hermes and cleanup gradle vars (#18832)
fixes #18831

We update the nix derivation to build android by passing `hermesEnabled` flag which checks the environment variable and if the environment variable is not set we default `hermesEnabled` to `true`.
This ensures that `hermes` is disabled for debug builds and enabled for release builds.

In this commit we also
- rename `nix/mobile/android/release.nix` → `nix/mobile/android/build.nix` since that nix file no longer generates release only builds.
- cleanup 2 other env vars and use the `gradle` project format
- replace `BUILD_NUMBER` with `verisonCode` for consistency
- replace `androidGradleOpts ` with `buildUrl ` 
- bump `status-jenkins-lib` to v1.8.7
2024-02-15 13:48:11 +05:30
Mohsen 1d2c01c16b [#18752] fix: The W/O address shouldn't be in account selection (#18809) 2024-02-14 20:37:52 +03:00
Andrea Maria Piana 8dc71f6432 Fix translations 2024-02-14 16:12:17 +00:00
Andrea Maria Piana 25f9499454 remove rounding 2024-02-14 16:12:17 +00:00
Siddarth Kumar 42cab08553 chore: Improve DX for building the app locally (#18784)
After upgrading `react-native` to `0.72.5` we frequently started seeing the _red screen of death_ on both `Android` and `iOS` simulators right after the app was built and installed.
This used to happen because our workflow required us to do the following :
- `make run-clojure`
- `make run-metro`
- `make run-ios` OR `make run-android`

The problem with this approach was after `metro` was started the `iOS`, `Android` build step would change the files that `metro` couldn't handle and hence metro would go out of sync.
The quick fix back then was to restart `metro` terminal and to open the app again from the simulator.
This was however not a good DX.

This commit fixes that.
We no longer rely on `react-native` cli to generate and deploy debug builds on simulators. We take control of the process via our own script. The new workflow introduced in this commit will first build the app, then install the app on the simulators and then start metro terminal. When `metro` is successfully running the script will then open the app.

The new workflow now is :
- `make run-clojure`
- `make run-ios` OR `make run-android`
2024-02-14 19:58:45 +05:30
Jamie Caprani 83523b1923 chore: clean up wallet reframe root keys (#18730) 2024-02-14 04:29:35 -08:00
Mohamed Javid f762cdcf9b [Feature] Wallet - Network filter UI in Wallet home and account screens (#18772)
This commit adds network filter UI in the Wallet home and account screens.

---------

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2024-02-14 15:37:04 +05:30
Ibrahem Khalil 357db52720 Use blurred background for all type shell? bottom sheets (#18400) 2024-02-13 22:54:43 +02:00
Yevheniia Berdnyk 5fdaf0054d e2e: updated join community 2024-02-13 15:26:19 +00:00
Andrea Maria Piana 4e3ce54ac4 Reduce renderings in community overview
https://github.com/status-im/status-go/compare/c15f9e73...1ea2bd99

This PR reduces the amount of rendering in community overview.
It limits the amount of data is passed to components to the necessary,
and it creates functions on component initialization so that they won't
cause a re-render.
Performance of the header are very noticeably better on my local
environment, though on nightly builds is less noticeable.

It also removes data from `communities/communities id` as that cause a
re-render of any component subscribed to it.
2024-02-13 15:26:19 +00:00
Andrea Maria Piana 37be08d2d7 Fix permission screen & trailing zeros
https://github.com/status-im/status-go/compare/c15f9e73...b7b7660a

There were a few issues with the permission screen:
1) Wrong permission was displayed when able to join
2) If not able to join, we were showing both admin/member permissions
3) Trailing zeros in token amount
2024-02-13 15:26:19 +00:00
Paul FitzgeraldandJamie Caprani 3c4d27b6ff chore(wallet): connect UI and data model for input page in bridge flow (#18678)
Co-authored-by: Jamie Caprani <jamiecaprani@gmail.com>
2024-02-13 07:24:02 -08:00
jakub c5df51d944 nix: upgrade from 2.13.4 to 2.19.3
We are moving location of symlinks for build derivations for `gcroots`
from `/nix/var/nix/gcroots/per-user` to `.nix-gcroots` in the repo to
avoid errors like this caused by profile migration in `2.14` release:
```
error: creating directory '/nix/var/nix/gcroots/per-user/joe': Permission denied
```
For more details see: https://github.com/NixOS/nix/issues/8564

To upgrade without using `make nix-purge` use `make nix-upgrade`.

Related infra change:
https://github.com/status-im/infra-ci/commit/37c6ce47

Signed-off-by: Jakub Sokołowski <jakub@status.im>
2024-02-13 13:32:50 +01:00
NikolayandJamie Caprani b1c467de19 feat(wallet): add ui for account origin info (#18725)
Co-authored-by: Jamie Caprani <jamiecaprani@gmail.com>
2024-02-13 03:27:28 -08:00
Shivek Khurana c766d21296 ⚒️ Fix typo (#18794) 2024-02-12 20:50:07 +00:00
e86faa0767 feat(quo): implement wallet - input amount component (#18687)
Co-authored-by: Siddarth Kumar <siddarthkay@gmail.com>
Co-authored-by: Jamie Caprani <jamiecaprani@gmail.com>
2024-02-12 07:36:23 -08:00
Shivek Khurana abe0342be0 🚟 Add schema batch 2 (#18696)
* 🚟 Add schema batch 2

*  Add maybe and optionals

* 🧑‍⚖️ Make theme a required prop

* 🍙 Fix misplaced square brackets that broke spec

* 🎨 Assume default theme and fix tests

- Fixes #18734

* ⬆️ Update schema and rebase

* 🧪 Update tests

* 🆙 Update progress bar value to be string or int

* 🔩 Tighten schema
2024-02-12 14:44:40 +00:00
Mohamed Javid 6669c33e33 [Fix] Edited wallet account name persists in other edit account screens (#18729)
This commit fixes the edited wallet account name that persists in other account edit screens.

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2024-02-12 19:11:17 +05:30
Ajay Sivan 1351a54fd1 fix - Button colors have become darker (#18776) 2024-02-12 05:06:18 -08:00
Ibrahem Khalil da192dcdb3 Dismiss keyboard when opening mute chat drawer (#18486) 2024-02-12 13:05:56 +02:00
Flavio Fraschetti 6e22e63158 [Communities] Add quo Collectible Tag (#18748)
* register and basic imports for collectible tag component

* implement variations and design for collectible tag component

* Refined based on review feedback and implemented schema enhancements
2024-02-12 07:40:12 -03:00
Mohamed Javid d1f22cd2b8 Wallet - Clear input on long press delete (#18732)
This commit allows clearing the amount input (in the send flow) by long-pressing the delete key.

---------

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2024-02-10 00:09:15 +05:30
Parvesh Monu 9dacc9e8af error and app freezing when deselecting account (#18759) 2024-02-09 23:32:58 +05:30
Jamie Caprani 9acf67dd5b feat: add basic infrastructure for contract tests (#18665) 2024-02-09 09:10:09 -08:00
Brian Sztamfater ab73c7e56a feat: show estimated fees when calculating route (#18309)
Signed-off-by: Brian Sztamfater <brian@status.im>
2024-02-09 13:43:14 -03:00
Yevheniia Berdnyk f1db73d149 e2e: updated deep/universal links tests 2024-02-09 15:51:14 +02:00
Ajay Sivan d78d7719cd Account permissions component malli schema fix (#18770) 2024-02-09 05:44:11 -08:00
Alexander e381998b19 Toast notification design fixes (#18468)
* Toast notification design fixes

* Code style fix

* Smaller fix

* Style fix

* Fixes

* Style fixes

* Fixes

* Fixes
2024-02-09 09:54:14 +01:00
Brian Sztamfater 025c38ae88 fix: address is lost on re-render (#18593)
Signed-off-by: Brian Sztamfater <brian@status.im>
2024-02-08 16:29:05 -03:00
mmilad75 523a5809d0 Wallet - Add malli spec to Quo Wallet components (batch 1) #18354 (#18707)
* draft

* add account_card

* add account-origin

* add account_overview

* add account_permission

* add address_text

* add confirmation_progress

* wrapping up

* resolve comments and fix ci issues

* fix ci issues

* fix transaction_progress tests

* fix all tests

* fix lint issues
2024-02-08 20:31:57 +03:30
Icaro Motta 505f2fe624 Communities: Show relevant tokens (#18636)
Show "relevant tokens" in account selection step. Related status-go PR
https://github.com/status-im/status-go/pull/4631.

- Balances are fetched on the account selection screen. Assuming most users
  won't care about selecting addresses, this eliminates unnecessary calls to get
  balances, at the expense of refetching them every time the component is
  mounted. I think for now this is acceptable.
- Relevant tokens is a terminology used in Figma, but more specifically, it
  means "balances for all assets that have at least one Token Criteria
  associated, and for a given address". Or, as I tried to give it a more
  distinct name, "permissioned balances".

Areas that may be impacted: None because all affected code is behind a disabled
feature flag.

Fixes https://github.com/status-im/status-mobile/issues/18126
2024-02-08 11:17:15 -03:00
Ajay Sivan ac32de89f9 Update drawer/bottom-actions component with top & top error variants (#18513) 2024-02-08 02:31:50 -08:00
Mohamed Javid 52a82fffab Bump status-go version to 0.174.2 (#18745)
This commit updates the status-go version to use v0.174.2

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2024-02-08 15:19:27 +05:30
Mohamed Javid d1c8f6411d [Feature] Wallet - Display token price (#18438)
This commit adds a feature to display token prices for each token in the Wallet home and Account screens.

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2024-02-08 13:31:42 +05:30
Mohsen 7b639f2ffe [#18453] feat: implement change accent colour screen (#18517) 2024-02-07 19:49:21 +03:00
Brian Sztamfater ae7ee33f00 fix: ENS regex not being used to detect an ENS on address input (#18626)
Signed-off-by: Brian Sztamfater <brian@status.im>
2024-02-07 12:39:49 -03:00
Mohamed Javid c70646bd5c [Fix] Scanned address not populated in send flow (#18727)
This commit fixes the scanned address not pasted/populated in the "Send to" input field.

---

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2024-02-07 19:56:42 +05:30
mmilad75andLungu Cristian 484ab919b1 Update Wallet: Share QR Code #18454 (#18479)
* add icon

* fix lint issues

* fix preview props and header icon

* fix watched-address icon condition

* fix header tabs

* draft

* fix network name in multichain mode

* fix lint issues

* update tests

* update comments

* add background color

* update preview file

* update usages

* feat: add schema to quo/share-qr-code component

* test: fixed & re-factored tests after adding schema

* bring back gradient structure

* fix scrolling issue

* fix lint issues

* fix usage

---------

Co-authored-by: Lungu Cristian <lungucristian95@gmail.com>
2024-02-07 17:08:41 +03:30
Omar Basem 052d88b531 Wallet: bridging - assets list (#18713)
Wallet: bridging - assets list
2024-02-07 16:55:14 +04:00
Omar Basem 0aba789b5c feat: new keypair - check your backup (#18667)
* feat: new keypair - check your backup
2024-02-07 15:53:29 +04:00
Omar Basem 73450d736a Wallet: fix nested scrollviews warning (#18714)
Wallet: fix nested scrollviews warning (#18714)
2024-02-07 09:32:53 +04:00
Siddarth Kumar cc63c8fff5 init: maestro dev automation (#18712)
The purpose of this commit is to allow running `maestro` automations to enhance productivity by **automating** mundane tasks.

Other flows like
- send contact request
- sync devices
- join a community
etc will be a part of follow ups.

In this commit we provide a `make` command to run a very common task like creating account & login :
`make auto-login`

We also allow another `make` command to run any other custom flow :
`make auto-custom FLOW="maestro/create-account-or-login.yaml"`

A `maestro` folder has been added with 3 `yaml` files which are very easy to understand
ref : https://maestro.mobile.dev/api-reference/commands
2024-02-06 23:29:35 +05:30
Jamie Caprani ef6027575f Add a UI for toggling developer feature flags (#18602) 2024-02-06 08:24:45 -08:00
247 changed files with 4696 additions and 1893 deletions
+13 -12
View File
@@ -16,18 +16,19 @@
:clj-kondo-config {:level :error}
:cond-else {:level :error}
:consistent-alias {:level :error
:aliases {clojure.set set
clojure.string string
clojure.walk walk
malli.core malli
malli.dev.pretty malli.pretty
malli.dev.virhe malli.virhe
malli.error malli.error
malli.generator malli.generator
malli.transform malli.transform
malli.util malli.util
schema.core schema
taoensso.timbre log}}
:aliases {clojure.set set
clojure.string string
clojure.walk walk
malli.core malli
malli.dev.pretty malli.pretty
malli.dev.virhe malli.virhe
malli.error malli.error
malli.generator malli.generator
malli.transform malli.transform
malli.util malli.util
schema.core schema
status-im.feature-flags ff
taoensso.timbre log}}
:deprecated-namespace {:level :warning}
:docstring-blank {:level :error}
:equals-true {:level :error}
+3
View File
@@ -194,3 +194,6 @@ test/appium/tests/users.py
## git hooks
lefthook.yml
## metro server logs
metro-server-logs.log
+28 -15
View File
@@ -44,7 +44,7 @@ export KEYSTORE_PATH ?= $(HOME)/.gradle/status-im.keystore
# Our custom config is located in nix/nix.conf
export NIX_USER_CONF_FILES = $(PWD)/nix/nix.conf
# Location of symlinks to derivations that should not be garbage collected
export _NIX_GCROOTS = /nix/var/nix/gcroots/per-user/$(USER)/status-mobile
export _NIX_GCROOTS = ./.nix-gcroots
# Defines which variables will be kept for Nix pure shell, use semicolon as divider
export _NIX_KEEP ?= TMPDIR,BUILD_ENV,\
BUILD_TYPE,BUILD_NUMBER,COMMIT_HASH,\
@@ -214,7 +214,7 @@ build-fdroid: ##@build Build release for F-Droid
build-android: export BUILD_ENV ?= prod
build-android: export BUILD_TYPE ?= nightly
build-android: export BUILD_NUMBER ?= $(TMP_BUILD_NUMBER)
build-android: export ORG_GRADLE_PROJECT_versionCode ?= $(TMP_BUILD_NUMBER)
build-android: export ANDROID_ABI_SPLIT ?= false
build-android: export ANDROID_ABI_INCLUDE ?= armeabi-v7a;arm64-v8a;x86
build-android: ##@build Build unsigned Android APK
@@ -267,7 +267,7 @@ run-clojure: ##@run Watch for and build Clojure changes for mobile
run-metro: export TARGET := clojure
run-metro: ##@run Start Metro to build React Native changes
@scripts/start-react-native.sh
@scripts/run-metro.sh
run-re-frisk: export TARGET := clojure
run-re-frisk: ##@run Start re-frisk server
@@ -278,22 +278,15 @@ run-android: export TARGET := android
# Disabled for debug builds to avoid 'maximum call stack exceeded' errors.
# https://github.com/status-im/status-mobile/issues/18493
run-android: export ORG_GRADLE_PROJECT_hermesEnabled := false
# INFO: If it's empty (no devices attached, parsing issues, script error) - for Nix it's the same as not set.
run-android: export ANDROID_ABI_INCLUDE ?= $(shell ./scripts/adb_devices_abis.sh)
run-android: ##@run Build Android APK and start it on the device
npx react-native run-android --appIdSuffix debug
@scripts/run-android.sh
SIMULATOR=iPhone 13
# TODO: fix IOS_STATUS_GO_TARGETS to be either amd64 or arm64 when RN is upgraded
run-ios: export TARGET := ios
run-ios: export IOS_STATUS_GO_TARGETS := ios/arm64;iossimulator/amd64
run-ios: export XCBeautify=$(shell which xcbeautify) # for react-native-cli to pick this up and to auto format output
run-ios: ##@run Build iOS app and start it in a simulator/device
ifneq ("$(SIMULATOR)", "")
npx react-native run-ios --simulator="$(SIMULATOR)"
else
npx react-native run-ios
endif
run-ios: ##@run Build iOS app and start it in on the simulator
@scripts/run-ios.sh "${SIMULATOR}"
show-ios-devices: ##@other shows connected ios device and its name
xcrun xctrace list devices
@@ -373,15 +366,20 @@ test-watch-for-repl: ##@test Watch all Clojure tests and support REPL connection
"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
test-unit: export SHADOW_NS_REGEXP := ^(?!status-im\.integration-test).*-test$$
test-unit: export SHADOW_NS_REGEXP := ^(?!tests\.integration-test)(?!tests-im\.contract-test).*-test$$
test-unit: ##@test Run unit tests
test-unit: _test-clojure
test-integration: export SHADOW_OUTPUT_TO := target/integration_test/test.js
test-integration: export SHADOW_NS_REGEXP := ^status-im\.integration-test.*$$
test-integration: export SHADOW_NS_REGEXP := ^tests\.integration-test.*$$
test-integration: ##@test Run integration tests
test-integration: _test-clojure
test-contract: export SHADOW_OUTPUT_TO := target/contract_test/test.js
test-contract: export SHADOW_NS_REGEXP := ^tests\.contract-test.*$$
test-contract: ##@test Run contract tests
test-contract: _test-clojure
android-test: jsbundle
android-test: export TARGET := android
android-test:
@@ -457,3 +455,18 @@ repl-clojure: ##@repl Start Clojure repl for mobile App
yarn shadow-cljs cljs-repl mobile
repl-nix: nix-repl ##@repl Start an interactive Nix REPL
#--------------
# Dev Automation Flows
#--------------
auto-login: export TARGET := default
auto-login: ##@auto runs flow for login or onboarding app on simulator/emulator
maestro test "maestro/create-account-or-login.yaml"
auto-custom: export TARGET := default
auto-custom: ##@auto runs any custom maestro automation flow on simulator/emulator
ifndef FLOW
$(error Usage: make automate FLOW=your-maestro-flow-file.yaml)
endif
maestro test "$(FLOW)"
+2 -2
View File
@@ -1,11 +1,11 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.8.5'
library 'status-jenkins-lib@v1.8.7'
/* Options section can't access functions in objects. */
def isPRBuild = utils.isPRBuild()
pipeline {
agent { label 'linux && x86_64 && nix-2.14' }
agent { label 'linux && x86_64 && nix-2.19' }
options {
timestamps()
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.8.5'
library 'status-jenkins-lib@v1.8.7'
pipeline {
agent { label 'linux' }
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.8.5'
library 'status-jenkins-lib@v1.8.7'
pipeline {
agent { label 'linux' }
+2 -2
View File
@@ -1,11 +1,11 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.8.5'
library 'status-jenkins-lib@v1.8.7'
/* Options section can't access functions in objects. */
def isPRBuild = utils.isPRBuild()
pipeline {
agent { label 'macos && arm64 && nix-2.14 && xcode-15.1' }
agent { label 'macos && arm64 && nix-2.19 && xcode-15.1' }
parameters {
string(
+2 -2
View File
@@ -1,11 +1,11 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.8.5'
library 'status-jenkins-lib@v1.8.7'
/* Options section can't access functions in objects. */
def isPRBuild = utils.isPRBuild()
pipeline {
agent { label 'linux && x86_64 && nix-2.14' }
agent { label 'linux && x86_64 && nix-2.19' }
options {
timestamps()
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.8.5'
library 'status-jenkins-lib@v1.8.7'
pipeline {
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.8.5'
library 'status-jenkins-lib@v1.8.7'
pipeline {
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.8.5'
library 'status-jenkins-lib@v1.8.7'
pipeline {
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.8.5'
library 'status-jenkins-lib@v1.8.7'
pipeline {
agent { label 'macos' }
+2 -2
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.8.5'
library 'status-jenkins-lib@v1.8.7'
pipeline {
agent { label params.AGENT_LABEL }
@@ -82,7 +82,7 @@ pipeline {
env.NIXPKGS_SYSTEM_OVERRIDE = nixSysOverride(os, arch, 'android')
/* Build/fetch deps required to build android release. */
nix.build(
attr: 'targets.mobile.android.release.buildInputs',
attr: 'targets.mobile.android.build.buildInputs',
sandbox: false,
pure: false,
link: false
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.8.5'
library 'status-jenkins-lib@v1.8.7'
pipeline {
agent { label 'linux' }
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.8.5'
library 'status-jenkins-lib@v1.8.7'
pipeline {
agent {
+1 -2
View File
@@ -122,10 +122,9 @@ See below:
Do the following:
Ensure you have 3 terminals running the following
Ensure you have 2 terminals running the following
- `make run-clojure`
- `make run-metro`
- `make run-ios` / `make run-android`
[See the STARTING GUIDE for details](STARTING_GUIDE.md#development)
+1 -2
View File
@@ -15,8 +15,7 @@ This step will take a while the first time as it will download all dependencies.
There are three steps necessary to start development, in this case for Android:
1. `make run-clojure` - Compiles Clojure into JavaScript, watches for changes on cljs files, and hot-reloads code in the app.
2. `make run-metro` - Starts metro bundler and watches JavaScript code.
3. `make run-android` or `make run-ios` - Builds the Android/iOS app and starts it on the device.
2. `make run-android` or `make run-ios` - Builds the Android/iOS app, starts it on the device and starts metro bundler.
The first two will continue watching for changes and keep re-building the app. They need to be ready first.
The last one will exit once the app is up and ready.
+1 -1
View File
@@ -793,7 +793,7 @@ CHECKOUT OPTIONS:
:submodules: true
SPEC CHECKSUMS:
boost: 57d2868c099736d80fcd648bf211b4431e51a558
boost: 64032b9e9b938fda23325e68a3771f0fabf414dc
BVLinearGradient: 612a04ff38e8480291f3379ee5b5a2c571f03fe0
CryptoSwift: c4f2debceb38bf44c80659afe009f71e23e4a082
DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
+11
View File
@@ -0,0 +1,11 @@
appId: im.status.ethereum.debug
---
- runFlow: # to run onboarding flow only when device is in onboarding state
when:
visible: "Im new to Status"
file: onboarding.yaml
- runFlow: # to run login flow only when device is on login screen
when:
visible: "Profile password"
file: login.yaml
+5
View File
@@ -0,0 +1,5 @@
appId: im.status.ethereum.debug
---
- tapOn: "password-input"
- inputText: "password123" # update your account password here
- tapOn: login-button
+15
View File
@@ -0,0 +1,15 @@
appId: im.status.ethereum.debug
---
- tapOn: "Im new to Status"
- tapOn: "small-option-card"
- tapOn: "Your name"
- inputText: "DeviceA"
- tapOn: "submit-create-profile-button"
- inputText: "password123"
- tapOn: "Repeat password"
- inputText: "password123"
- tapOn: "disclaimer-touchable-opacity"
- tapOn: "Confirm password"
- tapOn: "skip-identifiers"
- tapOn: "enable-notifications-later-button"
- tapOn: "welcome-button"
@@ -120,9 +120,12 @@ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(checkAddressChecksum:(NSString *)address)
return StatusgoCheckAddressChecksum(address);
}
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(isAddress:(NSString *)address) {
return StatusgoIsAddress(address);
}
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(toChecksumAddress:(NSString *)address) {
return StatusgoToChecksumAddress(address);
}
@end
+7 -5
View File
@@ -61,7 +61,7 @@ We will use the `make jsbundle` target as an example of a derivation you can bui
3. [`build.sh`](/nix/scripts/build.sh) calls `nix-build --attr targets.mobile.jsbundle` with extra arguments
4. `nix-build` builds the derivation from [`nix/mobile/jsbundle/default.nix`](/nix/mobile/jsbundle/default.nix)
The same can be done for other targets like `targets.mobile.android.release`.
The same can be done for other targets like `targets.mobile.android.build`.
Except in that case extra arguments are required which is why the [`scripts/release-android.sh`](/scripts/release-android.sh) is used in the `make release-android` target.
If you run `make release-android` you'll see the `nix-build` command used:
@@ -71,21 +71,23 @@ nix-build \
--fallback \
--no-out-link \
--show-trace \
--attr targets.mobile.android.release \
--attr targets.mobile.android.build \
--argstr secrets-file '/tmp/tmp-status-mobile-559a3a441/tmp.xAnrPuNtAP' \
--option extra-sandbox-paths '/home/joe/.gradle/status-im.keystore /tmp/tmp-status-mobile-559a3a441/tmp.xAnrPuNtAP' \
default.nix
```
Some of those are required which is why just calling:
```
nix-build --attr targets.mobile.android.release
nix-build --attr targets.mobile.android.build
```
Would fail.
# Garbage Collection
The `make nix-gc` target calls `nix-store --gc` and normally would remove almost everything, but to prevent that we place symlinks to protected derivations in `/nix/var/nix/gcroots` subfolder. Specifically:
The `make nix-gc` target calls `nix-store --gc` and normally would remove almost everything, but to prevent that we place symlinks to protected derivations in `.nix-gcroots` folder. Specifically:
```sh
_NIX_GCROOTS="${_NIX_GCROOTS:-/nix/var/nix/gcroots/per-user/${USER}/status-mobile}"
_NIX_GCROOTS="${_NIX_GCROOTS:-${GIT_ROOT}/.nix-gcroots}
```
These symlinks in turn will be symlinked from `/nix/var/nix/gcroots/auto` through use of `nix-store --add-gcroots`.
Whenever `nix/scripts/build.sh` or `nix/scripts/shell.sh` are called they update symlinks named after given targets in that folder. This in combination with `keep-outputs = true` set in `nix/nix.conf` prevents garbage collection from removing too much.
+1 -1
View File
@@ -28,7 +28,7 @@ export STATUS_GO_IPFS_GATEWAY_URL="https://ipfs.status.im/"
```
You can see the defaults in code:
- [`nix/status-go/default.nix`](./status-go/default.nix)
- [`nix/mobile/android/release.nix`](./mobile/android/release.nix)
- [`nix/mobile/android/release.nix`](./mobile/android/build.nix)
## Shell
+1 -1
View File
@@ -6,7 +6,7 @@ This directory contains the tools and the data that allows Nix to manage Gradle
Simply calling `generate.sh` should result in a `deps.json` file which is used in the derivation that provides Gradle dependencies when building the Android app.
You can see in [`nix/mobile/android/release.nix`](../../mobile/android/release.nix) that it's used via the `-Dmaven.repo.local='${deps.gradle}'` Gradle flag.
You can see in [`nix/mobile/android/release.nix`](../../mobile/android/build.nix) that it's used via the `-Dmaven.repo.local='${deps.gradle}'` Gradle flag.
# Files
@@ -7,15 +7,15 @@
# Path to the file containing secret environment variables
secretsFile ? "",
# Build type (influences which .env file gets used for feature flags)
# TODO: pr or relase for default?
buildType ? lib.getEnvWithDefault "BUILD_TYPE" "release",
# Used for versionCode
buildNumber ? lib.getEnvWithDefault "BUILD_NUMBER" 9999,
versionCode ? lib.getEnvWithDefault "ORG_GRADLE_PROJECT_versionCode" 9999,
# Included in APK Manifest for easier identification.
# TODO: or GIT_COMMIT from Jenkins?
commitHash ? lib.getEnvWithDefault "COMMIT_HASH" "unknown",
# Gradle options passed for Android builds
androidGradleOpts ? lib.getEnvWithDefault "ANDROID_GRADLE_OPTS" null,
commitHash ? lib.getEnvWithDefault "ORG_GRADLE_PROJECT_commitHash" "unknown",
# Disabled for debug builds to avoid 'maximum call stack exceeded' errors.
# https://github.com/status-im/status-mobile/issues/18493
hermesEnabled ? lib.getEnvWithDefault "ORG_GRADLE_PROJECT_hermesEnabled" "true",
buildUrl ? lib.getEnvWithDefault "ORG_GRADLE_PROJECT_buildUrl" null,
statusGoSrcOverride ? lib.getEnvWithDefault "STATUS_GO_SRC_OVERRIDE" null,
# If APKs should be split based on architectures
androidAbiSplit ? lib.getEnvWithDefault "ANDROID_ABI_SPLIT" "false",
@@ -40,8 +40,10 @@ let
else if (elem buildType ["pr" "manual"]) then ".env.jenkins"
else ".env";
# There are only two types of Gradle build targets: pr and release
gradleBuildType = if buildType == "pr" then "Pr" else "Release";
gradleBuildType =
if buildType == "pr" then "Pr"
else if buildType == "debug" then "Debug"
else "Release";
apksPath = "./android/app/build/outputs/apk/${toLower gradleBuildType}";
@@ -81,7 +83,10 @@ in stdenv.mkDerivation rec {
ANDROID_ABI_INCLUDE = androidAbiInclude;
# Disabled for debug builds to avoid 'maximum call stack exceeded' errors.
# https://github.com/status-im/status-mobile/issues/18493
ORG_GRADLE_PROJECT_hermesEnabled = true;
ORG_GRADLE_PROJECT_versionCode = versionCode;
ORG_GRADLE_PROJECT_commitHash = commitHash;
ORG_GRADLE_PROJECT_buildUrl = buildUrl;
ORG_GRADLE_PROJECT_hermesEnabled = hermesEnabled;
# Fix for ERR_OSSL_EVP_UNSUPPORTED error.
NODE_OPTIONS = "--openssl-legacy-provider";
@@ -116,8 +121,6 @@ in stdenv.mkDerivation rec {
# Symlink React Native entrypoint.
cp -Lr ${builtJsBundle} ./result
pwd
find -L result
# Copy android/ directory
mkdir -p ./android/build
@@ -144,7 +147,6 @@ in stdenv.mkDerivation rec {
"LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${makeLibraryPath [ pkgs.zlib ]}";
gradleCommand = ''
${pkgs.gradle}/bin/gradle \
${toString androidGradleOpts} \
--console=plain \
--offline \
--no-daemon \
@@ -152,8 +154,6 @@ in stdenv.mkDerivation rec {
--no-watch-fs \
--no-build-cache \
-Dmaven.repo.local='${deps.gradle}' \
-PversionCode=${toString buildNumber} \
-PcommitHash=${commitHash} \
assemble${gradleBuildType}
'';
in
@@ -171,12 +171,14 @@ in stdenv.mkDerivation rec {
${adhocEnvVars} ${gradleCommand}
popd > /dev/null
'';
doCheck = true;
checkPhase = ''
doCheck = buildType != "debug";
checkPhase = ''
ls ${apksPath}/*.apk \
| xargs -n1 ${pkgs.unzip}/bin/unzip -qql \
| grep 'index.android.bundle'
'';
installPhase = ''
mkdir -p $out
cp ${apksPath}/*.apk $out/
+2 -2
View File
@@ -2,7 +2,7 @@
, jsbundle, status-go, androidPkgs, androidShell }:
rec {
release = callPackage ./release.nix {
build = callPackage ./build.nix {
inherit jsbundle status-go;
};
@@ -15,7 +15,7 @@ rec {
];
inputsFrom = [
(release {})
(build {})
androidShell
];
+1
View File
@@ -97,4 +97,5 @@ in {
patchMavenSources = callPackage ./pkgs/patch-maven-srcs { };
goMavenResolver = callPackage ./pkgs/go-maven-resolver { };
xcbeautify = callPackage ./pkgs/xcbeautify { };
idb-companion = callPackage ./pkgs/idb-companion { };
}
+29
View File
@@ -0,0 +1,29 @@
{ stdenv, fetchurl, lib }:
stdenv.mkDerivation rec {
pname = "idb-companion";
version = "1.1.8";
src = fetchurl {
url = "https://github.com/facebook/idb/releases/download/v${version}/idb-companion.universal.tar.gz";
sha256 = "sha256-O3LMappbGiKhiCBahAkNOilDR6hGGA79dVzxo8hI4+c=";
};
buildInputs = [ ];
unpackPhase = ''
tar -xzf $src
'';
installPhase = ''
mkdir -p $out/bin
cp -r ./* $out/bin/
'';
meta = with lib; {
description = "A powerful command line tool for automating iOS simulators and devices";
homepage = "https://github.com/facebook/idb";
license = licenses.mit;
platforms = platforms.darwin;
};
}
+1 -1
View File
@@ -45,7 +45,7 @@ if [[ -z "${TARGET}" ]]; then
fi
# Hack fix for missing Android SDK for aarch64 on Darwin. See systemOverride in `nix/pkgs.nix`.
if [[ "${TARGET}" =~ ^(targets.status-go.mobile.android|targets.mobile.android.release)$ ]]; then
if [[ "${TARGET}" =~ ^(targets.status-go.mobile.android|targets.mobile.android.build)$ ]]; then
os=$(uname -s | tr '[:upper:]' '[:lower:]')
export NIXPKGS_SYSTEM_OVERRIDE="x86_64-${os}"
fi
+2 -2
View File
@@ -2,12 +2,12 @@
set -Ee
_NIX_GCROOTS="${_NIX_GCROOTS:-/nix/var/nix/gcroots/per-user/${USER}/status-mobile}"
GIT_ROOT=$(cd "${BASH_SOURCE%/*}" && git rev-parse --show-toplevel)
source "${GIT_ROOT}/nix/scripts/source.sh"
source "${GIT_ROOT}/scripts/colors.sh"
_NIX_GCROOTS="${_NIX_GCROOTS:-${GIT_ROOT}/.nix-gcroots}"
TARGET="${1}"
shift
if [[ -z "${TARGET}" ]]; then
+3 -5
View File
@@ -1,8 +1,6 @@
#!/usr/bin/env bash
# WARNING: Versions 2.14, 2.15, 2.16 have an issue creating gcroots:
# https://github.com/NixOS/nix/issues/8564
export NIX_VERSION="2.13.4"
export NIX_PACKAGE="nixVersions.nix_2_13"
export NIX_VERSION="2.19.3"
export NIX_PACKAGE="nixVersions.nix_2_19"
export NIX_INSTALL_URL="https://nixos.org/releases/nix/nix-${NIX_VERSION}/install"
export NIX_INSTALL_SHA256="a9908cc48f5886b4f22172bdd2f9657873276fd295e78c6ed97fb308c6d284d0"
export NIX_INSTALL_SHA256="73d47b0ab783fddca1b2d44a03d52a74c97c28a1fc8ff5a29419079302ef9c3d"
export NIX_INSTALL_PATH="/tmp/nix-install-${NIX_VERSION}"
+2 -2
View File
@@ -23,9 +23,9 @@ in mkShell {
# build specific utilities
clojure maven watchman
# other nice to have stuff
yarn nodejs python310
yarn nodejs python310 maestro
] # and some special cases
++ lib.optionals stdenv.isDarwin ([ cocoapods clang tcl ] ++ appleSDKFrameworks)
++ lib.optionals stdenv.isDarwin ([ cocoapods clang tcl idb-companion ] ++ appleSDKFrameworks)
++ lib.optionals (!stdenv.isDarwin) [ gcc8 ]
);
Binary file not shown.

After

Width:  |  Height:  |  Size: 695 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

+1 -3
View File
@@ -21,8 +21,6 @@ function append_env_export() {
VAR_VALUE=$(must_get_env "${VAR_NAME}")
echo "export ${VAR_NAME}=\"${VAR_VALUE}\";" >> "${SECRETS_FILE_PATH}"
}
export COMMIT_HASH="$(git rev-parse --verify HEAD)"
nixOpts=()
# We create if now so the trap knows its location
@@ -73,4 +71,4 @@ else
fi
"${GIT_ROOT}/nix/scripts/build.sh" targets.mobile.android.release "${nixOpts[@]}"
"${GIT_ROOT}/nix/scripts/build.sh" targets.mobile.android.build "${nixOpts[@]}"
+53
View File
@@ -0,0 +1,53 @@
#!/usr/bin/env bash
set -euo pipefail
set -m # needed to access jobs
GIT_ROOT=$(cd "${BASH_SOURCE%/*}" && git rev-parse --show-toplevel)
# We run Metro in background while calling adb.
cleanupMetro() {
pkill -f run-metro.sh
rm -f metro-server-logs.log
}
# Using function gives a neater jobspec name.
runMetro() {
nohup "${GIT_ROOT}/scripts/run-metro.sh" 2>&1 \
| tee metro-server-logs.log
}
waitForMetro() {
set +e # Allow grep command to fail in the loop.
TIMEOUT=5
echo "Waiting for Metro server..." >&2
while ! grep -q "Welcome to Metro" metro-server-logs.log; do
echo -n "." >&2
sleep 1
if ((TIMEOUT == 0)); then
echo -e "\nMetro server timed out, exiting" >&2
set -e # Restore errexit for rest of script.
return 1
fi
((TIMEOUT--))
done
set -e # Restore errexit for rest of script.
}
# Generate android debug build.
export ANDROID_ABI_INCLUDE=$("${GIT_ROOT}/scripts/adb_devices_abis.sh")
export BUILD_ENV=debug
export BUILD_TYPE=debug
"${GIT_ROOT}/scripts/build-android.sh"
# Install the APK on running emulator or android device.
adb install ./result/app-debug.apk
trap cleanupMetro EXIT ERR INT QUIT
runMetro &
waitForMetro
# Start the installed app.
adb shell monkey -p im.status.ethereum.debug 1
# bring metro job to foreground
fg 'runMetro'
+79
View File
@@ -0,0 +1,79 @@
#!/usr/bin/env bash
set -euo pipefail
set -m # needed to access jobs
GIT_ROOT=$(cd "${BASH_SOURCE%/*}" && git rev-parse --show-toplevel)
# We run Metro in background while calling adb.
cleanupMetro() {
pkill -f run-metro.sh
rm -f metro-server-logs.log
}
# Using function gives a neater jobspec name.
runMetro() {
nohup "${GIT_ROOT}/scripts/run-metro.sh" 2>&1 \
| tee metro-server-logs.log
}
waitForMetro() {
set +e # Allow grep command to fail in the loop.
TIMEOUT=5
echo "Waiting for Metro server..." >&2
while ! grep -q "Welcome to Metro" metro-server-logs.log; do
echo -n "." >&2
sleep 1
if ((TIMEOUT == 0)); then
echo -e "\nMetro server timed out, exiting" >&2
set -e # Restore errexit for rest of script.
return 1
fi
((TIMEOUT--))
done
set -e # Restore errexit for rest of script.
}
# Check if the first argument is provided
if [ -z "${1-}" ]; then
echo "Error: No simulator name provided." >&2
exit 1
fi
SIMULATOR=${1}
# get our desired UUID
UUID=$(xcrun simctl list devices | grep -E "$SIMULATOR \(" | head -n 1 | awk -F '[()]' '{print $2}')
# get simulator status
SIMULATOR_STATE=$(xcrun simctl list devices | grep -E "$SIMULATOR \(" | head -n 1 | awk '{print $NF}')
# sometimes a simulator is already running, shut it down to avoid errors
if [ "$SIMULATOR_STATE" != "(Shutdown)" ]; then
xcrun simctl shutdown "$UUID"
fi
# boot up iOS for simulator
xcrun simctl boot "$UUID"
# start the simulator
open -a Simulator --args -CurrentDeviceUDID "$UUID"
BUILD_DIR="${GIT_ROOT}/build"
#iOS build of debug scheme
xcodebuild -workspace "ios/StatusIm.xcworkspace" -configuration Debug -scheme StatusIm -destination id="$UUID" -derivedDataPath "${BUILD_DIR}" | xcbeautify
APP_PATH="${BUILD_DIR}/Build/Products/Debug-iphonesimulator/StatusIm.app"
# Install on the simulator
xcrun simctl install "$UUID" "$APP_PATH"
trap cleanupMetro EXIT ERR INT QUIT
runMetro &
waitForMetro
# launch the app when metro is ready
xcrun simctl launch "$UUID" im.status.ethereum.debug
# bring metro job to foreground
fg 'runMetro'
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
pkill -f 'react-native start'
react-native start --reset-cache
-17
View File
@@ -1,17 +0,0 @@
#!/usr/bin/env bash
GIT_ROOT=$(cd "${BASH_SOURCE%/*}" && git rev-parse --show-toplevel)
source "${GIT_ROOT}/scripts/colors.sh"
METRO_PORT=8081
METRO_PID="$(lsof -i :${METRO_PORT} | awk 'NR!=1 {print $2}' | sort -u | tr '\r\n' ' ')"
if [ ! -z "$METRO_PID" ]; then
echo -e "${YLW}TCP port ${METRO_PORT} is required by the Metro packager.\nThe following processes currently have the port open, preventing Metro from starting:${RST}"
ps -fp $METRO_PID
echo -e "${YLW}Do you want to terminate them (y/n)?${RST}"
read -n 1 term
[[ $term == 'y' ]] && kill $METRO_PID
fi
react-native start --reset-cache
-1
View File
@@ -176,7 +176,6 @@
;; option setupFilesAfterEnv.
test-helpers.component-tests-preload
status-im.setup.schema-preload
quo.core-spec
status-im.core-spec]
:ns-regexp "component-spec$"
@@ -36,7 +36,7 @@
[(mailserver/process-next-messages-request)
(bottom-sheet/hide-bottom-sheet-old)
(wallet/restart-wallet-service nil)
(add-new-contact/set-new-identity-reconnected)]
#(add-new-contact/set-new-identity-reconnected %)]
logged-in?
[(mailserver/process-next-messages-request)
+1 -1
View File
@@ -72,7 +72,7 @@
:PeerExchange true
:Port 0
:UDPPort 0
:LightClient true})
:LightClient false})
(def login-node-config
{:WalletConfig (cond-> {:Enabled true}
+5 -1
View File
@@ -81,5 +81,9 @@
{:db (-> db
(assoc :contacts/identity identity)
(assoc :contacts/ens-name ens-name))
:dispatch [:contacts/build-contact identity ens-name true]}
:dispatch [:contacts/build-contact
{:pubkey identity
:ens ens-name
:success-fn (fn [_]
{:dispatch [:open-modal :profile]})}]}
{:dispatch [:navigate-to :my-profile]})))
@@ -55,7 +55,8 @@
:on-change (fn [text]
(re-frame/dispatch [:wallet-legacy/search-recipient-filter-changed text])
(re-frame/dispatch [:set-in [:contacts/new-identity :state] :searching])
(debounce/debounce-and-dispatch [:contacts/set-new-identity text] 300))}]]))
(debounce/debounce-and-dispatch [:contacts/set-new-identity {:input text}]
300))}]]))
(defn section
[_ _ _]
@@ -4,14 +4,19 @@
(def backgrounds #{:photo :blur})
(defn custom-color-type
;; Note: We hardcode the :light theme for the background-color and apply an overlay when necessary, such
;; as for dark themes and pressed states. This approach is taken because, for communities, we only have
;; one color available, which is the light theme version.
;; For more information, see the related issue: https://github.com/status-im/status-mobile/issues/16396
(defn- custom-color-type
[customization-color icon-only?]
{:icon-color colors/white-opa-70
:label-color colors/white
:background-color (colors/custom-color customization-color 50)
:border-radius (when icon-only? 24)})
{:icon-color colors/white-opa-70
:label-color colors/white
:background-color (colors/resolve-color customization-color :light)
:border-radius (when icon-only? 24)
:overlay-customization-color customization-color})
(defn grey-photo
(defn- grey-photo
[theme pressed?]
{:icon-color (colors/theme-colors colors/neutral-80-opa-40 colors/white-opa-70 theme)
:label-color (colors/theme-colors colors/neutral-100 colors/white theme)
@@ -24,7 +29,7 @@
theme))
:blur-type (if (= theme :light) :light :dark)})
(defn grey-blur
(defn- grey-blur
[theme pressed?]
{:icon-color (colors/theme-colors colors/neutral-80-opa-40 colors/white-opa-70 theme)
:label-color (colors/theme-colors colors/neutral-100 colors/white theme)
@@ -32,7 +37,7 @@
(colors/theme-colors colors/neutral-80-opa-10 colors/white-opa-10 theme)
(colors/theme-colors colors/neutral-80-opa-5 colors/white-opa-5 theme))})
(defn outline-blur
(defn- outline-blur
[theme pressed?]
{:icon-color (colors/theme-colors colors/neutral-80-opa-40 colors/white-opa-40 theme)
:label-color (colors/theme-colors colors/neutral-100 colors/white theme)
@@ -40,7 +45,7 @@
(colors/theme-colors colors/neutral-80-opa-20 colors/white-opa-20 theme)
(colors/theme-colors colors/neutral-80-opa-10 colors/white-opa-10 theme))})
(defn grey
(defn- grey
[theme pressed?]
{:icon-color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)
:label-color (colors/theme-colors colors/neutral-100 colors/white theme)
@@ -50,7 +55,7 @@
colors/neutral-90
theme))})
(defn dark-grey
(defn- dark-grey
[theme pressed?]
{:icon-color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)
:label-color (colors/theme-colors colors/neutral-100 colors/white theme)
@@ -58,7 +63,7 @@
(colors/theme-colors colors/neutral-30 colors/neutral-60 theme)
(colors/theme-colors colors/neutral-20 colors/neutral-90 theme))})
(defn outline
(defn- outline
[theme pressed?]
{:icon-color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)
:label-color (colors/theme-colors colors/neutral-100 colors/white theme)
@@ -66,29 +71,32 @@
(colors/theme-colors colors/neutral-40 colors/neutral-60 theme)
(colors/theme-colors colors/neutral-30 colors/neutral-70 theme))})
(defn ghost
(defn- ghost
[theme pressed?]
{:icon-color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)
:label-color (colors/theme-colors colors/neutral-100 colors/white theme)
:background-color (when pressed?
(colors/theme-colors colors/neutral-10 colors/neutral-80 theme))})
(defn black
(defn- black
[pressed?]
{:label-color colors/white
:background-color (if pressed? colors/neutral-80 colors/neutral-95)})
(defn get-values
[{:keys [customization-color theme type background pressed? icon-only?]}]
(cond
(= type :primary) (custom-color-type customization-color icon-only?)
(= type :positive) (custom-color-type customization-color icon-only?)
(= type :danger) (custom-color-type customization-color icon-only?)
(and (= :photo background) (= type :grey)) (grey-photo theme pressed?)
(and (= :blur background) (= type :grey)) (grey-blur theme pressed?)
(and (= :blur background) (= type :outline)) (outline-blur theme pressed?)
(= type :grey) (grey theme pressed?)
(= type :dark-grey) (dark-grey theme pressed?)
(= type :outline) (outline theme pressed?)
(= type :ghost) (ghost theme pressed?)
(= type :black) (black pressed?)))
(let [customization-color (get {:primary customization-color
:positive :success
:danger :danger}
type)]
(cond
(contains? #{:primary :positive :danger} type) (custom-color-type customization-color
icon-only?)
(and (= :photo background) (= type :grey)) (grey-photo theme pressed?)
(and (= :blur background) (= type :grey)) (grey-blur theme pressed?)
(and (= :blur background) (= type :outline)) (outline-blur theme pressed?)
(= type :grey) (grey theme pressed?)
(= type :dark-grey) (dark-grey theme pressed?)
(= type :outline) (outline theme pressed?)
(= type :ghost) (ghost theme pressed?)
(= type :black) (black pressed?))))
+4 -7
View File
@@ -37,13 +37,10 @@
pressed? on-press-in on-press-out allow-multiple-presses?]
:or {type :primary
size 40
customization-color (cond (= type :primary) :blue
(= type :positive) :success
(= type :danger) :danger
:else nil)}}
customization-color (if (= type :primary) :blue nil)}}
children]
(let [{:keys [icon-color background-color label-color border-color blur-type
blur-overlay-color border-radius]}
blur-overlay-color border-radius overlay-customization-color]}
(button-properties/get-values {:customization-color customization-color
:background background
:type type
@@ -79,9 +76,9 @@
:icon-left icon-left
:icon-right icon-right})
inner-style)}
(when customization-color
(when overlay-customization-color
[customization-colors/overlay
{:customization-color customization-color
{:customization-color overlay-customization-color
:theme theme
:pressed? (if pressed? pressed? @pressed-state?)}])
(when (= background :photo)
@@ -35,7 +35,7 @@
(when (and @ref (>= index 0))
(some-> ^js @ref
(.scrollToIndex #js
{:animated true
{:animated false
:index index
:viewPosition 0.5})))))
50)))
@@ -3,7 +3,7 @@
(def container
{:height 40
:flex-direction :row
:padding-horizontal 20
:padding-horizontal 16
:align-items :center})
(defn text
@@ -30,7 +30,7 @@
(defn circular-progress
[{:keys [color percentage]}]
(let [strength-indicator-radius 6.5
(let [strength-indicator-radius 7.5
strength-indicator-circumference (* 2 Math/PI strength-indicator-radius)]
[svg/svg
{:view-box "0 0 16 16"
@@ -63,7 +63,10 @@
:alert [icon/icon :i/alert
{:color color
:size 16}]
[circular-progress {:color color :percentage percentage}])))
[rn/view
{:padding-left 4
:padding-right 2}
[circular-progress {:color color :percentage percentage}]])))
(defn view
"Options
@@ -3,34 +3,60 @@
[quo.components.drawers.bottom-actions.view :as bottom-actions]
[test-helpers.component :as h]))
(defn- render
[component]
(h/render-with-theme-provider component :light))
(h/describe "bottom actions tests"
(h/test "default render with no description and single action button"
(h/render [bottom-actions/view {:button-one-label "Request to join"}])
(render [bottom-actions/view {:actions :one-action :button-one-label "Request to join"}])
(h/is-truthy (h/get-by-text "Request to join")))
(h/test "render with description"
(h/render [bottom-actions/view {:description "Sample description"}])
(h/is-truthy (h/get-by-text "Sample description")))
(h/test "render with description & 1 action"
(render [bottom-actions/view
{:description :bottom
:description-text "Sample description"
:actions :one-action
:button-one-label "Request to join"}])
(h/is-truthy (h/get-by-text "Sample description"))
(h/is-truthy (h/get-by-text "Request to join")))
(h/test "render with 2 actions"
(let [button-one "Button One"
button-two "Button Two"]
(h/render [bottom-actions/view
{:actions :2-actions
:button-one-label button-one
:button-one-props {:icon-left :i/arrow-left}
:button-two-label button-two}])
(render [bottom-actions/view
{:actions :two-actions
:button-one-label button-one
:button-one-props {:icon-left :i/arrow-left}
:button-two-label button-two}])
(h/is-truthy (h/get-by-text button-one))
(h/is-truthy (h/get-by-label-text :icon))
(h/is-truthy (h/get-by-text button-two))))
(h/test "render disabled button"
(h/render [bottom-actions/view
{:description "Sample description"
:button-one-props {:disabled? true}
:button-one-label "button"}])
(render [bottom-actions/view
{:description :bottom
:actions :one-action
:description-text "Sample description"
:button-one-props {:disabled? true}
:button-one-label "button"}])
(h/is-disabled (h/get-by-label-text :button-one)))
(h/test "sane defaults"
(h/render [bottom-actions/view {}])
(h/is-truthy (h/get-by-label-text :button-one))))
(h/test "render with error & 1 action"
(render [bottom-actions/view
{:description :top-error
:error-message "Sample error"
:actions :one-action
:button-one-label "Request to join"}])
(h/is-truthy (h/get-by-text "Sample error"))
(h/is-truthy (h/get-by-text "Request to join")))
(h/test "render with top description & 1 action"
(render [bottom-actions/view
{:description :top
:role :admin
:actions :one-action
:button-one-label "Request to join"}])
(h/is-truthy (h/get-by-text "Eligible to join as"))
(h/is-truthy (h/get-by-text "Admin"))
(h/is-truthy (h/get-by-text "Request to join"))))
@@ -2,34 +2,58 @@
(:require
[quo.foundations.colors :as colors]))
(defn- text-color
[scroll? blur? theme]
(cond
scroll? (if blur?
colors/white-70-blur
(colors/theme-colors colors/neutral-80-opa-70
colors/white-opa-70
theme))
blur? colors/white-opa-40
:else (colors/theme-colors colors/neutral-50
colors/neutral-40
theme)))
(defn container
[scroll? blur? theme]
{:background-color (when-not (or scroll? blur?)
(colors/theme-colors colors/white colors/neutral-95 theme))})
(def buttons-container
{:flex-direction :row
:justify-content :space-around
:padding-vertical 12
:gap 12
:padding-horizontal 20})
(def button-container
{:flex 1})
(def button-container-2-actions
(assoc button-container :margin-right 12))
(def description-top
{:flex-direction :row
:align-items :center
:gap 5
:padding-top 12
:padding-bottom 4
:justify-content :center})
(defn description
[theme scroll?]
{:color (colors/theme-colors
(if scroll?
(colors/custom-color colors/neutral-80 70)
colors/neutral-50)
(if scroll?
colors/white-opa-70
colors/neutral-40)
theme)
(defn description-top-text
[scroll? blur? theme]
{:color (text-color scroll? blur? theme)})
(defn description-bottom
[scroll? blur? theme]
{:color (text-color scroll? blur? theme)
:text-align :center
:padding-bottom 12
:padding-horizontal 40})
(def scroll
{:margin-top 21
:margin-horizontal 120
:margin-bottom 8
:width 134
:height 5})
(def error-message
{:flex-direction :row
:gap 4
:justify-content :center
:align-items :center
:padding-top 15
:padding-horizontal 20
:padding-bottom 7})
@@ -2,55 +2,96 @@
(:require
[quo.components.buttons.button.view :as button]
[quo.components.drawers.bottom-actions.style :as style]
[quo.components.icon :as icon]
[quo.components.markdown.text :as text]
[quo.components.tags.context-tag.view :as context-tag]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]))
[react-native.core :as rn]
[schema.core :as schema]
[utils.i18n :as i18n]))
(def default-props
{:button-one-type :primary
:button-two-type :grey
:customization-color :blue})
(def ?schema
[:=>
[:catn
[:props
[:map {:closed true}
[:actions [:maybe [:enum :one-action :two-actions]]]
[:description {:optional true} [:maybe [:enum :top :bottom :top-error]]]
[:description-text {:optional true} [:maybe :string]]
[:error-message {:optional true} [:maybe :string]]
[:role {:optional true} [:maybe [:enum :admin :member :token-master :owner]]]
[:button-one-label {:optional true} [:maybe :string]]
[:button-two-label {:optional true} [:maybe :string]]
[:button-one-props {:optional true} [:maybe :map]]
[:button-two-props {:optional true} [:maybe :map]]
[:theme :schema.common/theme]
[:scroll? {:optional true} [:maybe :boolean]]
[:blur? {:optional true} [:maybe :boolean]]
[:container-style {:optional true} [:maybe :map]]]]]
:any])
(def ^:private role-icon
{:admin :i/gavel
:member :i/members
:token-master :i/token-master
:owner :i/crown})
(defn- view-internal
"Options:
[{:keys [actions description description-text error-message role button-one-label button-two-label
blur? button-one-props button-two-props theme scroll? container-style]}]
[rn/view
{:style (merge (style/container scroll? blur? theme) container-style)}
(when (= description :top-error)
[rn/view {:style style/error-message}
[icon/icon
:i/alert
{:color (colors/theme-colors colors/danger-50 colors/danger-60 theme)
:size 16}]
[text/text
{:size :paragraph-2
:style {:color (colors/theme-colors colors/danger-50 colors/danger-60 theme)}}
error-message]])
:actions - keyword (default nil) - :1-action/:2-actions
:description - string (default nil) - Description to display below the title
:button-one-label - string (default nil) - Label for the first button
:button-two-label - string (default nil) - Label for the second button
:button-one-props - map with props for button one
:button-two-props - map with props for button two
:theme - :light/:dark
:scroll? - bool (default false) - Whether the iOS Home Indicator should be rendered"
[props]
(let [{:keys [actions description button-one-label button-two-label
button-one-props button-two-props theme scroll? customization-color container-style]}
(merge default-props props)]
[rn/view {:style container-style}
[rn/view {:style style/buttons-container}
(when (= actions :2-actions)
[button/button
(merge
{:size 40
:background (when scroll? :blur)
:container-style style/button-container-2-actions
:theme theme
:accessibility-label :button-two}
button-two-props)
button-two-label])
(when (= description :top)
[rn/view
{:style style/description-top}
[text/text
{:size :paragraph-2
:style (style/description-top-text scroll? blur? theme)}
(i18n/label :t/eligible-to-join-as)]
[context-tag/view
{:type :icon
:size 24
:icon (role role-icon)
:blur? blur?
:context (i18n/label (keyword "t" role))}]])
[rn/view {:style style/buttons-container}
(when (= actions :two-actions)
[button/button
(merge
{:size 40
:background (when (or blur? scroll?) :blur)
:container-style style/button-container
:background (when scroll? :blur)
:theme theme
:accessibility-label :button-one
:customization-color customization-color}
button-one-props)
button-one-label]]
(when description
[text/text
{:size :paragraph-2
:style (style/description theme scroll?)} description])]))
:accessibility-label :button-two}
button-two-props)
button-two-label])
[button/button
(merge
{:size 40
:container-style style/button-container
:background (when (or blur? scroll?) :blur)
:theme theme
:accessibility-label :button-one}
button-one-props)
button-one-label]]
(when (= description :bottom)
[text/text
{:size :paragraph-2
:style (style/description-bottom scroll? blur? theme)} description-text])])
(def view (quo.theme/with-theme view-internal))
(def view
(quo.theme/with-theme
(schema/instrument #'view-internal ?schema)))
@@ -3,97 +3,110 @@
[quo.core :as quo]
[test-helpers.component :as h]))
(def ^:private theme :light)
(h/describe "drawer top tests"
(h/test "component renders in default type"
(h/render [quo/drawer-top
{:title "Title"
:type :default}])
(h/render-with-theme-provider [quo/drawer-top
{:title "Title"
:type :default}]
theme)
(h/is-truthy (h/get-by-text "Title")))
(h/test "component renders in default + description type"
(h/render [quo/drawer-top
{:title "Title"
:type :default
:description "Description"}])
(h/render-with-theme-provider [quo/drawer-top
{:title "Title"
:type :default
:description "Description"}]
theme)
(h/is-truthy (h/get-by-text "Title"))
(h/is-truthy (h/get-by-text "Description")))
(h/test "component renders in info type"
(h/render [quo/drawer-top
{:title "Title"
:type :info}])
(h/render-with-theme-provider [quo/drawer-top
{:title "Title"
:type :info}]
theme)
(h/is-truthy (h/get-by-text "Title"))
(h/is-truthy (h/get-by-label-text :info-icon)))
(h/test "component renders in info + description type"
(h/render [quo/drawer-top
{:title "Title"
:description "Description"
:type :info}])
(h/render-with-theme-provider [quo/drawer-top
{:title "Title"
:description "Description"
:type :info}]
theme)
(h/is-truthy (h/get-by-text "Title"))
(h/is-truthy (h/get-by-text "Description"))
(h/is-truthy (h/get-by-label-text :info-icon)))
(h/test "component renders in context-tag type"
(h/render [quo/drawer-top
{:title "Title"
:type :context-tag
:community-name "Coinbase"}])
(h/render-with-theme-provider [quo/drawer-top
{:title "Title"
:type :context-tag
:community-name "Coinbase"}]
theme)
(h/is-truthy (h/get-by-text "Title"))
(h/is-truthy (h/get-by-label-text :context-tag-wrapper)))
(h/test "component renders in context-tag + button type"
(h/render [quo/drawer-top
{:title "Title"
:type :context-tag
:button-icon :i/placeholder
:community-name "Coinbase"}])
(h/render-with-theme-provider [quo/drawer-top
{:title "Title"
:type :context-tag
:button-icon :i/placeholder
:community-name "Coinbase"}]
theme)
(h/is-truthy (h/get-by-text "Title"))
(h/is-truthy (h/get-by-label-text :button-icon))
(h/is-truthy (h/get-by-label-text :context-tag-wrapper)))
(h/test "component renders in account type"
(h/render [quo/drawer-top
{:title "Title"
:type :account
:account-avatar-emoji "🍿"
:networks [{:name :ethereum :short-name "eth"}]
:description "0x62b...0a5"
:customization-color :purple}])
(h/render-with-theme-provider [quo/drawer-top
{:title "Title"
:type :account
:account-avatar-emoji "🍿"
:networks [{:name :ethereum :short-name "eth"}]
:description "0x62b...0a5"
:customization-color :purple}]
theme)
(h/is-truthy (h/get-by-text "Title"))
(h/is-truthy (h/get-by-text "0x62b...0a5"))
(h/is-truthy (h/get-by-label-text :account-avatar)))
(h/test "component renders in keypair type when keycard? is false"
(h/render [quo/drawer-top
{:title "Title"
:keycard? false
:icon-avatar :i/placeholder
:type :keypair}])
(h/render-with-theme-provider [quo/drawer-top
{:title "Title"
:keycard? false
:icon-avatar :i/placeholder
:type :keypair}]
theme)
(h/is-truthy (h/get-by-text "Title"))
(-> (h/expect (h/get-by-translation-text :t/on-device))
(.toBeTruthy)))
(h/test "component renders in keypair type when keycard? is true"
(h/render [quo/drawer-top
{:title "Title"
:keycard? true
:icon-avatar :i/placeholder
:type :keypair}])
(h/render-with-theme-provider [quo/drawer-top
{:title "Title"
:keycard? true
:icon-avatar :i/placeholder
:type :keypair}]
theme)
(h/is-truthy (h/get-by-text "Title"))
(-> (h/expect (h/get-by-translation-text :t/on-keycard))
(.toBeTruthy)))
(h/test "component renders in default-keypair type"
(h/render [quo/drawer-top
{:title "Title"
:description "0x62b...0a5"
:type :default-keypair}])
(h/render-with-theme-provider [quo/drawer-top
{:title "Title"
:description "0x62b...0a5"
:type :default-keypair}]
theme)
(h/is-truthy (h/get-by-text "Title"))
(h/is-truthy (h/get-by-label-text :default-keypair-text)))
(h/test "component renders in label type"
(h/render [quo/drawer-top
{:label "label"
:type :label}])
(h/render-with-theme-provider [quo/drawer-top
{:label "label"
:type :label}]
theme)
(h/is-truthy (h/get-by-text "label"))))
@@ -137,7 +137,8 @@
:description description}]))
(defn- right-icon
[{:keys [theme type on-button-press on-button-long-press button-disabled? button-icon]}]
[{:keys [theme type on-button-press on-button-long-press button-disabled? button-icon button-type]
:or {button-type :primary}}]
(cond
(= :info type)
[icons/icon
@@ -153,7 +154,7 @@
:on-press on-button-press
:on-long-press on-button-long-press
:disabled? button-disabled?
:type :primary
:type button-type
:size 24
:icon-only? true}
button-icon]))
@@ -182,7 +183,7 @@
(defn- view-internal
[{:keys [title title-icon type theme description blur? community-name community-logo button-icon
account-name emoji context-tag-type
account-name emoji context-tag-type button-type
on-button-press
on-button-long-press
button-disabled? account-avatar-emoji account-avatar-type customization-color icon-avatar
@@ -221,6 +222,7 @@
[right-icon
{:theme theme
:type type
:button-type button-type
:on-button-press on-button-press
:on-button-long-press on-button-long-press
:button-disabled? button-disabled?
@@ -61,24 +61,30 @@
value (reagent/atom "")
focused? (atom false)]
(fn [{:keys [scanned-value theme blur? on-change-text on-blur on-focus on-clear on-scan
on-detect-ens on-detect-address address-regex valid-ens-or-address?
container-style]}]
on-detect-ens on-detect-address on-detect-unclassified address-regex ens-regex
valid-ens-or-address? container-style]}]
(let [on-change (fn [text]
(when (not= @value text)
(let [address? (when address-regex
(boolean (re-matches address-regex text)))]
(boolean (re-matches address-regex text)))
ens? (when ens-regex
(boolean (re-matches ens-regex text)))]
(if (> (count text) 0)
(reset! status :typing)
(reset! status :active))
(reset! value text)
(when on-change-text
(on-change-text text))
(when (and on-detect-ens (> (count text) 0))
(when (and on-detect-ens ens?)
(reset! status :loading)
(on-detect-ens text #(reset! status :typing)))
(when (and address? on-detect-address)
(reset! status :loading)
(on-detect-address text)))))
(on-detect-address text))
(when (and (not address?)
(not ens?)
on-detect-unclassified)
(on-detect-unclassified text)))))
on-paste (fn []
(clipboard/get-string
(fn [clipboard]
@@ -4,79 +4,83 @@
[quo.foundations.colors :as colors]
[test-helpers.component :as h]))
(def ^:private theme :light)
(h/describe "List items: account"
(h/test "default render'"
(h/render [account/view])
(h/render-with-theme-provider [account/view] theme)
(h/is-truthy (h/query-by-label-text :container)))
(h/test "on-press-in changes state to :pressed"
(h/render [account/view])
(h/render-with-theme-provider [account/view] theme)
(h/fire-event :on-press-in (h/get-by-label-text :container))
(h/wait-for #(h/has-style (h/query-by-label-text :container)
{:backgroundColor (colors/resolve-color :blue :light 5)})))
(h/test "on-press-in changes state to :pressed with blur? enabled"
(h/render [account/view {:blur? true}])
(h/render-with-theme-provider [account/view {:blur? true}] theme)
(h/fire-event :on-press-in (h/get-by-label-text :container))
(h/wait-for #(h/has-style (h/query-by-label-text :container)
{:backgroundColor colors/white-opa-5})))
(h/test "render with state :active"
(h/render [account/view {:state :active}])
(h/render-with-theme-provider [account/view {:state :active}] theme)
(h/has-style (h/query-by-label-text :container)
{:backgroundColor (colors/resolve-color :blue :light 10)}))
(h/test "render with state :active and blur? enabled"
(h/render [account/view
{:blur? true
:state :active}])
(h/render-with-theme-provider [account/view
{:blur? true
:state :active}]
theme)
(h/has-style (h/query-by-label-text :container)
{:backgroundColor colors/white-opa-10}))
(h/test "render with state :selected"
(h/render [account/view {:state :selected}])
(h/render-with-theme-provider [account/view {:state :selected}] theme)
(h/is-truthy (h/query-by-label-text :check-icon)))
(h/test "calls on-press"
(let [on-press (h/mock-fn)]
(h/render [account/view {:on-press on-press}])
(h/render-with-theme-provider [account/view {:on-press on-press}] theme)
(h/fire-event :on-press (h/get-by-label-text :container))
(h/was-called on-press)))
(h/test "renders token props if type :tag"
(h/render [account/view {:type :tag}])
(h/render-with-theme-provider [account/view {:type :tag}] theme)
(h/is-truthy (h/query-by-label-text :tag-container)))
(h/test "renders keycard icon if title-icon is present"
(h/render [account/view {:title-icon :i/placeholder}])
(h/render-with-theme-provider [account/view {:title-icon :i/placeholder}] theme)
(h/is-truthy (h/query-by-label-text :title-icon)))
(h/test "doesn't render keycard icon if title-icon is missing"
(h/render [account/view])
(h/render-with-theme-provider [account/view] theme)
(h/is-falsy (h/query-by-label-text :title-icon)))
(h/test "renders balance container but not arrow icon if type :balance-neutral"
(h/render [account/view {:type :balance-neutral}])
(h/render-with-theme-provider [account/view {:type :balance-neutral}] theme)
(h/is-truthy (h/query-by-label-text :balance-container))
(h/is-falsy (h/query-by-label-text :arrow-icon)))
(h/test "renders balance container and negative arrow icon if type :balance-negative"
(h/render [account/view {:type :balance-negative}])
(h/render-with-theme-provider [account/view {:type :balance-negative}] theme)
(h/is-truthy (h/query-by-label-text :balance-container))
(h/is-truthy (h/query-by-label-text :icon-negative))
(h/is-falsy (h/query-by-label-text :icon-positive)))
(h/test "renders balance container and positive arrow icon if type :balance-positive"
(h/render [account/view {:type :balance-positive}])
(h/render-with-theme-provider [account/view {:type :balance-positive}] theme)
(h/is-truthy (h/query-by-label-text :balance-container))
(h/is-falsy (h/query-by-label-text :icon-negative))
(h/is-truthy (h/query-by-label-text :icon-positive)))
(h/test "renders options button if type :action"
(let [on-options-press (h/mock-fn)]
(h/render [account/view
{:type :action
:on-options-press on-options-press}])
(h/render-with-theme-provider [account/view
{:type :action
:on-options-press on-options-press}]
theme)
(h/is-truthy (h/query-by-label-text :options-button))
(h/fire-event :on-press (h/get-by-label-text :options-button))
(h/was-called on-options-press))))
@@ -9,8 +9,10 @@
[utils.i18n :as i18n]))
(defn- view-internal
[{:keys [state theme number word] :as props}]
[rn/view {:style (style/container props)}
[{:keys [state theme number word on-press] :as props}]
[rn/pressable
{:style (style/container props)
:on-press on-press}
(if (or (= state :empty) (= state :disabled))
[rn/view
{:style (style/num-container props)
@@ -311,7 +311,7 @@
- title
- description
- picture: a valid rn/image `:source` value
`:wallet-network`
`:wallet-networks`
- networks: a vector of network image source
- networks-on-press: a callback
`:community`
@@ -24,9 +24,7 @@
{:background-color (colors/theme-colors colors/neutral-80-opa-70 colors/white-opa-70 theme)
:flex-direction :row
:justify-content :space-between
:padding-vertical 8
:padding-left 10
:padding-right 8
:padding 8
:border-radius 12})
(defn title
@@ -47,8 +45,13 @@
theme))
:container-style {:width 20 :height 20}})
(def left-side-container {:padding 2})
(def right-side-container {:padding 4 :flex 1})
(def left-side-container
{:padding 2
:padding-top 3})
(def right-side-container
{:padding 4
:flex 1})
(defn action-container
[theme]
@@ -45,7 +45,6 @@
:blur-radius 10
:blur-type :transparent
:overlay-color :transparent}]
[rn/view {:style (style/content-container theme)}
[rn/view {:style style/left-side-container}
left]
@@ -77,16 +76,19 @@
:or {type :neutral icon :i/placeholder}}]
(let [context-theme (or theme (quo.theme/get-theme))
icon-name (case type
:positive :i/correct
:negative :i/incorrect
:positive (if (= theme :light)
:i/correct
:i/correct-dark)
:negative (if (= theme :light)
:i/incorrect
:i/incorrect-dark)
:neutral icon)]
[quo.theme/provider {:theme context-theme}
[toast-container
{:left (cond user
[user-avatar/user-avatar user]
icon-name
[icon/icon icon-name (style/icon type context-theme)]
)
[icon/icon icon-name (style/icon type context-theme)])
:title title
:text text
:right (if undo-duration
@@ -15,7 +15,7 @@
(defn- view-internal
[]
(let [pressed? (reagent/atom false)]
(fn [{:keys [disabled? theme blur? on-press type]} label]
(fn [{:keys [disabled? theme blur? on-press on-long-press type]} label]
(let [label-color (style/get-label-color disabled? theme blur?)
background-color (style/toggle-background-color @pressed? blur? theme)]
[rn/pressable
@@ -24,6 +24,9 @@
:on-press (fn []
(when on-press
(on-press label)))
:on-long-press (fn []
(when (fn? on-long-press)
(on-long-press label)))
:allow-multiple-presses? true
:on-press-in #(reset! pressed? true)
:on-press-out #(reset! pressed? false)
@@ -6,18 +6,19 @@
[react-native.core :as rn]))
(defn keyboard-item
[{:keys [item type disabled? on-press blur? theme]}]
[{:keys [item type disabled? on-press on-long-press blur? theme]}]
[keyboard-key/view
{:disabled? disabled?
:on-press on-press
:blur? blur?
:theme theme
:type type}
{:disabled? disabled?
:on-press on-press
:on-long-press on-long-press
:blur? blur?
:theme theme
:type type}
item])
(defn- view-internal
[]
(fn [{:keys [disabled? theme blur? left-action delete-key? on-press on-delete
(fn [{:keys [disabled? theme blur? left-action delete-key? on-press on-delete on-long-press-delete
container-style]
:or {left-action :none}}]
[rn/view
@@ -62,12 +63,13 @@
:theme theme}]
(if delete-key?
[keyboard-item
{:item :i/backspace
:type :key
:disabled? disabled?
:on-press on-delete
:blur? blur?
:theme theme}]
{:item :i/backspace
:type :key
:disabled? disabled?
:on-press on-delete
:on-long-press on-long-press-delete
:blur? blur?
:theme theme}]
[keyboard-item])]]))
(def view (quo.theme/with-theme view-internal))
@@ -11,8 +11,8 @@
:padding-bottom 8})
(defn settings-items
[{:keys [blur? theme]}]
{:margin-top 12
[{:keys [label blur? theme]}]
{:margin-top (if label 12 4)
:border-radius 16
:background-color (if blur?
colors/white-opa-5
@@ -121,7 +121,7 @@
{:accessibility-label :data-item
:disabled (not icon-right?)
:on-press on-press
:style (merge container-style (style/container size card? blur? theme))}
:style (merge (style/container size card? blur? theme) container-style)}
[left-side props]
(when (and (= :default status) (not= :small size))
[right-side
@@ -2,9 +2,64 @@
(:require [quo.components.share.share-qr-code.view :as share-qr-code]
[test-helpers.component :as h]))
(def qr-label "Text shown below QR")
(def default-props
{:qr-data qr-label
:qr-image-uri "test-uri"
:full-name "Default name"})
(def profile-props
{:type :profile
:profile-picture 123})
(def wallet-props {:networks '(:ethereum)})
(def text-press-event
{:test-name "Text pressed"
:accessibility-label :share-qr-code-info-text
:event-name :press
:callback-prop-key :on-text-press})
(def text-long-press-event
{:test-name "Text long pressed"
:accessibility-label :share-qr-code-info-text
:event-name :long-press
:callback-prop-key :on-text-long-press})
(def share-press-event
{:test-name "Share button"
:accessibility-label :link-to-profile
:event-name :press
:callback-prop-key :on-share-press})
(def multichain-press-event
{:test-name "Multichain tab pressed"
:accessibility-label :share-qr-code-multichain-tab
:event-name :press
:callback-prop-key :on-multichain-press})
(def legacy-press-event
{:test-name "Legacy tab pressed"
:accessibility-label :share-qr-code-legacy-tab
:event-name :press
:callback-prop-key :on-legacy-press})
(def settings-press-event
{:test-name "Settings pressed"
:accessibility-label :share-qr-code-settings
:event-name :press
:callback-prop-key :on-settings-press})
(defn render-share-qr-code
[{share-qr-type :type :as props}]
(let [component-rendered (h/render [share-qr-code/view {:type share-qr-type}])
[props]
;; NOTE: rendering the profile type first, so that the re-render is triggered.
;; Passing a :key breaks it for some reason.
(let [component-rendered (h/render [share-qr-code/view
(merge
default-props
profile-props
{:qr-data "initial-render"})])
rerender-fn (h/get-rerender-fn component-rendered)
share-qr-code (h/get-by-label-text :share-qr-code)]
;; Fires on-layout since it's needed to render the content
@@ -13,23 +68,52 @@
(h/describe "Share QR Code component"
(h/describe "Renders share-qr-code component in all types"
(let [qr-label "Text shown below QR"]
(h/test "Profile"
(render-share-qr-code {:type :profile
:qr-data qr-label})
(h/is-truthy (h/get-by-text qr-label)))
(h/test "Profile"
(render-share-qr-code (merge default-props
profile-props))
(h/is-truthy (h/get-by-text qr-label)))
(h/test "Wallet Legacy"
(render-share-qr-code {:type :wallet-legacy
:qr-data qr-label
:emoji "👻"})
(h/is-truthy (h/get-by-text qr-label)))
(h/test "Wallet Legacy"
(render-share-qr-code (merge default-props
wallet-props
{:type :wallet
:address :legacy}))
(h/is-truthy (h/get-by-text qr-label)))
(h/test "Wallet Multichain"
(render-share-qr-code {:type :wallet-multichain
:qr-data qr-label
:emoji "👻"})
(h/is-truthy (h/get-by-text qr-label)))))
(h/test "Wallet multichain"
(render-share-qr-code (merge default-props
wallet-props
{:type :wallet
:address :multichain}))
(h/is-truthy (h/get-by-text qr-label)))
(h/test "Saved address legacy"
(render-share-qr-code (merge default-props
wallet-props
{:type :saved-address
:address :legacy}))
(h/is-truthy (h/get-by-text qr-label)))
(h/test "Saved address multichain"
(render-share-qr-code (merge default-props
wallet-props
{:type :saved-address
:address :multichain}))
(h/is-truthy (h/get-by-text qr-label)))
(h/test "Watched address legacy"
(render-share-qr-code (merge default-props
wallet-props
{:type :watched-address
:address :legacy}))
(h/is-truthy (h/get-by-text qr-label)))
(h/test "Watched address multichain"
(render-share-qr-code (merge default-props
wallet-props
{:type :watched-address
:address :multichain}))
(h/is-truthy (h/get-by-text qr-label))))
(h/describe "Fires all events for all types"
(letfn [(test-fire-events [props test-seq]
@@ -44,68 +128,86 @@
(h/describe "Profile"
(test-fire-events
{:type :profile}
[{:test-name "Text pressed"
:accessibility-label :share-qr-code-info-text
:event-name :press
:callback-prop-key :on-text-press}
{:test-name "Text long pressed"
:accessibility-label :share-qr-code-info-text
:event-name :long-press
:callback-prop-key :on-text-long-press}
{:test-name "Share button"
:accessibility-label :link-to-profile
:event-name :press
:callback-prop-key :on-share-press}]))
(merge default-props
profile-props)
[text-press-event
text-long-press-event
share-press-event]))
(h/describe "Wallet Legacy"
(test-fire-events
{:type :wallet-legacy :emoji "👽"}
[{:test-name "Text pressed"
:accessibility-label :share-qr-code-info-text
:event-name :press
:callback-prop-key :on-text-press}
{:test-name "Text long pressed"
:accessibility-label :share-qr-code-info-text
:event-name :long-press
:callback-prop-key :on-text-long-press}
{:test-name "Share button pressed"
:accessibility-label :link-to-profile
:event-name :press
:callback-prop-key :on-share-press}
{:test-name "Legacy tab pressed"
:accessibility-label :share-qr-code-legacy-tab
:event-name :press
:callback-prop-key :on-legacy-press}
{:test-name "Multichain tab pressed"
:accessibility-label :share-qr-code-multichain-tab
:event-name :press
:callback-prop-key :on-multichain-press}]))
(merge default-props
wallet-props
{:type :wallet
:address :legacy
:emoji "👻"})
[text-press-event
text-long-press-event
share-press-event
multichain-press-event
legacy-press-event]))
(h/describe "Wallet Multichain"
(test-fire-events
{:type :wallet-multichain :emoji "👽"}
[{:test-name "Text pressed"
:accessibility-label :share-qr-code-info-text
:event-name :press
:callback-prop-key :on-text-press}
{:test-name "Text long pressed"
:accessibility-label :share-qr-code-info-text
:event-name :long-press
:callback-prop-key :on-text-long-press}
{:test-name "Share button pressed"
:accessibility-label :link-to-profile
:event-name :press
:callback-prop-key :on-share-press}
{:test-name "Legacy tab pressed"
:accessibility-label :share-qr-code-legacy-tab
:event-name :press
:callback-prop-key :on-legacy-press}
{:test-name "Multichain tab pressed"
:accessibility-label :share-qr-code-multichain-tab
:event-name :press
:callback-prop-key :on-multichain-press}
{:test-name "Settings pressed"
:accessibility-label :share-qr-code-settings
:event-name :press
:callback-prop-key :on-settings-press}])))))
(merge default-props
wallet-props
{:type :wallet
:address :multichain
:emoji "👻"})
[text-press-event
text-long-press-event
share-press-event
legacy-press-event
multichain-press-event
settings-press-event]))
(h/describe "Saved Address Legacy"
(test-fire-events
(merge default-props
wallet-props
{:type :saved-address
:address :legacy})
[text-press-event
text-long-press-event
share-press-event
legacy-press-event
multichain-press-event]))
(h/describe "Saved Address Multichain"
(test-fire-events
(merge default-props
wallet-props
{:type :saved-address :address :multichain})
[text-press-event
text-long-press-event
share-press-event
legacy-press-event
multichain-press-event
settings-press-event]))
(h/describe "Watched Address Legacy"
(test-fire-events
(merge default-props
wallet-props
{:type :watched-address
:address :legacy
:emoji "👽"})
[text-press-event
text-long-press-event
share-press-event
legacy-press-event
multichain-press-event]))
(h/describe "Watched Address Multichain"
(test-fire-events
(merge default-props
wallet-props
{:type :watched-address
:address :multichain
:emoji "👽"})
[text-press-event
text-long-press-event
share-press-event
legacy-press-event
multichain-press-event
settings-press-event])))))
@@ -0,0 +1,68 @@
(ns quo.components.share.share-qr-code.schema
(:require
[quo.foundations.resources :as resources]))
(defn- valid-network?
[network]
(-> network resources/get-network boolean))
(def ?base
[:map
[:type [:enum :profile :wallet :saved-address :watched-address]]
[:full-name :string]
[:qr-image-uri :string]
[:qr-data :string]
[:on-text-press {:optional true} [:maybe fn?]]
[:on-text-long-press {:optional true} [:maybe fn?]]
[:on-share-press {:optional true} [:maybe fn?]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]
[:unblur-on-android? {:optional true} [:maybe :boolean]]])
(def ?emoji :string)
(def ?profile
[:map
[:type [:= :profile]]
[:profile-picture :schema.common/image-source]])
(def ?wallet
[:map
[:type [:= :wallet]]
[:emoji {:optional true} [:maybe ?emoji]]])
(def ?address-multichain
[:map
[:address [:= :multichain]]
[:on-settings-press {:optional true} fn?]])
(def ?address-base
[:merge
[:map
[:networks [:sequential [:fn valid-network?]]]
[:on-legacy-press {:optional true} [:maybe fn?]]
[:on-multichain-press {:optional true} [:maybe fn?]]
[:address [:enum :legacy :multichain]]]
[:multi {:dispatch :address}
[:multichain ?address-multichain]]])
(def ?saved-address
[:map
[:type [:= :saved-address]]
[:on-settings-press {:optional true} fn?]])
(def ?watched-address
[:map
[:type [:= :watched-address]]
[:emoji {:optional true} [:maybe ?emoji]]])
(def ?schema
[:=>
[:catn
[:props
[:multi {:dispatch :type}
[:profile [:merge ?base ?profile]]
[:wallet [:merge ?base ?address-base ?wallet]]
[:saved-address [:merge ?base ?address-base ?saved-address]]
[:watched-address [:merge ?base ?address-base ?watched-address]]]]]
:any])
@@ -106,3 +106,6 @@
{:color (-> network-short-name
(get-network-full-name :unknown)
(colors/resolve-color nil))})
(def watched-account-icon
{:margin-left 4})
@@ -3,19 +3,25 @@
[clojure.string :as string]
[oops.core :as oops]
[quo.components.avatars.account-avatar.view :as account-avatar]
[quo.components.avatars.user-avatar.view :as user-avatar]
[quo.components.avatars.wallet-user-avatar.view :as wallet-user-avatar]
[quo.components.buttons.button.view :as button]
[quo.components.gradient.gradient-cover.view :as gradient-cover]
[quo.components.icon :as icons]
[quo.components.markdown.text :as text]
[quo.components.share.qr-code.view :as qr-code]
[quo.components.share.share-qr-code.schema :as component-schema]
[quo.components.share.share-qr-code.style :as style]
[quo.components.tabs.tab.view :as tab]
[quo.foundations.colors :as colors]
[quo.theme]
[react-native.core :as rn]
[reagent.core :as reagent]
[schema.core :as schema]
[utils.i18n :as i18n]))
(defn- header
[{:keys [share-qr-type on-legacy-press on-multichain-press]}]
[{:keys [on-legacy-press on-multichain-press address]}]
[rn/view {:style style/header-container}
[tab/view
{:accessibility-label :share-qr-code-legacy-tab
@@ -23,7 +29,7 @@
:active-item-container-style style/header-tab-active
:item-container-style style/header-tab-inactive
:size 24
:active (= :wallet-legacy share-qr-type)
:active (= :legacy address)
:on-press on-legacy-press}
(i18n/label :t/legacy)]
[rn/view {:style style/space-between-tabs}]
@@ -33,7 +39,7 @@
:active-item-container-style style/header-tab-active
:item-container-style style/header-tab-inactive
:size 24
:active (= :wallet-multichain share-qr-type)
:active (= :multichain address)
:on-press on-multichain-press}
(i18n/label :t/multichain)]])
@@ -121,77 +127,69 @@
:on-press on-settings-press}
:i/advanced]])
(defn- header-icon
[{:keys [share-qr-type customization-color emoji profile-picture full-name]}]
(case share-qr-type
:profile [user-avatar/user-avatar
{:size :small
:status-indicator? false
:profile-picture profile-picture
:customization-color customization-color}]
(:wallet :watched-address) [account-avatar/view
{:customization-color customization-color
:emoji emoji
:size 32}]
:saved-address [wallet-user-avatar/wallet-user-avatar
{:size :size-32
:customization-color customization-color
:full-name full-name}]
nil))
(defn- share-qr-code
[{:keys [share-qr-type qr-image-uri component-width customization-color full-name
profile-picture emoji on-share-press]
profile-picture emoji on-share-press address]
:as props}]
[:<>
[rn/view {:style style/gradient-bg}
[gradient-cover/view {:customization-color customization-color :height 463}]]
[rn/view {:style style/content-container}
[rn/view
{:style style/share-qr-container}
[rn/view
{:style style/share-qr-inner-container}
[account-avatar/view
{:customization-color customization-color
:emoji emoji
:size 32}]
[rn/view {:style style/share-qr-container}
[rn/view {:style style/share-qr-inner-container}
[header-icon props]
[text/text
{:size :heading-2
:weight :semi-bold
:style {:margin-left 8}} full-name]]
:style {:margin-left 8}} full-name]
(when (= share-qr-type :watched-address)
[icons/icon
:i/reveal
{:color colors/white-opa-40
:container-style style/watched-account-icon}])]
[share-button {:on-press on-share-press}]]
(when (#{:wallet-legacy :wallet-multichain} share-qr-type)
(when (not= share-qr-type :profile)
[header props])
[quo.theme/provider {:theme :light}
[qr-code/view
{:qr-image-uri qr-image-uri
:size (style/qr-code-size component-width)
:avatar (if (= share-qr-type :profile)
:profile
:wallet-account)
:avatar (case share-qr-type
:profile :profile
(:watched-address :wallet) :wallet-account
:saved-address :saved-address
nil)
:customization-color customization-color
:full-name full-name
:profile-picture profile-picture
:emoji emoji}]]
[rn/view {:style style/bottom-container}
(case share-qr-type
:profile [profile-bottom props]
:wallet-legacy [wallet-legacy-bottom props]
:wallet-multichain [wallet-multichain-bottom props]
nil)]]])
(if (= share-qr-type :profile)
[profile-bottom props]
(case address
:legacy [wallet-legacy-bottom props]
:multichain [wallet-multichain-bottom props]
nil))]]])
(defn- view-internal
"Receives the following properties:
- type: :profile | :wallet-legacy | :wallet-multichain
- qr-image-uri: Image source value.
- qr-data: Text to show below the QR code.
- on-text-press: Callback for the `qr-data` text.
- on-text-long-press: Callback for the `qr-data` text.
- on-share-press: Callback for the share button.
- customization-color: Custom color for the QR code component.
- unblur-on-android?: [Android only] disables blur for this component.
Depending on the `type`, different properties are accepted:
`:profile`
- full-name: User full name.
- profile-picture: map ({:source image-source}) or any image source.
`:wallet-legacy`
- emoji: Emoji in a string to show in the QR code.
- on-legacy-press: Callback for the legacy tab.
- on-multichain-press: Callback for the multichain tab.
`:wallet-multichain`
- networks: A vector of network names as keywords (`[:ethereum, :my-net, ...]`).
- on-settings-press: Callback for the settings button.
- emoji: Emoji in a string to show in the QR code.
- on-legacy-press: Callback for the legacy tab.
- on-multichain-press: Callback for the multichain tab.
WARNING on Android:
Sometimes while using a blur layer on top of another on Android, this component looks
bad because of the `blur/view`, so we can set `unblur-on-android? true` to fix it.
"
[props]
(reagent/with-let [component-width (reagent/atom nil)
container-component [rn/view {:background-color style/overlay-color}]]
@@ -207,5 +205,6 @@
(assoc :component-width @component-width)
(clojure.set/rename-keys {:type :share-qr-type}))]))]]))
(def view (quo.theme/with-theme view-internal))
(def view
(quo.theme/with-theme
(schema/instrument #'view-internal component-schema/?schema)))
@@ -0,0 +1,52 @@
(ns quo.components.tags.collectible-tag.component-spec
(:require
[quo.components.tags.collectible-tag.view :as collectible-tag]
[test-helpers.component :as h]))
(def collectible-name "Collectible")
(def collectible-id "#123")
(defn get-test-data
[{:keys [options blur? size]}]
{:collectible-name collectible-name
:collectible-id collectible-id
:collectible-img-src {:uri "https://example.com/image.jpg"}
:options options
:blur? (or blur? false)
:size (or size :size-24)})
(h/describe "Collectible_tag tests"
(h/test "Renders Default option"
(let [data (get-test-data {})]
(h/render-with-theme-provider [collectible-tag/view data])
(h/is-truthy (h/get-by-text collectible-name))))
(h/test "Renders Add option"
(let [data (get-test-data {:options :add})]
(h/render-with-theme-provider [collectible-tag/view data])
(h/is-truthy (h/get-by-text collectible-name))))
(h/test "Renders Hold option"
(let [data (get-test-data {:options :hold})]
(h/render-with-theme-provider [collectible-tag/view data])
(h/is-truthy (h/get-by-text collectible-name))))
(h/test "Renders with Blur"
(let [data (get-test-data {:blur? true})]
(h/render-with-theme-provider [collectible-tag/view data])
(h/is-truthy (h/get-by-text collectible-name))))
(h/test "Renders without Blur"
(let [data (get-test-data {:blur? false})]
(h/render-with-theme-provider [collectible-tag/view data])
(h/is-truthy (h/get-by-text collectible-name))))
(h/test "Renders with Size 24"
(let [data (get-test-data {:size :size-24})]
(h/render-with-theme-provider [collectible-tag/view data])
(h/is-truthy (h/get-by-text collectible-name))))
(h/test "Renders with Size 32"
(let [data (get-test-data {:size :size-32})]
(h/render-with-theme-provider [collectible-tag/view data])
(h/is-truthy (h/get-by-text collectible-name)))))
@@ -0,0 +1,15 @@
(ns quo.components.tags.collectible-tag.schema)
(def ?schema
[:=>
[:catn
[:props
[:map
[:options {:optional true} [:maybe [:enum :add :hold]]]
[:size {:optional true} [:maybe [:enum :size-24 :size-32]]]
[:blur? {:optional true} [:maybe :boolean]]
[:theme :schema.common/theme]
[:collectible-img-src :schema.common/image-source]
[:collectible-name :string]
[:collectible-id :string]]]]
:any])
@@ -0,0 +1,50 @@
(ns quo.components.tags.collectible-tag.style
(:require
[quo.foundations.colors :as colors]))
(defn container
[size options blur? theme]
(let [hold? (= options :hold)]
(merge {:background-color (if blur?
(colors/theme-colors colors/neutral-80-opa-5 colors/white-opa-5 theme)
(colors/theme-colors colors/neutral-10 colors/neutral-90 theme))
:flex-direction :row
:align-items :center
:padding-left 2
:border-width (if hold? 1 0)
:border-radius 0
:border-color (colors/resolve-color :success theme)}
(condp = size
:size-24 {:height (if hold? 26 24)
:padding-right 10
:border-radius 8}
:size-32 {:height (if hold? 34 32)
:padding-right 12
:border-radius 10}))))
(defn options-icon
[size]
(assoc
(condp = size
:size-24 {:right -8
:top -8}
:size-32 {:right -6
:top -6})
:position
:absolute))
(defn collectible-img
[size]
(condp = size
:size-24 {:width 20
:height 20
:margin-right 6
:border-radius 6}
:size-32 {:width 28
:height 28
:margin-right 8
:border-radius 8}))
(defn label
[theme]
{:color (colors/theme-colors colors/neutral-100 colors/white theme)})
@@ -0,0 +1,59 @@
(ns quo.components.tags.collectible-tag.view
(:require
[oops.core :as oops]
[quo.components.icon :as icons]
[quo.components.markdown.text :as text]
[quo.components.tags.collectible-tag.schema :as component-schema]
[quo.components.tags.collectible-tag.style :as style]
[quo.theme]
[react-native.core :as rn]
[react-native.hole-view :as hole-view]
[reagent.core :as reagent]
[schema.core :as schema]))
(defn- view-internal
[]
(let [container-width (reagent/atom 0)
on-layout #(->> (oops/oget % :nativeEvent :layout :width)
(reset! container-width))]
(fn [{:keys [options blur? theme collectible-img-src collectible-name collectible-id] :as props}]
(let [size (or (:size props) :size-24)]
[rn/view
{:on-layout on-layout}
[hole-view/hole-view
{:holes (if options
[{:x (- @container-width
(case size
:size-24 10
:size-32 12
nil))
:y (case size
:size-24 -6
:size-32 -4
nil)
:width 16
:height 16
:borderRadius 8}]
[])}
[rn/view {:style (style/container size options blur? theme)}
[rn/image {:style (style/collectible-img size) :source collectible-img-src}]
[text/text
{:size :paragraph-2
:weight :medium
:style (style/label theme)}
collectible-name]
[text/text
{:size :paragraph-2
:weight :medium
:margin-left 5
:style (style/label theme)}
collectible-id]]]
(when options
[rn/view {:style (style/options-icon size)}
[icons/icon (if (= options :hold) :i/hold :i/add-token)
{:size 20
:no-color true}]])]))))
(def view
(quo.theme/with-theme
(schema/instrument #'view-internal component-schema/?schema)))
@@ -5,6 +5,7 @@
(def username "Alisher account")
(def empty-username "Account 1")
(def ^:private theme :light)
(defn get-test-data
[{:keys [type watch-only? empty-type? loading? no-metrics?]}]
@@ -21,54 +22,54 @@
(h/describe "Account_card tests"
(h/test "Renders Default"
(let [data (get-test-data {:type :default})]
(h/render [account-card/view data])
(h/render-with-theme-provider [account-card/view data] theme)
(h/is-truthy (h/get-by-text username))))
(h/test "Renders Watch-Only"
(let [data (get-test-data {:type :watch-only
:watch-only? true})]
(h/render [account-card/view data])
(h/render-with-theme-provider [account-card/view data] theme)
(h/is-truthy (h/get-by-text username))))
(h/test "Renders Add-Account"
(let [data {:type :add-account}]
(h/render [account-card/view data])
(h/render-with-theme-provider [account-card/view data] theme)
(h/is-truthy (h/get-by-label-text :add-account))))
(h/test "Renders Empty"
(let [data (get-test-data {:type :empty
:empty-type? true})]
(h/render [account-card/view data])
(h/render-with-theme-provider [account-card/view data] theme)
(h/is-truthy (h/get-by-text empty-username))))
(h/test "Renders Missing Keypair"
(let [data (get-test-data {:type :missing-keypair})]
(h/render [account-card/view data])
(h/render-with-theme-provider [account-card/view data] theme)
(h/is-truthy (h/get-by-text username))))
(h/test "Add account on press fires correctly"
(let [on-press (h/mock-fn)
data {:type :add-account
:on-press on-press}]
(h/render [account-card/view data])
(h/render-with-theme-provider [account-card/view data] theme)
(h/fire-event :on-press (h/get-by-label-text :add-account))
(h/was-called on-press)))
(h/test "Renders component without metrics"
(let [data (get-test-data {:type :default
:no-metrics? true})]
(h/render [account-card/view data])
(h/render-with-theme-provider [account-card/view data] theme)
(h/is-falsy (h/query-by-label-text :metrics))))
(h/test "Renders loading state"
(let [data (get-test-data {:type :default
:loading? true})]
(h/render [account-card/view data])
(h/render-with-theme-provider [account-card/view data] theme)
(h/is-truthy (h/get-by-label-text :loading))))
(h/test "Renders loading state without metrics"
(let [data (get-test-data {:type :default
:no-metrics? true
:loading? true})]
(h/render [account-card/view data])
(h/render-with-theme-provider [account-card/view data] theme)
(h/is-falsy (h/query-by-label-text :metrics)))))
@@ -0,0 +1,33 @@
(ns quo.components.wallet.account-card.schema)
(def ^:private ?base
[:map
[:type {:optional true} [:enum :default :watch-only :add-account :empty :missing-keypair]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]
[:theme :schema.common/theme]
[:metrics? {:optional true} [:maybe :boolean]]
[:on-press {:optional true} [:maybe fn?]]])
(def ^:private ?amount
[:map
[:amount {:optional true} [:maybe :string]]])
(def ^:private ?card
[:map
[:balance {:optional true} [:maybe :string]]
[:loading? {:optional true} [:maybe :boolean]]
[:name {:optional true} [:maybe :string]]
[:percentage-value {:optional true} [:maybe :string]]
[:emoji {:optional true} [:maybe :string]]])
(def ?schema
[:=>
[:catn
[:props
[:multi {:dispatch :type}
[:default [:merge ?base ?amount ?card]]
[:watch-only [:merge ?base ?amount ?card]]
[:missing-keypair [:merge ?base ?amount ?card]]
[:empty [:merge ?base ?card]]
[:add-account ?base]]]]
:any])
@@ -4,13 +4,15 @@
[quo.components.icon :as icon]
[quo.components.markdown.text :as text]
[quo.components.wallet.account-card.properties :as properties]
[quo.components.wallet.account-card.schema :as component-schema]
[quo.components.wallet.account-card.style :as style]
[quo.foundations.colors :as colors]
[quo.foundations.customization-colors :as customization-colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[react-native.linear-gradient :as linear-gradient]
[reagent.core :as reagent]))
[reagent.core :as reagent]
[schema.core :as schema]))
(defn- loading-view
[{:keys [customization-color type theme metrics?]}]
@@ -183,4 +185,6 @@
:add-account [add-account-view props]
nil))
(def view (quo.theme/with-theme view-internal))
(def view
(quo.theme/with-theme
(schema/instrument #'view-internal component-schema/?schema)))
@@ -3,92 +3,104 @@
[quo.core :as quo]
[test-helpers.component :as h]))
(def ^:private theme :light)
(h/describe "account origin tests"
(h/test "component renders"
(h/render [quo/account-origin
{:type :recovery-phrase
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}])
(h/render-with-theme-provider [quo/account-origin
{:type :recovery-phrase
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}]
theme)
(-> (h/expect (h/get-by-translation-text :t/origin))
(.toBeTruthy)))
(h/test "recovery phrase icon is visible when :type is :recovery-phrase"
(h/render [quo/account-origin
{:type :recovery-phrase
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}])
(h/render-with-theme-provider [quo/account-origin
{:type :recovery-phrase
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}]
theme)
(h/is-truthy (h/get-by-label-text :recovery-phrase-icon)))
(h/test "recovery phrase icon is visible when :type is :recovery-phrase"
(h/render [quo/account-origin
{:type :recovery-phrase
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}])
(h/render-with-theme-provider [quo/account-origin
{:type :recovery-phrase
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}]
theme)
(h/is-truthy (h/get-by-label-text :recovery-phrase-icon)))
(h/test "private-key-icon is visible when :type is :private-key"
(h/render [quo/account-origin
{:type :private-key
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}])
(h/render-with-theme-provider [quo/account-origin
{:type :private-key
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}]
theme)
(h/is-truthy (h/get-by-label-text :private-key-icon)))
(h/test "derivation-path-icon is visible when :type is :default-keypair"
(h/render [quo/account-origin
{:type :default-keypair
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}])
(h/render-with-theme-provider [quo/account-origin
{:type :default-keypair
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}]
theme)
(h/is-truthy (h/get-by-label-text :derivation-path-icon)))
(h/test "derivation-path-icon is visible when :type is :recovery-phrase"
(h/render [quo/account-origin
{:type :recovery-phrase
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}])
(h/render-with-theme-provider [quo/account-origin
{:type :recovery-phrase
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}]
theme)
(h/is-truthy (h/get-by-label-text :derivation-path-icon)))
(h/test "on-device text is visible when :stored is :on-device"
(h/render [quo/account-origin
{:type :recovery-phrase
:stored :on-device
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}])
(h/render-with-theme-provider [quo/account-origin
{:type :recovery-phrase
:stored :on-device
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}]
theme)
(-> (h/expect (h/get-by-translation-text :t/on-device))
(.toBeTruthy)))
(h/test "on-keycard text is visible when :stored is :on-keycard"
(h/render [quo/account-origin
{:type :recovery-phrase
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}])
(h/render-with-theme-provider [quo/account-origin
{:type :recovery-phrase
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}]
theme)
(-> (h/expect (h/get-by-translation-text :t/on-keycard))
(.toBeTruthy)))
(h/test "on-press is called when :type is :recovery-phrase"
(let [on-press (h/mock-fn)]
(h/render [quo/account-origin
{:type :recovery-phrase
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"
:on-press on-press}])
(h/render-with-theme-provider [quo/account-origin
{:type :recovery-phrase
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"
:on-press on-press}]
theme)
(h/fire-event :press (h/query-by-label-text :derivation-path-button))
(h/was-called on-press)))
(h/test "on-press is called when :type is :default-keypair"
(let [on-press (h/mock-fn)]
(h/render [quo/account-origin
{:type :default-keypair
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"
:on-press on-press}])
(h/render-with-theme-provider [quo/account-origin
{:type :default-keypair
:stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"
:on-press on-press}]
theme)
(h/fire-event :press (h/query-by-label-text :derivation-path-button))
(h/was-called on-press))))
@@ -0,0 +1,30 @@
(ns quo.components.wallet.account-origin.schema)
(def ^:private ?base
[:map
[:type {:optional true} [:enum :default-keypair :recovery-phrase :private-key]]
[:stored {:optional true} [:enum :on-device :on-keycard]]
[:theme :schema.common/theme]])
(def ^:private ?default-keypair
[:map
[:user-name {:optional true} [:maybe :string]]
[:profile-picture {:optional true} [:maybe [:or :schema.common/image-source :string]]]
[:derivation-path {:optional true} [:maybe :string]]
[:on-press {:optional true} [:maybe fn?]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]])
(def ^:private ?recovery-phrase
[:map
[:derivation-path {:optional true} [:maybe :string]]
[:on-press {:optional true} [:maybe fn?]]])
(def ?schema
[:=>
[:catn
[:props
[:multi {:dispatch :type}
[:default-keypair [:merge ?base ?default-keypair]]
[:recovery-phrase [:merge ?base ?recovery-phrase]]
[:private-key ?base]]]]
:any])
@@ -3,10 +3,12 @@
[quo.components.avatars.user-avatar.view :as user-avatar]
[quo.components.icon :as icons]
[quo.components.markdown.text :as text]
[quo.components.wallet.account-origin.schema :as component-schema]
[quo.components.wallet.account-origin.style :as style]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[schema.core :as schema]
[utils.i18n :as i18n]))
(defn- row-title
@@ -107,14 +109,5 @@
[card-view theme derivation-path secondary-color on-press])]))
(def view
"Create an account-origin UI component.
| key | values |
| ----------------------|------------------------------------------------|
| :type | :default-keypair :recovery-phrase :private-key
| :stored | :on-device :on-keycard
| :profile-picture | image source
| :customization-color | profile color
| :derivation-path | string
| :user-name | string
| :on-press | function"
(quo.theme/with-theme view-internal))
(quo.theme/with-theme
(schema/instrument #'view-internal component-schema/?schema)))
@@ -0,0 +1,20 @@
(ns quo.components.wallet.account-overview.schema)
(def ?schema
[:=>
[:catn
[:props
[:map
[:account-name {:optional true} [:maybe :string]]
[:currency-change {:optional true} [:maybe :string]]
[:current-value {:optional true} [:maybe :string]]
[:percentage-change {:optional true} [:maybe :string]]
[:time-frame-string {:optional true} [:maybe :string]]
[:time-frame-to-string {:optional true} [:maybe :string]]
[:state {:optional true} [:enum :default :loading]]
[:metrics {:optional true} [:enum :negative :positive]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]
[:account {:optional true} [:enum :default :watched-address]]
[:time-frame {:optional true}
[:enum :one-week :one-month :three-months :one-year :all-time :custom]]]]]
:any])
@@ -2,10 +2,12 @@
(:require
[quo.components.icon :as icons]
[quo.components.markdown.text :as text]
[quo.components.wallet.account-overview.schema :as component-schema]
[quo.components.wallet.account-overview.style :as style]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[schema.core :as schema]
[utils.i18n :as i18n]))
(defn- loading-state
@@ -129,16 +131,5 @@
[numeric-changes percentage-change currency-change customization-color theme up?])]])]))
(def view
"Create a account-overview UI component.
| key | description |
| -------------|-------------|
| `:account-name` | A value representing the account name (default `nil`)
| `:account` | A value that represents if the account is a `:watched-address` or a `:default` account. (default `nil`)
| `:time-frame` | A value that represents the type of the timeframe, Can be from a preset of time frames. `[:1-week :1-month :3-months :1-year :custom]` (default `nil`) if custom is set, We expect a start time and if there's a space between two times that are in `time-frame-string` we'll split them with an arrow to the right icon
| `:loading?` | A value that indicates that component is loading data. (default `nil`)
| `:metrics` | A value that indicates if the account value have increased can be `:positive` or `:negative` (default `nil`)
| `:time-frame-string` | A value representing time frame string. (default `nil`)
| `:current-value` | A value representing the current value of the selected account. (default `nil`)
| `:currency-change` | A value representing the change of the value of the selected account in the selected currency. (default `nil`)
| `:percentage-change` | A value representing the change of the value of the selected account in percentage relative to it's previous value. (default `nil`)"
(quo.theme/with-theme view-internal))
(quo.theme/with-theme
(schema/instrument #'view-internal component-schema/?schema)))
@@ -42,18 +42,22 @@
(render [account-permissions/view
{:account account
:token-details [{:token "SNT"
:amount "100"}]}])
:amount "100"
:type :token}]}])
(h/is-truthy (h/get-by-text "100 SNT")))
(h/test "render with multiple token details"
(render [account-permissions/view
{:account account
:token-details [{:token "SNT"
:amount "100"}
:amount "100"
:type :token}
{:token "ETH"
:amount "18"}
:amount "18"
:type :token}
{:token "BTM"
:amount "1000"}]}])
:amount "1000"
:type :token}]}])
(h/is-truthy (h/get-by-text "100 SNT"))
(h/is-truthy (h/get-by-text "18 ETH"))
(h/is-truthy (h/get-by-text "1000 BTM")))
@@ -0,0 +1,23 @@
(ns quo.components.wallet.account-permissions.schema
(:require [quo.components.wallet.required-tokens.view :as required-tokens]))
(def ?schema
[:=>
[:catn
[:props
[:map
[:account
[:map
[:name [:maybe :string]]
[:address [:maybe :string]]
[:emoji [:maybe :string]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]]]
[:token-details {:optional true} [:maybe [:sequential required-tokens/?schema]]]
[:keycard? {:optional true} [:maybe :boolean]]
[:checked? {:optional true} [:maybe :boolean]]
[:disabled? {:optional true} [:maybe :boolean]]
[:on-change {:optional true} [:maybe fn?]]
[:container-style {:optional true} [:maybe :map]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]
[:theme :schema.common/theme]]]]
:any])
@@ -4,6 +4,7 @@
[quo.components.icon :as icons]
[quo.components.markdown.text :as text]
[quo.components.selectors.selectors.view :as selectors]
[quo.components.wallet.account-permissions.schema :as component-schema]
[quo.components.wallet.account-permissions.style :as style]
[quo.components.wallet.address-text.view :as address-text]
[quo.components.wallet.required-tokens.view :as required-tokens]
@@ -13,26 +14,6 @@
[schema.core :as schema]
[utils.i18n :as i18n]))
(def ?schema
[:=>
[:catn
[:props
[:map {:closed true}
[:account
[:map {:closed true}
[:name [:maybe :string]]
[:address [:maybe :string]]
[:emoji [:maybe :string]]
[:customization-color [:maybe [:or :string :keyword]]]]]
[:token-details {:optional true} [:maybe [:vector required-tokens/?schema]]]
[:keycard? {:optional true} [:maybe :boolean]]
[:checked? {:optional true} [:maybe :boolean]]
[:disabled? {:optional true} [:maybe :boolean]]
[:on-change {:optional true} [:maybe fn?]]
[:container-style {:optional true} [:maybe :map]]
[:theme :schema.common/theme]]]]
:any])
(defn- token-details-section
[tokens]
(when tokens
@@ -47,21 +28,25 @@
(i18n/label :t/no-relevant-tokens)]
(let [token-length (dec (count tokens))]
(map-indexed (fn [idx {:keys [token amount]}]
(map-indexed (fn [idx {:keys [type token amount collectible-name collectible-img-src]}]
^{:key idx}
[required-tokens/view
{:container-style style/token-and-text
:type :token
:amount amount
:token token
:divider? (not= token-length idx)}])
{:container-style style/token-and-text
:type type
:amount amount
:token token
:collectible-img-src collectible-img-src
:collectible-name collectible-name
:divider? (not= token-length idx)}])
tokens)))]]))
(defn- view-internal
[{:keys
[checked? disabled? on-change token-details keycard? theme container-style]
[checked? disabled? on-change token-details keycard? theme container-style customization-color]
{:keys
[name address emoji customization-color]} :account}]
[name address emoji]
:as account} :account
:or {customization-color :blue}}]
[rn/view
{:style (merge (style/container theme) container-style)
:accessibility-label :wallet-account-permissions}
@@ -69,7 +54,7 @@
[account-avatar/view
{:size 32
:emoji emoji
:customization-color customization-color}]
:customization-color (:customization-color account)}]
[rn/view {:style style/account-details}
[rn/view {:style style/name-and-keycard}
[text/text
@@ -84,13 +69,14 @@
{:address address
:format :short}]]
[selectors/view
{:type :checkbox
:checked? checked?
:disabled? disabled?
:on-change on-change}]]
{:type :checkbox
:checked? checked?
:customization-color customization-color
:disabled? disabled?
:on-change on-change}]]
[token-details-section token-details]])
(def view
(quo.theme/with-theme
(schema/instrument #'view-internal ?schema)))
(schema/instrument #'view-internal component-schema/?schema)))
@@ -0,0 +1,14 @@
(ns quo.components.wallet.address-text.schema)
(def ?schema
[:=>
[:catn
[:props
[:map
[:address {:optional true} [:maybe :string]]
[:blur? {:optional true} [:maybe :boolean]]
[:format {:optional true} [:enum :short :long]]
[:theme :schema.common/theme]
[:networks {:optional true}
[:maybe [:sequential [:map [:name :keyword] [:short-name :string]]]]]]]]
:any])
@@ -1,8 +1,10 @@
(ns quo.components.wallet.address-text.view
(:require [quo.components.markdown.text :as text]
[quo.components.wallet.address-text.schema :as component-schema]
[quo.components.wallet.address-text.style :as style]
[quo.foundations.colors :as colors]
[quo.theme]
[schema.core :as schema]
[utils.address :as utils]))
(defn- colored-network-text
@@ -28,4 +30,6 @@
(into [text/text] network-text-xf $)
(conj $ address-text))))
(def view (quo.theme/with-theme view-internal))
(def view
(quo.theme/with-theme
(schema/instrument #'view-internal component-schema/?schema)))
@@ -0,0 +1,43 @@
(ns quo.components.wallet.amount-input.component-spec
(:require
[oops.core :as oops]
[quo.components.wallet.amount-input.view :as amount-input]
[quo.foundations.colors :as colors]
[test-helpers.component :as h]))
(defn- render
[component]
(h/render-with-theme-provider component :light))
(h/describe "Amount input component"
(h/test "Renders with default value"
(let [text-expected 0]
(render [amount-input/view {:init-value text-expected}])
(h/is-truthy (h/query-by-label-text :amount-input))
(h/is-equal (oops/oget (h/get-by-label-text :amount-input) "props" "value")
(str text-expected))))
(h/test "When the value = minimum dec button is disabled"
(render [amount-input/view
{:init-value 0
:min-value 0}])
(h/is-truthy
(oops/oget (h/get-by-label-text :amount-input-dec-button) "props" "accessibilityState" "disabled")))
(h/test "When the value = maximum inc button is disabled"
(render [amount-input/view
{:init-value 100
:max-value 100}])
(h/is-truthy
(oops/oget (h/get-by-label-text :amount-input-inc-button) "props" "accessibilityState" "disabled")))
(h/test "Renders the error state"
(render [amount-input/view {:status :error}])
(h/is-equal (colors/resolve-color :danger :light)
(oops/oget (h/get-by-label-text :amount-input) "props" "style" "color")))
(h/test "on-change-text function is fired"
(let [on-change-text (h/mock-fn)]
(render [amount-input/view {:on-change-text on-change-text}])
(h/fire-event :change-text (h/get-by-label-text :amount-input) "100")
(h/was-called on-change-text))))
@@ -0,0 +1,21 @@
(ns quo.components.wallet.amount-input.schema)
(def return-key-types
[:enum :done :go :next :search :send :none :previous :default
:emergency-call :google :join :route :yahoo])
(def ?schema
[:=>
[:catn
[:props
[:map {:closed true}
[:status {:optional true} [:maybe [:enum :default :error]]]
[:theme :schema.common/theme]
[:on-change-text {:optional true} [:maybe fn?]]
[:container-style {:optional true} [:maybe :map]]
[:auto-focus? {:optional true} [:maybe :boolean]]
[:min-value {:optional true} [:maybe :int]]
[:max-value {:optional true} [:maybe :int]]
[:return-key-type {:optional true} [:maybe return-key-types]]
[:init-value {:optional true} [:maybe :int]]]]]
:any])
@@ -0,0 +1,17 @@
(ns quo.components.wallet.amount-input.style
(:require
[quo.foundations.colors :as colors]))
(def container
{:flex-direction :row
:justify-content :center
:align-items :center})
(def input-container {:flex 1})
(defn input-text
[theme type]
{:padding 0
:color (if (= type :error)
(colors/resolve-color :danger theme)
(colors/theme-colors colors/neutral-100 colors/white theme))})
@@ -0,0 +1,81 @@
(ns quo.components.wallet.amount-input.view
(:require
[quo.components.buttons.button.view :as button]
[quo.components.markdown.text :as text]
[quo.components.wallet.amount-input.schema :as amount-input.schema]
[quo.components.wallet.amount-input.style :as style]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[reagent.core :as reagent]
[schema.core :as schema]))
(defn- amount-button
[{:keys [theme accessibility-label disabled? icon on-press]}]
[button/button
{:icon-only? true
:theme theme
:disabled? disabled?
:type :outline
:accessibility-label accessibility-label
:size 32
:on-press on-press}
icon])
(defn- process-amount
[input-value min-value max-value]
(let [parsed-input-value (parse-double input-value)]
(cond
(nil? parsed-input-value) min-value
(>= input-value max-value) max-value
(<= input-value min-value) min-value
:else parsed-input-value)))
(defn- view-internal
[{:keys [init-value]}]
(let [init-value (or init-value 0)
value (reagent/atom init-value)
on-dec-press #(swap! value dec)
on-inc-press #(swap! value inc)]
(fn [{:keys [theme status min-value max-value auto-focus?
return-key-type container-style on-change-text]}]
(let [min-value (or min-value 0)
max-value (or max-value 999999999)]
[rn/view
{:style (merge style/container container-style)}
[amount-button
{:theme theme
:accessibility-label :amount-input-dec-button
:icon :i/remove
:on-press on-dec-press
:disabled? (>= min-value @value)}]
[rn/view {:style style/input-container}
[rn/text-input
{:style
(text/text-style
{:size :heading-1
:weight :semi-bold
:align :center
:style (style/input-text theme (or status :default))})
:accessibility-label :amount-input
:editable true
:auto-focus (or auto-focus? false)
:value (str @value)
:keyboard-appearance (quo.theme/theme-value :light :dark theme)
:return-key-type (or return-key-type :done)
:input-mode :numeric
:on-change-text (fn [input-value]
(let [processed-amount (process-amount input-value min-value max-value)]
(reset! value processed-amount)
(when on-change-text
(on-change-text processed-amount))
(reagent/flush)))}]] ; Fixes the input flickering issue when typing.
[amount-button
{:theme theme
:icon :i/add
:accessibility-label :amount-input-inc-button
:on-press on-inc-press
:disabled? (>= @value max-value)}]]))))
(def view
(quo.theme/with-theme
(schema/instrument #'view-internal amount-input.schema/?schema)))
@@ -1,99 +1,115 @@
(ns quo.components.wallet.confirmation-progress.component-spec
(:require [quo.core :as quo]
[reagent.core :as reagent]
[test-helpers.component :as h]))
(defn- get-test-data
[{:keys [state network]
:or {state :pending network :mainnet}}]
{:counter (reagent/atom 0)
{:counter 0
:total-box 85
:progress-value "10"
:progress-value 10
:network network
:state state
:customization-color :blue})
(h/describe "Confirmation Progress"
(h/test "component renders when state is sending and network is optimism"
(h/render [quo/confirmation-propgress
(get-test-data {:state :sending
:network :optimism})])
(h/is-truthy (h/get-by-label-text :progress-box)))
(h/render-with-theme-provider [quo/confirmation-progress
(get-test-data {:state :sending
:network :optimism})])
(h/is-truthy (h/get-by-label-text :progress-box))))
(h/test "component renders when state is confirmed and network is optimism"
(h/render [quo/confirmation-propgress
(get-test-data {:state :confirmed
:network :optimism})])
(h/is-truthy (h/get-by-label-text :progress-box)))
(h/test "component renders when state is confirmed and network is optimism"
(h/render-with-theme-provider
[quo/confirmation-progress
(get-test-data {:state :confirmed
:network :optimism})])
(h/is-truthy (h/get-by-label-text :progress-box)))
(h/test "component renders when state is finalising and network is optimism"
(h/render [quo/confirmation-propgress
(get-test-data {:state :finalising
:network :optimism})])
(h/is-truthy (h/get-by-label-text :progress-box)))
(h/test "component renders when state is finalising and network is optimism"
(h/render-with-theme-provider
[quo/confirmation-progress
(get-test-data {:state :finalising
:network :optimism})])
(h/is-truthy (h/get-by-label-text :progress-box)))
(h/test "component renders when state is finalized and network is optimism"
(h/render [quo/confirmation-propgress
(get-test-data {:state :finalized
:network :optimism})])
(h/is-truthy (h/get-by-label-text :progress-box)))
(h/test "component renders when state is finalized and network is optimism"
(h/render-with-theme-provider
[quo/confirmation-progress
(get-test-data {:state :finalized
:network :optimism})])
(h/is-truthy (h/get-by-label-text :progress-box)))
(h/test "component renders when state is error and network is optimism"
(h/render [quo/confirmation-propgress
(get-test-data {:state :error
:network :optimism})])
(h/is-truthy (h/get-by-label-text :progress-box)))
(h/test "component renders when state is error and network is optimism"
(h/render-with-theme-provider
[quo/confirmation-progress
(get-test-data {:state :error
:network :optimism})])
(h/is-truthy (h/get-by-label-text :progress-box)))
(h/test "component renders when state is sending and network is arbitrum"
(h/render [quo/confirmation-propgress
(get-test-data {:state :sending
:network :arbitrum})])
(h/is-truthy (h/get-by-label-text :progress-box)))
(h/test "component renders when state is sending and network is arbitrum"
(h/render-with-theme-provider
[quo/confirmation-progress
(get-test-data {:state :sending
:network :arbitrum})])
(h/is-truthy (h/get-by-label-text :progress-box)))
(h/test "component renders when state is confirmed and network is arbitrum"
(h/render [quo/confirmation-propgress
(get-test-data {:state :confirmed
:network :arbitrum})])
(h/is-truthy (h/get-by-label-text :progress-box)))
(h/test "component renders when state is confirmed and network is arbitrum"
(h/render-with-theme-provider
[quo/confirmation-progress
(get-test-data {:state :confirmed
:network :arbitrum})])
(h/is-truthy (h/get-by-label-text :progress-box)))
(h/test "component renders when state is finalising and network is arbitrum"
(h/render [quo/confirmation-propgress
(get-test-data {:state :finalising
:network :arbitrum})])
(h/is-truthy (h/get-by-label-text :progress-box)))
(h/test "component renders when state is finalising and network is arbitrum"
(h/render-with-theme-provider
[quo/confirmation-progress
(get-test-data {:state :finalising
:network :arbitrum})])
(h/is-truthy (h/get-by-label-text :progress-box)))
(h/test "component renders when state is finalized and network is arbitrum"
(h/render [quo/confirmation-propgress
(get-test-data {:state :finalized
:network :arbitrum})])
(h/is-truthy (h/get-by-label-text :progress-box)))
(h/test "component renders when state is finalized and network is arbitrum"
(h/render-with-theme-provider
[quo/confirmation-progress
(get-test-data {:state :finalized
:network :arbitrum})])
(h/is-truthy (h/get-by-label-text :progress-box)))
(h/test "component renders when state is error and network is arbitrum"
(h/render [quo/confirmation-propgress
(get-test-data {:state :error
:network :arbitrum})])
(h/is-truthy (h/get-by-label-text :progress-box)))
(h/test "component renders when state is error and network is arbitrum"
(h/render-with-theme-provider
[quo/confirmation-progress
(get-test-data {:state :error
:network :arbitrum})])
(h/is-truthy (h/get-by-label-text :progress-box)))
(h/test "component renders when state is pending and network is mainnet"
(h/render [quo/confirmation-propgress (get-test-data {})])
(h/is-truthy (h/get-by-label-text :mainnet-progress-box)))
(h/test "component renders when state is pending and network is mainnet"
(h/render-with-theme-provider
[quo/confirmation-progress (get-test-data {})])
(h/is-truthy (h/get-by-label-text :mainnet-progress-box)))
(h/test "component renders when state is sending and network is mainnet"
(h/render [quo/confirmation-propgress (get-test-data {:state :sending})])
(h/is-truthy (h/get-by-label-text :mainnet-progress-box)))
(h/test "component renders when state is sending and network is mainnet"
(h/render-with-theme-provider
[quo/confirmation-progress (get-test-data {:state :sending})])
(h/is-truthy (h/get-by-label-text :mainnet-progress-box)))
(h/test "component renders when state is confirmed and network is mainnet"
(h/render [quo/confirmation-propgress (get-test-data {:state :confirmed})])
(h/is-truthy (h/get-by-label-text :mainnet-progress-box)))
(h/test "component renders when state is confirmed and network is mainnet"
(h/render-with-theme-provider
[quo/confirmation-progress (get-test-data {:state :confirmed})])
(h/is-truthy (h/get-by-label-text :mainnet-progress-box)))
(h/test "component renders when state is finalising and network is mainnet"
(h/render [quo/confirmation-propgress (get-test-data {:state :finalising})])
(h/is-truthy (h/get-by-label-text :mainnet-progress-box)))
(h/test "component renders when state is finalising and network is mainnet"
(h/render-with-theme-provider
[quo/confirmation-progress (get-test-data {:state :finalising})])
(h/is-truthy (h/get-by-label-text :mainnet-progress-box)))
(h/test "component renders when state is finalized and network is mainnet"
(h/render [quo/confirmation-propgress (get-test-data {:state :finalized})])
(h/is-truthy (h/get-by-label-text :mainnet-progress-box)))
(h/test "component renders when state is finalized and network is mainnet"
(h/render-with-theme-provider
[quo/confirmation-progress (get-test-data {:state :finalized})])
(h/is-truthy (h/get-by-label-text :mainnet-progress-box)))
(h/test "component renders when state is error and network is mainnet"
(h/render [quo/confirmation-propgress (get-test-data {:state :error})])
(h/is-truthy (h/get-by-label-text :mainnet-progress-box))))
(h/test "component renders when state is error and network is mainnet"
(h/render-with-theme-provider
[quo/confirmation-progress
(get-test-data {:state :error})])
(h/is-truthy (h/get-by-label-text :mainnet-progress-box)))
@@ -0,0 +1,15 @@
(ns quo.components.wallet.confirmation-progress.schema)
(def ?schema
[:=>
[:catn
[:props
[:map
[:total-box {:optional true} [:maybe :int]]
[:counter {:optional true} [:maybe :int]]
[:progress-value {:optional true} [:maybe :int]]
[:network {:optional true} [:enum :mainnet :optimism :arbitrum]]
[:state {:optional true} [:enum :pending :sending :confirmed :finalising :finalized :error]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]
[:theme :schema.common/theme]]]]
:any])
@@ -1,8 +1,10 @@
(ns quo.components.wallet.confirmation-progress.view
(:require [quo.components.wallet.confirmation-progress.style :as style]
(:require [quo.components.wallet.confirmation-progress.schema :as component-schema]
[quo.components.wallet.confirmation-progress.style :as style]
[quo.components.wallet.progress-bar.view :as progress-box]
[quo.theme :as quo.theme]
[react-native.core :as rn]))
[react-native.core :as rn]
[schema.core :as schema]))
(def ^:private max-progress 100)
(def ^:private min-progress 0)
@@ -66,4 +68,6 @@
(:arbitrum :optimism) [progress-boxes-sidenet props]
nil))
(def view (quo.theme/with-theme view-internal))
(def view
(quo.theme/with-theme
(schema/instrument #'view-internal component-schema/?schema)))
@@ -3,6 +3,8 @@
[quo.components.wallet.keypair.view :as keypair]
[test-helpers.component :as h]))
(def ^:private theme :light)
(def accounts
[{:account-props {:customization-color :turquoise
:size 32
@@ -23,41 +25,45 @@
(h/describe "Wallet: Keypair"
(h/test "Default keypair title renders"
(h/render [keypair/view
{:accounts accounts
:customization-color :blue
:type :default-keypair
:stored :on-device
:action :selector
:details default-details}])
(h/render-with-theme-provider [keypair/view
{:accounts accounts
:customization-color :blue
:type :default-keypair
:stored :on-device
:action :selector
:details default-details}]
theme)
(h/is-truthy (h/get-by-label-text :title)))
(h/test "On device renders"
(h/render [keypair/view
{:accounts accounts
:customization-color :blue
:type :other
:stored :on-device
:action :selector
:details other-details}])
(h/render-with-theme-provider [keypair/view
{:accounts accounts
:customization-color :blue
:type :other
:stored :on-device
:action :selector
:details other-details}]
theme)
(h/is-truthy (h/get-by-label-text :details)))
(h/test "Selector action renders"
(h/render [keypair/view
{:accounts accounts
:customization-color :blue
:type :other
:stored :on-keycard
:action :selector
:details other-details}])
(h/render-with-theme-provider [keypair/view
{:accounts accounts
:customization-color :blue
:type :other
:stored :on-keycard
:action :selector
:details other-details}]
theme)
(h/is-truthy (h/get-by-label-text :radio-on)))
(h/test "Options action renders"
(h/render [keypair/view
{:accounts accounts
:customization-color :blue
:type :other
:stored :on-keycard
:action :options
:details other-details}])
(h/render-with-theme-provider [keypair/view
{:accounts accounts
:customization-color :blue
:type :other
:stored :on-keycard
:action :options
:details other-details}]
theme)
(h/is-truthy (h/get-by-label-text :options-button))))

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