Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9abb794785 | ||
|
|
bfed36d19f | ||
|
|
aefb0338ec | ||
|
|
456ccb4c69 | ||
|
|
be2c697b7c | ||
|
|
b27f323dd7 | ||
|
|
a33e7f2e90 | ||
|
|
54d9e5ebeb | ||
|
|
49567596da | ||
|
|
2e23dc7ad7 | ||
|
|
dc53bddb66 | ||
|
|
10f9fe11db | ||
|
|
2e13cfc47f | ||
|
|
e6ea8ae9a9 | ||
|
|
834c0a3800 | ||
|
|
2c96c38339 | ||
|
|
1d2c01c16b | ||
|
|
8dc71f6432 | ||
|
|
25f9499454 | ||
|
|
42cab08553 | ||
|
|
83523b1923 | ||
|
|
f762cdcf9b | ||
|
|
357db52720 | ||
|
|
5fdaf0054d | ||
|
|
4e3ce54ac4 | ||
|
|
37be08d2d7 | ||
|
|
3c4d27b6ff | ||
|
|
c5df51d944 | ||
|
|
b1c467de19 | ||
|
|
c766d21296 | ||
|
|
e86faa0767 | ||
|
|
abe0342be0 | ||
|
|
6669c33e33 | ||
|
|
1351a54fd1 | ||
|
|
da192dcdb3 | ||
|
|
6e22e63158 | ||
|
|
d1f22cd2b8 | ||
|
|
9dacc9e8af | ||
|
|
9acf67dd5b | ||
|
|
ab73c7e56a | ||
|
|
f1db73d149 | ||
|
|
d78d7719cd | ||
|
|
e381998b19 | ||
|
|
025c38ae88 | ||
|
|
523a5809d0 | ||
|
|
505f2fe624 | ||
|
|
ac32de89f9 | ||
|
|
52a82fffab | ||
|
|
d1c8f6411d | ||
|
|
7b639f2ffe | ||
|
|
ae7ee33f00 | ||
|
|
c70646bd5c | ||
|
|
484ab919b1 | ||
|
|
052d88b531 | ||
|
|
0aba789b5c | ||
|
|
73450d736a | ||
|
|
cc63c8fff5 | ||
|
|
ef6027575f |
@@ -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}
|
||||
|
||||
@@ -194,3 +194,6 @@ test/appium/tests/users.py
|
||||
|
||||
## git hooks
|
||||
lefthook.yml
|
||||
|
||||
## metro server logs
|
||||
metro-server-logs.log
|
||||
|
||||
@@ -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)"
|
||||
|
||||
@@ -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,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,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,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(
|
||||
|
||||
@@ -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,5 +1,5 @@
|
||||
#!/usr/bin/env groovy
|
||||
library 'status-jenkins-lib@v1.8.5'
|
||||
library 'status-jenkins-lib@v1.8.7'
|
||||
|
||||
pipeline {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env groovy
|
||||
library 'status-jenkins-lib@v1.8.5'
|
||||
library 'status-jenkins-lib@v1.8.7'
|
||||
|
||||
pipeline {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env groovy
|
||||
library 'status-jenkins-lib@v1.8.5'
|
||||
library 'status-jenkins-lib@v1.8.7'
|
||||
|
||||
pipeline {
|
||||
|
||||
|
||||
@@ -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' }
|
||||
|
||||
@@ -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,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,5 +1,5 @@
|
||||
#!/usr/bin/env groovy
|
||||
library 'status-jenkins-lib@v1.8.5'
|
||||
library 'status-jenkins-lib@v1.8.7'
|
||||
|
||||
pipeline {
|
||||
agent {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -793,7 +793,7 @@ CHECKOUT OPTIONS:
|
||||
:submodules: true
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
boost: 57d2868c099736d80fcd648bf211b4431e51a558
|
||||
boost: 64032b9e9b938fda23325e68a3771f0fabf414dc
|
||||
BVLinearGradient: 612a04ff38e8480291f3379ee5b5a2c571f03fe0
|
||||
CryptoSwift: c4f2debceb38bf44c80659afe009f71e23e4a082
|
||||
DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
appId: im.status.ethereum.debug
|
||||
---
|
||||
- runFlow: # to run onboarding flow only when device is in onboarding state
|
||||
when:
|
||||
visible: "I’m 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
|
||||
@@ -0,0 +1,5 @@
|
||||
appId: im.status.ethereum.debug
|
||||
---
|
||||
- tapOn: "password-input"
|
||||
- inputText: "password123" # update your account password here
|
||||
- tapOn: login-button
|
||||
@@ -0,0 +1,15 @@
|
||||
appId: im.status.ethereum.debug
|
||||
---
|
||||
- tapOn: "I’m 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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,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
|
||||
];
|
||||
|
||||
|
||||
@@ -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 { };
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
@@ -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,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
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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 ]
|
||||
);
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 695 B |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 181 B |
|
After Width: | Height: | Size: 217 B |
@@ -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[@]}"
|
||||
|
||||
@@ -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'
|
||||
@@ -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'
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
pkill -f 'react-native start'
|
||||
|
||||
react-native start --reset-cache
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
:PeerExchange true
|
||||
:Port 0
|
||||
:UDPPort 0
|
||||
:LightClient true})
|
||||
:LightClient false})
|
||||
|
||||
(def login-node-config
|
||||
{:WalletConfig (cond-> {:Enabled true}
|
||||
|
||||
@@ -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?))))
|
||||
|
||||
@@ -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))))
|
||||
|
||||