Compare commits

..
572 changed files with 9470 additions and 14645 deletions
+20 -23
View File
@@ -1,23 +1,20 @@
{:lint-as {status-im.utils.views/defview clojure.core/defn
status-im.utils.views/letsubs clojure.core/let
reagent.core/with-let clojure.core/let
status-im.utils.fx/defn clj-kondo.lint-as/def-catch-all
utils.re-frame/defn clj-kondo.lint-as/def-catch-all
quo.react/with-deps-check clojure.core/fn
quo.previews.preview/list-comp clojure.core/for
status-im.utils.styles/def clojure.core/def
status-im.utils.styles/defn clojure.core/defn
test-helpers.unit/deftest-sub clojure.core/defn
taoensso.tufte/defnp clojure.core/defn}
:linters {:consistent-alias {:level :error
:aliases {clojure.string string
clojure.set set
clojure.walk walk
taoensso.timbre log}}
:invalid-arity {:skip-args [status-im.utils.fx/defn utils.re-frame/defn]}
;; TODO remove number when this is fixed
;; https://github.com/borkdude/clj-kondo/issues/867
:unresolved-symbol {:exclude [PersistentPriorityMap.EMPTY
number
status-im.test-helpers/restore-app-db]}}
:config-in-ns {mocks.js-dependencies {:linters {:clojure-lsp/unused-public-var {:level :off}}}}}
{:lint-as {status-im.utils.views/defview clojure.core/defn
status-im.utils.views/letsubs clojure.core/let
reagent.core/with-let clojure.core/let
status-im.utils.fx/defn clj-kondo.lint-as/def-catch-all
utils.re-frame/defn clj-kondo.lint-as/def-catch-all
quo.react/with-deps-check clojure.core/fn
quo.previews.preview/list-comp clojure.core/for
status-im.utils.styles/def clojure.core/def
status-im.utils.styles/defn clojure.core/defn
status-im.test-helpers/deftest-sub clojure.core/defn
taoensso.tufte/defnp clojure.core/defn}
:linters {:consistent-alias {:level :error
:aliases {clojure.string string
taoensso.timbre log}}
:invalid-arity {:skip-args [status-im.utils.fx/defn utils.re-frame/defn]}
;; TODO remove number when this is fixed
;; https://github.com/borkdude/clj-kondo/issues/867
:unresolved-symbol {:exclude [PersistentPriorityMap.EMPTY
number
status-im.test-helpers/restore-app-db]}}}
-1
View File
@@ -32,4 +32,3 @@ COMMANDS_ENABLED=1
TWO_MINUTES_SYNCING=1
SWAP_ENABLED=1
STICKERS_TEST_ENABLED=1
LOCAL_PAIRING_ENABLED=1
-1
View File
@@ -32,4 +32,3 @@ COMMUNITIES_MANAGEMENT_ENABLED=1
DELETE_MESSAGE_ENABLED=1
TWO_MINUTES_SYNCING=1
STICKERS_TEST_ENABLED=1
LOCAL_PAIRING_ENABLED=1
-1
View File
@@ -34,4 +34,3 @@ DELETE_MESSAGE_ENABLED=1
TWO_MINUTES_SYNCING=1
ENABLE_QUO_PREVIEW=1
STICKERS_TEST_ENABLED=1
LOCAL_PAIRING_ENABLED=1
+4 -5
View File
@@ -19,17 +19,15 @@
:respect-bl]
:fn-map
{"reg-sub" :arg1-pair
"h/describe" :arg1-body
"h/test" :arg1-body
"global.describe" :arg1-body
"global.test" :arg1-body
"list-comp" :binding
"defview" :arg1-body
"letsubs" :binding
"testing" :arg1-body
"deftest-sub" :arg1-body
"wait-for" :arg1-body
"with-deps-check" :arg1-body
"ns" [:arg1-body
{:list {:respect-nl? false}
:vector {:respect-nl? false}}]
"->" [:noarg1-body
{:list {:constant-pair? false :force-nl? false}
:next-inner-restore [[:list :constant-pair?]]}]
@@ -41,6 +39,7 @@
:remove {:fn-force-nl #{:noarg1-body}}
:style-map
{:no-comma {:map {:comma? false}}
:rj-var {:pair {:justify {:max-variance 1000}}}
:custom-justify
{:doc "Justify everything using pre-1.1.2 approach"
:binding {:justify? true :justify {:max-variance 1000}}
+7 -14
View File
@@ -289,14 +289,15 @@ lint: export TARGET := default
lint: ##@test Run code style checks
sh scripts/lint-re-frame-in-quo-components.sh && \
clj-kondo --config .clj-kondo/config.edn --cache false --lint src && \
ALL_CLOJURE_FILE=$$(git ls-files | grep -e \.clj$$ -e \.cljs$$ -e \.cljc$$ -e \.edn$$) && \
zprint '{:search-config? true}' -sfc $$ALL_CLOJURE_FILE
TARGETS=$$(git diff --diff-filter=d --cached --name-only | grep -e \.clj$$ -e \.cljs$$ -e \.cljc$$ -e \.edn$$ || echo shadow-cljs.edn) && \
zprint '{:search-config? true}' -fc $$TARGETS
lint-fix: export TARGET := default
lint-fix: ##@test Run code style checks and fix issues
ALL_CLOJURE_FILE=$$(git ls-files | grep -e \.clj$$ -e \.cljs$$ -e \.cljc$$ -e \.edn$$) && \
zprint '{:search-config? true}' -sw $$ALL_CLOJURE_FILE && \
zprint '{:search-config? true}' -sw $$ALL_CLOJURE_FILE
TARGETS=$$(git diff --diff-filter=d --cached --name-only | grep -e \.clj$$ -e \.cljs$$ -e \.cljc$$ -e \.edn$$ || echo shadow-cljs.edn) && \
clojure-lsp clean-ns --filenames $$(echo $$TARGETS | xargs | sed -e 's/ /,/g') && \
zprint '{:search-config? true}' -fw $$TARGETS && \
zprint '{:search-config? true}' -fw $$TARGETS
shadow-server: export TARGET := clojure
@@ -308,14 +309,6 @@ 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-watch-for-repl: export TARGET := clojure
test-watch-for-repl: ##@ Watch tests and support REPL connections
yarn install
rm -f target/test/test.js
concurrently --kill-others --prefix-colors 'auto' --names 'build,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: export TARGET := clojure
test: ##@test Run tests once in NodeJS
# Here we create the gyp bindings for nodejs
@@ -330,7 +323,7 @@ run-visual-test-ios: XCODE_DERIVED_DATA := $(HOME)/Library/Developer/Xcode/Deriv
run-visual-test-ios: APPLICATION_NAME := StatusIm-brfnruzfrkkycpbndmdoeyrigthc
run-visual-test-ios: export TEST_BINARY_PATH := $(XCODE_DERIVED_DATA)/$(APPLICATION_NAME)/Build/Products/Debug-iphonesimulator/StatusIm.app
run-visual-test-ios: ##@test Run tests once in NodeJS
detox test --configuration ios.sim.debug
detox test --configuration ios.sim.debug
component-test-watch: export TARGET := clojure
component-test-watch: export COMPONENT_TEST := true
@@ -20,8 +20,6 @@ printed out by mistake in a log entry (see https://github.com/status-im/status-m
To minimize the risk of leaking passwords through logs, we should not pass
passwords as strings in our codebase. We introduced a new type `MaskedData` in
`status-im.utils.security`.
update (16-Dec-2022) `status-im.utils.security` is now moved over to `utils.security.core`
We use `(security/mask-data <data to hide>` to wrap sensitive data into this
type and then use `(security/unmask <masked-data>)` to get the plaintext back.
+12 -28
View File
@@ -364,7 +364,7 @@ actually subscribing to them, so reframe's signal graph gets validated too.
(is (= expected (recipes [current-user all-recipes location]))))))
;; good
(require '[test-helpers.unit :as h])
(require '[status-im.test-helpers :as h])
(re-frame/reg-sub
:user/recipes
@@ -399,15 +399,13 @@ src
├── react_native
│ ├── gesture.cljs
│ └── platform.cljs
├── status_im/
├── status_im2
├── status_im
│ ├── common
│ │ └── components
│ │ └── bottom_sheet.cljs
│ ├── contexts/
│ ├── setup/
│ └── subs/
├── test_helpers/
└── utils.cljs
```
@@ -416,23 +414,17 @@ src
- `src/quo2/`: The component library for Status Mobile.
- `src/react_native/`: Contains only low-level constructs to help React Native
work in tandem with Clojure(Script).
- `src/status_im2/`: Directory where we try to be as strict as possible about
our guidelines and where we prefer to write code for the new, redesigned
mobile app.
- `src/status_im/`: Directory containing what we call "old code", not yet
migrated to new guidelines for the new mobile app.
- `src/status_im2/common/`: Directories named `common` can appear at any level
of the directory tree. Just like directories named `utils`, their directory
- `src/status_im/common/`: Directories named `common` can appear at any level of
the directory tree. Just like directories named `utils`, their directory
nesting level communicates their applicable limits.
- `src/status_im2/common/components/`: Contains reusable components that are not
- `src/status_im/common/components/`: Contains reusable components that are not
part of the design system (quo2).
- `src/status_im2/contexts/`: Contains [bounded contexts](#glossary), like
- `src/status_im/contexts/`: Contains [bounded contexts](#glossary), like
`browser/`, `messaging/`, etc. As much as possible, _bounded contexts_ should
not directly require each other's namespaces.
- `src/status_im2/setup/`: Contains namespaces that are mostly used to
initialize the application, configure test runners, etc. In general, such
namespaces should not be required from the outside.
- `src/test_helpers/`: Reusable utilities for writing all kinds of tests.
- `src/status_im/setup/`: Contains namespaces that are mostly used to initialize
the application, configure test runners, etc. In general, such namespaces
should not be required from the outside.
- `src/status_im/subs/`: All subscriptions should live inside it.
Directories named `utils/` can appear at any level of the directory tree. The
@@ -493,26 +485,18 @@ src
[Unit tests](#glossary) should be created alongside their respective source
implementation. We prefer them colocated with the source and not like most
Clojure (JVM) codebases which mirror the sources in a top-level test directory.
Component tests should be created in a separate directory `__tests__`. Example:
```
├── models
│ ├── message.cljs
│ └── message_test.cljs
├── __tests__
│ └── input_bla_test.cljs
├── models.cljs
└── models_test.cljs
```
Component tests should be created in a separate directory `__tests__`, colocated
with the source. When the entire component implementation is isolated under a
single directory, create a test file named `component_spec.cljs` instead.
```
└── filter
├── component_spec.cljs
├── style.cljs
└── view.cljs
```
There's no hard rule on how integration test namespaces should be split, but
we're at least striving to define them under appropriate bounded contexts that
mirror the source code.
@@ -19,11 +19,6 @@ def getStatusGoSHA1 = { ->
android {
compileSdkVersion rootProject.ext.compileSdkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
@@ -59,12 +59,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.Map;
import java.util.Stack;
import java.util.zip.ZipEntry;
@@ -531,33 +527,45 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
StatusThreadPoolExecutor.getInstance().execute(r);
}
private void executeRunnableStatusGoMethod(Supplier<String> method, Callback callback) throws JSONException {
@ReactMethod
public void verify(final String address, final String password, final Callback callback) {
Log.d(TAG, "verify");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable runnableTask = () -> {
String res = method.get();
callback.invoke(res);
};
StatusThreadPoolExecutor.getInstance().execute(runnableTask);
}
@ReactMethod
public void verify(final String address, final String password, final Callback callback) throws JSONException {
Activity currentActivity = getCurrentActivity();
final String absRootDirPath = this.getNoBackupDirectory();
final String newKeystoreDir = pathCombine(absRootDirPath, "keystore");
executeRunnableStatusGoMethod(() -> Statusgo.verifyAccountPassword(newKeystoreDir, address, password), callback);
Runnable r = new Runnable() {
@Override
public void run() {
String result = Statusgo.verifyAccountPassword(newKeystoreDir, address, password);
callback.invoke(result);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void verifyDatabasePassword(final String keyUID, final String password, final Callback callback) throws JSONException {
executeRunnableStatusGoMethod(() -> Statusgo.verifyDatabasePassword(keyUID, password), callback);
public void verifyDatabasePassword(final String keyUID, final String password, final Callback callback) {
Log.d(TAG, "verifyDatabasePassword");
Runnable r = new Runnable() {
@Override
public void run() {
String result = Statusgo.verifyDatabasePassword(keyUID, password);
callback.invoke(result);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
public String getKeyStorePath(String keyUID) {
@@ -727,209 +735,361 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
}
@ReactMethod
public void addPeer(final String enode, final Callback callback) throws JSONException {
executeRunnableStatusGoMethod(() -> Statusgo.addPeer(enode), callback);
}
@ReactMethod
public void multiAccountStoreAccount(final String json, final Callback callback) throws JSONException {
executeRunnableStatusGoMethod(() -> Statusgo.multiAccountStoreAccount(json), callback);
}
@ReactMethod
public void multiAccountLoadAccount(final String json, final Callback callback) throws JSONException {
executeRunnableStatusGoMethod(() -> Statusgo.multiAccountLoadAccount(json), callback);
}
@ReactMethod
public void multiAccountReset(final Callback callback) throws JSONException {
executeRunnableStatusGoMethod(() -> Statusgo.multiAccountReset(), callback);
}
@ReactMethod
public void multiAccountDeriveAddresses(final String json, final Callback callback) throws JSONException {
executeRunnableStatusGoMethod(() -> Statusgo.multiAccountDeriveAddresses(json), callback);
}
@ReactMethod
public void multiAccountGenerateAndDeriveAddresses(final String json, final Callback callback) throws JSONException {
executeRunnableStatusGoMethod(() -> Statusgo.multiAccountGenerateAndDeriveAddresses(json), callback);
}
@ReactMethod
public void multiAccountStoreDerived(final String json, final Callback callback) throws JSONException {
executeRunnableStatusGoMethod(() -> Statusgo.multiAccountStoreDerivedAccounts(json), callback);
}
@ReactMethod
public void multiAccountImportMnemonic(final String json, final Callback callback) throws JSONException {
executeRunnableStatusGoMethod(() -> Statusgo.multiAccountImportMnemonic(json), callback);
}
@ReactMethod
public void multiAccountImportPrivateKey(final String json, final Callback callback) throws JSONException {
executeRunnableStatusGoMethod(() -> Statusgo.multiAccountImportPrivateKey(json), callback);
}
@ReactMethod
public void hashTransaction(final String txArgsJSON, final Callback callback) throws JSONException {
executeRunnableStatusGoMethod(() -> Statusgo.hashTransaction(txArgsJSON), callback);
}
@ReactMethod
public void hashMessage(final String message, final Callback callback) throws JSONException {
executeRunnableStatusGoMethod(() -> Statusgo.hashMessage(message), callback);
}
@ReactMethod
public void getConnectionStringForBootstrappingAnotherDevice(final String configJSON, final Callback callback) throws JSONException {
final JSONObject jsonConfig = new JSONObject(configJSON);
final String keyUID = jsonConfig.getString("keyUID");
final String keyStorePath = this.getKeyStorePath(keyUID);
jsonConfig.put("keystorePath", keyStorePath);
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 String keyStorePath = pathCombine(this.getNoBackupDirectory(), "/keystore");
jsonConfig.put("keystorePath", keyStorePath);
executeRunnableStatusGoMethod(() -> Statusgo.inputConnectionStringForBootstrapping(connectionString, jsonConfig.toString()), callback);
}
@ReactMethod
public void multiformatSerializePublicKey(final String multiCodecKey, final String base58btc, final Callback callback) throws JSONException {
public void addPeer(final String enode, final Callback callback) {
Log.d(TAG, "addPeer");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable runnableTask = new Runnable() {
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.multiformatSerializePublicKey(multiCodecKey,base58btc);
String res = Statusgo.addPeer(enode);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(runnableTask);
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void multiformatDeserializePublicKey(final String multiCodecKey, final String base58btc, final Callback callback) throws JSONException {
public void multiAccountStoreAccount(final String json, final Callback callback) {
Log.d(TAG, "multiAccountStoreAccount");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.multiAccountStoreAccount(json);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void multiAccountLoadAccount(final String json, final Callback callback) {
Log.d(TAG, "multiAccountLoadAccount");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.multiAccountLoadAccount(json);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void multiAccountReset(final Callback callback) {
Log.d(TAG, "multiAccountReset");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.multiAccountReset();
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void multiAccountDeriveAddresses(final String json, final Callback callback) {
Log.d(TAG, "multiAccountDeriveAddresses");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.multiAccountDeriveAddresses(json);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void multiAccountGenerateAndDeriveAddresses(final String json, final Callback callback) {
Log.d(TAG, "multiAccountGenerateAndDeriveAddresses");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.multiAccountGenerateAndDeriveAddresses(json);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void multiAccountStoreDerived(final String json, final Callback callback) {
Log.d(TAG, "multiAccountStoreDerived");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.multiAccountStoreDerivedAccounts(json);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void multiAccountImportMnemonic(final String json, final Callback callback) {
Log.d(TAG, "multiAccountImportMnemonic");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.multiAccountImportMnemonic(json);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void multiAccountImportPrivateKey(final String json, final Callback callback) {
Log.d(TAG, "multiAccountImportPrivateKey");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.multiAccountImportPrivateKey(json);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void hashTransaction(final String txArgsJSON, final Callback callback) {
Log.d(TAG, "hashTransaction");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable runnableTask = new Runnable() {
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.multiformatDeserializePublicKey(multiCodecKey,base58btc);
String res = Statusgo.hashTransaction(txArgsJSON);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(runnableTask);
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void compressPublicKey(final String multiCodecKey, final Callback callback) throws JSONException {
public void hashMessage(final String message, final Callback callback) {
Log.d(TAG, "hashMessage");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable runnableTask = new Runnable() {
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.compressPublicKey(multiCodecKey);
String res = Statusgo.hashMessage(message);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(runnableTask);
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void decompressPublicKey(final String multiCodecKey, final Callback callback) throws JSONException {
public void hashTypedData(final String data, final Callback callback) {
Log.d(TAG, "hashTypedData");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable runnableTask = new Runnable() {
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.decompressPublicKey(multiCodecKey);
String res = Statusgo.hashTypedData(data);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(runnableTask);
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void deserializeAndCompressKey(final String desktopKey, final Callback callback) throws JSONException {
public void hashTypedDataV4(final String data, final Callback callback) {
Log.d(TAG, "hashTypedDataV4");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable runnableTask = new Runnable() {
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.deserializeAndCompressKey(desktopKey);
String res = Statusgo.hashTypedDataV4(data);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(runnableTask);
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void hashTypedData(final String data, final Callback callback) throws JSONException {
executeRunnableStatusGoMethod(() -> Statusgo.hashTypedData(data), callback);
public void sendTransactionWithSignature(final String txArgsJSON, final String signature, final Callback callback) {
Log.d(TAG, "sendTransactionWithSignature");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.sendTransactionWithSignature(txArgsJSON, signature);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void hashTypedDataV4(final String data, final Callback callback) throws JSONException {
executeRunnableStatusGoMethod(() -> Statusgo.hashTypedDataV4(data), callback);
public void sendTransaction(final String txArgsJSON, final String password, final Callback callback) {
Log.d(TAG, "sendTransaction");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.sendTransaction(txArgsJSON, password);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void sendTransactionWithSignature(final String txArgsJSON, final String signature, final Callback callback) throws JSONException {
executeRunnableStatusGoMethod(() -> Statusgo.sendTransactionWithSignature(txArgsJSON, signature), callback);
public void signMessage(final String rpcParams, final Callback callback) {
Log.d(TAG, "signMessage");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.signMessage(rpcParams);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void sendTransaction(final String txArgsJSON, final String password, final Callback callback) throws JSONException {
executeRunnableStatusGoMethod(() -> Statusgo.sendTransaction(txArgsJSON, password), callback);
public void recover(final String rpcParams, final Callback callback) {
Log.d(TAG, "recover");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.recover(rpcParams);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void signMessage(final String rpcParams, final Callback callback) throws JSONException {
executeRunnableStatusGoMethod(() -> Statusgo.signMessage(rpcParams), callback);
public void signTypedData(final String data, final String account, final String password, final Callback callback) {
Log.d(TAG, "signTypedData");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.signTypedData(data, account, password);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void recover(final String rpcParams, final Callback callback) throws JSONException {
executeRunnableStatusGoMethod(() -> Statusgo.recover(rpcParams), callback);
}
public void signTypedDataV4(final String data, final String account, final String password, final Callback callback) {
Log.d(TAG, "signTypedDataV4");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
@ReactMethod
public void signTypedData(final String data, final String account, final String password, final Callback callback) throws JSONException {
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 {
executeRunnableStatusGoMethod(() -> Statusgo.signTypedDataV4(data, account, password), callback);
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.signTypedDataV4(data, account, password);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
@@ -1034,13 +1194,29 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
}
@ReactMethod
public void callRPC(final String payload, final Callback callback) throws JSONException {
executeRunnableStatusGoMethod(() -> Statusgo.callRPC(payload), callback);
public void callRPC(final String payload, final Callback callback) {
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.callRPC(payload);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void callPrivateRPC(final String payload, final Callback callback) throws JSONException {
executeRunnableStatusGoMethod(() -> Statusgo.callPrivateRPC(payload), callback);
public void callPrivateRPC(final String payload, final Callback callback) {
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.callPrivateRPC(payload);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
@@ -1103,30 +1279,105 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
}
@ReactMethod
public void extractGroupMembershipSignatures(final String signaturePairs, final Callback callback) throws JSONException {
executeRunnableStatusGoMethod(() -> Statusgo.extractGroupMembershipSignatures(signaturePairs), callback);
public void extractGroupMembershipSignatures(final String signaturePairs, final Callback callback) {
Log.d(TAG, "extractGroupMembershipSignatures");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String result = Statusgo.extractGroupMembershipSignatures(signaturePairs);
callback.invoke(result);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void signGroupMembership(final String content, final Callback callback) throws JSONException {
executeRunnableStatusGoMethod(() -> Statusgo.signGroupMembership(content), callback);
public void signGroupMembership(final String content, final Callback callback) {
Log.d(TAG, "signGroupMembership");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String result = Statusgo.signGroupMembership(content);
callback.invoke(result);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void getNodeConfig(final Callback callback) throws JSONException {
executeRunnableStatusGoMethod(() -> Statusgo.getNodeConfig(), callback);
public void getNodeConfig(final Callback callback) {
Log.d(TAG, "getNodeConfig");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String result = Statusgo.getNodeConfig();
callback.invoke(result);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void deleteMultiaccount(final String keyUID, final Callback callback) throws JSONException {
public void deleteMultiaccount(final String keyUID, final Callback callback) {
Log.d(TAG, "deleteMultiaccount");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
final String keyStoreDir = this.getKeyStorePath(keyUID);
executeRunnableStatusGoMethod(() -> Statusgo.deleteMultiaccount(keyUID, keyStoreDir), callback);
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.deleteMultiaccount(keyUID, keyStoreDir);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void deleteImportedKey(final String keyUID, final String address, final String password, final Callback callback) throws JSONException {
public void deleteImportedKey(final String keyUID, final String address, final String password, final Callback callback) {
Log.d(TAG, "deleteImportedKey");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
final String keyStoreDir = this.getKeyStorePath(keyUID);
executeRunnableStatusGoMethod(() -> Statusgo.deleteImportedKey(address, password, keyStoreDir), callback);
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.deleteImportedKey(address, password, keyStoreDir);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod(isBlockingSynchronousMethod = true)
@@ -1135,8 +1386,24 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
}
@ReactMethod
public void generateAliasAsync(final String seed, final Callback callback) throws JSONException {
executeRunnableStatusGoMethod(() -> Statusgo.generateAlias(seed), callback);
public void generateAliasAsync(final String seed, final Callback callback) {
Log.d(TAG, "generateAliasAsync");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.generateAlias(seed);
Log.d(TAG, res);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod(isBlockingSynchronousMethod = true)
@@ -1200,33 +1467,47 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
}
@ReactMethod
public void identiconAsync(final String seed, final Callback callback) throws JSONException {
executeRunnableStatusGoMethod(() -> Statusgo.identicon(seed), callback);
public void identiconAsync(final String seed, final Callback callback) {
Log.d(TAG, "identiconAsync");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.identicon(seed);
Log.d(TAG, res);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void generateAliasAndIdenticonAsync(final String seed, final Callback callback) {
Log.d(TAG, "generateAliasAndIdenticonAsync");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Log.d(TAG, "generateAliasAndIdenticonAsync");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String resIdenticon = Statusgo.identicon(seed);
String resAlias = Statusgo.generateAlias(seed);
Runnable r = new Runnable() {
@Override
public void run() {
String resIdenticon = Statusgo.identicon(seed);
String resAlias = Statusgo.generateAlias(seed);
Log.d(TAG, resIdenticon);
Log.d(TAG, resAlias);
callback.invoke(resAlias, resIdenticon);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
Log.d(TAG, resIdenticon);
Log.d(TAG, resAlias);
callback.invoke(resAlias, resIdenticon);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@Override
@@ -1248,8 +1529,24 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
}
@ReactMethod
public void validateMnemonic(final String seed, final Callback callback) throws JSONException {
executeRunnableStatusGoMethod(() -> Statusgo.validateMnemonic(seed), callback);
public void validateMnemonic(final String seed, final Callback callback) {
Log.d(TAG, "validateMnemonic");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String resValidateMnemonic = Statusgo.validateMnemonic(seed);
Log.d(TAG, resValidateMnemonic);
callback.invoke(resValidateMnemonic);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
@@ -1300,14 +1597,35 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
}
@ReactMethod
public void reEncryptDbAndKeystore(final String keyUID, final String password, final String newPassword, final Callback callback) throws JSONException {
executeRunnableStatusGoMethod(() -> Statusgo.changeDatabasePassword(keyUID, password, newPassword), callback);
public void reEncryptDbAndKeystore(final String keyUID, final String password, final String newPassword, final Callback callback) {
Log.d(TAG, "reEncryptDbAndKeyStore");
Runnable r = new Runnable() {
@Override
public void run() {
// changes db password and re-encrypts keystore
String result = Statusgo.changeDatabasePassword(keyUID, password, newPassword);
callback.invoke(result);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void convertToKeycardAccount(final String keyUID, final String accountData, final String options, final String password, final String newPassword, final Callback callback) throws JSONException {
public void convertToKeycardAccount(final String keyUID, final String accountData, final String options, final String password, final String newPassword, final Callback callback) {
Log.d(TAG, "convertToKeycardAccount");
final String keyStoreDir = this.getKeyStorePath(keyUID);
executeRunnableStatusGoMethod(() -> Statusgo.convertToKeycardAccount(keyStoreDir, accountData, options, password, newPassword), callback);
Runnable r = new Runnable() {
@Override
public void run() {
String result = Statusgo.convertToKeycardAccount(keyStoreDir, accountData, options, password, newPassword);
callback.invoke(result);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
}
@@ -90,25 +90,26 @@ static RCTBridge *bridge;
RCT_EXPORT_MODULE();
////////////////////////////////////////////////////////////////////
#pragma mark - shouldMoveToInternalStorage
//////////////////////////////////////////////////////////////////// shouldMoveToInternalStorage
RCT_EXPORT_METHOD(shouldMoveToInternalStorage:(RCTResponseSenderBlock)onResultCallback) {
// Android only
onResultCallback(@[[NSNull null]]);
}
////////////////////////////////////////////////////////////////////
#pragma mark - moveToInternalStorage
//////////////////////////////////////////////////////////////////// moveToInternalStorage
RCT_EXPORT_METHOD(moveToInternalStorage:(RCTResponseSenderBlock)onResultCallback) {
// Android only
onResultCallback(@[[NSNull null]]);
}
////////////////////////////////////////////////////////////////////
#pragma mark - InitKeystore method
//////////////////////////////////////////////////////////////////// StopNode
RCT_EXPORT_METHOD(initKeystore:(NSString *)keyUID
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -131,9 +132,9 @@ RCT_EXPORT_METHOD(initKeystore:(NSString *)keyUID
});
}
////////////////////////////////////////////////////////////////////
#pragma mark - SendLogs method
//////////////////////////////////////////////////////////////////// sendLogs
RCT_EXPORT_METHOD(sendLogs:(NSString *)dbJson
jsLogs:(NSString *)jsLogs
callback:(RCTResponseSenderBlock)callback) {
@@ -195,6 +196,7 @@ RCT_EXPORT_METHOD(sendLogs:(NSString *)dbJson
callback(@[zipFile.absoluteString]);
}
//////////////////////////////////////////////////////////////////// addPeer
RCT_EXPORT_METHOD(exportLogs:(RCTResponseSenderBlock)callback) {
#if DEBUG
NSLog(@"exportLogs() method called");
@@ -203,6 +205,7 @@ RCT_EXPORT_METHOD(exportLogs:(RCTResponseSenderBlock)callback) {
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// addPeer
RCT_EXPORT_METHOD(addPeer:(NSString *)enode
callback:(RCTResponseSenderBlock)callback) {
NSString *result = StatusgoAddPeer(enode);
@@ -212,6 +215,7 @@ RCT_EXPORT_METHOD(addPeer:(NSString *)enode
#endif
}
//////////////////////////////////////////////////////////////////// multiAccountImportPrivateKey
RCT_EXPORT_METHOD(deleteMultiaccount:(NSString *)keyUID
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -222,6 +226,7 @@ RCT_EXPORT_METHOD(deleteMultiaccount:(NSString *)keyUID
callback(@[result]);
}
////////////////////////////////////verify//////////////////////////////// multiAccountImportPrivateKey
RCT_EXPORT_METHOD(deleteImportedKey:(NSString *)keyUID
address:(NSString *)address
password:(NSString *)password
@@ -234,6 +239,7 @@ RCT_EXPORT_METHOD(deleteImportedKey:(NSString *)keyUID
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// multiAccountGenerateAndDeriveAddresses
RCT_EXPORT_METHOD(multiAccountGenerateAndDeriveAddresses:(NSString *)json
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -243,6 +249,7 @@ RCT_EXPORT_METHOD(multiAccountGenerateAndDeriveAddresses:(NSString *)json
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// MultiAccountStoreAccount
RCT_EXPORT_METHOD(multiAccountStoreAccount:(NSString *)json
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -252,6 +259,7 @@ RCT_EXPORT_METHOD(multiAccountStoreAccount:(NSString *)json
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// MultiAccountLoadAccount
RCT_EXPORT_METHOD(multiAccountLoadAccount:(NSString *)json
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -261,6 +269,7 @@ RCT_EXPORT_METHOD(multiAccountLoadAccount:(NSString *)json
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// MultiAccountReset
RCT_EXPORT_METHOD(multiAccountReset:(RCTResponseSenderBlock)callback) {
#if DEBUG
NSLog(@"MultiAccountReset() method called");
@@ -269,6 +278,7 @@ RCT_EXPORT_METHOD(multiAccountReset:(RCTResponseSenderBlock)callback) {
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// multiAccountStoreDerived
RCT_EXPORT_METHOD(multiAccountStoreDerived:(NSString *)json
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -278,6 +288,7 @@ RCT_EXPORT_METHOD(multiAccountStoreDerived:(NSString *)json
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// multiAccountImportPrivateKey
RCT_EXPORT_METHOD(multiAccountImportPrivateKey:(NSString *)json
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -287,6 +298,7 @@ RCT_EXPORT_METHOD(multiAccountImportPrivateKey:(NSString *)json
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// hashTransaction
RCT_EXPORT_METHOD(hashTransaction:(NSString *)txArgsJSON
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -296,6 +308,7 @@ RCT_EXPORT_METHOD(hashTransaction:(NSString *)txArgsJSON
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// hashMessage
RCT_EXPORT_METHOD(hashMessage:(NSString *)message
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -305,72 +318,7 @@ RCT_EXPORT_METHOD(hashMessage:(NSString *)message
callback(@[result]);
}
RCT_EXPORT_METHOD(getConnectionStringForBootstrappingAnotherDevice:(NSString *)configJSON
callback:(RCTResponseSenderBlock)callback) {
NSData *configData = [configJSON dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *configDict = [NSJSONSerialization JSONObjectWithData:configData options:NSJSONReadingMutableContainers error:nil];
NSString *keyUID = [configDict objectForKey:@"keyUID"];
NSURL *multiaccountKeystoreDir = [self getKeyStoreDir:keyUID];
NSString *keystoreDir = multiaccountKeystoreDir.path;
[configDict setValue:keystoreDir forKey:@"keystorePath"];
NSString *modifiedConfigJSON = [configDict bv_jsonStringWithPrettyPrint:NO];
NSString *result = StatusgoGetConnectionStringForBootstrappingAnotherDevice(modifiedConfigJSON);
callback(@[result]);
}
RCT_EXPORT_METHOD(inputConnectionStringForBootstrapping:(NSString *)cs
configJSON:(NSString *)configJSON
callback:(RCTResponseSenderBlock)callback) {
NSData *configData = [configJSON dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *configDict = [NSJSONSerialization JSONObjectWithData:configData options:NSJSONReadingMutableContainers error:nil];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *rootUrl =[[fileManager URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask] lastObject];
NSURL *multiaccountKeystoreDir = [rootUrl URLByAppendingPathComponent:@"keystore"];
NSString *keystoreDir = multiaccountKeystoreDir.path;
[configDict setValue:keystoreDir forKey:@"keystorePath"];
NSString *modifiedConfigJSON = [configDict bv_jsonStringWithPrettyPrint:NO];
NSString *result = StatusgoInputConnectionStringForBootstrapping(cs,modifiedConfigJSON);
callback(@[result]);
}
RCT_EXPORT_METHOD(multiformatSerializePublicKey:(NSString *)multiCodecKey
base58btc:(NSString *)base58btc
callback:(RCTResponseSenderBlock)callback) {
NSString *result = StatusgoMultiformatSerializePublicKey(multiCodecKey,base58btc);
callback(@[result]);
}
RCT_EXPORT_METHOD(multiformatDeserializePublicKey:(NSString *)multiCodecKey
base58btc:(NSString *)base58btc
callback:(RCTResponseSenderBlock)callback) {
NSString *result = StatusgoMultiformatDeserializePublicKey(multiCodecKey,base58btc);
callback(@[result]);
}
RCT_EXPORT_METHOD(decompressPublicKey:(NSString *)multiCodecKey
callback:(RCTResponseSenderBlock)callback) {
NSString *result = StatusgoDecompressPublicKey(multiCodecKey);
callback(@[result]);
}
RCT_EXPORT_METHOD(compressPublicKey:(NSString *)multiCodecKey
callback:(RCTResponseSenderBlock)callback) {
NSString *result = StatusgoCompressPublicKey(multiCodecKey);
callback(@[result]);
}
RCT_EXPORT_METHOD(deserializeAndCompressKey:(NSString *)desktopKey
callback:(RCTResponseSenderBlock)callback) {
NSString *result = StatusgoDeserializeAndCompressKey(desktopKey);
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// hashTypedData
RCT_EXPORT_METHOD(hashTypedData:(NSString *)data
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -380,6 +328,7 @@ RCT_EXPORT_METHOD(hashTypedData:(NSString *)data
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// hashTypedDataV4
RCT_EXPORT_METHOD(hashTypedDataV4:(NSString *)data
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -389,6 +338,7 @@ RCT_EXPORT_METHOD(hashTypedDataV4:(NSString *)data
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// sendTransactionWithSignature
RCT_EXPORT_METHOD(sendTransactionWithSignature:(NSString *)txArgsJSON
signature:(NSString *)signature
callback:(RCTResponseSenderBlock)callback) {
@@ -399,6 +349,7 @@ RCT_EXPORT_METHOD(sendTransactionWithSignature:(NSString *)txArgsJSON
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// multiAccountImportMnemonic
RCT_EXPORT_METHOD(multiAccountImportMnemonic:(NSString *)json
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -408,6 +359,7 @@ RCT_EXPORT_METHOD(multiAccountImportMnemonic:(NSString *)json
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// multiAccountDeriveAddresses
RCT_EXPORT_METHOD(multiAccountDeriveAddresses:(NSString *)json
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -427,6 +379,7 @@ RCT_EXPORT_METHOD(multiAccountDeriveAddresses:(NSString *)json
return [json valueForKey:@"key-uid"];
}
//////////////////////////////////////////////////////////////////// prepareDirAndUpdateConfig
-(NSString *) prepareDirAndUpdateConfig:(NSString *)config
withKeyUID:(NSString *)keyUID {
NSFileManager *fileManager = [NSFileManager defaultManager];
@@ -499,6 +452,7 @@ RCT_EXPORT_METHOD(multiAccountDeriveAddresses:(NSString *)json
}
//////////////////////////////////////////////////////////////////// prepareDirAndUpdateConfig
RCT_EXPORT_METHOD(prepareDirAndUpdateConfig:(NSString *)keyUID
config:(NSString *)config
callback:(RCTResponseSenderBlock)callback) {
@@ -510,6 +464,7 @@ RCT_EXPORT_METHOD(prepareDirAndUpdateConfig:(NSString *)keyUID
callback(@[updatedConfig]);
}
//////////////////////////////////////////////////////////////////// saveAccountAndLogin
RCT_EXPORT_METHOD(saveAccountAndLogin:(NSString *)multiaccountData
password:(NSString *)password
settings:(NSString *)settings
@@ -526,6 +481,7 @@ RCT_EXPORT_METHOD(saveAccountAndLogin:(NSString *)multiaccountData
NSLog(@"%@", result);
}
//////////////////////////////////////////////////////////////////// saveAccountAndLoginWithKeycard
RCT_EXPORT_METHOD(saveAccountAndLoginWithKeycard:(NSString *)multiaccountData
password:(NSString *)password
settings:(NSString *)settings
@@ -546,11 +502,11 @@ RCT_EXPORT_METHOD(saveAccountAndLoginWithKeycard:(NSString *)multiaccountData
- (NSString *) getExportDbFilePath {
NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"export.db"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:filePath]) {
[fileManager removeItemAtPath:filePath error:nil];
}
return filePath;
}
@@ -587,6 +543,7 @@ RCT_EXPORT_METHOD(saveAccountAndLoginWithKeycard:(NSString *)multiaccountData
}
}
//////////////////////////////////////////////////////////////////// login
RCT_EXPORT_METHOD(login:(NSString *)accountData
password:(NSString *)password) {
#if DEBUG
@@ -598,6 +555,7 @@ RCT_EXPORT_METHOD(login:(NSString *)accountData
NSLog(@"%@", result);
}
//////////////////////////////////////////////////////////////////// loginWithConfig
RCT_EXPORT_METHOD(loginWithConfig:(NSString *)accountData
password:(NSString *)password
configJSON:(NSString *)configJSON) {
@@ -610,6 +568,7 @@ RCT_EXPORT_METHOD(loginWithConfig:(NSString *)accountData
NSLog(@"%@", result);
}
//////////////////////////////////////////////////////////////////// loginWithKeycard
RCT_EXPORT_METHOD(loginWithKeycard:(NSString *)accountData
password:(NSString *)password
chatKey:(NSString *)chatKey) {
@@ -624,6 +583,7 @@ RCT_EXPORT_METHOD(loginWithKeycard:(NSString *)accountData
NSLog(@"%@", result);
}
//////////////////////////////////////////////////////////////////// logout
RCT_EXPORT_METHOD(logout) {
#if DEBUG
NSLog(@"Logout() method called");
@@ -633,6 +593,7 @@ RCT_EXPORT_METHOD(logout) {
NSLog(@"%@", result);
}
//////////////////////////////////////////////////////////////////// openAccounts
RCT_EXPORT_METHOD(openAccounts:(RCTResponseSenderBlock)callback) {
#if DEBUG
NSLog(@"OpenAccounts() method called");
@@ -646,6 +607,7 @@ RCT_EXPORT_METHOD(openAccounts:(RCTResponseSenderBlock)callback) {
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// verityAccountPassword
RCT_EXPORT_METHOD(verify:(NSString *)address
password:(NSString *)password
callback:(RCTResponseSenderBlock)callback) {
@@ -662,6 +624,7 @@ RCT_EXPORT_METHOD(verify:(NSString *)address
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// verifyDatabasePassword
RCT_EXPORT_METHOD(verifyDatabasePassword:(NSString *)keyUID
password:(NSString *)password
callback:(RCTResponseSenderBlock)callback) {
@@ -672,6 +635,7 @@ RCT_EXPORT_METHOD(verifyDatabasePassword:(NSString *)keyUID
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// changeDatabasePassword
RCT_EXPORT_METHOD(reEncryptDbAndKeystore:(NSString *)keyUID
currentPassword:(NSString *)currentPassword
newPassword:(NSString *)newPassword
@@ -684,6 +648,7 @@ RCT_EXPORT_METHOD(reEncryptDbAndKeystore:(NSString *)keyUID
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// convertToKeycardAccount
RCT_EXPORT_METHOD(convertToKeycardAccount:(NSString *)keyUID
accountData:(NSString *)accountData
settings:(NSString *)settings
@@ -698,9 +663,9 @@ RCT_EXPORT_METHOD(convertToKeycardAccount:(NSString *)keyUID
callback(@[result]);
}
////////////////////////////////////////////////////////////////////
#pragma mark - SendTransaction
//////////////////////////////////////////////////////////////////// sendTransaction
RCT_EXPORT_METHOD(sendTransaction:(NSString *)txArgsJSON
password:(NSString *)password
callback:(RCTResponseSenderBlock)callback) {
@@ -711,9 +676,9 @@ RCT_EXPORT_METHOD(sendTransaction:(NSString *)txArgsJSON
callback(@[result]);
}
////////////////////////////////////////////////////////////////////
#pragma mark - SignMessage
//////////////////////////////////////////////////////////////////// signMessage
RCT_EXPORT_METHOD(signMessage:(NSString *)message
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -723,9 +688,9 @@ RCT_EXPORT_METHOD(signMessage:(NSString *)message
callback(@[result]);
}
////////////////////////////////////////////////////////////////////
#pragma mark - Recover
//////////////////////////////////////////////////////////////////// recover
RCT_EXPORT_METHOD(recover:(NSString *)message
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -735,9 +700,9 @@ RCT_EXPORT_METHOD(recover:(NSString *)message
callback(@[result]);
}
////////////////////////////////////////////////////////////////////
#pragma mark - SignTypedData
//////////////////////////////////////////////////////////////////// signTypedData
RCT_EXPORT_METHOD(signTypedData:(NSString *)data
account:(NSString *)account
password:(NSString *)password
@@ -749,9 +714,9 @@ RCT_EXPORT_METHOD(signTypedData:(NSString *)data
callback(@[result]);
}
////////////////////////////////////////////////////////////////////
#pragma mark - SignTypedDataV4
//////////////////////////////////////////////////////////////////// signTypedDataV4
RCT_EXPORT_METHOD(signTypedDataV4:(NSString *)data
account:(NSString *)account
password:(NSString *)password
@@ -763,9 +728,9 @@ RCT_EXPORT_METHOD(signTypedDataV4:(NSString *)data
callback(@[result]);
}
////////////////////////////////////////////////////////////////////
#pragma mark - SignGroupMembership
//////////////////////////////////////////////////////////////////// signGroupMembership
RCT_EXPORT_METHOD(signGroupMembership:(NSString *)content
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -775,9 +740,9 @@ RCT_EXPORT_METHOD(signGroupMembership:(NSString *)content
callback(@[result]);
}
////////////////////////////////////////////////////////////////////
#pragma mark - ExtractGroupMembershipSignatures
//////////////////////////////////////////////////////////////////// extractGroupMembershipSignatures
RCT_EXPORT_METHOD(extractGroupMembershipSignatures:(NSString *)content
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -787,9 +752,9 @@ RCT_EXPORT_METHOD(extractGroupMembershipSignatures:(NSString *)content
callback(@[result]);
}
////////////////////////////////////////////////////////////////////
#pragma mark - GetNodeConfig
//////////////////////////////////////////////////////////////////// getNodeConfig
RCT_EXPORT_METHOD(getNodeConfig:(RCTResponseSenderBlock)callback) {
#if DEBUG
NSLog(@"GetNodeConfig() method called");
@@ -799,9 +764,9 @@ RCT_EXPORT_METHOD(getNodeConfig:(RCTResponseSenderBlock)callback) {
}
////////////////////////////////////////////////////////////////////
#pragma mark - only android methods
////////////////////////////////////////////////////////////////////
RCT_EXPORT_METHOD(setAdjustResize) {
#if DEBUG
NSLog(@"setAdjustResize() works only on Android");
@@ -992,10 +957,10 @@ RCT_EXPORT_METHOD(exportUnencryptedDatabase:(NSString *)accountData
#if DEBUG
NSLog(@"exportUnencryptedDatabase() method called");
#endif
NSString *filePath = [self getExportDbFilePath];
StatusgoExportUnencryptedDatabase(accountData, password, filePath);
callback(@[filePath]);
}
@@ -1030,7 +995,7 @@ RCT_EXPORT_METHOD(deactivateKeepAwake)
});
}
#pragma mark - deviceinfo
//// deviceinfo
- (bool) is24Hour
{
+1 -1
View File
@@ -18,7 +18,7 @@ in mkShell {
# build specific utilities
clojure maven watchman
# lint specific utilities
clj-kondo zprint
clj-kondo zprint clojure-lsp
# other nice to have stuff
yarn nodejs python27
] # and some special cases
-1
View File
@@ -89,7 +89,6 @@
"@testing-library/jest-native": "^5.0.0",
"@testing-library/react-native": "^11.2.0",
"@types/jest": "^28.1.6",
"concurrently": "^7.6.0",
"detox": "^19.9.1",
"jest": "^25.1.0",
"jest-circus": "^26.0.0",
Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 878 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 377 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 738 KiB

+10 -16
View File
@@ -19,7 +19,6 @@
;; routing
[bidi "2.1.6"]
;; test dependencies
[camel-snake-kebab "0.4.3"]
[day8.re-frame/test "0.1.5"]
[com.taoensso/tufte "2.1.0"]]
@@ -82,25 +81,20 @@
:output-dir "target/test"
:optimizations :simple
:target :node-test
;; When running tests without a REPL you can uncomment below line to `make test-watch` a specific file
;;:ns-regexp "status-im2.subs.subs-test$"
:main
status-im.test-runner/main
:main status-im.test-runner/main
;; set :ui-driven to true to let shadow-cljs inject node-repl
:ui-driven
true
:ui-driven true
:closure-defines
{status-im.utils.config/POKT_TOKEN #shadow/env "POKT_TOKEN"
status-im.utils.config/OPENSEA_API_KEY #shadow/env "OPENSEA_API_KEY"}
:compiler-options
{;; needed because we override require and it
;; messes with source-map which reports callstack
;; exceeded exceptions instead of real issues
:source-map false
;; needed because we use deref in tests
:static-fns false
:optimizations :simple
:infer-externs true}}
:compiler-options {;; needed because we override require and it
;; messes with source-map which reports callstack
;; exceeded exceptions instead of real issues
:source-map false
;; needed because we use deref in tests
:static-fns false
:optimizations :simple
:infer-externs true}}
;; mock.js-dependencies is mocking the react-native libraries
;; we build it as a node library so that it can be required by
+1 -1
View File
@@ -3,7 +3,7 @@
[clojure.string :as string]
[status-im.goog.i18n :as goog.i18n]))
(defn setup
(defn init
[default-device-language translations-by-locale]
(set! (.-fallbacks i18n) true)
(set! (.-defaultSeparator i18n) "/")
-15
View File
@@ -1,15 +0,0 @@
import {useDerivedValue, runOnJS as reaRunOnJS, runOnJS} from "react-native-reanimated";
export function useTranslateY(initialTranslationY, bottomSheetDy, panY) {
return useDerivedValue(() => {
return initialTranslationY - (bottomSheetDy.value - panY.value)
})
}
export function useBackgroundOpacity(translateY, backgroundHeight, windowHeight) {
return useDerivedValue(() => {
const opacity = ((translateY.value - windowHeight) / -backgroundHeight) * 0.5
return Math.max(Math.min(opacity, 0.5), 0)
})
}
-18
View File
@@ -1,18 +0,0 @@
import { useDerivedValue } from 'react-native-reanimated';
const MAX_SCALE = 1.8;
export function ringScale(scale, subtract) {
return useDerivedValue(
function () {
'worklet'
const value = scale.value;
const maxDelta = MAX_SCALE - 1;
const maxDeltaDiff = 1 - maxDelta;
const maxVirtualScale = MAX_SCALE + maxDelta;
const decimals = value - Math.floor(value);
const normalizedValue = value >= maxVirtualScale ? (decimals + ((parseInt(value) - 1) * maxDeltaDiff) + 1) : value;
return (((normalizedValue - subtract) > MAX_SCALE ? normalizedValue - maxDelta : normalizedValue) - subtract);
}
);
}
-11
View File
@@ -1,5 +1,3 @@
import { useDerivedValue, interpolate } from 'react-native-reanimated';
// Generic Worklets
export function applyAnimationsToStyle(animations, style) {
@@ -29,12 +27,3 @@ export function applyAnimationsToStyle(animations, style) {
return Object.assign(animatedStyle, style);
};
};
export function interpolateValue(sharedValue, inputRange, outputRange) {
return useDerivedValue(
function () {
'worklet'
return interpolate(sharedValue.value, inputRange, outputRange);
}
);
}
+3 -29
View File
@@ -4,22 +4,6 @@
(:require [status-im.utils.test :as utils.test])
(:require [status-im.chat.default-chats :refer (default-chats)]))
;; to generate a js Proxy at js/__STATUS_MOBILE_JS_IDENTITY_PROXY__ that accept any (.xxx) call and
;; return itself
;; For the convenience to mock eg.
;; (-> reanimated/slide-out-up-animation .springify (.damping 20) (.stiffness 300))
;; (-> reanimated/slide-out-up-animation (.damping 20) .springify (.stiffness 300))
(js/eval
"
var globalThis
if (typeof window === \"undefined\") {
globalThis = global
} else {
globalThis = window
}
globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return () => globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__}})
")
(def action-button #js {:default #js {:Item #js {}}})
(def config #js {:default #js {}})
(def camera #js {:RNCamera #js {:Constants #js {}}})
@@ -138,10 +122,8 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return (
(def react-native-share #js {:default {}})
(def react-native-svg
#js
{:SvgUri #js {:render identity}
:SvgXml #js {:render identity}
:default #js {:render identity}
:Path #js {:render identity}})
{:SvgUri #js {:render identity}
:SvgXml #js {:render identity}})
(def react-native-webview #js {:default {}})
(def react-native-audio-toolkit #js {:MediaStates {}})
(def net-info #js {})
@@ -254,10 +236,7 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return (
:withTiming (fn [])
:withDelay (fn [])
:Easing #js {:bezier identity}
:Keyframe (fn [])
:SlideOutUp js/__STATUS_MOBILE_JS_IDENTITY_PROXY__
:SlideInUp js/__STATUS_MOBILE_JS_IDENTITY_PROXY__
:LinearTransition js/__STATUS_MOBILE_JS_IDENTITY_PROXY__})
:Keyframe (fn [])})
(def react-native-gesture-handler
#js
{:default #js {}
@@ -342,9 +321,6 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return (
(def shell-worklets #js {})
(def bottom-sheet #js {})
(def record-audio-worklets #js {})
;; Update i18n_resources.cljs
(defn mock
[module]
@@ -393,8 +369,6 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return (
"react-native-svg" react-native-svg
"../src/js/worklet_factory.js" worklet-factory
"../src/js/shell_worklets.js" shell-worklets
"../src/js/bottom_sheet.js" bottom-sheet
"../src/js/record_audio_worklets.js" record-audio-worklets
"./fleets.js" default-fleets
"./chats.js" default-chats
"@walletconnect/client" wallet-connect-client
@@ -1,8 +1,13 @@
(ns quo2.components.avatars.account-avatar
(:require [quo2.components.icon :as icons]
[quo2.foundations.colors :as colors]
[quo2.theme :as theme]
[react-native.core :as rn]))
(def icon-color-value
{:dark (get-in colors/customization [:dark :purple])
:light (get-in colors/customization [:light :purple])})
(defn get-border-radius
[size]
(case size
@@ -22,11 +27,12 @@
20 11))
(defn account-avatar
[{:keys [size icon color]
:or {size 80
icon :i/placeholder
color :purple}}]
(let [icon-color (colors/custom-color-by-theme color 50 60)
[{:keys [size icon]
:or {size 80
icon :main-icons/placeholder}}]
(let [icon-color (if (theme/dark?)
(:dark icon-color-value)
(:light icon-color-value))
avatar-border-radius (get-border-radius size)
inner-icon-size (get-inner-icon-sizes size)]
[rn/view
@@ -24,9 +24,7 @@
:align-items :center
:justify-content :center
:border-radius (/ container-size 2)
;:background-color (colors/custom-color-by-theme color 50 60) ; TODO: this is temporary only.
;Issue: https://github.com/status-im/status-mobile/issues/14566
:background-color color}
:background-color (colors/custom-color-by-theme color 50 60)}
[icon/icon :i/group
{:size icon-size
:color colors/white-opa-70}]])))
+1 -1
View File
@@ -141,7 +141,7 @@
(if profile-picture
;; display image
[fast-image/fast-image
{:source profile-picture
{:source {:uri profile-picture}
:style (container-styling inner-dimensions outer-dimensions)}]
;; else display initials
[container inner-dimensions outer-dimensions
@@ -1,17 +0,0 @@
(ns quo2.components.banners.--tests--.banner-component-spec
(:require ["@testing-library/react-native" :as rtl]
[quo2.components.banners.banner.view :as banner]
[reagent.core :as reagent]))
(defn render-banner
[opts]
(rtl/render (reagent/as-element [banner/banner opts])))
(js/global.test "basic render of banner component"
(fn []
(render-banner {:pins-count "5"
:latest-pin-text "this message"})
(-> (js/expect (rtl/screen.getByText "this message"))
(.toBeTruthy))
(-> (js/expect (rtl/screen.getByText "5"))
(.toBeTruthy))))
@@ -1,19 +0,0 @@
(ns quo2.components.banners.banner.style
(:require [quo2.foundations.colors :as colors]))
(def container
{:width "100%"
:height 50
:background-color colors/primary-50-opa-20
:flex-direction :row
:align-items :center
:padding-horizontal 20
:padding-vertical 10})
(def counter
{:padding-right 22
:height 20
:width 20
:justify-content :center
:align-items :center})
@@ -1,24 +0,0 @@
(ns quo2.components.banners.banner.view
(:require [quo2.components.banners.banner.style :as style]
[quo2.components.counter.counter :as counter]
[quo2.components.icon :as icons]
[quo2.components.markdown.text :as text]
[react-native.core :as rn]))
(defn banner
[{:keys [show-pin? latest-pin-text pins-count on-press]}]
[rn/touchable-opacity
{:accessibility-label :pinned-banner
:style style/container
:active-opacity 1
:on-press on-press}
(when show-pin? [icons/icon :i/pin {:size 20}])
[text/text
{:number-of-lines 1
:size :paragraph-2
:style {:margin-left 10 :margin-right 50}}
latest-pin-text]
[rn/view
{:accessibility-label :pins-count
:style style/counter}
(when (pos? pins-count) [counter/counter {:type :secondary} pins-count])]])
@@ -1,28 +0,0 @@
(ns quo2.components.buttons.--tests--.buttons-component-spec
(:require ["@testing-library/react-native" :as rtl]
[quo2.components.buttons.button :as button]
[reagent.core :as reagent]))
(defn render-button
([options label]
(rtl/render (reagent/as-element [button/button options label]))))
(js/global.test "default render of button component"
(fn []
(render-button {:accessibility-label "test-button"} "")
(-> (js/expect (rtl/screen.getByLabelText "test-button"))
(.toBeTruthy))))
(js/global.test "button renders with a label"
(fn []
(render-button {} "test-label")
(-> (js/expect (rtl/screen.getByText "test-label"))
(.toBeTruthy))))
(js/global.test "button on-press works"
(let [event (js/jest.fn)]
(fn []
(render-button {:on-press event} "test-label")
(rtl/fireEvent.press (rtl/screen.getByText "test-label"))
(-> (js/expect event)
(.toHaveBeenCalledTimes 1)))))
+3 -4
View File
@@ -32,9 +32,9 @@
:outline {:icon-color colors/neutral-50
:icon-secondary-color colors/neutral-50
:label-color colors/neutral-100
:border-color {:default colors/neutral-30
:border-color {:default colors/neutral-20
:pressed colors/neutral-40
:disabled colors/neutral-30}}
:disabled colors/neutral-20}}
:ghost {:icon-color colors/neutral-50
:icon-secondary-color colors/neutral-50
:label-color colors/neutral-100
@@ -138,8 +138,7 @@
(defn shape-style-container
[type icon size]
{:height size
:border-radius (if (and icon (#{:primary :secondary :danger} type))
{:border-radius (if (and icon (#{:primary :secondary :danger} type))
24
(case size
56 12
@@ -31,7 +31,7 @@
:notification-up :i/arrow-up
:search-with-label :i/search
:search :i/search
:scroll-to-bottom :i/arrow-down)
:bottom :i/arrow-down)
{:size 12
:color (get-icon-and-text-color type)
:container-style {:margin-top 6
@@ -43,7 +43,7 @@
:notification-up 2
:search-with-label 8
:search 6
:scroll-to-bottom 6)
:bottom 6)
:margin-right (case type
:jump-to 8
:mention 2
@@ -51,12 +51,12 @@
:notification-up 8
:search-with-label 4
:search 6
:scroll-to-bottom 6)}}])
:bottom 6)}}])
(defn dynamic-button
"[dynamic-button opts]
opts
{:type :jump-to/:mention/:notification-down/:notification-up/:search/:search-with-label/:scroll-to-bottom
{:type :jump-to/:mention/:notification-down/:notification-up/:search/:search-with-label/:bottom
:on-press fn
:count mentions or notifications count
:customization-color customize jump-to and mention button color}"
@@ -77,7 +77,7 @@
:border-radius 12
:background-color (get-button-color type @pressed? (or customization-color :primary))}
style)}
(when (#{:mention :search :search-with-label :scroll-to-bottom} type)
(when (#{:mention :search :search-with-label :bottom} type)
[icon-view type])
(when (#{:jump-to :mention :notification-down :notification-up :search-with-label} type)
[text/text
@@ -1,15 +1,12 @@
(ns quo2.components.community.community-card-view
(:require [quo2.components.community.community-view :as community-view]
[quo2.components.community.icon :as icon]
[quo2.components.community.style :as style]
[react-native.core :as rn]))
(defn community-card-view-item
[{:keys [name description locked images
[{:keys [name description locked
status tokens cover tags width]} on-press]
[rn/touchable-opacity
{:accessibility-label :community-card-item
:on-press on-press}
[rn/touchable-opacity {:on-press on-press}
[rn/view {:style (style/community-card 20)}
[rn/view
{:style {:width width
@@ -25,9 +22,9 @@
{:flex 1
:border-radius 20}}]]
[rn/view (style/card-view-content-container 12)
[rn/view (style/card-view-chat-icon 48)
[icon/community-icon {:images images} 48]]
[rn/view (style/card-view-chat-icon 48)]
;;TODO new pure component based on quo2 should be implemented without status-im usage
;[communities.icon/community-icon-redesign community 48]]
(when (= status :gated)
[rn/view (style/permission-tag-styles)
[community-view/permission-tag-container
-13
View File
@@ -1,13 +0,0 @@
(ns quo2.components.community.icon
(:require [react-native.core :as rn]))
(defn community-icon
[{:keys [images]} size]
(let [thumbnail-image (get-in images [:thumbnail :uri])]
[rn/image
{:source {:uri thumbnail-image}
:style {:border-radius 50
:border-width 0
:border-color :transparent
:width size
:height size}}]))
@@ -7,7 +7,7 @@
[quo2.components.markdown.text :as text]
[quo2.components.tags.token-tag :as token-tag]
[quo2.foundations.colors :as colors]
[i18n.i18n :as i18n]
[status-im.i18n.i18n :as i18n]
[status-im.ui.components.fast-image :as fast-image]))
(def ^:private token-tag-horizontal-spacing 7)
@@ -10,25 +10,25 @@
(rtl/render (reagent/as-element [counter/counter opts value]))))
(js/global.test "default render of counter component"
(fn []
(render-counter)
(-> (js/expect (rtl/screen.getByTestId "counter-component"))
(.toBeTruthy))))
(fn []
(render-counter)
(-> (js/expect (rtl/screen.getByTestId "counter-component"))
(.toBeTruthy))))
(js/global.test "renders counter with a string value"
(fn []
(render-counter {} "1")
(-> (js/expect (rtl/screen.getByText "1"))
(.toBeTruthy))))
(fn []
(render-counter {} "1")
(-> (js/expect (rtl/screen.getByText "1"))
(.toBeTruthy))))
(js/global.test "renders counter with an integer value"
(fn []
(render-counter {} 1)
(-> (js/expect (rtl/screen.getByText "1"))
(.toBeTruthy))))
(fn []
(render-counter {} 1)
(-> (js/expect (rtl/screen.getByText "1"))
(.toBeTruthy))))
(js/global.test "renders counter with value 99+ when the value is greater than 99"
(fn []
(render-counter {} "100")
(-> (js/expect (rtl/screen.getByText "99+"))
(.toBeTruthy))))
(fn []
(render-counter {} "100")
(-> (js/expect (rtl/screen.getByText "99+"))
(.toBeTruthy))))
@@ -1,45 +0,0 @@
(ns quo2.components.dividers.--tests--.divider-label-component-spec
(:require ["@testing-library/react-native" :as rtl]
[quo2.components.dividers.divider-label :as divider-label]
[reagent.core :as reagent]))
(defn render-divider-label
([]
(render-divider-label {}))
([opts]
(rtl/render (reagent/as-element [divider-label/divider-label opts]))))
(js/global.test "default render of divider-label component"
(fn []
(render-divider-label)
(-> (js/expect (rtl/screen.getByLabelText "divider-label"))
(.toBeTruthy))))
(js/global.test "render divider-label component with a label"
(fn []
(render-divider-label {:label :hello})
(-> (js/expect (rtl/screen.getByText "hello"))
(.toBeTruthy))))
(js/global.test "render divider-label component with a counter-value"
(fn []
(render-divider-label {:label :hello
:counter-value "1"})
(-> (js/expect (rtl/screen.getByText "1"))
(.toBeTruthy))))
(js/global.test "divider-label chevron icon renders to the left when set"
(fn []
(render-divider-label {:label :hello
:counter-value "1"
:chevron-position :left})
(-> (js/expect (rtl/screen.getByTestId "divider-label-icon-left"))
(.toBeTruthy))))
(js/global.test "divider-label chevron icon renders to the right when set"
(fn []
(render-divider-label {:label :hello
:counter-value "1"
:chevron-position :right})
(-> (js/expect (rtl/screen.getByTestId "divider-label-icon-right"))
(.toBeTruthy))))
+3 -3
View File
@@ -7,9 +7,9 @@
(defn date
[value]
[rn/view
{:margin-vertical 8
:padding-right 20
:padding-left 60}
{:margin-vertical 16
:padding-right 8
:padding-left 62}
[text/text
{:weight :medium
:accessibility-label :divider-date-text
+11 -16
View File
@@ -23,18 +23,14 @@
text-and-icon-color (if dark? colors/neutral-40 colors/neutral-50)
counter-text-color (if dark? colors/white colors/neutral-100)]
[rn/view
{:accessible true
:accessibility-label :divider-label
:style {:border-top-width 1
:border-top-color border-and-counter-bg-color
:padding-top padding-top
:padding-horizontal 16
:align-items :center
:flex-direction :row}}
{:style {:border-top-width 1
:border-top-color border-and-counter-bg-color
:padding-top padding-top
:padding-horizontal 16
:align-items :center
:flex-direction :row}}
(when (= chevron-position :left)
[rn/view
{:test-ID :divider-label-icon-left
:style {:margin-right 4}}
[rn/view {:style {:margin-right 4}}
[icons/icon
:main-icons/chevron-down
{:color text-and-icon-color
@@ -47,11 +43,10 @@
:flex 1}}
label]
(when (= chevron-position :right)
[rn/view {:test-ID :divider-label-icon-right}
[icons/icon
:main-icons/chevron-down
{:color text-and-icon-color
:size chevron-icon-container-width}]])
[icons/icon
:main-icons/chevron-down
{:color text-and-icon-color
:size chevron-icon-container-width}])
(when (pos? counter-value)
[rn/view
{:style {:border-radius 6
@@ -8,46 +8,56 @@
(rtl/render (reagent/as-element [action-drawer/action-drawer options]))))
(js/global.test "action-drawer renders with elements label displaying"
(fn []
(render-action-drawer [[{:icon :i/friend
:label "a sample label"}]])
(-> (js/expect (rtl/screen.getByText "a sample label"))
(.toBeTruthy))))
(fn []
(render-action-drawer [[{:icon :i/friend
:label "a sample label"}]])
(-> (js/expect (rtl/screen.getByText "a sample label"))
(.toBeTruthy))))
(js/global.test "action-drawer renders with elements sub-label displaying"
(fn []
(render-action-drawer [[{:icon :i/friend
:label "a sample label"
:sub-label "a sample sub label"}]])
(-> (js/expect (rtl/screen.getByText "a sample sub label"))
(.toBeTruthy))))
(fn []
(render-action-drawer [[{:icon :i/friend
:label "a sample label"
:sub-label "a sample sub label"}]])
(-> (js/expect (rtl/screen.getByText "a sample sub label"))
(.toBeTruthy))))
(js/global.test "action-drawer on click action works on element"
(let [event (js/jest.fn)]
(fn []
(render-action-drawer [[{:icon :i/friend
:label "a sample label"
:on-press event}]])
(rtl/fireEvent.press (rtl/screen.getByText "a sample label"))
(-> (js/expect event)
(.toHaveBeenCalled)))))
(let [event (js/jest.fn)]
(fn []
(render-action-drawer [[{:icon :i/friend
:label "a sample label"
:on-press event}]])
(rtl/fireEvent.press (rtl/screen.getByText "a sample label"))
(-> (js/expect event)
(.toHaveBeenCalled)))))
(js/global.test "action-drawer renders two icons when set"
(fn []
(render-action-drawer [[{:icon :i/friend
:label "a sample label"
:right-icon :i/friend
:accessibility-label :first-element}]])
(-> (js/expect (rtl/screen.getByLabelText "right-icon-for-action"))
(.toBeTruthy))
(-> (js/expect (rtl/screen.queryByLabelText "left-icon-for-action"))
(.toBeTruthy))))
(fn []
(render-action-drawer [[{:icon :i/friend
:label "a sample label"
:right-icon :i/friend
:accessibility-label :first-element}]])
(-> (js/expect (rtl/screen.getByLabelText "right-icon-for-action"))
(.toBeTruthy))
(-> (js/expect (rtl/screen.queryByLabelText "left-icon-for-action"))
(.toBeTruthy))))
(js/global.test "action-drawer does not render a divider when the add-divider? prop is false"
(fn []
(render-action-drawer [[{:icon :i/friend
:label "a sample label"
:add-divider? false
:accessibility-label :first-element}]])
(-> (js/expect (rtl/screen.getAllByLabelText "divider"))
(.not)
(.toBeTruthy))))
(js/global.test "action-drawer renders a divider when the add-divider? prop is true"
(fn []
(render-action-drawer [[{:icon :i/friend
:label "a sample label"
:add-divider? true
:accessibility-label :first-element}]])
(-> (js/expect (rtl/screen.getAllByLabelText "divider"))
(.toBeTruthy))))
(fn []
(render-action-drawer [[{:icon :i/friend
:label "a sample label"
:add-divider? true
:accessibility-label :first-element}]])
(-> (js/expect (rtl/screen.getAllByLabelText "divider"))
(.toBeTruthy))))
+1 -2
View File
@@ -41,5 +41,4 @@
(get-icons 16)
(get-icons 20)
(get-icons 24)
(get-icons 32)
(get-icons 120)))
(get-icons 32)))
+13 -24
View File
@@ -15,24 +15,22 @@
:text-color (theme-colors colors/danger-50 colors/danger-60)}))
(defn menu-item
[{:keys [type title accessibility-label icon on-press style-props subtitle subtitle-color]
[{:keys [type title accessibility-label icon on-press]
:or {type :main}}]
(let [{:keys [icon-color text-color background]} (themes type)]
[rn/touchable-opacity
(merge {:accessibility-label accessibility-label
:style (merge style-props
{:background-color background
:height 48
:flex-direction :row
:align-items :center})}
:style {:background-color background
:height 48
:flex-direction :row
:align-items :center}}
(when on-press
{:on-press on-press}))
[rn/view
{:style {:flex-direction :row
:flex-grow 0
:flex-shrink 1
:padding-horizontal 20
:align-items :center}}
:padding-horizontal 20}}
[rn/view
{:style {:width 20
:height 20
@@ -40,19 +38,10 @@
:justify-content :center
:margin-right 12}}
[icons/icon icon {:color icon-color}]]
[rn/view
[text/text
{:weight :medium
:style {:color text-color}
:ellipsize-mode :tail
:number-of-lines 1
:size :paragraph-1}
title]
(when subtitle
[text/text
{:weight :medium
:style {:color subtitle-color}
:ellipsize-mode :tail
:number-of-lines 1
:size :paragraph-1}
subtitle])]]]))
[text/text
{:weight :medium
:style {:color text-color}
:ellipsize-mode :tail
:number-of-lines 1
:size :paragraph-1}
title]]]))
@@ -1,14 +0,0 @@
(ns quo2.components.markdown.--tests--.text-component-spec
(:require ["@testing-library/react-native" :as rtl]
[quo2.components.markdown.text :as text]
[reagent.core :as reagent]))
(defn render-text
([options value]
(rtl/render (reagent/as-element [text/text options value]))))
(js/global.test "text renders with text"
(fn []
(render-text {} "hello")
(-> (js/expect (rtl/screen.getByText "hello"))
(.toBeTruthy))))
@@ -4,6 +4,7 @@
(def container
{:flex 1
:flex-wrap :wrap
:height 18
:flex-direction :row
:align-items :center})
@@ -39,4 +40,4 @@
(defn time-text
[ens?]
{:color colors/neutral-50
:margin-left (if ens? 8 4)})
:margin-left (if ens? 8 4)})
@@ -93,8 +93,7 @@
:style style/middle-dot-chat-key}
middle-dot])
[text/text
{:monospace true
:size :paragraph-2
:accessibility-label :message-timestamp
:style (style/time-text ens?)}
{:monospace true
:size :paragraph-2
:style (style/time-text ens?)}
time-str]]))])
@@ -50,4 +50,4 @@
[dynamic-button-view :mention dynamic-buttons {:margin-left 8}]
[dynamic-button-view :notification-down dynamic-buttons {:margin-left 8}]
[dynamic-button-view :notification-up dynamic-buttons {:margin-left 8}]
[dynamic-button-view :scroll-to-bottom dynamic-buttons {:margin-left 8}]]]]))]))
[dynamic-button-view :bottom dynamic-buttons {:margin-left 8}]]]]))]))
+3 -2
View File
@@ -146,11 +146,12 @@
[rn/view
{:style (assoc centrify-style
:flex-direction :row
:flex 1
:justify-content :flex-end)}
(let [last-icon-index (-> right-section-buttons count dec)]
(map-indexed (fn [index {:keys [icon on-press type style] :or {type :grey}}]
(map-indexed (fn [index {:keys [icon on-press type] :or {type :grey}}]
^{:key index}
[rn/view {:style (merge {:margin-right (if (not= index last-icon-index) 8 0)} style)}
[rn/view {:style {:margin-right (if (not= index last-icon-index) 8 0)}}
[button/button {:on-press on-press :icon true :type type :size 32}
icon]])
right-section-buttons))])
@@ -9,7 +9,7 @@
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
[reagent.core :as reagent]
[i18n.i18n :as i18n]))
[status-im.i18n.i18n :as i18n]))
(def ^:private max-reply-length
280)
@@ -81,21 +81,19 @@
context))))
(defn- activity-message
[{:keys [title body title-number-of-lines body-number-of-lines]}]
[{:keys [title body]}]
[rn/view {:style style/message-container}
(when title
[text/text
{:size :paragraph-2
:accessibility-label :activity-message-title
:style style/message-title
:number-of-lines title-number-of-lines}
:style style/message-title}
title])
(if (string? body)
[text/text
{:style style/message-body
:accessibility-label :activity-message-body
:size :paragraph-1
:number-of-lines body-number-of-lines}
:size :paragraph-1}
body]
body)])
@@ -1,105 +0,0 @@
(ns quo2.components.notifications.count-down-circle
(:require [goog.string :as gstring]
[quo2.foundations.colors :as colors]
[quo2.theme :as theme]
[react-native.core :as rn]
[react-native.svg :as svg]
[reagent.core :as reagent]))
(defn- get-path-props
[size stroke-width rotation]
(let [half-size (/ size 2)
half-stroke-width (/ stroke-width 2)
arc-radius (- half-size half-stroke-width)
arc-diameter (* arc-radius 2)
rotation-indicator (if (= rotation :clockwise) "1,0" "0,1")
path-length (* js/Math.PI arc-diameter)
path (gstring/format
"m %s,%s a %s,%s 0 %s 0,%s a %s,%s 0 %s 0 ,-%s"
half-size
half-stroke-width
arc-radius
arc-radius
rotation-indicator
arc-diameter
arc-radius
arc-radius
rotation-indicator
arc-diameter)]
{:path path
:path-length path-length}))
(defn- linear-ease
[time start goal duration]
(if (zero? duration)
start
(-> time
(/ duration)
(* goal)
(+ start))))
(defn- get-start-at
[duration initial-remaining-time]
(cond (or (zero? duration) (= duration initial-remaining-time)) 0
(number? initial-remaining-time) (- duration initial-remaining-time)
:else 0))
(def ^:private themes
{:color {:light colors/neutral-80-opa-40
:dark colors/white-opa-40}})
(defn circle-timer
[{:keys [color duration size stroke-width trail-color rotation initial-remaining-time]}]
(let [rotation (or rotation :clockwise)
duration (or duration 4)
stroke-width (or stroke-width 1)
size (or size 9)
max-stroke-width (max stroke-width 0)
{:keys [path path-length]} (get-path-props size max-stroke-width rotation)
start-at (get-start-at duration initial-remaining-time)
elapsed-time (reagent/atom 0)
prev-frame-time (reagent/atom nil)
frame-request (reagent/atom nil)
display-time (reagent/atom start-at)
;; get elapsed frame time
swap-elapsed-time-each-frame (fn swap-elapsed-time-each-frame [frame-time]
(if (nil? @prev-frame-time)
(do (reset! prev-frame-time frame-time)
(reset! frame-request (js/requestAnimationFrame
swap-elapsed-time-each-frame)))
(let [delta (- (/ frame-time 1000)
(/ @prev-frame-time 1000))
current-elapsed (swap! elapsed-time + delta)
current-display-time (+ start-at current-elapsed)
completed? (>= current-display-time duration)]
(reset! display-time (if completed?
duration
current-display-time))
(when-not completed?
(reset! prev-frame-time frame-time)
(reset! frame-request (js/requestAnimationFrame
swap-elapsed-time-each-frame))))))]
(reagent/create-class
{:component-will-unmount #(js/cancelAnimationFrame @frame-request)
:reagent-render
(fn []
(reset! frame-request (js/requestAnimationFrame swap-elapsed-time-each-frame))
[rn/view
{:style {:position :relative
:width size
:height size}}
[svg/svg
{:view-box (str "0 0 " size " " size)
:width size
:height size}
[svg/path
{:d path :fill :none :stroke (or trail-color :transparent) :stroke-width stroke-width}]
(when-not (= @display-time duration)
[svg/path
{:d path
:fill :none
:stroke (or color (get-in themes [:color (theme/get-theme)]))
:stroke-linecap :square
:stroke-width stroke-width
:stroke-dasharray path-length
:stroke-dashoffset (linear-ease @display-time 0 path-length duration)}])]])})))
@@ -4,23 +4,18 @@
[react-native.core :as rn]))
(defn info-count
([count]
(info-count {} count))
([props count]
(when (> count 0)
[rn/view
(merge props
{:style (merge {:width 16
:height 16
:position :absolute
:right 22
:border-radius 6
:justify-content :center
:align-items :center
:background-color (colors/theme-colors colors/primary-50 colors/primary-60)}
(:style props))})
[rn/text
{:style (merge typography/font-medium
typography/label
{:color colors/white :text-align :center})}
count]])))
[count style]
(when (> count 0)
[rn/view
{:style (merge {:width 16
:height 16
:position :absolute
:right 22
:border-radius 6
:justify-content :center
:align-items :center
:background-color (colors/theme-colors colors/primary-50 colors/primary-60)}
style)}
[rn/text
{:style (merge typography/font-medium typography/label {:color colors/white :text-align :center})}
count]]))
@@ -1,81 +0,0 @@
(ns quo2.components.notifications.toast
(:require [i18n.i18n :as i18n]
[quo2.components.icon :as icon]
[quo2.components.markdown.text :as text]
[quo2.components.notifications.count-down-circle :as count-down-circle]
[quo2.foundations.colors :as colors]
[quo2.theme :as theme]
[react-native.core :as rn]))
(def ^:private themes
{:container {:light {:background-color colors/white-opa-70}
:dark {:background-color colors/neutral-80-opa-70}}
:text {:light {:color colors/neutral-100}
:dark {:color colors/white}}
:icon {:light {:color colors/neutral-100}
:dark {:color colors/white}}
:action-container {:light {:background-color :colors/neutral-80-opa-5}
:dark {:background-color :colors/white-opa-5}}})
(defn- merge-theme-style
[component-key styles]
(merge (get-in themes [component-key (theme/get-theme)]) styles))
(defn toast-action-container
[{:keys [on-press style]} & children]
[rn/touchable-highlight {:on-press on-press}
[into
[rn/view
{:style (merge
{:flex-direction :row
:padding-vertical 3
:padding-horizontal 8
:align-items :center
:border-radius 8
:background-color (get-in themes
[:action-container (theme/get-theme)
:background-color])}
style)}]
children]])
(defn toast-undo-action
[duration on-press]
[toast-action-container {:on-press on-press}
[rn/view {:style {:margin-right 5}}
[count-down-circle/circle-timer {:duration duration}]]
[text/text
{:size :paragraph-2 :weight :medium :style (merge-theme-style :text {})}
[i18n/label :t/undo]]])
(defn- toast-container
[{:keys [left middle right]}]
[rn/view {:style {:padding-left 12 :padding-right 12}}
[rn/view
{:style (merge-theme-style :container
{:flex-direction :row
:width "100%"
:margin :auto
:justify-content :space-between
:padding-vertical 8
:padding-left 10
:padding-right 8
:border-radius 12})}
[rn/view {:style {:padding 2}} left]
[rn/view {:style {:padding 4 :flex 1}}
[text/text
{:size :paragraph-2 :weight :medium :style (merge-theme-style :text {})}
middle]]
(when right right)]])
(defn toast
[{:keys [icon icon-color text action undo-duration undo-on-press]}]
[toast-container
{:left (when icon
[icon/icon icon
{:container-style {:width 20 :height 20}
:color (or icon-color
(get-in themes [:icon (theme/get-theme) :color]))}])
:middle text
:right (if undo-duration
[toast-undo-action undo-duration undo-on-press]
action)}])
@@ -1,40 +0,0 @@
(ns quo2.components.profile.profile-card.style
(:require [quo2.foundations.colors :as colors]))
(defn card-container
[customization-color]
{:flex-direction :column
:padding 12
:flex 1
:border-radius 16
:background-color (colors/custom-color customization-color 50 40)})
(def card-header
{:flex-direction :row
:justify-content :space-between})
(def name-container
{:flex-direction :row
:margin-top 8
:margin-bottom 2
:align-items :center
:padding-right 12})
(def user-name
{:margin-right 4
:color colors/white})
(def emoji-hash
{:margin-top 10})
(def user-hash
{:color colors/white-opa-60})
(def sign-button
{:margin-top 14})
(def keycard-icon
{:color colors/white-opa-40})
(def option-button
{:background-color colors/white-opa-5})
@@ -1,64 +0,0 @@
(ns quo2.components.profile.profile-card.view
(:require
[quo2.components.profile.profile-card.style :as style]
[quo2.foundations.colors :as colors]
[quo2.components.avatars.user-avatar :as user-avatar]
[quo2.components.icon :as icon]
[quo2.components.markdown.text :as text]
[quo2.components.buttons.button :as button]
[react-native.core :as rn]))
(defn profile-card
[{:keys [show-sign-profile? key-card? profile-picture name hash customization-color sign-label
emoji-hash on-press-dots on-press-sign show-emoji-hash?]
:or {show-sign-profile? false
show-emoji-hash? false
customization-color :turquoise
key-card? false}}]
[rn/view
{:style (style/card-container customization-color)}
[rn/view
{:style style/card-header}
[user-avatar/user-avatar
{:full-name name
:profile-picture profile-picture
:override-theme :dark
:size :medium
:status-indicator? false
:ring? true}]
[button/button
{:size 32
:type :blur-bg
:icon true
:override-theme :dark
:style style/option-button
:on-press on-press-dots}
:i/options]]
[rn/view
{:style style/name-container}
[text/text
{:size :heading-2
:weight :semi-bold
:number-of-lines 1
:style style/user-name} name]
(when key-card?
(icon/icon
:i/keycard
style/keycard-icon))]
[text/text
{:weight :monospace
:number-of-lines 1
:style style/user-hash} hash]
(when (and show-emoji-hash? emoji-hash)
[text/text
{:weight :monospace
:number-of-lines 1
:style style/emoji-hash} emoji-hash])
(when show-sign-profile?
[button/button
{:on-press on-press-sign
:type :community
:community-color (colors/custom-color customization-color 60)
:community-text-color colors/white
:style style/sign-button} sign-label])])
+44 -9
View File
@@ -1,18 +1,31 @@
(ns quo2.components.reactions.reaction
(:require [quo2.components.icon :as icons]
[quo2.components.markdown.text :as text]
[quo2.components.reactions.style :as style]
[quo2.components.markdown.text :as quo2.text]
[quo2.foundations.colors :as colors]
[quo2.theme :as theme]
[react-native.core :as rn]))
(defn add-reaction
(def reaction-styling
{:flex-direction :row
:justify-content :center
:align-items :center
:padding-horizontal 8
:border-radius 8
:height 24})
(defn open-reactions-menu
[{:keys [on-press]}]
(let [dark? (theme/dark?)]
[rn/touchable-opacity
{:on-press on-press
:accessibility-label :emoji-reaction-add
:style (style/add-reaction)}
:style (merge reaction-styling
{:padding-horizontal 9
:border-width 1
:margin-top 5
:border-color (if dark?
colors/neutral-70
colors/neutral-30)})}
[icons/icon :i/add
{:size 20
:color (if dark?
@@ -22,19 +35,41 @@
(defn reaction
"Add your emoji as a param here"
[{:keys [emoji clicks neutral? on-press accessibility-label]}]
(let [numeric-value (int clicks)]
(let [dark? (theme/dark?)
text-color (if dark?
colors/white
colors/neutral-100)
numeric-value (int clicks)
clicks-positive? (pos? numeric-value)]
[rn/touchable-opacity
{:on-press on-press
:accessibility-label accessibility-label
:style (style/reaction neutral?)}
:style (merge reaction-styling
(cond->
{:background-color
(if dark?
(if neutral?
colors/neutral-70
:transparent)
(if neutral?
colors/neutral-30
:transparent))}
(and dark? (not neutral?))
(assoc :border-color colors/neutral-70
:border-width 1)
(and (not dark?) (not neutral?))
(assoc :border-color colors/neutral-30
:border-width 1)))}
[icons/icon emoji
{:no-color true
:size 16}]
[text/text
[quo2.text/text
{:size :paragraph-2
:weight :semi-bold
:color text-color
:flex-direction :row
:align-items :center
:justify-content :center}
(when (pos? numeric-value)
(str " " numeric-value))]]))
(if clicks-positive?
(str " " numeric-value)
"")]]))
-34
View File
@@ -1,34 +0,0 @@
(ns quo2.components.reactions.style
(:require [quo2.foundations.colors :as colors]))
(def reaction-styling
{:flex-direction :row
:justify-content :center
:align-items :center
:padding-horizontal 8
:border-radius 8
:height 24})
(defn add-reaction
[]
(merge reaction-styling
{:padding-horizontal 9
:border-width 1
:border-color (colors/theme-colors colors/neutral-30 colors/neutral-70)}))
(defn reaction
[neutral?]
(merge reaction-styling
(cond->
{:background-color (colors/theme-colors (if neutral?
colors/neutral-30
:transparent)
(if neutral?
colors/neutral-70
:transparent))}
(and (colors/dark?) (not neutral?))
(assoc :border-color colors/neutral-70
:border-width 1)
(and (not (colors/dark?)) (not neutral?))
(assoc :border-color colors/neutral-30
:border-width 1))))
@@ -1,228 +0,0 @@
(ns quo2.components.record-audio.record-audio.style
(:require [quo2.foundations.colors :as colors]
[react-native.reanimated :as reanimated]))
(defn animated-circle
[scale opacity color]
(reanimated/apply-animations-to-style
{:transform [{:scale scale}]
:opacity opacity}
{:width 56
:height 56
:border-width 1
:border-color color
:border-radius 28
:position :absolute
:justify-content :center
:align-items :center
:z-index 0}))
(defn record-button-big-container
[translate-x translate-y opacity]
(reanimated/apply-animations-to-style
{:transform [{:translateY translate-y}
{:translateX translate-x}]
:opacity opacity}
{:position :absolute
:bottom 0
:right 0
:width 96
:height 96
:align-items :center
:justify-content :center
:z-index 0}))
(defn record-button-big-body
[button-color]
{:width 56
:height 56
:border-radius 28
:justify-content :center
:align-items :center
:background-color button-color
:overflow :hidden})
(defn record-button-big-red-overlay
[red-overlay-opacity]
(reanimated/apply-animations-to-style
{:opacity red-overlay-opacity}
{:position :absolute
:top 0
:left 0
:right 0
:bottom 0
:background-color colors/danger-50}))
(defn record-button-big-gray-overlay
[gray-overlay-opacity]
(reanimated/apply-animations-to-style
{:opacity gray-overlay-opacity}
{:position :absolute
:top 0
:left 0
:right 0
:bottom 0
:background-color (colors/theme-colors colors/neutral-80-opa-5-opaque colors/neutral-80)}))
(defn record-button-big-icon-container
[icon-opacity]
(reanimated/apply-animations-to-style
{:opacity icon-opacity}
{}))
(def stop-icon
{:width 13
:height 13
:border-radius 4
:background-color colors/white})
(defn send-button-container
[opacity]
(reanimated/apply-animations-to-style
{:opacity opacity}
{:justify-content :center
:align-items :center
:position :absolute
:width 56
:height 56
:top 0
:right 20}))
(defn send-button-connector
[opacity width height border-radius-first-half border-radius-second-half]
(reanimated/apply-animations-to-style
{:opacity opacity
:width width
:height height
:border-bottom-left-radius border-radius-second-half
:border-top-left-radius border-radius-first-half
:border-top-right-radius border-radius-first-half
:border-bottom-right-radius border-radius-second-half}
{:justify-content :center
:align-items :center
:align-self :center
:background-color colors/primary-50
:z-index 0}))
(defn send-button
[translate-y opacity]
(reanimated/apply-animations-to-style
{:transform [{:translateY translate-y}]
:opacity opacity}
{:justify-content :center
:align-items :center
:background-color colors/primary-50
:width 32
:height 32
:border-radius 16
:position :absolute
:top 0
:right 32
:z-index 10}))
(def send-icon-container
{:z-index 10})
(defn lock-button-container
[opacity]
(reanimated/apply-animations-to-style
{:opacity opacity}
{:transform [{:rotate "45deg"}]
:justify-content :center
:align-items :center
:position :absolute
:width 56
:height 56
:top 20
:left 20}))
(defn lock-button-connector
[opacity width height border-radius-first-half border-radius-second-half]
(reanimated/apply-animations-to-style
{:opacity opacity
:width width
:height height
:border-bottom-left-radius border-radius-first-half
:border-top-left-radius border-radius-first-half
:border-top-right-radius border-radius-second-half
:border-bottom-right-radius border-radius-second-half}
{:justify-content :center
:align-items :center
:align-self :center
:background-color (colors/theme-colors colors/neutral-80-opa-5-opaque colors/neutral-80)
:overflow :hidden}))
(defn lock-button
[translate-x-y opacity]
(reanimated/apply-animations-to-style
{:transform [{:translateX translate-x-y}
{:translateY translate-x-y}]
:opacity opacity}
{:width 32
:height 32
:justify-content :center
:align-items :center
:background-color (colors/theme-colors colors/neutral-80-opa-5-opaque colors/neutral-80)
:border-radius 16
:position :absolute
:top 24
:left 24
:overflow :hidden
:z-index 12}))
(defn delete-button-container
[opacity]
(reanimated/apply-animations-to-style
{:opacity opacity}
{:justify-content :center
:align-items :center
:position :absolute
:width 56
:height 56
:bottom 20
:left 0}))
(defn delete-button-connector
[opacity width height border-radius-first-half border-radius-second-half]
(reanimated/apply-animations-to-style
{:opacity opacity
:width width
:height height
:border-bottom-left-radius border-radius-first-half
:border-top-left-radius border-radius-first-half
:border-top-right-radius border-radius-second-half
:border-bottom-right-radius border-radius-second-half}
{:justify-content :center
:align-items :center
:align-self :center
:background-color colors/danger-50
:z-index 0}))
(defn delete-button
[translate-x opacity]
(reanimated/apply-animations-to-style
{:transform [{:translateX translate-x}]
:opacity opacity}
{:width 32
:height 32
:justify-content :center
:align-items :center
:background-color colors/danger-50
:border-radius 16
:position :absolute
:top 76
:left 0
:z-index 11}))
(defn record-button-container
[opacity]
(reanimated/apply-animations-to-style
{:opacity opacity}
{:margin-bottom 32
:margin-right 32}))
(def input-container
{:width 140
:height 140
:align-items :flex-end
:justify-content :flex-end})
@@ -1,767 +0,0 @@
(ns quo2.components.record-audio.record-audio.view
(:require [cljs-bean.core :as bean]
[oops.core :as oops]
[quo.react :refer [effect! memo]]
[quo2.components.buttons.button :as button]
[quo2.components.icon :as icons]
[quo2.components.record-audio.record-audio.style :as style]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
[react-native.reanimated :as reanimated]
[reagent.core :as reagent]
[status-im.utils.utils :as utils]))
(def ^:private scale-to-each 1.8)
(def ^:private scale-to-total 2.6)
(def ^:private scale-padding 0.16)
(def ^:private opacity-from-lock 1)
(def ^:private opacity-from-default 0.5)
(def ^:private signal-anim-duration 3900)
(def ^:private signal-anim-duration-2 1950)
(def ^:private animated-ring
(reagent/adapt-react-class
(memo
(fn [props]
(let [{:keys [scale opacity color]} (bean/bean props)]
(reagent/as-element
[reanimated/view {:style (style/animated-circle scale opacity color)}]))))))
(def ^:private record-button-area-big
{:width 56
:height 56
:x 64
:y 64})
(def ^:private record-button-area
{:width 48
:height 48
:x 68
:y 68})
(defn- delete-button-area
[active?]
{:width (if active? 72 82)
:height 56
:x (if active? -16 -32)
:y (if active? 64 70)})
(defn- lock-button-area
[active?]
{:width (if active? 72 100)
:height (if active? 72 102)
:x -32
:y -32})
(defn- send-button-area
[active?]
{:width 56
:height (if active? 72 92)
:x 68
:y (if active? -16 -32)})
(defn- touch-inside-area?
[{:keys [location-x location-y ignore-min-y? ignore-max-y? ignore-min-x? ignore-max-x?]}
{:keys [width height x y]}]
(let [max-x (+ x width)
max-y (+ y height)]
(and
(and
(or ignore-min-x? (>= location-x x))
(or ignore-max-x? (<= location-x max-x)))
(and
(or ignore-min-y? (>= location-y y))
(or ignore-max-y? (<= location-y max-y))))))
(def record-audio-worklets (js/require "../src/js/record_audio_worklets.js"))
(defn- ring-scale
[scale substract]
(.ringScale ^js record-audio-worklets
scale
substract))
(defn- record-button-big
[recording? ready-to-send? ready-to-lock? ready-to-delete? record-button-is-animating?
record-button-at-initial-position? locked? reviewing-audio? recording-timer recording-length-ms
clear-timeout touch-active?]
[:f>
(fn []
(let [scale (reanimated/use-shared-value 1)
opacity (reanimated/use-shared-value 0)
opacity-from (if @ready-to-lock? opacity-from-lock opacity-from-default)
animations (map
(fn [index]
(let [ring-scale (ring-scale scale (* scale-padding index))]
{:scale ring-scale
:opacity (reanimated/interpolate ring-scale
[1 scale-to-each]
[opacity-from 0])}))
(range 0 5))
rings-color (cond
@ready-to-lock? (colors/theme-colors colors/neutral-80-opa-5-opaque
colors/neutral-80)
@ready-to-delete? colors/danger-50
:else colors/primary-50)
translate-y (reanimated/use-shared-value 0)
translate-x (reanimated/use-shared-value 0)
button-color colors/primary-50
icon-color (if (and (not (colors/dark?)) @ready-to-lock?) colors/black colors/white)
icon-opacity (reanimated/use-shared-value 1)
red-overlay-opacity (reanimated/use-shared-value 0)
gray-overlay-opacity (reanimated/use-shared-value 0)
complete-animation (fn []
(cond
(and @ready-to-lock? (not @record-button-is-animating?))
(do
(reset! locked? true)
(reset! ready-to-lock? false))
(and (not @locked?) (not @reviewing-audio?))
(do
(reset! recording? false)
(reset! ready-to-send? false)
(reset! ready-to-delete? false)
(reset! ready-to-lock? false)
(utils/clear-interval @recording-timer)
(reset! recording-length-ms 0))))
start-animation (fn []
(reanimated/set-shared-value opacity 1)
(reanimated/animate-shared-value-with-timing scale
2.6
signal-anim-duration
:linear)
;; TODO: Research if we can implement this with withSequence method
;; from Reanimated 2
;; GitHub issue [#14561]:
;; https://github.com/status-im/status-mobile/issues/14561
(reset! clear-timeout
(utils/set-timeout
#(do (reanimated/set-shared-value scale scale-to-each)
(reanimated/animate-shared-value-with-delay-repeat
scale
scale-to-total
signal-anim-duration-2
:linear
0
-1))
signal-anim-duration)))
stop-animation (fn []
(reanimated/set-shared-value opacity 0)
(reanimated/cancel-animation scale)
(reanimated/set-shared-value scale 1)
(when @clear-timeout (utils/clear-timeout @clear-timeout)))
start-y-animation (fn []
(reset! record-button-at-initial-position? false)
(reset! record-button-is-animating? true)
(reanimated/animate-shared-value-with-timing translate-y
-64
250
:easing1)
(reanimated/animate-shared-value-with-delay icon-opacity
0 33.33
:linear 76.66)
(utils/set-timeout (fn []
(reset! record-button-is-animating? false)
(when-not @touch-active? (complete-animation)))
250))
reset-y-animation (fn []
(reanimated/animate-shared-value-with-timing translate-y
0
300
:easing1)
(reanimated/animate-shared-value-with-timing icon-opacity
1
500
:linear)
(utils/set-timeout (fn []
(reset! record-button-at-initial-position? true))
500))
start-x-animation (fn []
(reset! record-button-at-initial-position? false)
(reset! record-button-is-animating? true)
(reanimated/animate-shared-value-with-timing translate-x
-64
250
:easing1)
(reanimated/animate-shared-value-with-delay icon-opacity
0 33.33
:linear 76.66)
(reanimated/animate-shared-value-with-timing red-overlay-opacity
1
33.33
:linear)
(utils/set-timeout (fn []
(reset! record-button-is-animating? false)
(when-not @touch-active? (complete-animation)))
250))
reset-x-animation (fn []
(reanimated/animate-shared-value-with-timing translate-x
0
300
:easing1)
(reanimated/animate-shared-value-with-timing icon-opacity
1
500
:linear)
(reanimated/animate-shared-value-with-timing red-overlay-opacity
0
100
:linear)
(utils/set-timeout (fn []
(reset! record-button-at-initial-position? true))
500))
start-x-y-animation (fn []
(reset! record-button-at-initial-position? false)
(reset! record-button-is-animating? true)
(reanimated/animate-shared-value-with-timing translate-y
-44
200
:easing1)
(reanimated/animate-shared-value-with-timing translate-x
-44
200
:easing1)
(reanimated/animate-shared-value-with-delay icon-opacity
0 33.33
:linear 33.33)
(reanimated/animate-shared-value-with-timing gray-overlay-opacity
1
33.33
:linear)
(utils/set-timeout (fn []
(reset! record-button-is-animating? false)
(when-not @touch-active? (complete-animation)))
200))
reset-x-y-animation (fn []
(reanimated/animate-shared-value-with-timing translate-y
0
300
:easing1)
(reanimated/animate-shared-value-with-timing translate-x
0
300
:easing1)
(reanimated/animate-shared-value-with-timing icon-opacity
1
500
:linear)
(reanimated/animate-shared-value-with-timing gray-overlay-opacity
0
800
:linear)
(utils/set-timeout (fn []
(reset! record-button-at-initial-position? true))
800))]
(effect! #(cond
@recording?
(start-animation)
(not @ready-to-lock?)
(stop-animation))
[@recording?])
(effect! #(if @ready-to-lock?
(start-x-y-animation)
(reset-x-y-animation))
[@ready-to-lock?])
(effect! #(if @ready-to-send?
(start-y-animation)
(reset-y-animation))
[@ready-to-send?])
(effect! #(if @ready-to-delete?
(start-x-animation)
(reset-x-animation))
[@ready-to-delete?])
[reanimated/view
{:style (style/record-button-big-container translate-x translate-y opacity)
:pointer-events :none}
[:<>
(map-indexed
(fn [id animation]
^{:key id}
[animated-ring
{:scale (:scale animation)
:opacity (:opacity animation)
:color rings-color}])
animations)]
[rn/view {:style (style/record-button-big-body button-color)}
[reanimated/view {:style (style/record-button-big-red-overlay red-overlay-opacity)}]
[reanimated/view {:style (style/record-button-big-gray-overlay gray-overlay-opacity)}]
[reanimated/view {:style (style/record-button-big-icon-container icon-opacity)}
(if @locked?
[rn/view {:style style/stop-icon}]
[icons/icon :i/audio {:color icon-color}])]]]))])
(defn- send-button
[recording? ready-to-send? reviewing-audio?]
[:f>
(fn []
(let [opacity (reanimated/use-shared-value 0)
translate-y (reanimated/use-shared-value 20)
connector-opacity (reanimated/use-shared-value 0)
width (reanimated/use-shared-value 12)
height (reanimated/use-shared-value 24)
border-radius-first-half (reanimated/use-shared-value 16)
border-radius-second-half (reanimated/use-shared-value 8)
start-y-animation (fn []
(reanimated/animate-shared-value-with-delay translate-y
12 50
:linear 133.33)
(reanimated/animate-shared-value-with-delay connector-opacity
1 0
:easing1 93.33)
(reanimated/animate-shared-value-with-delay width
56 83.33
:easing1 80)
(reanimated/animate-shared-value-with-delay height
56 83.33
:easing1 80)
(reanimated/animate-shared-value-with-delay
border-radius-first-half
28 83.33
:easing1 80)
(reanimated/animate-shared-value-with-delay
border-radius-second-half
28 83.33
:easing1 80))
reset-y-animation (fn []
(reanimated/animate-shared-value-with-timing translate-y
0
100
:linear)
(reanimated/set-shared-value connector-opacity 0)
(reanimated/set-shared-value width 12)
(reanimated/set-shared-value height 24)
(reanimated/set-shared-value border-radius-first-half 16)
(reanimated/set-shared-value border-radius-second-half 8))
fade-in-animation (fn []
(reanimated/animate-shared-value-with-timing translate-y
0
200
:linear)
(reanimated/animate-shared-value-with-timing opacity
1
200
:linear))
fade-out-animation (fn []
(reanimated/animate-shared-value-with-timing translate-y
(if @reviewing-audio?
76
20)
200
:linear)
(when-not @reviewing-audio?
(reanimated/animate-shared-value-with-timing opacity
0
200
:linear))
(reanimated/set-shared-value connector-opacity 0)
(reanimated/set-shared-value width 24)
(reanimated/set-shared-value height 12)
(reanimated/set-shared-value border-radius-first-half 8)
(reanimated/set-shared-value border-radius-second-half 16))
fade-out-reset-animation (fn []
(reanimated/animate-shared-value-with-timing opacity
0
200
:linear)
(reanimated/animate-shared-value-with-delay translate-y
20 0
:linear 200)
(reanimated/set-shared-value connector-opacity 0)
(reanimated/set-shared-value width 24)
(reanimated/set-shared-value height 12)
(reanimated/set-shared-value border-radius-first-half 8)
(reanimated/set-shared-value border-radius-second-half 16))]
(effect! #(if @recording?
(fade-in-animation)
(fade-out-animation))
[@recording?])
(effect! #(when-not @reviewing-audio?
(fade-out-reset-animation))
[@reviewing-audio?])
(effect! #(cond
@ready-to-send?
(start-y-animation)
@recording? (reset-y-animation))
[@ready-to-send?])
[:<>
[reanimated/view {:style (style/send-button-container opacity)}
[reanimated/view
{:style (style/send-button-connector connector-opacity
width
height
border-radius-first-half
border-radius-second-half)}]]
[reanimated/view
{:style (style/send-button translate-y opacity)
:pointer-events :none}
[icons/icon :i/arrow-up
{:color colors/white
:size 20
:container-style style/send-icon-container}]]]))])
(defn- lock-button
[recording? ready-to-lock? locked?]
[:f>
(fn []
(let [translate-x-y (reanimated/use-shared-value 20)
opacity (reanimated/use-shared-value 0)
connector-opacity (reanimated/use-shared-value 0)
width (reanimated/use-shared-value 24)
height (reanimated/use-shared-value 12)
border-radius-first-half (reanimated/use-shared-value 8)
border-radius-second-half (reanimated/use-shared-value 8)
start-x-y-animation (fn []
(reanimated/animate-shared-value-with-delay translate-x-y
8 50
:linear 116.66)
(reanimated/animate-shared-value-with-delay connector-opacity
1 0
:easing1 80)
(reanimated/animate-shared-value-with-delay width
56 83.33
:easing1 63.33)
(reanimated/animate-shared-value-with-delay height
56 83.33
:easing1 63.33)
(reanimated/animate-shared-value-with-delay
border-radius-first-half
28 83.33
:easing1 63.33)
(reanimated/animate-shared-value-with-delay
border-radius-second-half
28 83.33
:easing1 63.33))
reset-x-y-animation (fn []
(reanimated/animate-shared-value-with-timing translate-x-y
0
100
:linear)
(reanimated/set-shared-value connector-opacity 0)
(reanimated/set-shared-value width 24)
(reanimated/set-shared-value height 12)
(reanimated/set-shared-value border-radius-first-half 8)
(reanimated/set-shared-value border-radius-second-half 16))
fade-in-animation (fn []
(reanimated/animate-shared-value-with-timing translate-x-y
0
220
:linear)
(reanimated/animate-shared-value-with-timing opacity
1
220
:linear))
fade-out-animation (fn []
(reanimated/animate-shared-value-with-timing translate-x-y
20
200
:linear)
(reanimated/animate-shared-value-with-timing opacity
0
200
:linear)
(reanimated/set-shared-value connector-opacity 0)
(reanimated/set-shared-value width 24)
(reanimated/set-shared-value height 12)
(reanimated/set-shared-value border-radius-first-half 8)
(reanimated/set-shared-value border-radius-second-half 16))]
(effect! #(if @recording?
(fade-in-animation)
(fade-out-animation))
[@recording?])
(effect! #(cond
@ready-to-lock?
(start-x-y-animation)
(and @recording? (not @locked?))
(reset-x-y-animation))
[@ready-to-lock?])
(effect! #(if @locked?
(fade-out-animation)
(reset-x-y-animation))
[@locked?])
[:<>
[reanimated/view {:style (style/lock-button-container opacity)}
[reanimated/view
{:style (style/lock-button-connector connector-opacity
width
height
border-radius-first-half
border-radius-second-half)}]]
[reanimated/view
{:style (style/lock-button translate-x-y opacity)
:pointer-events :none}
[icons/icon (if @ready-to-lock? :i/locked :i/unlocked)
{:color (colors/theme-colors colors/black colors/white)
:size 20}]]]))])
(defn- delete-button
[recording? ready-to-delete?]
[:f>
(fn []
(let [opacity (reanimated/use-shared-value 0)
translate-x (reanimated/use-shared-value 20)
connector-opacity (reanimated/use-shared-value 0)
width (reanimated/use-shared-value 24)
height (reanimated/use-shared-value 12)
border-radius-first-half (reanimated/use-shared-value 8)
border-radius-second-half (reanimated/use-shared-value 8)
start-x-animation (fn []
(reanimated/animate-shared-value-with-delay translate-x
12 50
:linear 133.33)
(reanimated/animate-shared-value-with-delay connector-opacity
1 0
:easing1 93.33)
(reanimated/animate-shared-value-with-delay width
56 83.33
:easing1 80)
(reanimated/animate-shared-value-with-delay height
56 83.33
:easing1 80)
(reanimated/animate-shared-value-with-delay
border-radius-first-half
28 83.33
:easing1 80)
(reanimated/animate-shared-value-with-delay
border-radius-second-half
28 83.33
:easing1 80))
reset-x-animation (fn []
(reanimated/animate-shared-value-with-timing translate-x
0
100
:linear)
(reanimated/set-shared-value connector-opacity 0)
(reanimated/set-shared-value width 24)
(reanimated/set-shared-value height 12)
(reanimated/set-shared-value border-radius-first-half 8)
(reanimated/set-shared-value border-radius-second-half 16))
fade-in-animation (fn []
(reanimated/animate-shared-value-with-timing translate-x
0
200
:linear)
(reanimated/animate-shared-value-with-timing opacity
1
200
:linear))
fade-out-animation (fn []
(reanimated/animate-shared-value-with-timing translate-x
20
200
:linear)
(reanimated/animate-shared-value-with-timing opacity
0
200
:linear)
(reanimated/set-shared-value connector-opacity 0)
(reanimated/set-shared-value width 24)
(reanimated/set-shared-value height 12)
(reanimated/set-shared-value border-radius-first-half 8)
(reanimated/set-shared-value border-radius-second-half 16))]
(effect! #(if @recording?
(fade-in-animation)
(fade-out-animation))
[@recording?])
(effect! #(cond
@ready-to-delete?
(start-x-animation)
@recording?
(reset-x-animation))
[@ready-to-delete?])
[:<>
[reanimated/view {:style (style/delete-button-container opacity)}
[reanimated/view
{:style (style/delete-button-connector connector-opacity
width
height
border-radius-first-half
border-radius-second-half)}]]
[reanimated/view
{:style (style/delete-button translate-x opacity)
:pointer-events :none}
[icons/icon :i/delete
{:color colors/white
:size 20}]]]))])
(defn- record-button
[recording? reviewing-audio?]
[:f>
(fn []
(let [opacity (reanimated/use-shared-value 1)
show-animation #(reanimated/set-shared-value opacity 1)
hide-animation #(reanimated/set-shared-value opacity 0)]
(effect! #(if (or @recording? @reviewing-audio?)
(hide-animation)
(show-animation))
[@recording? @reviewing-audio?])
[reanimated/view {:style (style/record-button-container opacity)}
[button/button
{:type :outline
:size 32
:width 32
:accessibility-label :mic-button}
[icons/icon :i/audio {:color (colors/theme-colors colors/neutral-100 colors/white)}]]]))])
(defn input-view
[]
[:f>
(fn []
(let [recording? (reagent/atom false)
locked? (reagent/atom false)
ready-to-send? (reagent/atom false)
ready-to-lock? (reagent/atom false)
ready-to-delete? (reagent/atom false)
reviewing-audio? (reagent/atom false)
clear-timeout (atom nil)
record-button-at-initial-position? (atom true)
record-button-is-animating? (atom false)
touch-active? (atom false)
recording-timer (atom nil)
recording-length-ms (atom 0)
on-start-should-set-responder
(fn [^js e]
(when-not @locked?
(let [pressed-record-button? (touch-inside-area?
{:location-x (oops/oget e "nativeEvent.locationX")
:location-y (oops/oget e "nativeEvent.locationY")
:ignore-min-y? false
:ignore-max-y? false
:ignore-min-x? false
:ignore-max-x? false}
record-button-area)]
(when-not @reviewing-audio?
(reset! recording? pressed-record-button?)
(when pressed-record-button?
;; TODO: By now we just track recording length, we need to add actual audio
;; recording logic
;; GitHub issue [#14558]: https://github.com/status-im/status-mobile/issues/14558
(reset! recording-timer (utils/set-interval #(reset! recording-length-ms
(+ @recording-length-ms 500))
500))))
(reset! touch-active? true)))
true)
on-responder-move
(fn [^js e]
(when-not @locked?
(let [location-x (oops/oget e "nativeEvent.locationX")
location-y (oops/oget e "nativeEvent.locationY")
page-x (oops/oget e "nativeEvent.pageX")
page-y (oops/oget e "nativeEvent.pageY")
moved-to-send-button? (touch-inside-area?
{:location-x location-x
:location-y location-y
:ignore-min-y? true
:ignore-max-y? false
:ignore-min-x? false
:ignore-max-x? true}
(send-button-area @ready-to-send?))
moved-to-delete-button? (touch-inside-area?
{:location-x location-x
:location-y location-y
:ignore-min-y? false
:ignore-max-y? true
:ignore-min-x? true
:ignore-max-x? false}
(delete-button-area @ready-to-delete?))
moved-to-lock-button? (touch-inside-area?
{:location-x location-x
:location-y location-y
:ignore-min-y? false
:ignore-max-y? false
:ignore-min-x? false
:ignore-max-x? false}
(lock-button-area @ready-to-lock?))
moved-to-record-button? (and
(touch-inside-area?
{:location-x location-x
:location-y location-y
:ignore-min-y? false
:ignore-max-y? false
:ignore-min-x? false
:ignore-max-x? false}
record-button-area-big)
(not= location-x page-x)
(not= location-y page-y))]
(cond
(and
(or
(and moved-to-record-button? @ready-to-lock?)
(and (not @locked?) moved-to-lock-button? @record-button-at-initial-position?))
(not @ready-to-delete?)
(not @ready-to-send?)
@recording?)
(reset! ready-to-lock? moved-to-lock-button?)
(and
(or
(and moved-to-record-button? @ready-to-delete?)
(and moved-to-delete-button? @record-button-at-initial-position?))
(not @ready-to-lock?)
(not @ready-to-send?)
@recording?)
(reset! ready-to-delete? moved-to-delete-button?)
(and
(or
(and moved-to-record-button? @ready-to-send?)
(and moved-to-send-button? @record-button-at-initial-position?))
(not @ready-to-lock?)
(not @ready-to-delete?)
@recording?)
(reset! ready-to-send? moved-to-send-button?)))))
on-responder-release
(fn [^js e]
(let [on-record-button? (touch-inside-area?
{:location-x (oops/oget e "nativeEvent.locationX")
:location-y (oops/oget e "nativeEvent.locationY")
:ignore-min-y? false
:ignore-max-y? false
:ignore-min-x? false
:ignore-max-x? false}
(if @reviewing-audio? record-button-area record-button-area-big))]
(cond
(and @reviewing-audio? on-record-button?)
(reset! reviewing-audio? false)
(and @ready-to-lock? (not @record-button-is-animating?))
(do
(reset! locked? true)
(reset! ready-to-lock? false))
(and (not @reviewing-audio?) on-record-button?)
(do
(when (>= @recording-length-ms 500) (reset! reviewing-audio? true))
(reset! locked? false)
(reset! recording? false)
(reset! ready-to-lock? false)
(utils/clear-interval @recording-timer)
(reset! recording-length-ms 0))
(and (not @locked?) (not @reviewing-audio?) (not @record-button-is-animating?))
(do
(reset! recording? false)
(reset! ready-to-send? false)
(reset! ready-to-delete? false)
(reset! ready-to-lock? false)
(utils/clear-interval @recording-timer)
(reset! recording-length-ms 0))))
(reset! touch-active? false))]
(fn []
[rn/view
{:style style/input-container
:pointer-events :box-only
:on-start-should-set-responder on-start-should-set-responder
:on-responder-move on-responder-move
:on-responder-release on-responder-release}
[delete-button recording? ready-to-delete?]
[lock-button recording? ready-to-lock? locked?]
[send-button recording? ready-to-send? reviewing-audio?]
[record-button-big
recording?
ready-to-send?
ready-to-lock?
ready-to-delete?
record-button-is-animating?
record-button-at-initial-position?
locked?
reviewing-audio?
recording-timer
recording-length-ms
clear-timeout
touch-active?]
[record-button recording? reviewing-audio?]])))])
@@ -28,57 +28,57 @@
(rtl/render (reagent/as-element [selectors/radio opts]))))
(js/global.test "default render of toggle component"
(fn []
(render-toggle)
(-> (js/expect (rtl/screen.getByTestId "toggle-component"))
(.toBeTruthy))))
(fn []
(render-toggle)
(-> (js/expect (rtl/screen.getByTestId "toggle-component"))
(.toBeTruthy))))
(js/global.test "toggle component on change is working"
(let [mock-fn (js/jest.fn)]
(fn []
(render-toggle {:on-change mock-fn})
(rtl/fireEvent.press (rtl/screen.getByTestId "toggle-component"))
(-> (js/expect mock-fn)
(.toHaveBeenCalledTimes 1)))))
(let [mock-fn (js/jest.fn)]
(fn []
(render-toggle {:on-change mock-fn})
(rtl/fireEvent.press (rtl/screen.getByTestId "toggle-component"))
(-> (js/expect mock-fn)
(.toHaveBeenCalledTimes 1)))))
(js/global.test "default render of radio component"
(fn []
(render-radio)
(-> (js/expect (rtl/screen.getByTestId "radio-component"))
(.toBeTruthy))))
(fn []
(render-radio)
(-> (js/expect (rtl/screen.getByTestId "radio-component"))
(.toBeTruthy))))
(js/global.test "radio component on change is working"
(let [mock-fn (js/jest.fn)]
(fn []
(render-radio {:on-change mock-fn})
(rtl/fireEvent.press (rtl/screen.getByTestId "radio-component"))
(-> (js/expect mock-fn)
(.toHaveBeenCalledTimes 1)))))
(let [mock-fn (js/jest.fn)]
(fn []
(render-radio {:on-change mock-fn})
(rtl/fireEvent.press (rtl/screen.getByTestId "radio-component"))
(-> (js/expect mock-fn)
(.toHaveBeenCalledTimes 1)))))
(js/global.test "default render of checkbox component"
(fn []
(render-checkbox)
(-> (js/expect (rtl/screen.getByTestId "checkbox-component"))
(.toBeTruthy))))
(fn []
(render-checkbox)
(-> (js/expect (rtl/screen.getByTestId "checkbox-component"))
(.toBeTruthy))))
(js/global.test "checkbox component on change is working"
(let [mock-fn (js/jest.fn)]
(fn []
(render-checkbox {:on-change mock-fn})
(rtl/fireEvent.press (rtl/screen.getByTestId "checkbox-component"))
(-> (js/expect mock-fn)
(.toHaveBeenCalledTimes 1)))))
(let [mock-fn (js/jest.fn)]
(fn []
(render-checkbox {:on-change mock-fn})
(rtl/fireEvent.press (rtl/screen.getByTestId "checkbox-component"))
(-> (js/expect mock-fn)
(.toHaveBeenCalledTimes 1)))))
(js/global.test "default render of checkbox-prefill component"
(fn []
(render-checkbox-prefill)
(-> (js/expect (rtl/screen.getByTestId "checkbox-prefill-component"))
(.toBeTruthy))))
(fn []
(render-checkbox-prefill)
(-> (js/expect (rtl/screen.getByTestId "checkbox-prefill-component"))
(.toBeTruthy))))
(js/global.test "checkbox-prefill component on change is working"
(let [mock-fn (js/jest.fn)]
(fn []
(render-checkbox-prefill {:on-change mock-fn})
(rtl/fireEvent.press (rtl/screen.getByTestId "checkbox-prefill-component"))
(-> (js/expect mock-fn)
(.toHaveBeenCalledTimes 1)))))
(let [mock-fn (js/jest.fn)]
(fn []
(render-checkbox-prefill {:on-change mock-fn})
(rtl/fireEvent.press (rtl/screen.getByTestId "checkbox-prefill-component"))
(-> (js/expect mock-fn)
(.toHaveBeenCalledTimes 1)))))
@@ -0,0 +1,32 @@
(ns quo2.components.selectors.disclaimer
(:require [quo2.components.markdown.text :as text]
[quo2.components.selectors.selectors :as selectors]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]))
(defn disclaimer
[{:keys [on-change accessibility-label container-style]} label]
[rn/view
{:style
(merge
container-style
{:flex 1
:flex-direction :row
:background-color (colors/theme-colors
colors/neutral-5
colors/neutral-80-opa-40)
:padding 11
:align-self :stretch
:border-radius 12
:border-width 1
:border-color (colors/theme-colors
colors/neutral-20
colors/neutral-70)})}
[selectors/checkbox
{:on-change on-change}]
[text/text
{:accessibility-label accessibility-label
:size :paragraph-2
:style {:margin-left 8}}
label]])
@@ -1,16 +0,0 @@
(ns quo2.components.selectors.disclaimer.style
(:require [quo2.foundations.colors :as colors]))
(defn container
[]
{:flex 1
:flex-direction :row
:background-color (colors/theme-colors colors/neutral-5 colors/neutral-80-opa-40)
:padding 11
:align-self :stretch
:border-radius 12
:border-width 1
:border-color (colors/theme-colors colors/neutral-20 colors/neutral-70)})
(def text
{:margin-left 8})
@@ -1,17 +0,0 @@
(ns quo2.components.selectors.disclaimer.view
(:require [quo2.components.markdown.text :as text]
[quo2.components.selectors.disclaimer.style :as style]
[quo2.components.selectors.selectors :as selectors]
[react-native.core :as rn]))
(defn view
[{:keys [on-change accessibility-label container-style]} label]
[rn/view
{:style (merge container-style (style/container))}
[selectors/checkbox
{:accessibility-label accessibility-label
:on-change on-change}]
[text/text
{:size :paragraph-2
:style style/text}
label]])
@@ -1,16 +0,0 @@
(ns quo2.components.selectors.filter.component-spec
(:require [quo2.components.selectors.filter.view :as quo]
[test-helpers.component :as h]))
(h/describe "selector filter component"
(h/test "renders component"
(h/render [quo/view])
(-> (js/expect (h/get-by-label-text :selector-filter))
(.toBeTruthy)))
(h/test "calls custom event handler when on-press-out is triggered"
(let [on-press-out-mock (js/jest.fn)]
(h/render [quo/view {:on-press-out on-press-out-mock}])
(h/fire-event :press-out (h/get-by-label-text :selector-filter))
(-> (js/expect on-press-out-mock)
(.toHaveBeenCalledTimes 1)))))
@@ -1,60 +0,0 @@
(ns quo2.components.selectors.filter.style
(:require [quo2.foundations.colors :as colors]))
(def container-default
{:width 32
:height 32
:border-radius 10
:align-items :center
:justify-content :center
:padding 6})
(defn container-border-color
[pressed? blur? override-theme]
(let [dark? (= :dark override-theme)]
(cond
(and (not pressed?) (not dark?) (not blur?))
colors/neutral-20
(and (not pressed?) dark? (not blur?))
colors/neutral-80
(and pressed? (not dark?) blur?)
colors/neutral-80-opa-20
(or (and pressed? (not dark?) (not blur?))
(and (not pressed?) (not dark?) blur?))
colors/neutral-80-opa-10
(or (and pressed? dark? (not blur?))
(and (not pressed?) dark? blur?)
(and pressed? dark? blur?))
colors/white-opa-10
:else
nil)))
(defn container-background-color
[pressed? override-theme]
(when pressed?
(if (= :dark override-theme)
colors/primary-60
colors/primary-50)))
(defn container-outer
[pressed? override-theme]
(merge container-default
{:background-color (container-background-color pressed? override-theme)}))
(defn container-inner
[pressed? blur? override-theme]
(merge container-default
{:border-width 1
:border-color (container-border-color pressed? blur? override-theme)}))
(defn icon-color
[pressed? override-theme]
(if (and (not pressed?)
(= :light override-theme))
colors/neutral-100
colors/white))
@@ -1,23 +0,0 @@
(ns quo2.components.selectors.filter.view
(:require [quo2.components.icon :as icon]
[quo2.components.selectors.filter.style :as style]
[quo2.theme :as theme]
[react-native.core :as rn]
[reagent.core :as reagent]))
(defn view
[initial-props]
(let [pressed? (reagent/atom (:pressed? initial-props))]
(fn [{:keys [blur? override-theme on-press-out]
:or {override-theme (theme/get-theme)}}]
[rn/touchable-without-feedback
{:accessibility-label :selector-filter
:on-press-out (fn []
(swap! pressed? not)
(when on-press-out
(on-press-out @pressed?)))}
[rn/view {:style (style/container-outer @pressed? override-theme)}
[rn/view {:style (style/container-inner @pressed? blur? override-theme)}
[icon/icon :i/unread
{:color (style/icon-color @pressed? override-theme)
:size 20}]]]])))
+4 -39
View File
@@ -2,9 +2,7 @@
(:require [quo2.components.icon :as icons]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
[reagent.core :as reagent]
[quo2.components.selectors.styles :as style]))
[reagent.core :as reagent]))
(defn- get-color
[checked? disabled? blurred-background?]
@@ -68,7 +66,6 @@
(colors/alpha colors/neutral-100 (if disabled? 0.3 1))
(colors/alpha colors/white (if disabled? 0.3 1)))}]])]])))
(defn checkbox
[{:keys [default-checked?]}]
(let [checked? (reagent/atom (or default-checked? false))]
@@ -110,34 +107,6 @@
{:size 20
:color colors/white}]])]]])))
;; TODO (Omar): issue https://github.com/status-im/status-mobile/issues/14681
;(defn checkbox
; [{:keys [default-checked?]}]
; (let [checked? (reagent/atom (or default-checked? false))]
; @(reagent/track
; (fn [{:keys [on-change disabled? blurred-background? container-style]}]
; [rn/touchable-without-feedback
; {:on-press (handle-press disabled? on-change checked?)}
; [rn/view
; {:style (merge
; container-style
; {:height 20
; :width 20})}
; [rn/view
; {:style (style/checkbox-toggle checked? disabled? blurred-background?)
; :accessibility-label (str "checkbox-" (if @checked? "on" "off"))
; :accessibility-role :checkbox
; :testID "checkbox-component"}
; (when @checked?
; [rn/view
; {:style
; {:height 20
; :width 20}}
; [icons/icon :i/check-small
; {:size 20
; :color colors/white}]])]]])
; checked?)))
(defn radio
[{:keys [default-checked?]}]
(let [checked? (reagent/atom (or default-checked? false))]
@@ -151,9 +120,7 @@
:width 20
:border-radius 20
:border-width 1
:border-color (style/get-color @checked?
disabled?
blurred-background?)
:border-color (get-color @checked? disabled? blurred-background?)
:background-color (when-not blurred-background?
(colors/theme-colors colors/white
(colors/alpha colors/neutral-80
@@ -167,7 +134,7 @@
{:margin-left :auto
:height 14
:width 14
:background-color (when @checked? (style/get-color @checked? disabled? blurred-background?))
:background-color (when @checked? (get-color @checked? disabled? blurred-background?))
:border-radius 20
:margin-right :auto
:margin-top :auto
@@ -185,9 +152,7 @@
{:height 20
:width 30
:border-radius 20
:background-color (style/get-color @checked?
disabled?
blurred-background?)})
:background-color (get-color @checked? disabled? blurred-background?)})
:accessibility-label (str "toggle-" (if @checked? "on" "off"))
:accessibility-role :checkbox
:testID "toggle-component"}
-41
View File
@@ -1,41 +0,0 @@
(ns quo2.components.selectors.styles
(:require [quo2.foundations.colors :as colors]))
(defn get-color
[checked? disabled? blurred-background?]
(cond
checked?
(colors/custom-color-by-theme
:primary
50
60
(when disabled? 30)
(when disabled? 30))
blurred-background?
(colors/theme-colors
(colors/alpha colors/neutral-80 (if disabled? 0.05 0.1))
(colors/alpha colors/white (if disabled? 0.05 0.1)))
:else
(colors/theme-colors
(colors/alpha colors/neutral-20 (if disabled? 0.4 1))
(colors/alpha colors/neutral-70 (if disabled? 0.3 1)))))
(defn checkbox-toggle
[checked? disabled? blurred-background?]
{:flex 1
:border-radius 6
:border-width (if @checked? 0 1)
:background-color (cond
@checked?
(get-color @checked? disabled? blurred-background?)
blurred-background?
(colors/theme-colors
colors/white-opa-5
colors/white-opa-10)
:else
(colors/theme-colors
colors/white
colors/neutral-80-opa-40))
:border-color (if @checked?
:none
(get-color @checked? disabled? blurred-background?))})
@@ -1,71 +0,0 @@
(ns quo2.components.settings.accounts.style
(:require [quo2.foundations.colors :as colors]))
(def card
{:position :relative
:padding 8
:border-radius 16
:height 160
:width 160})
(defn background-top
[custom-color]
{:position :absolute
:top 0
:border-top-left-radius 16
:border-top-right-radius 16
:width 160
:height 64
:background-color (-> colors/customization
(get-in [custom-color 50])
(colors/alpha 0.2))})
(defn background-bottom
[]
{:position :absolute
:top 40
:border-radius 16
:width 160
:height 120
:background-color (colors/theme-colors
colors/white
colors/neutral-90)})
(defn avatar-border
[]
{:margin 2
:justify-content :center
:align-items :center
:width 52
:height 52
:border-radius 14
:border-width 2
:border-color (colors/theme-colors
colors/white
colors/neutral-90)})
(def menu-button-container
{:justify-content :center
:align-items :center
:align-self :flex-start})
(defn menu-button-color
[]
{:background-color (colors/theme-colors
colors/white-opa-40
colors/neutral-80-opa-40)})
(defn address-text
[]
{:color (colors/theme-colors
colors/neutral-50
colors/neutral-40)})
(def card-top
{:flex-direction :row
:align-items :center
:justify-content :space-between})
(def card-bottom
{:flex 1
:margin-horizontal 4
:margin-top 4})
@@ -1,46 +0,0 @@
(ns quo2.components.settings.accounts.view
(:require [quo2.components.avatars.account-avatar :as account-avatar]
[quo2.components.buttons.button :as button]
[quo2.components.markdown.text :as text]
[quo2.components.settings.accounts.style :as style]
[react-native.core :as rn]))
(defn card-background
[{:keys [custom-color]}]
[:<>
[rn/view {:style (style/background-top custom-color)}]
[rn/view {:style (style/background-bottom)}]])
(defn avatar
[avatar-props]
[rn/view {:style (style/avatar-border)}
[account-avatar/account-avatar (assoc avatar-props :size 48)]])
(defn menu-button
[{:keys [on-press]}]
[rn/view {:style style/menu-button-container}
[button/button
{:style (style/menu-button-color)
:type :gray
:icon true
:size 24
:on-press on-press}
:i/more]])
(defn account
[{:keys [account-name account-address avatar-icon custom-color on-press-menu]}]
[rn/view {:style style/card}
[card-background {:custom-color custom-color}]
[rn/view {:style style/card-top}
[avatar
{:color custom-color
:icon avatar-icon}]
[menu-button {:on-press on-press-menu}]]
[rn/view {:style style/card-bottom}
[text/text {:size :paragraph-1 :weight :semi-bold}
account-name]
[text/text
{:style (style/address-text)
:size :paragraph-2
:weight :medium}
account-address]]])
@@ -1,67 +0,0 @@
(ns quo2.components.settings.privacy-option
(:require [quo2.components.icon :as icons]
[quo2.components.markdown.text :as text]
[quo2.components.selectors.selectors :as selectors]
[quo2.components.settings.style :as style]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]))
(defn- bullet
[]
[rn/view {:style style/bullet-container}
[rn/view {:style (style/bullet)}]])
(defn- unordered-list
[container-style list-items]
[rn/view {:style (merge style/list-container container-style)}
(for [item list-items]
^{:key (hash item)}
[rn/view {:style style/list-item}
[bullet]
[text/text {:size :paragraph-2} item]])])
(defn- card-footer
[{:keys [active? label on-toggle]}]
[rn/touchable-without-feedback
[rn/view {:style (style/card-footer)}
[rn/view {:style style/card-footer-label-container}
[text/text {:size :paragraph-2} label]]
[rn/view {:style style/card-footer-toggle-container}
[selectors/toggle
{:disabled? (not active?)
:on-change on-toggle}]]]])
(defn- selection-indicator
[active?]
[rn/view {:style (style/selection-indicator-container active?)}
[rn/view {:style (style/selection-indicator active?)}]])
(defn- card-header
[{:keys [icon label active?]}]
[rn/view {:style style/card-header-container}
[icons/icon icon
{:size 20
:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}]
[rn/view {:style style/card-header-label-container}
[text/text {:weight :semi-bold} label]]
[selection-indicator active?]])
(defn card
[{:keys [active? header footer list-items icon on-select on-toggle]
:or {icon :i/world
active? false}}]
[rn/touchable-without-feedback
{:on-press on-select
:accessibility-label :privacy-option-card
:testID :privacy-option-card}
[rn/view (style/privacy-option-card active?)
[card-header
{:active? active?
:icon icon
:label header}]
[unordered-list (when-not footer {:margin-bottom 8}) list-items]
(when footer
[card-footer
{:active? active?
:label footer
:on-toggle on-toggle}])]])
-93
View File
@@ -1,93 +0,0 @@
(ns quo2.components.settings.style
(:require [quo2.foundations.colors :as colors]))
(def bullet-container
{:width 20
:height 20
:margin-right 8
:align-self :flex-start})
(defn bullet
[]
{:background-color (colors/theme-colors colors/neutral-40 colors/neutral-50)
:border-radius 100
:width 8
:height 8
:position :absolute
:left 6
:top 6})
(def list-container
{:margin-right 12})
(def list-item
{:flex 1
:flex-direction :row
:align-items :center
:padding-vertical 6
:padding-horizontal 12})
(defn selection-indicator-container
[active?]
{:height 20
:width 20
:border-radius 20
:border-width 1
:border-color (if active?
(colors/theme-colors colors/primary-50 colors/primary-60)
(colors/theme-colors colors/neutral-20 colors/neutral-80))})
(defn selection-indicator
[active?]
{:margin-left :auto
:height 14
:width 14
:background-color (if active?
(colors/theme-colors colors/primary-50 colors/primary-60)
(colors/theme-colors colors/white-opa-40 colors/neutral-80-opa-40))
:border-radius 20
:margin-right :auto
:margin-top :auto
:margin-bottom :auto})
(def card-footer-label-container
{:flex 0.9
:margin-left 16})
(def card-footer-toggle-container
{:flex 0.1
:margin-horizontal 12
:align-self :center})
(defn card-footer
[]
{:flex 1
:flex-direction :row
:justify-content :space-between
:margin-horizontal 12
:margin-top 8
:margin-bottom 12
:padding-vertical 12
:border-radius 12
:border-width 1
:background-color (colors/theme-colors colors/neutral-5 colors/neutral-90)
:border-color (colors/theme-colors colors/neutral-10 colors/neutral-80)})
(def card-header-container
{:flex-direction :row
:align-items :center
:padding-top 12
:padding-bottom 8
:padding-horizontal 12})
(def card-header-label-container
{:flex 1
:margin-left 4})
(defn privacy-option-card
[active?]
{:border-radius 16
:border-width 1
:border-color (if active?
(colors/theme-colors colors/primary-50 colors/primary-60)
(colors/theme-colors colors/neutral-10 colors/neutral-80))})
+10 -12
View File
@@ -15,17 +15,16 @@
(defn indicator
[]
[rn/view
{:accessibility-label :notification-dot
:style {:position :absolute
:z-index 1
:right -2
:top -2
:width 10
:height 10
:border-radius 5
:justify-content :center
:align-items :center
:background-color (colors/theme-colors colors/neutral-5 colors/neutral-95)}}
{:position :absolute
:z-index 1
:right -2
:top -2
:width 10
:height 10
:border-radius 5
:justify-content :center
:align-items :center
:background-color (colors/theme-colors colors/neutral-5 colors/neutral-95)}
[notification-dot]])
(defn tabs
@@ -139,7 +138,6 @@
:shows-horizontal-scroll-indicator false
:data data
:key-fn (comp str :id)
:on-scroll-to-index-failed identity
:on-scroll (fn [^js e]
(when fade-end?
(let [offset-x (oget
+22 -70
View File
@@ -1,11 +1,16 @@
(ns quo2.components.tags.context-tags
(:require [quo2.components.avatars.group-avatar :as group-avatar]
[quo2.components.icon :as icons]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]
[quo2.theme :as quo2.theme]
[react-native.core :as rn]))
(defn padding-left-for-type
[type]
(case type
:group-avatar 3
8))
(defn trim-public-key
[pk]
(str (subs pk 0 6) "..." (subs pk (- (count pk) 3))))
@@ -24,7 +29,7 @@
:padding-left 8
:background-color (if (= theme :light)
colors/neutral-10
colors/neutral-90)}
colors/neutral-80)}
style)]
children))))
@@ -34,8 +39,7 @@
[base-tag
(-> opts
(select-keys [:override-theme :style])
(assoc-in [:style :padding-left] 3)
(assoc-in [:style :padding-vertical] 2))
(assoc-in [:style :padding-left] 3))
[group-avatar/group-avatar opts]
[text/text
{:weight :medium
@@ -53,74 +57,22 @@
(trim-public-key public-key)]]))
(defn context-tag
[_ _]
(fn [params photo name channel-name]
(let [text-style (:text-style params)
text-params {:weight :medium
:size :paragraph-2
:style (assoc text-style :justify-content :center)}
icon-color (colors/theme-colors colors/neutral-50 colors/neutral-40)]
[base-tag (assoc-in params [:style :padding-left] 3)
[rn/image
{:style {:width 20
:border-radius 10
:background-color :red
:height 20}
:source photo}]
[rn/view
{:style {:align-items :center
:flex-direction :row}}
[text/text text-params (str " " name)]
(when channel-name
[:<>
[icons/icon
:i/chevron-right
{:color icon-color
:size 16}]
[text/text text-params (str "# " channel-name)]])]])))
[params photo name]
(let [text-style (params :text-style)]
[base-tag (assoc-in params [:style :padding-left] 3)
[rn/image
{:style {:width 20
:border-radius 10
:background-color :white
:height 20}
:source photo}]
[text/text
(merge {:weight :medium
:size :paragraph-2}
{:style text-style})
(str " " name)]]))
(defn user-avatar-tag
[]
(fn [params username photo]
[context-tag params {:uri photo} username]))
(defn audio-tag
[duration params]
[base-tag
(merge
{:style {:padding-left 2
:padding-vertical 2}}
params)
[rn/view
{:width 20
:height 20
:border-radius 10
:align-items :center
:justify-content :center
:background-color colors/primary-50}
[icons/icon
:i/play
{:color colors/white
:size 12}]]
[text/text
{:weight :medium
:size :paragraph-2
:style {:margin-left 4
:color (colors/theme-colors
colors/neutral-100
colors/white
(:override-theme params))}}
duration]])
(defn community-tag
[avatar community-name params]
[context-tag
(merge
{:style {:padding-vertical 2}
:text-style {:margin-left 2
:color (colors/theme-colors
colors/neutral-100
colors/white
(:override-theme params))}}
params)
avatar community-name])
+45 -74
View File
@@ -1,61 +1,49 @@
(ns quo2.core
(:refer-clojure :exclude [filter])
(:require
quo2.components.avatars.account-avatar
quo2.components.avatars.channel-avatar
quo2.components.avatars.group-avatar
quo2.components.avatars.icon-avatar
quo2.components.avatars.user-avatar
quo2.components.avatars.wallet-user-avatar
quo2.components.banners.banner.view
quo2.components.buttons.button
quo2.components.buttons.dynamic-button
quo2.components.community.community-card-view
quo2.components.community.community-list-view
quo2.components.community.community-view
quo2.components.community.discover-card
quo2.components.community.icon
quo2.components.community.token-gating
quo2.components.counter.counter
quo2.components.dividers.date
quo2.components.dividers.divider-label
quo2.components.dividers.new-messages
quo2.components.drawers.action-drawers
quo2.components.dropdowns.dropdown
quo2.components.header
quo2.components.icon
quo2.components.info.info-message
quo2.components.info.information-box
quo2.components.list-items.channel
quo2.components.list-items.menu-item
quo2.components.list-items.preview-list
quo2.components.loaders.skeleton
quo2.components.markdown.text
quo2.components.messages.author.view
quo2.components.messages.gap
quo2.components.messages.system-message
quo2.components.navigation.floating-shell-button
quo2.components.navigation.page-nav
quo2.components.notifications.activity-log.view
quo2.components.notifications.count-down-circle
quo2.components.notifications.info-count
quo2.components.notifications.notification-dot
quo2.components.notifications.toast
quo2.components.reactions.reaction
quo2.components.selectors.disclaimer.view
quo2.components.selectors.filter.view
quo2.components.selectors.selectors
quo2.components.separator
quo2.components.settings.accounts.view
quo2.components.settings.privacy-option
quo2.components.tabs.account-selector
quo2.components.tabs.tabs
quo2.components.tags.context-tags
quo2.components.tags.status-tags
quo2.components.profile.profile-card.view
quo2.components.tags.tags))
(:require quo2.components.avatars.account-avatar
quo2.components.avatars.channel-avatar
quo2.components.avatars.group-avatar
quo2.components.avatars.icon-avatar
quo2.components.avatars.user-avatar
quo2.components.avatars.wallet-user-avatar
quo2.components.buttons.button
quo2.components.buttons.dynamic-button
quo2.components.community.community-card-view
quo2.components.community.community-list-view
quo2.components.community.community-view
quo2.components.community.discover-card
quo2.components.community.token-gating
quo2.components.counter.counter
quo2.components.dividers.date
quo2.components.dividers.divider-label
quo2.components.dividers.new-messages
quo2.components.drawers.action-drawers
quo2.components.dropdowns.dropdown
quo2.components.header
quo2.components.icon
quo2.components.info.info-message
quo2.components.info.information-box
quo2.components.list-items.channel
quo2.components.list-items.menu-item
quo2.components.list-items.preview-list
quo2.components.loaders.skeleton
quo2.components.markdown.text
quo2.components.messages.gap
quo2.components.messages.system-message
quo2.components.navigation.floating-shell-button
quo2.components.navigation.page-nav
quo2.components.notifications.activity-log.view
quo2.components.notifications.info-count
quo2.components.notifications.notification-dot
quo2.components.reactions.reaction
quo2.components.selectors.disclaimer
quo2.components.selectors.selectors
quo2.components.separator
quo2.components.tabs.account-selector
quo2.components.tabs.tabs
quo2.components.tags.context-tags
quo2.components.tags.status-tags
quo2.components.tags.tags))
(def toast quo2.components.notifications.toast/toast)
(def button quo2.components.buttons.button/button)
(def dynamic-button quo2.components.buttons.dynamic-button/dynamic-button)
(def text quo2.components.markdown.text/text)
@@ -70,24 +58,19 @@
(def gap quo2.components.messages.gap/gap)
(def system-message quo2.components.messages.system-message/system-message)
(def reaction quo2.components.reactions.reaction/reaction)
(def add-reaction quo2.components.reactions.reaction/add-reaction)
(def tags quo2.components.tags.tags/tags)
(def user-avatar-tag quo2.components.tags.context-tags/user-avatar-tag)
(def context-tag quo2.components.tags.context-tags/context-tag)
(def group-avatar-tag quo2.components.tags.context-tags/group-avatar-tag)
(def audio-tag quo2.components.tags.context-tags/audio-tag)
(def community-tag quo2.components.tags.context-tags/community-tag)
(def tabs quo2.components.tabs.tabs/tabs)
(def scrollable-tabs quo2.components.tabs.tabs/scrollable-tabs)
(def account-selector quo2.components.tabs.account-selector/account-selector)
(def floating-shell-button quo2.components.navigation.floating-shell-button/floating-shell-button)
(def status-tag quo2.components.tags.status-tags/status-tag)
(def page-nav quo2.components.navigation.page-nav/page-nav)
(def disclaimer quo2.components.selectors.disclaimer.view/view)
(def disclaimer quo2.components.selectors.disclaimer/disclaimer)
(def checkbox quo2.components.selectors.selectors/checkbox)
(def filter quo2.components.selectors.filter.view/view)
(def skeleton quo2.components.loaders.skeleton/skeleton)
(def author quo2.components.messages.author.view/author)
;;;; AVATAR
(def account-avatar quo2.components.avatars.account-avatar/account-avatar)
@@ -97,9 +80,6 @@
(def user-avatar quo2.components.avatars.user-avatar/user-avatar)
(def wallet-user-avatar quo2.components.avatars.wallet-user-avatar/wallet-user-avatar)
;;;; BANNER
(def banner quo2.components.banners.banner.view/banner)
;;;; COMMUNITY
(def community-card-view-item quo2.components.community.community-card-view/community-card-view-item)
(def communities-list-view-item quo2.components.community.community-list-view/communities-list-view-item)
@@ -112,7 +92,6 @@
(def permission-tag-container quo2.components.community.community-view/permission-tag-container)
(def discover-card quo2.components.community.discover-card/discover-card)
(def token-gating quo2.components.community.token-gating/token-gating)
(def community-icon quo2.components.community.icon/community-icon)
;;;; DIVIDERS
(def divider-label quo2.components.dividers.divider-label/divider-label)
@@ -128,11 +107,3 @@
(def activity-log quo2.components.notifications.activity-log.view/view)
(def info-count quo2.components.notifications.info-count/info-count)
(def notification-dot quo2.components.notifications.notification-dot/notification-dot)
(def count-down-circle quo2.components.notifications.count-down-circle/circle-timer)
;;;; PROFILE
(def profile-card quo2.components.profile.profile-card.view/profile-card)
;;;; SETTINGS
(def privacy-option quo2.components.settings.privacy-option/card)
(def account quo2.components.settings.accounts.view/account)
+2 -7
View File
@@ -1,9 +1,4 @@
(ns quo2.core-spec
(:require [quo2.components.banners.--tests--.banner-component-spec]
[quo2.components.buttons.--tests--.buttons-component-spec]
[quo2.components.counter.--tests--.counter-component-spec]
[quo2.components.dividers.--tests--.divider-label-component-spec]
(:require [quo2.components.counter.--tests--.counter-component-spec]
[quo2.components.drawers.--tests--.action-drawers-component-spec]
[quo2.components.markdown.--tests--.text-component-spec]
[quo2.components.selectors.--tests--.selectors-component-spec]
[quo2.components.selectors.filter.component-spec]))
[quo2.components.selectors.--tests--.selectors-component-spec]))
-27
View File
@@ -14,21 +14,6 @@
(let [rgb (string/split value #",")]
(str (string/join "," (butlast rgb)) "," opacity ")")))))
(defn- alpha-opaque
[value opacity]
(when value
(if (string/starts-with? value "#")
(let [hex (string/replace value #"#" "")
r (- 255 (* opacity (- 255 (js/parseInt (subs hex 0 2) 16))))
g (- 255 (* opacity (- 255 (js/parseInt (subs hex 2 4) 16))))
b (- 255 (* opacity (- 255 (js/parseInt (subs hex 4 6) 16))))]
(str "rgb(" r "," g "," b ")"))
(let [rgb (string/split value #",")
r (- 255 (* opacity (- 255 (get rgb 0))))
g (- 255 (* opacity (- 255 (get rgb 1))))
b (- 255 (* opacity (- 255 (get rgb 2))))]
(str "rgb(" r "," g "," b ")")))))
(def theme-alpha
(memoize
(fn
@@ -65,7 +50,6 @@
;;80 with transparency
(def neutral-80-opa-5 (alpha neutral-80 0.05))
(def neutral-80-opa-10 (alpha neutral-80 0.1))
(def neutral-80-opa-15 (alpha neutral-80 0.15))
(def neutral-80-opa-20 (alpha neutral-80 0.2))
(def neutral-80-opa-30 (alpha neutral-80 0.3))
(def neutral-80-opa-40 (alpha neutral-80 0.4))
@@ -84,17 +68,11 @@
(def neutral-95-opa-95 (alpha neutral-95 0.95))
;;100 with transparency
(def neutral-100-opa-0 (alpha neutral-100 0))
(def neutral-100-opa-10 (alpha neutral-100 0.1))
(def neutral-100-opa-60 (alpha neutral-100 0.6))
(def neutral-100-opa-70 (alpha neutral-100 0.7))
(def neutral-100-opa-80 (alpha neutral-100 0.8))
(def neutral-100-opa-90 (alpha neutral-100 0.9))
(def neutral-100-opa-95 (alpha neutral-100 0.95))
(def neutral-100-opa-100 (alpha neutral-100 1))
;;80 with transparency opaque
(def neutral-80-opa-5-opaque (alpha-opaque neutral-80 0.05))
;;;;White
@@ -114,11 +92,6 @@
(def white-opa-90 (alpha white 0.9))
(def white-opa-95 (alpha white 0.95))
;;;;Black
;;Solid
(def black "#000000")
;;;;Primary
;;Solid
-22
View File
@@ -1,9 +1,6 @@
(ns react-native.core
(:require ["@react-native-community/blur" :as blur]
["react" :as react]
["react-native" :as react-native]
[cljs-bean.core :as bean]
[oops.core :as oops]
[react-native.flat-list :as flat-list]
[react-native.platform :as platform]
[react-native.section-list :as section-list]
@@ -67,12 +64,6 @@
(def status-bar (.-StatusBar ^js react-native))
(def style-sheet (.-StyleSheet ^js react-native))
(defn status-bar-height
[]
(.-currentHeight ^js status-bar))
(defn hw-back-add-listener
[callback]
(.addEventListener (.-BackHandler ^js react-native) "hardwareBackPress" callback))
@@ -89,16 +80,3 @@
(merge (when platform/ios? {:behavior :padding})
props)]
children))
(defn use-effect
([effect] (use-effect effect []))
([effect deps]
(react/useEffect effect (bean/->js deps))))
(def use-ref react/useRef)
(defn use-effect-once [effect] (use-effect effect))
(defn use-unmount
[f]
(let [fn-ref (use-ref f)]
(oops/oset! fn-ref "current" f)
(use-effect-once (fn [] #((oops/oget fn-ref "current"))))))
-8
View File
@@ -1,8 +0,0 @@
(ns react-native.hooks
(:require ["@react-native-community/hooks" :as hooks]))
(defn use-keyboard
[]
(let [kb (.useKeyboard hooks)]
{:keyboard-shown (.-keyboardShown ^js kb)
:keyboard-height (.-keyboardHeight ^js kb)}))
+8 -52
View File
@@ -1,25 +1,11 @@
(ns react-native.reanimated
(:require ["react-native" :as rn]
["react-native-linear-gradient" :default LinearGradient]
["react-native-reanimated" :default reanimated :refer
(useSharedValue useAnimatedStyle
withTiming
withDelay
withSpring
withRepeat
Easing
Keyframe
cancelAnimation
SlideInUp
SlideOutUp
LinearTransition)]
[clojure.string :as string]
[reagent.core :as reagent]))
;; Animations
(def slide-in-up-animation SlideInUp)
(def slide-out-up-animation SlideOutUp)
(def linear-transition LinearTransition)
(:require
["react-native" :as rn]
["react-native-linear-gradient" :default LinearGradient]
["react-native-reanimated" :default reanimated :refer
(useSharedValue useAnimatedStyle withTiming withDelay withSpring withRepeat Easing Keyframe)]
[clojure.string :as string]
[reagent.core :as reagent]))
;; Animated Components
(def create-animated-component (comp reagent/adapt-react-class (.-createAnimatedComponent reanimated)))
@@ -40,7 +26,6 @@
(def with-spring withSpring)
(def key-frame Keyframe)
(def with-repeat withRepeat)
(def cancel-animation cancelAnimation)
;; Easings
(def bezier (.-bezier ^js Easing))
@@ -75,13 +60,6 @@
;; Worklets
(def worklet-factory (js/require "../src/js/worklet_factory.js"))
(defn interpolate
[shared-value input-range output-range]
(.interpolateValue ^js worklet-factory
shared-value
(clj->js input-range)
(clj->js output-range)))
;;;; Component Animations
;; kebab-case styles are not working for worklets
@@ -116,26 +94,4 @@
(js-obj "duration" duration
"easing" (get easings easing)))
number-of-repetitions
reverse?)))
(defn animate-shared-value-with-delay-repeat
([anim val duration easing delay number-of-repetitions]
(animate-shared-value-with-delay-repeat anim val duration easing delay number-of-repetitions false))
([anim val duration easing delay number-of-repetitions reverse?]
(set-shared-value anim
(with-delay delay
(with-repeat
(with-timing val
#js
{:duration duration
:easing (get easings easing)})
number-of-repetitions
reverse?)))))
(defn animate-shared-value-with-spring
[anim val {:keys [mass stiffness damping]}]
(set-shared-value anim
(with-spring val
(js-obj "mass" mass
"damping" damping
"stiffness" stiffness))))
reverse?)))
+1 -1
View File
@@ -8,7 +8,7 @@
(defn consumer
[component]
[consumer-raw
(fn [^js insets]
(fn [insets]
(reagent/as-element
[component (js->clj insets :keywordize-keys true)]))])
View File
-6
View File
@@ -1,6 +0,0 @@
(ns react-native.svg
(:require ["react-native-svg" :as Svg]
[reagent.core :as reagent]))
(def svg (reagent/adapt-react-class Svg/default))
(def path (reagent/adapt-react-class Svg/Path))
@@ -1,8 +1,10 @@
(ns status-im2.contexts.activity-center.events
(:require [status-im.data-store.activities :as data-store.activities]
[status-im2.contexts.activity-center.notification-types :as types]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
(ns status-im.activity-center.core
(:require [re-frame.core :as rf]
[status-im.activity-center.notification-types :as types]
[status-im.data-store.activities :as data-store.activities]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.utils.fx :as fx]
[taoensso.timbre :as log]))
(def defaults
{:filter-status :unread
@@ -14,26 +16,20 @@
;;;; Navigation
(rf/defn open-activity-center
(fx/defn open-activity-center
{:events [:activity-center/open]}
[{:keys [db]} {:keys [filter-type filter-status]}]
{:db (cond-> db
filter-status
(assoc-in [:activity-center :filter :status] filter-status)
filter-type
(assoc-in [:activity-center :filter :type] filter-type))
:dispatch [:show-popover
{:view :activity-center
:style {:margin 0}
:disable-touchable-overlay? true
:blur-view? true
:blur-view-props {:blur-amount 20
:blur-type :dark}}]})
[_]
(rf/dispatch [:show-popover
{:view :activity-center
:style {:margin 0}
:disable-touchable-overlay? true
:blur-view? true
:blur-view-props {:blur-amount 20
:blur-type :dark}}]))
;;;; Misc
(rf/defn process-notification-failure
(fx/defn process-notification-failure
{:events [:activity-center/process-notification-failure]}
[_ notification-id action error]
(log/warn (str "Failed to " action)
@@ -73,7 +69,7 @@
db-notifications
new-notifications))
(rf/defn notifications-reconcile
(fx/defn notifications-reconcile
{:events [:activity-center.notifications/reconcile]}
[{:keys [db]} new-notifications]
(when (seq new-notifications)
@@ -82,7 +78,7 @@
update-notifications
new-notifications)}))
(rf/defn notifications-reconcile-from-response
(fx/defn notifications-reconcile-from-response
{:events [:activity-center/reconcile-notifications-from-response]}
[cofx response]
(->> response
@@ -90,28 +86,6 @@
(map data-store.activities/<-rpc)
(notifications-reconcile cofx)))
(defn- remove-pending-contact-request
[notifications contact-id]
(remove #(= contact-id (:author %))
notifications))
(rf/defn notifications-remove-pending-contact-request
{:events [:activity-center/remove-pending-contact-request]}
[{:keys [db]} contact-id]
{:db (-> db
(update-in [:activity-center :notifications types/no-type :all :data]
remove-pending-contact-request
contact-id)
(update-in [:activity-center :notifications types/no-type :unread :data]
remove-pending-contact-request
contact-id)
(update-in [:activity-center :notifications types/contact-request :all :data]
remove-pending-contact-request
contact-id)
(update-in [:activity-center :notifications types/contact-request :unread :data]
remove-pending-contact-request
contact-id))})
;;;; Mark notifications as read
(defn- get-notification
@@ -125,73 +99,73 @@
(filter #(= notification-id (:id %)))
first))
(rf/defn mark-as-read
(fx/defn mark-as-read
{:events [:activity-center.notifications/mark-as-read]}
[{:keys [db]} notification-id]
(when-let [notification (get-notification db notification-id)]
{:json-rpc/call [{:method "wakuext_markActivityCenterNotificationsRead"
:params [[notification-id]]
:on-success #(rf/dispatch [:activity-center.notifications/mark-as-read-success
notification])
:on-error #(rf/dispatch [:activity-center/process-notification-failure
notification-id
:notification/mark-as-read
%])}]}))
{::json-rpc/call [{:method "wakuext_markActivityCenterNotificationsRead"
:params [[notification-id]]
:on-success #(rf/dispatch [:activity-center.notifications/mark-as-read-success
notification])
:on-error #(rf/dispatch [:activity-center/process-notification-failure
notification-id
:notification/mark-as-read
%])}]}))
(rf/defn mark-as-read-success
(fx/defn mark-as-read-success
{:events [:activity-center.notifications/mark-as-read-success]}
[cofx notification]
(notifications-reconcile cofx [(assoc notification :read true)]))
;;;; Contact verification
(rf/defn contact-verification-decline
(fx/defn contact-verification-decline
{:events [:activity-center.contact-verification/decline]}
[_ notification-id]
{:json-rpc/call [{:method "wakuext_declineContactVerificationRequest"
:params [notification-id]
:on-success #(rf/dispatch [:activity-center/reconcile-notifications-from-response
%])
:on-error #(rf/dispatch [:activity-center/process-notification-failure
notification-id
:contact-verification/decline
%])}]})
{::json-rpc/call [{:method "wakuext_declineContactVerificationRequest"
:params [notification-id]
:on-success #(rf/dispatch [:activity-center/reconcile-notifications-from-response
%])
:on-error #(rf/dispatch [:activity-center/process-notification-failure
notification-id
:contact-verification/decline
%])}]})
(rf/defn contact-verification-reply
(fx/defn contact-verification-reply
{:events [:activity-center.contact-verification/reply]}
[_ notification-id reply]
{:json-rpc/call [{:method "wakuext_acceptContactVerificationRequest"
:params [notification-id reply]
:on-success #(rf/dispatch [:activity-center/reconcile-notifications-from-response
%])
:on-error #(rf/dispatch [:activity-center/process-notification-failure
notification-id
:contact-verification/reply
%])}]})
{::json-rpc/call [{:method "wakuext_acceptContactVerificationRequest"
:params [notification-id reply]
:on-success #(rf/dispatch [:activity-center/reconcile-notifications-from-response
%])
:on-error #(rf/dispatch [:activity-center/process-notification-failure
notification-id
:contact-verification/reply
%])}]})
(rf/defn contact-verification-mark-as-trusted
(fx/defn contact-verification-mark-as-trusted
{:events [:activity-center.contact-verification/mark-as-trusted]}
[_ notification-id]
{:json-rpc/call [{:method "wakuext_verifiedTrusted"
:params [{:id notification-id}]
:on-success #(rf/dispatch [:activity-center/reconcile-notifications-from-response
%])
:on-error #(rf/dispatch [:activity-center/process-notification-failure
notification-id
:contact-verification/mark-as-trusted
%])}]})
{::json-rpc/call [{:method "wakuext_verifiedTrusted"
:params [{:id notification-id}]
:on-success #(rf/dispatch [:activity-center/reconcile-notifications-from-response
%])
:on-error #(rf/dispatch [:activity-center/process-notification-failure
notification-id
:contact-verification/mark-as-trusted
%])}]})
(rf/defn contact-verification-mark-as-untrustworthy
(fx/defn contact-verification-mark-as-untrustworthy
{:events [:activity-center.contact-verification/mark-as-untrustworthy]}
[_ notification-id]
{:json-rpc/call [{:method "wakuext_verifiedUntrustworthy"
:params [{:id notification-id}]
:on-success #(rf/dispatch [:activity-center/reconcile-notifications-from-response
%])
:on-error #(rf/dispatch [:activity-center/process-notification-failure
notification-id
:contact-verification/mark-as-untrustworthy
%])}]})
{::json-rpc/call [{:method "wakuext_verifiedUntrustworthy"
:params [{:id notification-id}]
:on-success #(rf/dispatch [:activity-center/reconcile-notifications-from-response
%])
:on-error #(rf/dispatch [:activity-center/process-notification-failure
notification-id
:contact-verification/mark-as-untrustworthy
%])}]})
;;;; Notifications fetching and pagination
@@ -213,21 +187,21 @@
:all status-all
99))
(rf/defn notifications-fetch
(fx/defn notifications-fetch
[{:keys [db]} {:keys [cursor per-page filter-type filter-status reset-data?]}]
(when-not (get-in db [:activity-center :notifications filter-type filter-status :loading?])
(let [per-page (or per-page (defaults :notifications-per-page))]
{:db (assoc-in db
[:activity-center :notifications filter-type filter-status :loading?]
true)
:json-rpc/call [{:method "wakuext_activityCenterNotificationsBy"
:params [cursor per-page filter-type (status filter-status)]
:on-success #(rf/dispatch [:activity-center.notifications/fetch-success
filter-type filter-status reset-data? %])
:on-error #(rf/dispatch [:activity-center.notifications/fetch-error
filter-type filter-status %])}]})))
{:db (assoc-in db
[:activity-center :notifications filter-type filter-status :loading?]
true)
::json-rpc/call [{:method "wakuext_activityCenterNotificationsBy"
:params [cursor per-page filter-type (status filter-status)]
:on-success #(rf/dispatch [:activity-center.notifications/fetch-success
filter-type filter-status reset-data? %])
:on-error #(rf/dispatch [:activity-center.notifications/fetch-error
filter-type filter-status %])}]})))
(rf/defn notifications-fetch-first-page
(fx/defn notifications-fetch-first-page
{:events [:activity-center.notifications/fetch-first-page]}
[{:keys [db] :as cofx} {:keys [filter-type filter-status]}]
(let [filter-type (or filter-type
@@ -238,7 +212,7 @@
(get-in db
[:activity-center :filter :status]
(defaults :filter-status)))]
(rf/merge cofx
(fx/merge cofx
{:db (-> db
(assoc-in [:activity-center :filter :type] filter-type)
(assoc-in [:activity-center :filter :status] filter-status))}
@@ -247,7 +221,7 @@
:filter-status filter-status
:reset-data? true}))))
(rf/defn notifications-fetch-next-page
(fx/defn notifications-fetch-next-page
{:events [:activity-center.notifications/fetch-next-page]}
[{:keys [db] :as cofx}]
(let [{:keys [type status]} (get-in db [:activity-center :filter])
@@ -259,7 +233,7 @@
:filter-status status
:reset-data? false}))))
(rf/defn notifications-fetch-success
(fx/defn notifications-fetch-success
{:events [:activity-center.notifications/fetch-success]}
[{:keys [db]}
filter-type
@@ -275,7 +249,7 @@
(constantly processed)
#(concat %1 processed))))}))
(rf/defn notifications-fetch-unread-contact-requests
(fx/defn notifications-fetch-unread-contact-requests
"Unread contact requests are, in practical terms, the same as pending contact
requests in the new Activity Center, because pending contact requests are
always marked as unread, and once the user declines/accepts the request, they
@@ -292,21 +266,21 @@
:per-page 20
:reset-data? true}))
(rf/defn notifications-fetch-unread-count
(fx/defn notifications-fetch-unread-count
{:events [:activity-center.notifications/fetch-unread-count]}
[_]
{:json-rpc/call [{:method "wakuext_unreadActivityCenterNotificationsCount"
:params []
:on-success #(rf/dispatch [:activity-center.notifications/fetch-unread-count-success
%])
:on-error #()}]})
{::json-rpc/call [{:method "wakuext_unreadActivityCenterNotificationsCount"
:params []
:on-success #(rf/dispatch [:activity-center.notifications/fetch-unread-count-success
%])
:on-error #()}]})
(rf/defn notifications-fetch-unread-count-success
(fx/defn notifications-fetch-unread-count-success
{:events [:activity-center.notifications/fetch-unread-count-success]}
[{:keys [db]} result]
{:db (assoc-in db [:activity-center :unread-count] result)})
(rf/defn notifications-fetch-error
(fx/defn notifications-fetch-error
{:events [:activity-center.notifications/fetch-error]}
[{:keys [db]} filter-type filter-status error]
(log/warn "Failed to load Activity Center notifications" error)
@@ -1,13 +1,13 @@
(ns status-im2.contexts.activity-center.events-test
(ns status-im.activity-center.core-test
(:require [cljs.test :refer [deftest is testing]]
[day8.re-frame.test :as rf-test]
[re-frame.core :as rf]
[status-im.activity-center.core :as activity-center]
[status-im.activity-center.notification-types :as types]
[status-im.constants :as constants]
[status-im.ethereum.json-rpc :as json-rpc]
status-im.events
[test-helpers.unit :as h]
[status-im2.contexts.activity-center.events :as activity-center]
[status-im2.contexts.activity-center.notification-types :as types]
[utils.re-frame :as rf]))
(h/use-log-fixture)
[status-im.test-helpers :as h]))
(def notification-id "0x1")
@@ -18,60 +18,30 @@
(defn test-log-on-failure
[{:keys [before-test notification-id event action]}]
(h/run-test-sync
(rf-test/run-test-sync
(setup)
(when before-test
(before-test))
(h/stub-fx-with-callbacks :json-rpc/call :on-error (constantly :fake-error))
(h/using-log-test-appender
(fn [logs]
(when before-test
(before-test))
(h/stub-fx-with-callbacks ::json-rpc/call :on-error (constantly :fake-error))
(rf/dispatch event)
(rf/dispatch event)
(is (= {:args [(str "Failed to " action)
{:notification-id notification-id
:error :fake-error}]
:level :warn}
(last @h/logs)))))
(is (= {:args [(str "Failed to " action)
{:notification-id notification-id
:error :fake-error}]
:level :warn}
(last @logs)))))))
;;;; Misc
(deftest open-activity-center-test
(testing "opens the activity center with filters enabled"
(h/run-test-sync
(let [spy-queue (atom [])]
(setup)
(h/spy-fx spy-queue :show-popover)
(rf/dispatch [:activity-center/open
{:filter-type types/contact-request
:filter-status :unread}])
(is (= {:status :unread
:type types/contact-request}
(get-in (h/db) [:activity-center :filter])))
(is (= [{:id :show-popover :args nil}]
@spy-queue)))))
(testing "opens the activity center without custom filters"
(h/run-test-sync
(let [spy-queue (atom [])
existing-filters {:status :all}]
(setup)
(h/spy-fx spy-queue :show-popover)
(rf/dispatch [:test/assoc-in [:activity-center :filter] existing-filters])
(rf/dispatch [:activity-center/open])
(is (= existing-filters
(get-in (h/db) [:activity-center :filter])))
(is (= [{:id :show-popover :args nil}]
@spy-queue))))))
(deftest mark-as-read-test
(testing "does nothing if the notification ID cannot be found in the app db"
(h/run-test-sync
(rf-test/run-test-sync
(setup)
(let [spy-queue (atom [])]
(h/spy-fx spy-queue :json-rpc/call)
(h/spy-fx spy-queue ::json-rpc/call)
(let [notifications {types/one-to-one-chat
{:all {:cursor ""
:data [{:id notification-id
@@ -89,14 +59,14 @@
(is (= notifications (get-in (h/db) [:activity-center :notifications])))))))
(testing "marks notifications as read and updates app db"
(h/run-test-sync
(rf-test/run-test-sync
(setup)
(let [notif-1 {:id "0x1" :read true :type types/one-to-one-chat}
notif-2 {:id "0x2" :read false :type types/one-to-one-chat}
notif-3 {:id "0x3" :read false :type types/one-to-one-chat}
new-notif-3 (assoc notif-3 :read true)
new-notif-2 (assoc notif-2 :read true)]
(h/stub-fx-with-callbacks :json-rpc/call :on-success (constantly nil))
(h/stub-fx-with-callbacks ::json-rpc/call :on-success (constantly nil))
(rf/dispatch [:test/assoc-in [:activity-center]
{:notifications {types/one-to-one-chat
{:all {:cursor "" :data [notif-3 notif-2 notif-1]}
@@ -187,13 +157,13 @@
(defn test-contact-verification-event
[{:keys [event expected-rpc-call]}]
(h/run-test-sync
(rf-test/run-test-sync
(setup)
(let [spy-queue (atom [])]
(h/stub-fx-with-callbacks :json-rpc/call
(h/stub-fx-with-callbacks ::json-rpc/call
:on-success
(constantly contact-verification-rpc-response))
(h/spy-fx spy-queue :json-rpc/call)
(h/spy-fx spy-queue ::json-rpc/call)
(rf/dispatch event)
(is (= {types/no-type
@@ -262,7 +232,7 @@
(deftest notifications-reconcile-test
(testing "does nothing when there are no new notifications"
(h/run-test-sync
(rf-test/run-test-sync
(setup)
(let [notifications {types/one-to-one-chat
{:all {:cursor ""
@@ -288,7 +258,7 @@
(is (= notifications (get-in (h/db) [:activity-center :notifications]))))))
(testing "removes dismissed or accepted notifications"
(h/run-test-sync
(rf-test/run-test-sync
(setup)
(let [notif-1 {:id "0x1" :read true :type types/one-to-one-chat}
notif-2 {:id "0x2" :read false :type types/one-to-one-chat}
@@ -321,7 +291,7 @@
(get-in (h/db) [:activity-center :notifications]))))))
(testing "replaces old notifications with newly arrived ones"
(h/run-test-sync
(rf-test/run-test-sync
(setup)
(let [notif-1 {:id "0x1" :read true :type types/one-to-one-chat}
notif-4 {:id "0x4" :read false :type types/private-group-chat}
@@ -351,7 +321,7 @@
(get-in (h/db) [:activity-center :notifications]))))))
(testing "reconciles notifications that switched their read/unread status"
(h/run-test-sync
(rf-test/run-test-sync
(setup)
(let [notif-1 {:id "0x1" :read true :type types/one-to-one-chat}
new-notif-1 (assoc notif-1 :read false)]
@@ -373,7 +343,7 @@
;; Sorting by timestamp and ID is compatible with what the backend does when
;; returning paginated results.
(testing "sorts notifications by timestamp and id in descending order"
(h/run-test-sync
(rf-test/run-test-sync
(setup)
(let [notif-1 {:id "0x1" :read true :type types/one-to-one-chat :timestamp 1}
notif-2 {:id "0x2" :read true :type types/one-to-one-chat :timestamp 1}
@@ -397,52 +367,22 @@
:unread {:cursor "" :data [notif-5 new-notif-4 notif-3]}}}
(get-in (h/db) [:activity-center :notifications])))))))
(deftest remove-pending-contact-request-test
(testing "removes notification from all related filter types and status"
(h/run-test-sync
(setup)
(let [contact-pub-key "0x99"
notif-1 {:id "0x1" :read true :type types/contact-request}
notif-2 {:id "0x2" :read false :type types/contact-request :author contact-pub-key}
notif-3 {:id "0x3" :read false :type types/private-group-chat}
notifications {types/contact-request
{:all {:cursor "" :data [notif-2 notif-1]}
:unread {:cursor "" :data [notif-2]}}
types/private-group-chat
{:unread {:cursor "" :data [notif-3]}}
types/no-type
{:all {:cursor "" :data [notif-3 notif-2 notif-1]}
:unread {:cursor "" :data [notif-2 notif-3]}}}]
(rf/dispatch [:test/assoc-in [:activity-center :notifications] notifications])
(rf/dispatch [:activity-center/remove-pending-contact-request contact-pub-key])
(is (= {types/contact-request
{:all {:cursor "" :data [notif-1]}
:unread {:cursor "" :data []}}
types/private-group-chat
{:unread {:cursor "" :data [notif-3]}}
types/no-type
{:all {:cursor "" :data [notif-3 notif-1]}
:unread {:cursor "" :data [notif-3]}}}
(get-in (h/db) [:activity-center :notifications])))))))
;;;; Notifications fetching and pagination
(deftest notifications-fetch-test
(testing "fetches first page"
(h/run-test-sync
(rf-test/run-test-sync
(setup)
(let [spy-queue (atom [])]
(h/stub-fx-with-callbacks
:json-rpc/call
::json-rpc/call
:on-success
(constantly {:cursor "10"
:notifications [{:id "0x1"
:type types/one-to-one-chat
:read false
:chatId "0x9"}]}))
(h/spy-fx spy-queue :json-rpc/call)
(h/spy-fx spy-queue ::json-rpc/call)
(rf/dispatch [:activity-center.notifications/fetch-first-page
{:filter-type types/one-to-one-chat}])
@@ -466,10 +406,10 @@
(get-in (h/db) [:activity-center :notifications]))))))
(testing "does not fetch next page when pagination cursor reached the end"
(h/run-test-sync
(rf-test/run-test-sync
(setup)
(let [spy-queue (atom [])]
(h/spy-fx spy-queue :json-rpc/call)
(h/spy-fx spy-queue ::json-rpc/call)
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
:unread])
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
@@ -486,10 +426,10 @@
;; about updating the cursor value, but we have to make sure the next page is
;; only fetched if the current cursor is valid.
(testing "does not fetch next page when cursor is nil"
(h/run-test-sync
(rf-test/run-test-sync
(setup)
(let [spy-queue (atom [])]
(h/spy-fx spy-queue :json-rpc/call)
(h/spy-fx spy-queue ::json-rpc/call)
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
:unread])
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
@@ -503,18 +443,18 @@
(is (= [] @spy-queue)))))
(testing "fetches next page when pagination cursor is not empty"
(h/run-test-sync
(rf-test/run-test-sync
(setup)
(let [spy-queue (atom [])]
(h/stub-fx-with-callbacks
:json-rpc/call
::json-rpc/call
:on-success
(constantly {:cursor ""
:notifications [{:id "0x1"
:type types/mention
:read false
:chatId "0x9"}]}))
(h/spy-fx spy-queue :json-rpc/call)
(h/spy-fx spy-queue ::json-rpc/call)
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
:unread])
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
@@ -541,10 +481,10 @@
(get-in (h/db) [:activity-center :notifications]))))))
(testing "does not fetch next page while it is still loading"
(h/run-test-sync
(rf-test/run-test-sync
(setup)
(let [spy-queue (atom [])]
(h/spy-fx spy-queue :json-rpc/call)
(h/spy-fx spy-queue ::json-rpc/call)
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
:all])
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
@@ -560,12 +500,12 @@
(is (= [] @spy-queue)))))
(testing "resets loading flag after an error"
(h/run-test-sync
(rf-test/run-test-sync
(setup)
(let [spy-queue (atom [])]
(h/stub-fx-with-callbacks :json-rpc/call :on-error (constantly :fake-error))
(h/stub-fx-with-callbacks ::json-rpc/call :on-error (constantly :fake-error))
(h/spy-event-fx spy-queue :activity-center.notifications/fetch-error)
(h/spy-fx spy-queue :json-rpc/call)
(h/spy-fx spy-queue ::json-rpc/call)
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
:unread])
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
@@ -597,17 +537,17 @@
(is (= {:method "wakuext_activityCenterNotificationsBy"
:params ["" per-page types/contact-request activity-center/status-unread]}
(-> actual
:json-rpc/call
::json-rpc/call
first
(select-keys [:method :params])))))))
(deftest notifications-fetch-unread-count-test
(testing "fetches total notification count and store in db"
(h/run-test-sync
(rf-test/run-test-sync
(setup)
(let [spy-queue (atom [])]
(h/stub-fx-with-callbacks :json-rpc/call :on-success (constantly 9))
(h/spy-fx spy-queue :json-rpc/call)
(h/stub-fx-with-callbacks ::json-rpc/call :on-success (constantly 9))
(h/spy-fx spy-queue ::json-rpc/call)
(rf/dispatch [:activity-center.notifications/fetch-unread-count])
@@ -1,4 +1,4 @@
(ns status-im2.contexts.activity-center.notification-types)
(ns status-im.activity-center.notification-types)
(def ^:const no-type 0)
(def ^:const one-to-one-chat 1)
@@ -8,6 +8,11 @@
(def ^:const contact-request 5)
(def ^:const contact-verification 10)
;; TODO: Remove this constant once the old Notification Center code is removed.
;; Its value clashes with the new constant `-contact-verification`
;; used in status-go.
(def ^:const contact-request-retracted 6)
;; TODO: Replace with correct enum values once status-go implements them.
(def ^:const admin 66610)
(def ^:const tx 66612)
+7 -7
View File
@@ -7,10 +7,10 @@
[status-im.ethereum.core :as ethereum]
[status-im.ethereum.ens :as ens]
[status-im.ethereum.stateofus :as stateofus]
[i18n.i18n :as i18n]
[status-im.i18n.i18n :as i18n]
[status-im.router.core :as router]
[status-im.utils.db :as utils.db]
[utils.re-frame :as rf]
[status-im.utils.fx :as fx]
[status-im.utils.random :as random]
[status-im.utils.utils :as utils]
[status-im2.navigation.events :as navigation]))
@@ -24,7 +24,7 @@
;;NOTE we want to handle only last resolve
(def resolve-last-id (atom nil))
(rf/defn new-chat-set-new-identity
(fx/defn new-chat-set-new-identity
{:events [:new-chat/set-new-identity]}
[{db :db} new-identity-raw new-ens-name id]
(let [ens-error (and (= new-identity-raw "0x") (not (string/blank? new-ens-name)))]
@@ -61,12 +61,12 @@
new-identity
@resolve-last-id])}})))))))
(rf/defn clear-new-identity
(fx/defn clear-new-identity
{:events [::clear-new-identity ::new-chat-focus]}
[{:keys [db]}]
{:db (dissoc db :contacts/new-identity)})
(rf/defn qr-code-handled
(fx/defn qr-code-handled
{:events [::qr-code-handled]}
[{:keys [db] :as cofx} {:keys [type public-key chat-id data ens-name]}
{:keys [new-contact? nickname] :as opts}]
@@ -81,7 +81,7 @@
validation-result (db/validate-pub-key db chat-key)]
(if-not validation-result
(if new-contact?
(rf/merge cofx
(fx/merge cofx
(contact/add-contact chat-key nickname ens-name)
(navigation/navigate-to-cofx :contacts-list {}))
(chat/start-chat cofx chat-key ens-name))
@@ -93,7 +93,7 @@
(i18n/label :t/can-not-add-yourself))
:on-dismiss #(re-frame/dispatch [:pop-to-root-tab :chat-stack])}})))
(rf/defn qr-code-scanned
(fx/defn qr-code-scanned
{:events [:contact/qr-code-scanned]}
[{:keys [db]} data opts]
{::router/handle-uri {:chain (ethereum/chain-keyword db)
+12 -11
View File
@@ -1,25 +1,26 @@
(ns status-im.backup.core
(:require [re-frame.core :as re-frame]
[utils.re-frame :as rf]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.utils.fx :as fx]
[taoensso.timbre :as log]))
(rf/defn handle-backup-failed
(fx/defn handle-backup-failed
{:events [::backup-failed]}
[{:keys [db]}]
{:db (dissoc db :backup/performing-backup)})
(rf/defn handle-backup-perfomed
(fx/defn handle-backup-perfomed
{:events [::backup-performed]}
[{:keys [db]}]
{:db (dissoc db :backup/performing-backup)})
(rf/defn handle-perform-backup-pressed
(fx/defn handle-perform-backup-pressed
{:events [:multiaccounts.ui/perform-backup-pressed]}
[{:keys [db]}]
{:db (assoc db :backup/performing-backup true)
:json-rpc/call [{:method "wakuext_backupData"
:params []
:on-error #(do
(log/error "failed to perfom backup" %)
(re-frame/dispatch [::backup-failed %]))
:on-success #(re-frame/dispatch [::backup-performed %])}]})
{:db (assoc db :backup/performing-backup true)
::json-rpc/call [{:method "wakuext_backupData"
:params []
:on-error #(do
(log/error "failed to perfom backup" %)
(re-frame/dispatch [::backup-failed %]))
:on-success #(re-frame/dispatch [::backup-performed %])}]})
+15 -15
View File
@@ -1,9 +1,9 @@
(ns status-im.bootnodes.core
(:require [clojure.string :as string]
[re-frame.core :as re-frame]
[i18n.i18n :as i18n]
[status-im.i18n.i18n :as i18n]
[status-im.multiaccounts.update.core :as multiaccounts.update]
[utils.re-frame :as rf]
[status-im.utils.fx :as fx]
[status-im2.navigation.events :as navigation]))
(def address-regex #"enode://[a-zA-Z0-9]+:?(.*)\@\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b:(\d{1,5})")
@@ -19,12 +19,12 @@
:id (string/replace id "-" "")
:name bootnode-name})
(rf/defn fetch
(fx/defn fetch
[cofx id]
(let [network (get-in cofx [:db :networks/current-network])]
(get-in cofx [:db :multiaccount :custom-bootnodes network id])))
(rf/defn set-input
(fx/defn set-input
{:events [:bootnodes.ui/input-changed]}
[{:keys [db]} input-key value]
{:db (update
@@ -38,14 +38,14 @@
:name (string/blank? value)
:url (not (valid-address? value)))})})
(rf/defn edit
(fx/defn edit
{:events [:bootnodes.ui/add-bootnode-pressed]}
[{:keys [db] :as cofx} id]
(let [{:keys [id
address
name]}
(fetch cofx id)
fxs (rf/merge cofx
fxs (fx/merge cofx
(set-input :id id)
(set-input :url (str address))
(set-input :name (str name)))]
@@ -56,11 +56,11 @@
(let [network (:networks/current-network db)]
(get-in db [:multiaccount :custom-bootnodes-enabled? network])))
(rf/defn delete
(fx/defn delete
[{{:keys [multiaccount] :as db} :db :as cofx} id]
(let [network (:networks/current-network db)
new-multiaccount (update-in multiaccount [:custom-bootnodes network] dissoc id)]
(rf/merge cofx
(fx/merge cofx
{:db (assoc db :multiaccount new-multiaccount)}
(multiaccounts.update/multiaccount-update
:custom-bootnodes
@@ -68,7 +68,7 @@
{:success-event (when (custom-bootnodes-in-use? cofx)
[:multiaccounts.update.callback/save-settings-success])}))))
(rf/defn upsert
(fx/defn upsert
{:events [:bootnodes.ui/save-pressed]
:interceptors [(re-frame/inject-cofx :random-id-generator)]}
[{{:bootnodes/keys [manage] :keys [multiaccount] :as db} :db
@@ -86,7 +86,7 @@
[network (:id bootnode)]
bootnode)]
(rf/merge cofx
(fx/merge cofx
{:db (dissoc db :bootnodes/manage)
:dispatch [:navigate-back]}
(multiaccounts.update/multiaccount-update
@@ -95,7 +95,7 @@
{:success-event (when (custom-bootnodes-in-use? cofx)
[:multiaccounts.update.callback/save-settings-success])}))))
(rf/defn toggle-custom-bootnodes
(fx/defn toggle-custom-bootnodes
{:events [:bootnodes.ui/custom-bootnodes-switch-toggled]}
[{:keys [db] :as cofx} value]
(let [current-network (:networks/current-network db)
@@ -106,14 +106,14 @@
(assoc bootnodes-settings current-network value)
{:success-event [:multiaccounts.update.callback/save-settings-success]})))
(rf/defn set-bootnodes-from-qr
(fx/defn set-bootnodes-from-qr
{:events [:bootnodes.callback/qr-code-scanned]}
[cofx url]
(assoc (set-input cofx :url (string/trim url))
:dispatch
[:navigate-back]))
(rf/defn show-delete-bootnode-confirmation
(fx/defn show-delete-bootnode-confirmation
{:events [:bootnodes.ui/delete-pressed]}
[_ bootnode-id]
{:ui/show-confirmation {:title (i18n/label :t/delete-bootnode-title)
@@ -122,9 +122,9 @@
:on-accept #(re-frame/dispatch [:bootnodes.ui/delete-confirmed
bootnode-id])}})
(rf/defn delete-bootnode
(fx/defn delete-bootnode
{:events [:bootnodes.ui/delete-confirmed]}
[cofx bootnode-id]
(rf/merge cofx
(fx/merge cofx
(delete bootnode-id)
(navigation/navigate-back)))
+6 -10
View File
@@ -1,7 +1,7 @@
(ns status-im.bottom-sheet.core
(:require [utils.re-frame :as rf]))
(:require [status-im.utils.fx :as fx]))
(rf/defn show-bottom-sheet
(fx/defn show-bottom-sheet
[{:keys [db]} {:keys [view options]}]
{:show-bottom-sheet nil
:db (assoc db
@@ -9,7 +9,7 @@
:bottom-sheet/view view
:bottom-sheet/options options)})
(rf/defn show-bottom-sheet-event
(fx/defn show-bottom-sheet-event
{:events [:bottom-sheet/show-sheet]}
[cofx view options]
(show-bottom-sheet
@@ -17,12 +17,8 @@
{:view view
:options options}))
(rf/defn hide-bottom-sheet
(fx/defn hide-bottom-sheet
{:events [:bottom-sheet/hide]}
[{:keys [db]}]
{:db (assoc db :bottom-sheet/show? false)})
(rf/defn hide-bottom-sheet-navigation-overlay
{:events [:bottom-sheet/hide-navigation-overlay]}
[{}]
{:hide-bottom-sheet nil})
{:hide-bottom-sheet nil
:db (assoc db :bottom-sheet/show? false)})
+114 -116
View File
@@ -1,37 +1,36 @@
(ns status-im.browser.core
(:require
["eth-phishing-detect" :as eth-phishing-detect]
[clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.bottom-sheet.core :as bottom-sheet]
[status-im.browser.eip3085 :as eip3085]
[status-im.browser.eip3326 :as eip3326]
[status-im.browser.permissions :as browser.permissions]
[status-im.browser.webview-ref :as webview-ref]
[status-im.constants :as constants]
[status-im.ethereum.core :as ethereum]
[status-im.ethereum.ens :as ens]
[i18n.i18n :as i18n]
[status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im.native-module.core :as status]
[status-im.signing.core :as signing]
[status-im.ui.components.list-selection :as list-selection]
[utils.re-frame :as rf]
[status-im.utils.http :as http]
[status-im.utils.platform :as platform]
[status-im.utils.random :as random]
[status-im.utils.types :as types]
[status-im.utils.universal-links.utils :as links]
[status-im2.navigation.events :as navigation]
[taoensso.timbre :as log]
[utils.debounce :as debounce]
[utils.security.core :as security]))
(:require ["eth-phishing-detect" :as eth-phishing-detect]
[clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.bottom-sheet.core :as bottom-sheet]
[status-im.browser.eip3085 :as eip3085]
[status-im.browser.eip3326 :as eip3326]
[status-im.browser.permissions :as browser.permissions]
[status-im.browser.webview-ref :as webview-ref]
[status-im.constants :as constants]
[status-im.ethereum.core :as ethereum]
[status-im.ethereum.ens :as ens]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.i18n.i18n :as i18n]
[status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im.native-module.core :as status]
[status-im.signing.core :as signing]
[status-im.ui.components.list-selection :as list-selection]
[status-im.utils.fx :as fx]
[status-im.utils.http :as http]
[status-im.utils.platform :as platform]
[status-im.utils.random :as random]
[status-im.utils.types :as types]
[status-im.utils.universal-links.utils :as links]
[status-im2.navigation.events :as navigation]
[taoensso.timbre :as log]
[utils.debounce :as debounce]))
(rf/defn update-browser-option
(fx/defn update-browser-option
[{:keys [db]} option-key option-value]
{:db (assoc-in db [:browser/options option-key] option-value)})
(rf/defn update-browser-options
(fx/defn update-browser-options
[{:keys [db]} options]
{:db (update db :browser/options merge options)})
@@ -52,22 +51,22 @@
(when url
(string/starts-with? url "https://")))))
(rf/defn remove-browser
(fx/defn remove-browser
{:events [:browser.ui/remove-browser-pressed]}
[{:keys [db]} browser-id]
{:db (update-in db [:browser/browsers] dissoc browser-id)
:json-rpc/call [{:method "wakuext_deleteBrowser"
:params [browser-id]
:on-success #()}]})
{:db (update-in db [:browser/browsers] dissoc browser-id)
::json-rpc/call [{:method "wakuext_deleteBrowser"
:params [browser-id]
:on-success #()}]})
(rf/defn clear-all-browsers
(fx/defn clear-all-browsers
{:events [:browser.ui/clear-all-browsers-pressed]}
[{:keys [db]}]
{:db (dissoc db :browser/browsers)
:json-rpc/call (for [browser-id (keys (get db :browser/browsers))]
{:method "wakuext_deleteBrowser"
:params [browser-id]
:on-success #()})})
{:db (dissoc db :browser/browsers)
::json-rpc/call (for [browser-id (keys (get db :browser/browsers))]
{:method "wakuext_deleteBrowser"
:params [browser-id]
:on-success #()})})
(defn update-dapp-name
[{:keys [name] :as browser}]
@@ -84,7 +83,7 @@
(re-frame/dispatch [:browser.callback/resolve-ens-multihash-success url])
(re-frame/dispatch [:browser.callback/resolve-ens-multihash-error])))
(rf/defn resolve-url
(fx/defn resolve-url
[{:keys [db]} {:keys [error? resolved-url]}]
(when (not error?)
(let [current-url (get-current-url (get-current-browser db))
@@ -96,69 +95,69 @@
:cb resolve-ens-contenthash-callback}}
{:db (update db :browser/options assoc :url (or resolved-url current-url) :resolving? false)}))))
(rf/defn update-browser
(fx/defn update-browser
[{:keys [db]}
{:keys [browser-id] :as browser}]
(let [updated-browser (-> browser
(update-dapp-name)
(check-if-phishing-url))]
{:db (update-in db
[:browser/browsers browser-id]
merge
updated-browser)
:json-rpc/call [{:method "wakuext_addBrowser"
:params [(select-keys updated-browser
[:browser-id :timestamp :name :dapp? :history
:history-index])]
:on-success #()}]}))
{:db (update-in db
[:browser/browsers browser-id]
merge
updated-browser)
::json-rpc/call [{:method "wakuext_addBrowser"
:params [(select-keys updated-browser
[:browser-id :timestamp :name :dapp? :history
:history-index])]
:on-success #()}]}))
(rf/defn store-bookmark
(fx/defn store-bookmark
{:events [:browser/store-bookmark]}
[{:keys [db]}
{:keys [url] :as bookmark}]
{:db (assoc-in db [:bookmarks/bookmarks url] bookmark)
:json-rpc/call [{:method "wakuext_addBookmark"
:params [bookmark]
:on-success #()}]})
{:db (assoc-in db [:bookmarks/bookmarks url] bookmark)
::json-rpc/call [{:method "wakuext_addBookmark"
:params [bookmark]
:on-success #()}]})
(rf/defn update-bookmark
(fx/defn update-bookmark
{:events [:browser/update-bookmark]}
[{:keys [db] :as cofx}
{:keys [url] :as bookmark}]
(let [old-bookmark (get-in db [:bookmarks/bookmarks url])
new-bookmark (merge old-bookmark bookmark)]
(rf/merge cofx
{:db (assoc-in db [:bookmarks/bookmarks url] new-bookmark)
:json-rpc/call [{:method "wakuext_updateBookmark"
:params [url bookmark]
:on-success #()}]})))
(fx/merge cofx
{:db (assoc-in db [:bookmarks/bookmarks url] new-bookmark)
::json-rpc/call [{:method "wakuext_updateBookmark"
:params [url bookmark]
:on-success #()}]})))
(rf/defn delete-bookmark
(fx/defn delete-bookmark
{:events [:browser/delete-bookmark]}
[{:keys [db] :as cofx}
url]
(let [old-bookmark (get-in db [:bookmarks/bookmarks url])
removed-bookmark (merge old-bookmark {:removed true})]
(rf/merge cofx
{:db (update db :bookmarks/bookmarks dissoc url)
:json-rpc/call [{:method "wakuext_removeBookmark"
:params [url]
:on-success #()}]})))
(fx/merge cofx
{:db (update db :bookmarks/bookmarks dissoc url)
::json-rpc/call [{:method "wakuext_removeBookmark"
:params [url]
:on-success #()}]})))
(defn can-go-back?
[{:keys [history-index]}]
(pos? history-index))
(rf/defn navigate-to-previous-page
(fx/defn navigate-to-previous-page
{:events [:browser.ui/previous-page-button-pressed]}
[cofx]
(let [{:keys [history-index] :as browser} (get-current-browser (:db cofx))]
(when (can-go-back? browser)
(rf/merge cofx
(fx/merge cofx
(update-browser (assoc browser :history-index (dec history-index)))
(resolve-url nil)))))
(rf/defn ignore-unsafe
(fx/defn ignore-unsafe
{:events [:browser/ignore-unsafe]}
[cofx]
(let [browser (get-current-browser (:db cofx))
@@ -169,16 +168,16 @@
[{:keys [history-index history]}]
(< history-index (dec (count history))))
(rf/defn navigate-to-next-page
(fx/defn navigate-to-next-page
{:events [:browser.ui/next-page-button-pressed]}
[cofx]
(let [{:keys [history-index] :as browser} (get-current-browser (:db cofx))]
(when (can-go-forward? browser)
(rf/merge cofx
(fx/merge cofx
(update-browser (assoc browser :history-index (inc history-index)))
(resolve-url nil)))))
(rf/defn update-browser-history
(fx/defn update-browser-history
[cofx browser url]
(let [history-index (:history-index browser)
history (:history browser)]
@@ -190,14 +189,14 @@
:history new-history
:history-index new-index))))))
(rf/defn resolve-ens-multihash-success
(fx/defn resolve-ens-multihash-success
{:events [:browser.callback/resolve-ens-multihash-success]}
[{:keys [db] :as cofx} url]
(let [current-url (get-current-url (get-current-browser db))
host (http/url-host current-url)
path (subs current-url (+ (.indexOf ^js current-url host) (count host)))
gateway url]
(rf/merge cofx
(fx/merge cofx
{:db (-> (update db
:browser/options
assoc
@@ -205,7 +204,7 @@
:resolving? false)
(assoc-in [:browser/options :resolved-ens host] gateway))})))
(rf/defn resolve-ens-multihash-error
(fx/defn resolve-ens-multihash-error
{:events [:browser.callback/resolve-ens-multihash-error]}
[{:keys [db] :as cofx}]
(update-browser-options cofx
@@ -213,27 +212,26 @@
:resolving? false
:error? true}))
(rf/defn handle-browser-error
(fx/defn handle-browser-error
{:events [:browser/error-occured]}
[cofx]
(rf/merge cofx
(fx/merge cofx
(update-browser-option :error? true)
(update-browser-option :loading? false)))
(rf/defn handle-pdf
(fx/defn handle-pdf
[_ url]
(when (and platform/android? (string/ends-with? url ".pdf"))
{:browser/show-web-browser-selection url}))
(rf/defn handle-message-link
(fx/defn handle-message-link
{:events [:browser.ui/message-link-pressed]}
[_ link]
(when (security/safe-link? link)
(if (links/universal-link? link)
{:dispatch [:universal-links/handle-url link]}
{:browser/show-browser-selection link})))
(if (links/universal-link? link)
{:dispatch [:universal-links/handle-url link]}
{:browser/show-browser-selection link}))
(rf/defn update-browser-on-nav-change
(fx/defn update-browser-on-nav-change
[cofx url error?]
(let [browser (get-current-browser (:db cofx))
options (get-in cofx [:db :browser/options])
@@ -251,18 +249,18 @@
(second resolved-ens)
(first resolved-ens)))
url)]
(rf/merge cofx
(fx/merge cofx
(update-browser-history browser resolved-url)
(handle-pdf url)
(resolve-url {:error? error? :resolved-url (when resolved-ens url)}))))))
(rf/defn update-browser-name
(fx/defn update-browser-name
[cofx title]
(let [browser (get-current-browser (:db cofx))]
(when (and browser (not (:dapp? browser)) title (not (string/blank? title)))
(update-browser cofx (assoc browser :name title)))))
(rf/defn navigation-state-changed
(fx/defn navigation-state-changed
{:events [:browser/navigation-state-changed]}
[cofx event error?]
(let [{:strs [url loading title]} (js->clj event)
@@ -270,12 +268,12 @@
(if (links/universal-link? url)
(when-not (and deep-link? platform/ios?) ;; ios webview handles this
{:dispatch [:universal-links/handle-url url]})
(rf/merge cofx
(fx/merge cofx
(update-browser-option :loading? loading)
(update-browser-name title)
(update-browser-on-nav-change url error?)))))
(rf/defn open-url-in-current-browser
(fx/defn open-url-in-current-browser
"Opens a url in the current browser, which mean no new entry is added to the home page
and history of the current browser is updated so that the user can navigate back to the
origin url"
@@ -285,12 +283,12 @@
normalized-url (http/normalize-and-decode-url url)]
(if (links/universal-link? normalized-url)
{:dispatch [:universal-links/handle-url normalized-url]}
(rf/merge cofx
(fx/merge cofx
(update-browser-option :url-editing? false)
(update-browser-history browser normalized-url)
(resolve-url nil)))))
(rf/defn open-url
(fx/defn open-url
"Opens a url in the browser. If a host can be extracted from the url and
there is already a browser for this host, this browser is reused
If the browser is reused, the history is flushed"
@@ -302,7 +300,7 @@
:history [normalized-url]}]
(if (links/universal-link? normalized-url)
{:dispatch [:universal-links/handle-url normalized-url]}
(rf/merge cofx
(fx/merge cofx
{:db (assoc db
:browser/options
{:browser-id (:browser-id browser)})}
@@ -311,12 +309,12 @@
(update-browser browser)
(resolve-url nil)))))
(rf/defn open-existing-browser
(fx/defn open-existing-browser
"Opens an existing browser with it's history"
{:events [:browser.ui/browser-item-selected]}
[{:keys [db] :as cofx} browser-id]
(let [browser (get-in db [:browser/browsers browser-id])]
(rf/merge cofx
(fx/merge cofx
{:db (assoc db
:browser/options
{:browser-id browser-id})}
@@ -324,7 +322,7 @@
(navigation/set-stack-root :browser-stack :browser)
(resolve-url nil))))
(rf/defn web3-error-callback
(fx/defn web3-error-callback
{:events [:browser.dapp/transaction-on-error]}
[_ message-id message]
{:browser/send-to-bridge
@@ -332,7 +330,7 @@
:messageId message-id
:error message}})
(rf/defn dapp-complete-transaction
(fx/defn dapp-complete-transaction
{:events [:browser.dapp/transaction-on-result]}
[_ message-id id result]
;;TODO check and test id
@@ -361,7 +359,7 @@
(ethereum/address? second-param)
[second-param (if typed? first-param (normalize-message first-param))]))))
(rf/defn send-to-bridge
(fx/defn send-to-bridge
{:events [:browser.callback/call-rpc]}
[_ message]
{:browser/send-to-bridge message})
@@ -373,7 +371,7 @@
constants/web3-eth-sign constants/web3-keycard-sign-typed-data}
method))
(rf/defn web3-send-async
(fx/defn web3-send-async
[cofx dapp-name {:keys [method params id] :as payload} message-id]
(let [message? (web3-sign-message? method)
dapps-address (get-in cofx [:db :multiaccount :dapps-address])
@@ -433,7 +431,7 @@
:error %1
:result %2}])]}))))
(rf/defn handle-no-permissions
(fx/defn handle-no-permissions
[cofx {:keys [method id]} message-id]
(if (= method "eth_accounts")
;; eth_accounts returns empty array for compatibility with meta-mask
@@ -459,28 +457,28 @@
(and (permissioned-method method)
(not (some #{constants/dapp-permission-web3} (get-in permissions [dapp-name :permissions]))))))
(rf/defn web3-send-async-read-only
(fx/defn web3-send-async-read-only
[{:keys [db] :as cofx} dapp-name {:keys [method] :as payload} message-id]
(if (has-permissions? db dapp-name method)
(handle-no-permissions cofx payload message-id)
(web3-send-async cofx dapp-name payload message-id)))
(rf/defn handle-scanned-qr-code
(fx/defn handle-scanned-qr-code
{:events [:browser.bridge.callback/qr-code-scanned]}
[cofx data {{:keys [dapp-name permission message-id]} :data}]
(rf/merge (assoc-in cofx [:db :browser/options :yielding-control?] false)
(fx/merge (assoc-in cofx [:db :browser/options :yielding-control?] false)
(browser.permissions/send-response-to-bridge permission message-id true data)
(browser.permissions/process-next-permission dapp-name)
(navigation/navigate-back)))
(rf/defn handle-canceled-qr-code
(fx/defn handle-canceled-qr-code
{:events [:browser.bridge.callback/qr-code-canceled]}
[cofx {{:keys [dapp-name permission message-id]} :data}]
(rf/merge (assoc-in cofx [:db :browser/options :yielding-control?] false)
(fx/merge (assoc-in cofx [:db :browser/options :yielding-control?] false)
(browser.permissions/send-response-to-bridge permission message-id true nil)
(browser.permissions/process-next-permission dapp-name)))
(rf/defn process-bridge-message
(fx/defn process-bridge-message
{:events [:browser/bridge-message-received]}
[{:keys [db] :as cofx} message]
(let [browser (get-current-browser
@@ -557,10 +555,10 @@
message (i18n/label :t/share-dapp-text {:link link})]
(list-selection/open-share {:message message})))
(rf/defn dapps-account-selected
(fx/defn dapps-account-selected
{:events [:dapps-account-selected]}
[{:keys [db] :as cofx} address]
(rf/merge cofx
(fx/merge cofx
{:browser/clear-web-data nil}
(bottom-sheet/hide-bottom-sheet)
(browser.permissions/clear-dapps-permissions)
@@ -570,38 +568,38 @@
{:dispatch [:browser.ui/browser-item-selected
(get-in db [:browser/options :browser-id])]}))))
(rf/defn open-empty-tab
(fx/defn open-empty-tab
{:events [:browser.ui/open-empty-tab]}
[cofx]
(debounce/clear :browser/navigation-state-changed)
(navigation/set-stack-root cofx :browser-stack :empty-tab))
(rf/defn url-input-pressed
(fx/defn url-input-pressed
{:events [:browser.ui/url-input-pressed]}
[cofx _]
(update-browser-option cofx :url-editing? true))
(rf/defn url-input-blured
(fx/defn url-input-blured
{:events [:browser.ui/url-input-blured]}
[cofx]
(update-browser-option cofx :url-editing? false))
(rf/defn lock-pressed
(fx/defn lock-pressed
{:events [:browser.ui/lock-pressed]}
[cofx secure?]
(update-browser-option cofx :show-tooltip (if secure? :secure :not-secure)))
(rf/defn close-tooltip-pressed
(fx/defn close-tooltip-pressed
{:events [:browser.ui/close-tooltip-pressed]}
[cofx]
(update-browser-option cofx :show-tooltip nil))
(rf/defn loading-started
(fx/defn loading-started
{:events [:browser/loading-started]}
[cofx]
(update-browser-options cofx {:error? false :loading? true}))
(rf/defn handle-bookmarks
(fx/defn handle-bookmarks
[{:keys [db]} bookmarks]
(let [changed-bookmarks (reduce (fn [acc {:keys [url] :as bookmark}]
(assoc acc url bookmark))
+13 -12
View File
@@ -4,13 +4,14 @@
(:require [clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.constants :as constants]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.network.core :as network]
[status-im.ui.screens.browser.eip3085.sheet :as sheet]
[utils.re-frame :as rf]
[status-im.utils.fx :as fx]
[status-im.utils.random :as random]
[taoensso.timbre :as log]))
(rf/defn send-success-call-to-bridge
(fx/defn send-success-call-to-bridge
{:events [:eip3085/send-success-call-to-bridge]}
[_ id messageId]
{:browser/send-to-bridge {:type constants/web3-send-async-callback
@@ -19,18 +20,18 @@
:id (int id)
:result nil}}})
(rf/defn allow-permission
(fx/defn allow-permission
{:events [:eip3085.ui/dapp-permission-allowed]}
[{:keys [db] :as cofx} message-id {:keys [new-networks id]}]
{:db (assoc db :networks/networks new-networks)
:json-rpc/call [{:method "settings_saveSetting"
:params [:networks/networks (vals new-networks)]
:on-success #(re-frame/dispatch [:eip3085/send-success-call-to-bridge cofx id
message-id])
:on-error #(log/error "failed to perform settings_saveSetting" %)}]
:dispatch [:bottom-sheet/hide]})
{:db (assoc db :networks/networks new-networks)
::json-rpc/call [{:method "settings_saveSetting"
:params [:networks/networks (vals new-networks)]
:on-success #(re-frame/dispatch [:eip3085/send-success-call-to-bridge cofx id
message-id])
:on-error #(log/error "failed to perform settings_saveSetting" %)}]
:dispatch [:bottom-sheet/hide]})
(rf/defn deny-permission
(fx/defn deny-permission
{:events [:eip3085.ui/dapp-permission-denied]}
[_ message-id _]
{:browser/send-to-bridge {:type constants/web3-send-async-callback
@@ -39,7 +40,7 @@
:message "User rejected the request."}}
:dispatch [:bottom-sheet/hide]})
(rf/defn handle-add-ethereum-chain
(fx/defn handle-add-ethereum-chain
{:events [:eip3085/handle-add-ethereum-chain]}
[{{:networks/keys [networks] :as db} :db :as cofx}
dapp-name id message-id
+3 -3
View File
@@ -4,9 +4,9 @@
(:require [status-im.constants :as constants]
[status-im.ethereum.core :as ethereum]
[status-im.ui.screens.browser.eip3326.sheet :as sheet]
[utils.re-frame :as rf]))
[status-im.utils.fx :as fx]))
(rf/defn deny-permission
(fx/defn deny-permission
{:events [:eip3326.ui/dapp-permission-denied]}
[{:keys [db] :as cofx} message-id _]
{:browser/send-to-bridge {:type constants/web3-send-async-callback
@@ -15,7 +15,7 @@
:message "User rejected the request."}}
:dispatch [:bottom-sheet/hide]})
(rf/defn handle-switch-ethereum-chain
(fx/defn handle-switch-ethereum-chain
{:events [:eip3326/handle-switch-ethereum-chain]}
[{:keys [db] :as cofx} dapp-name id message-id {:keys [chainId] :as params}]
(let [target-chain-id (js/parseInt chainId 16)
+34 -33
View File
@@ -1,8 +1,9 @@
(ns status-im.browser.permissions
(:require [status-im.constants :as constants]
[i18n.i18n :as i18n]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.i18n.i18n :as i18n]
[status-im.qr-scanner.core :as qr-scanner]
[utils.re-frame :as rf]
[status-im.utils.fx :as fx]
[status-im2.navigation.events :as navigation]))
(declare process-next-permission)
@@ -20,18 +21,18 @@
:description (i18n/label :t/allowing-authorizes-this-dapp)
:icon :main-icons/wallet}})
(rf/defn permission-yield-control
(fx/defn permission-yield-control
[{:keys [db] :as cofx} dapp-name permission message-id params]
(cond
(= permission constants/dapp-permission-qr-code)
(rf/merge (assoc-in cofx [:db :browser/options :yielding-control?] true)
(fx/merge (assoc-in cofx [:db :browser/options :yielding-control?] true)
(qr-scanner/scan-qr-code {:handler :browser.bridge.callback/qr-code-scanned
:cancel-handler :browser.bridge.callback/qr-code-canceled
:data {:dapp-name dapp-name
:permission permission
:message-id message-id}}))))
(rf/defn permission-show-permission
(fx/defn permission-show-permission
[{:keys [db] :as cofx} dapp-name permission message-id yield-control?]
{:db (assoc-in db
[:browser/options :show-permission]
@@ -47,7 +48,7 @@
constants/dapp-permission-web3 [(:dapps-address multiaccount)]}
allowed-permission)))
(rf/defn send-response-to-bridge
(fx/defn send-response-to-bridge
"Send response to the bridge. If the permission is allowed, send data associated
with the permission"
[{:keys [db] :as cofx} permission message-id allowed? data]
@@ -58,7 +59,7 @@
allowed?
(assoc :data data))})
(rf/defn update-dapp-permissions
(fx/defn update-dapp-permissions
[{:keys [db]} dapp-name permission allowed?]
(let [dapp-permissions-set (set (get-in db [:dapps/permissions dapp-name :permissions]))
allowed-permissions-set (if allowed?
@@ -66,37 +67,37 @@
(disj dapp-permissions-set permission))
allowed-permissions {:dapp dapp-name
:permissions (vec allowed-permissions-set)}]
{:db (assoc-in db [:dapps/permissions dapp-name] allowed-permissions)
:json-rpc/call [{:method "permissions_addDappPermissions"
:params [allowed-permissions]
:on-success #()}]}))
{:db (assoc-in db [:dapps/permissions dapp-name] allowed-permissions)
::json-rpc/call [{:method "permissions_addDappPermissions"
:params [allowed-permissions]
:on-success #()}]}))
(rf/defn revoke-permissions
(fx/defn revoke-permissions
{:events [:browser/revoke-dapp-permissions]}
[{:keys [db] :as cofx} dapp]
(rf/merge cofx
{:db (update-in db [:dapps/permissions] dissoc dapp)
:json-rpc/call [{:method "permissions_deleteDappPermissions"
:params [dapp]
:on-success #()}]}))
(fx/merge cofx
{:db (update-in db [:dapps/permissions] dissoc dapp)
::json-rpc/call [{:method "permissions_deleteDappPermissions"
:params [dapp]
:on-success #()}]}))
(rf/defn revoke-dapp-permissions
(fx/defn revoke-dapp-permissions
{:events [:dapps/revoke-access]}
[cofx dapp]
(rf/merge cofx
(fx/merge cofx
(revoke-permissions dapp)
(navigation/navigate-back)))
(rf/defn clear-dapps-permissions
(fx/defn clear-dapps-permissions
[{:keys [db]}]
(let [dapp-permissions (keys (:dapps/permissions db))]
{:db (dissoc db :dapps/permissions)
:json-rpc/call (for [dapp dapp-permissions]
{:method "permissions_deleteDappPermissions"
:params [dapp]
:on-success #()})}))
{:db (dissoc db :dapps/permissions)
::json-rpc/call (for [dapp dapp-permissions]
{:method "permissions_deleteDappPermissions"
:params [dapp]
:on-success #()})}))
(rf/defn process-next-permission
(fx/defn process-next-permission
"Process next permission by removing it from pending permissions and prompting user
if there is no pending permissions left, save all granted permissions
and return the result to the bridge"
@@ -112,41 +113,41 @@
(permission-yield-control new-cofx dapp-name permission message-id params)
(permission-show-permission new-cofx dapp-name permission message-id yield-control?)))))))
(rf/defn send-response-and-process-next-permission
(fx/defn send-response-and-process-next-permission
[{:keys [db] :as cofx} dapp-name requested-permission message-id]
(rf/merge cofx
(fx/merge cofx
(send-response-to-bridge requested-permission
message-id
true
(get-permission-data cofx requested-permission))
(process-next-permission dapp-name)))
(rf/defn allow-permission
(fx/defn allow-permission
"Add permission to set of allowed permission and process next permission"
{:events [:browser.permissions.ui/dapp-permission-allowed]}
[{:keys [db] :as cofx}]
(let [{:keys [requested-permission message-id dapp-name yield-control? params]}
(get-in db [:browser/options :show-permission])]
(rf/merge (assoc-in cofx [:db :browser/options :show-permission] nil)
(fx/merge (assoc-in cofx [:db :browser/options :show-permission] nil)
(update-dapp-permissions dapp-name requested-permission true)
(if yield-control?
(permission-yield-control dapp-name requested-permission message-id params)
(send-response-and-process-next-permission dapp-name requested-permission message-id)))))
(rf/defn deny-permission
(fx/defn deny-permission
"Add permission to set of allowed permission and process next permission"
{:events [:browser.permissions.ui/dapp-permission-denied]}
[{:keys [db] :as cofx}]
(let [{:keys [requested-permission message-id dapp-name]} (get-in db
[:browser/options :show-permission])]
(rf/merge (assoc-in cofx [:db :browser/options :show-permission] nil)
(fx/merge (assoc-in cofx [:db :browser/options :show-permission] nil)
(send-response-to-bridge requested-permission
message-id
false
(get-permission-data cofx requested-permission))
(process-next-permission dapp-name))))
(rf/defn process-permission
(fx/defn process-permission
"Process the permission requested by a dapp
If supported permission is already granted, return the result immediatly to the bridge
Otherwise process the first permission which will prompt user"
+94 -93
View File
@@ -3,21 +3,22 @@
[quo.design-system.colors :as colors]
[re-frame.core :as re-frame]
[status-im.add-new.db :as new-public-chat.db]
[status-im.chat.models.delete-message :as delete-message]
[status-im.chat.models.delete-message-for-me :as delete-for-me]
[status-im.chat.models.loading :as loading]
[status-im.chat.models.message-list :as message-list]
[status-im.constants :as constants]
[status-im.data-store.chats :as chats-store]
[status-im.data-store.contacts :as contacts-store]
[i18n.i18n :as i18n]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.i18n.i18n :as i18n]
[status-im.mailserver.core :as mailserver]
[status-im.multiaccounts.model :as multiaccounts.model]
[status-im.ui.screens.chat.state :as chat.state]
[status-im.utils.clocks :as utils.clocks]
[utils.re-frame :as rf]
[status-im.utils.fx :as fx]
[status-im.utils.types :as types]
[status-im.utils.utils :as utils]
[status-im2.contexts.chat.messages.delete-message-for-me.events :as delete-for-me]
[status-im2.contexts.chat.messages.delete-message.events :as delete-message]
[status-im2.navigation.events :as navigation]
[taoensso.timbre :as log]))
@@ -112,14 +113,14 @@
(fn [val]
(and (not (get-in db [:chats (:chat-id val)])) (:public? val))))
(rf/defn leave-removed-chat
(fx/defn leave-removed-chat
[{{:keys [view-id current-chat-id chats]} :db
:as cofx}]
(when (and (= view-id :chat)
(not (contains? chats current-chat-id)))
(navigation/navigate-back cofx)))
(rf/defn ensure-chats
(fx/defn ensure-chats
"Add chats to db and update"
[{:keys [db] :as cofx} chats]
(let [{:keys [all-chats chats-home-list removed-chats]}
@@ -136,7 +137,7 @@
:chats-home-list #{}
:removed-chats #{}}
(map (map-chats cofx) chats))]
(rf/merge
(fx/merge
cofx
(merge {:db (-> db
(update :chats merge all-chats)
@@ -149,7 +150,7 @@
(get-in db [:multiaccount :remote-push-notifications-enabled?])]}))
leave-removed-chat)))
(rf/defn clear-history
(fx/defn clear-history
"Clears history of the particular chat"
[{:keys [db] :as cofx} chat-id remove-chat?]
(let [{:keys [last-message public?
@@ -175,40 +176,40 @@
:deleted-at-clock-value last-message-clock-value})
chats))))}))
(rf/defn clear-history-handler
(fx/defn clear-history-handler
"Clears history of the particular chat"
{:events [:chat.ui/clear-history]}
[{:keys [db] :as cofx} chat-id remove-chat?]
(rf/merge cofx
{:db db
:json-rpc/call [{:method "wakuext_clearHistory"
:params [{:id chat-id}]
:on-success #(re-frame/dispatch [::history-cleared chat-id %])
:on-error #(log/error "failed to clear history " chat-id %)}]}
(fx/merge cofx
{:db db
::json-rpc/call [{:method "wakuext_clearHistory"
:params [{:id chat-id}]
:on-success #(re-frame/dispatch [::history-cleared chat-id %])
:on-error #(log/error "failed to clear history " chat-id %)}]}
(clear-history chat-id remove-chat?)))
(rf/defn chat-deactivated
(fx/defn chat-deactivated
{:events [::chat-deactivated]}
[_ chat-id]
(log/debug "chat deactivated" chat-id))
(rf/defn deactivate-chat
(fx/defn deactivate-chat
"Deactivate chat in db, no side effects"
[{:keys [db now] :as cofx} chat-id]
(rf/merge
(fx/merge
cofx
{:db (-> (if (get-in db [:chats chat-id :muted])
(assoc-in db [:chats chat-id :active] false)
(update db :chats dissoc chat-id))
(update :chats-home-list disj chat-id)
(assoc :current-chat-id nil))
:json-rpc/call [{:method "wakuext_deactivateChat"
:params [{:id chat-id}]
:on-success #(re-frame/dispatch [::chat-deactivated chat-id])
:on-error #(log/error "failed to create public chat" chat-id %)}]}
{:db (-> (if (get-in db [:chats chat-id :muted])
(assoc-in db [:chats chat-id :active] false)
(update db :chats dissoc chat-id))
(update :chats-home-list disj chat-id)
(assoc :current-chat-id nil))
::json-rpc/call [{:method "wakuext_deactivateChat"
:params [{:id chat-id}]
:on-success #(re-frame/dispatch [::chat-deactivated chat-id])
:on-error #(log/error "failed to create public chat" chat-id %)}]}
(clear-history chat-id true)))
(rf/defn offload-messages
(fx/defn offload-messages
{:events [:offload-messages]}
[{:keys [db]} chat-id]
(merge {:db (-> db
@@ -218,52 +219,52 @@
(when (and (= chat-id constants/timeline-chat-id) (= (:view-id db) :status))
{:dispatch [:init-timeline-chat]})))
(rf/defn close-chat
(fx/defn close-chat
{:events [:close-chat]}
[{:keys [db] :as cofx}]
(when-let [chat-id (:current-chat-id db)]
(chat.state/reset-visible-item)
(rf/merge cofx
(fx/merge cofx
{:db (dissoc db :current-chat-id)}
(delete-for-me/sync-all)
(delete-message/send-all)
(offload-messages chat-id))))
(rf/defn force-close-chat
(fx/defn force-close-chat
[{:keys [db] :as cofx} chat-id]
(do
(chat.state/reset-visible-item)
(rf/merge cofx
(fx/merge cofx
{:db (dissoc db :current-chat-id)}
(offload-messages chat-id))))
(rf/defn remove-chat
(fx/defn remove-chat
"Removes chat completely from app, producing all necessary effects for that"
{:events [:chat.ui/remove-chat]}
[{:keys [db now] :as cofx} chat-id]
(rf/merge cofx
(fx/merge cofx
{:clear-message-notifications
[[chat-id] (get-in db [:multiaccount :remote-push-notifications-enabled?])]}
(deactivate-chat chat-id)
(offload-messages chat-id)))
(rf/defn show-more-chats
(fx/defn show-more-chats
{:events [:chat.ui/show-more-chats]}
[{:keys [db]}]
(when (< (:home-items-show-number db) (count (:chats db)))
{:db (update db :home-items-show-number + 40)}))
(rf/defn preload-chat-data
(fx/defn preload-chat-data
"Takes chat-id and coeffects map, returns effects necessary when navigating to chat"
{:events [:chat.ui/preload-chat-data]}
[cofx chat-id]
(loading/load-messages cofx chat-id))
(rf/defn navigate-to-chat
(fx/defn navigate-to-chat
"Takes coeffects map and chat-id, returns effects necessary for navigation and preloading data"
{:events [:chat.ui/navigate-to-chat]}
[{db :db :as cofx} chat-id]
(rf/merge cofx
(fx/merge cofx
{:dispatch [:navigate-to :chat]}
(navigation/change-tab :chat)
(when-not (= (:view-id db) :community)
@@ -276,11 +277,11 @@
#(when (group-chat? cofx chat-id)
(loading/load-chat % chat-id))))
(rf/defn navigate-to-chat-nav2
(fx/defn navigate-to-chat-nav2
"Takes coeffects map and chat-id, returns effects necessary for navigation and preloading data"
{:events [:chat.ui/navigate-to-chat-nav2]}
[{db :db :as cofx} chat-id from-shell?]
(rf/merge cofx
(fx/merge cofx
{:dispatch [:navigate-to-nav2 :chat chat-id from-shell?]}
(when-not (= (:view-id db) :community)
(navigation/pop-to-root-tab :shell-stack))
@@ -292,13 +293,13 @@
#(when (group-chat? cofx chat-id)
(loading/load-chat % chat-id))))
(rf/defn handle-clear-history-response
(fx/defn handle-clear-history-response
{:events [::history-cleared]}
[{:keys [db]} chat-id response]
(let [chat (chats-store/<-rpc (first (:chats response)))]
{:db (assoc-in db [:chats chat-id] chat)}))
(rf/defn handle-one-to-one-chat-created
(fx/defn handle-one-to-one-chat-created
{:events [::one-to-one-chat-created]}
[{:keys [db]} chat-id response]
(let [chat (chats-store/<-rpc (first (:chats response)))
@@ -313,22 +314,22 @@
(update :chats-home-list conj chat-id))
:dispatch [:chat.ui/navigate-to-chat chat-id]}))
(rf/defn navigate-to-user-pinned-messages
(fx/defn navigate-to-user-pinned-messages
"Takes coeffects map and chat-id, returns effects necessary for navigation and preloading data"
{:events [:chat.ui/navigate-to-pinned-messages]}
[cofx chat-id]
(navigation/navigate-to cofx :chat-pinned-messages {:chat-id chat-id}))
(rf/defn start-chat
(fx/defn start-chat
"Start a chat, making sure it exists"
{:events [:chat.ui/start-chat]}
[{:keys [db] :as cofx} chat-id ens-name]
;; don't allow to open chat with yourself
(when (not= (multiaccounts.model/current-public-key cofx) chat-id)
{:json-rpc/call [{:method "wakuext_createOneToOneChat"
:params [{:id chat-id :ensName ens-name}]
:on-success #(re-frame/dispatch [::one-to-one-chat-created chat-id %])
:on-error #(log/error "failed to create one-to-on chat" chat-id %)}]}))
{::json-rpc/call [{:method "wakuext_createOneToOneChat"
:params [{:id chat-id :ensName ens-name}]
:on-success #(re-frame/dispatch [::one-to-one-chat-created chat-id %])
:on-error #(log/error "failed to create one-to-on chat" chat-id %)}]}))
(defn profile-chat-topic
[public-key]
@@ -338,7 +339,7 @@
[db]
(profile-chat-topic (get-in db [:multiaccount :public-key])))
(rf/defn handle-public-chat-created
(fx/defn handle-public-chat-created
{:events [::public-chat-created]}
[{:keys [db]} chat-id response]
{:db (-> db
@@ -346,14 +347,14 @@
(update :chats-home-list conj chat-id))
:dispatch [:chat.ui/navigate-to-chat chat-id]})
(rf/defn create-public-chat-go
(fx/defn create-public-chat-go
[_ chat-id]
{:json-rpc/call [{:method "wakuext_createPublicChat"
:params [{:id chat-id}]
:on-success #(re-frame/dispatch [::public-chat-created chat-id %])
:on-error #(log/error "failed to create public chat" chat-id %)}]})
{::json-rpc/call [{:method "wakuext_createPublicChat"
:params [{:id chat-id}]
:on-success #(re-frame/dispatch [::public-chat-created chat-id %])
:on-error #(log/error "failed to create public chat" chat-id %)}]})
(rf/defn start-public-chat
(fx/defn start-public-chat
"Starts a new public chat"
{:events [:chat.ui/start-public-chat]}
[cofx topic]
@@ -366,10 +367,10 @@
{:utils/show-popup {:title (i18n/label :t/cant-open-public-chat)
:content (i18n/label :t/invalid-public-chat-topic)}}))
(rf/defn profile-chat-created
(fx/defn profile-chat-created
{:events [::profile-chat-created]}
[{:keys [db] :as cofx} chat-id response navigate-to?]
(rf/merge
(fx/merge
cofx
{:db db}
#(when response
@@ -379,19 +380,19 @@
{:dispatch-n [[:chat.ui/preload-chat-data chat-id]
[:open-modal :profile]]})))
(rf/defn start-profile-chat
(fx/defn start-profile-chat
"Starts a new profile chat"
{:events [:start-profile-chat]}
[cofx profile-public-key navigate-to?]
(let [chat-id (profile-chat-topic profile-public-key)]
(if (active-chat? cofx chat-id)
{:dispatch [::profile-chat-created chat-id nil navigate-to?]}
{:json-rpc/call [{:method "wakuext_createProfileChat"
:params [{:id profile-public-key}]
:on-success #(re-frame/dispatch [::profile-chat-created chat-id % navigate-to?])
:on-error #(log/error "failed to create profile chat" chat-id %)}]})))
{::json-rpc/call [{:method "wakuext_createProfileChat"
:params [{:id profile-public-key}]
:on-success #(re-frame/dispatch [::profile-chat-created chat-id % navigate-to?])
:on-error #(log/error "failed to create profile chat" chat-id %)}]})))
(rf/defn disable-chat-cooldown
(fx/defn disable-chat-cooldown
"Turns off chat cooldown (protection against message spamming)"
{:events [:chat/disable-cooldown]}
[{:keys [db]}]
@@ -405,40 +406,40 @@
(i18n/label :cooldown/warning-message)
#())))
(rf/defn mute-chat-failed
(fx/defn mute-chat-failed
{:events [::mute-chat-failed]}
[{:keys [db] :as cofx} chat-id muted? error]
(log/error "mute chat failed" chat-id error)
{:db (assoc-in db [:chats chat-id :muted] (not muted?))})
(rf/defn mute-chat-toggled-successfully
(fx/defn mute-chat-toggled-successfully
{:events [::mute-chat-toggled-successfully]}
[_ chat-id]
(log/debug "muted chat successfully" chat-id))
(rf/defn mute-chat
(fx/defn mute-chat
{:events [::mute-chat-toggled]}
[{:keys [db] :as cofx} chat-id muted?]
(let [method (if muted? "wakuext_muteChat" "wakuext_unmuteChat")]
{:db (assoc-in db [:chats chat-id :muted] muted?)
:json-rpc/call [{:method method
:params [chat-id]
:on-error #(re-frame/dispatch [::mute-chat-failed chat-id muted? %])
:on-success #(re-frame/dispatch [::mute-chat-toggled-successfully chat-id])}]}))
{:db (assoc-in db [:chats chat-id :muted] muted?)
::json-rpc/call [{:method method
:params [chat-id]
:on-error #(re-frame/dispatch [::mute-chat-failed chat-id muted? %])
:on-success #(re-frame/dispatch [::mute-chat-toggled-successfully chat-id])}]}))
(rf/defn show-profile
(fx/defn show-profile
{:events [:chat.ui/show-profile]}
[{:keys [db] :as cofx} identity ens-name]
(let [my-public-key (get-in db [:multiaccount :public-key])]
(when (not= my-public-key identity)
(rf/merge
(fx/merge
cofx
{:db (-> db
(assoc :contacts/identity identity)
(assoc :contacts/ens-name ens-name))}
(start-profile-chat identity true)))))
(rf/defn clear-history-pressed
(fx/defn clear-history-pressed
{:events [:chat.ui/clear-history-pressed]}
[_ chat-id]
{:ui/show-confirmation
@@ -449,19 +450,19 @@
(re-frame/dispatch [:bottom-sheet/hide])
(re-frame/dispatch [:chat.ui/clear-history chat-id false]))}})
(rf/defn gaps-failed
(fx/defn gaps-failed
{:events [::gaps-failed]}
[{:keys [db]} chat-id gap-ids error]
(log/error "failed to fetch gaps" chat-id gap-ids error)
{:db (dissoc db :mailserver/fetching-gaps-in-progress)})
(rf/defn sync-chat-from-sync-from-failed
(fx/defn sync-chat-from-sync-from-failed
{:events [::sync-chat-from-sync-from-failed]}
[{:keys [db]} chat-id error]
(log/error "failed to sync chat" chat-id error)
{:db (dissoc db :mailserver/fetching-gaps-in-progress)})
(rf/defn sync-chat-from-sync-from-success
(fx/defn sync-chat-from-sync-from-success
{:events [::sync-chat-from-sync-from-success]}
[{:keys [db] :as cofx} chat-id synced-from]
(log/debug "synced success" chat-id synced-from)
@@ -470,44 +471,44 @@
(assoc-in [:chats chat-id :synced-from] synced-from)
(dissoc :mailserver/fetching-gaps-in-progress))})
(rf/defn gaps-filled
(fx/defn gaps-filled
{:events [::gaps-filled]}
[{:keys [db] :as cofx} chat-id message-ids]
(rf/merge
(fx/merge
cofx
{:db (-> db
(update-in [:messages chat-id] (fn [messages] (apply dissoc messages message-ids)))
(dissoc :mailserver/fetching-gaps-in-progress))}
(message-list/rebuild-message-list chat-id)))
(rf/defn fill-gaps
(fx/defn fill-gaps
[cofx chat-id gap-ids]
{:json-rpc/call [{:method "wakuext_fillGaps"
:params [chat-id gap-ids]
:on-success #(re-frame/dispatch [::gaps-filled chat-id gap-ids %])
:on-error #(re-frame/dispatch [::gaps-failed chat-id gap-ids %])}]})
{::json-rpc/call [{:method "wakuext_fillGaps"
:params [chat-id gap-ids]
:on-success #(re-frame/dispatch [::gaps-filled chat-id gap-ids %])
:on-error #(re-frame/dispatch [::gaps-failed chat-id gap-ids %])}]})
(rf/defn sync-chat-from-sync-from
(fx/defn sync-chat-from-sync-from
[cofx chat-id]
(log/debug "syncing chat from sync from")
{:json-rpc/call [{:method "wakuext_syncChatFromSyncedFrom"
:params [chat-id]
:on-success #(re-frame/dispatch [::sync-chat-from-sync-from-success chat-id %])
:on-error #(re-frame/dispatch [::sync-chat-from-sync-from-failed chat-id %])}]})
{::json-rpc/call [{:method "wakuext_syncChatFromSyncedFrom"
:params [chat-id]
:on-success #(re-frame/dispatch [::sync-chat-from-sync-from-success chat-id %])
:on-error #(re-frame/dispatch [::sync-chat-from-sync-from-failed chat-id %])}]})
(rf/defn chat-ui-fill-gaps
(fx/defn chat-ui-fill-gaps
{:events [:chat.ui/fill-gaps]}
[{:keys [db] :as cofx} chat-id gap-ids]
(let [use-status-nodes? (mailserver/fetch-use-mailservers? {:db db})]
(log/info "filling gaps if use-status-nodes = true" chat-id gap-ids)
(when use-status-nodes?
(rf/merge cofx
(fx/merge cofx
{:db (assoc db :mailserver/fetching-gaps-in-progress gap-ids)}
(if (= gap-ids #{:first-gap})
(sync-chat-from-sync-from chat-id)
(fill-gaps chat-id gap-ids))))))
(rf/defn chat-ui-remove-chat-pressed
(fx/defn chat-ui-remove-chat-pressed
{:events [:chat.ui/remove-chat-pressed]}
[_ chat-id]
{:ui/show-confirmation
@@ -518,7 +519,7 @@
(re-frame/dispatch [:bottom-sheet/hide])
(re-frame/dispatch [:chat.ui/remove-chat chat-id]))}})
(rf/defn decrease-unviewed-count
(fx/defn decrease-unviewed-count
{:events [:chat/decrease-unviewed-count]}
[{:keys [db]} chat-id {:keys [count countWithMentions]}]
{:db (-> db
@@ -1,8 +1,11 @@
(ns status-im2.contexts.chat.messages.delete-message.events
(:require [status-im.chat.models.message-list :as message-list]
[utils.datetime :as datetime]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
(ns status-im.chat.models.delete-message
(:require [re-frame.core :as re-frame]
[status-im.chat.models.message-list :as message-list]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.utils.datetime :as datetime]
[status-im.utils.fx :as fx]
[taoensso.encore :as enc]
[taoensso.timbre :as log]))
(defn- update-db-clear-undo-timer
[db chat-id message-id]
@@ -43,7 +46,7 @@
(when (get-in db [:messages chat-id message-id])
(update-in db [:messages chat-id message-id] assoc :deleted? true)))
(rf/defn delete
(fx/defn delete
"Delete message now locally and broadcast after undo time limit timeout"
{:events [:chat.ui/delete-message]}
[{:keys [db]} {:keys [chat-id message-id]} undo-time-limit-ms]
@@ -58,7 +61,7 @@
:message-id message-id}]
:ms undo-time-limit-ms}])))
(rf/defn undo
(fx/defn undo
{:events [:chat.ui/undo-delete-message]}
[{:keys [db]} {:keys [chat-id message-id]}]
(when (get-in db [:messages chat-id message-id])
@@ -66,22 +69,21 @@
{:db (update-db-undo-locally db chat-id message-id)}
chat-id)))
(rf/defn delete-and-send
(fx/defn delete-and-send
{:events [:chat.ui/delete-message-and-send]}
[{:keys [db]} {:keys [message-id chat-id]}]
(when-let [message (get-in db [:messages chat-id message-id])]
(cond-> {:db (update-db-clear-undo-timer db chat-id message-id)
:json-rpc/call [{:method "wakuext_deleteMessageAndSend"
:params [message-id]
:js-response true
:on-error #(log/error "failed to delete message "
{:message-id message-id :error %})
:on-success #(rf/dispatch [:sanitize-messages-and-process-response
%])}]}
(get-in db [:pin-messages chat-id message-id])
(assoc :dispatch
[:pin-message/send-pin-message
{:chat-id chat-id :message-id message-id :pinned false}]))))
(enc/assoc-when
{:db (update-db-clear-undo-timer db chat-id message-id)
::json-rpc/call [{:method "wakuext_deleteMessageAndSend"
:params [message-id]
:js-response true
:on-error #(log/error "failed to delete message "
{:message-id message-id :error %})
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]}
:dispatch
(and (get-in db [:pin-messages chat-id message-id])
[:pin-message/send-pin-message {:chat-id chat-id :message-id message-id :pinned false}]))))
(defn- filter-pending-send-messages
"traverse all messages find not yet synced deleted? messages"
@@ -91,14 +93,14 @@
(map (fn [message] {:chat-id chat-id :message-id (first message)}))
(concat acc)))
(rf/defn send-all
(fx/defn send-all
"Get all deleted messages that not yet synced with status-go and send them"
{:events [:chat.ui/send-all-deleted-messages]}
[{:keys [db] :as cofx}]
(let [pending-send-messages (reduce-kv filter-pending-send-messages [] (:messages db))]
(apply rf/merge cofx (map delete-and-send pending-send-messages))))
(apply fx/merge cofx (map delete-and-send pending-send-messages))))
(rf/defn delete-messages-localy
(fx/defn delete-messages-localy
"Mark messages :deleted? localy in client"
{:events [:chat.ui/delete-messages-localy]}
[{:keys [db]} messages chat-id]
@@ -1,8 +1,10 @@
(ns status-im2.contexts.chat.messages.delete-message-for-me.events
(:require [status-im.chat.models.message-list :as message-list]
[utils.datetime :as datetime]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
(ns status-im.chat.models.delete-message-for-me
(:require [re-frame.core :as re-frame]
[status-im.chat.models.message-list :as message-list]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.utils.datetime :as datetime]
[status-im.utils.fx :as fx]
[taoensso.timbre :as log]))
(defn- update-db-clear-undo-timer
[db chat-id message-id]
@@ -37,7 +39,7 @@
:deleted-for-me-undoable-till)
(update-db-clear-undo-timer db chat-id message-id))))
(rf/defn delete
(fx/defn delete
"Delete message for me now locally and broadcast after undo time limit timeout"
{:events [:chat.ui/delete-message-for-me]}
[{:keys [db]} {:keys [chat-id message-id]} undo-time-limit-ms]
@@ -52,7 +54,7 @@
:message-id message-id}]
:ms undo-time-limit-ms}])))
(rf/defn undo
(fx/defn undo
{:events [:chat.ui/undo-delete-message-for-me]}
[{:keys [db]} {:keys [chat-id message-id]}]
(when (get-in db [:messages chat-id message-id])
@@ -60,17 +62,17 @@
{:db (update-db-undo-locally db chat-id message-id)}
chat-id)))
(rf/defn delete-and-sync
(fx/defn delete-and-sync
{:events [:chat.ui/delete-message-for-me-and-sync]}
[{:keys [db]} {:keys [message-id chat-id]}]
(when (get-in db [:messages chat-id message-id])
{:db (update-db-clear-undo-timer db chat-id message-id)
:json-rpc/call [{:method "wakuext_deleteMessageForMeAndSync"
:params [chat-id message-id]
:js-response true
:on-error #(log/error "failed to delete message for me, message id: "
{:message-id message-id :error %})
:on-success #(rf/dispatch [:sanitize-messages-and-process-response %])}]}))
{:db (update-db-clear-undo-timer db chat-id message-id)
::json-rpc/call [{:method "wakuext_deleteMessageForMeAndSync"
:params [chat-id message-id]
:js-response true
:on-error #(log/error "failed to delete message for me, message id: "
{:message-id message-id :error %})
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]}))
(defn- filter-pending-sync-messages
"traverse all messages find not yet synced deleted-for-me? messages"
@@ -81,9 +83,9 @@
(map (fn [message] {:chat-id chat-id :message-id (first message)}))
(concat acc)))
(rf/defn sync-all
(fx/defn sync-all
"Get all deleted-for-me messages that not yet synced with status-go and sync them"
{:events [:chat.ui/sync-all-deleted-for-me-messages]}
[{:keys [db] :as cofx}]
(let [pending-sync-messages (reduce-kv filter-pending-sync-messages [] (:messages db))]
(apply rf/merge cofx (map delete-and-sync pending-sync-messages))))
(apply fx/merge cofx (map delete-and-sync pending-sync-messages))))
@@ -1,8 +1,7 @@
(ns status-im2.contexts.chat.messages.delete-message-for-me.events-test
(ns status-im.chat.models.delete-message-for-me-test
(:require [cljs.test :refer-macros [deftest is testing]]
[utils.datetime :as datetime]
[status-im2.contexts.chat.messages.delete-message-for-me.events :as
delete-message-for-me]))
[status-im.chat.models.delete-message-for-me :as delete-message-for-me]
[status-im.utils.datetime :as datetime]))
(def mid "message-id")
(def cid "chat-id")
@@ -63,7 +62,7 @@
(let [expected-db {:messages {cid {mid {:id mid}}}}
effects (delete-message-for-me/delete-and-sync {:db db} message)
result-db (:db effects)
rpc-calls (:json-rpc/call effects)]
rpc-calls (:status-im.ethereum.json-rpc/call effects)]
(is (= result-db expected-db))
(is (= (count rpc-calls) 1))
(is (= (-> rpc-calls
@@ -1,7 +1,7 @@
(ns status-im2.contexts.chat.messages.delete-message.events-test
(ns status-im.chat.models.delete-message-test
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im2.contexts.chat.messages.delete-message.events :as delete-message]
[utils.datetime :as datetime]))
[status-im.chat.models.delete-message :as delete-message]
[status-im.utils.datetime :as datetime]))
(def mid "message-id")
(def cid "chat-id")
@@ -61,7 +61,7 @@
(let [expected-db {:messages {cid {mid {:id mid}}}}
effects (delete-message/delete-and-send {:db db} message)
result-db (:db effects)
rpc-calls (:json-rpc/call effects)]
rpc-calls (:status-im.ethereum.json-rpc/call effects)]
(is (= result-db expected-db))
(is (= (count rpc-calls) 1))
(is (= (-> rpc-calls

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