Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
75c68bcc60 | ||
|
|
8375eebe71 | ||
|
|
a4717ae5d0 | ||
|
|
a47998a7a0 | ||
|
|
141bf5a333 | ||
|
|
231272603d | ||
|
|
255a3b9172 | ||
|
|
6156bfc472 | ||
|
|
6870000490 | ||
|
|
0ef7dc34e4 | ||
|
|
0f7e98cbe2 | ||
|
|
f4d6f64a66 | ||
|
|
a416267094 | ||
|
|
2358eb195b | ||
|
|
21be0ed16c | ||
|
|
89d1bd3f6a | ||
|
|
5148a3c748 | ||
|
|
8701f3545d | ||
|
|
26eeb43569 | ||
|
|
4d7b1baace | ||
|
|
89c2b1c818 | ||
|
|
17df7a0d35 | ||
|
|
0552713143 | ||
|
|
3223c71526 | ||
|
|
914604bbe4 | ||
|
|
821e39a562 | ||
|
|
4cef868eab | ||
|
|
34f7a48503 | ||
|
|
e0f83384a2 | ||
|
|
8fe7fad540 | ||
|
|
4dd5d88f0f | ||
|
|
27f11fcb2d | ||
|
|
8e7c6fe778 | ||
|
|
79daa4d529 | ||
|
|
bdedccdda6 | ||
|
|
cace2a32a0 | ||
|
|
92403f5d30 | ||
|
|
4c9ccf3069 | ||
|
|
50f3bb005e | ||
|
|
a45f353356 | ||
|
|
e4a2ac9cce | ||
|
|
bdfdf687a4 | ||
|
|
95ec35343c | ||
|
|
e186f513ed | ||
|
|
774687bef6 | ||
|
|
66e3b60703 | ||
|
|
99294fa8ea | ||
|
|
e11a761fcc | ||
|
|
999588e67d | ||
|
|
9cae50dc88 | ||
|
|
aecad2136e | ||
|
|
673b1afc3a | ||
|
|
b0f748cb73 | ||
|
|
d988296ecf | ||
|
|
dc8a09ce75 | ||
|
|
0dea703ca0 | ||
|
|
50134ebeff | ||
|
|
a6710f902f |
@@ -95,11 +95,11 @@ status-im.hardwallet.simulated-keycard/sign-typed-data
|
||||
status-im.utils.core/safe-read-message-content
|
||||
status-im.ui.components.react/native-modules
|
||||
status-im.ui.components.react/progress-bar
|
||||
status-im.utils.fs/move-file
|
||||
status-im.utils.fs/read-dir
|
||||
status-im.utils.fs/mkdir
|
||||
status-im.utils.fs/unlink
|
||||
status-im.utils.fs/file-exists?
|
||||
react-native.fs/move-file
|
||||
react-native.fs/read-dir
|
||||
react-native.fs/mkdir
|
||||
react-native.fs/unlink
|
||||
react-native.fs/file-exists?
|
||||
quo.animated/code
|
||||
quo.animated/eq
|
||||
quo.animated/neq
|
||||
|
||||
@@ -4,6 +4,8 @@ apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.grad
|
||||
|
||||
import com.android.build.OutputFile
|
||||
import com.sun.org.apache.xalan.internal.xsltc.compiler.Copy
|
||||
import groovy.json.JsonBuilder
|
||||
import groovy.json.JsonSlurper
|
||||
|
||||
/**
|
||||
* The react.gradle file registers a task for each build variant:
|
||||
@@ -463,6 +465,32 @@ task hemroidBuild(type: Exec) {
|
||||
executable execPath
|
||||
}
|
||||
|
||||
// Add this task definition at the bottom of your build.gradle file
|
||||
task printAllDependencies {
|
||||
doLast {
|
||||
println "Listing all dependencies for project..."
|
||||
|
||||
// Iterate through all configurations
|
||||
configurations.all { config ->
|
||||
int dependencyCount = 0
|
||||
|
||||
// Count the dependencies for this configuration
|
||||
config.allDependencies.each { dependency ->
|
||||
dependencyCount++
|
||||
}
|
||||
|
||||
// Print the configuration name and dependency count
|
||||
println "Configuration: ${config.name} (Total Dependencies: ${dependencyCount})"
|
||||
|
||||
// Iterate through all dependencies in the current configuration
|
||||
config.allDependencies.each { dependency ->
|
||||
println " - ${dependency.group}:${dependency.name}:${dependency.version}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
preBuild.dependsOn hemroidBuild
|
||||
|
||||
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
|
||||
|
||||
@@ -16,7 +16,7 @@ The responsibilities of the team are to implement the new wallet UI, with the su
|
||||
|
||||
## Decision
|
||||
|
||||
Some team members have already expressed interest and some have been asked and accepted to join the wallet team, these team members are:
|
||||
The team members for the initial wallet team are:
|
||||
|
||||
- @rasom
|
||||
- @OmarBasem
|
||||
@@ -25,7 +25,6 @@ Some team members have already expressed interest and some have been asked and a
|
||||
- @briansztamfater
|
||||
- @smohamedjavid
|
||||
- @ulisesmac
|
||||
|
||||
# Follow-up Actions
|
||||
|
||||
- We still need 3/4 team members to join the wallet team
|
||||
- @erikseppanen
|
||||
- @mmilad75
|
||||
- @tumanov-alex
|
||||
|
||||
@@ -33,7 +33,7 @@ The people identified for each sections are as follow:
|
||||
|
||||
- Messaging @flexsurfer, followed by @alwx
|
||||
- Syncing & Onboarding @siddarthkay
|
||||
- Communities @ilmotta
|
||||
- Communities @ilmotta, followed by @ajayesivan
|
||||
- Activity Center & Shell @J-Son89, followed by @Parveshdhull
|
||||
- Wallet (once the vertical team is created) @J-Son89, followed by @briansztamfater
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ Ready for testing PR should meet the following criteria:
|
||||
3. Wait for the review.
|
||||
4. Make sure that after review and before requesting manual QA your PR is rebased to current develop.
|
||||
5. Once the PR has been approved by reviewer(s) - label `request-manual-qa` should be applied to the PR
|
||||
6. Move PR to the E2E column when it is ready for testing. That will also trigger e2e tests run. QAs are monitoring PRs from E2E column and take it into test.
|
||||
6. Move PR to the E2E column when it is ready for testing (**mandatory for all PRs**). That will also trigger e2e tests run. QAs are monitoring PRs from E2E column and take it into test.
|
||||
|
||||
After that - PR will be taken into manual testing by the QA team.
|
||||
|
||||
@@ -41,27 +41,37 @@ After that - PR will be taken into manual testing by the QA team.
|
||||
- QA engineer picks up one of PRs with the ```request-manual-qa``` label, drags the item to the ```IN TESTING``` column and assigns it to themselves.
|
||||
- During testing, QA will add comments describing the issues found, and also review automation tests results.
|
||||
Usually found issues are numbered as "Issue 1, Issue 2", etc.
|
||||
When the first round of testing is completed and all issues for this stage are found, tester can add the ```Tested - Issues``` label and drag the card to the ```CONTRIBUTOR``` column. These two actions are optional.
|
||||
- When manual testing of PR is fully completed and all issues are fixed, QA adds the ```Tested - OK``` label and drags the card to the ```MERGE``` column, after which developer merges PR into develop.
|
||||
If manual testing was not carried out, developer drags PR to the ```MERGE``` column themselves.
|
||||
When the first round of testing is completed and all issues for this stage are found, the QA can add the ```Tested - Issues``` label and drag the card to the ```CONTRIBUTOR``` column. These two actions are optional.
|
||||
- When manual testing of the PR is fully completed and all the issues are fixed, the QA adds the ```Tested - OK``` label and drags the card to the ```Design review``` column (the cases when design review is necessary are described below).
|
||||
- If design review is not required, the QA drags the PR to the ```MERGE``` column. After that the developer merges PR into develop.
|
||||
- If design review has been done, the designer (```@Francesca-G```) drags the PR to the ```MERGE``` column.
|
||||
After that the developer merges PR into develop.
|
||||
- If manual testing/design review was not carried out, the developer drags PR to the ```MERGE``` column themselves.
|
||||
|
||||
### Design review
|
||||
_**How do I know if a design review is needed?**_
|
||||
|
||||
There are three cases here depending on the changes in the PR:
|
||||
1. **Functional PRs with UI changes:** after the ```Tested - OK``` label is added, the QA moves the PR to the ```Design review``` column + mentions ```@Francesca-G``` in comments.
|
||||
2. **Component PRs:** can be moved directly to the ```Design review``` column by the developer when get a review from devs (manual testing step can be skipped) + the developer mentions ```@Francesca-G``` in comments.
|
||||
3. **Functional PRs changes in which are not related to UI (e.g. a crash fix):** skip the ```Design review``` step (the PR should only be manually tested by QA).
|
||||
|
||||
There are three possible scenarios when the design review is completed:
|
||||
1. **Approved by design** > the PR is moved to the ```MERGE``` column by Francesca
|
||||
2. **Some changes requested, but can be fixed as followups** > need to be negotiated with Francesca > Francesca moves PR to the ```MERGE``` column and adds the ```Follow-up required``` label > follow-ups should be added **by the dev** right after the PR is merged
|
||||
3. **Some changes requested, they should be fixed before merge** > Francesca reviews the PR again after the fixes and moves PR to the ```MERGE``` column from ```Design review```
|
||||
|
||||
---
|
||||
**Notes:**
|
||||
- If your PR has a long story and started from `develop` branch several days ago, please rebase it to current develop before adding label
|
||||
- if PR can be tested by developer (in case of small changes) and/or developer is sure that the changes made cannot introduce a regression, then PR can be merged without manual testing. Also, currently, PRs are not manually tested if the changes relate only the design (creation of components, etc.) and do not affect the functionality.
|
||||
---
|
||||
|
||||
**Notes:**
|
||||
- If your PR has a long story and started from `develop` branch several days ago, please rebase it to current develop before adding label
|
||||
- if PR can be tested by developer (in case of small changes) and/or developer is sure that the changes made cannot introduce a regression, then PR can be merged without manual testing. Also, currently, PRs are not manually tested if the changes relate only the design (creation of components, etc.) and do not affect the functionality.
|
||||
|
||||
#### Why my PR is in `Contributor` column?
|
||||
PR can be moved to this column by the ```status-github-bot``` or by QA engineer with label `Tested-issues`.
|
||||
In the first case most often this happens due to conflicting files in PR.
|
||||
In the second case - after fixing of all found issues, the developer should ping the QA in the PR comments for retesting.
|
||||
|
||||
|
||||
#### Why is my PR in `To Rebase` column?
|
||||
PR is moved to the "To Rebase" column in two cases:
|
||||
- automatically by github bot if PR branch has conflicts that should be resolved
|
||||
- manually (by QAs) if PR branch is out-of-date with the base branch and requires rebasing to the latest develop
|
||||
|
||||
If PR appeared in the "To Rebase" column dev who is working on the PR should resolve conflicts/rebase branch to the latest develop. After resolving conflicts/rebasing PR should be moved by developer to the right column depending on PR work progress.
|
||||
|
||||
## Merging a PR
|
||||
**Merge conditions:**
|
||||
1. Required number of reviews received
|
||||
@@ -73,6 +83,7 @@ If PR appeared in the "To Rebase" column dev who is working on the PR should res
|
||||
|
||||
|
||||
6. In case of manual testing - the label ```Tested - OK``` from QA
|
||||
7. In case of design review - the approval from the designer
|
||||
|
||||
|
||||
You can merge your PR into develop - some useful clues you can find [here](https://notes.status.im/setup-e2e#3-Merging-PR)
|
||||
|
||||
@@ -15,19 +15,11 @@ rec {
|
||||
];
|
||||
|
||||
inputsFrom = [
|
||||
release
|
||||
(release {})
|
||||
androidShell
|
||||
];
|
||||
|
||||
shellHook = ''
|
||||
export ANDROID_SDK_ROOT="${androidPkgs.sdk}"
|
||||
export ANDROID_NDK_ROOT="${androidPkgs.ndk}"
|
||||
|
||||
export STATUS_NIX_MAVEN_REPO="${deps.gradle}"
|
||||
|
||||
# required by some makefile targets
|
||||
export STATUS_GO_ANDROID_LIBDIR=${status-go}
|
||||
|
||||
# check if node modules changed and if so install them
|
||||
$STATUS_MOBILE_HOME/nix/scripts/node_modules.sh ${deps.nodejs-patched}
|
||||
'';
|
||||
|
||||
@@ -30,6 +30,9 @@ let
|
||||
# Pass secretsFile for POKT_TOKEN to jsbundle build
|
||||
builtJsBundle = jsbundle { inherit secretsFile; };
|
||||
|
||||
# Map ANDROID_ABI_INCLUDE to status-go targets
|
||||
androidAbiIncludeSplit = lib.splitString ";" androidAbiInclude;
|
||||
|
||||
|
||||
envFileName =
|
||||
if androidAbiInclude == "x86" then ".env.e2e"
|
||||
@@ -77,20 +80,25 @@ in stdenv.mkDerivation rec {
|
||||
ANDROID_ABI_SPLIT = androidAbiSplit;
|
||||
ANDROID_ABI_INCLUDE = androidAbiInclude;
|
||||
|
||||
# Android SDK/NDK for use by Gradle
|
||||
ANDROID_SDK_ROOT = "${androidPkgs.sdk}";
|
||||
ANDROID_NDK_ROOT = "${androidPkgs.ndk}";
|
||||
|
||||
# Fix for ERR_OSSL_EVP_UNSUPPORTED error.
|
||||
NODE_OPTIONS = "--openssl-legacy-provider";
|
||||
|
||||
# Used by the Android Gradle build script in android/build.gradle
|
||||
STATUS_GO_ANDROID_LIBDIR = status-go;
|
||||
|
||||
phases = [
|
||||
"unpackPhase" "secretsPhase" "buildPhase" "checkPhase" "installPhase"
|
||||
"shellHook" "unpackPhase" "secretsPhase" "buildPhase" "checkPhase" "installPhase"
|
||||
];
|
||||
|
||||
# We use shellHook as a single place to setup env vars for both build derivation and shell
|
||||
shellHook = ''
|
||||
# Used by the Android Gradle build script in android/build.gradle
|
||||
export STATUS_GO_ANDROID_LIBDIR=${ status-go { abis = androidAbiIncludeSplit; } }
|
||||
|
||||
# Android SDK/NDK for use by Gradle
|
||||
export ANDROID_SDK_ROOT="${androidPkgs.sdk}"
|
||||
export ANDROID_NDK_ROOT="${androidPkgs.ndk}"
|
||||
|
||||
export STATUS_NIX_MAVEN_REPO="${deps.gradle}"
|
||||
'';
|
||||
|
||||
unpackPhase = ''
|
||||
cp -ar $src/. ./
|
||||
chmod u+w -R ./
|
||||
|
||||
@@ -66,7 +66,7 @@ let
|
||||
# helpers for use with target argument
|
||||
ios = targets.mobile.ios.shell;
|
||||
android = targets.mobile.android.shell;
|
||||
status-go = targets.status-go.mobile.android;
|
||||
status-go = targets.status-go.mobile.android {};
|
||||
};
|
||||
|
||||
# for merging the default shell with others
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
{ callPackage, meta, source, goBuildLdFlags }:
|
||||
|
||||
{
|
||||
android = callPackage ./build.nix {
|
||||
android = {abis ? [ "armeabi-v7a" "arm64-v8a" "x86" ]}: callPackage ./build.nix {
|
||||
platform = "android";
|
||||
platformVersion = "23";
|
||||
targets = [ "android/arm" "android/arm64" "android/386" ];
|
||||
# Hide different arch naming in gomobile from Android builds.
|
||||
targets = let
|
||||
abiMap = {
|
||||
"armeabi-v7a" = "android/arm";
|
||||
"arm64-v8a" = "android/arm64";
|
||||
"x86" = "android/386";
|
||||
"x86_64" = "android/amd64";
|
||||
};
|
||||
in map (arch: abiMap."${arch}") abis;
|
||||
outputFileName = "status-go-${source.shortRev}.aar";
|
||||
inherit meta source goBuildLdFlags;
|
||||
};
|
||||
|
||||
@@ -66,13 +66,13 @@
|
||||
"react-native-randombytes": "^3.6.1",
|
||||
"react-native-reanimated": "2.11.0",
|
||||
"react-native-redash": "^16.0.11",
|
||||
"react-native-svg": "13.10.0",
|
||||
"react-native-shake": "^3.3.1",
|
||||
"react-native-share": "^8.2.2",
|
||||
"react-native-static-safe-area-insets": "^2.2.0",
|
||||
"react-native-status-keycard": "git+https://github.com/status-im/react-native-status-keycard.git#refs/tags/v2.5.39",
|
||||
"react-native-svg": "13.10.0",
|
||||
"react-native-touch-id": "^4.4.1",
|
||||
"react-native-transparent-video": "git+https://github.com/status-im/react-native-transparent-video.git#refs/tags/0.0.9",
|
||||
"react-native-transparent-video": "git+https://github.com/status-im/react-native-transparent-video.git#refs/tags/0.1.1",
|
||||
"react-native-webview": "git+https://github.com/status-im/react-native-webview.git#refs/tags/v11.16.0-status",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"rn-emoji-keyboard": "0.7.0",
|
||||
|
||||
|
After Width: | Height: | Size: 7.8 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 737 B |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 7.0 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 9.2 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 923 B |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 721 B |
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -10,14 +10,14 @@
|
||||
;; (-> reanimated/slide-out-up-animation (.damping 20) .springify (.stiffness 300))
|
||||
(js/eval
|
||||
"
|
||||
var globalThis
|
||||
if (typeof window === \"undefined\") {
|
||||
globalThis = global
|
||||
} else {
|
||||
globalThis = window
|
||||
}
|
||||
globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return () => globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__}})
|
||||
")
|
||||
var globalThis
|
||||
if (typeof window === \"undefined\") {
|
||||
globalThis = global
|
||||
} else {
|
||||
globalThis = window
|
||||
}
|
||||
globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return () => globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__}})
|
||||
")
|
||||
|
||||
(def action-button #js {:default #js {:Item #js {}}})
|
||||
(def config #js {:default #js {}})
|
||||
@@ -90,7 +90,8 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return (
|
||||
:configureNext (fn [])}
|
||||
:requireNativeComponent (fn [] {:propTypes ""})
|
||||
:Appearance {:getColorScheme (fn [])
|
||||
:addChangeListener (fn [])}}))
|
||||
:addChangeListener (fn [])}
|
||||
:PixelRatio {:get (fn [])}}))
|
||||
|
||||
(set! js/ReactNative react-native)
|
||||
|
||||
@@ -138,15 +139,17 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return (
|
||||
(def react-native-share #js {:default {}})
|
||||
(def react-native-svg
|
||||
#js
|
||||
{:ClipPath #js {:render identity}
|
||||
:Circle #js {:render identity}
|
||||
:Defs #js {:render identity}
|
||||
:G #js {:render identity}
|
||||
:Path #js {:render identity}
|
||||
:Rect #js {:render identity}
|
||||
:SvgUri #js {:render identity}
|
||||
:SvgXml #js {:render identity}
|
||||
:default #js {:render identity}})
|
||||
{:ClipPath #js {:render identity}
|
||||
:Circle #js {:render identity}
|
||||
:Defs #js {:render identity}
|
||||
:G #js {:render identity}
|
||||
:Path #js {:render identity}
|
||||
:Rect #js {:render identity}
|
||||
:SvgUri #js {:render identity}
|
||||
:SvgXml #js {:render identity}
|
||||
:LinearGradient #js {:render identity}
|
||||
:Stop #js {:render identity}
|
||||
:default #js {:render identity}})
|
||||
(def react-native-webview #js {:default {}})
|
||||
(def react-native-audio-toolkit
|
||||
#js
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
|
||||
(defn container
|
||||
[{:keys [container-size customization-color theme]}]
|
||||
{:width container-size
|
||||
:height container-size
|
||||
:align-items :center
|
||||
:justify-content :center
|
||||
:border-radius (/ container-size 2)
|
||||
:background-color
|
||||
(colors/theme-colors (colors/custom-color customization-color 50)
|
||||
(colors/custom-color customization-color 60)
|
||||
theme)})
|
||||
{:width container-size
|
||||
:height container-size
|
||||
:align-items :center
|
||||
:justify-content :center
|
||||
:border-radius (/ container-size 2)
|
||||
:overflow :hidden
|
||||
:background-color (colors/theme-colors (colors/custom-color customization-color 50)
|
||||
(colors/custom-color customization-color 60)
|
||||
theme)})
|
||||
|
||||
@@ -1,31 +1,32 @@
|
||||
(ns quo2.components.avatars.group-avatar.view
|
||||
(:require [quo2.components.icon :as icon]
|
||||
[quo2.theme :as theme]
|
||||
[quo2.theme :as quo.theme]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[react-native.core :as rn]
|
||||
[react-native.fast-image :as fast-image]
|
||||
[quo2.components.avatars.group-avatar.style :as style]))
|
||||
|
||||
(def sizes
|
||||
{:icon {:x-small 12
|
||||
:small 16
|
||||
:medium 16
|
||||
:large 20
|
||||
:x-large 32}
|
||||
:container {:x-small 20
|
||||
:small 28
|
||||
:medium 32
|
||||
:large 48
|
||||
:x-large 80}})
|
||||
{:size/s-20 {:icon 12
|
||||
:container 20}
|
||||
:size/s-28 {:icon 16
|
||||
:container 28}
|
||||
:size/s-32 {:icon 16
|
||||
:container 32}
|
||||
:size/s-48 {:icon 20
|
||||
:container 48}
|
||||
:size/s-80 {:icon 32
|
||||
:container 80}})
|
||||
|
||||
(defn- view-internal
|
||||
[_]
|
||||
(fn [{:keys [size theme customization-color picture]
|
||||
:or {size :x-small
|
||||
(fn [{:keys [size theme customization-color picture icon-name]
|
||||
:or {size :size/s-20
|
||||
customization-color :blue
|
||||
picture nil}}]
|
||||
(let [container-size (get-in sizes [:container size])
|
||||
icon-size (get-in sizes [:icon size])]
|
||||
picture nil
|
||||
icon-name :i/group}}]
|
||||
(let [container-size (get-in sizes [size :container])
|
||||
icon-size (get-in sizes [size :icon])]
|
||||
[rn/view
|
||||
{:style (style/container {:container-size container-size
|
||||
:customization-color customization-color
|
||||
@@ -35,8 +36,8 @@
|
||||
{:source picture
|
||||
:style {:width container-size
|
||||
:height container-size}}]
|
||||
[icon/icon :i/members
|
||||
[icon/icon icon-name
|
||||
{:size icon-size
|
||||
:color colors/white-opa-70}])])))
|
||||
|
||||
(def view (theme/with-theme view-internal))
|
||||
(def view (quo.theme/with-theme view-internal))
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
(ns quo2.components.avatars.icon-avatar
|
||||
(:require [quo2.components.icon :as icons]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.theme :as quo.theme]
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(def sizes
|
||||
@@ -8,8 +9,8 @@
|
||||
:medium 32
|
||||
:small 20})
|
||||
|
||||
(defn icon-avatar
|
||||
[{:keys [size icon color opacity]
|
||||
(defn icon-avatar-internal
|
||||
[{:keys [size icon color opacity border? theme]
|
||||
:or {opacity 20}}]
|
||||
(let [component-size (size sizes)
|
||||
circle-color (colors/custom-color color 50 opacity)
|
||||
@@ -22,9 +23,13 @@
|
||||
{:style {:width component-size
|
||||
:height component-size
|
||||
:border-radius component-size
|
||||
:border-width (when border? 1)
|
||||
:border-color (colors/theme-colors colors/neutral-20 colors/neutral-80 theme)
|
||||
:background-color circle-color
|
||||
:justify-content :center
|
||||
:align-items :center}}
|
||||
[icons/icon icon
|
||||
{:size icon-size
|
||||
:color icon-color}]]))
|
||||
|
||||
(def icon-avatar (quo.theme/with-theme icon-avatar-internal))
|
||||
|
||||
@@ -2,14 +2,9 @@
|
||||
(:require [quo2.components.avatars.user-avatar.view :as user-avatar]
|
||||
[test-helpers.component :as h]))
|
||||
|
||||
(defonce mock-picture (js/require "../resources/images/mock2/user_picture_male4.png"))
|
||||
(defonce mock-picture {:uri (js/require "../resources/images/mock2/user_picture_male4.png")})
|
||||
|
||||
(h/describe "user avatar"
|
||||
(h/test "Default render"
|
||||
(h/render [user-avatar/user-avatar])
|
||||
(h/is-truthy (h/get-by-label-text :user-avatar))
|
||||
(h/is-truthy (h/get-by-text "EN")))
|
||||
|
||||
(h/describe "Profile picture"
|
||||
(h/test "Renders"
|
||||
(h/render
|
||||
@@ -18,124 +13,5 @@
|
||||
|
||||
(h/test "Renders even if `:full-name` is passed"
|
||||
(h/render
|
||||
[user-avatar/user-avatar
|
||||
{:profile-picture mock-picture
|
||||
:full-name "New User1"}])
|
||||
(h/is-truthy (h/get-by-label-text :profile-picture))
|
||||
(h/is-null (h/query-by-label-text :initials-avatar)))
|
||||
|
||||
(h/describe "Status indicator"
|
||||
(h/test "Render"
|
||||
(h/render
|
||||
[user-avatar/user-avatar
|
||||
{:profile-picture mock-picture
|
||||
:status-indicator? true}])
|
||||
(h/is-truthy (h/get-by-label-text :profile-picture))
|
||||
(h/is-truthy (h/get-by-label-text :status-indicator)))
|
||||
|
||||
(h/test "Do not render"
|
||||
(h/render
|
||||
[user-avatar/user-avatar
|
||||
{:profile-picture mock-picture
|
||||
:status-indicator? false}])
|
||||
(h/is-truthy (h/get-by-label-text :profile-picture))
|
||||
(h/is-null (h/query-by-label-text :status-indicator)))))
|
||||
|
||||
(h/describe "Initials Avatar"
|
||||
(h/describe "Render initials"
|
||||
(letfn [(user-avatar-component [size]
|
||||
[user-avatar/user-avatar
|
||||
{:full-name "New User"
|
||||
:size size}])]
|
||||
(h/describe "Two letters"
|
||||
(h/test "Size :big"
|
||||
(h/render (user-avatar-component :big))
|
||||
(h/is-truthy (h/get-by-text "NU")))
|
||||
|
||||
(h/test "Size :medium"
|
||||
(h/render (user-avatar-component :medium))
|
||||
(h/is-truthy (h/get-by-text "NU")))
|
||||
|
||||
(h/test "Size :small"
|
||||
(h/render (user-avatar-component :small))
|
||||
(h/is-truthy (h/get-by-text "NU")))
|
||||
|
||||
(h/test "Two letters with excess whitespace"
|
||||
(h/render [user-avatar/user-avatar
|
||||
{:full-name "New User"
|
||||
:size :big}])
|
||||
(h/is-truthy (h/get-by-text "NU")))
|
||||
|
||||
(h/test "Two letters with leading whitespace"
|
||||
(h/render [user-avatar/user-avatar
|
||||
{:full-name " New User"
|
||||
:size :big}])
|
||||
(h/is-truthy (h/get-by-text "NU"))))
|
||||
|
||||
(h/describe "One letter"
|
||||
(h/test "Size :xs"
|
||||
(h/render (user-avatar-component :xs))
|
||||
(h/is-truthy (h/get-by-text "N")))
|
||||
|
||||
(h/test "Size :xxs"
|
||||
(h/render (user-avatar-component :xxs))
|
||||
(h/is-truthy (h/get-by-text "N")))
|
||||
|
||||
(h/test "Size :xxxs"
|
||||
(h/render (user-avatar-component :xxxs))
|
||||
(h/is-truthy (h/get-by-text "N"))))))
|
||||
|
||||
(h/describe "Render ring"
|
||||
(letfn [(user-avatar-component [size]
|
||||
[user-avatar/user-avatar
|
||||
{:full-name "New User"
|
||||
:ring-background mock-picture
|
||||
:size size}])]
|
||||
(h/describe "Passed and drawn"
|
||||
(h/test "Size :big"
|
||||
(h/render (user-avatar-component :big))
|
||||
(h/is-truthy (h/get-by-label-text :initials-avatar))
|
||||
(h/is-truthy (h/get-by-label-text :ring-background)))
|
||||
|
||||
(h/test "Size :medium"
|
||||
(h/render (user-avatar-component :medium))
|
||||
(h/is-truthy (h/get-by-label-text :initials-avatar))
|
||||
(h/is-truthy (h/get-by-label-text :ring-background)))
|
||||
|
||||
(h/test "Size :small"
|
||||
(h/render (user-avatar-component :small))
|
||||
(h/is-truthy (h/get-by-label-text :initials-avatar))
|
||||
(h/is-truthy (h/get-by-label-text :ring-background))))
|
||||
|
||||
(h/describe "Passed and not drawn (because of invalid size for ring)"
|
||||
(h/test "Size :xs"
|
||||
(h/render (user-avatar-component :xs))
|
||||
(h/is-truthy (h/get-by-label-text :initials-avatar))
|
||||
(h/is-null (h/query-by-label-text :ring-background)))
|
||||
|
||||
(h/test "Size :xxs"
|
||||
(h/render (user-avatar-component :xxs))
|
||||
(h/is-truthy (h/get-by-label-text :initials-avatar))
|
||||
(h/is-null (h/query-by-label-text :ring-background)))
|
||||
|
||||
(h/test "Size :xxxs"
|
||||
(h/render (user-avatar-component :xxxs))
|
||||
(h/is-truthy (h/get-by-label-text :initials-avatar))
|
||||
(h/is-null (h/query-by-label-text :ring-background))))))
|
||||
|
||||
(h/describe "Status indicator"
|
||||
(h/test "Render"
|
||||
(h/render
|
||||
[user-avatar/user-avatar
|
||||
{:full-name "Test User"
|
||||
:status-indicator? true}])
|
||||
(h/is-truthy (h/get-by-label-text :initials-avatar))
|
||||
(h/is-truthy (h/get-by-label-text :status-indicator)))
|
||||
|
||||
(h/test "Do not render"
|
||||
(h/render
|
||||
[user-avatar/user-avatar
|
||||
{:full-name "Test User"
|
||||
:status-indicator? false}])
|
||||
(h/is-truthy (h/get-by-label-text :initials-avatar))
|
||||
(h/is-null (h/query-by-label-text :status-indicator))))))
|
||||
[user-avatar/user-avatar {:profile-picture mock-picture}])
|
||||
(h/is-truthy (h/get-by-label-text :profile-picture)))))
|
||||
|
||||
@@ -2,92 +2,65 @@
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
|
||||
(def sizes
|
||||
{:big {:outer 80
|
||||
:inner 72
|
||||
{:big {:dimensions 80
|
||||
:status-indicator 20
|
||||
:status-indicator-border 4
|
||||
:font-size :heading-1}
|
||||
:medium {:outer 48
|
||||
:inner 44
|
||||
:medium {:dimensions 48
|
||||
:status-indicator 12
|
||||
:status-indicator-border 2
|
||||
:font-size :heading-2}
|
||||
:small {:outer 32
|
||||
:inner 28
|
||||
:small {:dimensions 32
|
||||
:status-indicator 12
|
||||
:status-indicator-border 2
|
||||
:font-size :paragraph-2}
|
||||
:xs {:outer 24
|
||||
:inner 24
|
||||
28 {:dimensions 28
|
||||
:status-indicator 0
|
||||
:status-indicator-border 0
|
||||
:font-size :paragraph-2}
|
||||
:xxs {:outer 20
|
||||
:inner 20
|
||||
:xs {:dimensions 24
|
||||
:status-indicator 0
|
||||
:status-indicator-border 0
|
||||
:font-size :paragraph-2}
|
||||
:xxs {:dimensions 20
|
||||
:status-indicator 0
|
||||
:status-indicator-border 0
|
||||
:font-size :label}
|
||||
:xxxs {:outer 16
|
||||
:inner 16
|
||||
:xxxs {:dimensions 16
|
||||
:status-indicator 0
|
||||
:status-indicator-border 0
|
||||
:font-size :label}})
|
||||
|
||||
(defn outer
|
||||
[size]
|
||||
(let [dimensions (get-in sizes [size :outer])]
|
||||
{:width dimensions
|
||||
:height dimensions
|
||||
:border-radius dimensions}))
|
||||
|
||||
(defn initials-avatar
|
||||
[size draw-ring? customization-color theme]
|
||||
(let [outer-dimensions (get-in sizes [size :outer])
|
||||
inner-dimensions (get-in sizes [size (if draw-ring? :inner :outer)])]
|
||||
[size customization-color theme]
|
||||
(let [dimensions (get-in sizes [size :dimensions])]
|
||||
{:position :absolute
|
||||
:top (/ (- outer-dimensions inner-dimensions) 2)
|
||||
:left (/ (- outer-dimensions inner-dimensions) 2)
|
||||
:width inner-dimensions
|
||||
:height inner-dimensions
|
||||
:border-radius inner-dimensions
|
||||
:top 0
|
||||
:left 0
|
||||
:width dimensions
|
||||
:height dimensions
|
||||
:border-radius dimensions
|
||||
:justify-content :center
|
||||
:align-items :center
|
||||
:background-color (colors/custom-color-by-theme customization-color 50 60 nil nil theme)}))
|
||||
|
||||
(def indicator-color
|
||||
{:online colors/success-50
|
||||
:offline colors/neutral-40})
|
||||
|
||||
(defn outer
|
||||
[size static-profile-picture?]
|
||||
(let [dimensions (get-in sizes [size :dimensions])
|
||||
outer-style {:width dimensions
|
||||
:height dimensions}
|
||||
outer-style-with-border-radius (assoc outer-style :border-radius dimensions)]
|
||||
(if static-profile-picture?
|
||||
outer-style-with-border-radius
|
||||
outer-style)))
|
||||
|
||||
(defn customization-color
|
||||
[color-id theme]
|
||||
(colors/custom-color-by-theme color-id 50 60 nil nil theme))
|
||||
|
||||
(def initials-avatar-text
|
||||
{:color colors/white-opa-70})
|
||||
|
||||
(defn inner-dot
|
||||
[size online?]
|
||||
(let [background (if online? colors/success-50 colors/neutral-40)
|
||||
dimensions (get-in sizes [size :status-indicator])]
|
||||
{:width (- dimensions 4)
|
||||
:height (- dimensions 4)
|
||||
:border-radius (- dimensions 4)
|
||||
:background-color background}))
|
||||
|
||||
(defn dot
|
||||
[size ring? theme]
|
||||
(let [dimensions (get-in sizes [size :status-indicator])
|
||||
border-width (get-in sizes [size :status-indicator-border])
|
||||
right (case size
|
||||
:big 2
|
||||
:medium 0
|
||||
:small -2
|
||||
0)
|
||||
bottom (case size
|
||||
:big (if ring? -1 2)
|
||||
:medium (if ring? 0 -2)
|
||||
:small -2
|
||||
0)]
|
||||
{:position :absolute
|
||||
:justify-content :center
|
||||
:align-items :center
|
||||
:bottom bottom
|
||||
:right right
|
||||
:width dimensions
|
||||
:height dimensions
|
||||
:border-width border-width
|
||||
:border-radius dimensions
|
||||
:background-color (colors/theme-colors colors/white colors/neutral-100 theme)
|
||||
:border-color (colors/theme-colors colors/white colors/neutral-100 theme)}))
|
||||
|
||||
@@ -1,64 +1,108 @@
|
||||
(ns quo2.components.avatars.user-avatar.view
|
||||
(:require [quo2.components.avatars.user-avatar.style :as style]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.theme :as quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[react-native.fast-image :as fast-image]
|
||||
utils.string))
|
||||
(:require
|
||||
[quo2.components.avatars.user-avatar.style :as style]
|
||||
[quo2.components.common.no-flicker-image :as no-flicker-image]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.theme]
|
||||
[react-native.core :as rn]
|
||||
[react-native.fast-image :as fast-image]
|
||||
utils.string))
|
||||
|
||||
(defn initials-avatar
|
||||
[{:keys [full-name size draw-ring? customization-color theme]}]
|
||||
[{:keys [full-name size customization-color theme]}]
|
||||
(let [font-size (get-in style/sizes [size :font-size])
|
||||
amount-initials (if (#{:xs :xxs :xxxs} size) 1 2)]
|
||||
[rn/view
|
||||
{:accessibility-label :initials-avatar
|
||||
:style (style/initials-avatar size draw-ring? customization-color theme)}
|
||||
:style (style/initials-avatar size customization-color theme)}
|
||||
[text/text
|
||||
{:style style/initials-avatar-text
|
||||
:size font-size
|
||||
:weight :semi-bold}
|
||||
(utils.string/get-initials full-name amount-initials)]]))
|
||||
|
||||
(def valid-ring-sizes #{:big :medium :small})
|
||||
(defn user-avatar-internal
|
||||
"Render user avatar with `profile-picture`
|
||||
`profile-picture` should be one of {:uri profile-picture-uri} or {:fn profile-picture-fn}
|
||||
|
||||
(defn- user-avatar-internal
|
||||
"If no `profile-picture` is given, draws the initials based on the `full-name` and
|
||||
uses `ring-background` to display the ring behind the initials when given. Otherwise,
|
||||
shows the `profile-picture` which already comes with the ring drawn."
|
||||
[{:keys [full-name status-indicator? online? size profile-picture ring-background
|
||||
customization-color static? muted? theme ring?]
|
||||
:or {status-indicator? true
|
||||
`profile-picture-fn` should return an image URI, there's helper fn to generate
|
||||
it in `utils.image-server`
|
||||
|
||||
params for `profile-picture-fn`
|
||||
{:length initials' length
|
||||
:full-name used to generate initials
|
||||
:font-size initials font size
|
||||
:indicator-size status indicator outer radius, set to nil or 0 when no indicator
|
||||
:indicator-border `indicator-size`-`indicator-border` is the inner radius
|
||||
:indicator-color color for status indicator
|
||||
:override-theme override theme for ring
|
||||
:background-color intials avatar background color
|
||||
:color intials avatar text color
|
||||
:size intials avatar radius
|
||||
:ring? render ident ring around avatar?}
|
||||
|
||||
supported color formats:
|
||||
#RRGGBB
|
||||
#RRGGBBAA
|
||||
rgb(255,255,255)
|
||||
rgba(255,255,255,0.1) note alpha is 0-1
|
||||
|
||||
the reason we use the `profile-picture-fn` here is to separate
|
||||
logic (pubkey, key-uid... in subs) and style (color, size... in this component)"
|
||||
[{:keys [full-name size profile-picture customization-color static?
|
||||
status-indicator? online? ring? theme]
|
||||
:or {size :big
|
||||
status-indicator? true
|
||||
online? true
|
||||
ring? true
|
||||
size :big
|
||||
customization-color :turquoise}}]
|
||||
(let [full-name (or full-name "empty name")
|
||||
draw-ring? (and ring? (when-not muted? (and ring-background (valid-ring-sizes size))))
|
||||
outer-styles (style/outer size)
|
||||
;; Once image is loaded, fast image rerenders view with the help of reagent atom,
|
||||
customization-color :turquoise}
|
||||
:as props}]
|
||||
(let [full-name (or full-name "Your Name")
|
||||
;; image generated with profile-picture-fn is round cropped
|
||||
;; no need to add border-radius for them
|
||||
outer-styles (style/outer size (not (:fn profile-picture)))
|
||||
;; Once image is loaded, fast image re-renders view with the help of reagent atom,
|
||||
;; But dynamic updates don't work when user-avatar is used inside hole-view
|
||||
;; https://github.com/status-im/status-mobile/issues/15553
|
||||
image-view (if static? rn/image fast-image/fast-image)]
|
||||
[rn/view {:style outer-styles :accessibility-label :user-avatar}
|
||||
;; The `profile-picture` already has the ring in it
|
||||
(when-let [image (or profile-picture ring-background)]
|
||||
[image-view
|
||||
{:accessibility-label (if draw-ring? :ring-background :profile-picture)
|
||||
:style outer-styles
|
||||
:source image}])
|
||||
(when-not profile-picture
|
||||
[initials-avatar
|
||||
{:full-name full-name
|
||||
:size size
|
||||
:draw-ring? draw-ring?
|
||||
:customization-color customization-color
|
||||
:theme theme}])
|
||||
(when status-indicator?
|
||||
[rn/view
|
||||
{:accessibility-label :status-indicator
|
||||
:style (style/dot size draw-ring? theme)}
|
||||
[rn/view
|
||||
{:accessibility-label :inner-status-indicator-dot
|
||||
:style (style/inner-dot size online?)}]])]))
|
||||
image-view (if static? no-flicker-image/image fast-image/fast-image)
|
||||
font-size (get-in style/sizes [size :font-size])
|
||||
amount-initials (if (#{:xs :xxs :xxxs} size) 1 2)
|
||||
sizes (get style/sizes size)
|
||||
indicator-color (get style/indicator-color (if online? :online :offline))
|
||||
profile-picture-fn (:fn profile-picture)]
|
||||
|
||||
(def user-avatar (quo.theme/with-theme user-avatar-internal))
|
||||
[rn/view {:style outer-styles :accessibility-label :user-avatar}
|
||||
(if (and full-name (not (or profile-picture-fn profile-picture)))
|
||||
;; this is for things that's not user-avatar
|
||||
;; but are currently using user-avatar to render the initials
|
||||
;; e.g. community avatar
|
||||
[initials-avatar props]
|
||||
[image-view
|
||||
{:accessibility-label :profile-picture
|
||||
:style outer-styles
|
||||
:source
|
||||
(cond profile-picture-fn
|
||||
{:uri (profile-picture-fn
|
||||
{:length amount-initials
|
||||
:full-name full-name
|
||||
:font-size (:font-size (text/text-style {:size
|
||||
font-size}))
|
||||
:indicator-size (when status-indicator?
|
||||
(:status-indicator sizes))
|
||||
:indicator-border (when status-indicator?
|
||||
(:status-indicator-border sizes))
|
||||
:indicator-color indicator-color
|
||||
:override-theme theme
|
||||
:background-color (style/customization-color customization-color theme)
|
||||
:color (:color style/initials-avatar-text)
|
||||
:size (:width outer-styles)
|
||||
:ring? ring?})}
|
||||
(:uri profile-picture)
|
||||
profile-picture
|
||||
|
||||
(number? profile-picture)
|
||||
profile-picture
|
||||
|
||||
:else {:uri profile-picture})}])]))
|
||||
|
||||
(def user-avatar (quo2.theme/with-theme user-avatar-internal))
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
:icon-top :icon-keyword
|
||||
:icon-left :icon-keyword
|
||||
:icon-right :icon-keyword}
|
||||
:customization-color keyword or hexstring
|
||||
:customization-color keyword or hexstring
|
||||
:theme :light/:dark
|
||||
only icon
|
||||
[button {:icon-only? true} :i/close-circle]"
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
:active-opacity 1
|
||||
:hit-slop {:top 5 :bottom 5 :left 5 :right 5}
|
||||
:pointer-events :auto
|
||||
:style {:height 24}
|
||||
:accessibility-label type}
|
||||
[rn/view
|
||||
{:style (merge
|
||||
|
||||
@@ -26,7 +26,8 @@
|
||||
track-icon
|
||||
disabled?
|
||||
customization-color
|
||||
size]}]
|
||||
size
|
||||
container-style]}]
|
||||
(let [x-pos (reanimated/use-shared-value 0)
|
||||
dimensions (partial utils/get-dimensions
|
||||
(or @track-width constants/default-width)
|
||||
@@ -49,7 +50,8 @@
|
||||
sliding-complete?)}
|
||||
[reanimated/view
|
||||
{:test-ID :slide-button-track
|
||||
:style (style/track disabled? customization-color (dimensions :track-height))
|
||||
:style (merge (style/track disabled? customization-color (dimensions :track-height))
|
||||
container-style)
|
||||
:on-layout (when-not (some? @track-width)
|
||||
on-track-layout)}
|
||||
[reanimated/view {:style (style/track-cover interpolate-track)}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
(ns quo2.components.buttons.wallet-button.component-spec
|
||||
(:require [quo2.components.buttons.wallet-button.view :as wallet-button]
|
||||
[test-helpers.component :as h]))
|
||||
|
||||
(h/describe "button tests"
|
||||
(h/test "default render of wallet button component"
|
||||
(h/render [wallet-button/view
|
||||
{:icon :i/placeholder
|
||||
:accessibility-label "test-button"}])
|
||||
(h/is-truthy (h/get-by-label-text "test-button")))
|
||||
|
||||
(h/test "button on-press works"
|
||||
(let [event (h/mock-fn)]
|
||||
(h/render [wallet-button/view
|
||||
{:icon :i/placeholder
|
||||
:on-press event
|
||||
:accessibility-label "test-button"}])
|
||||
(h/fire-event :press (h/get-by-label-text "test-button"))
|
||||
(h/was-called event)))
|
||||
|
||||
(h/test "button on-press disabled when disabled"
|
||||
(let [event (h/mock-fn)]
|
||||
(h/render [wallet-button/view
|
||||
{:icon :i/placeholder
|
||||
:on-press event
|
||||
:accessibility-label "test-button"
|
||||
:disabled? true}])
|
||||
(h/fire-event :press (h/get-by-label-text "test-button"))
|
||||
(h/was-not-called event)))
|
||||
|
||||
(h/test "button on-long-press works"
|
||||
(let [event (h/mock-fn)]
|
||||
(h/render [wallet-button/view
|
||||
{:icon :i/placeholder
|
||||
:on-long-press event
|
||||
:accessibility-label "test-button"}])
|
||||
(h/fire-event :long-press (h/get-by-label-text "test-button"))
|
||||
(h/was-called event))))
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
(ns quo2.components.buttons.wallet-button.style
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
|
||||
(defn get-border-color
|
||||
[{:keys [pressed? theme]}]
|
||||
(if (= pressed? true)
|
||||
(colors/theme-colors colors/neutral-40 colors/neutral-60 theme)
|
||||
(colors/theme-colors colors/neutral-30 colors/neutral-70 theme)))
|
||||
|
||||
(defn main
|
||||
[{:keys [pressed? theme disabled?]}]
|
||||
{:border-color (get-border-color {:pressed? pressed?
|
||||
:theme theme})
|
||||
:border-width 1
|
||||
:border-radius 10
|
||||
:width 32
|
||||
:height 32
|
||||
:justify-content :center
|
||||
:align-items :center
|
||||
:opacity (when disabled? 0.3)})
|
||||
@@ -0,0 +1,29 @@
|
||||
(ns quo2.components.buttons.wallet-button.view
|
||||
(:require
|
||||
[quo2.components.buttons.wallet-button.style :as style]
|
||||
[quo2.components.icon :as quo2.icons]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.theme :as theme]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
(defn- view-internal
|
||||
[]
|
||||
(let [pressed? (reagent/atom false)]
|
||||
(fn
|
||||
[{:keys [on-press on-long-press disabled? icon accessibility-label container-style theme]}]
|
||||
[rn/pressable
|
||||
{:accessibility-label (or accessibility-label :wallet-button)
|
||||
:on-press on-press
|
||||
:on-press-in #(reset! pressed? true)
|
||||
:on-press-out #(reset! pressed? nil)
|
||||
:on-long-press on-long-press
|
||||
:disabled disabled?
|
||||
:style (merge (style/main {:pressed? @pressed?
|
||||
:theme theme
|
||||
:disabled? disabled?})
|
||||
container-style)}
|
||||
[quo2.icons/icon icon
|
||||
{:color (colors/theme-colors colors/neutral-100 colors/white theme)}]])))
|
||||
|
||||
(def view (theme/with-theme view-internal))
|
||||
@@ -0,0 +1,11 @@
|
||||
(ns quo2.components.buttons.wallet-ctas.component-spec
|
||||
(:require [test-helpers.component :as h]
|
||||
[quo2.components.buttons.wallet-ctas.view :as wallet-ctas]))
|
||||
|
||||
(h/describe "Wallet CTAs test"
|
||||
(h/test "Buttons render"
|
||||
(h/render [wallet-ctas/view])
|
||||
(h/is-truthy (h/get-by-label-text :buy))
|
||||
(h/is-truthy (h/get-by-label-text :send))
|
||||
(h/is-truthy (h/get-by-label-text :receive))
|
||||
(h/is-truthy (h/get-by-label-text :bridge))))
|
||||
@@ -0,0 +1,16 @@
|
||||
(ns quo2.components.buttons.wallet-ctas.style)
|
||||
|
||||
(def container
|
||||
{:padding-top 24
|
||||
:padding-bottom 12
|
||||
:padding-horizontal 20
|
||||
:flex-direction :row
|
||||
:justify-content :center
|
||||
:flex 1
|
||||
:max-height 106})
|
||||
|
||||
(def button-container
|
||||
{:padding-vertical 8
|
||||
:width 77.75
|
||||
:justify-content :center
|
||||
:align-items :center})
|
||||
@@ -0,0 +1,55 @@
|
||||
(ns quo2.components.buttons.wallet-ctas.view
|
||||
(:require
|
||||
[quo2.components.buttons.wallet-button.view :as wallet-button]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.theme :as quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[utils.i18n :as i18n]
|
||||
[quo2.components.buttons.wallet-ctas.style :as style]))
|
||||
|
||||
|
||||
(defn action-button
|
||||
[{:keys [icon text on-press theme accessibility-label]}]
|
||||
[rn/view
|
||||
{:style style/button-container
|
||||
:accessibility-label accessibility-label}
|
||||
[wallet-button/view
|
||||
{:icon icon
|
||||
:on-press on-press
|
||||
:on-long-press #(js/alert "long pressed")}]
|
||||
[text/text
|
||||
{:weight :medium
|
||||
:size :paragraph-2
|
||||
:style {:margin-top 4
|
||||
:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)}} text]])
|
||||
|
||||
(defn view-internal
|
||||
[{:keys [theme buy-action send-action receive-action bridge-action]}]
|
||||
[rn/view {:style style/container}
|
||||
[action-button
|
||||
{:icon :i/add
|
||||
:text (i18n/label :t/buy)
|
||||
:on-press buy-action
|
||||
:theme theme
|
||||
:accessibility-label :buy}]
|
||||
[action-button
|
||||
{:icon :i/send
|
||||
:text (i18n/label :t/send)
|
||||
:on-press send-action
|
||||
:theme theme
|
||||
:accessibility-label :send}]
|
||||
[action-button
|
||||
{:icon :i/receive
|
||||
:text (i18n/label :t/receive)
|
||||
:on-press receive-action
|
||||
:theme theme
|
||||
:accessibility-label :receive}]
|
||||
[action-button
|
||||
{:icon :i/bridge
|
||||
:text (i18n/label :t/bridge)
|
||||
:on-press bridge-action
|
||||
:theme theme
|
||||
:accessibility-label :bridge}]])
|
||||
|
||||
(def view (quo.theme/with-theme view-internal))
|
||||
@@ -2,8 +2,7 @@
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
|
||||
(def color-picker-container
|
||||
{:flex 1
|
||||
:flex-direction :row
|
||||
{:flex-direction :row
|
||||
:flex-wrap :wrap
|
||||
:justify-content :space-between})
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
(ns quo2.components.common.no-flicker-image
|
||||
(:require
|
||||
[oops.core :as oops]
|
||||
[react-native.core :as rn]
|
||||
[react-native.platform :as platform]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
|
||||
(def cached-sources (js/Set. (js/Array.)))
|
||||
|
||||
(defn- caching-image
|
||||
[_ on-source-loaded]
|
||||
(let [this (reagent/current-component)]
|
||||
(reagent/create-class
|
||||
{:component-did-update
|
||||
(fn []
|
||||
(let [source (-> this reagent/props :source)]
|
||||
(when (oops/ocall cached-sources "has" source)
|
||||
(on-source-loaded source))))
|
||||
:reagent-render
|
||||
(fn [{:keys [source] :as props}]
|
||||
[rn/image
|
||||
(assoc props
|
||||
;; hide the cache image under the real one
|
||||
;; have to render it for the on-load event
|
||||
:style {:width 1
|
||||
:height 1
|
||||
:left "50%"
|
||||
:top "50%"
|
||||
:position :absolute}
|
||||
:on-load
|
||||
(fn [_]
|
||||
(when-not (oops/ocall cached-sources "has" source)
|
||||
(oops/ocall cached-sources "add" source)
|
||||
(on-source-loaded source)))
|
||||
:on-error js/console.error)])})))
|
||||
|
||||
(defn image
|
||||
"Same as rn/image but cache the image source in a js/Set, so the image won't
|
||||
flicker when re-render on android"
|
||||
[]
|
||||
(let [loaded-source (reagent/atom nil)
|
||||
on-source-loaded #(reset! loaded-source %)]
|
||||
(fn [props]
|
||||
(if platform/ios?
|
||||
[rn/image props]
|
||||
[:<>
|
||||
[rn/image (assoc props :source @loaded-source)]
|
||||
[caching-image props on-source-loaded]]))))
|
||||
@@ -0,0 +1,12 @@
|
||||
(ns quo2.components.common.not-implemented.style
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
|
||||
(defn text
|
||||
[blur? theme]
|
||||
{:border-color :red
|
||||
:border-width 1
|
||||
:color (if blur?
|
||||
colors/white
|
||||
(colors/theme-colors colors/neutral-100
|
||||
colors/white
|
||||
theme))})
|
||||
@@ -0,0 +1,11 @@
|
||||
(ns quo2.components.common.not-implemented.view
|
||||
(:require [react-native.core :as rn]
|
||||
[quo2.theme :as quo.theme]
|
||||
[quo2.components.common.not-implemented.style :as style]))
|
||||
|
||||
(defn- view-internal
|
||||
[{:keys [blur? theme]}]
|
||||
[rn/text {:style (style/text blur? theme)}
|
||||
"not implemented"])
|
||||
|
||||
(def view (quo.theme/with-theme view-internal))
|
||||
@@ -1,13 +1,11 @@
|
||||
(ns quo2.components.community.banner.style
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
(:require [quo2.foundations.colors :as colors]
|
||||
[quo2.foundations.shadows :as shadows]))
|
||||
|
||||
(defn community-card
|
||||
[theme]
|
||||
{:shadow-offset {:width 0 :height 2}
|
||||
:shadow-radius 16
|
||||
:shadow-opacity 1
|
||||
:shadow-color colors/shadow
|
||||
:elevation 1
|
||||
(assoc
|
||||
(shadows/get 2 theme)
|
||||
:border-radius 16
|
||||
:justify-content :space-between
|
||||
:background-color (colors/theme-colors colors/white colors/neutral-90 theme)
|
||||
@@ -16,7 +14,7 @@
|
||||
:margin-top 8
|
||||
:margin-bottom 8
|
||||
:height 56
|
||||
:padding-right 12})
|
||||
:padding-right 12))
|
||||
|
||||
(def banner-content
|
||||
{:flex 1
|
||||
|
||||
@@ -16,25 +16,26 @@
|
||||
unread-messages?
|
||||
unread-mentions-count
|
||||
customization-color]}]
|
||||
(cond
|
||||
muted?
|
||||
[icons/icon :i/muted
|
||||
{:container-style {:align-items :center
|
||||
:justify-content :center}
|
||||
:resize-mode :center
|
||||
:size 20
|
||||
:color (colors/theme-colors
|
||||
colors/neutral-50
|
||||
colors/neutral-40
|
||||
theme)}]
|
||||
(pos? unread-mentions-count)
|
||||
[counter/view
|
||||
{:customization-color customization-color
|
||||
:type :default}
|
||||
unread-mentions-count]
|
||||
[rn/view {:style style/notification-container}
|
||||
(cond
|
||||
muted?
|
||||
[icons/icon :i/muted
|
||||
{:container-style {:align-items :center
|
||||
:justify-content :center}
|
||||
:resize-mode :center
|
||||
:size 20
|
||||
:color (colors/theme-colors
|
||||
colors/neutral-50
|
||||
colors/neutral-40
|
||||
theme)}]
|
||||
(pos? unread-mentions-count)
|
||||
[counter/view
|
||||
{:customization-color customization-color
|
||||
:type :default}
|
||||
unread-mentions-count]
|
||||
|
||||
unread-messages?
|
||||
[unread-grey-dot :unviewed-messages-public]))
|
||||
unread-messages?
|
||||
[unread-grey-dot :unviewed-messages-public])])
|
||||
|
||||
(defn- communities-list-view-item-internal
|
||||
[{:keys [theme customization-color] :as props}
|
||||
|
||||
@@ -4,18 +4,14 @@
|
||||
|
||||
(defn community-card
|
||||
[radius theme]
|
||||
{:shadow-offset {:width 0
|
||||
:height 2}
|
||||
:shadow-radius radius
|
||||
:shadow-opacity 1
|
||||
:shadow-color colors/shadow
|
||||
:elevation 1
|
||||
(assoc
|
||||
(shadows/get 2 theme)
|
||||
:border-radius radius
|
||||
:justify-content :space-between
|
||||
:background-color (colors/theme-colors
|
||||
colors/white
|
||||
colors/neutral-90
|
||||
theme)})
|
||||
theme)))
|
||||
|
||||
(def detail-container
|
||||
{:flex 1})
|
||||
@@ -252,3 +248,8 @@
|
||||
:margin-top margin-top})
|
||||
|
||||
(def loading-card-content-container {:margin-top 36})
|
||||
|
||||
(def notification-container
|
||||
{:width 20
|
||||
:display :flex
|
||||
:align-items :center})
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
(ns quo2.components.dividers.divider-label
|
||||
(:require [quo2.components.icon :as icons]
|
||||
[quo2.components.markdown.text :as markdown.text]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.theme :as theme]
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(def chevron-icon-container-width 20)
|
||||
|
||||
(def chevron-icon-container-height 20)
|
||||
|
||||
(defn themed-view
|
||||
"label -> string
|
||||
chevron-position -> :left, :right
|
||||
chevron-icon -> keyword
|
||||
on-press -> function
|
||||
padding-bottom -> number
|
||||
counter-value -> number
|
||||
increase-padding-top? -> boolean
|
||||
blur? -> boolean
|
||||
theme -> theme value passed from with-theme HOC"
|
||||
[{:keys [label
|
||||
chevron-position
|
||||
chevron-icon
|
||||
counter-value
|
||||
increase-padding-top?
|
||||
padding-bottom
|
||||
blur?
|
||||
container-style
|
||||
on-press
|
||||
theme]}]
|
||||
(let [dark? (= :dark theme)
|
||||
border-and-counter-bg-color (if dark?
|
||||
(if blur? colors/white-opa-5 colors/neutral-70)
|
||||
colors/neutral-10)
|
||||
padding-top (if increase-padding-top? 16 8)
|
||||
text-and-icon-color (if dark? colors/neutral-40 colors/neutral-50)
|
||||
counter-text-color (if dark? colors/white colors/neutral-100)]
|
||||
[rn/touchable-without-feedback {:on-press on-press}
|
||||
[rn/view
|
||||
{:accessible true
|
||||
:accessibility-label :divider-label
|
||||
:style (merge {:border-top-width 1
|
||||
:border-top-color border-and-counter-bg-color
|
||||
:padding-top padding-top
|
||||
:padding-bottom padding-bottom
|
||||
:padding-left 16
|
||||
:padding-right 16
|
||||
:align-items :center
|
||||
:flex-direction :row}
|
||||
container-style)}
|
||||
(when (= chevron-position :left)
|
||||
[rn/view
|
||||
{:test-ID :divider-label-icon-left
|
||||
:style {:margin-right 4}}
|
||||
[icons/icon
|
||||
(or chevron-icon :i/chevron-down)
|
||||
{:color text-and-icon-color
|
||||
:width chevron-icon-container-width
|
||||
:height chevron-icon-container-height}]])
|
||||
[markdown.text/text
|
||||
{:size :paragraph-2
|
||||
:weight :medium
|
||||
:style {:color text-and-icon-color
|
||||
:flex 1}}
|
||||
label]
|
||||
(when (= chevron-position :right)
|
||||
[rn/view {:test-ID :divider-label-icon-right}
|
||||
[icons/icon
|
||||
(or chevron-icon :i/chevron-down)
|
||||
{:color text-and-icon-color
|
||||
:size chevron-icon-container-width}]])
|
||||
(when (pos? counter-value)
|
||||
[rn/view
|
||||
{:style {:border-radius 6
|
||||
:height 16
|
||||
:width (case (count counter-value)
|
||||
1 16
|
||||
2 20
|
||||
28)
|
||||
:background-color border-and-counter-bg-color
|
||||
:align-items :center
|
||||
:justify-content :center}}
|
||||
[markdown.text/text
|
||||
{:size :label
|
||||
:weight :medium
|
||||
:style {:color counter-text-color}}
|
||||
counter-value]])]]))
|
||||
|
||||
(def divider-label (theme/with-theme themed-view))
|
||||
@@ -0,0 +1,20 @@
|
||||
(ns quo2.components.dividers.divider-label.component-spec
|
||||
(:require [quo2.components.dividers.divider-label.view :as divider-label]
|
||||
[test-helpers.component :as h]))
|
||||
|
||||
(h/describe "Divider Label"
|
||||
(h/test "default render"
|
||||
(h/render [divider-label/view "Welcome"])
|
||||
(h/is-truthy (h/query-by-label-text :divider-label)))
|
||||
(h/test "with label"
|
||||
(h/render [divider-label/view {:tight? true} "Welcome"])
|
||||
(h/is-truthy (h/query-by-text "Welcome")))
|
||||
(h/test "with label & counter value"
|
||||
(h/render [divider-label/view {:tight? true :counter? true :counter-value 5} "Public"])
|
||||
(h/is-truthy (h/query-by-text "Public"))
|
||||
(h/is-truthy (h/query-by-text "5")))
|
||||
(h/test "on-press event"
|
||||
(let [on-press (h/mock-fn)]
|
||||
(h/render [divider-label/view {:tight? false :on-press on-press} "General"])
|
||||
(h/fire-event :press (h/query-by-label-text :divider-label))
|
||||
(h/was-called on-press))))
|
||||
@@ -0,0 +1,39 @@
|
||||
(ns quo2.components.dividers.divider-label.style
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
|
||||
(defn- get-border-color
|
||||
[blur? theme]
|
||||
(colors/theme-colors (if blur? colors/neutral-80-opa-5 colors/neutral-10)
|
||||
(if blur? colors/white-opa-5 colors/neutral-90)
|
||||
theme))
|
||||
|
||||
(defn get-content-color
|
||||
[blur? theme]
|
||||
(colors/theme-colors (if blur? colors/neutral-80-opa-70 colors/neutral-50)
|
||||
(if blur? colors/white-opa-70 colors/neutral-40)
|
||||
theme))
|
||||
|
||||
(defn container
|
||||
[blur? tight? chevron theme]
|
||||
{:border-top-width 1
|
||||
:border-top-color (get-border-color blur? theme)
|
||||
:padding-top (if tight? 6 14)
|
||||
:padding-bottom 7
|
||||
:padding-left (if (= :left chevron) 16 20)
|
||||
:padding-right 20
|
||||
:align-items :center
|
||||
:flex-direction :row})
|
||||
|
||||
(defn content
|
||||
[chevron]
|
||||
{:flex-direction (if (= chevron :right)
|
||||
:row-reverse
|
||||
:row)
|
||||
:align-items :center
|
||||
:height 20
|
||||
:flex 1})
|
||||
|
||||
(defn text
|
||||
[blur? theme]
|
||||
{:color (get-content-color blur? theme)
|
||||
:flex 1})
|
||||
@@ -0,0 +1,60 @@
|
||||
(ns quo2.components.dividers.divider-label.view
|
||||
(:require [quo2.components.markdown.text :as text]
|
||||
[quo2.components.dividers.divider-label.style :as style]
|
||||
[quo2.theme :as quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[quo2.components.counter.counter.view :as counter]
|
||||
[quo2.components.icon :as icons]))
|
||||
|
||||
(defn- view-internal
|
||||
"Options:
|
||||
- `counter?` (default: nil) - Display a counter on the right side of the divider.
|
||||
- `counter-value` (default: nil) - Number to display if counter? is true.
|
||||
- `chevron` nil/:right/:left (default: nil) - Display a chevron on the left or right side of the divider.
|
||||
- `chevron-icon` (default: :i/chevron-right) - Icon to display when chevron is :left/:right.
|
||||
- `tight?` (default: true) - Use a tighter padding.
|
||||
- `blur?` (default: nil) - Use a blur background.
|
||||
- `theme` - Theme value from with-theme HOC.
|
||||
- `on-press` - Function called when the divider is pressed.
|
||||
- `container-style` - Style applied to the container view.
|
||||
|
||||
label - String or markdown text component to display in the divider label."
|
||||
[{:keys [counter?
|
||||
counter-value
|
||||
chevron
|
||||
chevron-icon
|
||||
tight?
|
||||
blur?
|
||||
theme
|
||||
on-press
|
||||
container-style]
|
||||
:or {tight? true}}
|
||||
label]
|
||||
[rn/pressable
|
||||
{:on-press on-press
|
||||
:accessibility-label :divider-label
|
||||
:style (merge (style/container blur? tight? chevron theme)
|
||||
container-style)}
|
||||
[rn/view
|
||||
{:style (style/content chevron)}
|
||||
(when chevron
|
||||
[icons/icon (or chevron-icon :i/chevron-right)
|
||||
{:color (style/get-content-color blur? theme)
|
||||
:container-style {(if (= chevron :right)
|
||||
:margin-left
|
||||
:margin-right)
|
||||
2}}])
|
||||
[text/text
|
||||
{:size :paragraph-2
|
||||
:weight :medium
|
||||
:style (style/text blur? theme)}
|
||||
label]]
|
||||
(when counter?
|
||||
[counter/view
|
||||
{:type (if blur? :secondary :grey)
|
||||
:container-style {:margin-left 2}}
|
||||
counter-value])])
|
||||
|
||||
(defn view
|
||||
([_ _] (quo.theme/with-theme view-internal))
|
||||
([label] [view {} label]))
|
||||
@@ -1,45 +0,0 @@
|
||||
(ns quo2.components.dividers.divider-label-component-spec
|
||||
(:require ["@testing-library/react-native" :as rtl]
|
||||
[quo2.components.dividers.divider-label :as divider-label]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
(defn render-divider-label
|
||||
([]
|
||||
(render-divider-label {}))
|
||||
([opts]
|
||||
(rtl/render (reagent/as-element [divider-label/divider-label opts]))))
|
||||
|
||||
(js/global.test "default render of divider-label component"
|
||||
(fn []
|
||||
(render-divider-label)
|
||||
(-> (js/expect (rtl/screen.getByLabelText "divider-label"))
|
||||
(.toBeTruthy))))
|
||||
|
||||
(js/global.test "render divider-label component with a label"
|
||||
(fn []
|
||||
(render-divider-label {:label :hello})
|
||||
(-> (js/expect (rtl/screen.getByText "hello"))
|
||||
(.toBeTruthy))))
|
||||
|
||||
(js/global.test "render divider-label component with a counter-value"
|
||||
(fn []
|
||||
(render-divider-label {:label :hello
|
||||
:counter-value "1"})
|
||||
(-> (js/expect (rtl/screen.getByText "1"))
|
||||
(.toBeTruthy))))
|
||||
|
||||
(js/global.test "divider-label chevron icon renders to the left when set"
|
||||
(fn []
|
||||
(render-divider-label {:label :hello
|
||||
:counter-value "1"
|
||||
:chevron-position :left})
|
||||
(-> (js/expect (rtl/screen.getByTestId "divider-label-icon-left"))
|
||||
(.toBeTruthy))))
|
||||
|
||||
(js/global.test "divider-label chevron icon renders to the right when set"
|
||||
(fn []
|
||||
(render-divider-label {:label :hello
|
||||
:counter-value "1"
|
||||
:chevron-position :right})
|
||||
(-> (js/expect (rtl/screen.getByTestId "divider-label-icon-right"))
|
||||
(.toBeTruthy))))
|
||||
@@ -23,10 +23,11 @@
|
||||
:margin-horizontal 12
|
||||
:flex-direction :row})
|
||||
|
||||
(def left-icon
|
||||
(defn left-icon
|
||||
[sub-label?]
|
||||
{:height 20
|
||||
:margin-top :auto
|
||||
:margin-bottom :auto
|
||||
:margin-top (if sub-label? 10 :auto)
|
||||
:margin-bottom (when-not sub-label? :auto)
|
||||
:margin-right 12
|
||||
:width 20})
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
[rn/view
|
||||
{:accessibility-label :left-icon-for-action
|
||||
:accessible true
|
||||
:style style/left-icon}
|
||||
:style (style/left-icon sub-label)}
|
||||
[icon/icon icon
|
||||
{:color (or icon-color (get-icon-color danger? theme))
|
||||
:size 20}]]
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
(ns quo2.components.dropdowns.network-dropdown.component-spec
|
||||
(:require [quo2.components.dropdowns.network-dropdown.view :as network-dropdown]
|
||||
[quo2.foundations.resources :as quo.resources]
|
||||
[test-helpers.component :as h]))
|
||||
|
||||
(def ^:private networks-list
|
||||
[{:source (quo.resources/get-network :ethereum)}
|
||||
{:source (quo.resources/get-network :optimism)}
|
||||
{:source (quo.resources/get-network :arbitrum)}])
|
||||
|
||||
(h/describe "Network dropdown"
|
||||
(h/test "default render"
|
||||
(h/render [network-dropdown/view {:state :default}
|
||||
networks-list])
|
||||
(h/is-truthy (h/query-by-label-text :network-dropdown)))
|
||||
|
||||
(h/test "Should call :on-press in enabled state"
|
||||
(let [on-press (h/mock-fn)]
|
||||
(h/render [network-dropdown/view
|
||||
{:state :default
|
||||
:on-press on-press}
|
||||
networks-list])
|
||||
(h/is-truthy (h/query-by-label-text :network-dropdown))
|
||||
(h/fire-event :press (h/query-by-label-text :network-dropdown))
|
||||
(h/was-called on-press)))
|
||||
|
||||
(h/test "Should not call :on-press in disabled state"
|
||||
(let [on-press (h/mock-fn)]
|
||||
(h/render [network-dropdown/view
|
||||
{:state :disabled
|
||||
:on-press on-press}
|
||||
networks-list])
|
||||
(h/is-truthy (h/query-by-label-text :network-dropdown))
|
||||
(h/fire-event :press (h/query-by-label-text :network-dropdown))
|
||||
(h/was-not-called on-press))))
|
||||
@@ -0,0 +1,24 @@
|
||||
(ns quo2.components.dropdowns.network-dropdown.style
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
|
||||
(defn container-border-color
|
||||
[{:keys [state blur? theme pressed?]}]
|
||||
(let [default-color (if blur?
|
||||
colors/neutral-80-opa-5
|
||||
(colors/theme-colors colors/neutral-30 colors/neutral-70 theme))
|
||||
active-color (if blur?
|
||||
colors/neutral-80-opa-10
|
||||
(colors/theme-colors colors/neutral-40 colors/neutral-60 theme))]
|
||||
(condp = state
|
||||
:disabled default-color
|
||||
:default (if pressed? active-color default-color))))
|
||||
|
||||
(defn dropdown-container
|
||||
[{:keys [state] :as props}]
|
||||
{:border-width 1
|
||||
:border-radius 10
|
||||
:padding-horizontal 8
|
||||
:padding-vertical 6
|
||||
:opacity (if (= state :disabled) 0.3 1)
|
||||
:border-color (container-border-color props)
|
||||
:align-items :center})
|
||||
@@ -0,0 +1,26 @@
|
||||
(ns quo2.components.dropdowns.network-dropdown.view
|
||||
(:require [quo2.theme :as quo.theme]
|
||||
[quo2.components.dropdowns.network-dropdown.style :as style]
|
||||
[react-native.core :as rn]
|
||||
[quo2.components.list-items.preview-list.view :as preview-list]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
(defn- internal-view
|
||||
[_ _]
|
||||
(let [pressed? (reagent/atom false)]
|
||||
(fn
|
||||
[{:keys [on-press state] :as props} networks]
|
||||
[rn/pressable
|
||||
{:style (style/dropdown-container (merge props {:pressed? @pressed?}))
|
||||
:accessibility-label :network-dropdown
|
||||
:disabled (= state :disabled)
|
||||
:on-press on-press
|
||||
:on-press-in (fn [] (reset! pressed? true))
|
||||
:on-press-out (fn [] (reset! pressed? false))}
|
||||
[preview-list/view
|
||||
{:type :network
|
||||
:list-size (count networks)
|
||||
:size :size/s-20}
|
||||
networks]])))
|
||||
|
||||
(def view (quo.theme/with-theme internal-view))
|
||||
@@ -24,3 +24,8 @@
|
||||
{:color colors/white}))
|
||||
|
||||
(def button-container {:margin-top 20})
|
||||
|
||||
(def image-placeholder
|
||||
{:width 80
|
||||
:height 80
|
||||
:background-color colors/danger})
|
||||
|
||||
@@ -7,14 +7,16 @@
|
||||
[quo2.theme :as theme]))
|
||||
|
||||
(defn- empty-state-internal
|
||||
[{:keys [customization-color image title description blur?]
|
||||
[{:keys [customization-color image title description blur? placeholder? container-style]
|
||||
upper-button :upper-button
|
||||
lower-button :lower-button
|
||||
:or {customization-color :blue}}]
|
||||
[rn/view {:style styles/container}
|
||||
[fast-image/fast-image
|
||||
{:style styles/image
|
||||
:source image}]
|
||||
[rn/view {:style (merge styles/container container-style)}
|
||||
(if placeholder?
|
||||
[rn/view {:style styles/image-placeholder}]
|
||||
[fast-image/fast-image
|
||||
{:style styles/image
|
||||
:source image}])
|
||||
[rn/view {:style styles/text-container}
|
||||
[text/text
|
||||
{:style (styles/title blur?)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
[react-native.linear-gradient :as linear-gradient]))
|
||||
|
||||
(defn- view-internal
|
||||
[{:keys [customization-color] :or {customization-color :blue}}]
|
||||
[{:keys [customization-color container-style] :or {customization-color :blue}}]
|
||||
(let [color-top (colors/custom-color customization-color 50 20)
|
||||
color-bottom (colors/custom-color customization-color 50 0)]
|
||||
[linear-gradient/linear-gradient
|
||||
@@ -13,6 +13,6 @@
|
||||
:colors [color-top color-bottom]
|
||||
:start {:x 0 :y 0}
|
||||
:end {:x 0 :y 1}
|
||||
:style style/root-container}]))
|
||||
:style (merge style/root-container container-style)}]))
|
||||
|
||||
(def view (quo.theme/with-theme view-internal))
|
||||
|
||||
@@ -2,8 +2,11 @@
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
|
||||
(def gradient-background
|
||||
{:height 294
|
||||
:justify-content :flex-end})
|
||||
{:height 294
|
||||
:position :absolute
|
||||
:left 0
|
||||
:right 0
|
||||
:bottom 0})
|
||||
|
||||
(def x-axis-label-text-style ; We need this to remove unnecessary bottom spacing from graph
|
||||
{:margin-bottom -3
|
||||
|
||||
@@ -37,11 +37,12 @@
|
||||
:style style/illustration}
|
||||
[text/text {:style {:color colors/white}}
|
||||
"Illustration here"]]
|
||||
[linear-gradient/linear-gradient
|
||||
{:colors gradient-colors
|
||||
:start {:x 0 :y 1}
|
||||
:end {:x 0 :y 0}
|
||||
:style style/gradient-background}
|
||||
[rn/view
|
||||
[linear-gradient/linear-gradient
|
||||
{:colors gradient-colors
|
||||
:start {:x 0 :y 1}
|
||||
:end {:x 0 :y 0}
|
||||
:style style/gradient-background}]
|
||||
[rn/view {:accessibility-label :line-chart}
|
||||
[charts/line-chart
|
||||
{:height 96
|
||||
|
||||
@@ -3,23 +3,6 @@
|
||||
[test-helpers.component :as h]))
|
||||
|
||||
(h/describe "Profile Input"
|
||||
(h/test "renders user avatar with placeholder name if no value is specified"
|
||||
(h/render [profile-input/profile-input
|
||||
{:placeholder "Your Name"
|
||||
:image-picker-props {:full-name "Your Name"}}])
|
||||
(-> (js/expect (h/get-by-text "YN"))
|
||||
(.toBeTruthy)))
|
||||
|
||||
(h/test "renders user avatar with full name if a value is specified"
|
||||
(let [event (h/mock-fn)]
|
||||
(h/render [profile-input/profile-input
|
||||
{:on-change event
|
||||
:placeholder "Your Name"
|
||||
:image-picker-props {:full-name "Test Name"}}])
|
||||
(h/fire-event :change (h/get-by-text "TN"))
|
||||
(-> (js/expect (h/get-by-text "TN"))
|
||||
(.toBeTruthy))))
|
||||
|
||||
(h/test "on press event fires"
|
||||
(let [event (h/mock-fn)]
|
||||
(h/render [profile-input/profile-input
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
(ns quo2.components.inputs.profile-input.view
|
||||
(:require
|
||||
[quo2.components.buttons.button.view :as buttons]
|
||||
[quo2.components.avatars.user-avatar.view :as user-avatar]
|
||||
[quo2.components.buttons.button.view :as buttons]
|
||||
[quo2.components.inputs.profile-input.style :as style]
|
||||
[quo2.components.inputs.title-input.view :as title-input]
|
||||
[react-native.core :as rn]
|
||||
@@ -13,38 +13,40 @@
|
||||
on-press
|
||||
title-input-props
|
||||
image-picker-props]}]
|
||||
[rn/view
|
||||
{:style (style/container customization-color)}
|
||||
[rn/view
|
||||
[hole-view/hole-view
|
||||
;; Force re-render hole view when props are changed
|
||||
;; https://github.com/status-im/status-mobile/issues/15577
|
||||
{:key (hash
|
||||
(if (:profile-picture image-picker-props)
|
||||
(:profile-picture image-picker-props)
|
||||
image-picker-props))
|
||||
:holes [{:x 33
|
||||
:y 24
|
||||
:width 24
|
||||
:height 24
|
||||
:borderRadius 12}]}
|
||||
[user-avatar/user-avatar
|
||||
(assoc image-picker-props
|
||||
:static? true
|
||||
:status-indicator? false
|
||||
:size :medium)]]
|
||||
[buttons/button
|
||||
{:accessibility-label :select-profile-picture-button
|
||||
:type :grey
|
||||
:background :blur
|
||||
:on-press on-press
|
||||
:size 24
|
||||
:icon-only? true
|
||||
:container-style style/button
|
||||
:inner-style style/button-inner} :i/camera]]
|
||||
[rn/view {:style style/input-container}
|
||||
[title-input/title-input
|
||||
(merge title-input-props
|
||||
{:blur? true
|
||||
:placeholder placeholder
|
||||
:customization-color customization-color})]]])
|
||||
(let [full-name (:full-name image-picker-props)]
|
||||
[rn/view
|
||||
{:style (style/container customization-color)}
|
||||
[rn/view
|
||||
[hole-view/hole-view
|
||||
;; Force re-render hole view when props are changed
|
||||
;; https://github.com/status-im/status-mobile/issues/15577
|
||||
{:key (hash
|
||||
(if (:profile-picture image-picker-props)
|
||||
(:profile-picture image-picker-props)
|
||||
image-picker-props))
|
||||
:holes [{:x 33
|
||||
:y 24
|
||||
:width 24
|
||||
:height 24
|
||||
:borderRadius 12}]}
|
||||
[user-avatar/user-avatar
|
||||
(assoc image-picker-props
|
||||
:static? true
|
||||
:status-indicator? false
|
||||
:full-name (if (seq full-name) full-name placeholder)
|
||||
:size :medium)]]
|
||||
[buttons/button
|
||||
{:accessibility-label :select-profile-picture-button
|
||||
:type :grey
|
||||
:background :blur
|
||||
:on-press on-press
|
||||
:size 24
|
||||
:icon-only? true
|
||||
:container-style style/button
|
||||
:inner-style style/button-inner} :i/camera]]
|
||||
[rn/view {:style style/input-container}
|
||||
[title-input/title-input
|
||||
(merge title-input-props
|
||||
{:blur? true
|
||||
:placeholder placeholder
|
||||
:customization-color customization-color})]]]))
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
(defn title-text
|
||||
[disabled? blur? theme]
|
||||
{:text-align-vertical :bottom
|
||||
:padding 0
|
||||
:color (if disabled?
|
||||
(get-disabled-color blur? theme)
|
||||
(colors/theme-colors colors/neutral-100 colors/white theme))})
|
||||
@@ -50,7 +51,6 @@
|
||||
|
||||
(def container
|
||||
{:flex-direction :row
|
||||
:flex 1
|
||||
:justify-content :center
|
||||
:align-items :center})
|
||||
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
placeholder
|
||||
max-length
|
||||
default-value
|
||||
theme]
|
||||
theme
|
||||
container-style]
|
||||
:or {max-length 0
|
||||
auto-focus false
|
||||
default-value ""}}]
|
||||
@@ -31,7 +32,7 @@
|
||||
(on-change-text v)))]
|
||||
(fn [{:keys [customization-color disabled?]}]
|
||||
[rn/view
|
||||
{:style style/container}
|
||||
{:style (merge style/container container-style)}
|
||||
[rn/view {:style style/text-input-container}
|
||||
[rn/text-input
|
||||
{:style
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
(ns quo2.components.list-items.dapp.component-spec
|
||||
(:require [quo2.components.list-items.dapp.view :as dapp]
|
||||
[test-helpers.component :as h]))
|
||||
|
||||
(def props
|
||||
{:dapp {:avatar nil
|
||||
:name "Coingecko"
|
||||
:value "coingecko.com"}
|
||||
:state :default
|
||||
:action :icon
|
||||
:blur? false
|
||||
:customization-color :blue})
|
||||
|
||||
(h/describe
|
||||
"dApp component test"
|
||||
(h/test "default render"
|
||||
(h/render [dapp/view props])
|
||||
(h/is-truthy (h/get-by-text (:name (:dapp props))))
|
||||
(h/is-truthy (h/get-by-text (:value (:dapp props)))))
|
||||
(h/test "on-press event"
|
||||
(let [mock-fn (h/mock-fn)]
|
||||
(h/render [dapp/view (assoc props :on-press-icon mock-fn)])
|
||||
(h/fire-event :press (h/get-by-test-id "dapp-component-icon"))
|
||||
(h/was-called mock-fn))))
|
||||
@@ -0,0 +1,47 @@
|
||||
(ns quo2.components.list-items.dapp.style
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
|
||||
(defn get-background-color
|
||||
[{:keys [pressed? state blur? customization-color theme]}]
|
||||
(cond
|
||||
(and pressed? (= theme :dark) blur?) colors/white-opa-5
|
||||
|
||||
pressed? (colors/theme-colors
|
||||
(colors/custom-color customization-color 50 5)
|
||||
(colors/custom-color customization-color 60 5)
|
||||
theme)
|
||||
|
||||
(and (= state :active) (= theme :dark) blur?) colors/white-opa-10
|
||||
|
||||
(= state :active) (colors/theme-colors
|
||||
(colors/custom-color customization-color 50 10)
|
||||
(colors/custom-color customization-color 60 10)
|
||||
theme)
|
||||
|
||||
:else :transparent))
|
||||
|
||||
(defn container
|
||||
[props]
|
||||
{:flex 1
|
||||
:padding-horizontal 12
|
||||
:padding-vertical 8
|
||||
:border-radius 12
|
||||
:background-color (get-background-color props)
|
||||
:flex-direction :row
|
||||
:justify-content :space-between
|
||||
:align-items :center})
|
||||
|
||||
(def container-info
|
||||
{:flex-direction :row
|
||||
:align-items :center})
|
||||
|
||||
(def user-info
|
||||
{:margin-left 8})
|
||||
|
||||
(defn style-text-name
|
||||
[theme]
|
||||
{:color (colors/theme-colors colors/neutral-100 colors/white theme)})
|
||||
|
||||
(defn style-text-value
|
||||
[theme]
|
||||
{:color (colors/theme-colors colors/neutral-50 colors/white theme)})
|
||||
@@ -0,0 +1,47 @@
|
||||
(ns quo2.components.list-items.dapp.view
|
||||
(:require [quo2.components.icon :as icons]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[react-native.core :as rn]
|
||||
[react-native.fast-image :as fast-image]
|
||||
[quo2.components.list-items.dapp.style :as style]
|
||||
[quo2.theme :as quo.theme]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
(defn- view-internal
|
||||
[]
|
||||
(let [pressed? (reagent/atom false)]
|
||||
(fn [{:keys [dapp action on-press on-press-icon theme] :as props}]
|
||||
[rn/pressable
|
||||
{:style (style/container (assoc props :pressed? @pressed?))
|
||||
:on-press on-press
|
||||
:on-press-in #(reset! pressed? true)
|
||||
:on-press-out #(reset! pressed? false)}
|
||||
[rn/view {:style style/container-info}
|
||||
[fast-image/fast-image
|
||||
{:source (:avatar dapp)
|
||||
:style {:width 32 :height 32}}]
|
||||
[rn/view {:style style/user-info}
|
||||
[text/text
|
||||
{:weight :semi-bold
|
||||
:size :paragraph-1
|
||||
:style (style/style-text-name theme)}
|
||||
(:name dapp)]
|
||||
[text/text
|
||||
{:weight :regular
|
||||
:size :paragraph-2
|
||||
:style (style/style-text-value theme)}
|
||||
(:value dapp)]]]
|
||||
(when (= action :icon)
|
||||
[rn/pressable
|
||||
{:on-press on-press-icon
|
||||
:testID "dapp-component-icon"}
|
||||
[icons/icon :i/options
|
||||
{:color (colors/theme-colors
|
||||
colors/neutral-50
|
||||
colors/neutral-40
|
||||
theme)
|
||||
:accessibility-label :icon}]])])))
|
||||
|
||||
(def view
|
||||
(quo.theme/with-theme view-internal))
|
||||