Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b52afcf5a | ||
|
|
1f7686d798 | ||
|
|
052331d06b | ||
|
|
b380877f86 | ||
|
|
d0d12b68b1 | ||
|
|
4a4c783cf7 | ||
|
|
1065f780e7 |
@@ -1,5 +0,0 @@
|
||||
{:lint-as
|
||||
{rewrite-clj.zip/subedit-> clojure.core/->
|
||||
rewrite-clj.zip/subedit->> clojure.core/->>
|
||||
rewrite-clj.zip/edit-> clojure.core/->
|
||||
rewrite-clj.zip/edit->> clojure.core/->>}}
|
||||
@@ -1 +0,0 @@
|
||||
{:hooks {:analyze-call {taoensso.encore/defalias taoensso.encore/defalias}}}
|
||||
@@ -1,16 +0,0 @@
|
||||
(ns taoensso.encore
|
||||
(:require
|
||||
[clj-kondo.hooks-api :as hooks]))
|
||||
|
||||
(defn defalias [{:keys [node]}]
|
||||
(let [[sym-raw src-raw] (rest (:children node))
|
||||
src (if src-raw src-raw sym-raw)
|
||||
sym (if src-raw
|
||||
sym-raw
|
||||
(symbol (name (hooks/sexpr src))))]
|
||||
{:node (with-meta
|
||||
(hooks/list-node
|
||||
[(hooks/token-node 'def)
|
||||
(hooks/token-node (hooks/sexpr sym))
|
||||
(hooks/token-node (hooks/sexpr src))])
|
||||
(meta src))}))
|
||||
@@ -194,3 +194,8 @@ test/appium/tests/users.py
|
||||
|
||||
## git hooks
|
||||
lefthook.yml
|
||||
|
||||
## clj-kondo
|
||||
/.clj-kondo/taoensso/*
|
||||
/.clj-kondo/babashka/*
|
||||
/.clj-kondo/rewrite-clj/rewrite-clj/config.edn
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
;; -*- mode: clojure -*-
|
||||
;; vi: ft=clojure
|
||||
{:width 105
|
||||
:remove {:fn-force-nl #{:noarg1-body}}
|
||||
{:width 105
|
||||
:style [;; community style
|
||||
;; https://github.com/kkinnear/zprint/blob/main/doc/reference.md#community
|
||||
:community
|
||||
|
||||
:style
|
||||
[;; community style https://github.com/kkinnear/zprint/blob/main/doc/reference.md#community
|
||||
:community
|
||||
;; no comma in map
|
||||
:no-comma
|
||||
|
||||
;; no comma in map
|
||||
:no-comma
|
||||
:custom-justify
|
||||
|
||||
:custom-justify
|
||||
;; respect all newlines
|
||||
;; https://github.com/kkinnear/zprint/blob/main/doc/reference.md#respect-nl
|
||||
:respect-nl
|
||||
|
||||
;; respect all newlines https://github.com/kkinnear/zprint/blob/main/doc/reference.md#respect-nl
|
||||
:respect-nl
|
||||
|
||||
;; respect blank line https://github.com/kkinnear/zprint/blob/main/doc/reference.md#respect-bl
|
||||
:respect-bl
|
||||
|
||||
;; hang multiline left-hand-thing https://github.com/kkinnear/zprint/issues/273
|
||||
:multi-lhs-hang]
|
||||
;; respect blank line
|
||||
;; https://github.com/kkinnear/zprint/blob/main/doc/reference.md#respect-bl
|
||||
:respect-bl
|
||||
|
||||
;; hang multiline left-hand-thing
|
||||
;; https://github.com/kkinnear/zprint/issues/273
|
||||
:multi-lhs-hang]
|
||||
:fn-map
|
||||
{"reg-sub" :arg1-pair
|
||||
"h/describe" :arg1-body
|
||||
@@ -50,9 +50,9 @@
|
||||
"assoc-some" "assoc"
|
||||
"conj-when" "conj"
|
||||
"conj-some" "conj"}
|
||||
|
||||
:remove {:fn-force-nl #{:noarg1-body}}
|
||||
:style-map
|
||||
{:no-comma {:map {:comma? false}}
|
||||
{:no-comma {:map {:comma? false}}
|
||||
:custom-justify
|
||||
{:doc "Justify everything using pre-1.1.2 approach"
|
||||
:binding {:justify? true :justify {:max-variance 1000}}
|
||||
|
||||
@@ -279,9 +279,8 @@ run-android: ##@run Build Android APK and start it on the device
|
||||
npx react-native run-android --appIdSuffix debug
|
||||
|
||||
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 IOS_STATUS_GO_TARGETS := iossimulator/amd64
|
||||
run-ios: ##@run Build iOS app and start it in a simulator/device
|
||||
ifneq ("$(SIMULATOR)", "")
|
||||
npx react-native run-ios --simulator="$(SIMULATOR)" | xcbeautify
|
||||
@@ -292,9 +291,8 @@ endif
|
||||
show-ios-devices: ##@other shows connected ios device and its name
|
||||
xcrun xctrace list devices
|
||||
|
||||
# TODO: fix IOS_STATUS_GO_TARGETS to be either amd64 or arm64 when RN is upgraded
|
||||
run-ios-device: export TARGET := ios
|
||||
run-ios-device: export IOS_STATUS_GO_TARGETS := ios/arm64;iossimulator/amd64
|
||||
run-ios-device: export IOS_STATUS_GO_TARGETS := ios/arm64
|
||||
run-ios-device: ##@run iOS app and start it on a connected device by its name
|
||||
ifndef DEVICE_NAME
|
||||
$(error Usage: make run-ios-device DEVICE_NAME=your-device-name)
|
||||
@@ -307,7 +305,7 @@ endif
|
||||
|
||||
# Get all clojure files, including untracked, excluding removed
|
||||
define find_all_clojure_files
|
||||
$$(comm -23 <(sort <(git ls-files --cached --others --exclude-standard)) <(sort <(git ls-files --deleted)) | grep -E '((\.clj-kondo\/status-im)|(src).*\.clj[sc]?$$)|((\.clj-kondo\/(status-im|config))|(src).*\.edn$$)|shadow-cljs\.edn')
|
||||
$$(comm -23 <(sort <(git ls-files --cached --others --exclude-standard)) <(sort <(git ls-files --deleted)) | grep -e \.clj$$ -e \.cljs$$ -e \.cljc$$ -e \.edn)
|
||||
endef
|
||||
|
||||
lint: export TARGET := clojure
|
||||
@@ -329,7 +327,7 @@ lint-fix: ##@test Run code style checks and fix issues
|
||||
ALL_CLOJURE_FILES=$(call find_all_clojure_files) && \
|
||||
zprint '{:search-config? true}' -sw $$ALL_CLOJURE_FILES && \
|
||||
zprint '{:search-config? true}' -sw $$ALL_CLOJURE_FILES && \
|
||||
clojure-lsp --ns-exclude-regex ".*/\.clj-kondo/.*|.*/src/status_im/core\.cljs|.*/src/test_helpers/component_tests_preload\.cljs$$" clean-ns && \
|
||||
clojure-lsp --ns-exclude-regex ".*/src/status_im/core\.cljs|.*/src/test_helpers/component_tests_preload\.cljs$$" clean-ns && \
|
||||
sh scripts/lint/trailing-newline.sh --fix && \
|
||||
node_modules/.bin/prettier --write .
|
||||
|
||||
@@ -337,44 +335,26 @@ shadow-server: export TARGET := clojure
|
||||
shadow-server:##@ Start shadow-cljs in server mode for watching
|
||||
yarn shadow-cljs server
|
||||
|
||||
_test-clojure: export TARGET := clojure
|
||||
_test-clojure: export WATCH ?= false
|
||||
_test-clojure:
|
||||
ifeq ($(WATCH), true)
|
||||
yarn install && \
|
||||
yarn shadow-cljs compile mocks && \
|
||||
nodemon --exec "yarn shadow-cljs compile test && node --require ./test-resources/override.js $$SHADOW_OUTPUT_TO" -e cljs
|
||||
else
|
||||
yarn install && \
|
||||
yarn shadow-cljs compile mocks && \
|
||||
yarn shadow-cljs compile test && \
|
||||
node --require ./test-resources/override.js "$$SHADOW_OUTPUT_TO"
|
||||
endif
|
||||
test-watch: export TARGET := clojure
|
||||
test-watch: ##@ Watch tests and re-run no changes to cljs files
|
||||
yarn install
|
||||
nodemon --exec 'yarn shadow-cljs compile mocks && yarn shadow-cljs compile test && node --require ./test-resources/override.js target/test/test.js' -e cljs
|
||||
|
||||
test: export SHADOW_OUTPUT_TO := target/test/test.js
|
||||
test: export SHADOW_NS_REGEXP := .*-test$$
|
||||
test: ##@test Run all Clojure tests
|
||||
test: _test-clojure
|
||||
|
||||
test-watch-for-repl: export SHADOW_OUTPUT_TO := target/test/test.js
|
||||
test-watch-for-repl: export SHADOW_NS_REGEXP := .*-test$$
|
||||
test-watch-for-repl: ##@test Watch all Clojure tests and support REPL connections
|
||||
test-watch-for-repl: export TARGET := clojure
|
||||
test-watch-for-repl: ##@ Watch tests and support REPL connections
|
||||
yarn install
|
||||
rm -f target/test/test.js
|
||||
yarn shadow-cljs compile mocks && \
|
||||
concurrently --kill-others --prefix-colors 'auto' --names 'build,repl' \
|
||||
'yarn shadow-cljs watch test --verbose' \
|
||||
"until [ -f $$SHADOW_OUTPUT_TO ] ; do sleep 1 ; done ; node --require ./test-resources/override.js $$SHADOW_OUTPUT_TO --repl"
|
||||
'yarn shadow-cljs compile mocks && yarn shadow-cljs watch test --verbose' \
|
||||
'until [ -f ./target/test/test.js ] ; do sleep 1 ; done ; node --require ./test-resources/override.js ./target/test/test.js --repl'
|
||||
|
||||
test-unit: export SHADOW_OUTPUT_TO := target/unit_test/test.js
|
||||
test-unit: export SHADOW_NS_REGEXP := ^(?!status-im\.integration-test).*-test$$
|
||||
test-unit: ##@test Run unit tests
|
||||
test-unit: _test-clojure
|
||||
|
||||
test-integration: export SHADOW_OUTPUT_TO := target/integration_test/test.js
|
||||
test-integration: export SHADOW_NS_REGEXP := ^status-im\.integration-test.*$$
|
||||
test-integration: ##@test Run integration tests
|
||||
test-integration: _test-clojure
|
||||
test: export TARGET := clojure
|
||||
test: ##@test Run tests once in NodeJS
|
||||
# Here we create the gyp bindings for nodejs
|
||||
yarn install
|
||||
yarn shadow-cljs compile mocks && \
|
||||
yarn shadow-cljs compile test && \
|
||||
node --require ./test-resources/override.js target/test/test.js
|
||||
|
||||
android-test: jsbundle
|
||||
android-test: export TARGET := android
|
||||
|
||||
+2
-4
@@ -5,7 +5,7 @@ library 'status-jenkins-lib@v1.8.4'
|
||||
def isPRBuild = utils.isPRBuild()
|
||||
|
||||
pipeline {
|
||||
agent { label 'macos && arm64 && nix-2.14 && xcode-15.1' }
|
||||
agent { label 'macos && arm64 && nix-2.14 && xcode-14.3' }
|
||||
|
||||
parameters {
|
||||
string(
|
||||
@@ -78,9 +78,7 @@ pipeline {
|
||||
}
|
||||
stage('Upload') {
|
||||
steps { script {
|
||||
catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
|
||||
env.DIAWI_URL = ios.uploadToDiawi()
|
||||
}
|
||||
env.DIAWI_URL = ios.uploadToDiawi()
|
||||
env.PKG_URL = env.DIAWI_URL
|
||||
jenkins.setBuildDesc(IPA: env.PKG_URL)
|
||||
} }
|
||||
|
||||
+2
-10
@@ -52,24 +52,16 @@ pipeline {
|
||||
"""
|
||||
}
|
||||
}
|
||||
stage('Unit Tests') {
|
||||
stage('Tests') {
|
||||
steps {
|
||||
sh """#!/bin/bash
|
||||
set -eo pipefail
|
||||
make test-unit 2>&1 | tee -a ${LOG_FILE}
|
||||
make test 2>&1 | tee -a ${LOG_FILE}
|
||||
"""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Integration Tests') {
|
||||
steps {
|
||||
sh """#!/bin/bash
|
||||
set -eo pipefail
|
||||
make test-integration 2>&1 | tee -a ${LOG_FILE}
|
||||
"""
|
||||
}
|
||||
}
|
||||
stage('Component Tests') {
|
||||
steps {
|
||||
sh """#!/bin/bash
|
||||
|
||||
@@ -21,10 +21,8 @@ pipeline {
|
||||
/* See nix/README.md */
|
||||
NIX_IGNORE_SYMLINK_STORE = 1
|
||||
/* we source .bash_profile to be able to use nix-store */
|
||||
NIX_SSHOPTS = "-oStrictHostKeyChecking=no"
|
||||
NIX_SSHOPTS = "-o StrictHostKeyChecking=no source .profile;"
|
||||
NIX_CONF_DIR = "${env.WORKSPACE}/nix"
|
||||
NIX_STORE_CMD = '/nix/var/nix/profiles/default/bin/nix-store'
|
||||
NIX_SSH_REMOTE = "ssh://${params.NIX_CACHE_USER}@${params.NIX_CACHE_HOST}?remote-program=${env.NIX_STORE_CMD}"
|
||||
}
|
||||
|
||||
options {
|
||||
@@ -44,21 +42,19 @@ pipeline {
|
||||
steps { script {
|
||||
nix.shell('nix-env -i openssh', sandbox: false, pure: false)
|
||||
/* some build targets don't build on MacOS */
|
||||
os = sh(script: 'uname', returnStdout: true).trim()
|
||||
arch = sh(script: 'arch', returnStdout: true).trim()
|
||||
os = sh(script: 'uname', returnStdout: true)
|
||||
arch = sh(script: 'arch', returnStdout: true)
|
||||
} }
|
||||
}
|
||||
stage('Build status-go') {
|
||||
steps { script {
|
||||
def platforms = ['mobile.android', 'mobile.ios', 'library']
|
||||
if (os != 'Darwin') { platforms.removeAll { it == 'mobile.ios' } }
|
||||
/* FIXME: "'x86_64-darwin' with features {} is required to build" */
|
||||
/* FIXME: Remove this when #16237 is merged. */
|
||||
if (arch == 'arm64') { platforms.removeAll { it == 'mobile.android' } }
|
||||
platforms.each { platform ->
|
||||
/* Allow for Android builds on Apple ARM. */
|
||||
env.NIXPKGS_SYSTEM_OVERRIDE = nixSysOverride(os, arch, platform)
|
||||
platforms.each { os ->
|
||||
nix.build(
|
||||
attr: "targets.status-go.${platform}",
|
||||
attr: "targets.status-go.${os}",
|
||||
sandbox: false,
|
||||
link: false
|
||||
)
|
||||
@@ -78,8 +74,6 @@ pipeline {
|
||||
}
|
||||
stage('Build android deps') {
|
||||
steps { script {
|
||||
/* Allow for Android builds on Apple ARM. */
|
||||
env.NIXPKGS_SYSTEM_OVERRIDE = nixSysOverride(os, arch, 'android')
|
||||
/* Build/fetch deps required to build android release. */
|
||||
nix.build(
|
||||
attr: 'targets.mobile.android.release.buildInputs',
|
||||
@@ -92,13 +86,9 @@ pipeline {
|
||||
stage('Build nix shell deps') {
|
||||
steps { script {
|
||||
def shells = ['android', 'ios', 'fastlane', 'keytool', 'clojure', 'gradle']
|
||||
if (os != 'Darwin') { shells.removeAll { it == 'ios' } }
|
||||
/* FIXME: "'x86_64-darwin' with features {} is required to build" */
|
||||
if (arch == 'arm64') { shells.removeAll { it == 'android' } }
|
||||
if (os != "Darwin") { shells.removeAll { it == 'ios' } }
|
||||
/* Build/fetch deps required to start default Nix shell. */
|
||||
shells.each { shell ->
|
||||
/* Allow for Android builds on Apple ARM. */
|
||||
env.NIXPKGS_SYSTEM_OVERRIDE = nixSysOverride(os, arch, shell)
|
||||
nix.build(
|
||||
attr: "shells.${shell}.buildInputs",
|
||||
sandbox: false,
|
||||
@@ -113,7 +103,8 @@ pipeline {
|
||||
nix.shell("""
|
||||
find /nix/store/ -mindepth 1 -maxdepth 1 -type d \
|
||||
-not -name '*.links' -and -not -name '*-status-mobile-*' \
|
||||
| xargs nix copy --to ${NIX_SSH_REMOTE}
|
||||
| xargs nix copy \
|
||||
--to ssh-ng://${params.NIX_CACHE_USER}@${params.NIX_CACHE_HOST}
|
||||
""",
|
||||
pure: false
|
||||
)
|
||||
@@ -128,11 +119,3 @@ pipeline {
|
||||
} }
|
||||
}
|
||||
}
|
||||
|
||||
def nixSysOverride(os, arch, target='android') {
|
||||
return (
|
||||
os == 'Darwin' &&
|
||||
arch == 'arm64' &&
|
||||
target =~ /.*android$/
|
||||
) ? 'x86_64-darwin' : ''
|
||||
}
|
||||
|
||||
+4
-35
@@ -212,15 +212,16 @@ Properties must be set on view level
|
||||
:bottom 0}
|
||||
```
|
||||
|
||||
### Animated styles in the style file
|
||||
### Apply animated styles in the style file
|
||||
|
||||
```clojure
|
||||
;; bad
|
||||
(defn circle
|
||||
[]
|
||||
(let [opacity (reanimated/use-shared-value 1)]
|
||||
[reanimated/view {:style [{:opacity opacity}
|
||||
style/circle-container]}]))
|
||||
[reanimated/view {:style (reanimated/apply-animations-to-style
|
||||
{:opacity opacity}
|
||||
style/circle-container)}]))
|
||||
|
||||
;; good
|
||||
(defn circle
|
||||
@@ -229,38 +230,6 @@ Properties must be set on view level
|
||||
[reanimated/view {:style (style/circle-container opacity)}]))
|
||||
```
|
||||
|
||||
### Pass a vector of styles to Reanimated view
|
||||
|
||||
Prefer to pass a vector of styles to `react-native.reanimated/view` `:style`
|
||||
prop instead of using `apply-animations-to-style` directly. For more details, check out Reanimated docs about [inline
|
||||
styles](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/your-first-animation/#defining-a-shared-value)
|
||||
and [useAnimatedStyle](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/animating-styles-and-props/#animating-styles).
|
||||
|
||||
```clojure
|
||||
(defn f-view []
|
||||
(let [scroll-x (reanimated/use-shared-value 0)
|
||||
opacity (reanimated/interpolate scroll-x [0 45 50] [1 1 0])]
|
||||
[reanimated/view
|
||||
;; bad
|
||||
{:style (reanimated/apply-animations-to-style
|
||||
{:opacity opacity
|
||||
:transform [{:translate-x scroll-x}]}
|
||||
{:flex-direction :row})}
|
||||
|
||||
;; good
|
||||
{:style [{:opacity opacity
|
||||
:transform [{:translate-x scroll-x}]}
|
||||
{:flex-direction :row}]}
|
||||
|
||||
;; other valid and good variants
|
||||
{:style [{:opacity opacity}
|
||||
{:transform [{:translate-x scroll-x}]}
|
||||
{:flex-direction :row}]}
|
||||
|
||||
{:style {:opacity opacity
|
||||
:transform [{:translate-x scroll-x}]
|
||||
:flex-direction :row}}]))
|
||||
```
|
||||
### Don't use percents to define width/height
|
||||
|
||||
In ReactNative, all layouts use the [flexbox
|
||||
|
||||
+3
-3
@@ -5,7 +5,7 @@
|
||||
To run tests:
|
||||
|
||||
```
|
||||
make test
|
||||
make test
|
||||
```
|
||||
|
||||
|
||||
@@ -13,10 +13,10 @@ make test
|
||||
Also test watcher can be launched. It will re-run the entire test suite when any file is modified
|
||||
|
||||
```
|
||||
make test WATCH=true
|
||||
make test-watch
|
||||
```
|
||||
|
||||
Developers can also manually change the shadow-cljs option `:ns-regex` to control which namespaces the test runner should pick.
|
||||
Developers can also manually change the shadow-cljs option `:ns-regex` to control which namespaces the test runner should pick.
|
||||
|
||||
## Testing with REPL
|
||||
|
||||
|
||||
+1
-1
@@ -793,7 +793,7 @@ CHECKOUT OPTIONS:
|
||||
:submodules: true
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
boost: 64032b9e9b938fda23325e68a3771f0fabf414dc
|
||||
boost: 57d2868c099736d80fcd648bf211b4431e51a558
|
||||
BVLinearGradient: 612a04ff38e8480291f3379ee5b5a2c571f03fe0
|
||||
CryptoSwift: c4f2debceb38bf44c80659afe009f71e23e4a082
|
||||
DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
|
||||
|
||||
-345
@@ -1,345 +0,0 @@
|
||||
package im.status.ethereum.module;
|
||||
|
||||
import com.facebook.react.bridge.Callback;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import statusgo.Statusgo;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.IOException;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONException;
|
||||
import android.util.Log;
|
||||
import android.content.Context;
|
||||
import android.app.Activity;
|
||||
|
||||
public class AccountManager extends ReactContextBaseJavaModule {
|
||||
private static final String TAG = "AccountManager";
|
||||
private static final String gethLogFileName = "geth.log";
|
||||
private ReactApplicationContext reactContext;
|
||||
|
||||
private Utils utils;
|
||||
private LogManager logManager;
|
||||
|
||||
public AccountManager(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
this.reactContext = reactContext;
|
||||
this.utils = new Utils(reactContext);
|
||||
this.logManager = new LogManager(reactContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "AccountManager";
|
||||
}
|
||||
|
||||
private String getTestnetDataDir(final String absRootDirPath) {
|
||||
return this.utils.pathCombine(absRootDirPath, "ethereum/testnet");
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void createAccountAndLogin(final String createAccountRequest) {
|
||||
Log.d(TAG, "createAccountAndLogin");
|
||||
String result = Statusgo.createAccountAndLogin(createAccountRequest);
|
||||
if (result.startsWith("{\"error\":\"\"")) {
|
||||
Log.d(TAG, "createAccountAndLogin success: " + result);
|
||||
Log.d(TAG, "Geth node started");
|
||||
} else {
|
||||
Log.e(TAG, "createAccountAndLogin failed: " + result);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void restoreAccountAndLogin(final String restoreAccountRequest) {
|
||||
Log.d(TAG, "restoreAccountAndLogin");
|
||||
String result = Statusgo.restoreAccountAndLogin(restoreAccountRequest);
|
||||
if (result.startsWith("{\"error\":\"\"")) {
|
||||
Log.d(TAG, "restoreAccountAndLogin success: " + result);
|
||||
Log.d(TAG, "Geth node started");
|
||||
} else {
|
||||
Log.e(TAG, "restoreAccountAndLogin failed: " + result);
|
||||
}
|
||||
}
|
||||
|
||||
private String updateConfig(final String jsonConfigString, final String absRootDirPath, final String keystoreDirPath) throws JSONException {
|
||||
final JSONObject jsonConfig = new JSONObject(jsonConfigString);
|
||||
// retrieve parameters from app config, that will be applied onto the Go-side config later on
|
||||
final String dataDirPath = jsonConfig.getString("DataDir");
|
||||
final Boolean logEnabled = jsonConfig.getBoolean("LogEnabled");
|
||||
final Context context = this.getReactApplicationContext();
|
||||
final File gethLogFile = logEnabled ? this.logManager.prepareLogsFile(context) : null;
|
||||
String gethLogDirPath = null;
|
||||
if (gethLogFile != null) {
|
||||
gethLogDirPath = gethLogFile.getParent();
|
||||
}
|
||||
|
||||
Log.d(TAG, "log dir: " + gethLogDirPath + " log name: " + gethLogFileName);
|
||||
|
||||
jsonConfig.put("DataDir", dataDirPath);
|
||||
jsonConfig.put("KeyStoreDir", keystoreDirPath);
|
||||
jsonConfig.put("LogDir", gethLogDirPath);
|
||||
jsonConfig.put("LogFile", gethLogFileName);
|
||||
|
||||
return jsonConfig.toString();
|
||||
}
|
||||
|
||||
private static void prettyPrintConfig(final String config) {
|
||||
Log.d(TAG, "startNode() with config (see below)");
|
||||
String configOutput = config;
|
||||
final int maxOutputLen = 4000;
|
||||
Log.d(TAG, "********************** NODE CONFIG ****************************");
|
||||
while (!configOutput.isEmpty()) {
|
||||
Log.d(TAG, "Node config:" + configOutput.substring(0, Math.min(maxOutputLen, configOutput.length())));
|
||||
if (configOutput.length() > maxOutputLen) {
|
||||
configOutput = configOutput.substring(maxOutputLen);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "******************* ENDOF NODE CONFIG *************************");
|
||||
}
|
||||
|
||||
private void copyDirectory(File sourceLocation, File targetLocation) throws IOException {
|
||||
if (sourceLocation.isDirectory()) {
|
||||
if (!targetLocation.exists() && !targetLocation.mkdirs()) {
|
||||
throw new IOException("Cannot create dir " + targetLocation.getAbsolutePath());
|
||||
}
|
||||
|
||||
String[] children = sourceLocation.list();
|
||||
for (int i = 0; i < children.length; i++) {
|
||||
copyDirectory(new File(sourceLocation, children[i]), new File(targetLocation, children[i]));
|
||||
}
|
||||
} else {
|
||||
File directory = targetLocation.getParentFile();
|
||||
if (directory != null && !directory.exists() && !directory.mkdirs()) {
|
||||
throw new IOException("Cannot create dir " + directory.getAbsolutePath());
|
||||
}
|
||||
|
||||
InputStream in = new FileInputStream(sourceLocation);
|
||||
OutputStream out = new FileOutputStream(targetLocation);
|
||||
|
||||
byte[] buf = new byte[1024];
|
||||
int len;
|
||||
while ((len = in.read(buf)) > 0) {
|
||||
out.write(buf, 0, len);
|
||||
}
|
||||
in.close();
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
private String prepareDirAndUpdateConfig(final String jsonConfigString, final String keyUID) {
|
||||
|
||||
final String absRootDirPath = this.utils.getNoBackupDirectory();
|
||||
final String dataFolder = this.getTestnetDataDir(absRootDirPath);
|
||||
Log.d(TAG, "Starting Geth node in folder: " + dataFolder);
|
||||
|
||||
try {
|
||||
final File newFile = new File(dataFolder);
|
||||
// todo handle error?
|
||||
newFile.mkdir();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "error making folder: " + dataFolder, e);
|
||||
}
|
||||
|
||||
final String ropstenFlagPath = this.utils.pathCombine(absRootDirPath, "ropsten_flag");
|
||||
final File ropstenFlag = new File(ropstenFlagPath);
|
||||
if (!ropstenFlag.exists()) {
|
||||
try {
|
||||
final String chaindDataFolderPath = this.utils.pathCombine(dataFolder, "StatusIM/lightchaindata");
|
||||
final File lightChainFolder = new File(chaindDataFolderPath);
|
||||
if (lightChainFolder.isDirectory()) {
|
||||
String[] children = lightChainFolder.list();
|
||||
for (int i = 0; i < children.length; i++) {
|
||||
new File(lightChainFolder, children[i]).delete();
|
||||
}
|
||||
}
|
||||
lightChainFolder.delete();
|
||||
ropstenFlag.createNewFile();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
String testnetDataDir = dataFolder;
|
||||
String oldKeystoreDir = this.utils.pathCombine(testnetDataDir, "keystore");
|
||||
String newKeystoreDir = this.utils.pathCombine(absRootDirPath, "keystore");
|
||||
final File oldKeystore = new File(oldKeystoreDir);
|
||||
if (oldKeystore.exists()) {
|
||||
try {
|
||||
final File newKeystore = new File(newKeystoreDir);
|
||||
copyDirectory(oldKeystore, newKeystore);
|
||||
|
||||
if (oldKeystore.isDirectory()) {
|
||||
String[] children = oldKeystore.list();
|
||||
for (int i = 0; i < children.length; i++) {
|
||||
new File(oldKeystoreDir, children[i]).delete();
|
||||
}
|
||||
}
|
||||
oldKeystore.delete();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
final String multiaccountKeystoreDir = this.utils.pathCombine("/keystore", keyUID);
|
||||
final String updatedJsonConfigString = this.updateConfig(jsonConfigString, absRootDirPath, multiaccountKeystoreDir);
|
||||
|
||||
prettyPrintConfig(updatedJsonConfigString);
|
||||
|
||||
return updatedJsonConfigString;
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "updateConfig failed: " + e.getMessage());
|
||||
System.exit(1);
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void prepareDirAndUpdateConfig(final String keyUID, final String config, final Callback callback) {
|
||||
Log.d(TAG, "prepareDirAndUpdateConfig");
|
||||
String finalConfig = prepareDirAndUpdateConfig(config, keyUID);
|
||||
callback.invoke(finalConfig);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void saveAccountAndLoginWithKeycard(final String multiaccountData, final String password, final String settings, final String config, final String accountsData, final String chatKey) {
|
||||
try {
|
||||
Log.d(TAG, "saveAccountAndLoginWithKeycard");
|
||||
String finalConfig = prepareDirAndUpdateConfig(config, this.utils.getKeyUID(multiaccountData));
|
||||
String result = Statusgo.saveAccountAndLoginWithKeycard(multiaccountData, password, settings, finalConfig, accountsData, chatKey);
|
||||
if (result.startsWith("{\"error\":\"\"")) {
|
||||
Log.d(TAG, "saveAccountAndLoginWithKeycard result: " + result);
|
||||
Log.d(TAG, "Geth node started");
|
||||
} else {
|
||||
Log.e(TAG, "saveAccountAndLoginWithKeycard failed: " + result);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "JSON conversion failed: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void loginWithKeycard(final String accountData, final String password, final String chatKey, final String nodeConfigJSON) {
|
||||
Log.d(TAG, "loginWithKeycard");
|
||||
this.utils.migrateKeyStoreDir(accountData, password);
|
||||
String result = Statusgo.loginWithKeycard(accountData, password, chatKey, nodeConfigJSON);
|
||||
if (result.startsWith("{\"error\":\"\"")) {
|
||||
Log.d(TAG, "LoginWithKeycard result: " + result);
|
||||
} else {
|
||||
Log.e(TAG, "LoginWithKeycard failed: " + result);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void loginWithConfig(final String accountData, final String password, final String configJSON) {
|
||||
Log.d(TAG, "loginWithConfig");
|
||||
this.utils.migrateKeyStoreDir(accountData, password);
|
||||
String result = Statusgo.loginWithConfig(accountData, password, configJSON);
|
||||
if (result.startsWith("{\"error\":\"\"")) {
|
||||
Log.d(TAG, "LoginWithConfig result: " + result);
|
||||
} else {
|
||||
Log.e(TAG, "LoginWithConfig failed: " + result);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void loginAccount(final String request) {
|
||||
Log.d(TAG, "loginAccount");
|
||||
String result = Statusgo.loginAccount(request);
|
||||
if (result.startsWith("{\"error\":\"\"")) {
|
||||
Log.d(TAG, "loginAccount result: " + result);
|
||||
} else {
|
||||
Log.e(TAG, "loginAccount failed: " + result);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void verify(final String address, final String password, final Callback callback) throws JSONException {
|
||||
Activity currentActivity = getCurrentActivity();
|
||||
|
||||
final String absRootDirPath = this.utils.getNoBackupDirectory();
|
||||
final String newKeystoreDir = this.utils.pathCombine(absRootDirPath, "keystore");
|
||||
|
||||
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.verifyAccountPassword(newKeystoreDir, address, password), callback);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void verifyDatabasePassword(final String keyUID, final String password, final Callback callback) throws JSONException {
|
||||
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.verifyDatabasePassword(keyUID, password), callback);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
private void openAccounts(final Callback callback) throws JSONException {
|
||||
Log.d(TAG, "openAccounts");
|
||||
final String rootDir = this.utils.getNoBackupDirectory();
|
||||
Log.d(TAG, "[Opening accounts" + rootDir);
|
||||
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.openAccounts(rootDir), callback);
|
||||
}
|
||||
|
||||
//TODO : refactor logout usage to accept callback so that we can do this.utils.executeRunnableStatusGoMethod
|
||||
@ReactMethod
|
||||
public void logout() {
|
||||
Log.d(TAG, "logout");
|
||||
Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
String result = Statusgo.logout();
|
||||
if (result.startsWith("{\"error\":\"\"")) {
|
||||
Log.d(TAG, "Logout result: " + result);
|
||||
} else {
|
||||
Log.e(TAG, "Logout failed: " + result);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
StatusThreadPoolExecutor.getInstance().execute(r);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void multiAccountStoreAccount(final String json, final Callback callback) throws JSONException {
|
||||
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.multiAccountStoreAccount(json), callback);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void multiAccountLoadAccount(final String json, final Callback callback) throws JSONException {
|
||||
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.multiAccountLoadAccount(json), callback);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void multiAccountDeriveAddresses(final String json, final Callback callback) throws JSONException {
|
||||
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.multiAccountDeriveAddresses(json), callback);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void multiAccountGenerateAndDeriveAddresses(final String json, final Callback callback) throws JSONException {
|
||||
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.multiAccountGenerateAndDeriveAddresses(json), callback);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void multiAccountStoreDerived(final String json, final Callback callback) throws JSONException {
|
||||
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.multiAccountStoreDerivedAccounts(json), callback);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void multiAccountImportMnemonic(final String json, final Callback callback) throws JSONException {
|
||||
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.multiAccountImportMnemonic(json), callback);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void multiAccountImportPrivateKey(final String json, final Callback callback) throws JSONException {
|
||||
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.multiAccountImportPrivateKey(json), callback);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void deleteMultiaccount(final String keyUID, final Callback callback) throws JSONException {
|
||||
final String keyStoreDir = this.utils.getKeyStorePath(keyUID);
|
||||
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.deleteMultiaccount(keyUID, keyStoreDir), callback);
|
||||
}
|
||||
}
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
package im.status.ethereum.module;
|
||||
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.Callback;
|
||||
import statusgo.Statusgo;
|
||||
import android.util.Log;
|
||||
import java.io.File;
|
||||
import android.os.Environment;
|
||||
import android.content.Context;
|
||||
public class DatabaseManager extends ReactContextBaseJavaModule {
|
||||
private static final String TAG = "DatabaseManager";
|
||||
private ReactApplicationContext reactContext;
|
||||
private static final String exportDBFileName = "export.db";
|
||||
private Utils utils;
|
||||
public DatabaseManager(ReactApplicationContext reactContext) {
|
||||
this.reactContext = reactContext;
|
||||
this.utils = new Utils(reactContext);
|
||||
}
|
||||
@Override
|
||||
public String getName() {
|
||||
return "DatabaseManager";
|
||||
}
|
||||
|
||||
private File getExportDBFile() {
|
||||
final Context context = this.getReactApplicationContext();
|
||||
// Environment.getExternalStoragePublicDirectory doesn't work as expected on Android Q
|
||||
// https://developer.android.com/reference/android/os/Environment#getExternalStoragePublicDirectory(java.lang.String)
|
||||
final File pubDirectory = context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
|
||||
final File filename = new File(pubDirectory, exportDBFileName);
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void exportUnencryptedDatabase(final String accountData, final String password, final Callback callback) {
|
||||
Log.d(TAG, "login");
|
||||
|
||||
final File newFile = getExportDBFile();
|
||||
|
||||
this.utils.migrateKeyStoreDir(accountData, password);
|
||||
String result = Statusgo.exportUnencryptedDatabase(accountData, password, newFile.getAbsolutePath());
|
||||
if (result.startsWith("{\"error\":\"\"")) {
|
||||
Log.d(TAG, "Login result: " + result);
|
||||
} else {
|
||||
Log.e(TAG, "Login failed: " + result);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void importUnencryptedDatabase(final String accountData, final String password) {
|
||||
Log.d(TAG, "importUnencryptedDatabase");
|
||||
|
||||
final File newFile = getExportDBFile();
|
||||
|
||||
this.utils.migrateKeyStoreDir(accountData, password);
|
||||
String result = Statusgo.importUnencryptedDatabase(accountData, password, newFile.getAbsolutePath());
|
||||
if (result.startsWith("{\"error\":\"\"")) {
|
||||
Log.d(TAG, "import result: " + result);
|
||||
} else {
|
||||
Log.e(TAG, "import failed: " + result);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-173
@@ -1,173 +0,0 @@
|
||||
package im.status.ethereum.module;
|
||||
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.Callback;
|
||||
import android.util.Log;
|
||||
import statusgo.Statusgo;
|
||||
import org.json.JSONException;
|
||||
import java.util.function.Function;
|
||||
import android.app.Activity;
|
||||
import android.view.WindowManager;
|
||||
import android.os.Build;
|
||||
import android.view.Window;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
public class EncryptionUtils extends ReactContextBaseJavaModule {
|
||||
private static final String TAG = "EncryptionUtils";
|
||||
|
||||
private ReactApplicationContext reactContext;
|
||||
private Utils utils;
|
||||
|
||||
public EncryptionUtils(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
this.reactContext = reactContext;
|
||||
this.utils = new Utils(reactContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "EncryptionUtils";
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
private void initKeystore(final String keyUID, final Callback callback) throws JSONException {
|
||||
Log.d(TAG, "initKeystore");
|
||||
|
||||
final String commonKeydir = this.utils.pathCombine(this.utils.getNoBackupDirectory(), "/keystore");
|
||||
final String keydir = this.utils.pathCombine(commonKeydir, keyUID);
|
||||
|
||||
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.initKeystore(keydir), callback);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void reEncryptDbAndKeystore(final String keyUID, final String password, final String newPassword, final Callback callback) throws JSONException {
|
||||
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.changeDatabasePassword(keyUID, password, newPassword), callback);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void convertToKeycardAccount(final String keyUID, final String accountData, final String options, final String keycardUID, final String password,
|
||||
final String newPassword, final Callback callback) throws JSONException {
|
||||
final String keyStoreDir = this.utils.getKeyStorePath(keyUID);
|
||||
this.utils.executeRunnableStatusGoMethod(() -> {
|
||||
Statusgo.initKeystore(keyStoreDir);
|
||||
return Statusgo.convertToKeycardAccount(accountData, options, keycardUID, password, newPassword);
|
||||
}, callback);
|
||||
}
|
||||
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public String encodeTransfer(final String to, final String value) {
|
||||
return Statusgo.encodeTransfer(to, value);
|
||||
}
|
||||
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public String decodeParameters(final String decodeParamJSON) {
|
||||
return Statusgo.decodeParameters(decodeParamJSON);
|
||||
}
|
||||
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public String hexToNumber(final String hex) {
|
||||
return Statusgo.hexToNumber(hex);
|
||||
}
|
||||
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public String numberToHex(final String numString) {
|
||||
return Statusgo.numberToHex(numString);
|
||||
}
|
||||
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public String sha3(final String str) {
|
||||
return Statusgo.sha3(str);
|
||||
}
|
||||
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public String utf8ToHex(final String str) {
|
||||
return Statusgo.utf8ToHex(str);
|
||||
}
|
||||
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public String hexToUtf8(final String str) {
|
||||
return Statusgo.hexToUtf8(str);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void setBlankPreviewFlag(final Boolean blankPreview) {
|
||||
final SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this.reactContext);
|
||||
sharedPrefs.edit().putBoolean("BLANK_PREVIEW", blankPreview).commit();
|
||||
setSecureFlag();
|
||||
}
|
||||
|
||||
private void setSecureFlag() {
|
||||
final SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this.reactContext);
|
||||
final boolean setSecure = sharedPrefs.getBoolean("BLANK_PREVIEW", true);
|
||||
final Activity activity = this.reactContext.getCurrentActivity();
|
||||
if (activity != null) {
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final Window window = activity.getWindow();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH && setSecure) {
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_SECURE);
|
||||
} else {
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void hashTransaction(final String txArgsJSON, final Callback callback) throws JSONException {
|
||||
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.hashTransaction(txArgsJSON), callback);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void hashMessage(final String message, final Callback callback) throws JSONException {
|
||||
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.hashMessage(message), callback);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void multiformatDeserializePublicKey(final String multiCodecKey, final String base58btc, final Callback callback) throws JSONException {
|
||||
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.multiformatDeserializePublicKey(multiCodecKey,base58btc), callback);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void deserializeAndCompressKey(final String desktopKey, final Callback callback) throws JSONException {
|
||||
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.deserializeAndCompressKey(desktopKey), callback);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void hashTypedData(final String data, final Callback callback) throws JSONException {
|
||||
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.hashTypedData(data), callback);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void hashTypedDataV4(final String data, final Callback callback) throws JSONException {
|
||||
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.hashTypedDataV4(data), callback);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void signMessage(final String rpcParams, final Callback callback) throws JSONException {
|
||||
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.signMessage(rpcParams), callback);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void signTypedData(final String data, final String account, final String password, final Callback callback) throws JSONException {
|
||||
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.signTypedData(data, account, password), callback);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void signTypedDataV4(final String data, final String account, final String password, final Callback callback) throws JSONException {
|
||||
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.signTypedDataV4(data, account, password), callback);
|
||||
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void signGroupMembership(final String content, final Callback callback) throws JSONException {
|
||||
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.signGroupMembership(content), callback);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
-227
@@ -1,227 +0,0 @@
|
||||
package im.status.ethereum.module;
|
||||
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.Callback;
|
||||
import java.io.File;
|
||||
import java.util.Stack;
|
||||
import android.util.Log;
|
||||
import android.net.Uri;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.IOException;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import androidx.core.content.FileProvider;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import org.json.JSONObject;
|
||||
import statusgo.Statusgo;
|
||||
import android.content.Context;
|
||||
import org.json.JSONException;
|
||||
public class LogManager extends ReactContextBaseJavaModule {
|
||||
private static final String TAG = "LogManager";
|
||||
private static final String gethLogFileName = "geth.log";
|
||||
private static final String statusLogFileName = "Status.log";
|
||||
private static final String logsZipFileName = "Status-debug-logs.zip";
|
||||
private ReactApplicationContext reactContext;
|
||||
private Utils utils;
|
||||
|
||||
public LogManager(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
this.reactContext = reactContext;
|
||||
this.utils = new Utils(reactContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "LogManager";
|
||||
}
|
||||
|
||||
private File getLogsFile() {
|
||||
final File pubDirectory = this.utils.getPublicStorageDirectory();
|
||||
final File logFile = new File(pubDirectory, gethLogFileName);
|
||||
|
||||
return logFile;
|
||||
}
|
||||
|
||||
public File prepareLogsFile(final Context context) {
|
||||
final File logFile = this.utils.getLogsFile();
|
||||
|
||||
try {
|
||||
logFile.setReadable(true);
|
||||
File parent = logFile.getParentFile();
|
||||
if (!parent.canWrite()) {
|
||||
return null;
|
||||
}
|
||||
if (!parent.exists()) {
|
||||
parent.mkdirs();
|
||||
}
|
||||
logFile.createNewFile();
|
||||
logFile.setWritable(true);
|
||||
Log.d(TAG, "Can write " + logFile.canWrite());
|
||||
Uri gethLogUri = Uri.fromFile(logFile);
|
||||
|
||||
String gethLogFilePath = logFile.getAbsolutePath();
|
||||
Log.d(TAG, gethLogFilePath);
|
||||
|
||||
return logFile;
|
||||
} catch (Exception e) {
|
||||
Log.d(TAG, "Can't create geth.log file! " + e.getMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void showErrorMessage(final String message) {
|
||||
final Activity activity = getCurrentActivity();
|
||||
|
||||
new AlertDialog.Builder(activity)
|
||||
.setTitle("Error")
|
||||
.setMessage(message)
|
||||
.setNegativeButton("Exit", new DialogInterface.OnClickListener() {
|
||||
public void onClick(final DialogInterface dialog, final int id) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
}).show();
|
||||
}
|
||||
|
||||
private void dumpAdbLogsTo(final FileOutputStream statusLogStream) throws IOException {
|
||||
final String filter = "logcat -d -b main ReactNativeJS:D StatusModule:D StatusService:D StatusNativeLogs:D *:S";
|
||||
final java.lang.Process p = Runtime.getRuntime().exec(filter);
|
||||
final java.io.BufferedReader in = new java.io.BufferedReader(new java.io.InputStreamReader(p.getInputStream()));
|
||||
final java.io.BufferedWriter out = new java.io.BufferedWriter(new java.io.OutputStreamWriter(statusLogStream));
|
||||
String line;
|
||||
while ((line = in.readLine()) != null) {
|
||||
out.write(line);
|
||||
out.newLine();
|
||||
}
|
||||
out.close();
|
||||
in.close();
|
||||
}
|
||||
|
||||
private Boolean zip(File[] _files, File zipFile, Stack<String> errorList) {
|
||||
final int BUFFER = 0x8000;
|
||||
|
||||
try {
|
||||
BufferedInputStream origin = null;
|
||||
FileOutputStream dest = new FileOutputStream(zipFile);
|
||||
ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(dest));
|
||||
byte data[] = new byte[BUFFER];
|
||||
|
||||
for (int i = 0; i < _files.length; i++) {
|
||||
final File file = _files[i];
|
||||
if (file == null || !file.exists()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Log.v("Compress", "Adding: " + file.getAbsolutePath());
|
||||
try {
|
||||
FileInputStream fi = new FileInputStream(file);
|
||||
origin = new BufferedInputStream(fi, BUFFER);
|
||||
|
||||
ZipEntry entry = new ZipEntry(file.getName());
|
||||
out.putNextEntry(entry);
|
||||
int count;
|
||||
|
||||
while ((count = origin.read(data, 0, BUFFER)) != -1) {
|
||||
out.write(data, 0, count);
|
||||
}
|
||||
origin.close();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, e.getMessage());
|
||||
errorList.push(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
out.close();
|
||||
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, e.getMessage());
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void sendLogs(final String dbJson, final String jsLogs, final Callback callback) {
|
||||
Log.d(TAG, "sendLogs");
|
||||
if (!this.utils.checkAvailability()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Context context = this.getReactApplicationContext();
|
||||
final File logsTempDir = new File(context.getCacheDir(), "logs"); // This path needs to be in sync with android/app/src/main/res/xml/file_provider_paths.xml
|
||||
logsTempDir.mkdir();
|
||||
|
||||
final File dbFile = new File(logsTempDir, "db.json");
|
||||
try {
|
||||
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream(dbFile));
|
||||
outputStreamWriter.write(dbJson);
|
||||
outputStreamWriter.close();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "File write failed: " + e.toString());
|
||||
showErrorMessage(e.getLocalizedMessage());
|
||||
}
|
||||
|
||||
final File zipFile = new File(logsTempDir, logsZipFileName);
|
||||
final File statusLogFile = new File(logsTempDir, statusLogFileName);
|
||||
final File gethLogFile = getLogsFile();
|
||||
|
||||
try {
|
||||
if (zipFile.exists() || zipFile.createNewFile()) {
|
||||
final long usableSpace = zipFile.getUsableSpace();
|
||||
if (usableSpace < 20 * 1024 * 1024) {
|
||||
final String message = String.format("Insufficient space available on device (%s) to write logs.\nPlease free up some space.", android.text.format.Formatter.formatShortFileSize(context, usableSpace));
|
||||
Log.e(TAG, message);
|
||||
showErrorMessage(message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
dumpAdbLogsTo(new FileOutputStream(statusLogFile));
|
||||
|
||||
final Stack<String> errorList = new Stack<String>();
|
||||
final Boolean zipped = zip(new File[]{dbFile, gethLogFile, statusLogFile}, zipFile, errorList);
|
||||
if (zipped && zipFile.exists()) {
|
||||
zipFile.setReadable(true, false);
|
||||
Uri extUri = FileProvider.getUriForFile(context, context.getPackageName() + ".provider", zipFile);
|
||||
callback.invoke(extUri.toString());
|
||||
} else {
|
||||
Log.d(TAG, "File " + zipFile.getAbsolutePath() + " does not exist");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, e.getMessage());
|
||||
showErrorMessage(e.getLocalizedMessage());
|
||||
e.printStackTrace();
|
||||
return;
|
||||
} finally {
|
||||
dbFile.delete();
|
||||
statusLogFile.delete();
|
||||
zipFile.deleteOnExit();
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void initLogging(final boolean enabled, final boolean mobileSystem, final String logLevel, final Callback callback) throws JSONException {
|
||||
final JSONObject jsonConfig = new JSONObject();
|
||||
jsonConfig.put("Enabled", enabled);
|
||||
jsonConfig.put("MobileSystem", mobileSystem);
|
||||
jsonConfig.put("Level", logLevel);
|
||||
jsonConfig.put("File", getLogsFile().getAbsolutePath());
|
||||
final String config = jsonConfig.toString();
|
||||
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.initLogging(config), callback);
|
||||
}
|
||||
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public String logFileDirectory() {
|
||||
return this.utils.getPublicStorageDirectory().getAbsolutePath();
|
||||
}
|
||||
|
||||
}
|
||||
-78
@@ -1,78 +0,0 @@
|
||||
package im.status.ethereum.module;
|
||||
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.Callback;
|
||||
import org.json.JSONException;
|
||||
import statusgo.Statusgo;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class NetworkManager extends ReactContextBaseJavaModule {
|
||||
private ReactApplicationContext reactContext;
|
||||
private Utils utils;
|
||||
|
||||
public NetworkManager(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
this.reactContext = reactContext;
|
||||
this.utils = new Utils(reactContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "NetworkManager";
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void startSearchForLocalPairingPeers(final Callback callback) throws JSONException {
|
||||
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.startSearchForLocalPairingPeers(), callback);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getConnectionStringForBootstrappingAnotherDevice(final String configJSON, final Callback callback) throws JSONException {
|
||||
final JSONObject jsonConfig = new JSONObject(configJSON);
|
||||
final JSONObject senderConfig = jsonConfig.getJSONObject("senderConfig");
|
||||
final String keyUID = senderConfig.getString("keyUID");
|
||||
final String keyStorePath = this.utils.getKeyStorePath(keyUID);
|
||||
senderConfig.put("keystorePath", keyStorePath);
|
||||
|
||||
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.getConnectionStringForBootstrappingAnotherDevice(jsonConfig.toString()), callback);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void inputConnectionStringForBootstrapping(final String connectionString, final String configJSON, final Callback callback) throws JSONException {
|
||||
final JSONObject jsonConfig = new JSONObject(configJSON);
|
||||
final JSONObject receiverConfig = jsonConfig.getJSONObject("receiverConfig");
|
||||
final String keyStorePath = this.utils.pathCombine(this.utils.getNoBackupDirectory(), "/keystore");
|
||||
receiverConfig.put("keystorePath", keyStorePath);
|
||||
receiverConfig.getJSONObject("nodeConfig").put("rootDataDir", this.utils.getNoBackupDirectory());
|
||||
|
||||
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.inputConnectionStringForBootstrapping(connectionString, jsonConfig.toString()), callback);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void sendTransactionWithSignature(final String txArgsJSON, final String signature, final Callback callback) throws JSONException {
|
||||
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.sendTransactionWithSignature(txArgsJSON, signature), callback);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void sendTransaction(final String txArgsJSON, final String password, final Callback callback) throws JSONException {
|
||||
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.sendTransaction(txArgsJSON, password), callback);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void callRPC(final String payload, final Callback callback) throws JSONException {
|
||||
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.callRPC(payload), callback);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void callPrivateRPC(final String payload, final Callback callback) throws JSONException {
|
||||
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.callPrivateRPC(payload), callback);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void recover(final String rpcParams, final Callback callback) throws JSONException {
|
||||
this.utils.executeRunnableStatusGoMethod(() -> Statusgo.recover(rpcParams), callback);
|
||||
}
|
||||
|
||||
}
|
||||
+1222
-10
File diff suppressed because it is too large
Load Diff
-7
@@ -30,13 +30,6 @@ public class StatusPackage implements ReactPackage {
|
||||
List<NativeModule> modules = new ArrayList<>();
|
||||
|
||||
modules.add(new StatusModule(reactContext, this.rootedDevice));
|
||||
modules.add(new AccountManager(reactContext));
|
||||
modules.add(new EncryptionUtils(reactContext));
|
||||
modules.add(new DatabaseManager(reactContext));
|
||||
modules.add(new UIHelper(reactContext));
|
||||
modules.add(new LogManager(reactContext));
|
||||
modules.add(new Utils(reactContext));
|
||||
modules.add(new NetworkManager(reactContext));
|
||||
modules.add(new RNSelectableTextInputModule(reactContext));
|
||||
|
||||
return modules;
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package im.status.ethereum.module;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/** Uses an unbounded queue, but allows timeout of core threads
|
||||
* (modified case 2 in
|
||||
* https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html ) */
|
||||
public class StatusThreadPoolExecutor {
|
||||
private static final int NUMBER_OF_CORES =
|
||||
Runtime.getRuntime().availableProcessors();
|
||||
private static final int THREADS_TO_CORES_RATIO = 100;
|
||||
private static final int KEEP_ALIVE_TIME = 1;
|
||||
private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
|
||||
|
||||
private final BlockingQueue<Runnable> mQueue;
|
||||
private final ThreadPoolExecutor mThreadPool;
|
||||
|
||||
private StatusThreadPoolExecutor() {
|
||||
mQueue = new LinkedBlockingQueue<>();
|
||||
|
||||
mThreadPool = new ThreadPoolExecutor(
|
||||
THREADS_TO_CORES_RATIO * NUMBER_OF_CORES,
|
||||
THREADS_TO_CORES_RATIO * NUMBER_OF_CORES,
|
||||
KEEP_ALIVE_TIME,
|
||||
KEEP_ALIVE_TIME_UNIT,
|
||||
mQueue);
|
||||
|
||||
// Allow pool to drain
|
||||
mThreadPool.allowCoreThreadTimeOut(true);
|
||||
}
|
||||
|
||||
/** Pugh singleton */
|
||||
private static class Holder {
|
||||
private static StatusThreadPoolExecutor instance = new StatusThreadPoolExecutor();
|
||||
}
|
||||
|
||||
public static StatusThreadPoolExecutor getInstance() {
|
||||
return Holder.instance;
|
||||
}
|
||||
|
||||
public void execute(final Runnable r) {
|
||||
mThreadPool.execute(r);
|
||||
}
|
||||
}
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
package im.status.ethereum.module
|
||||
|
||||
import java.util.concurrent.*
|
||||
|
||||
/**
|
||||
* Uses an unbounded queue but allows timeout of core threads
|
||||
* (modified case 2 in
|
||||
* https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html )
|
||||
*/
|
||||
class StatusThreadPoolExecutor private constructor() {
|
||||
private val NUMBER_OF_CORES: Int = Runtime.getRuntime().availableProcessors()
|
||||
private val THREADS_TO_CORES_RATIO: Int = 100
|
||||
private val KEEP_ALIVE_TIME: Int = 1
|
||||
private val KEEP_ALIVE_TIME_UNIT: TimeUnit = TimeUnit.SECONDS
|
||||
|
||||
private val mQueue: BlockingQueue<Runnable> = LinkedBlockingQueue()
|
||||
private val mThreadPool: ThreadPoolExecutor
|
||||
|
||||
init {
|
||||
mThreadPool = ThreadPoolExecutor(
|
||||
THREADS_TO_CORES_RATIO * NUMBER_OF_CORES,
|
||||
THREADS_TO_CORES_RATIO * NUMBER_OF_CORES,
|
||||
KEEP_ALIVE_TIME.toLong(),
|
||||
KEEP_ALIVE_TIME_UNIT,
|
||||
mQueue
|
||||
)
|
||||
|
||||
// Allow pool to drain
|
||||
mThreadPool.allowCoreThreadTimeOut(true)
|
||||
}
|
||||
|
||||
/** Singleton holder */
|
||||
private object Holder {
|
||||
val instance = StatusThreadPoolExecutor()
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun getInstance(): StatusThreadPoolExecutor {
|
||||
return Holder.instance
|
||||
}
|
||||
}
|
||||
|
||||
fun execute(r: Runnable) {
|
||||
mThreadPool.execute(r)
|
||||
}
|
||||
}
|
||||
-124
@@ -1,124 +0,0 @@
|
||||
package im.status.ethereum.module;
|
||||
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import android.app.Activity;
|
||||
import android.util.Log;
|
||||
import android.os.Build;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.CookieManager;
|
||||
import android.webkit.CookieSyncManager;
|
||||
import android.webkit.WebStorage;
|
||||
import android.view.WindowManager;
|
||||
import com.facebook.react.uimanager.UIManagerModule;
|
||||
import com.facebook.react.uimanager.UIBlock;
|
||||
import com.facebook.react.uimanager.NativeViewHierarchyManager;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.content.Context;
|
||||
|
||||
public class UIHelper extends ReactContextBaseJavaModule {
|
||||
private static final String TAG = "UIHelper";
|
||||
private ReactApplicationContext reactContext;
|
||||
|
||||
public UIHelper(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
this.reactContext = reactContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "UIHelper";
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void setSoftInputMode(final int mode) {
|
||||
Log.d(TAG, "setSoftInputMode");
|
||||
final Activity activity = getCurrentActivity();
|
||||
if (activity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
activity.getWindow().setSoftInputMode(mode);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@ReactMethod
|
||||
public void clearCookies() {
|
||||
Log.d(TAG, "clearCookies");
|
||||
final Activity activity = getCurrentActivity();
|
||||
if (activity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
|
||||
CookieManager.getInstance().removeAllCookies(null);
|
||||
CookieManager.getInstance().flush();
|
||||
} else {
|
||||
CookieSyncManager cookieSyncManager = CookieSyncManager.createInstance(activity);
|
||||
cookieSyncManager.startSync();
|
||||
CookieManager cookieManager = CookieManager.getInstance();
|
||||
cookieManager.removeAllCookie();
|
||||
cookieManager.removeSessionCookie();
|
||||
cookieSyncManager.stopSync();
|
||||
cookieSyncManager.sync();
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void toggleWebviewDebug(final boolean val) {
|
||||
Log.d(TAG, "toggleWebviewDebug");
|
||||
final Activity activity = getCurrentActivity();
|
||||
if (activity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
WebView.setWebContentsDebuggingEnabled(val);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void clearStorageAPIs() {
|
||||
Log.d(TAG, "clearStorageAPIs");
|
||||
final Activity activity = getCurrentActivity();
|
||||
if (activity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
WebStorage storage = WebStorage.getInstance();
|
||||
if (storage != null) {
|
||||
storage.deleteAllData();
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void resetKeyboardInputCursor(final int reactTagToReset, final int selection) {
|
||||
UIManagerModule uiManager = getReactApplicationContext().getNativeModule(UIManagerModule.class);
|
||||
uiManager.addUIBlock(new UIBlock() {
|
||||
@Override
|
||||
public void execute(NativeViewHierarchyManager nativeViewHierarchyManager) {
|
||||
InputMethodManager imm = (InputMethodManager) getReactApplicationContext().getBaseContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
if (imm != null) {
|
||||
View viewToReset = nativeViewHierarchyManager.resolveView(reactTagToReset);
|
||||
imm.restartInput(viewToReset);
|
||||
try {
|
||||
EditText textView = (EditText) viewToReset;
|
||||
textView.setSelection(selection);
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
-155
@@ -1,155 +0,0 @@
|
||||
package im.status.ethereum.module;
|
||||
|
||||
import com.facebook.react.bridge.Callback;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import android.util.Log;
|
||||
import java.util.function.Supplier;
|
||||
import java.io.File;
|
||||
import android.content.Context;
|
||||
import android.os.Environment;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONException;
|
||||
import statusgo.Statusgo;
|
||||
|
||||
public class Utils extends ReactContextBaseJavaModule {
|
||||
private static final String gethLogFileName = "geth.log";
|
||||
|
||||
private static final String TAG = "Utils";
|
||||
private ReactApplicationContext reactContext;
|
||||
|
||||
public Utils(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
this.reactContext = reactContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Utils";
|
||||
}
|
||||
public String getNoBackupDirectory() {
|
||||
return this.getReactApplicationContext().getNoBackupFilesDir().getAbsolutePath();
|
||||
}
|
||||
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public String backupDisabledDataDir() {
|
||||
return getNoBackupDirectory();
|
||||
}
|
||||
|
||||
public File getPublicStorageDirectory() {
|
||||
final Context context = this.getReactApplicationContext();
|
||||
// Environment.getExternalStoragePublicDirectory doesn't work as expected on Android Q
|
||||
// https://developer.android.com/reference/android/os/Environment#getExternalStoragePublicDirectory(java.lang.String)
|
||||
return context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
|
||||
}
|
||||
|
||||
public File getLogsFile() {
|
||||
final File pubDirectory = getPublicStorageDirectory();
|
||||
final File logFile = new File(pubDirectory, gethLogFileName);
|
||||
|
||||
return logFile;
|
||||
}
|
||||
|
||||
public String getKeyUID(final String json) throws JSONException {
|
||||
final JSONObject jsonObj = new JSONObject(json);
|
||||
return jsonObj.getString("key-uid");
|
||||
}
|
||||
|
||||
public String pathCombine(final String path1, final String path2) {
|
||||
// Replace this logic with Paths.get(path1, path2) once API level 26+ becomes the minimum supported API level
|
||||
final File file = new File(path1, path2);
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
|
||||
public String getKeyStorePath(String keyUID) {
|
||||
final String commonKeydir = pathCombine(getNoBackupDirectory(), "/keystore");
|
||||
final String keydir = pathCombine(commonKeydir, keyUID);
|
||||
|
||||
return keydir;
|
||||
}
|
||||
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public String keystoreDir() {
|
||||
final String absRootDirPath = getNoBackupDirectory();
|
||||
return pathCombine(absRootDirPath, "keystore");
|
||||
}
|
||||
|
||||
public void migrateKeyStoreDir(final String accountData, final String password) {
|
||||
try {
|
||||
final String commonKeydir = pathCombine(getNoBackupDirectory(), "/keystore");
|
||||
final String keydir = getKeyStorePath(getKeyUID(accountData));
|
||||
Log.d(TAG, "before migrateKeyStoreDir " + keydir);
|
||||
|
||||
File keydirFile = new File(keydir);
|
||||
if(!keydirFile.exists() || keydirFile.list().length == 0) {
|
||||
Log.d(TAG, "migrateKeyStoreDir");
|
||||
Statusgo.migrateKeyStoreDir(accountData, password, commonKeydir, keydir);
|
||||
Statusgo.initKeystore(keydir);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "JSON conversion failed: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean checkAvailability() {
|
||||
// We wait at least 10s for getCurrentActivity to return a value,
|
||||
// otherwise we give up
|
||||
for (int attempts = 0; attempts < 100; attempts++) {
|
||||
if (getCurrentActivity() != null) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException ex) {
|
||||
if (getCurrentActivity() != null) {
|
||||
return true;
|
||||
}
|
||||
Log.d(TAG, "Activity doesn't exist");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(TAG, "Activity doesn't exist");
|
||||
return false;
|
||||
}
|
||||
|
||||
public void executeRunnableStatusGoMethod(Supplier<String> method, Callback callback) throws JSONException {
|
||||
if (!checkAvailability()) {
|
||||
callback.invoke(false);
|
||||
return;
|
||||
}
|
||||
|
||||
Runnable runnableTask = () -> {
|
||||
String res = method.get();
|
||||
callback.invoke(res);
|
||||
};
|
||||
|
||||
StatusThreadPoolExecutor.getInstance().execute(runnableTask);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void validateMnemonic(final String seed, final Callback callback) throws JSONException {
|
||||
executeRunnableStatusGoMethod(() -> Statusgo.validateMnemonic(seed), callback);
|
||||
}
|
||||
|
||||
public Boolean is24Hour() {
|
||||
return android.text.format.DateFormat.is24HourFormat(this.reactContext.getApplicationContext());
|
||||
}
|
||||
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public String checkAddressChecksum(final String address) {
|
||||
return Statusgo.checkAddressChecksum(address);
|
||||
}
|
||||
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public String isAddress(final String address) {
|
||||
return Statusgo.isAddress(address);
|
||||
}
|
||||
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public String toChecksumAddress(final String address) {
|
||||
return Statusgo.toChecksumAddress(address);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package android.util;
|
||||
|
||||
public class Log {
|
||||
public static int d(String tag, String msg) {
|
||||
System.out.println("DEBUG: " + tag + ": " + msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int i(String tag, String msg) {
|
||||
System.out.println("INFO: " + tag + ": " + msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int w(String tag, String msg) {
|
||||
System.out.println("WARN: " + tag + ": " + msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int e(String tag, String msg) {
|
||||
System.out.println("ERROR: " + tag + ": " + msg);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package android.util
|
||||
|
||||
object Log {
|
||||
@JvmStatic
|
||||
fun d(tag: String, msg: String): Int {
|
||||
println("DEBUG: $tag: $msg")
|
||||
return 0
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun i(tag: String, msg: String): Int {
|
||||
println("INFO: $tag: $msg")
|
||||
return 0
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun w(tag: String, msg: String): Int {
|
||||
println("WARN: $tag: $msg")
|
||||
return 0
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun e(tag: String, msg: String): Int {
|
||||
println("ERROR: $tag: $msg")
|
||||
return 0
|
||||
}
|
||||
}
|
||||
@@ -93,16 +93,6 @@ RCT_EXPORT_METHOD(restoreAccountAndLogin:(NSString *)request) {
|
||||
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(deleteMultiaccount:(NSString *)keyUID
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"DeleteMultiaccount() method called");
|
||||
#endif
|
||||
NSURL *multiaccountKeystoreDir = [Utils getKeyStoreDirForKeyUID:keyUID];
|
||||
NSString *result = StatusgoDeleteMultiaccount(keyUID, multiaccountKeystoreDir.path);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(prepareDirAndUpdateConfig:(NSString *)keyUID
|
||||
config:(NSString *)config
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
@@ -114,6 +104,22 @@ RCT_EXPORT_METHOD(prepareDirAndUpdateConfig:(NSString *)keyUID
|
||||
callback(@[updatedConfig]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(saveAccountAndLogin:(NSString *)multiaccountData
|
||||
password:(NSString *)password
|
||||
settings:(NSString *)settings
|
||||
config:(NSString *)config
|
||||
accountsData:(NSString *)accountsData) {
|
||||
#if DEBUG
|
||||
NSLog(@"SaveAccountAndLogin() method called");
|
||||
#endif
|
||||
[Utils getExportDbFilePath];
|
||||
NSString *keyUID = [Utils getKeyUID:multiaccountData];
|
||||
NSString *finalConfig = [self prepareDirAndUpdateConfig:config
|
||||
withKeyUID:keyUID];
|
||||
NSString *result = StatusgoSaveAccountAndLogin(multiaccountData, password, settings, finalConfig, accountsData);
|
||||
NSLog(@"%@", result);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(saveAccountAndLoginWithKeycard:(NSString *)multiaccountData
|
||||
password:(NSString *)password
|
||||
settings:(NSString *)settings
|
||||
@@ -131,6 +137,17 @@ RCT_EXPORT_METHOD(saveAccountAndLoginWithKeycard:(NSString *)multiaccountData
|
||||
NSLog(@"%@", result);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(login:(NSString *)accountData
|
||||
password:(NSString *)password) {
|
||||
#if DEBUG
|
||||
NSLog(@"Login() method called");
|
||||
#endif
|
||||
[Utils getExportDbFilePath];
|
||||
[Utils migrateKeystore:accountData password:password];
|
||||
NSString *result = StatusgoLogin(accountData, password);
|
||||
NSLog(@"%@", result);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(loginWithKeycard:(NSString *)accountData
|
||||
password:(NSString *)password
|
||||
chatKey:(NSString *)chatKey
|
||||
@@ -214,4 +231,4 @@ RCT_EXPORT_METHOD(logout) {
|
||||
NSLog(@"%@", result);
|
||||
}
|
||||
|
||||
@end
|
||||
@end
|
||||
@@ -30,4 +30,4 @@ RCT_EXPORT_METHOD(importUnencryptedDatabase:(NSString *)accountData
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@end
|
||||
@@ -121,6 +121,13 @@ RCT_EXPORT_METHOD(hashMessage:(NSString *)message
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(multiformatSerializePublicKey:(NSString *)multiCodecKey
|
||||
base58btc:(NSString *)base58btc
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
NSString *result = StatusgoMultiformatSerializePublicKey(multiCodecKey,base58btc);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(localPairingPreflightOutboundCheck:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"LocalPairingPreflightOutboundCheck() method called");
|
||||
@@ -136,6 +143,18 @@ RCT_EXPORT_METHOD(multiformatDeserializePublicKey:(NSString *)multiCodecKey
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(compressPublicKey:(NSString *)multiCodecKey
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
NSString *result = StatusgoCompressPublicKey(multiCodecKey);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(decompressPublicKey:(NSString *)multiCodecKey
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
NSString *result = StatusgoDecompressPublicKey(multiCodecKey);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(deserializeAndCompressKey:(NSString *)desktopKey
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
NSString *result = StatusgoDeserializeAndCompressKey(desktopKey);
|
||||
@@ -197,4 +216,26 @@ RCT_EXPORT_METHOD(signTypedDataV4:(NSString *)data
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
#pragma mark - ExtractGroupMembershipSignatures
|
||||
|
||||
RCT_EXPORT_METHOD(extractGroupMembershipSignatures:(NSString *)content
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"ExtractGroupMembershipSignatures() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoExtractGroupMembershipSignatures(content);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
#pragma mark - SignGroupMembership
|
||||
|
||||
RCT_EXPORT_METHOD(signGroupMembership:(NSString *)content
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"SignGroupMembership() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoSignGroupMembership(content);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -8,6 +8,15 @@
|
||||
|
||||
RCT_EXPORT_MODULE();
|
||||
|
||||
RCT_EXPORT_METHOD(addPeer:(NSString *)enode
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
NSString *result = StatusgoAddPeer(enode);
|
||||
callback(@[result]);
|
||||
#if DEBUG
|
||||
NSLog(@"AddPeer() method called");
|
||||
#endif
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(startSearchForLocalPairingPeers:(RCTResponseSenderBlock)callback) {
|
||||
NSString *result = StatusgoStartSearchForLocalPairingPeers();
|
||||
callback(@[result]);
|
||||
@@ -106,4 +115,4 @@ RCT_EXPORT_METHOD(recover:(NSString *)message
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
@end
|
||||
@end
|
||||
@@ -71,6 +71,16 @@ RCT_EXPORT_METHOD(exportLogs:(RCTResponseSenderBlock)callback) {
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(deleteMultiaccount:(NSString *)keyUID
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"DeleteMultiaccount() method called");
|
||||
#endif
|
||||
NSURL *multiaccountKeystoreDir = [Utils getKeyStoreDirForKeyUID:keyUID];
|
||||
NSString *result = StatusgoDeleteMultiaccount(keyUID, multiaccountKeystoreDir.path);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(deleteImportedKey:(NSString *)keyUID
|
||||
address:(NSString *)address
|
||||
password:(NSString *)password
|
||||
@@ -110,6 +120,14 @@ RCT_EXPORT_METHOD(multiAccountLoadAccount:(NSString *)json
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(multiAccountReset:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"MultiAccountReset() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoMultiAccountReset();
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(multiAccountStoreDerived:(NSString *)json
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
@@ -179,6 +197,13 @@ RCT_EXPORT_METHOD(appStateChange:(NSString *)type) {
|
||||
StatusgoAppStateChange(type);
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(stopLocalNotifications) {
|
||||
#if DEBUG
|
||||
NSLog(@"StopLocalNotifications() method called");
|
||||
#endif
|
||||
StatusgoStopLocalNotifications();
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(startLocalNotifications) {
|
||||
#if DEBUG
|
||||
NSLog(@"StartLocalNotifications() method called");
|
||||
@@ -186,6 +211,20 @@ RCT_EXPORT_METHOD(startLocalNotifications) {
|
||||
StatusgoStartLocalNotifications();
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(activateKeepAwake)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[UIApplication sharedApplication] setIdleTimerDisabled:YES];
|
||||
});
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(deactivateKeepAwake)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[UIApplication sharedApplication] setIdleTimerDisabled:NO];
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - deviceinfo
|
||||
|
||||
- (bool) is24Hour
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#import <sys/utsname.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <React/RCTBridgeModule.h>
|
||||
#import "Statusgo.h"
|
||||
#import "RCTLog.h"
|
||||
|
||||
@interface UIHelper : NSObject <RCTBridgeModule>
|
||||
|
||||
@@ -8,6 +8,18 @@ RCT_EXPORT_MODULE();
|
||||
|
||||
#pragma mark - only android methods
|
||||
|
||||
RCT_EXPORT_METHOD(setAdjustResize) {
|
||||
#if DEBUG
|
||||
NSLog(@"setAdjustResize() works only on Android");
|
||||
#endif
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(setAdjustPan) {
|
||||
#if DEBUG
|
||||
NSLog(@"setAdjustPan() works only on Android");
|
||||
#endif
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(setSoftInputMode: (NSInteger) i) {
|
||||
#if DEBUG
|
||||
NSLog(@"setSoftInputMode() works only on Android");
|
||||
|
||||
@@ -12,7 +12,6 @@ stdenv.mkDerivation {
|
||||
"patchBuildIdPhase"
|
||||
"patchKeyChainPhase"
|
||||
"patchGlogPhase"
|
||||
"patchBoostPodSpec"
|
||||
"installPhase"
|
||||
];
|
||||
|
||||
@@ -78,14 +77,6 @@ stdenv.mkDerivation {
|
||||
--replace 'export CXX="' '#export CXX="'
|
||||
'';
|
||||
|
||||
# to fix pod checksum issue : https://github.com/facebook/react-native/issues/42180
|
||||
# TODO remove this patch after upgrading to react-native 0.73.2
|
||||
patchBoostPodSpec = ''
|
||||
substituteInPlace ./node_modules/react-native/third-party-podspecs/boost.podspec \
|
||||
--replace 'https://boostorg.jfrog.io/artifactory/main/release/1.76.0/source/boost_1_76_0.tar.bz2' \
|
||||
'https://sourceforge.net/projects/boost/files/boost/1.76.0/boost_1_76_0.tar.bz2' \
|
||||
'';
|
||||
|
||||
# The ELF types are incompatible with the host platform, so let's not even try
|
||||
# TODO: Use Android NDK to strip binaries manually
|
||||
dontPatchELF = true;
|
||||
|
||||
@@ -3,6 +3,6 @@ name: default: let
|
||||
logEnvOverride = value:
|
||||
builtins.trace "getEnvWithDefault ${name} (default: ${toString default}): ${value}" value;
|
||||
in
|
||||
if envOverride != "" && envOverride != default
|
||||
if envOverride != ""
|
||||
then logEnvOverride envOverride
|
||||
else default
|
||||
|
||||
+1
-1
@@ -69,7 +69,7 @@ in {
|
||||
yarn = super.yarn.override { nodejs = super.nodejs-18_x; };
|
||||
openjdk = super.openjdk11_headless;
|
||||
xcodeWrapper = super.xcodeenv.composeXcodeWrapper {
|
||||
version = "15.0";
|
||||
version = "14.0";
|
||||
allowHigher = true;
|
||||
};
|
||||
go = super.go_1_19;
|
||||
|
||||
@@ -89,7 +89,7 @@ if [[ -n "${nixResultPath}" ]]; then
|
||||
toDelete=$(findByResult "${nixResultPath}")
|
||||
else
|
||||
# use regular expression that should match all status-mobile build artifacts
|
||||
toDelete=$(findByRegex '.*-status-(mobile|go)-(shell|source|build|patched-npm-gradle-modules).*')
|
||||
toDelete=$(findByRegex '.*-status-(react|go)-(shell|source|build|patched-npm-gradle-modules).*')
|
||||
fi
|
||||
|
||||
# remove duplicates and return
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
ios = {targets ? [ "ios/arm64" "iossimulator/amd64"]}: callPackage ./build.nix {
|
||||
platform = "ios";
|
||||
platformVersion = "11.0";
|
||||
platformVersion = "8.0";
|
||||
outputFileName = "Statusgo.xcframework";
|
||||
inherit meta source goBuildLdFlags targets;
|
||||
};
|
||||
|
||||
@@ -102,12 +102,6 @@
|
||||
"rn-snoopy": "git+https://github.com/status-im/rn-snoopy.git#refs/tags/v2.0.2-status",
|
||||
"shadow-cljs": "2.26.2"
|
||||
},
|
||||
"resolutions": {
|
||||
"_comment": "This prevents random metro crashes till we upgrade to react-native 0.73.x",
|
||||
"metro": "^0.79.1",
|
||||
"metro-config": "^0.79.1",
|
||||
"metro-core": "^0.79.1"
|
||||
},
|
||||
"binary": {
|
||||
"module_name": "status_nodejs_addon",
|
||||
"module_path": "./lib/binding/",
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.0 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.6 KiB |
@@ -2,7 +2,7 @@
|
||||
|
||||
set -eof pipefail
|
||||
|
||||
FILES=$(comm -23 <(sort <(git ls-files --cached --others --exclude-standard)) <(sort <(git ls-files --deleted)) | grep --ignore-case -E '\.(java|cpp|nix|json|sh|md|js|clj|cljs|cljc|edn|kt|m)$')
|
||||
FILES=$(comm -23 <(sort <(git ls-files --cached --others --exclude-standard)) <(sort <(git ls-files --deleted)) | grep --ignore-case -E '\.(java|cpp|nix|json|sh|md|js|clj|cljs|cljc|edn)$')
|
||||
N_FILES=$(echo "$FILES" | wc -l)
|
||||
LINT_SHOULD_FIX=0
|
||||
|
||||
|
||||
+2
-2
@@ -108,13 +108,13 @@
|
||||
;; produced by the target :mocks below and redefines node require
|
||||
;; function to use the mocks instead of the rn libraries
|
||||
:test
|
||||
{:output-to #shadow/env "SHADOW_OUTPUT_TO"
|
||||
{:output-to "target/test/test.js"
|
||||
:output-dir "target/test"
|
||||
:optimizations :simple
|
||||
:target :node-test
|
||||
:dev {:devtools {:preloads [status-im.setup.schema-preload]}}
|
||||
;; Uncomment line below to `make test-watch` a specific file
|
||||
:ns-regexp #shadow/env "SHADOW_NS_REGEXP"
|
||||
;; :ns-regexp "status-im.subs.messages-test$"
|
||||
:main legacy.status-im.test-runner/main
|
||||
;; set :ui-driven to true to let shadow-cljs inject node-repl
|
||||
:ui-driven true
|
||||
|
||||
@@ -39,16 +39,14 @@ export function interpolateNavigationViewOpacity(props) {
|
||||
});
|
||||
}
|
||||
|
||||
export function messagesListOnScroll(distanceFromListTop, chatListScrollY, callback) {
|
||||
export function messagesListOnScroll(distanceFromListTop, callback) {
|
||||
return function (event) {
|
||||
'worklet';
|
||||
const currentY = event.contentOffset.y;
|
||||
const layoutHeight = event.layoutMeasurement.height;
|
||||
const contentSizeY = event.contentSize.height - layoutHeight;
|
||||
const newDistance = contentSizeY - currentY;
|
||||
distanceFromListTop.value = newDistance;
|
||||
chatListScrollY.value = currentY;
|
||||
runOnJS(callback)(layoutHeight, newDistance);
|
||||
distanceFromListTop.value = contentSizeY - currentY;
|
||||
runOnJS(callback)(currentY, layoutHeight);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -65,28 +63,3 @@ export function placeholderZIndex(isCalculationsComplete) {
|
||||
return isCalculationsComplete.value ? 0 : 2;
|
||||
});
|
||||
}
|
||||
|
||||
export function scrollDownButtonOpacity(chatListScrollY, isComposerFocused, windowHeight) {
|
||||
return useDerivedValue(function () {
|
||||
'worklet';
|
||||
if (isComposerFocused.value) {
|
||||
return 0;
|
||||
} else {
|
||||
return chatListScrollY.value > windowHeight * 0.75 ? 1 : 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function jumpToButtonOpacity(scrollDownButtonOpacity, isComposerFocused) {
|
||||
return useDerivedValue(function () {
|
||||
'worklet';
|
||||
return withTiming(scrollDownButtonOpacity.value == 1 || isComposerFocused.value ? 0 : 1);
|
||||
});
|
||||
}
|
||||
|
||||
export function jumpToButtonPosition(scrollDownButtonOpacity, isComposerFocused) {
|
||||
return useDerivedValue(function () {
|
||||
'worklet';
|
||||
return withTiming(scrollDownButtonOpacity.value == 1 || isComposerFocused.value ? 35 : 0);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
[legacy.status-im.data-store.messages :as data-store.messages]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.contexts.chat.messenger.messages.list.events :as message-list]
|
||||
[status-im.contexts.chat.messages.list.events :as message-list]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
[legacy.status-im.utils.deprecated-types :as types]
|
||||
[re-frame.core :as re-frame]
|
||||
[react-native.platform :as platform]
|
||||
[status-im.contexts.chat.messenger.messages.delete-message.events :as delete-message]
|
||||
[status-im.contexts.chat.messenger.messages.list.events :as message-list]
|
||||
[status-im.contexts.chat.messenger.messages.list.state :as view.state]
|
||||
[status-im.contexts.chat.messages.delete-message.events :as delete-message]
|
||||
[status-im.contexts.chat.messages.list.events :as message-list]
|
||||
[status-im.contexts.chat.messages.list.state :as view.state]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn- message-loaded?
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
[cljs.test :refer-macros [deftest is testing]]
|
||||
[legacy.status-im.chat.models.loading :as loading]
|
||||
[legacy.status-im.chat.models.message :as message]
|
||||
[status-im.contexts.chat.messenger.messages.list.state :as list.state]))
|
||||
[status-im.contexts.chat.messages.list.state :as list.state]))
|
||||
|
||||
(deftest add-received-message-test
|
||||
(with-redefs [message/add-message #(identity %1)]
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
[legacy.status-im.utils.deprecated-types :as types]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.contexts.chat.contacts.events :as contacts-store]
|
||||
[status-im.contexts.chat.messenger.messages.list.events :as message-list]
|
||||
[status-im.contexts.chat.messages.list.events :as message-list]
|
||||
[status-im.contexts.shell.activity-center.events :as activity-center]
|
||||
[status-im.navigation.events :as navigation]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
@@ -36,13 +36,6 @@
|
||||
:always
|
||||
(update :contacts conj (:id member))))
|
||||
|
||||
(defn decode-chat-id
|
||||
[chat-id]
|
||||
(let [community-id (subs chat-id 0 constants/community-id-length)
|
||||
channel-id (subs chat-id constants/community-id-length)]
|
||||
{:community-id community-id
|
||||
:channel-id channel-id}))
|
||||
|
||||
(defn- unmarshal-members
|
||||
[{:keys [members chat-type] :as chat}]
|
||||
(cond
|
||||
|
||||
@@ -41,11 +41,3 @@
|
||||
:timestamp 2}]
|
||||
(testing "from-rpc"
|
||||
(is (= expected-chat (chats/<-rpc chat))))))
|
||||
|
||||
(deftest decode-chat-id-test
|
||||
(let [community-id "0x0322b9b84acb1631d6a31d41d9cf8e1938d352a624bc130de92869e025c7ca79c4"
|
||||
channel-id "02081bc7-20e4-4362-99e2-37669c28cc08"
|
||||
chat-id (str community-id channel-id)]
|
||||
(is (= {:community-id community-id
|
||||
:channel-id channel-id}
|
||||
(chats/decode-chat-id chat-id)))))
|
||||
|
||||
@@ -51,9 +51,9 @@
|
||||
|
||||
(rf/defn remove-members
|
||||
{:events [:group-chats.ui/remove-members-pressed]}
|
||||
[{{:group-chat/keys [deselected-members]} :db :as cofx} chat-id]
|
||||
[{{:keys [current-chat-id] :group-chat/keys [deselected-members]} :db :as cofx}]
|
||||
{:json-rpc/call [{:method "wakuext_removeMembersFromGroupChat"
|
||||
:params [nil chat-id deselected-members]
|
||||
:params [nil current-chat-id deselected-members]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:chat-updated % true])
|
||||
:on-error #()}]})
|
||||
@@ -97,19 +97,19 @@
|
||||
(rf/defn add-members
|
||||
"Add members to a group chat"
|
||||
{:events [:group-chats.ui/add-members-pressed]}
|
||||
[{{:group-chat/keys [selected-participants]} :db :as cofx} chat-id]
|
||||
[{{:keys [current-chat-id] :group-chat/keys [selected-participants]} :db :as cofx}]
|
||||
{:json-rpc/call [{:method "wakuext_addMembersToGroupChat"
|
||||
:params [nil chat-id selected-participants]
|
||||
:params [nil current-chat-id selected-participants]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:chat-updated % true])}]})
|
||||
|
||||
(rf/defn add-members-from-invitation
|
||||
"Add members to a group chat"
|
||||
{:events [:group-chats.ui/add-members-from-invitation]}
|
||||
[{:keys [db]} id participant chat-id]
|
||||
[{{:keys [current-chat-id] :as db} :db :as cofx} id participant]
|
||||
{:db (assoc-in db [:group-chat/invitations id :state] constants/invitation-state-approved)
|
||||
:json-rpc/call [{:method "wakuext_addMembersToGroupChat"
|
||||
:params [nil chat-id [participant]]
|
||||
:params [nil current-chat-id [participant]]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:chat-updated %])}]})
|
||||
|
||||
@@ -150,23 +150,23 @@
|
||||
|
||||
(rf/defn membership-retry
|
||||
{:events [:group-chats.ui/membership-retry]}
|
||||
[{:keys [db]} chat-id]
|
||||
{:db (assoc-in db [:chat/memberships chat-id :retry?] true)})
|
||||
[{{:keys [current-chat-id] :as db} :db}]
|
||||
{:db (assoc-in db [:chat/memberships current-chat-id :retry?] true)})
|
||||
|
||||
(rf/defn membership-message
|
||||
{:events [:group-chats.ui/update-membership-message]}
|
||||
[{:keys [db]} message chat-id]
|
||||
{:db (assoc-in db [:chat/memberships chat-id :message] message)})
|
||||
[{{:keys [current-chat-id] :as db} :db} message]
|
||||
{:db (assoc-in db [:chat/memberships current-chat-id :message] message)})
|
||||
|
||||
(rf/defn send-group-chat-membership-request
|
||||
"Send group chat membership request"
|
||||
{:events [:send-group-chat-membership-request]}
|
||||
[{{:keys [chats] :as db} :db :as cofx} chat-id]
|
||||
(let [{:keys [invitation-admin]} (get chats chat-id)
|
||||
message (get-in db [:chat/memberships chat-id :message])]
|
||||
{:db (assoc-in db [:chat/memberships chat-id] nil)
|
||||
[{{:keys [current-chat-id chats] :as db} :db :as cofx}]
|
||||
(let [{:keys [invitation-admin]} (get chats current-chat-id)
|
||||
message (get-in db [:chat/memberships current-chat-id :message])]
|
||||
{:db (assoc-in db [:chat/memberships current-chat-id] nil)
|
||||
:json-rpc/call [{:method "wakuext_sendGroupChatInvitationRequest"
|
||||
:params [nil chat-id invitation-admin message]
|
||||
:params [nil current-chat-id invitation-admin message]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]}))
|
||||
|
||||
@@ -242,6 +242,15 @@
|
||||
[{db :db}]
|
||||
{:db (assoc db :group-chat/deselected-members #{})})
|
||||
|
||||
(rf/defn show-group-chat-profile
|
||||
{:events [:show-group-chat-profile]}
|
||||
[{:keys [db] :as cofx} chat-id]
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(assoc :new-chat-name (get-in db [:chats chat-id :name]))
|
||||
(assoc :current-chat-id chat-id))}
|
||||
(navigation/navigate-to :group-chat-profile nil)))
|
||||
|
||||
(rf/defn ui-leave-chat-pressed
|
||||
{:events [:group-chats.ui/leave-chat-pressed]}
|
||||
[{:keys [db]} chat-id]
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
(def message-max-length 100)
|
||||
|
||||
(defn request-membership
|
||||
[{:keys [state introduction-message] :as invitation} chat-id]
|
||||
[{:keys [state introduction-message] :as invitation}]
|
||||
(let [{:keys [message retry?]} @(re-frame/subscribe [:chats/current-chat-membership])
|
||||
message-length (count message)]
|
||||
[react/view {:margin-horizontal 16 :margin-top 10}
|
||||
@@ -47,7 +47,7 @@
|
||||
[react/text (i18n/label :t/introduce-yourself)]
|
||||
[quo/text-input
|
||||
{:placeholder (i18n/label :t/message)
|
||||
:on-change-text #(re-frame/dispatch [:group-chats.ui/update-membership-message % chat-id])
|
||||
:on-change-text #(re-frame/dispatch [:group-chats.ui/update-membership-message %])
|
||||
:max-length (if platform/android?
|
||||
message-max-length
|
||||
(when (>= message-length message-max-length)
|
||||
@@ -63,4 +63,4 @@
|
||||
[chat-id invitation-admin]
|
||||
(letsubs [invitations [:group-chat/invitations-by-chat-id chat-id]]
|
||||
(when invitation-admin
|
||||
[request-membership (first invitations) chat-id])))
|
||||
[request-membership (first invitations)])))
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
[quo.foundations.typography :as typography]
|
||||
[react-native.core :as rn]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.contexts.chat.messenger.messages.delete-message-for-me.events]
|
||||
[status-im.contexts.chat.messenger.messages.delete-message.events]
|
||||
[status-im.contexts.chat.messages.delete-message-for-me.events]
|
||||
[status-im.contexts.chat.messages.delete-message.events]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf])
|
||||
(:require-macros [legacy.status-im.utils.views :refer [defview letsubs]]))
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
[legacy.status-im.ui.components.react :as react]
|
||||
[legacy.status-im.ui.screens.link-previews-settings.styles :as styles]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.contexts.chat.messenger.messages.link-preview.events]
|
||||
[status-im.contexts.chat.messages.link-preview.events]
|
||||
[utils.i18n :as i18n]))
|
||||
|
||||
(defn prepare-urls-items-data
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
[legacy.status-im.ui.components.typography :as typography]
|
||||
[legacy.status-im.utils.utils :as utils]
|
||||
[re-frame.core :as re-frame]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.common.json-rpc.events :as json-rpc]
|
||||
[taoensso.timbre :as log]
|
||||
@@ -85,28 +86,29 @@
|
||||
|
||||
(defn stats-table
|
||||
[{:keys [total filtered-total stats]}]
|
||||
[react/scroll-view
|
||||
{:style {:padding-horizontal 8}}
|
||||
[react/view
|
||||
[react/view
|
||||
{:style {:flex-direction :row
|
||||
:justify-content :space-between
|
||||
:margin-bottom 2}}
|
||||
[legacy.status-im.ui.components.core/text {:style typography/font-semi-bold}
|
||||
(i18n/label :t/rpc-usage-total)]
|
||||
[legacy.status-im.ui.components.core/text {:style typography/font-semi-bold}
|
||||
(i18n/label :t/rpc-usage-filtered-total {:filtered-total filtered-total :total total})]]
|
||||
[react/view
|
||||
{:style {:flex-direction :column
|
||||
:flex 1}}
|
||||
(when (seq stats)
|
||||
(for [[k v] stats]
|
||||
^{:key (str k v)}
|
||||
[react/view
|
||||
{:style {:flex-direction :row
|
||||
:align-items :stretch}}
|
||||
[legacy.status-im.ui.components.core/text {:style {:flex 7}} k]
|
||||
[legacy.status-im.ui.components.core/text {:style {:flex 1}} v]]))]]])
|
||||
rn/scroll-view
|
||||
{:style {:padding-horizontal 8}}
|
||||
rn/view
|
||||
{:style {:flex-direction :row
|
||||
:justify-content :space-between
|
||||
:margin-bottom 2}}
|
||||
[legacy.status-im.ui.components.core/text {:style typography/font-semi-bold}
|
||||
(i18n/label :t/rpc-usage-total)]
|
||||
[legacy.status-im.ui.components.core/text {:style typography/font-semi-bold}
|
||||
(i18n/label :t/rpc-usage-filtered-total {:filtered-total filtered-total :total total})]
|
||||
(when (seq stats)
|
||||
(for [[k v] stats]
|
||||
^{:key (str k v)}
|
||||
[:<>
|
||||
rn/view
|
||||
{:style {:flex-direction :row
|
||||
:align-items :center
|
||||
:margin-vertical 10}}
|
||||
[legacy.status-im.ui.components.core/text {:style {:flex 1}}
|
||||
k]
|
||||
[legacy.status-im.ui.components.core/text {:style {:margin-left 16}}
|
||||
v]
|
||||
[legacy.status-im.ui.components.core/separator]])))
|
||||
|
||||
(defn prepare-stats
|
||||
[{:keys [stats]}]
|
||||
@@ -123,32 +125,30 @@
|
||||
[react/view
|
||||
{:flex 1
|
||||
:margin-horizontal 8}
|
||||
[react/view
|
||||
{:style {:flex-direction :column
|
||||
:justify-content :space-between}}
|
||||
[react/view
|
||||
{:style {:flex-direction :row
|
||||
:justify-content :space-between}}
|
||||
[legacy.status-im.ui.components.core/button
|
||||
{:on-press #(re-frame/dispatch [::reset])
|
||||
:accessibility-label :rpc-usage-reset}
|
||||
(i18n/label :t/rpc-usage-reset)]
|
||||
[legacy.status-im.ui.components.core/button
|
||||
{:on-press
|
||||
#(react/copy-to-clipboard (prepare-stats stats))
|
||||
:accessibility-label :rpc-usage-copy}
|
||||
(i18n/label :t/rpc-usage-copy)]]
|
||||
[legacy.status-im.ui.components.core/text-input
|
||||
{:on-change-text #(re-frame/dispatch [::set-filter %])
|
||||
:label (i18n/label :t/rpc-usage-filter-methods)
|
||||
:placeholder (i18n/label :t/rpc-usage-filter)
|
||||
:container-style {:margin-vertical 18}
|
||||
:before {:icon :main-icons/search
|
||||
:style {:padding-horizontal 8}}
|
||||
:default-value methods-filter
|
||||
:auto-capitalize :none
|
||||
:show-cancel false
|
||||
:auto-focus false}]]
|
||||
rn/view
|
||||
{:style {:flex-direction :row
|
||||
:margin-vertical 8
|
||||
:justify-content :space-between}}
|
||||
[legacy.status-im.ui.components.core/button
|
||||
{:on-press #(re-frame/dispatch [::reset])
|
||||
:accessibility-label :rpc-usage-reset}
|
||||
(i18n/label :t/rpc-usage-reset)]
|
||||
[legacy.status-im.ui.components.core/button
|
||||
{:on-press
|
||||
#(react/copy-to-clipboard (prepare-stats stats))
|
||||
:accessibility-label :rpc-usage-copy}
|
||||
(i18n/label :t/rpc-usage-copy)]
|
||||
[legacy.status-im.ui.components.core/text-input
|
||||
{:on-change-text #(re-frame/dispatch [::set-filter %])
|
||||
:label (i18n/label :t/rpc-usage-filter-methods)
|
||||
:placeholder (i18n/label :t/rpc-usage-filter)
|
||||
:container-style {:margin-vertical 18}
|
||||
:before {:icon :main-icons/search
|
||||
:style {:padding-horizontal 8}}
|
||||
:default-value methods-filter
|
||||
:auto-capitalize :none
|
||||
:show-cancel false
|
||||
:auto-focus false}]
|
||||
[stats-table stats]]))
|
||||
|
||||
(defn usage-info
|
||||
|
||||
@@ -31,81 +31,104 @@
|
||||
(.pollSignal native-status signal-received-callback)
|
||||
100))))
|
||||
|
||||
(def ui-helper
|
||||
(def status
|
||||
(clj->js
|
||||
{:clearCookies identity
|
||||
:clearStorageAPIs identity}))
|
||||
{:openAccounts
|
||||
(fn [callback]
|
||||
(callback (.openAccounts native-status test-dir)))
|
||||
|
||||
:multiAccountStoreDerived
|
||||
(fn [json callback]
|
||||
(callback (.multiAccountStoreDerivedAccounts native-status json)))
|
||||
|
||||
:getNodeConfig (fn [] (types/clj->json {:WakuV2Config ""}))
|
||||
|
||||
:backupDisabledDataDir (fn [] (str test-dir "/backup"))
|
||||
:keystoreDir (fn [] "")
|
||||
:logFileDirectory (fn [] (str test-dir "/log"))
|
||||
:clearCookies identity
|
||||
:clearStorageAPIs identity
|
||||
:setBlankPreviewFlag identity
|
||||
|
||||
:callPrivateRPC
|
||||
(fn [payload callback]
|
||||
(callback (.callPrivateRPC native-status payload)))
|
||||
|
||||
:createAccountAndLogin (fn [request] (.createAccountAndLogin native-status request))
|
||||
|
||||
:saveAccountAndLogin
|
||||
(fn [multiaccount-data password settings config accounts-data]
|
||||
(.saveAccountAndLogin native-status multiaccount-data password settings config accounts-data))
|
||||
|
||||
:logout
|
||||
(fn [] (.logout native-status))
|
||||
|
||||
:multiAccountGenerateAndDeriveAddresses
|
||||
(fn [json callback]
|
||||
(callback (.multiAccountGenerateAndDeriveAddresses native-status json)))
|
||||
|
||||
:multiAccountImportMnemonic
|
||||
(fn [json callback]
|
||||
(callback (.multiAccountImportMnemonic native-status json)))
|
||||
|
||||
:multiAccountLoadAccount
|
||||
(fn [json callback]
|
||||
(callback (.multiAccountLoadAccount native-status json)))
|
||||
|
||||
:multiAccountDeriveAddresses
|
||||
(fn [json callback]
|
||||
(callback (.multiAccountDeriveAddresses native-status json)))
|
||||
|
||||
(def encryption-utils
|
||||
(clj->js
|
||||
{:sha3
|
||||
(fn [s] (.sha3 native-status s))
|
||||
:setBlankPreviewFlag
|
||||
identity
|
||||
:encodeTransfer
|
||||
(fn [to-norm amount-hex]
|
||||
(.encodeTransfer native-status to-norm amount-hex))
|
||||
:hexToNumber
|
||||
(fn [hex] (.hexToNumber native-status hex))
|
||||
:decodeParameters
|
||||
(fn [decode-param-json]
|
||||
(.decodeParameters native-status decode-param-json))
|
||||
:numberToHex
|
||||
(fn [num-str] (.numberToHex native-status num-str))
|
||||
:initKeystore
|
||||
(fn [key-uid callback]
|
||||
(callback (.initKeystore native-status
|
||||
(str test-dir "/keystore/" key-uid))))
|
||||
:multiformatDeserializePublicKey
|
||||
(fn [public-key deserialization-key callback]
|
||||
(callback (.multiformatDeserializePublicKey
|
||||
native-status
|
||||
public-key
|
||||
deserialization-key)))}))
|
||||
deserialization-key)))
|
||||
|
||||
(def account-manager
|
||||
(clj->js
|
||||
{:openAccounts
|
||||
(fn [callback]
|
||||
(callback (.openAccounts native-status test-dir)))
|
||||
:createAccountAndLogin
|
||||
(fn [request] (.createAccountAndLogin native-status request))
|
||||
:logout
|
||||
(fn [] (.logout native-status))
|
||||
:multiAccountImportMnemonic
|
||||
(fn [json callback]
|
||||
(callback (.multiAccountImportMnemonic native-status json)))
|
||||
:multiAccountLoadAccount
|
||||
(fn [json callback]
|
||||
(callback (.multiAccountLoadAccount native-status json)))
|
||||
:multiAccountDeriveAddresses
|
||||
(fn [json callback]
|
||||
(callback (.multiAccountDeriveAddresses native-status json)))
|
||||
:multiAccountGenerateAndDeriveAddresses
|
||||
(fn [json callback]
|
||||
(callback (.multiAccountGenerateAndDeriveAddresses native-status json)))
|
||||
:multiAccountStoreDerived
|
||||
(fn [json callback]
|
||||
(callback (.multiAccountStoreDerivedAccounts native-status json)))}))
|
||||
:initKeystore
|
||||
(fn [key-uid callback]
|
||||
(callback (.initKeystore native-status
|
||||
(str test-dir "/keystore/" key-uid))))
|
||||
|
||||
:encodeTransfer
|
||||
(fn [to-norm amount-hex]
|
||||
(.encodeTransfer native-status to-norm amount-hex))
|
||||
|
||||
:encodeFunctionCall
|
||||
(fn [method params-json]
|
||||
(.encodeFunctionCall native-status method params-json))
|
||||
|
||||
:decodeParameters
|
||||
(fn [decode-param-json]
|
||||
(.decodeParameters native-status decode-param-json))
|
||||
|
||||
:hexToNumber
|
||||
(fn [hex] (.hexToNumber native-status hex))
|
||||
|
||||
:numberToHex
|
||||
(fn [num-str] (.numberToHex native-status num-str))
|
||||
|
||||
(def utils
|
||||
(clj->js
|
||||
{:backupDisabledDataDir
|
||||
(fn [] (str test-dir "/backup"))
|
||||
:keystoreDir (fn [] "")
|
||||
:toChecksumAddress
|
||||
(fn [address] (.toChecksumAddress native-status address))
|
||||
:checkAddressChecksum
|
||||
(fn [address] (.checkAddressChecksum native-status address))
|
||||
|
||||
:sha3
|
||||
(fn [s] (.sha3 native-status s))
|
||||
|
||||
:toChecksumAddress
|
||||
(fn [address] (.toChecksumAddress native-status address))
|
||||
|
||||
:isAddress
|
||||
(fn [address] (.isAddress native-status address))
|
||||
|
||||
:fleets
|
||||
(fn [] (.fleets native-status))
|
||||
|
||||
:validateMnemonic
|
||||
(fn [json callback] (callback (.validateMnemonic native-status json)))
|
||||
:isAddress
|
||||
(fn [address] (.isAddress native-status address))}))
|
||||
|
||||
(def log-manager
|
||||
(clj->js
|
||||
{:logFileDirectory
|
||||
(fn [] (str test-dir "/log"))
|
||||
:startLocalNotifications identity
|
||||
|
||||
:initLogging
|
||||
(fn [enabled mobile-system log-level callback]
|
||||
(callback (.initLogging native-status
|
||||
@@ -113,18 +136,3 @@
|
||||
:MobileSystem mobile-system
|
||||
:Level log-level
|
||||
:File (str test-dir "/geth.log")}))))}))
|
||||
|
||||
(def network
|
||||
(clj->js
|
||||
{:callPrivateRPC
|
||||
(fn [payload callback]
|
||||
(callback (.callPrivateRPC native-status payload)))}))
|
||||
|
||||
(def status
|
||||
(clj->js
|
||||
{:getNodeConfig
|
||||
(fn [] (types/clj->json {:WakuV2Config ""}))
|
||||
:fleets
|
||||
(fn [] (.fleets native-status))
|
||||
:startLocalNotifications
|
||||
identity}))
|
||||
|
||||
@@ -38,12 +38,6 @@
|
||||
{:NativeModules {:RNGestureHandlerModule {:Direction (fn [])}
|
||||
:PushNotifications {}
|
||||
:Status utils.test/status
|
||||
:EncryptionUtils utils.test/encryption-utils
|
||||
:AccountManager utils.test/account-manager
|
||||
:Utils utils.test/utils
|
||||
:LogManager utils.test/log-manager
|
||||
:NetworkManager utils.test/network
|
||||
:UIHelper utils.test/ui-helper
|
||||
:ReanimatedModule {:configureProps (fn [])}}
|
||||
|
||||
:View {}
|
||||
|
||||
@@ -29,22 +29,22 @@
|
||||
(defn ui-helper
|
||||
[]
|
||||
(when (exists? (.-NativeModules react-native))
|
||||
(.-UIHelper ^js (.-NativeModules react-native))))
|
||||
(.-UIHelper ^js (.-NativeModules react-native))))
|
||||
|
||||
(defn log-manager
|
||||
[]
|
||||
(when (exists? (.-NativeModules react-native))
|
||||
(.-LogManager ^js (.-NativeModules react-native))))
|
||||
(.-LogManager ^js (.-NativeModules react-native))))
|
||||
|
||||
(defn utils
|
||||
[]
|
||||
(when (exists? (.-NativeModules react-native))
|
||||
(.-Utils ^js (.-NativeModules react-native))))
|
||||
(.-Utils ^js (.-NativeModules react-native))))
|
||||
|
||||
(defn network
|
||||
[]
|
||||
(when (exists? (.-NativeModules react-native))
|
||||
(.-NetworkManager ^js (.-NativeModules react-native))))
|
||||
(.-NetworkManager ^js (.-NativeModules react-native))))
|
||||
|
||||
(defn init
|
||||
[handler]
|
||||
@@ -53,7 +53,7 @@
|
||||
(defn clear-web-data
|
||||
[]
|
||||
(log/debug "[native-module] clear-web-data")
|
||||
(when (ui-helper)
|
||||
(when (status)
|
||||
(.clearCookies ^js (ui-helper))
|
||||
(.clearStorageAPIs ^js (ui-helper))))
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
(defn login-account
|
||||
"NOTE: beware, the password has to be sha3 hashed"
|
||||
[{:keys [keyUid] :as request}]
|
||||
(log/debug "[native-module] loginAccount")
|
||||
(log/debug "[native-module] loginWithConfig")
|
||||
(clear-web-data)
|
||||
(init-keystore
|
||||
keyUid
|
||||
@@ -247,11 +247,7 @@
|
||||
(clear-web-data)
|
||||
(init-keystore
|
||||
key-uid
|
||||
#(.loginWithKeycard ^js (account-manager)
|
||||
multiaccount-data
|
||||
password
|
||||
chat-key
|
||||
(types/clj->json node-config))))
|
||||
#(.loginWithKeycard ^js (account-manager) multiaccount-data password chat-key (types/clj->json node-config))))
|
||||
|
||||
(defn set-soft-input-mode
|
||||
[mode]
|
||||
@@ -291,7 +287,7 @@
|
||||
[callback]
|
||||
(log/info "[native-module] Performing local pairing preflight check")
|
||||
(when platform/ios?
|
||||
(.localPairingPreflightOutboundCheck ^js (encryption) callback)))
|
||||
(.localPairingPreflightOutboundCheck ^js (status) callback)))
|
||||
|
||||
(defn get-connection-string-for-bootstrapping-another-device
|
||||
"Generates connection string form status-go for the purpose of local pairing on the sender end"
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
(ns quo.components.buttons.slide-button.style
|
||||
(:require
|
||||
[quo.components.buttons.slide-button.constants :as constants]
|
||||
[quo.components.buttons.slide-button.utils :as utils]))
|
||||
[quo.components.buttons.slide-button.utils :as utils]
|
||||
[react-native.reanimated :as reanimated]))
|
||||
|
||||
(def absolute-fill
|
||||
{:position :absolute
|
||||
@@ -12,25 +13,28 @@
|
||||
|
||||
(defn thumb-container
|
||||
[{:keys [interpolate-track thumb-size customization-color theme]}]
|
||||
[{:transform [{:translate-x (interpolate-track :track-clamp)}]}
|
||||
(reanimated/apply-animations-to-style
|
||||
{:transform [{:translate-x (interpolate-track :track-clamp)}]}
|
||||
{:background-color (utils/main-color customization-color theme)
|
||||
:border-radius 12
|
||||
:height thumb-size
|
||||
:width thumb-size
|
||||
:align-items :center
|
||||
:overflow :hidden
|
||||
:justify-content :center}])
|
||||
:justify-content :center}))
|
||||
|
||||
(defn arrow-icon-container
|
||||
[interpolate-track]
|
||||
{:transform [{:translate-x (interpolate-track :arrow-icon-position)}]
|
||||
:flex 1
|
||||
:align-items :center
|
||||
:justify-content :center})
|
||||
(reanimated/apply-animations-to-style
|
||||
{:transform [{:translate-x (interpolate-track :arrow-icon-position)}]}
|
||||
{:flex 1
|
||||
:align-items :center
|
||||
:justify-content :center}))
|
||||
|
||||
(defn action-icon
|
||||
[interpolate-track size]
|
||||
[{:transform [{:translate-x (interpolate-track :action-icon-position)}]}
|
||||
(reanimated/apply-animations-to-style
|
||||
{:transform [{:translate-x (interpolate-track :action-icon-position)}]}
|
||||
{:height size
|
||||
:width size
|
||||
:position :absolute
|
||||
@@ -38,7 +42,7 @@
|
||||
:left 0
|
||||
:top 0
|
||||
:flex-direction :row
|
||||
:justify-content :space-around}])
|
||||
:justify-content :space-around}))
|
||||
|
||||
(defn track
|
||||
[{:keys [disabled? customization-color height blur?]}]
|
||||
@@ -53,8 +57,9 @@
|
||||
|
||||
(defn track-cover
|
||||
[interpolate-track]
|
||||
[{:left (interpolate-track :track-cover)}
|
||||
(assoc absolute-fill :overflow :hidden)])
|
||||
(reanimated/apply-animations-to-style
|
||||
{:left (interpolate-track :track-cover)}
|
||||
(assoc absolute-fill :overflow :hidden)))
|
||||
|
||||
(defn track-cover-text-container
|
||||
[track-width]
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
utils.money))
|
||||
|
||||
(defn view-internal
|
||||
[{:keys [value icon theme style accessibility-label text-size]}]
|
||||
[{:keys [value icon theme style accessibility-label]}]
|
||||
[rn/view
|
||||
{:style (merge style/container style)
|
||||
:accessibility-label accessibility-label}
|
||||
@@ -19,7 +19,7 @@
|
||||
:resize-mode :center
|
||||
:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)}]
|
||||
[quo.text/text
|
||||
{:size (or text-size :paragraph-1)
|
||||
{:size :paragraph-1
|
||||
:weight :regular
|
||||
:style (style/text theme)} (utils.money/format-amount value)]])
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
:container-style])
|
||||
|
||||
(defn- base-input
|
||||
[{:keys [on-change-text on-char-limit-reach weight default-value]}]
|
||||
[{:keys [on-change-text on-char-limit-reach weight]}]
|
||||
(let [status (reagent/atom :default)
|
||||
internal-on-focus #(reset! status :focus)
|
||||
internal-on-blur #(reset! status :default)
|
||||
@@ -72,7 +72,7 @@
|
||||
(if (> height min-height)
|
||||
(reset! multiple-lines? true)
|
||||
(reset! multiple-lines? false)))
|
||||
char-count (reagent/atom (count default-value))
|
||||
char-count (reagent/atom 0)
|
||||
update-char-limit! (fn [new-text char-limit]
|
||||
(when on-change-text (on-change-text new-text))
|
||||
(let [amount-chars (count new-text)]
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
(ns quo.components.links.internal-link-card.channel.style
|
||||
(:require [quo.foundations.colors :as colors]))
|
||||
|
||||
(defn loading-circle
|
||||
[theme]
|
||||
{:background-color (colors/theme-colors colors/neutral-5 colors/neutral-80 theme)
|
||||
:margin-right 4
|
||||
:width 16
|
||||
:height 16
|
||||
:border-radius 16})
|
||||
|
||||
(defn loading-first-line-bar
|
||||
[theme margin-right?]
|
||||
{:background-color (colors/theme-colors colors/neutral-5 colors/neutral-80 theme)
|
||||
:width 72
|
||||
:margin-right (when margin-right? 4)
|
||||
:height 16
|
||||
:border-radius 6})
|
||||
|
||||
(defn loading-second-line-bar
|
||||
[theme]
|
||||
{:background-color (colors/theme-colors colors/neutral-5 colors/neutral-80 theme)
|
||||
:width 112
|
||||
:height 8
|
||||
:border-radius 6
|
||||
:margin-bottom 17})
|
||||
|
||||
(defn loading-thumbnail-box
|
||||
[theme size]
|
||||
{:background-color (colors/theme-colors colors/neutral-5 colors/neutral-80 theme)
|
||||
:height (if (= :message size) 139 160)
|
||||
:border-radius 12})
|
||||
|
||||
(defn thumbnail
|
||||
[size]
|
||||
{:width "100%"
|
||||
:height (if (= :message size) 139 160)
|
||||
:margin-top 8
|
||||
:border-radius 12})
|
||||
|
||||
(defn container
|
||||
[size theme]
|
||||
{:border-width 1
|
||||
:border-color (colors/theme-colors colors/neutral-20 colors/neutral-80 theme)
|
||||
:background-color (colors/theme-colors colors/white colors/neutral-80-opa-40 theme)
|
||||
:border-radius 16
|
||||
:padding-horizontal 12
|
||||
:padding-top 10
|
||||
:padding-bottom 12
|
||||
:height (if (= :message size) 215 236)
|
||||
:width (if (= :message size) 295 335)})
|
||||
|
||||
(def header-container
|
||||
{:flex-direction :row
|
||||
:align-items :center})
|
||||
|
||||
(def title
|
||||
{:margin-bottom 2})
|
||||
|
||||
(def logo
|
||||
{:margin-right 6
|
||||
:width 16
|
||||
:height 16
|
||||
:border-radius 8})
|
||||
|
||||
(defn channel-chevron-props
|
||||
[theme]
|
||||
{:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)})
|
||||
|
||||
(def row-spacing
|
||||
{:flex-direction :row
|
||||
:margin-bottom 13})
|
||||
@@ -1,92 +0,0 @@
|
||||
(ns quo.components.links.internal-link-card.channel.view
|
||||
(:require
|
||||
[quo.components.icon :as icon]
|
||||
[quo.components.links.internal-link-card.channel.style :as style]
|
||||
[quo.components.links.internal-link-card.schema :as component-schema]
|
||||
[quo.components.markdown.text :as text]
|
||||
[quo.theme :as quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[schema.core :as schema]))
|
||||
|
||||
(defn- description-comp
|
||||
[description]
|
||||
[rn/view {:style {:margin-bottom 4}}
|
||||
[text/text
|
||||
{:size :paragraph-2
|
||||
:number-of-lines 3
|
||||
:accessibility-label :description}
|
||||
description]])
|
||||
|
||||
(defn- title-comp
|
||||
[title channel-name theme]
|
||||
[rn/view
|
||||
{:style {:flex-direction :row
|
||||
:align-items :center}}
|
||||
[text/text
|
||||
{:size :paragraph-1
|
||||
:number-of-lines 1
|
||||
:weight :semi-bold
|
||||
:style style/title
|
||||
:accessibility-label :title}
|
||||
title]
|
||||
[icon/icon :i/chevron-right (style/channel-chevron-props theme)]
|
||||
[text/text
|
||||
{:size :paragraph-1
|
||||
:number-of-lines 1
|
||||
:weight :semi-bold
|
||||
:style style/title
|
||||
:accessibility-label :title}
|
||||
channel-name]])
|
||||
|
||||
(defn- banner-comp
|
||||
[thumbnail size]
|
||||
[rn/image
|
||||
{:style (style/thumbnail size)
|
||||
:source thumbnail
|
||||
:accessibility-label :banner}])
|
||||
|
||||
(defn- logo-comp
|
||||
[logo]
|
||||
[rn/image
|
||||
{:accessibility-label :logo
|
||||
:source logo
|
||||
:style (assoc style/logo :margin-bottom 2)}])
|
||||
|
||||
(defn- loading-view
|
||||
[theme size]
|
||||
[rn/view
|
||||
{:accessibility-label :loading-channel-link-view
|
||||
:style {:height 215}}
|
||||
[rn/view {:style {:flex-direction :row}}
|
||||
[rn/view {:style style/row-spacing}
|
||||
[rn/view {:style (style/loading-circle theme)}]
|
||||
[rn/view {:style (style/loading-first-line-bar theme true)}]]
|
||||
[rn/view {:style style/row-spacing}
|
||||
[rn/view {:style (style/loading-circle theme)}]
|
||||
[rn/view {:style (style/loading-first-line-bar theme false)}]]]
|
||||
[rn/view {:style (style/loading-second-line-bar theme)}]
|
||||
[rn/view {:style (style/loading-thumbnail-box theme size)}]])
|
||||
|
||||
(defn view-internal
|
||||
[{:keys [title description loading? icon banner
|
||||
theme on-press channel-name size]
|
||||
:or {channel-name "empty name"}}]
|
||||
[rn/pressable
|
||||
{:style (style/container size theme)
|
||||
:accessibility-label :internal-link-card
|
||||
:on-press on-press}
|
||||
(if loading?
|
||||
[loading-view theme size]
|
||||
[:<>
|
||||
[rn/view {:style style/header-container}
|
||||
(when icon
|
||||
[logo-comp icon])
|
||||
[title-comp title channel-name theme]]
|
||||
(when description
|
||||
[description-comp description])
|
||||
(when banner
|
||||
[banner-comp banner size])])])
|
||||
|
||||
(def view
|
||||
(quo.theme/with-theme
|
||||
(schema/instrument #'view-internal component-schema/?schema)))
|
||||
@@ -1,87 +0,0 @@
|
||||
(ns quo.components.links.internal-link-card.community.style
|
||||
(:require [quo.foundations.colors :as colors]))
|
||||
|
||||
(defn loading-circle
|
||||
[theme]
|
||||
{:background-color (colors/theme-colors colors/neutral-5 colors/neutral-80 theme)
|
||||
:margin-right 4
|
||||
:width 16
|
||||
:height 16
|
||||
:border-radius 16})
|
||||
|
||||
(defn loading-stat
|
||||
[theme]
|
||||
{:background-color (colors/theme-colors colors/neutral-5 colors/neutral-80 theme)
|
||||
:margin-right 4
|
||||
:width 32
|
||||
:height 8
|
||||
:border-radius 16})
|
||||
|
||||
(def loading-stat-container
|
||||
{:flex-direction :row
|
||||
:align-items :center
|
||||
:margin-right 12
|
||||
:margin-bottom -6})
|
||||
|
||||
(defn loading-first-line-bar
|
||||
[theme]
|
||||
{:background-color (colors/theme-colors colors/neutral-5 colors/neutral-80 theme)
|
||||
:width 145
|
||||
:height 16
|
||||
:border-radius 6})
|
||||
|
||||
(defn loading-second-line-bar
|
||||
[theme]
|
||||
{:background-color (colors/theme-colors colors/neutral-5 colors/neutral-80 theme)
|
||||
:width 112
|
||||
:height 8
|
||||
:border-radius 6
|
||||
:margin-bottom 17})
|
||||
|
||||
(defn loading-thumbnail-box
|
||||
[theme size]
|
||||
{:background-color (colors/theme-colors colors/neutral-5 colors/neutral-80 theme)
|
||||
:height (if (= :message size) 139 160)
|
||||
:border-radius 12})
|
||||
|
||||
(defn thumbnail
|
||||
[size]
|
||||
{:width "100%"
|
||||
:height (if (= :message size) 139 160)
|
||||
:margin-top 6
|
||||
:border-radius 12})
|
||||
|
||||
(defn container
|
||||
[size theme]
|
||||
{:border-width 1
|
||||
:border-color (colors/theme-colors colors/neutral-20 colors/neutral-80 theme)
|
||||
:background-color (colors/theme-colors colors/white colors/neutral-80-opa-40 theme)
|
||||
:border-radius 16
|
||||
:padding-horizontal 12
|
||||
:padding-top 10
|
||||
:padding-bottom 12
|
||||
:height (if (= :message size) 245 266)
|
||||
:width (if (= :message size) 295 335)})
|
||||
|
||||
(def header-container
|
||||
{:flex-direction :row
|
||||
:align-items :center})
|
||||
|
||||
(def title
|
||||
{:margin-bottom 2})
|
||||
|
||||
(def logo
|
||||
{:width 16
|
||||
:height 16
|
||||
:border-radius 8
|
||||
:margin-right 4
|
||||
:margin-bottom 2})
|
||||
|
||||
(def row-spacing
|
||||
{:flex-direction :row
|
||||
:margin-bottom 12
|
||||
:margin-top 4})
|
||||
|
||||
(def stat-container
|
||||
{:flex-direction :row
|
||||
:margin-top 12})
|
||||
@@ -1,96 +0,0 @@
|
||||
(ns quo.components.links.internal-link-card.community.view
|
||||
(:require
|
||||
[quo.components.community.community-stat.view :as community-stat]
|
||||
[quo.components.links.internal-link-card.community.style :as style]
|
||||
[quo.components.links.internal-link-card.schema :as component-schema]
|
||||
[quo.components.markdown.text :as text]
|
||||
[quo.theme :as quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[schema.core :as schema]))
|
||||
|
||||
(defn- description-comp
|
||||
[description members-count active-members-count]
|
||||
[rn/view
|
||||
[text/text
|
||||
{:size :paragraph-2
|
||||
:number-of-lines 3
|
||||
:accessibility-label :description}
|
||||
description]
|
||||
[rn/view {:style style/stat-container}
|
||||
[community-stat/view
|
||||
{:value members-count
|
||||
:icon :i/members
|
||||
:accessibility-label :members-count
|
||||
:style {:margin-right 12}
|
||||
:text-size :paragraph-2}]
|
||||
(when active-members-count
|
||||
[community-stat/view
|
||||
{:value active-members-count
|
||||
:icon :i/active-members
|
||||
:accessibility-label :active-members-count
|
||||
:text-size :paragraph-2}])]])
|
||||
|
||||
(defn- title-comp
|
||||
[title]
|
||||
[text/text
|
||||
{:size :paragraph-1
|
||||
:number-of-lines 1
|
||||
:weight :semi-bold
|
||||
:style style/title
|
||||
:accessibility-label :title}
|
||||
title])
|
||||
|
||||
(defn- thumbnail-comp
|
||||
[thumbnail size]
|
||||
[rn/image
|
||||
{:style (style/thumbnail size)
|
||||
:source thumbnail
|
||||
:accessibility-label :thumbnail}])
|
||||
|
||||
(defn- logo-comp
|
||||
[logo]
|
||||
[rn/image
|
||||
{:accessibility-label :logo
|
||||
:source logo
|
||||
:style style/logo}])
|
||||
|
||||
(defn- stat-loading
|
||||
[theme]
|
||||
[rn/view {:style style/loading-stat-container}
|
||||
[rn/view {:style (style/loading-circle theme)}]
|
||||
[rn/view {:style (style/loading-stat theme)}]])
|
||||
|
||||
(defn- loading-view
|
||||
[theme size]
|
||||
[rn/view {:accessibility-label :loading-community-link-view}
|
||||
[rn/view {:style style/row-spacing}
|
||||
[rn/view {:style (style/loading-circle theme)}]
|
||||
[rn/view {:style (style/loading-first-line-bar theme)}]]
|
||||
[rn/view {:style (style/loading-second-line-bar theme)}]
|
||||
[rn/view {:style style/row-spacing}
|
||||
[stat-loading theme]
|
||||
[stat-loading theme]]
|
||||
[rn/view {:style (style/loading-thumbnail-box theme size)}]])
|
||||
|
||||
(defn- view-internal
|
||||
[{:keys [title description loading? icon banner members-count active-members-count
|
||||
theme on-press size]}]
|
||||
[rn/pressable
|
||||
{:style (style/container size theme)
|
||||
:accessibility-label :internal-link-card
|
||||
:on-press on-press}
|
||||
(if loading?
|
||||
[loading-view theme size]
|
||||
[:<>
|
||||
[rn/view {:style style/header-container}
|
||||
(when icon
|
||||
[logo-comp icon])
|
||||
[title-comp title]]
|
||||
(when description
|
||||
[description-comp description members-count active-members-count])
|
||||
(when banner
|
||||
[thumbnail-comp banner size])])])
|
||||
|
||||
(def view
|
||||
(quo.theme/with-theme
|
||||
(schema/instrument #'view-internal component-schema/?schema)))
|
||||
@@ -1,81 +0,0 @@
|
||||
(ns quo.components.links.internal-link-card.component-spec
|
||||
(:require
|
||||
[quo.components.links.internal-link-card.view :as view]
|
||||
[test-helpers.component :as h]))
|
||||
|
||||
(defn- render
|
||||
[component]
|
||||
(h/render-with-theme-provider component :light))
|
||||
|
||||
(def user-props
|
||||
{:title "Some title"
|
||||
:subtitle "Some description"
|
||||
:loading? false
|
||||
:icon "data:image/png,logo-x"
|
||||
:type :user
|
||||
:customization-color "#ff0000"
|
||||
:emoji-hash "🌟🚀🐠🌈🏰🔮🦉🐼🍉🎨🚲🌙🍔🌵"})
|
||||
|
||||
(h/describe "Internal link card - User"
|
||||
(h/test "renders with most common props"
|
||||
(render [view/view user-props])
|
||||
(h/is-truthy (h/query-by-text (:title user-props)))
|
||||
(h/is-truthy (h/query-by-text (:subtitle user-props))))
|
||||
|
||||
(h/test "does not render logo if prop is not present"
|
||||
(render [view/view (dissoc user-props :icon)])
|
||||
(h/is-null (h/query-by-label-text :logo))))
|
||||
|
||||
(def community-props
|
||||
{:title "Some title"
|
||||
:description "Some description"
|
||||
:icon "data:image/png,logo-x"
|
||||
:banner "data:image/png,whatever"
|
||||
:members-count "20"
|
||||
:loading? false
|
||||
:active-members-count "15"
|
||||
:type :community})
|
||||
|
||||
(h/describe "Internal link card - Community"
|
||||
(h/test "renders with most common props"
|
||||
(render [view/view community-props])
|
||||
(h/is-truthy (h/query-by-text (:title community-props)))
|
||||
(h/is-truthy (h/query-by-text (:description community-props)))
|
||||
(h/is-truthy (h/query-by-text (:members-count community-props)))
|
||||
(h/is-truthy (h/query-by-text (:active-members-count community-props)))
|
||||
(h/is-truthy (h/query-by-label-text :logo))
|
||||
(h/is-truthy (h/query-by-label-text :thumbnail)))
|
||||
|
||||
(h/test "does not render thumbnail if prop is not present"
|
||||
(render [view/view (dissoc community-props :banner)])
|
||||
(h/is-null (h/query-by-label-text :thumbnail)))
|
||||
|
||||
(h/test "does not render logo if prop is not present"
|
||||
(render [view/view (dissoc community-props :icon)])
|
||||
(h/is-null (h/query-by-label-text :logo))))
|
||||
|
||||
(def channel-props
|
||||
{:title "Doodles"
|
||||
:description "Coloring the world with joy • ᴗ •"
|
||||
:icon "data:image/png,logo-x"
|
||||
:banner "data:image/png,whatever"
|
||||
:loading? false
|
||||
:channel-name "#general"
|
||||
:type :channel})
|
||||
|
||||
(h/describe "Internal link card - Channel"
|
||||
(h/test "renders with most common props"
|
||||
(render [view/view channel-props])
|
||||
(h/is-truthy (h/query-by-text (:title channel-props)))
|
||||
(h/is-truthy (h/query-by-text (:description channel-props)))
|
||||
(h/is-truthy (h/query-by-text (:channel-name channel-props)))
|
||||
(h/is-truthy (h/query-by-label-text :logo))
|
||||
(h/is-truthy (h/query-by-label-text :banner)))
|
||||
|
||||
(h/test "does not render banner if prop is not present"
|
||||
(render [view/view (dissoc channel-props :banner)])
|
||||
(h/is-null (h/query-by-label-text :banner)))
|
||||
|
||||
(h/test "does not render logo if prop is not present"
|
||||
(render [view/view (dissoc channel-props :icon)])
|
||||
(h/is-null (h/query-by-label-text :logo))))
|
||||
@@ -1,23 +0,0 @@
|
||||
(ns quo.components.links.internal-link-card.schema)
|
||||
|
||||
(def ?schema
|
||||
[:=>
|
||||
[:catn
|
||||
[:props
|
||||
[:map {:closed true}
|
||||
[:title {:optional true} [:maybe :string]]
|
||||
[:description {:optional true} [:maybe :string]]
|
||||
[:channel-name {:optional true} [:maybe :string]]
|
||||
[:loading? {:optional true} [:maybe :boolean]]
|
||||
[:subtitle {:optional true} [:maybe :string]]
|
||||
[:icon {:optional true} [:maybe [:or :string :int]]]
|
||||
[:banner {:optional true} [:maybe [:or :string :int]]]
|
||||
[:type {:optional true} [:maybe :keyword]]
|
||||
[:on-press {:optional true} [:maybe fn?]]
|
||||
[:members-count {:optional true} [:maybe [:or :int :string]]]
|
||||
[:active-members-count {:optional true} [:maybe [:or :int :string]]]
|
||||
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]
|
||||
[:emoji-hash {:optional true} [:maybe :string]]
|
||||
[:size {:optional true} [:maybe :keyword]]
|
||||
[:theme :schema.common/theme]]]]
|
||||
:any])
|
||||
@@ -1,66 +0,0 @@
|
||||
(ns quo.components.links.internal-link-card.user.style
|
||||
(:require [quo.foundations.colors :as colors]))
|
||||
|
||||
(defn loading-circle
|
||||
[theme]
|
||||
{:background-color (colors/theme-colors colors/neutral-5 colors/neutral-80 theme)
|
||||
:margin-right 4
|
||||
:width 16
|
||||
:height 16
|
||||
:border-radius 16})
|
||||
|
||||
(defn loading-first-line-bar
|
||||
[theme]
|
||||
{:background-color (colors/theme-colors colors/neutral-5 colors/neutral-80 theme)
|
||||
:width 145
|
||||
:height 16
|
||||
:border-radius 6})
|
||||
|
||||
(defn loading-second-line-bar
|
||||
[theme]
|
||||
{:background-color (colors/theme-colors colors/neutral-5 colors/neutral-80 theme)
|
||||
:width 112
|
||||
:height 8
|
||||
:border-radius 6
|
||||
:margin-bottom 17})
|
||||
|
||||
(defn last-bar-line-bar
|
||||
[theme]
|
||||
{:background-color (colors/theme-colors colors/neutral-5 colors/neutral-80 theme)
|
||||
:width 271
|
||||
:height 16
|
||||
:border-radius 6})
|
||||
|
||||
(defn gradient-start-color
|
||||
[customization-color theme]
|
||||
(colors/theme-colors (colors/resolve-color customization-color theme 10)
|
||||
(colors/resolve-color customization-color theme 20)
|
||||
theme))
|
||||
|
||||
(defn container
|
||||
[loading? theme size]
|
||||
{:border-width 1
|
||||
:border-color (colors/theme-colors colors/neutral-20 colors/neutral-80 theme)
|
||||
:background-color (colors/theme-colors colors/white colors/neutral-80-opa-40 theme)
|
||||
:border-radius 16
|
||||
:padding-horizontal 12
|
||||
:padding-top 10
|
||||
:padding-bottom 12
|
||||
:height (if loading? 92 110)
|
||||
:width (if (= :message size) 295 335)})
|
||||
|
||||
(def header-container
|
||||
{:flex-direction :row
|
||||
:align-items :center})
|
||||
|
||||
(def title
|
||||
{:margin-bottom 2})
|
||||
|
||||
(def logo
|
||||
{:margin-right 6
|
||||
:width 16
|
||||
:height 16
|
||||
:border-radius 8
|
||||
:margin-bottom 2})
|
||||
|
||||
(def row-spacing {:flex-direction :row :margin-bottom 12})
|
||||
@@ -1,83 +0,0 @@
|
||||
(ns quo.components.links.internal-link-card.user.view
|
||||
(:require
|
||||
[quo.components.links.internal-link-card.schema :as component-schema]
|
||||
[quo.components.links.internal-link-card.user.style :as style]
|
||||
[quo.components.markdown.text :as text]
|
||||
[quo.theme :as quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[react-native.linear-gradient :as linear-gradient]
|
||||
[schema.core :as schema]))
|
||||
|
||||
(defn- subtitle-comp
|
||||
[subtitle emoji-hash]
|
||||
[rn/view
|
||||
[text/text
|
||||
{:size :paragraph-2
|
||||
:number-of-lines 2
|
||||
:accessibility-label :subtitle
|
||||
:style {:margin-bottom 12}}
|
||||
subtitle]
|
||||
[rn/view {:style {:flex-direction :row}}
|
||||
[text/text
|
||||
{:size :paragraph-2
|
||||
:number-of-lines 1
|
||||
:weight :regular
|
||||
:accessibility-label :emoji-hash}
|
||||
emoji-hash]]])
|
||||
|
||||
(defn- title-comp
|
||||
[title]
|
||||
[text/text
|
||||
{:size :paragraph-1
|
||||
:number-of-lines 1
|
||||
:weight :semi-bold
|
||||
:style style/title
|
||||
:accessibility-label :title}
|
||||
title])
|
||||
|
||||
(defn- logo-comp
|
||||
[logo]
|
||||
[rn/image
|
||||
{:accessibility-label :logo
|
||||
:source logo
|
||||
:style style/logo}])
|
||||
|
||||
(defn- loading-view
|
||||
[theme]
|
||||
[rn/view {:accessibility-label :loading-user-link-view}
|
||||
[rn/view {:style style/row-spacing}
|
||||
[rn/view {:style (style/loading-circle theme)}]
|
||||
[rn/view {:style (style/loading-first-line-bar theme)}]]
|
||||
[rn/view {:style (style/loading-second-line-bar theme)}]
|
||||
[rn/view {:style (style/last-bar-line-bar theme)}]])
|
||||
|
||||
(defn- linear-gradient-props
|
||||
[theme customization-color]
|
||||
[(style/gradient-start-color customization-color theme) :transparent])
|
||||
|
||||
(defn view-internal
|
||||
[{:keys [title loading? icon
|
||||
theme on-press subtitle emoji-hash customization-color size]}]
|
||||
(if loading?
|
||||
[rn/pressable
|
||||
{:accessibility-label :internal-link-card
|
||||
:on-press on-press
|
||||
:style (style/container loading? theme size)}
|
||||
[loading-view theme]]
|
||||
[linear-gradient/linear-gradient
|
||||
(assoc {:style (style/container loading? theme size)}
|
||||
:colors
|
||||
(linear-gradient-props theme customization-color))
|
||||
[rn/pressable
|
||||
{:accessibility-label :internal-link-card
|
||||
:on-press on-press}
|
||||
[rn/view {:style style/header-container}
|
||||
(when icon
|
||||
[logo-comp icon])
|
||||
[title-comp title]]
|
||||
(when subtitle
|
||||
[subtitle-comp subtitle emoji-hash])]]))
|
||||
|
||||
(def view
|
||||
(quo.theme/with-theme
|
||||
(schema/instrument #'view-internal component-schema/?schema)))
|
||||
@@ -1,11 +0,0 @@
|
||||
(ns quo.components.links.internal-link-card.view
|
||||
(:require [quo.components.links.internal-link-card.channel.view :as channel.view]
|
||||
[quo.components.links.internal-link-card.community.view :as community.view]
|
||||
[quo.components.links.internal-link-card.user.view :as user.view]))
|
||||
|
||||
(defn view
|
||||
[{card-type :type :as props}]
|
||||
(case card-type
|
||||
:community [community.view/view props]
|
||||
:channel [channel.view/view props]
|
||||
:user [user.view/view props]))
|
||||
@@ -26,7 +26,6 @@
|
||||
(on-press label)))
|
||||
:on-press-in #(reset! pressed? true)
|
||||
:on-press-out #(reset! pressed? false)
|
||||
:hit-slop {:top 8 :bottom 8 :left 25 :right 25}
|
||||
:style (style/container background-color)}
|
||||
(case type
|
||||
:key [icons/icon
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(defn view
|
||||
[{:keys [type container-style]} & children]
|
||||
[{:keys [type]} & children]
|
||||
[rn/view {:style (style/overlay-background type)}
|
||||
(if (= type :shell)
|
||||
[blur/view
|
||||
@@ -14,7 +14,7 @@
|
||||
:blur-type :transparent
|
||||
:overlay-color :transparent
|
||||
:style style/container}
|
||||
[rn/view {:style (merge style/blur-container container-style)}
|
||||
[rn/view {:style style/blur-container}
|
||||
children]]
|
||||
[rn/view {:style (merge style/container container-style)}
|
||||
[rn/view {:style style/container}
|
||||
children])])
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
[react-native.linear-gradient :as linear-gradient]))
|
||||
|
||||
(defn- view-internal
|
||||
[{:keys [address theme on-press icon title customization-color container-style]}]
|
||||
[{:keys [address theme on-press icon title customization-color]}]
|
||||
[rn/pressable
|
||||
{:accessibility-label :link-card
|
||||
:on-press on-press}
|
||||
@@ -17,7 +17,7 @@
|
||||
(properties/gradient-end-color theme customization-color)]
|
||||
:start {:x 0 :y 1}
|
||||
:end {:x 1 :y 1}
|
||||
:style (merge (style/container theme) container-style)}
|
||||
:style (style/container theme)}
|
||||
[rn/view {:style style/icon-container}
|
||||
[social/view
|
||||
{:accessibility-label :social-icon
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(defn- category-internal
|
||||
[{:keys [label data container-style] :as props}]
|
||||
[rn/view {:style (merge (style/container label) container-style)}
|
||||
[{:keys [label data] :as props}]
|
||||
[rn/view {:style (style/container label)}
|
||||
(when label
|
||||
[text/text
|
||||
{:weight :medium
|
||||
|
||||
@@ -19,12 +19,10 @@
|
||||
(defn sub-container
|
||||
[align-action]
|
||||
{:flex-direction :row
|
||||
:padding-right 0.5
|
||||
:align-items (or align-action :center)})
|
||||
|
||||
(defn left-container
|
||||
[image?]
|
||||
{:margin-horizontal (if image? 12 0)
|
||||
(def left-container
|
||||
{:margin-horizontal 12
|
||||
:flex 1
|
||||
:height "100%"
|
||||
:justify-content :flex-start})
|
||||
@@ -59,7 +57,6 @@
|
||||
{:width 15
|
||||
:height 15
|
||||
:border-radius 12
|
||||
:margin-right 4
|
||||
:background-color background-color})
|
||||
|
||||
(def status-tag-container
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
:accessibility-label accessibility-label}
|
||||
[rn/view {:style (style/left-sub-container props)}
|
||||
[image-component props]
|
||||
[rn/view {:style (style/left-container (:image props))}
|
||||
[rn/view {:style style/left-container}
|
||||
[text/text
|
||||
{:weight :medium
|
||||
:style {:color (when blur? colors/white)}} title]
|
||||
|
||||
@@ -21,12 +21,7 @@
|
||||
(def collectible-image
|
||||
{:width 24
|
||||
:height 24
|
||||
:border-radius 8})
|
||||
|
||||
(def network
|
||||
{:width 24
|
||||
:height 24
|
||||
:border-radius 12})
|
||||
:border-radius 10})
|
||||
|
||||
(def token-image
|
||||
{:border-radius 12})
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
:network
|
||||
[rn/image
|
||||
{:source image-source
|
||||
:style style/network}]
|
||||
:style style/token-image}]
|
||||
:saved-address
|
||||
[wallet-user-avatar/wallet-user-avatar
|
||||
{:full-name label
|
||||
|
||||
@@ -37,14 +37,13 @@
|
||||
(def ^:private b64-png-image-prefix "data:image/png;base64,")
|
||||
|
||||
(defn temp-empty-symbol
|
||||
[token size style]
|
||||
[token size]
|
||||
[rn/view
|
||||
{:style (token-style (merge {:justify-content :center
|
||||
:align-items :center
|
||||
:border-radius 20
|
||||
:border-width 1
|
||||
:border-color :grey}
|
||||
style)
|
||||
{:style (token-style {:justify-content :center
|
||||
:align-items :center
|
||||
:border-radius 20
|
||||
:border-width 1
|
||||
:border-color :grey}
|
||||
size)}
|
||||
[quo/text {:style {:color :grey}}
|
||||
(some-> token
|
||||
@@ -74,6 +73,6 @@
|
||||
[rn/image
|
||||
{:style (token-style style size)
|
||||
:source source}]
|
||||
[temp-empty-symbol token size style])))
|
||||
[temp-empty-symbol token size])))
|
||||
|
||||
(def view (schema/instrument #'view-internal ?schema))
|
||||
|
||||
@@ -11,9 +11,8 @@
|
||||
colors/white))
|
||||
|
||||
(defn card
|
||||
[{:keys [customization-color type theme pressed? metrics?]}]
|
||||
{:width 161
|
||||
:height (if metrics? 88 68)
|
||||
[{:keys [customization-color type theme pressed?]}]
|
||||
{:width 162
|
||||
:background-color (when (not= :watch-only type)
|
||||
(colors/theme-colors
|
||||
(colors/resolve-color customization-color
|
||||
|
||||
@@ -21,8 +21,7 @@
|
||||
:style (style/card {:customization-color customization-color
|
||||
:type type
|
||||
:theme theme
|
||||
:pressed? false
|
||||
:metrics? metrics?})}
|
||||
:pressed? false})}
|
||||
[rn/view {:style style/loader-container}
|
||||
[rn/view
|
||||
{:style (assoc (style/loader-view {:width 16
|
||||
@@ -114,8 +113,7 @@
|
||||
:style (style/card {:customization-color customization-color
|
||||
:type type
|
||||
:theme theme
|
||||
:pressed? @pressed?
|
||||
:metrics? metrics?})
|
||||
:pressed? @pressed?})
|
||||
:on-press on-press}
|
||||
(when (and customization-color (and (not watch-only?) (not missing-keypair?)))
|
||||
[customization-colors/overlay
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
[:checked? {:optional true} [:maybe :boolean]]
|
||||
[:disabled? {:optional true} [:maybe :boolean]]
|
||||
[:on-change {:optional true} [:maybe fn?]]
|
||||
[:container-style {:optional true} [:maybe :map]]
|
||||
[:theme :schema.common/theme]]]]
|
||||
:any])
|
||||
|
||||
@@ -59,11 +58,11 @@
|
||||
|
||||
(defn- view-internal
|
||||
[{:keys
|
||||
[checked? disabled? on-change token-details keycard? theme container-style]
|
||||
[checked? disabled? on-change token-details keycard? theme]
|
||||
{:keys
|
||||
[name address emoji customization-color]} :account}]
|
||||
[rn/view
|
||||
{:style (merge (style/container theme) container-style)
|
||||
{:style (style/container theme)
|
||||
:accessibility-label :wallet-account-permissions}
|
||||
[rn/view {:style style/row1}
|
||||
[account-avatar/view
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
(:require
|
||||
[quo.components.avatars.account-avatar.view :as account-avatar]
|
||||
[quo.components.avatars.user-avatar.view :as user-avatar]
|
||||
[quo.components.avatars.wallet-user-avatar.view :as wallet-user-avatar]
|
||||
[quo.components.markdown.text :as text]
|
||||
[quo.components.wallet.summary-info.style :as style]
|
||||
[quo.foundations.colors :as colors]
|
||||
@@ -27,29 +26,24 @@
|
||||
|
||||
(defn networks
|
||||
[values theme]
|
||||
(let [{:keys [ethereum optimism arbitrum]} values
|
||||
show-optimism? (pos? optimism)
|
||||
show-arbitrum? (pos? arbitrum)]
|
||||
(let [{:keys [ethereum optimism arbitrum]} values]
|
||||
[rn/view
|
||||
{:style style/networks-container
|
||||
:accessibility-label :networks}
|
||||
(when (pos? ethereum)
|
||||
[network-amount
|
||||
{:network :ethereum
|
||||
:amount (str ethereum " ETH")
|
||||
:divider? (or show-arbitrum? show-optimism?)
|
||||
:theme theme}])
|
||||
(when show-optimism?
|
||||
[network-amount
|
||||
{:network :optimism
|
||||
:amount (str optimism " OPT")
|
||||
:divider? show-arbitrum?
|
||||
:theme theme}])
|
||||
(when show-arbitrum?
|
||||
[network-amount
|
||||
{:network :arbitrum
|
||||
:amount (str arbitrum " ARB")
|
||||
:theme theme}])]))
|
||||
[network-amount
|
||||
{:network :ethereum
|
||||
:amount (str ethereum " ETH")
|
||||
:divider? true
|
||||
:theme theme}]
|
||||
[network-amount
|
||||
{:network :optimism
|
||||
:amount (str optimism " ETH")
|
||||
:divider? true
|
||||
:theme theme}]
|
||||
[network-amount
|
||||
{:network :arbitrum
|
||||
:amount (str arbitrum " ETH")
|
||||
:theme theme}]]))
|
||||
|
||||
(defn- view-internal
|
||||
[{:keys [theme type account-props networks? values]}]
|
||||
@@ -57,13 +51,8 @@
|
||||
{:style (style/container networks? theme)}
|
||||
[rn/view
|
||||
{:style style/info-container}
|
||||
(case type
|
||||
:status-account [account-avatar/view account-props]
|
||||
:saved-account [wallet-user-avatar/wallet-user-avatar (assoc account-props :size :size-32)]
|
||||
:account [wallet-user-avatar/wallet-user-avatar
|
||||
(assoc account-props
|
||||
:size :size-32
|
||||
:neutral? true)]
|
||||
(if (= type :status-account)
|
||||
[account-avatar/view account-props]
|
||||
[user-avatar/user-avatar account-props])
|
||||
[rn/view {:style {:margin-left 8}}
|
||||
(when (not= type :account) [text/text {:weight :semi-bold} (:name account-props)])
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
:margin-right (if platform/ios? 6 4)
|
||||
:color (colors/theme-colors colors/neutral-100 colors/white theme)
|
||||
:padding 0
|
||||
:flex 1
|
||||
:text-align :center
|
||||
:height "100%"}))
|
||||
|
||||
(defn divider
|
||||
|
||||
@@ -69,7 +69,6 @@
|
||||
quo.components.inputs.title-input.view
|
||||
quo.components.ios.drawer-bar.view
|
||||
quo.components.keycard.view
|
||||
quo.components.links.internal-link-card.view
|
||||
quo.components.links.link-preview.view
|
||||
quo.components.links.url-preview-list.view
|
||||
quo.components.links.url-preview.view
|
||||
@@ -289,7 +288,6 @@
|
||||
(def numbered-keyboard quo.components.numbered-keyboard.numbered-keyboard.view/view)
|
||||
|
||||
;;;; Links
|
||||
(def internal-link-card quo.components.links.internal-link-card.view/view)
|
||||
(def link-preview quo.components.links.link-preview.view/view)
|
||||
(def url-preview quo.components.links.url-preview.view/view)
|
||||
(def url-preview-list quo.components.links.url-preview-list.view/view)
|
||||
|
||||
@@ -39,7 +39,6 @@
|
||||
quo.components.inputs.recovery-phrase.component-spec
|
||||
quo.components.inputs.title-input.component-spec
|
||||
quo.components.keycard.component-spec
|
||||
quo.components.links.internal-link-card.component-spec
|
||||
quo.components.links.link-preview.component-spec
|
||||
quo.components.links.url-preview-list.component-spec
|
||||
quo.components.links.url-preview.component-spec
|
||||
|
||||
@@ -22,10 +22,8 @@
|
||||
useAnimatedScrollHandler
|
||||
runOnJS)]
|
||||
["react-native-redash" :refer (withPause)]
|
||||
[oops.core :as oops]
|
||||
[react-native.flat-list :as rn-flat-list]
|
||||
[reagent.core :as reagent]
|
||||
[utils.transforms :as transforms]
|
||||
[utils.worklets.core :as worklets.core]))
|
||||
|
||||
(def enable-layout-animations enableLayoutAnimations)
|
||||
@@ -40,20 +38,7 @@
|
||||
;; Animated Components
|
||||
(def create-animated-component (comp reagent/adapt-react-class (.-createAnimatedComponent reanimated)))
|
||||
|
||||
(def ^:private view* (reagent/adapt-react-class (.-View reanimated)))
|
||||
|
||||
(defn view
|
||||
[]
|
||||
(let [current-component (reagent/current-component)
|
||||
reagent-props (reagent/props current-component)
|
||||
children (reagent/children current-component)
|
||||
updated-props (update reagent-props :style transforms/styles-with-vectors)
|
||||
;; Some components add JS props to their children (such as TouchableWithoutFeedback), to make
|
||||
;; this component fully compatible we are passing those props to the inner component (`view*`).
|
||||
external-props (oops/gobj-get current-component "props")
|
||||
all-props (transforms/copy-js-obj-to-map external-props updated-props #(not= % "argv"))]
|
||||
(into [view* all-props] children)))
|
||||
|
||||
(def view (reagent/adapt-react-class (.-View reanimated)))
|
||||
(def text (reagent/adapt-react-class (.-Text reanimated)))
|
||||
(def scroll-view (reagent/adapt-react-class (.-ScrollView reanimated)))
|
||||
(def image (reagent/adapt-react-class (.-Image reanimated)))
|
||||
|
||||
+2
-8
@@ -5,17 +5,11 @@
|
||||
in non-debug environments.
|
||||
|
||||
`value` is first transformed via `malli.core/schema` to make sure we fail fast
|
||||
and only register valid schemas.
|
||||
|
||||
Pass raw schema `value` instead of schema instance when it's `:function` schema
|
||||
https://github.com/status-im/status-mobile/pull/18364#issuecomment-1875543794"
|
||||
and only register valid schemas."
|
||||
[sym value]
|
||||
`(if ^boolean js/goog.DEBUG
|
||||
(try
|
||||
(let [schema-instance# (malli.core/schema ~value)]
|
||||
(if (and (seq ~value) (= :function (first ~value)))
|
||||
(malli.core/=> ~sym ~value)
|
||||
(malli.core/=> ~sym schema-instance#)))
|
||||
(malli.core/=> ~sym (malli.core/schema ~value))
|
||||
(catch js/Error e#
|
||||
(taoensso.timbre/error "Failed to instrument function"
|
||||
{:symbol ~sym :error e#})
|
||||
|
||||
@@ -24,17 +24,11 @@
|
||||
|
||||
(defn get-label-by-type
|
||||
[biometric-type]
|
||||
(condp = biometric-type
|
||||
(case biometric-type
|
||||
:fingerprint (i18n/label :t/biometric-fingerprint)
|
||||
:FaceID (i18n/label :t/biometric-faceid)
|
||||
(i18n/label :t/biometric-touchid)))
|
||||
|
||||
(defn get-icon-by-type
|
||||
[biometric-type]
|
||||
(condp = biometric-type
|
||||
:FaceID :i/face-id
|
||||
:i/touch-id))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:biometric/get-supported-biometric-type
|
||||
(fn []
|
||||
@@ -132,26 +126,3 @@
|
||||
{:events [:biometric/authenticate]}
|
||||
[_ opts]
|
||||
{:biometric/authenticate opts})
|
||||
|
||||
(rf/reg-event-fx
|
||||
:biometric/on-enable-success
|
||||
(fn [{:keys [db]} [password]]
|
||||
(let [key-uid (get-in db [:profile/profile :key-uid])]
|
||||
{:db (assoc db :auth-method constants/auth-method-biometric)
|
||||
:dispatch [:keychain/save-password-and-auth-method
|
||||
{:key-uid key-uid
|
||||
:masked-password password}]})))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:biometric/enable
|
||||
(fn [_ [password]]
|
||||
{:dispatch [:biometric/authenticate
|
||||
{:on-success #(rf/dispatch [:biometric/on-enable-success password])
|
||||
:on-fail #(rf/dispatch [:biometric/show-message %])}]}))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:biometric/disable
|
||||
(fn [{:keys [db]}]
|
||||
(let [key-uid (get-in db [:profile/profile :key-uid])]
|
||||
{:db (assoc db :auth-method constants/auth-method-none)
|
||||
:keychain/clear-user-password key-uid})))
|
||||
|
||||
+2
-18
@@ -1,11 +1,8 @@
|
||||
(ns status-im.contexts.wallet.data-store
|
||||
(ns status-im.common.data-store.wallet
|
||||
(:require
|
||||
[camel-snake-kebab.core :as csk]
|
||||
[camel-snake-kebab.extras :as cske]
|
||||
[clojure.set :as set]
|
||||
[clojure.string :as string]
|
||||
[status-im.constants :as constants]
|
||||
[utils.money :as money]
|
||||
[utils.number :as utils.number]))
|
||||
|
||||
(defn chain-ids-string->set
|
||||
@@ -51,20 +48,7 @@
|
||||
(update :testPreferredChainIds chain-ids-set->string)
|
||||
(dissoc :watch-only?)))
|
||||
|
||||
(defn- rpc->balances-per-chain
|
||||
[token]
|
||||
(-> token
|
||||
(update :balances-per-chain update-vals #(update % :raw-balance money/bignumber))
|
||||
(update :balances-per-chain update-keys (comp utils.number/parse-int name))))
|
||||
|
||||
(defn rpc->tokens
|
||||
[tokens]
|
||||
(-> tokens
|
||||
(update-keys name)
|
||||
(update-vals #(cske/transform-keys csk/->kebab-case %))
|
||||
(update-vals #(mapv rpc->balances-per-chain %))))
|
||||
|
||||
(defn rpc->network
|
||||
(defn <-rpc
|
||||
[network]
|
||||
(-> network
|
||||
(set/rename-keys
|
||||
@@ -95,8 +95,7 @@
|
||||
:button-text (i18n/label :t/confirm)
|
||||
:close-button-text (i18n/label :t/cancel)
|
||||
:on-press (fn []
|
||||
(hide-sheet-and-dispatch [:chat.ui/close-and-remove-chat
|
||||
chat-id])
|
||||
(hide-sheet-and-dispatch [:chat.ui/close-chat chat-id])
|
||||
(when inside-chat?
|
||||
(rf/dispatch [:navigate-back])))}])}]))
|
||||
|
||||
@@ -349,7 +348,7 @@
|
||||
[chat-id]
|
||||
(entry {:icon :i/members
|
||||
:label (i18n/label :t/group-details)
|
||||
:on-press #(hide-sheet-and-dispatch [:navigate-to :group-chat-profile chat-id])
|
||||
:on-press #(hide-sheet-and-dispatch [:show-group-chat-profile chat-id])
|
||||
:danger? false
|
||||
:accessibility-label :group-details
|
||||
:sub-label nil
|
||||
|
||||
@@ -131,7 +131,6 @@
|
||||
:keychain/clear-user-password
|
||||
(fn [key-uid]
|
||||
(keychain/reset-credentials (password-migration-key-name key-uid))
|
||||
(keychain/reset-credentials (str key-uid "-auth"))
|
||||
(keychain/reset-credentials key-uid)))
|
||||
|
||||
(re-frame/reg-fx
|
||||
@@ -143,11 +142,6 @@
|
||||
(.then #(when on-success (on-success)))
|
||||
(.catch #(when on-error (on-error %))))))
|
||||
|
||||
(re-frame/reg-event-fx
|
||||
:keychain/save-password-and-auth-method
|
||||
(fn [_ [opts]]
|
||||
{:keychain/save-password-and-auth-method opts}))
|
||||
|
||||
;; NOTE: migrating the plaintext password in the keychain
|
||||
;; with the hashed one. Added due to the sync onboarding
|
||||
;; flow, where the password arrives already hashed.
|
||||
|
||||
@@ -58,16 +58,15 @@
|
||||
(def picture-border-width 4)
|
||||
|
||||
(defn display-picture-container
|
||||
[animation theme]
|
||||
[animation]
|
||||
(reanimated/apply-animations-to-style
|
||||
{:transform [{:scale animation}]}
|
||||
{:border-radius picture-diameter
|
||||
:border-width picture-border-width
|
||||
:border-color (colors/theme-colors colors/white colors/neutral-95 theme)
|
||||
:background-color (colors/theme-colors colors/white colors/neutral-95 theme)
|
||||
:position :absolute
|
||||
:top (- (+ picture-radius picture-border-width))
|
||||
:left (+ (/ picture-radius 2) picture-border-width)}))
|
||||
{:border-radius picture-diameter
|
||||
:border-width picture-border-width
|
||||
:border-color (colors/theme-colors colors/white colors/neutral-95)
|
||||
:position :absolute
|
||||
:top (- (+ picture-radius picture-border-width))
|
||||
:left (+ (/ picture-radius 2) picture-border-width)}))
|
||||
|
||||
(defn display-picture
|
||||
[theme]
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
js/undefined)
|
||||
[scroll-height])
|
||||
[reanimated/view
|
||||
{:style (style/display-picture-container animation theme)}
|
||||
{:style (style/display-picture-container animation)}
|
||||
[rn/image
|
||||
{:source cover
|
||||
:style (style/display-picture theme)}]]))
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
[legacy.status-im.mailserver.core :as mailserver]
|
||||
[legacy.status-im.visibility-status-updates.core :as visibility-status-updates]
|
||||
[status-im.common.pairing.events :as pairing]
|
||||
[status-im.contexts.chat.messenger.messages.link-preview.events :as link-preview]
|
||||
[status-im.contexts.chat.messenger.messages.transport.events :as messages.transport]
|
||||
[status-im.contexts.chat.messages.link-preview.events :as link-preview]
|
||||
[status-im.contexts.chat.messages.transport.events :as messages.transport]
|
||||
[status-im.contexts.communities.discover.events]
|
||||
[status-im.contexts.profile.login.events :as profile.login]
|
||||
[status-im.contexts.profile.push-notifications.local.events :as local-notifications]
|
||||
|
||||
@@ -6,8 +6,3 @@
|
||||
(fn [{:keys [db]} [callback]]
|
||||
(let [key-uid (get-in db [:profile/profile :key-uid])]
|
||||
{:fx [[:keychain/get-user-password [key-uid callback]]]})))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:standard-auth/reset-login-password
|
||||
(fn [{:keys [db]}]
|
||||
{:db (update db :profile/login dissoc :password :error)}))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
(ns status-im.common.standard-authentication.standard-auth.authorize
|
||||
(:require
|
||||
[native-module.core :as native-module]
|
||||
[react-native.touch-id :as biometric]
|
||||
[status-im.common.standard-authentication.enter-password.view :as enter-password]
|
||||
[taoensso.timbre :as log]
|
||||
@@ -17,11 +18,11 @@
|
||||
auth-button-label theme blur? auth-button-icon-left]}]
|
||||
(let [handle-auth-success (fn [biometric?]
|
||||
(fn [entered-password]
|
||||
(let [sha3-masked-password (if biometric?
|
||||
entered-password
|
||||
(security/hash-masked-password
|
||||
entered-password))]
|
||||
(on-auth-success sha3-masked-password))))
|
||||
(let [sha3-pwd (if biometric?
|
||||
(str (security/safe-unmask-data entered-password))
|
||||
(native-module/sha3 (str (security/safe-unmask-data
|
||||
entered-password))))]
|
||||
(on-auth-success sha3-pwd))))
|
||||
password-login (fn [{:keys [on-press-biometrics]}]
|
||||
(rf/dispatch [:show-bottom-sheet
|
||||
{:on-close on-close
|
||||
@@ -52,12 +53,10 @@
|
||||
(password-login {:on-press-biometrics
|
||||
#(on-press-biometrics
|
||||
on-press-biometrics)}))}))]
|
||||
(if biometric-auth?
|
||||
(biometric/get-supported-type
|
||||
(fn [biometric-type]
|
||||
(if biometric-type
|
||||
(biometrics-login biometrics-login)
|
||||
(do
|
||||
(reset-password)
|
||||
(password-login {})))))
|
||||
(password-login {}))))
|
||||
(biometric/get-supported-type
|
||||
(fn [biometric-type]
|
||||
(if (and biometric-auth? biometric-type)
|
||||
(biometrics-login biometrics-login)
|
||||
(do
|
||||
(reset-password)
|
||||
(password-login {})))))))
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.common.standard-authentication.standard-auth.authorize :as authorize]
|
||||
[status-im.constants :as constants]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn- view-internal
|
||||
@@ -16,7 +15,7 @@
|
||||
#(reset! reset-slider? true)
|
||||
200))
|
||||
auth-method (rf/sub [:auth-method])
|
||||
biometric-auth? (= auth-method constants/auth-method-biometric)]
|
||||
biometric-auth? (= auth-method "biometric")]
|
||||
(fn [{:keys [track-text
|
||||
customization-color
|
||||
auth-button-label
|
||||
@@ -26,11 +25,11 @@
|
||||
size
|
||||
theme
|
||||
blur?
|
||||
container-style]
|
||||
:or {container-style {:flex 1}}}]
|
||||
[rn/view {:style container-style}
|
||||
container-style]}]
|
||||
[rn/view {:style {:flex 1}}
|
||||
[quo/slide-button
|
||||
{:size size
|
||||
:container-style container-style
|
||||
:customization-color customization-color
|
||||
:on-reset (when @reset-slider? #(reset! reset-slider? false))
|
||||
:on-complete #(authorize/authorize {:on-close on-close
|
||||
|
||||
@@ -200,23 +200,24 @@
|
||||
(handle-url url))))
|
||||
|
||||
(defn generate-profile-url
|
||||
[{:keys [db]} [{:keys [public-key cb]}]]
|
||||
(let [profile-public-key (get-in db [:profile/profile :public-key])
|
||||
profile? (or (not public-key) (= public-key profile-public-key))
|
||||
ens-name? (if profile?
|
||||
(get-in db [:profile/profile :ens-name?])
|
||||
(get-in db [:contacts/contacts public-key :ens-name]))
|
||||
public-key (if profile? profile-public-key public-key)]
|
||||
(when public-key
|
||||
{:json-rpc/call
|
||||
[{:method (if ens-name? "wakuext_shareUserURLWithENS" "wakuext_shareUserURLWithData")
|
||||
:params [public-key]
|
||||
:on-success (fn [url]
|
||||
(rf/dispatch [:universal-links/save-profile-url public-key url])
|
||||
(when (fn? cb) (cb)))
|
||||
:on-error #(log/error "failed to wakuext_shareUserURLWithData"
|
||||
{:error %
|
||||
:public-key public-key})}]})))
|
||||
([cofx] (generate-profile-url cofx nil))
|
||||
([{:keys [db]} [{:keys [public-key cb]}]]
|
||||
(let [profile-public-key (get-in db [:profile/profile :public-key])
|
||||
profile? (or (not public-key) (= public-key profile-public-key))
|
||||
ens-name? (if profile?
|
||||
(get-in db [:profile/profile :ens-name?])
|
||||
(get-in db [:contacts/contacts public-key :ens-name]))
|
||||
public-key (if profile? profile-public-key public-key)]
|
||||
(when public-key
|
||||
{:json-rpc/call
|
||||
[{:method (if ens-name? "wakuext_shareUserURLWithENS" "wakuext_shareUserURLWithData")
|
||||
:params [public-key]
|
||||
:on-success (fn [url]
|
||||
(rf/dispatch [:universal-links/save-profile-url public-key url])
|
||||
(when (fn? cb) (cb)))
|
||||
:on-error #(log/error "failed to wakuext_shareUserURLWithData"
|
||||
{:error %
|
||||
:public-key public-key})}]}))))
|
||||
|
||||
(schema/=> generate-profile-url
|
||||
[:=>
|
||||
|
||||
@@ -5,14 +5,11 @@
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.common.universal-links :as links]))
|
||||
|
||||
(def pubkey
|
||||
"0x04fbce10971e1cd7253b98c7b7e54de3729ca57ce41a2bfb0d1c4e0a26f72c4b6913c3487fa1b4bb86125770f1743fb4459da05c1cbe31d938814cfaf36e252073")
|
||||
|
||||
(deftest handle-url-test
|
||||
(testing "the user is not logged in"
|
||||
(testing "it stores the url for later processing"
|
||||
(is (match? {:db {:universal-links/url "some-url"}}
|
||||
(links/handle-url {:db {}} "some-url")))))
|
||||
(is (= {:db {:universal-links/url "some-url"}}
|
||||
(links/handle-url {:db {}} "some-url")))))
|
||||
(testing "the user is logged in"
|
||||
(let [db {:profile/profile {:public-key "pk"}
|
||||
:app-state "active"
|
||||
@@ -21,9 +18,9 @@
|
||||
(is (nil? (get-in (links/handle-url {:db db} "some-url")
|
||||
[:db :universal-links/url]))))
|
||||
(testing "Handle a custom string"
|
||||
(is (match? (get-in (links/handle-url {:db db} "https://status.app/u#statuse2e")
|
||||
[:router/handle-uri :uri])
|
||||
"https://status.app/u#statuse2e"))))))
|
||||
(is (= (get-in (links/handle-url {:db db} "https://status.app/u#statuse2e")
|
||||
[:router/handle-uri :uri])
|
||||
"https://status.app/u#statuse2e"))))))
|
||||
|
||||
(deftest url-event-listener
|
||||
(testing "the url is not nil"
|
||||
@@ -31,58 +28,77 @@
|
||||
(let [actual (atom nil)]
|
||||
(with-redefs [re-frame/dispatch #(reset! actual %)]
|
||||
(links/url-event-listener #js {:url "some-url"})
|
||||
(is (match? [:universal-links/handle-url "some-url"] @actual))))))
|
||||
(is (= [:universal-links/handle-url "some-url"] @actual))))))
|
||||
(testing "the url is nil"
|
||||
(testing "it does not dispatches the url"
|
||||
(let [actual (atom nil)]
|
||||
(with-redefs [re-frame/dispatch #(reset! actual %)]
|
||||
(links/url-event-listener #js {})
|
||||
(is (match? nil @actual)))))))
|
||||
(is (= nil @actual)))))))
|
||||
|
||||
(deftest generate-profile-url
|
||||
(testing "user has ens name"
|
||||
(testing "it calls the ens rpc method with ens name as param"
|
||||
(let [db {:profile/profile {:ens-name? true :public-key pubkey}}
|
||||
rst (links/generate-profile-url {:db db} [])]
|
||||
(are [result expected] (match? result expected)
|
||||
(let [pubkey "pubkey"
|
||||
db {:profile/profile {:ens-name? true :public-key pubkey}}
|
||||
rst (links/generate-profile-url {:db db})]
|
||||
(are [result expected] (= result expected)
|
||||
"wakuext_shareUserURLWithENS" (-> rst :json-rpc/call first :method)
|
||||
pubkey (-> rst :json-rpc/call first :params first)))))
|
||||
(testing "user has no ens name"
|
||||
(testing "it calls the ens rpc method with public keyas param"
|
||||
(let [db {:profile/profile {:public-key pubkey}}
|
||||
rst (links/generate-profile-url {:db db} [])]
|
||||
(are [result expected] (match? result expected)
|
||||
(let [pubkey "pubkey"
|
||||
db {:profile/profile {:public-key pubkey}}
|
||||
rst (links/generate-profile-url {:db db})]
|
||||
(are [result expected] (= result expected)
|
||||
"wakuext_shareUserURLWithData" (-> rst :json-rpc/call first :method)
|
||||
pubkey (-> rst :json-rpc/call first :params first)))))
|
||||
(testing "contact has ens name"
|
||||
(testing "it calls the ens rpc method with ens name as param"
|
||||
(let [ens "ensname.eth"
|
||||
db {:contacts/contacts {pubkey {:ens-name ens}}}
|
||||
rst (links/generate-profile-url {:db db} [{:public-key pubkey}])]
|
||||
(are [result expected] (match? result expected)
|
||||
(let [pubkey "pubkey"
|
||||
ens "ensname.eth"
|
||||
db {:contacts/contacts {pubkey {:ens-name ens}}}
|
||||
rst (links/generate-profile-url {:db db} [{:public-key pubkey}])]
|
||||
(are [result expected] (= result expected)
|
||||
"wakuext_shareUserURLWithENS" (-> rst :json-rpc/call first :method)
|
||||
pubkey (-> rst :json-rpc/call first :params first)))))
|
||||
(testing "contact has no ens name"
|
||||
(testing "it calls the ens rpc method with public keyas param"
|
||||
(let [db {:contacts/contacts {pubkey {:public-key pubkey}}}
|
||||
rst (links/generate-profile-url {:db db} [{:public-key pubkey}])]
|
||||
(are [result expected] (match? result expected)
|
||||
(let [pubkey "pubkey"
|
||||
db {:contacts/contacts {pubkey {:public-key pubkey}}}
|
||||
rst (links/generate-profile-url {:db db} [{:public-key pubkey}])]
|
||||
(are [result expected] (= result expected)
|
||||
"wakuext_shareUserURLWithData" (-> rst :json-rpc/call first :method)
|
||||
pubkey (-> rst :json-rpc/call first :params first))))))
|
||||
|
||||
(deftest save-profile-url
|
||||
(testing "given a contact public key and profile url"
|
||||
(testing "it updates the contact in db"
|
||||
(let [url "url"
|
||||
db {:contacts/contacts {pubkey {:public-key pubkey}}}
|
||||
rst (links/save-profile-url {:db db} [pubkey url])]
|
||||
(is (match? (get-in rst [:db :contacts/contacts pubkey :universal-profile-url]) url)))))
|
||||
(let [pubkey "pubkey"
|
||||
url "url"
|
||||
db {:contacts/contacts {pubkey {:public-key pubkey}}}
|
||||
rst (links/save-profile-url {:db db} [pubkey url])]
|
||||
(is (= (get-in rst [:db :contacts/contacts pubkey :universal-profile-url]) url)))))
|
||||
(testing "given a user public key and profile url"
|
||||
(testing "it updates the user profile in db"
|
||||
(let [url "url"
|
||||
db {:profile/profile {:public-key pubkey}}
|
||||
rst (links/save-profile-url {:db db} [pubkey url])]
|
||||
(is (match? (get-in rst [:db :profile/profile :universal-profile-url]) url))))))
|
||||
(let [pubkey "pubkey"
|
||||
url "url"
|
||||
db {:profile/profile {:public-key pubkey}}
|
||||
rst (links/save-profile-url {:db db} [pubkey url])]
|
||||
(is (= (get-in rst [:db :profile/profile :universal-profile-url]) url)))))
|
||||
(testing "given a invalid url"
|
||||
(testing "it returns the db untouched"
|
||||
(let [pubkey "pubkey"
|
||||
url "url"
|
||||
db {:profile/profile {:public-key pubkey}}
|
||||
rst (links/save-profile-url {:db db} ["invalid pubkey" url])]
|
||||
(is (= (:db rst) db)))))
|
||||
(testing "given a nil as url"
|
||||
(testing "it returns nil"
|
||||
(let [pubkey "pubkey"
|
||||
db {:profile/profile {:public-key pubkey}}
|
||||
rst (links/save-profile-url {:db db} ["invalid pubkey"])]
|
||||
(is (nil? rst))))))
|
||||
|
||||
(deftest universal-link-test
|
||||
(testing "universal-link?"
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
(ns status-im.common.validation.profile
|
||||
(:require [clojure.string :as string]
|
||||
[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])")
|
||||
|
||||
(def status-regex #"^[a-zA-Z0-9\-_ ]+$")
|
||||
|
||||
(def common-names ["Ethereum" "Bitcoin"])
|
||||
|
||||
(defn has-emojis? [s] (boolean (re-find emoji-regex s)))
|
||||
|
||||
(defn has-common-names? [s] (pos? (count (filter #(string/includes? s %) common-names))))
|
||||
|
||||
(defn has-special-characters? [s] (not (re-find status-regex s)))
|
||||
|
||||
(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 validation-name
|
||||
[s]
|
||||
(cond
|
||||
(or (= s nil) (= s "")) nil
|
||||
(string/ends-with? s "-eth") (i18n/label :t/ending-not-allowed {:ending "-eth"})
|
||||
(string/ends-with? s "_eth") (i18n/label :t/ending-not-allowed {:ending "_eth"})
|
||||
(string/ends-with? s ".eth") (i18n/label :t/ending-not-allowed {:ending ".eth"})
|
||||
(string/starts-with? s " ") (i18n/label :t/start-with-space)
|
||||
(string/ends-with? s " ") (i18n/label :t/ends-with-space)
|
||||
(has-common-names? s) (i18n/label :t/are-not-allowed {:check (i18n/label :t/common-names)})
|
||||
(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)})
|
||||
(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)))
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user