Compare commits
30
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 |
@@ -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
|
||||
|
||||
@@ -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' }
|
||||
|
||||
+2
-2
@@ -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 {
|
||||
|
||||
+1
-2
@@ -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.
|
||||
|
||||
+1
-1
@@ -793,7 +793,7 @@ CHECKOUT OPTIONS:
|
||||
:submodules: true
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
boost: 57d2868c099736d80fcd648bf211b4431e51a558
|
||||
boost: 64032b9e9b938fda23325e68a3771f0fabf414dc
|
||||
BVLinearGradient: 612a04ff38e8480291f3379ee5b5a2c571f03fe0
|
||||
CryptoSwift: c4f2debceb38bf44c80659afe009f71e23e4a082
|
||||
DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
|
||||
|
||||
@@ -120,9 +120,12 @@ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(checkAddressChecksum:(NSString *)address)
|
||||
return StatusgoCheckAddressChecksum(address);
|
||||
}
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(isAddress:(NSString *)address) {
|
||||
return StatusgoIsAddress(address);
|
||||
}
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(toChecksumAddress:(NSString *)address) {
|
||||
return StatusgoToChecksumAddress(address);
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
+7
-5
@@ -61,7 +61,7 @@ We will use the `make jsbundle` target as an example of a derivation you can bui
|
||||
3. [`build.sh`](/nix/scripts/build.sh) calls `nix-build --attr targets.mobile.jsbundle` with extra arguments
|
||||
4. `nix-build` builds the derivation from [`nix/mobile/jsbundle/default.nix`](/nix/mobile/jsbundle/default.nix)
|
||||
|
||||
The same can be done for other targets like `targets.mobile.android.release`.
|
||||
The same can be done for other targets like `targets.mobile.android.build`.
|
||||
Except in that case extra arguments are required which is why the [`scripts/release-android.sh`](/scripts/release-android.sh) is used in the `make release-android` target.
|
||||
|
||||
If you run `make release-android` you'll see the `nix-build` command used:
|
||||
@@ -71,21 +71,23 @@ nix-build \
|
||||
--fallback \
|
||||
--no-out-link \
|
||||
--show-trace \
|
||||
--attr targets.mobile.android.release \
|
||||
--attr targets.mobile.android.build \
|
||||
--argstr secrets-file '/tmp/tmp-status-mobile-559a3a441/tmp.xAnrPuNtAP' \
|
||||
--option extra-sandbox-paths '/home/joe/.gradle/status-im.keystore /tmp/tmp-status-mobile-559a3a441/tmp.xAnrPuNtAP' \
|
||||
default.nix
|
||||
```
|
||||
Some of those are required which is why just calling:
|
||||
```
|
||||
nix-build --attr targets.mobile.android.release
|
||||
nix-build --attr targets.mobile.android.build
|
||||
```
|
||||
Would fail.
|
||||
|
||||
# Garbage Collection
|
||||
|
||||
The `make nix-gc` target calls `nix-store --gc` and normally would remove almost everything, but to prevent that we place symlinks to protected derivations in `/nix/var/nix/gcroots` subfolder. Specifically:
|
||||
The `make nix-gc` target calls `nix-store --gc` and normally would remove almost everything, but to prevent that we place symlinks to protected derivations in `.nix-gcroots` folder. Specifically:
|
||||
```sh
|
||||
_NIX_GCROOTS="${_NIX_GCROOTS:-/nix/var/nix/gcroots/per-user/${USER}/status-mobile}"
|
||||
_NIX_GCROOTS="${_NIX_GCROOTS:-${GIT_ROOT}/.nix-gcroots}
|
||||
```
|
||||
These symlinks in turn will be symlinked from `/nix/var/nix/gcroots/auto` through use of `nix-store --add-gcroots`.
|
||||
|
||||
Whenever `nix/scripts/build.sh` or `nix/scripts/shell.sh` are called they update symlinks named after given targets in that folder. This in combination with `keep-outputs = true` set in `nix/nix.conf` prevents garbage collection from removing too much.
|
||||
|
||||
+1
-1
@@ -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
|
||||
];
|
||||
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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[@]}"
|
||||
|
||||
Executable
+53
@@ -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'
|
||||
Executable
+79
@@ -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'
|
||||
Executable
+5
@@ -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
|
||||
@@ -45,9 +45,6 @@
|
||||
;; To match the folder created by Nix build of JSBundle.
|
||||
:output-dir "result"
|
||||
:init-fn status-im.core/init
|
||||
:build-hooks [(status-im.setup.build-hooks.worklets/transform-output)
|
||||
(status-im.setup.build-hooks.worklets/optimize-start)
|
||||
(status-im.setup.build-hooks.worklets/optimize-output)]
|
||||
;; When false, the Shadow-CLJS watcher won't automatically refresh
|
||||
;; the target files (a.k.a hot reload). When false, you can manually
|
||||
;; reload by calling `shadow.cljs.devtools.api/watch-compile-all!`.
|
||||
|
||||
@@ -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
|
||||
[_ _ _]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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?
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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])
|
||||
@@ -1,70 +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]))
|
||||
|
||||
(def ?schema
|
||||
[:=>
|
||||
[:catn
|
||||
[:props
|
||||
[:map {:closed true}
|
||||
[:options {:optional true} [:maybe [:enum false :add :hold]]]
|
||||
[:size {:optional true} [:enum :size-24 :size-32]]
|
||||
[:blur? {:optional true} :boolean]
|
||||
[:theme :schema.common/theme]
|
||||
[:collectible-img-src [:or :int :string]]
|
||||
[:collectible-name :string]
|
||||
[:collectible-id :string]
|
||||
[:container-width :number]
|
||||
[:on-layout {:optional true} [:maybe fn?]]]]]
|
||||
:any])
|
||||
|
||||
(defn- view-internal
|
||||
[]
|
||||
(fn [{:keys [options size blur? theme collectible-img-src collectible-name collectible-id
|
||||
container-width on-layout]
|
||||
:or {size :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}]])]))
|
||||
(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 ?schema)))
|
||||
(schema/instrument #'view-internal component-schema/?schema)))
|
||||
|
||||
@@ -18,5 +18,6 @@
|
||||
[: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])
|
||||
|
||||
@@ -42,9 +42,11 @@
|
||||
|
||||
(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}
|
||||
@@ -52,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
|
||||
@@ -67,10 +69,11 @@
|
||||
{: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]])
|
||||
|
||||
|
||||
@@ -15,101 +15,101 @@
|
||||
|
||||
(h/describe "Confirmation Progress"
|
||||
(h/test "component renders when state is sending and network is optimism"
|
||||
(h/render-with-theme-provider [quo/confirmation-propgress
|
||||
(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-with-theme-provider
|
||||
[quo/confirmation-propgress
|
||||
[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-with-theme-provider
|
||||
[quo/confirmation-propgress
|
||||
[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-with-theme-provider
|
||||
[quo/confirmation-propgress
|
||||
[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-with-theme-provider
|
||||
[quo/confirmation-propgress
|
||||
[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-with-theme-provider
|
||||
[quo/confirmation-propgress
|
||||
[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-with-theme-provider
|
||||
[quo/confirmation-propgress
|
||||
[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-with-theme-provider
|
||||
[quo/confirmation-propgress
|
||||
[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-with-theme-provider
|
||||
[quo/confirmation-propgress
|
||||
[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-with-theme-provider
|
||||
[quo/confirmation-propgress
|
||||
[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-with-theme-provider
|
||||
[quo/confirmation-propgress (get-test-data {})])
|
||||
[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-with-theme-provider
|
||||
[quo/confirmation-propgress (get-test-data {:state :sending})])
|
||||
[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-with-theme-provider
|
||||
[quo/confirmation-propgress (get-test-data {:state :confirmed})])
|
||||
[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-with-theme-provider
|
||||
[quo/confirmation-propgress (get-test-data {:state :finalising})])
|
||||
[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-with-theme-provider
|
||||
[quo/confirmation-propgress (get-test-data {:state :finalized})])
|
||||
[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-with-theme-provider
|
||||
[quo/confirmation-propgress
|
||||
[quo/confirmation-progress
|
||||
(get-test-data {:state :error})])
|
||||
(h/is-truthy (h/get-by-label-text :mainnet-progress-box)))
|
||||
|
||||
+1
-1
@@ -418,7 +418,7 @@
|
||||
(def account-permissions quo.components.wallet.account-permissions.view/view)
|
||||
(def address-text quo.components.wallet.address-text.view/view)
|
||||
(def amount-input quo.components.wallet.amount-input.view/view)
|
||||
(def confirmation-propgress quo.components.wallet.confirmation-progress.view/view)
|
||||
(def confirmation-progress quo.components.wallet.confirmation-progress.view/view)
|
||||
(def keypair quo.components.wallet.keypair.view/view)
|
||||
(def network-amount quo.components.wallet.network-amount.view/view)
|
||||
(def network-bridge quo.components.wallet.network-bridge.view/view)
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
quo.components.share.share-qr-code.component-spec
|
||||
quo.components.switchers.base-card.component-spec
|
||||
quo.components.switchers.group-messaging-card.component-spec
|
||||
quo.components.tags.collectible-tag.component-spec
|
||||
quo.components.tags.network-tags.component-spec
|
||||
quo.components.tags.status-tags-component-spec
|
||||
quo.components.tags.summary-tag.component-spec
|
||||
|
||||
@@ -20,8 +20,7 @@
|
||||
LinearTransition
|
||||
enableLayoutAnimations
|
||||
useAnimatedScrollHandler
|
||||
runOnJS)
|
||||
:as reanimated*]
|
||||
runOnJS)]
|
||||
["react-native-redash" :refer (withPause)]
|
||||
[oops.core :as oops]
|
||||
[react-native.flat-list :as rn-flat-list]
|
||||
@@ -114,8 +113,6 @@
|
||||
(when (and anim (some? v))
|
||||
(set! (.-value anim) v)))
|
||||
|
||||
(def interpolate* reanimated*/interpolate)
|
||||
|
||||
(defn interpolate
|
||||
([shared-value input-range output-range]
|
||||
(interpolate shared-value input-range output-range nil))
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
item-height (reagent/atom 0)]
|
||||
(fn [{:keys [hide? insets theme]}
|
||||
{:keys [content selected-item padding-bottom-override border-radius on-close shell?
|
||||
gradient-cover? customization-color hide-handle?]
|
||||
gradient-cover? customization-color hide-handle? blur-radius]
|
||||
:or {border-radius 12}}]
|
||||
(let [{window-height :height} (rn/get-window)
|
||||
bg-opacity (reanimated/use-shared-value 0)
|
||||
@@ -106,7 +106,11 @@
|
||||
{:transform [{:translateY translate-y}]}
|
||||
(style/sheet insets window-height selected-item))}
|
||||
(when shell?
|
||||
[blur/ios-view {:style style/shell-bg}])
|
||||
[blur/ios-view
|
||||
{:style style/shell-bg
|
||||
:blur-radius (or blur-radius 20)
|
||||
:blur-type :transparent
|
||||
:overlay-color :transparent}])
|
||||
(when selected-item
|
||||
[rn/view
|
||||
{:on-layout #(reset! item-height (.-nativeEvent.layout.height ^js %))
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
(def ^:const drag-threshold 200)
|
||||
|
||||
(defn drag-gesture
|
||||
[{:keys [translate-y opacity scroll-enabled curr-scroll close reset-open-sheet set-animating-true]}]
|
||||
[{:keys [translate-y opacity scroll-enabled? curr-scroll close reset-open-sheet set-animating-true]}]
|
||||
(-> (gesture/gesture-pan)
|
||||
(gesture/on-start (fn [e]
|
||||
(set-animating-true)
|
||||
(when (< (oops/oget e "velocityY") 0)
|
||||
(reset! scroll-enabled true))))
|
||||
(reset! scroll-enabled? true))))
|
||||
(gesture/on-update (fn [e]
|
||||
(let [translation (oops/oget e "translationY")
|
||||
progress (Math/abs (/ translation drag-threshold))]
|
||||
@@ -34,7 +34,7 @@
|
||||
(gesture/on-finalize (fn [e]
|
||||
(when (and (>= (oops/oget e "velocityY") 0)
|
||||
(<= @curr-scroll (if platform/ios? -1 0)))
|
||||
(reset! scroll-enabled false))))))
|
||||
(reset! scroll-enabled? false))))))
|
||||
|
||||
(defn on-scroll
|
||||
[e curr-scroll]
|
||||
@@ -43,7 +43,7 @@
|
||||
|
||||
(defn- f-view
|
||||
[_]
|
||||
(let [scroll-enabled (reagent/atom true)
|
||||
(let [scroll-enabled? (reagent/atom true)
|
||||
curr-scroll (reagent/atom 0)
|
||||
animating? (reagent/atom true)
|
||||
set-animating-true #(reset! animating? true)
|
||||
@@ -63,7 +63,7 @@
|
||||
(reanimated/animate translate-y 0 300)
|
||||
(reanimated/animate opacity 1 300)
|
||||
(set-animating-false 300)
|
||||
(reset! scroll-enabled true))]
|
||||
(reset! scroll-enabled? true))]
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(reanimated/animate translate-y 0 300)
|
||||
@@ -76,7 +76,7 @@
|
||||
[gesture/gesture-detector
|
||||
{:gesture (drag-gesture {:translate-y translate-y
|
||||
:opacity opacity
|
||||
:scroll-enabled scroll-enabled
|
||||
:scroll-enabled? scroll-enabled?
|
||||
:curr-scroll curr-scroll
|
||||
:close close
|
||||
:reset-open-sheet reset-open-sheet
|
||||
@@ -87,7 +87,7 @@
|
||||
[content
|
||||
{:insets insets
|
||||
:close close
|
||||
:scroll-enabled scroll-enabled
|
||||
:scroll-enabled? @scroll-enabled?
|
||||
:current-scroll curr-scroll
|
||||
:on-scroll #(on-scroll % curr-scroll)
|
||||
:sheet-animating? animating?}]]]]))))
|
||||
|
||||
@@ -94,11 +94,11 @@
|
||||
:container-style style/empty-results}])
|
||||
|
||||
(defn- render-list
|
||||
[{:keys [theme filtered-data on-viewable-items-changed scroll-enabled on-scroll
|
||||
[{:keys [theme filtered-data on-viewable-items-changed scroll-enabled? on-scroll
|
||||
on-select set-scroll-ref close sheet-animating?]}]
|
||||
[gesture/flat-list
|
||||
{:ref set-scroll-ref
|
||||
:scroll-enabled @scroll-enabled
|
||||
:scroll-enabled scroll-enabled?
|
||||
:data (or filtered-data emoji-picker.data/flatten-data)
|
||||
:initial-num-to-render 14
|
||||
:max-to-render-per-batch 10
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
nil)]
|
||||
[quo/top-nav
|
||||
{:avatar-on-press #(rf/dispatch [:open-modal :settings])
|
||||
:scan-on-press #(js/alert "to be implemented")
|
||||
:scan-on-press #(rf/dispatch [:open-modal :shell-qr-reader])
|
||||
:activity-center-on-press #(rf/dispatch [:activity-center/open])
|
||||
:qr-code-on-press #(rf/dispatch [:open-modal :share-shell])
|
||||
:container-style (merge style/top-nav-container container-style)
|
||||
|
||||
@@ -26,8 +26,18 @@
|
||||
|
||||
(def web2-domain "status.app")
|
||||
|
||||
(def user-path "u#")
|
||||
(def user-with-data-path "u/")
|
||||
(def community-path "c#")
|
||||
(def community-with-data-path "c/")
|
||||
(def channel-path "cc/")
|
||||
|
||||
(def web-urls (map #(str % web2-domain "/") web-prefixes))
|
||||
|
||||
(defn path-urls
|
||||
[path]
|
||||
(map #(str % path) web-urls))
|
||||
|
||||
(def handled-schemes (set (into uri-schemes web-urls)))
|
||||
|
||||
(def group-chat-extractor
|
||||
@@ -41,15 +51,15 @@
|
||||
|
||||
(def routes
|
||||
[""
|
||||
{handled-schemes {["c/" :community-data] :community
|
||||
["cc/" :community-data] :community-chat
|
||||
["p/" :chat-id] :private-chat
|
||||
["cr/" :community-id] :community-requests
|
||||
"g/" group-chat-extractor
|
||||
["wallet/" :account] :wallet-account
|
||||
["u/" :user-data] :user
|
||||
"c" :community
|
||||
"u" :user}
|
||||
{handled-schemes {[community-with-data-path :community-data] :community
|
||||
[channel-path :community-data] :community-chat
|
||||
["p/" :chat-id] :private-chat
|
||||
["cr/" :community-id] :community-requests
|
||||
"g/" group-chat-extractor
|
||||
["wallet/" :account] :wallet-account
|
||||
[user-with-data-path :user-data] :user
|
||||
"c" :community
|
||||
"u" :user}
|
||||
ethereum-scheme eip-extractor}])
|
||||
|
||||
(defn parse-query-params
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
(ns status-im.common.validation.profile
|
||||
(:require [clojure.string :as string]
|
||||
[status-im.constants :as constants]
|
||||
[utils.i18n :as i18n]))
|
||||
|
||||
;; NOTE - validation should match with Desktop
|
||||
;; https://github.com/status-im/status-desktop/blob/2ba96803168461088346bf5030df750cb226df4c/ui/imports/utils/Constants.qml#L468
|
||||
(def min-length 5)
|
||||
(def max-length 24)
|
||||
|
||||
(def emoji-regex
|
||||
#"(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])")
|
||||
@@ -22,7 +22,9 @@
|
||||
|
||||
(defn name-too-short? [s] (< (count (string/trim (str s))) min-length))
|
||||
|
||||
(defn name-too-long? [s] (> (count (string/trim (str s))) max-length))
|
||||
(defn name-too-long? [s] (> (count (string/trim (str s))) constants/profile-name-max-length))
|
||||
|
||||
(defn bio-too-long? [s] (> (count (string/trim (str s))) constants/profile-bio-max-length))
|
||||
|
||||
(defn validation-name
|
||||
[s]
|
||||
@@ -39,3 +41,12 @@
|
||||
{:check (i18n/label :t/special-characters)})
|
||||
(name-too-short? s) (i18n/label :t/minimum-characters {:min-chars min-length})
|
||||
(name-too-long? s) (i18n/label :t/profile-name-is-too-long)))
|
||||
|
||||
(defn validation-bio
|
||||
[s]
|
||||
(cond
|
||||
(or (= s nil) (= s "")) nil
|
||||
(has-emojis? s) (i18n/label :t/are-not-allowed {:check (i18n/label :t/emojis)})
|
||||
(has-special-characters? s) (i18n/label :t/are-not-allowed
|
||||
{:check (i18n/label :t/special-characters)})
|
||||
(bio-too-long? s) (i18n/label :t/bio-is-too-long)))
|
||||
|
||||
@@ -167,5 +167,5 @@
|
||||
|
||||
(def default-kdf-iterations 3200)
|
||||
|
||||
(def community-accounts-selection-enabled? (enabled? (get-config :ACCOUNT_SELECTION_ENABLED "0")))
|
||||
(def community-accounts-selection-enabled? true)
|
||||
(def fetch-messages-enabled? (enabled? (get-config :FETCH_MESSAGES_ENABLED "1")))
|
||||
|
||||
@@ -107,6 +107,7 @@
|
||||
|
||||
(def ^:const profile-default-color :blue)
|
||||
(def ^:const profile-name-max-length 24)
|
||||
(def ^:const profile-bio-max-length 240)
|
||||
(def ^:const profile-default-currency :usd)
|
||||
|
||||
(def ^:const profile-pictures-show-to-contacts-only 1)
|
||||
@@ -257,6 +258,7 @@
|
||||
(def ^:const terms-of-service-link "https://status.im/terms-of-use")
|
||||
(def ^:const docs-link "https://status.im/docs/")
|
||||
(def ^:const principles-link "https://our.status.im/our-principles/")
|
||||
(def ^:const create-account-link "https://status.app/help/wallet/create-wallet-accounts")
|
||||
|
||||
(def ^:const visibility-status-unknown 0)
|
||||
(def ^:const visibility-status-automatic 1)
|
||||
@@ -431,6 +433,8 @@
|
||||
(def ^:const optimism-network-name :optimism)
|
||||
(def ^:const arbitrum-network-name :arbitrum)
|
||||
|
||||
(def ^:const default-network-names #{mainnet-network-name optimism-network-name arbitrum-network-name})
|
||||
|
||||
(def ^:const chain-id-separator ":")
|
||||
|
||||
(def ^:const account-default-customization-color :blue)
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
(ns status-im.contexts.chat.home.add-new-contact.events
|
||||
(:require
|
||||
[clojure.string :as string]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.common.validators :as validators]
|
||||
[status-im.contexts.chat.contacts.events :as data-store.contacts]
|
||||
status-im.contexts.chat.home.add-new-contact.effects
|
||||
[status-im.navigation.events :as navigation]
|
||||
[utils.ens.stateofus :as stateofus]
|
||||
[utils.ethereum.chain :as chain]
|
||||
[utils.re-frame :as rf]
|
||||
[utils.string :as utils.string]))
|
||||
|
||||
(defn init-contact
|
||||
@@ -87,72 +86,64 @@
|
||||
|
||||
(def validate-contact (comp ->state ->type ->id))
|
||||
|
||||
(defn dispatcher [event input] (fn [arg] (rf/dispatch [event input arg])))
|
||||
(declare build-contact)
|
||||
|
||||
(rf/defn set-new-identity
|
||||
{:events [:contacts/set-new-identity]}
|
||||
[{:keys [db]} input scanned]
|
||||
(defn set-new-identity
|
||||
[{:keys [db]} [{:keys [input build-success-fn failure-fn]}]]
|
||||
(let [user-public-key (get-in db [:profile/profile :public-key])
|
||||
{:keys [input id ens state]
|
||||
:as contact} (-> {:user-public-key user-public-key
|
||||
:input input
|
||||
:scanned scanned}
|
||||
:scanned input}
|
||||
init-contact
|
||||
validate-contact)]
|
||||
(case state
|
||||
|
||||
:empty {:db (dissoc db :contacts/new-identity)}
|
||||
(:valid :invalid) {:db (assoc db :contacts/new-identity contact)}
|
||||
:decompress-key {:db (assoc db :contacts/new-identity contact)
|
||||
:serialization/decompress-public-key
|
||||
{:compressed-key id
|
||||
:on-success
|
||||
(dispatcher :contacts/set-new-identity-success input)
|
||||
#(re-frame/dispatch [:contacts/set-new-identity-success
|
||||
{:input input
|
||||
:pubkey %
|
||||
:build-success-fn build-success-fn}])
|
||||
:on-error
|
||||
(dispatcher :contacts/set-new-identity-error input)}}
|
||||
#(re-frame/dispatch [:contacts/set-new-identity-error
|
||||
{:input input
|
||||
:pubkey %
|
||||
:failure-fn failure-fn}])}}
|
||||
:resolve-ens {:db (assoc db :contacts/new-identity contact)
|
||||
:effects.contacts/resolve-public-key-from-ens
|
||||
{:chain-id (chain/chain-id db)
|
||||
:ens ens
|
||||
:on-success
|
||||
(dispatcher :contacts/set-new-identity-success input)
|
||||
#(re-frame/dispatch [:contacts/set-new-identity-success
|
||||
{:input input
|
||||
:pubkey %
|
||||
:build-success-fn build-success-fn}])
|
||||
:on-error
|
||||
(dispatcher :contacts/set-new-identity-error input)}})))
|
||||
#(re-frame/dispatch [:contacts/set-new-identity-error
|
||||
{:input input
|
||||
:pubkey %
|
||||
:failure-fn failure-fn}])}})))
|
||||
|
||||
(re-frame/reg-event-fx :contacts/set-new-identity set-new-identity)
|
||||
|
||||
|
||||
(rf/defn build-contact
|
||||
{:events [:contacts/build-contact]}
|
||||
[_ pubkey ens open-profile-modal?]
|
||||
{:json-rpc/call [{:method "wakuext_buildContact"
|
||||
:params [{:publicKey pubkey
|
||||
:ENSName ens}]
|
||||
:js-response true
|
||||
:on-success #(rf/dispatch [:contacts/contact-built
|
||||
pubkey
|
||||
open-profile-modal?
|
||||
(data-store.contacts/<-rpc-js %)])}]})
|
||||
|
||||
(rf/defn contact-built
|
||||
{:events [:contacts/contact-built]}
|
||||
[{:keys [db]} pubkey open-profile-modal? contact]
|
||||
(merge {:db (assoc-in db [:contacts/contacts pubkey] contact)}
|
||||
(when open-profile-modal?
|
||||
{:dispatch [:open-modal :profile]})))
|
||||
|
||||
(rf/defn set-new-identity-success
|
||||
{:events [:contacts/set-new-identity-success]}
|
||||
[{:keys [db]} input pubkey]
|
||||
(defn- set-new-identity-success
|
||||
[{:keys [db]} [{:keys [input pubkey build-success-fn]}]]
|
||||
(let [contact (get-in db [:contacts/new-identity])]
|
||||
(when (= (:input contact) input)
|
||||
(rf/merge {:db (assoc db
|
||||
:contacts/new-identity
|
||||
(->state (assoc contact :public-key pubkey)))}
|
||||
(build-contact pubkey (:ens contact) false)))))
|
||||
{:db (assoc db :contacts/new-identity (->state (assoc contact :public-key pubkey)))
|
||||
:dispatch [:contacts/build-contact
|
||||
{:pubkey pubkey
|
||||
:ens (:ens contact)
|
||||
:success-fn build-success-fn}]})))
|
||||
|
||||
(rf/defn set-new-identity-error
|
||||
{:events [:contacts/set-new-identity-error]}
|
||||
[{:keys [db]} input err]
|
||||
(re-frame/reg-event-fx :contacts/set-new-identity-success set-new-identity-success)
|
||||
|
||||
(defn- set-new-identity-error
|
||||
[{:keys [db]} [{:keys [input err failure-fn]}]]
|
||||
(let [contact (get-in db [:contacts/new-identity])]
|
||||
(when (= (:input contact) input)
|
||||
(let [state (cond
|
||||
@@ -163,21 +154,41 @@
|
||||
(string/includes? (:message err) "no such host")))
|
||||
{:state :invalid :msg :t/lost-connection}
|
||||
:else {:state :invalid})]
|
||||
{:db (assoc db :contacts/new-identity (merge contact state))}))))
|
||||
(merge {:db (assoc db :contacts/new-identity (merge contact state))}
|
||||
(when failure-fn
|
||||
(failure-fn)))))))
|
||||
|
||||
(rf/defn clear-new-identity
|
||||
{:events [:contacts/clear-new-identity :contacts/new-chat-focus]}
|
||||
(re-frame/reg-event-fx :contacts/set-new-identity-error set-new-identity-error)
|
||||
|
||||
(defn- build-contact
|
||||
[_ [{:keys [pubkey ens success-fn]}]]
|
||||
{:json-rpc/call [{:method "wakuext_buildContact"
|
||||
:params [{:publicKey pubkey
|
||||
:ENSName ens}]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:contacts/build-contact-success
|
||||
{:pubkey pubkey
|
||||
:contact (data-store.contacts/<-rpc-js %)
|
||||
:success-fn success-fn}])}]})
|
||||
|
||||
(re-frame/reg-event-fx :contacts/build-contact build-contact)
|
||||
|
||||
(defn- build-contact-success
|
||||
[{:keys [db]} [{:keys [pubkey contact success-fn]}]]
|
||||
(merge {:db (assoc-in db [:contacts/contacts pubkey] contact)}
|
||||
(when success-fn
|
||||
(success-fn contact))))
|
||||
|
||||
(re-frame/reg-event-fx :contacts/build-contact-success build-contact-success)
|
||||
|
||||
(defn- clear-new-identity
|
||||
[{:keys [db]}]
|
||||
{:db (dissoc db :contacts/new-identity)})
|
||||
|
||||
(rf/defn qr-code-scanned
|
||||
{:events [:contacts/qr-code-scanned]}
|
||||
[{:keys [db] :as cofx} scanned]
|
||||
(rf/merge cofx
|
||||
(set-new-identity scanned scanned)
|
||||
(navigation/navigate-back)))
|
||||
(re-frame/reg-event-fx :contacts/clear-new-identity clear-new-identity)
|
||||
|
||||
(rf/defn set-new-identity-reconnected
|
||||
(defn set-new-identity-reconnected
|
||||
[{:keys [db]}]
|
||||
(let [input (get-in db [:contacts/new-identity :input])]
|
||||
(rf/dispatch [:contacts/set-new-identity input])))
|
||||
(re-frame/dispatch [:contacts/set-new-identity {:input input}])))
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
(:require
|
||||
[cljs.test :refer-macros [deftest are]]
|
||||
matcher-combinators.test
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.contexts.chat.home.add-new-contact.events :as events]))
|
||||
|
||||
(def user-ukey
|
||||
@@ -90,8 +91,8 @@
|
||||
:config {:NetworkId 1}}}})
|
||||
|
||||
(deftest set-new-identity-test
|
||||
(with-redefs [events/dispatcher (fn [& args] args)]
|
||||
(are [i edb] (match? (events/set-new-identity {:db db} i nil) edb)
|
||||
(with-redefs [re-frame/dispatch (fn [& args] args)]
|
||||
(are [i edb] (match? (events/set-new-identity {:db db} [{:input i}]) edb)
|
||||
|
||||
"" {:db db}
|
||||
|
||||
@@ -103,6 +104,7 @@
|
||||
:id ukey
|
||||
:type :public-key
|
||||
:public-key ukey
|
||||
:scanned ukey
|
||||
:state :invalid
|
||||
:msg :t/not-a-chatkey}))}
|
||||
|
||||
@@ -115,6 +117,7 @@
|
||||
:type :ens
|
||||
:ens ens-stateofus-eth
|
||||
:public-key nil ; not yet...
|
||||
:scanned ens
|
||||
:state :resolve-ens}))
|
||||
:effects.contacts/resolve-public-key-from-ens
|
||||
{:chain-id 1
|
||||
@@ -131,6 +134,7 @@
|
||||
:id user-ckey
|
||||
:type :compressed-key
|
||||
:public-key nil ; not yet...
|
||||
:scanned user-ckey
|
||||
:state :decompress-key}))
|
||||
:serialization/decompress-public-key
|
||||
{:compressed-key user-ckey
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
(rn/dismiss-keyboard!))
|
||||
[scan-qr-code/view
|
||||
{:title (i18n/label :t/scan-qr)
|
||||
:on-success-scan #(debounce/debounce-and-dispatch [:contacts/set-new-identity % %] 300)}]]))
|
||||
:on-success-scan #(debounce/debounce-and-dispatch [:contacts/set-new-identity {:input %}]
|
||||
300)}]]))
|
||||
|
||||
(defn view
|
||||
[]
|
||||
|
||||
@@ -59,7 +59,8 @@
|
||||
paste-on-input #(clipboard/get-string
|
||||
(fn [clipboard-text]
|
||||
(reset! input-value clipboard-text)
|
||||
(rf/dispatch [:contacts/set-new-identity clipboard-text nil])))]
|
||||
(rf/dispatch [:contacts/set-new-identity
|
||||
{:input clipboard-text}])))]
|
||||
(let [{:keys [scanned]} (rf/sub [:contacts/new-identity])
|
||||
empty-input? (and (string/blank? @input-value)
|
||||
(string/blank? scanned))]
|
||||
@@ -86,7 +87,7 @@
|
||||
:value (or scanned @input-value)
|
||||
:on-change-text (fn [new-text]
|
||||
(reset! input-value new-text)
|
||||
(as-> [:contacts/set-new-identity new-text nil] $
|
||||
(as-> [:contacts/set-new-identity {:input new-text}] $
|
||||
(if (string/blank? scanned)
|
||||
(debounce/debounce-and-dispatch $ 600)
|
||||
(rf/dispatch-sync $))))}]
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
item])))
|
||||
|
||||
(defn- view-internal
|
||||
[{:keys [scroll-enabled on-scroll close theme]}]
|
||||
[{:keys [scroll-enabled? on-scroll close theme]}]
|
||||
(let [contacts (rf/sub [:contacts/sorted-and-grouped-by-first-letter])
|
||||
selected-contacts-count (rf/sub [:selected-contacts-count])
|
||||
selected-contacts (rf/sub [:group/selected-contacts])
|
||||
@@ -105,7 +105,7 @@
|
||||
:render-section-header-fn contact-list/contacts-section-header
|
||||
:content-container-style {:padding-bottom 70}
|
||||
:render-fn contact-item-render
|
||||
:scroll-enabled @scroll-enabled
|
||||
:scroll-enabled scroll-enabled?
|
||||
:on-scroll on-scroll}])
|
||||
(when contacts-selected?
|
||||
[quo/button
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
(str (:title item) index))
|
||||
|
||||
(defn- f-album-selector
|
||||
[{:keys [scroll-enabled on-scroll]} album? selected-album top]
|
||||
[{:keys [scroll-enabled? on-scroll]} album? selected-album top]
|
||||
(let [albums (rf/sub [:camera-roll/albums])
|
||||
total-photos-count-android (rf/sub [:camera-roll/total-photos-count-android])
|
||||
total-photos-count-ios (rf/sub [:camera-roll/total-photos-count-ios])
|
||||
@@ -87,7 +87,7 @@
|
||||
:content-container-style {:padding-top 64
|
||||
:padding-bottom 40}
|
||||
:key-fn key-fn
|
||||
:scroll-enabled @scroll-enabled
|
||||
:scroll-enabled scroll-enabled?
|
||||
:on-scroll on-scroll
|
||||
:style {:height window-height}}]]))
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
(inc (utils.collection/first-index #(= (:uri item) (:uri %)) @selected))]])]))
|
||||
|
||||
(defn photo-selector
|
||||
[{:keys [scroll-enabled on-scroll current-scroll close] :as sheet}]
|
||||
[{:keys [scroll-enabled? on-scroll current-scroll close] :as sheet}]
|
||||
(rf/dispatch [:photo-selector/get-photos-for-selected-album])
|
||||
(rf/dispatch [:photo-selector/camera-roll-get-albums])
|
||||
(let [album? (reagent/atom false)
|
||||
@@ -134,7 +134,7 @@
|
||||
:padding-bottom (+ (safe-area/get-bottom) 100)
|
||||
:padding-top 64}
|
||||
:on-scroll on-scroll
|
||||
:scroll-enabled @scroll-enabled
|
||||
:scroll-enabled scroll-enabled?
|
||||
:on-end-reached (fn []
|
||||
(when (and (not loading?) has-next-page?)
|
||||
(rf/dispatch [:photo-selector/camera-roll-loading-more true])
|
||||
|
||||
@@ -7,10 +7,6 @@
|
||||
(def container
|
||||
{:flex 1})
|
||||
|
||||
(def page-top
|
||||
{:padding-vertical 12
|
||||
:padding-horizontal screen-horizontal-padding})
|
||||
|
||||
(def section-title
|
||||
{:padding-top 12
|
||||
:padding-bottom 4
|
||||
@@ -18,11 +14,6 @@
|
||||
|
||||
(defn bottom-actions
|
||||
[]
|
||||
{:position :absolute
|
||||
:background-color (colors/theme-colors colors/white colors/neutral-95)
|
||||
:bottom 0
|
||||
:left 0
|
||||
:right 0
|
||||
{:padding-top 12
|
||||
:padding-horizontal screen-horizontal-padding
|
||||
:padding-vertical 12
|
||||
:flex 1})
|
||||
:background-color (colors/theme-colors colors/white colors/neutral-95)})
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
highest-role-text (i18n/label (communities.utils/role->translation-key
|
||||
highest-permission-role
|
||||
:t/member))]
|
||||
[rn/view {:style style/container}
|
||||
[rn/safe-area-view {:style style/container}
|
||||
[quo/page-nav
|
||||
{:text-align :left
|
||||
:icon-name :i/close
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
(:require [quo.core :as quo]
|
||||
[quo.foundations.colors :as colors]
|
||||
[react-native.core :as rn]
|
||||
[react-native.gesture :as gesture]
|
||||
[status-im.common.not-implemented :as not-implemented]
|
||||
[status-im.common.resources :as resources]
|
||||
[status-im.constants :as constants]
|
||||
@@ -27,32 +28,37 @@
|
||||
:collectible-img-src (resources/get-mock-image :collectible))
|
||||
|
||||
(= type constants/community-token-type-erc20)
|
||||
(assoc :amount (str (money/with-precision (money/token->unit amount decimals) 3))
|
||||
(assoc :amount (str (money/token->unit amount decimals))
|
||||
:token (:symbol balance)))))
|
||||
|
||||
(defn- account-item
|
||||
[{:keys [customization-color address name emoji]} _ _ [selected-addresses community-id]]
|
||||
[{:keys [color address name emoji]} _ _
|
||||
{:keys [selected-addresses community-id share-all-addresses? community-color]}]
|
||||
(let [balances (rf/sub [:communities/permissioned-balances-by-address community-id address])]
|
||||
[quo/account-permissions
|
||||
{:account {:name name
|
||||
:address address
|
||||
:emoji emoji
|
||||
:customization-color customization-color}
|
||||
:token-details (balances->components-props balances)
|
||||
:checked? (contains? selected-addresses address)
|
||||
:on-change #(rf/dispatch [:communities/toggle-selected-permission-address
|
||||
address community-id])
|
||||
:container-style {:margin-bottom 8}}]))
|
||||
{:account {:name name
|
||||
:address address
|
||||
:emoji emoji
|
||||
:customization-color color}
|
||||
:token-details (balances->components-props balances)
|
||||
:checked? (contains? selected-addresses address)
|
||||
:disabled? share-all-addresses?
|
||||
:on-change #(rf/dispatch [:communities/toggle-selected-permission-address
|
||||
address community-id])
|
||||
:container-style {:margin-bottom 8}
|
||||
:customization-color community-color}]))
|
||||
|
||||
(defn view
|
||||
[]
|
||||
[{:keys [scroll-enabled? on-scroll]}]
|
||||
(let [{id :community-id} (rf/sub [:get-screen-params])]
|
||||
(rf/dispatch [:communities/get-permissioned-balances id])
|
||||
(fn []
|
||||
(let [{:keys [name color images]} (rf/sub [:communities/community id])
|
||||
{:keys [highest-permission-role]} (rf/sub [:community/token-gated-overview id])
|
||||
accounts (rf/sub [:wallet/accounts-with-customization-color])
|
||||
accounts (rf/sub [:wallet/accounts-without-watched-accounts])
|
||||
selected-addresses (rf/sub [:communities/selected-permission-addresses id])
|
||||
share-all-addresses? (rf/sub [:communities/share-all-addresses? id])
|
||||
unsaved-address-changes? (rf/sub [:communities/unsaved-address-changes? id])
|
||||
highest-role-text (when highest-permission-role
|
||||
(i18n/label (communities.utils/role->translation-key
|
||||
highest-permission-role)))]
|
||||
@@ -63,14 +69,31 @@
|
||||
:context-tag-type :community
|
||||
:community-name name
|
||||
:button-icon :i/info
|
||||
:button-type :grey
|
||||
:on-button-press not-implemented/alert
|
||||
:community-logo (get-in images [:thumbnail :uri])
|
||||
:customization-color color}]
|
||||
|
||||
[rn/flat-list
|
||||
[quo/category
|
||||
{:list-type :settings
|
||||
:data [{:title (i18n/label :t/share-all-current-and-future-addresses)
|
||||
:action :selector
|
||||
:action-props {:on-change #(rf/dispatch
|
||||
[:communities/toggle-share-all-addresses
|
||||
id])
|
||||
:customization-color color
|
||||
:checked? share-all-addresses?}}]
|
||||
:container-style {:padding-bottom 16}}]
|
||||
|
||||
[gesture/flat-list
|
||||
{:render-fn account-item
|
||||
:render-data [selected-addresses id]
|
||||
:content-container-style {:padding 20}
|
||||
:render-data {:selected-addresses selected-addresses
|
||||
:community-id id
|
||||
:share-all-addresses? share-all-addresses?
|
||||
:community-color color}
|
||||
:content-container-style {:padding-horizontal 20}
|
||||
:scroll-enabled scroll-enabled?
|
||||
:on-scroll on-scroll
|
||||
:key-fn :address
|
||||
:data accounts}]
|
||||
|
||||
@@ -106,7 +129,9 @@
|
||||
[quo/button
|
||||
{:container-style {:flex 1}
|
||||
:customization-color color
|
||||
:disabled? (empty? selected-addresses)
|
||||
:disabled? (or (empty? selected-addresses)
|
||||
(not highest-permission-role)
|
||||
(not unsaved-address-changes?))
|
||||
:on-press (fn []
|
||||
(rf/dispatch [:communities/update-previous-permission-addresses id])
|
||||
(rf/dispatch [:navigate-back]))}
|
||||
|
||||
@@ -3,5 +3,6 @@
|
||||
(def community-rule
|
||||
{:flex 1
|
||||
:align-items :flex-start
|
||||
:padding-vertical 8
|
||||
:padding-top 8
|
||||
:margin-bottom 12
|
||||
:padding-horizontal 20})
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
(defn token-requirements
|
||||
[]
|
||||
(fn [id]
|
||||
(let [{:keys [can-request-access?
|
||||
number-of-hold-tokens tokens]} (rf/sub [:community/token-gated-overview id])]
|
||||
(let [{:keys [can-request-access? tokens]} (rf/sub [:community/token-gated-overview id])]
|
||||
[rn/view {:style {:padding-horizontal 12}}
|
||||
[quo/text {:weight :medium}
|
||||
(if can-request-access?
|
||||
@@ -17,7 +16,6 @@
|
||||
(i18n/label :t/you-not-eligible-to-join))]
|
||||
[quo/text {:style {:padding-bottom 18} :size :paragraph-2}
|
||||
(if can-request-access?
|
||||
(i18n/label :t/you-hold-number-of-hold-tokens-of-these
|
||||
{:number-of-hold-tokens number-of-hold-tokens})
|
||||
(i18n/label :t/you-hodl)
|
||||
(i18n/label :t/you-must-hold))]
|
||||
[quo/token-requirement-list {:tokens tokens}]])))
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
status-im.contexts.communities.actions.addresses-for-permissions.events
|
||||
status-im.contexts.communities.actions.community-options.events
|
||||
status-im.contexts.communities.actions.leave.events
|
||||
[status-im.contexts.communities.utils :as utils]
|
||||
[status-im.navigation.events :as navigation]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.i18n :as i18n]
|
||||
@@ -163,15 +164,16 @@
|
||||
(defn initialize-permission-addresses
|
||||
[{:keys [db]} [community-id]]
|
||||
(when community-id
|
||||
(let [accounts (get-in db [:wallet :accounts])
|
||||
sorted-accounts (sort-by :position (vals accounts))
|
||||
addresses (set (map :address sorted-accounts))]
|
||||
(let [accounts (utils/sorted-non-watch-only-accounts db)
|
||||
addresses (set (map :address accounts))]
|
||||
{:db (update-in db
|
||||
[:communities community-id]
|
||||
assoc
|
||||
:previous-share-all-addresses? true
|
||||
:share-all-addresses? true
|
||||
:previous-permission-addresses addresses
|
||||
:selected-permission-addresses addresses
|
||||
:airdrop-address (:address (first sorted-accounts)))})))
|
||||
:airdrop-address (:address (first accounts)))})))
|
||||
|
||||
(rf/reg-event-fx :communities/initialize-permission-addresses
|
||||
initialize-permission-addresses)
|
||||
@@ -179,17 +181,18 @@
|
||||
(defn update-previous-permission-addresses
|
||||
[{:keys [db]} [community-id]]
|
||||
(when community-id
|
||||
(let [accounts (get-in db [:wallet :accounts])
|
||||
sorted-accounts (sort-by :position (vals accounts))
|
||||
(let [accounts (utils/sorted-non-watch-only-accounts db)
|
||||
selected-permission-addresses (get-in db
|
||||
[:communities community-id
|
||||
:selected-permission-addresses])
|
||||
selected-accounts (filter #(contains? selected-permission-addresses (:address %))
|
||||
sorted-accounts)
|
||||
current-airdrop-address (get-in db [:communities community-id :airdrop-address])]
|
||||
accounts)
|
||||
current-airdrop-address (get-in db [:communities community-id :airdrop-address])
|
||||
share-all-addresses? (get-in db [:communities community-id :share-all-addresses?])]
|
||||
{:db (update-in db
|
||||
[:communities community-id]
|
||||
assoc
|
||||
:previous-share-all-addresses? share-all-addresses?
|
||||
:previous-permission-addresses selected-permission-addresses
|
||||
:airdrop-address (if (contains? selected-permission-addresses
|
||||
current-airdrop-address)
|
||||
@@ -218,12 +221,32 @@
|
||||
(rf/reg-event-fx :communities/toggle-selected-permission-address
|
||||
toggle-selected-permission-address)
|
||||
|
||||
(defn toggle-share-all-addresses
|
||||
[{:keys [db]} [community-id]]
|
||||
(let [share-all-addresses? (get-in db [:communities community-id :share-all-addresses?])
|
||||
accounts (utils/sorted-non-watch-only-accounts db)
|
||||
addresses (set (map :address accounts))]
|
||||
{:db (update-in db
|
||||
[:communities community-id]
|
||||
(fn [community]
|
||||
(-> community
|
||||
(assoc :share-all-addresses? (not share-all-addresses?))
|
||||
(cond-> (not share-all-addresses?)
|
||||
(assoc :selected-permission-addresses addresses)))))}))
|
||||
|
||||
(rf/reg-event-fx :communities/toggle-share-all-addresses
|
||||
toggle-share-all-addresses)
|
||||
|
||||
(rf/reg-event-fx :communities/reset-selected-permission-addresses
|
||||
(fn [{:keys [db]} [community-id]]
|
||||
(when community-id
|
||||
{:db (assoc-in db
|
||||
[:communities community-id :selected-permission-addresses]
|
||||
(get-in db [:communities community-id :previous-permission-addresses]))
|
||||
{:db (update-in db
|
||||
[:communities community-id]
|
||||
assoc
|
||||
:selected-permission-addresses
|
||||
(get-in db [:communities community-id :previous-permission-addresses])
|
||||
:share-all-addresses?
|
||||
(get-in db [:communities community-id :previous-share-all-addresses?]))
|
||||
:fx [[:dispatch [:communities/check-permissions-to-join-community community-id]]]})))
|
||||
|
||||
(rf/reg-event-fx :communities/share-community-channel-url-with-data
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
expected-db {:db (update-in (:db initial-db)
|
||||
[:communities community-id]
|
||||
assoc
|
||||
:share-all-addresses? true
|
||||
:previous-share-all-addresses? true
|
||||
:previous-permission-addresses #{"0x1" "0x2"}
|
||||
:selected-permission-addresses #{"0x1" "0x2"}
|
||||
:airdrop-address "0x1")}]
|
||||
@@ -39,12 +41,16 @@
|
||||
:communities {community-id {:previous-permission-addresses #{"0x1" "0x2"}
|
||||
:selected-permission-addresses #{"0x1" "0x2"
|
||||
"0x3"}
|
||||
:share-all-addresses? true
|
||||
:previous-share-all-addresses? false
|
||||
:airdrop-address "0x1"}}}}
|
||||
expected-db {:db {:wallet wallet
|
||||
:communities {community-id {:previous-permission-addresses #{"0x1" "0x2"
|
||||
"0x3"}
|
||||
:selected-permission-addresses #{"0x1" "0x2"
|
||||
"0x3"}
|
||||
:share-all-addresses? true
|
||||
:previous-share-all-addresses? true
|
||||
:airdrop-address "0x1"}}}}]
|
||||
(is
|
||||
(match? expected-db
|
||||
@@ -61,6 +67,40 @@
|
||||
(match? expected-db
|
||||
(events/update-previous-permission-addresses initial-db [community-id]))))))
|
||||
|
||||
(deftest toggle-share-all-addresses-test
|
||||
(let [initial-db {:db {:wallet {:accounts {"0x1" {:address "0x1"
|
||||
:position 0}
|
||||
"0x2" {:address "0x2"
|
||||
:position 1}}}
|
||||
:communities {community-id {:share-all-addresses? true
|
||||
:previous-share-all-addresses? true
|
||||
:previous-permission-addresses #{"0x1"}
|
||||
:selected-permission-addresses #{"0x1"}
|
||||
:airdrop-address "0x1"}}}}
|
||||
expected-db (update-in initial-db
|
||||
[:db :communities community-id]
|
||||
assoc
|
||||
:previous-share-all-addresses? true
|
||||
:share-all-addresses?
|
||||
false)]
|
||||
(is (match? expected-db (events/toggle-share-all-addresses initial-db [community-id]))))
|
||||
(let [initial-db {:db {:wallet {:accounts {"0x1" {:address "0x1"
|
||||
:position 0}
|
||||
"0x2" {:address "0x2"
|
||||
:position 1}}}
|
||||
:communities {community-id {:share-all-addresses? false
|
||||
:previous-share-all-addresses? false
|
||||
:previous-permission-addresses #{"0x1"}
|
||||
:selected-permission-addresses #{"0x1"}
|
||||
:airdrop-address "0x1"}}}}
|
||||
expected-db (update-in initial-db
|
||||
[:db :communities community-id]
|
||||
assoc
|
||||
:selected-permission-addresses #{"0x1" "0x2"}
|
||||
:previous-share-all-addresses? false
|
||||
:share-all-addresses? true)]
|
||||
(is (match? expected-db (events/toggle-share-all-addresses initial-db [community-id])))))
|
||||
|
||||
(deftest fetch-community
|
||||
(testing "with community id"
|
||||
(testing "update fetching indicator in db"
|
||||
|
||||
@@ -10,16 +10,16 @@
|
||||
{:db (-> db
|
||||
(assoc-in [:community-channels-permissions community-id]
|
||||
(data-store/rpc->channel-permissions (:channels response)))
|
||||
(assoc-in [:communities community-id :checking-all-channels-permissions?] false))}))
|
||||
(assoc-in [:communities/channel-permissions-check community-id] false))}))
|
||||
|
||||
(rf/reg-event-fx :communities/check-all-community-channels-permissions-failed
|
||||
(fn [{:keys [db]} [community-id]]
|
||||
{:db (assoc-in db [:communities community-id :checking-all-channels-permissions?] false)}))
|
||||
{:db (assoc-in db [:communities/channel-permissions-check community-id] false)}))
|
||||
|
||||
(rf/reg-event-fx :communities/check-all-community-channels-permissions
|
||||
(fn [{:keys [db]} [community-id]]
|
||||
(when (get-in db [:communities community-id])
|
||||
{:db (assoc-in db [:communities community-id :checking-all-channels-permissions?] true)
|
||||
{:db (assoc-in db [:communities/channel-permissions-check community-id] true)
|
||||
:fx [[:json-rpc/call
|
||||
[{:method "wakuext_checkAllCommunityChannelsPermissions"
|
||||
:params [{:CommunityID community-id}]
|
||||
@@ -38,21 +38,19 @@
|
||||
(let [token-permissions-check (cond-> result
|
||||
based-on-client-selection? (assoc :based-on-client-selection? true))]
|
||||
{:db (-> db
|
||||
(assoc-in [:communities community-id :checking-permissions?] false)
|
||||
(assoc-in [:communities community-id :token-permissions-check]
|
||||
token-permissions-check))})))
|
||||
(assoc-in [:communities/permissions-check community-id]
|
||||
{:checking? false
|
||||
:check token-permissions-check}))})))
|
||||
|
||||
(rf/reg-event-fx :communities/check-permissions-to-join-community-failed
|
||||
(fn [{:keys [db]} [community-id]]
|
||||
{:db (assoc-in db [:communities community-id :checking-permissions?] false)}))
|
||||
{:db (assoc-in db [:communities/permissions-check community-id :checking?] false)}))
|
||||
|
||||
(rf/reg-event-fx :communities/check-permissions-to-join-community
|
||||
(fn [{:keys [db]} [community-id addresses based-on-client-selection?]]
|
||||
(when-let [community (get-in db [:communities community-id])]
|
||||
(when-not (:checking-permissions? community)
|
||||
{:db (-> db
|
||||
(assoc-in [:communities community-id :checking-permissions?] true)
|
||||
(assoc-in [:communities community-id :can-request-access?] false))
|
||||
{:db (assoc-in db [:communities/permissions-check community-id :checking?] true)
|
||||
:json-rpc/call [{:method "wakuext_checkPermissionsToJoinCommunity"
|
||||
:params [(cond-> {:communityId community-id}
|
||||
addresses
|
||||
|
||||
@@ -110,66 +110,82 @@
|
||||
[rn/view
|
||||
[quo/icon :i/info {:no-color true}]]])
|
||||
|
||||
(defn- network-not-supported
|
||||
[]
|
||||
[quo/text (i18n/label :t/network-not-supported)])
|
||||
|
||||
(defn- request-access-button
|
||||
[id color]
|
||||
[quo/button
|
||||
{:on-press (if config/community-accounts-selection-enabled?
|
||||
#(rf/dispatch [:open-modal :community-account-selection
|
||||
{:community-id id}])
|
||||
#(rf/dispatch [:open-modal :community-requests-to-join {:id id}]))
|
||||
:accessibility-label :show-request-to-join-screen-button
|
||||
:customization-color color
|
||||
:icon-left :i/communities}
|
||||
(i18n/label :t/request-to-join)])
|
||||
|
||||
(defn- token-requirements
|
||||
[{:keys [id color]}]
|
||||
[{:keys [id color role-permissions?]}]
|
||||
(let [{:keys [can-request-access?
|
||||
number-of-hold-tokens tokens
|
||||
no-member-permission?
|
||||
tokens
|
||||
networks-not-supported?
|
||||
highest-permission-role]} (rf/sub [:community/token-gated-overview id])
|
||||
highest-role-text
|
||||
(i18n/label
|
||||
(communities.utils/role->translation-key highest-permission-role :t/member))]
|
||||
[rn/view {:style (style/token-gated-container)}
|
||||
[rn/view
|
||||
{:style {:padding-horizontal 12
|
||||
:flex-direction :row
|
||||
:align-items :center
|
||||
:justify-content :space-between
|
||||
:flex 1}}
|
||||
[quo/text {:weight :medium}
|
||||
(if can-request-access?
|
||||
(i18n/label :t/you-eligible-to-join-as {:role highest-role-text})
|
||||
(i18n/label :t/you-not-eligible-to-join))]
|
||||
[info-button]]
|
||||
(when (pos? number-of-hold-tokens)
|
||||
(cond
|
||||
|
||||
networks-not-supported?
|
||||
[network-not-supported]
|
||||
|
||||
(or (not role-permissions?) no-member-permission?)
|
||||
[request-access-button id color]
|
||||
|
||||
:else
|
||||
[rn/view {:style (style/token-gated-container)}
|
||||
[rn/view
|
||||
{:style {:padding-horizontal 12
|
||||
:flex-direction :row
|
||||
:align-items :center
|
||||
:justify-content :space-between
|
||||
:flex 1}}
|
||||
[quo/text {:weight :medium}
|
||||
(if (and can-request-access? highest-permission-role)
|
||||
(i18n/label :t/you-eligible-to-join-as {:role highest-role-text})
|
||||
(i18n/label :t/you-not-eligible-to-join))]
|
||||
[info-button]]
|
||||
[quo/text {:style {:padding-horizontal 12 :padding-bottom 18} :size :paragraph-2}
|
||||
(if can-request-access?
|
||||
(i18n/label :t/you-hold-number-of-hold-tokens-of-these
|
||||
{:number-of-hold-tokens number-of-hold-tokens})
|
||||
(i18n/label :t/you-must-hold))])
|
||||
[quo/token-requirement-list
|
||||
{:tokens tokens
|
||||
:padding? true}]
|
||||
[quo/button
|
||||
{:on-press (if config/community-accounts-selection-enabled?
|
||||
#(rf/dispatch [:open-modal :community-account-selection
|
||||
{:community-id id}])
|
||||
#(rf/dispatch [:open-modal :community-requests-to-join {:id id}]))
|
||||
:accessibility-label :join-community-button
|
||||
:customization-color color
|
||||
:container-style {:margin-horizontal 12 :margin-top 12 :margin-bottom 12}
|
||||
:disabled? (not can-request-access?)
|
||||
:icon-left (if can-request-access? :i/unlocked :i/locked)}
|
||||
(i18n/label :t/join-open-community)]]))
|
||||
(i18n/label :t/you-hodl)
|
||||
(i18n/label :t/you-must-hold))]
|
||||
[quo/token-requirement-list
|
||||
{:tokens tokens
|
||||
:padding? true}]
|
||||
[quo/button
|
||||
{:on-press (if config/community-accounts-selection-enabled?
|
||||
#(rf/dispatch [:open-modal :community-account-selection
|
||||
{:community-id id}])
|
||||
#(rf/dispatch [:open-modal :community-requests-to-join {:id id}]))
|
||||
:accessibility-label :join-community-button
|
||||
:customization-color color
|
||||
:container-style {:margin-horizontal 12 :margin-top 12 :margin-bottom 12}
|
||||
:disabled? (not can-request-access?)
|
||||
:icon-left (if can-request-access? :i/unlocked :i/locked)}
|
||||
(i18n/label :t/join-open-community)]])))
|
||||
|
||||
|
||||
(defn- join-community
|
||||
[{:keys [id color joined permissions role-permissions? can-join?] :as community}]
|
||||
[{:keys [id joined permissions role-permissions? can-join?] :as community}]
|
||||
(let [pending? (rf/sub [:communities/my-pending-request-to-join id])
|
||||
access-type (get-access-type (:access permissions))
|
||||
unknown-access? (= access-type :unknown-access)
|
||||
invite-only? (= access-type :invite-only)]
|
||||
[:<>
|
||||
(when-not (or joined pending? invite-only? unknown-access?)
|
||||
(if role-permissions?
|
||||
[token-requirements community]
|
||||
[quo/button
|
||||
{:on-press (if config/community-accounts-selection-enabled?
|
||||
#(rf/dispatch [:open-modal :community-account-selection
|
||||
{:community-id id}])
|
||||
#(rf/dispatch [:open-modal :community-requests-to-join {:id id}]))
|
||||
:accessibility-label :show-request-to-join-screen-button
|
||||
:customization-color color
|
||||
:icon-left :i/communities}
|
||||
(i18n/label :t/request-to-join)]))
|
||||
[token-requirements community])
|
||||
(when (not (or pending? role-permissions? can-join?))
|
||||
[quo/text
|
||||
{:size :paragraph-2
|
||||
@@ -237,15 +253,17 @@
|
||||
:description-accessibility-label :community-description}])
|
||||
|
||||
(defn- community-content
|
||||
[{:keys [id]}]
|
||||
[id]
|
||||
(rf/dispatch [:communities/check-all-community-channels-permissions id])
|
||||
(fn [{:keys [name description joined spectated images tags color id membership-permissions?]
|
||||
:as community}
|
||||
(fn [id
|
||||
{:keys [on-category-layout
|
||||
collapsed?
|
||||
on-first-channel-height-changed]}]
|
||||
(let [joined-or-spectated (or joined spectated)
|
||||
chats-by-category (rf/sub [:communities/categorized-channels id])]
|
||||
(let [{:keys [name description joined spectated images tags color id membership-permissions?]
|
||||
:as community}
|
||||
(rf/sub [:communities/community id])
|
||||
joined-or-spectated (or joined spectated)
|
||||
chats-by-category (rf/sub [:communities/categorized-channels id])]
|
||||
[:<>
|
||||
[rn/view {:style style/community-content-container}
|
||||
(when-not collapsed?
|
||||
@@ -295,20 +313,24 @@
|
||||
(and (>= scroll-height (+ height first-channel-height))
|
||||
category)))))
|
||||
|
||||
(defn- community-scroll-page
|
||||
[{:keys [joined]}]
|
||||
(let [scroll-height (reagent/atom 0)
|
||||
categories-heights (reagent/atom {})
|
||||
first-channel-height (reagent/atom 0)
|
||||
;; We track the initial value of joined
|
||||
;; as we open the page to avoid switching
|
||||
;; from not collapsed to collapsed if the
|
||||
;; user is on this page
|
||||
initial-joined? joined]
|
||||
(fn [{:keys [id name images] :as community}]
|
||||
|
||||
(defn- community-scroll-page
|
||||
[_ initial-joined? _ _]
|
||||
(let [scroll-height (reagent/atom 0)
|
||||
categories-heights (reagent/atom {})
|
||||
first-channel-height (reagent/atom 0)
|
||||
on-category-layout (partial add-category-height categories-heights)
|
||||
on-first-channel-height-changed (fn [height categories]
|
||||
(swap! categories-heights select-keys categories)
|
||||
(reset! first-channel-height height))]
|
||||
(fn [id joined name images]
|
||||
(let [cover {:uri (get-in images [:banner :uri])}
|
||||
logo {:uri (get-in images [:thumbnail :uri])}
|
||||
collapsed? (and initial-joined? (:joined community))
|
||||
collapsed? (and initial-joined? joined)
|
||||
overlay-shown? (boolean (:sheets (rf/sub [:bottom-sheet])))]
|
||||
[scroll-page/scroll-page
|
||||
{:cover-image cover
|
||||
@@ -331,24 +353,22 @@
|
||||
@first-channel-height
|
||||
@categories-heights)}]}
|
||||
[community-content
|
||||
community
|
||||
{:on-category-layout (partial add-category-height categories-heights)
|
||||
id
|
||||
{:on-category-layout on-category-layout
|
||||
:collapsed? collapsed?
|
||||
:on-first-channel-height-changed
|
||||
;; Here we set the height of the component and we filter out the categories, as some
|
||||
;; might have been removed.
|
||||
(fn [height categories]
|
||||
(swap! categories-heights select-keys categories)
|
||||
(reset! first-channel-height height))}]]))))
|
||||
on-first-channel-height-changed}]]))))
|
||||
|
||||
(defn- community-card-page-view
|
||||
[id]
|
||||
(let [{:keys [id joined]
|
||||
(let [{:keys [id joined name images]
|
||||
:as community} (rf/sub [:communities/community id])]
|
||||
(when community
|
||||
(when joined
|
||||
(rf/dispatch [:activity-center.notifications/dismiss-community-overview id]))
|
||||
[community-scroll-page community])))
|
||||
[community-scroll-page id joined name images])))
|
||||
|
||||
(defn view
|
||||
[id]
|
||||
|
||||
@@ -11,3 +11,10 @@
|
||||
constants/community-token-permission-become-admin :t/admin
|
||||
constants/community-token-permission-become-member :t/member
|
||||
fallback-to)))
|
||||
|
||||
(defn sorted-non-watch-only-accounts
|
||||
[db]
|
||||
(->> (get-in db [:wallet :accounts])
|
||||
(vals)
|
||||
(remove :watch-only?)
|
||||
(sort-by :position)))
|
||||
|
||||
@@ -8,30 +8,8 @@
|
||||
(def heading-subtitle {:color colors/white})
|
||||
(def heading-title (assoc heading-subtitle :margin-bottom 8))
|
||||
|
||||
(def label-container
|
||||
{:margin-top 8
|
||||
:flex-direction :row
|
||||
:align-items :center
|
||||
:height 16})
|
||||
|
||||
(def label-icon
|
||||
{:width 16
|
||||
:height 18
|
||||
:margin-right 4})
|
||||
|
||||
(defn label-icon-color
|
||||
[status]
|
||||
(get {:neutral colors/neutral-40
|
||||
:success colors/success-60
|
||||
:danger colors/danger-60}
|
||||
status))
|
||||
|
||||
(defn label-color
|
||||
[status]
|
||||
(let [colors {:neutral colors/white-opa-70
|
||||
:success colors/success-60
|
||||
:danger colors/danger-60}]
|
||||
{:color (get colors status)}))
|
||||
(def info-message
|
||||
{:margin-top 8})
|
||||
|
||||
(def space-between-inputs {:height 16})
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
(:require
|
||||
[oops.core :refer [ocall]]
|
||||
[quo.core :as quo]
|
||||
[quo.foundations.colors :as colors]
|
||||
[react-native.core :as rn]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[reagent.core :as reagent]
|
||||
@@ -33,24 +34,25 @@
|
||||
(dissoc :hint)
|
||||
(assoc :type :password
|
||||
:blur? true))]
|
||||
[rn/view {:style style/label-container}
|
||||
[rn/view {:style style/info-message}
|
||||
(when shown
|
||||
[:<>
|
||||
[quo/icon (if (= status :success) :i/check-circle :i/info)
|
||||
{:container-style style/label-icon
|
||||
:color (style/label-icon-color status)
|
||||
:size 16}]
|
||||
[quo/text
|
||||
{:style (style/label-color status)
|
||||
:size :paragraph-2}
|
||||
text]])]])
|
||||
[quo/info-message
|
||||
{:type status
|
||||
:size :default
|
||||
:icon (if (= status :success) :i/positive-state :i/info)
|
||||
:text-color (when (= status :default)
|
||||
colors/white-70-blur)
|
||||
:icon-color (when (= status :default)
|
||||
colors/white-70-blur)
|
||||
:style {}}
|
||||
text])]])
|
||||
|
||||
(defn password-inputs
|
||||
[{:keys [passwords-match? on-change-password on-change-repeat-password on-input-focus
|
||||
password-long-enough? empty-password? show-password-validation?
|
||||
on-blur-repeat-password]}]
|
||||
(let [hint-1-status (if password-long-enough? :success :neutral)
|
||||
hint-2-status (if passwords-match? :success :danger)
|
||||
(let [hint-1-status (if password-long-enough? :success :default)
|
||||
hint-2-status (if passwords-match? :success :error)
|
||||
hint-2-text (if passwords-match?
|
||||
(i18n/label :t/password-creation-match)
|
||||
(i18n/label :t/password-creation-dont-match))
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
(ns status-im.contexts.preview.quo.tags.collectible-tag
|
||||
(:require
|
||||
[oops.core :refer [oget]]
|
||||
[quo.core :as quo]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]
|
||||
@@ -16,9 +15,7 @@
|
||||
:value "Size 32"}]}
|
||||
{:key :options
|
||||
:type :select
|
||||
:options [{:key false
|
||||
:value false}
|
||||
{:key :add
|
||||
:options [{:key :add
|
||||
:value :add}
|
||||
{:key :hold
|
||||
:value :hold}]}
|
||||
@@ -31,16 +28,11 @@
|
||||
|
||||
(defn view
|
||||
[]
|
||||
(let [state (reagent/atom {:size :size-24
|
||||
:collectible-name "Collectible"
|
||||
:collectible-id "#123"
|
||||
:collectible-img-src (resources/mock-images :collectible)
|
||||
:options false
|
||||
:blur? false
|
||||
:container-width 0})
|
||||
on-layout #(swap! state assoc
|
||||
:container-width
|
||||
(oget % :nativeEvent :layout :width))]
|
||||
(let [state (reagent/atom {:size :size-24
|
||||
:collectible-name "Collectible"
|
||||
:collectible-id "#123"
|
||||
:collectible-img-src (resources/mock-images :collectible)
|
||||
:blur? false})]
|
||||
(fn []
|
||||
[preview/preview-container
|
||||
{:state state
|
||||
@@ -48,4 +40,4 @@
|
||||
:show-blur-background? true
|
||||
:descriptor descriptor}
|
||||
[rn/view {:style {:align-items :center}}
|
||||
[quo/collectible-tag (assoc @state :on-layout on-layout)]]])))
|
||||
[quo/collectible-tag @state]]])))
|
||||
|
||||
@@ -24,7 +24,8 @@
|
||||
{:key :empty-token-list}
|
||||
{:key :1-token}
|
||||
{:key :3-tokens}
|
||||
{:key :5-tokens}]}])
|
||||
{:key :5-tokens}]}
|
||||
(preview/customization-color-option {:key :customization-color})])
|
||||
|
||||
(def ^:private token-details
|
||||
{:no-tokens nil
|
||||
@@ -50,23 +51,25 @@
|
||||
|
||||
(defn view
|
||||
[]
|
||||
(let [state (reagent/atom {:name "Trip to Vegas"
|
||||
:address "0x2f0fbf0a93c5999e9b4410848403a02b38983eb2"
|
||||
:emoji "😊"
|
||||
:account-color :blue
|
||||
:token-details :no-tokens
|
||||
:keycard? true
|
||||
:checked? true
|
||||
:disabled? false})]
|
||||
(let [state (reagent/atom {:name "Trip to Vegas"
|
||||
:address "0x2f0fbf0a93c5999e9b4410848403a02b38983eb2"
|
||||
:emoji "😊"
|
||||
:account-color :blue
|
||||
:token-details :no-tokens
|
||||
:customization-color :blue
|
||||
:keycard? true
|
||||
:checked? true
|
||||
:disabled? false})]
|
||||
(fn []
|
||||
[preview/preview-container {:state state :descriptor descriptor}
|
||||
[quo/account-permissions
|
||||
{:account {:name (:name @state)
|
||||
:address (:address @state)
|
||||
:emoji (:emoji @state)
|
||||
:customization-color (:account-color @state)}
|
||||
:token-details (token-details (:token-details @state))
|
||||
:keycard? (:keycard? @state)
|
||||
:checked? (:checked? @state)
|
||||
:disabled? (:disabled? @state)
|
||||
:on-change (fn [checked?] (swap! state assoc :checked? checked?))}]])))
|
||||
{:account {:name (:name @state)
|
||||
:address (:address @state)
|
||||
:emoji (:emoji @state)
|
||||
:customization-color (:account-color @state)}
|
||||
:token-details (token-details (:token-details @state))
|
||||
:keycard? (:keycard? @state)
|
||||
:checked? (:checked? @state)
|
||||
:disabled? (:disabled? @state)
|
||||
:customization-color (:customization-color @state)
|
||||
:on-change (fn [checked?] (swap! state assoc :checked? checked?))}]])))
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
(stop-interval)))
|
||||
[(:state @state)])
|
||||
[preview/preview-container {:state state :descriptor descriptor}
|
||||
[quo/confirmation-propgress
|
||||
[quo/confirmation-progress
|
||||
(assoc @state
|
||||
:counter
|
||||
@counter)]]))
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
:upstreamConfig config/default-network-rpc-url
|
||||
:networkId config/default-network-id
|
||||
:currentNetwork config/default-network
|
||||
:wakuV2LightClient true
|
||||
:wakuV2LightClient false
|
||||
:previewPrivacy config/blank-preview?})))
|
||||
|
||||
(defn strip-file-prefix
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
(ns status-im.contexts.profile.edit.bio.events
|
||||
(:require [utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(rf/reg-event-fx :profile/edit-profile-bio-success
|
||||
(fn [_]
|
||||
{:fx [[:dispatch [:navigate-back]]
|
||||
[:dispatch
|
||||
[:toasts/upsert
|
||||
{:type :positive
|
||||
:theme :dark
|
||||
:text (i18n/label :t/bio-added)}]]]}))
|
||||
|
||||
(defn edit-profile-bio
|
||||
[{:keys [db]} [bio]]
|
||||
{:db (assoc-in db [:profile/profile :bio] bio)
|
||||
:fx [[:json-rpc/call
|
||||
[{:method "wakuext_setBio"
|
||||
:params [bio]
|
||||
:on-success [:profile/edit-profile-bio-success]}]]]})
|
||||
|
||||
(rf/reg-event-fx :profile/edit-bio edit-profile-bio)
|
||||
@@ -0,0 +1,15 @@
|
||||
(ns status-im.contexts.profile.edit.bio.events-test
|
||||
(:require [cljs.test :refer [deftest is]]
|
||||
matcher-combinators.test
|
||||
[status-im.contexts.profile.edit.bio.events :as sut]))
|
||||
|
||||
(deftest edit-bio-test
|
||||
(let [new-bio "New Bio text"
|
||||
cofx {:db {:profile/profile {:bio "Old Bio text"}}}
|
||||
expected {:db {:profile/profile {:bio new-bio}}
|
||||
:fx [[:json-rpc/call
|
||||
[{:method "wakuext_setBio"
|
||||
:params [new-bio]
|
||||
:on-success [:profile/edit-profile-bio-success]}]]]}]
|
||||
(is (match? expected
|
||||
(sut/edit-profile-bio cofx [new-bio])))))
|
||||
@@ -0,0 +1,17 @@
|
||||
(ns status-im.contexts.profile.edit.bio.style)
|
||||
|
||||
(defn page-wrapper
|
||||
[insets]
|
||||
{:padding-top (:top insets)
|
||||
:padding-bottom (:bottom insets)
|
||||
:padding-horizontal 1
|
||||
:flex 1})
|
||||
|
||||
(def screen-container
|
||||
{:flex 1
|
||||
:padding-top 14
|
||||
:padding-horizontal 20
|
||||
:justify-content :space-between})
|
||||
|
||||
(def button-wrapper
|
||||
{:margin-vertical 12})
|
||||
@@ -0,0 +1,71 @@
|
||||
(ns status-im.contexts.profile.edit.bio.view
|
||||
(:require [clojure.string :as string]
|
||||
[quo.core :as quo]
|
||||
[react-native.core :as rn]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.common.validation.profile :as profile-validator]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.contexts.profile.edit.bio.style :as style]
|
||||
[utils.debounce :as debounce]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn view
|
||||
[]
|
||||
(let [insets (safe-area/get-insets)
|
||||
profile (rf/sub [:profile/profile-with-image])
|
||||
customization-color (rf/sub [:profile/customization-color])
|
||||
profile-bio (:bio profile)
|
||||
unsaved-bio (reagent/atom profile-bio)
|
||||
error-msg (reagent/atom nil)
|
||||
typing? (reagent/atom false)
|
||||
validate-bio (debounce/debounce (fn [bio]
|
||||
(reset! error-msg
|
||||
(profile-validator/validation-bio bio))
|
||||
(reset! typing? false))
|
||||
300)
|
||||
on-change-text (fn [s]
|
||||
(reset! typing? true)
|
||||
(reset! unsaved-bio s)
|
||||
(validate-bio s))]
|
||||
(fn []
|
||||
[quo/overlay
|
||||
{:type :shell
|
||||
:container-style (style/page-wrapper insets)}
|
||||
[quo/page-nav
|
||||
{:key :header
|
||||
:background :blur
|
||||
:icon-name :i/arrow-left
|
||||
:on-press #(rf/dispatch [:navigate-back])}]
|
||||
[rn/keyboard-avoiding-view
|
||||
{:key :content
|
||||
:style style/screen-container}
|
||||
[rn/view {:style {:gap 22}}
|
||||
[quo/text-combinations {:title (i18n/label :t/bio)}]
|
||||
[quo/input
|
||||
{:theme :dark
|
||||
:blur? true
|
||||
:multiline? true
|
||||
:error? (not (string/blank? @error-msg))
|
||||
:container-style {:margin-bottom -11}
|
||||
:default-value @unsaved-bio
|
||||
:auto-focus true
|
||||
:char-limit constants/profile-bio-max-length
|
||||
:label (i18n/label :t/profile-bio)
|
||||
:placeholder (i18n/label :t/something-about-you)
|
||||
:on-change-text on-change-text}]
|
||||
(when-not (string/blank? @error-msg)
|
||||
[quo/info-message
|
||||
{:type :error
|
||||
:size :default
|
||||
:icon :i/info}
|
||||
@error-msg])]
|
||||
[rn/view {:style style/button-wrapper}
|
||||
[quo/button
|
||||
{:type :primary
|
||||
:customization-color customization-color
|
||||
:on-press (fn []
|
||||
(rf/dispatch [:profile/edit-bio @unsaved-bio]))
|
||||
:disabled? (boolean (or @typing? (not (string/blank? @error-msg))))}
|
||||
(i18n/label :t/save-bio)]]]])))
|
||||
@@ -1,5 +1,6 @@
|
||||
(ns status-im.contexts.profile.edit.list-items
|
||||
(:require [quo.foundations.colors :as colors]
|
||||
(:require [legacy.status-im.utils.core :as utils]
|
||||
[quo.foundations.colors :as colors]
|
||||
[status-im.common.not-implemented :as not-implemented]
|
||||
[status-im.contexts.profile.edit.style :as style]
|
||||
[status-im.contexts.profile.utils :as profile.utils]
|
||||
@@ -10,6 +11,7 @@
|
||||
[theme]
|
||||
(let [profile (rf/sub [:profile/profile-with-image])
|
||||
customization-color (rf/sub [:profile/customization-color])
|
||||
bio (:bio profile)
|
||||
full-name (profile.utils/displayed-name profile)]
|
||||
[{:label (i18n/label :t/profile)
|
||||
:items [{:title (i18n/label :t/name)
|
||||
@@ -20,8 +22,10 @@
|
||||
:action :arrow
|
||||
:container-style style/item-container}
|
||||
{:title (i18n/label :t/bio)
|
||||
:on-press not-implemented/alert
|
||||
:on-press #(rf/dispatch [:open-modal :edit-bio])
|
||||
:blur? true
|
||||
:label :text
|
||||
:label-props (utils/truncate-str bio 15)
|
||||
:action :arrow
|
||||
:container-style style/item-container}
|
||||
{:title (i18n/label :t/accent-colour)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
[native-module.core :as native-module]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.contexts.profile.edit.accent-colour.events]
|
||||
[status-im.contexts.profile.edit.bio.events]
|
||||
[status-im.contexts.profile.edit.header.events]
|
||||
[status-im.contexts.profile.edit.name.events]
|
||||
[status-im.contexts.profile.login.events :as profile.login]
|
||||
|
||||
@@ -1,36 +1,34 @@
|
||||
(ns ^:workletize status-im.contexts.profile.settings.header.avatar
|
||||
(ns status-im.contexts.profile.settings.header.avatar
|
||||
(:require [quo.core :as quo]
|
||||
[quo.theme :as quo.theme]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[status-im.contexts.profile.settings.header.style :as style]))
|
||||
|
||||
(def scroll-animation-input-range [0 50])
|
||||
(def header-extrapolation-option
|
||||
{:extrapolateLeft "clamp"
|
||||
:extrapolateRight "clamp"})
|
||||
|
||||
(defn f-avatar
|
||||
[{:keys [scroll-y full-name online? profile-picture customization-color]}]
|
||||
(let [avatar-total-size 88
|
||||
border-height 4
|
||||
translation-to-bottom (fn [scale inverse-scale]
|
||||
(js* "'worklet'")
|
||||
(let [current-avatar-size (* avatar-total-size (- 1 scale))
|
||||
current-center (/ current-avatar-size 2)]
|
||||
(* current-center inverse-scale)))
|
||||
transform (reanimated/use-animated-style
|
||||
(fn []
|
||||
(js* "'worklet'")
|
||||
(let [scale (reanimated/interpolate*
|
||||
(.-value scroll-y)
|
||||
#js [0 48]
|
||||
#js [1 0.4]
|
||||
"clamp")
|
||||
inverse-scale (/ 1 scale)
|
||||
translation (translation-to-bottom scale inverse-scale)
|
||||
bottom (* border-height inverse-scale (- 1 scale))]
|
||||
#js
|
||||
{:transform #js
|
||||
[#js {:scale scale}
|
||||
#js {:translateX (- translation)}
|
||||
#js {:translateY (- translation bottom)}]})))
|
||||
theme (quo.theme/get-theme)]
|
||||
[reanimated/view {:style [transform (style/avatar-container theme)]}
|
||||
(let [image-scale-animation (reanimated/interpolate scroll-y
|
||||
scroll-animation-input-range
|
||||
[1 0.4]
|
||||
header-extrapolation-option)
|
||||
image-top-margin-animation (reanimated/interpolate scroll-y
|
||||
scroll-animation-input-range
|
||||
[0 20]
|
||||
header-extrapolation-option)
|
||||
image-side-margin-animation (reanimated/interpolate scroll-y
|
||||
scroll-animation-input-range
|
||||
[0 -20]
|
||||
header-extrapolation-option)
|
||||
theme (quo.theme/get-theme)]
|
||||
[reanimated/view
|
||||
{:style (style/avatar-container theme
|
||||
image-scale-animation
|
||||
image-top-margin-animation
|
||||
image-side-margin-animation)}
|
||||
[quo/user-avatar
|
||||
{:full-name full-name
|
||||
:online? online?
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
(ns ^:workletize status-im.contexts.profile.settings.header.header-shape
|
||||
(ns status-im.contexts.profile.settings.header.header-shape
|
||||
(:require [quo.foundations.colors :as colors]
|
||||
[react-native.core :as rn]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[react-native.svg :as svg]
|
||||
[status-im.contexts.profile.settings.header.style :as style]))
|
||||
|
||||
|
||||
(defn left-radius
|
||||
[background-color]
|
||||
[svg/svg {:width "20" :height "20" :viewBox "0 0 20 20" :fill "none"}
|
||||
@@ -22,17 +21,13 @@
|
||||
|
||||
(defn f-view
|
||||
[{:keys [scroll-y customization-color theme]}]
|
||||
(let [background-color (colors/resolve-color customization-color theme 40)
|
||||
opacity (reanimated/use-animated-style
|
||||
(fn []
|
||||
(js* "'worklet'")
|
||||
#js
|
||||
{:opacity (reanimated/interpolate* (.-value scroll-y)
|
||||
#js [0 45 50]
|
||||
#js [1 1 0])}))]
|
||||
(let [background-color (colors/resolve-color customization-color theme 40)
|
||||
opacity-animation (reanimated/interpolate scroll-y
|
||||
[0 45 50]
|
||||
[1 1 0])]
|
||||
[:<>
|
||||
[rn/view {:style (style/header-middle-shape background-color)}]
|
||||
[reanimated/view {:style [opacity style/radius-container]}
|
||||
[reanimated/view {:style (style/radius-container opacity-animation)}
|
||||
[left-radius background-color]
|
||||
[right-radius background-color]]]))
|
||||
|
||||
|
||||
@@ -27,13 +27,19 @@
|
||||
:height 48
|
||||
:flex-grow 1})
|
||||
|
||||
(def radius-container
|
||||
{:flex-direction :row
|
||||
(defn radius-container
|
||||
[opacity-animation]
|
||||
{:opacity opacity-animation
|
||||
:flex-direction :row
|
||||
:justify-content :space-between})
|
||||
|
||||
(defn avatar-container
|
||||
[theme]
|
||||
{:align-items :flex-start
|
||||
:border-width 4
|
||||
:border-color (colors/theme-colors colors/border-avatar-light colors/neutral-80-opa-80 theme)
|
||||
:border-radius 44})
|
||||
[theme scale-animation top-margin-animation side-margin-animation]
|
||||
[{:transform [{:scale scale-animation}]
|
||||
:margin-top top-margin-animation
|
||||
:margin-left side-margin-animation
|
||||
:margin-bottom side-margin-animation}
|
||||
{:align-items :flex-start
|
||||
:border-width 4
|
||||
:border-color (colors/theme-colors colors/border-avatar-light colors/neutral-80-opa-80 theme)
|
||||
:border-radius 100}])
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
[:visibility-status-updates/visibility-status-update
|
||||
public-key])
|
||||
customization-color (rf/sub [:profile/customization-color])
|
||||
bio (:bio profile)
|
||||
full-name (profile.utils/displayed-name profile)
|
||||
profile-picture (profile.utils/photo profile)
|
||||
emoji-string (string/join emoji-hash)
|
||||
@@ -53,6 +54,7 @@
|
||||
{:title-accessibility-label :username
|
||||
:container-style style/title-container
|
||||
:emoji-hash emoji-string
|
||||
:description bio
|
||||
:title full-name}]]))
|
||||
|
||||
(def view (quo.theme/with-theme f-view))
|
||||
|
||||
@@ -40,9 +40,10 @@
|
||||
:size 32
|
||||
:accessibility-label :activity-center-open-more
|
||||
:on-press #(rf/dispatch [:show-bottom-sheet
|
||||
{:content drawer/options
|
||||
:theme :dark
|
||||
:shell? true}])}
|
||||
{:content drawer/options
|
||||
:theme :dark
|
||||
:shell? true
|
||||
:blur-radius 20}])}
|
||||
:i/options]]
|
||||
[quo/text
|
||||
{:size :heading-1
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
(ns status-im.contexts.shell.qr-reader.view
|
||||
(:require
|
||||
[clojure.string :as string]
|
||||
[react-native.core :as rn]
|
||||
[react-native.hooks :as hooks]
|
||||
[status-im.common.router :as router]
|
||||
[status-im.common.scan-qr-code.view :as scan-qr-code]
|
||||
[status-im.common.validators :as validators]
|
||||
[status-im.contexts.communities.events]
|
||||
[status-im.contexts.wallet.common.validation :as wallet-validation]
|
||||
[utils.debounce :as debounce]
|
||||
[utils.ethereum.eip.eip681 :as eip681]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.url :as url]))
|
||||
|
||||
(def invalid-qr-toast
|
||||
{:type :negative
|
||||
:theme :dark
|
||||
:text (i18n/label :t/invalid-qr)})
|
||||
|
||||
(defn- text-for-url-path?
|
||||
[text path]
|
||||
(some #(string/starts-with? text %) (router/path-urls path)))
|
||||
|
||||
(defn- extract-id
|
||||
[scanned-text]
|
||||
(let [index (string/index-of scanned-text "#")]
|
||||
(subs scanned-text (inc index))))
|
||||
|
||||
(defn eth-address?
|
||||
[scanned-text]
|
||||
(wallet-validation/eth-address? scanned-text))
|
||||
|
||||
(defn eip681-address?
|
||||
[scanned-text]
|
||||
(-> scanned-text
|
||||
eip681/parse-uri
|
||||
:address
|
||||
boolean))
|
||||
|
||||
(defn pairing-qr-code?
|
||||
[_]
|
||||
false)
|
||||
|
||||
(defn wallet-connect-code?
|
||||
[scanned-text]
|
||||
(string/starts-with? scanned-text "wc:"))
|
||||
|
||||
(defn url?
|
||||
[scanned-text]
|
||||
(url/url? scanned-text))
|
||||
|
||||
(defn load-and-show-profile
|
||||
[address]
|
||||
(debounce/debounce-and-dispatch
|
||||
[:contacts/set-new-identity
|
||||
{:input address
|
||||
:build-success-fn (fn [{:keys [public-key ens-name]}]
|
||||
{:dispatch-n [[:chat.ui/show-profile public-key ens-name]
|
||||
[:contacts/clear-new-identity]]})
|
||||
:failure-fn (fn []
|
||||
{:dispatch [:toasts/upsert invalid-qr-toast]})}]
|
||||
300))
|
||||
|
||||
(defn show-invalid-qr-toast
|
||||
[]
|
||||
(debounce/debounce-and-dispatch
|
||||
[:toasts/upsert invalid-qr-toast]
|
||||
300))
|
||||
|
||||
(defn on-qr-code-scanned
|
||||
[scanned-text]
|
||||
(cond
|
||||
(text-for-url-path? scanned-text router/community-with-data-path)
|
||||
;; TODO: https://github.com/status-im/status-mobile/issues/18743
|
||||
nil
|
||||
|
||||
(text-for-url-path? scanned-text router/channel-path)
|
||||
;; TODO: https://github.com/status-im/status-mobile/issues/18743
|
||||
nil
|
||||
|
||||
(text-for-url-path? scanned-text router/user-with-data-path)
|
||||
(let [address (extract-id scanned-text)]
|
||||
(load-and-show-profile address))
|
||||
|
||||
(or (validators/valid-public-key? scanned-text)
|
||||
(validators/valid-compressed-key? scanned-text))
|
||||
(load-and-show-profile scanned-text)
|
||||
|
||||
(eth-address? scanned-text)
|
||||
(debounce/debounce-and-dispatch [:navigate-to :wallet-accounts scanned-text] 300)
|
||||
|
||||
(eip681-address? scanned-text)
|
||||
(do
|
||||
(debounce/debounce-and-dispatch [:wallet-legacy/request-uri-parsed
|
||||
(eip681/parse-uri scanned-text)]
|
||||
300)
|
||||
(debounce/debounce-and-dispatch [:navigate-change-tab :wallet-stack] 300))
|
||||
|
||||
(pairing-qr-code? scanned-text)
|
||||
;; TODO: https://github.com/status-im/status-mobile/issues/18744
|
||||
nil
|
||||
|
||||
(wallet-connect-code? scanned-text)
|
||||
;; WalletConnect is not working yet, this flow should be updated once WalletConnect is ready
|
||||
nil
|
||||
|
||||
(url? scanned-text)
|
||||
(debounce/debounce-and-dispatch [:browser.ui/open-url scanned-text] 300)
|
||||
|
||||
:else
|
||||
(show-invalid-qr-toast)))
|
||||
|
||||
(defn- f-internal-view
|
||||
[]
|
||||
(let [{:keys [keyboard-shown]} (hooks/use-keyboard)]
|
||||
[:<>
|
||||
(when keyboard-shown
|
||||
(rn/dismiss-keyboard!))
|
||||
[scan-qr-code/view
|
||||
{:title (i18n/label :t/scan-qr)
|
||||
:on-success-scan on-qr-code-scanned}]]))
|
||||
|
||||
(defn view
|
||||
[]
|
||||
[:f> f-internal-view])
|
||||
@@ -29,7 +29,9 @@
|
||||
:background :blur
|
||||
:size 32
|
||||
:accessibility-label :shell-scan-button
|
||||
:on-press #(rf/dispatch [:navigate-back])}
|
||||
:on-press (fn []
|
||||
(rf/dispatch [:navigate-back])
|
||||
(rf/dispatch [:open-modal :shell-qr-reader]))}
|
||||
:i/scan]]
|
||||
[quo/text
|
||||
{:size :heading-1
|
||||
|
||||
@@ -31,4 +31,3 @@
|
||||
(rf/dispatch [:wallet/bridge-select-token
|
||||
{:token token
|
||||
:stack-id :wallet-bridge}]))}]])))
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
(ns status-im.contexts.wallet.account.bridge-send.style)
|
||||
|
||||
(def bridge-send-wrapper
|
||||
{:flex 1})
|
||||
@@ -0,0 +1,19 @@
|
||||
(ns status-im.contexts.wallet.account.bridge-send.view
|
||||
(:require
|
||||
[quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[status-im.contexts.wallet.account.bridge-send.style :as style]
|
||||
[status-im.contexts.wallet.send.input-amount.view :as input-amount]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn- view-internal
|
||||
[]
|
||||
[rn/view {:style style/bridge-send-wrapper}
|
||||
[input-amount/view
|
||||
{:button-one-label (i18n/label :t/confirm-bridge)
|
||||
:button-one-props {:icon-left :i/bridge}
|
||||
:on-navigate-back (fn []
|
||||
(rf/dispatch [:navigate-back-within-stack :wallet-bridge-send]))}]])
|
||||
|
||||
(def view (quo.theme/with-theme view-internal))
|
||||
@@ -28,7 +28,10 @@
|
||||
{:label (name network-name)
|
||||
:network-image (quo.resources/get-network (:network-name network))
|
||||
:token-value (str crypto-formatted " " (:symbol token))
|
||||
:fiat-value fiat-formatted}])))
|
||||
:fiat-value fiat-formatted
|
||||
:on-press #(rf/dispatch [:wallet/select-bridge-network
|
||||
{:network-chain-id chain-id
|
||||
:stack-id :wallet-bridge}])}])))
|
||||
|
||||
(defn- view-internal
|
||||
[]
|
||||
@@ -50,7 +53,7 @@
|
||||
:accessibility-label :top-bar}]
|
||||
[quo/text-combinations
|
||||
{:container-style style/header-container
|
||||
:title (i18n/label :t/bridge-to {:name (string/upper-case (str (:label token)))})}]
|
||||
:title (i18n/label :t/bridge-to {:name (string/upper-case (str (:name token)))})}]
|
||||
[rn/view style/content-container
|
||||
[bridge-token-component (assoc mainnet :network-name :t/mainnet) account-token]]
|
||||
|
||||
|
||||
@@ -38,7 +38,9 @@
|
||||
(let [{:keys [name color formatted-balance
|
||||
watch-only?]} (rf/sub [:wallet/current-viewing-account])]
|
||||
[rn/view {:style {:flex 1}}
|
||||
[account-switcher/view {:on-press #(rf/dispatch [:wallet/close-account-page])}]
|
||||
[account-switcher/view
|
||||
{:type :wallet-networks
|
||||
:on-press #(rf/dispatch [:wallet/close-account-page])}]
|
||||
[quo/account-overview
|
||||
{:current-value formatted-balance
|
||||
:account-name name
|
||||
@@ -54,7 +56,7 @@
|
||||
:buy-action #(rf/dispatch [:show-bottom-sheet
|
||||
{:content buy-drawer}])
|
||||
:bridge-action #(ff/alert ::ff/wallet.bridge-token
|
||||
(fn [] (rf/dispatch [:open-modal :wallet-bridge])))}])
|
||||
(fn [] (rf/dispatch [:wallet/start-bridge])))}])
|
||||
[quo/tabs
|
||||
{:style style/tabs
|
||||
:size 32
|
||||
|
||||
+6
-12
@@ -1,20 +1,14 @@
|
||||
(ns status-im.contexts.wallet.add-address-to-watch.confirm-address.component-spec
|
||||
(:require
|
||||
[status-im.contexts.wallet.add-address-to-watch.confirm-address.view :as confirm-address]
|
||||
[test-helpers.component :as h]
|
||||
[utils.re-frame :as rf]))
|
||||
[test-helpers.component :as h]))
|
||||
|
||||
(h/describe "Add Watch Only Account Page"
|
||||
(h/setup-restorable-re-frame)
|
||||
|
||||
(h/test "Create Account button is disabled while no account name exists"
|
||||
(let [callback (h/mock-fn)]
|
||||
(with-redefs [rf/dispatch #(callback)]
|
||||
(h/setup-subs {:profile/wallet-accounts []
|
||||
:get-screen-params {:address "0xmock-address"}})
|
||||
(h/render [confirm-address/view {}])
|
||||
(h/is-truthy (h/get-by-text "0xmock-address"))
|
||||
(h/was-not-called callback)
|
||||
(h/fire-event :change-text (h/get-by-label-text :profile-title-input) "NAME")
|
||||
(h/fire-event :press (h/get-by-translation-text :t/add-watched-address))
|
||||
(h/was-called callback)))))
|
||||
(h/setup-subs {:wallet/watch-only-accounts []
|
||||
:get-screen-params {:address "0xmock-address"}})
|
||||
(h/render [confirm-address/view])
|
||||
(h/is-truthy (h/get-by-text "0xmock-address"))
|
||||
(h/is-disabled (h/get-by-label-text :confirm-button-label))))
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user