Compare commits

..
Author SHA1 Message Date
Omar Basem 0e08529da0 tests 2022-12-07 19:13:25 +04:00
Omar Basem 95d927c941 lint 2022-12-06 09:38:30 +04:00
Omar Basem 9dfb855090 refactor 2022-12-06 09:38:30 +04:00
Omar Basem 775d9e5457 lint 2022-12-06 09:38:30 +04:00
Omar Basem 294eb34418 updates 2022-12-06 09:38:29 +04:00
373 changed files with 12559 additions and 17782 deletions
+14 -20
View File
@@ -1,20 +1,14 @@
{: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
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 [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
taoensso.tufte/defnp clojure.core/defn}
:linters {: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]}}}
+1 -2
View File
@@ -4,7 +4,7 @@ DEFAULT_NETWORK=mainnet_rpc
DEV_BUILD=1
ETHEREUM_DEV_CLUSTER=1
EXTENSIONS=0
FLEET=status.prod
FLEET=eth.prod
GROUP_CHATS_ENABLED=1
LOG_LEVEL=info
MAILSERVER_CONFIRMATIONS_ENABLED=1
@@ -32,4 +32,3 @@ COMMANDS_ENABLED=1
TWO_MINUTES_SYNCING=1
SWAP_ENABLED=1
STICKERS_TEST_ENABLED=1
LOCAL_PAIRING_ENABLED=1
+1 -2
View File
@@ -2,7 +2,7 @@ DEBUG_WEBVIEW=1
DEFAULT_NETWORK=goerli_rpc
ETHEREUM_DEV_CLUSTER=1
EXTENSIONS=0
FLEET=status.prod
FLEET=eth.staging
GROUP_CHATS_ENABLED=1
LOG_LEVEL=debug
MAILSERVER_CONFIRMATIONS_ENABLED=0
@@ -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 -2
View File
@@ -3,7 +3,7 @@ DEBUG_WEBVIEW=1
DEFAULT_NETWORK=goerli_rpc
ETHEREUM_DEV_CLUSTER=1
EXTENSIONS=0
FLEET=status.prod
FLEET=eth.prod
GROUP_CHATS_ENABLED=1
LOG_LEVEL=debug
MAILSERVER_CONFIRMATIONS_ENABLED=1
@@ -34,4 +34,3 @@ DELETE_MESSAGE_ENABLED=1
TWO_MINUTES_SYNCING=1
ENABLE_QUO_PREVIEW=1
STICKERS_TEST_ENABLED=1
LOCAL_PAIRING_ENABLED=1
+1 -1
View File
@@ -2,7 +2,7 @@ DEBUG_WEBVIEW=1
DEFAULT_NETWORK=mainnet_rpc
ETHEREUM_DEV_CLUSTER=1
EXTENSIONS=0
FLEET=status.prod
FLEET=eth.prod
GROUP_CHATS_ENABLED=1
LOG_LEVEL=info
MAILSERVER_CONFIRMATIONS_ENABLED=1
+1 -1
View File
@@ -2,7 +2,7 @@ DEBUG_WEBVIEW=0
DEFAULT_NETWORK=mainnet_rpc
ETHEREUM_DEV_CLUSTER=0
EXTENSIONS=0
FLEET=status.prod
FLEET=eth.prod
GROUP_CHATS_ENABLED=1
LOG_LEVEL=
MAILSERVER_CONFIRMATIONS_ENABLED=1
-3
View File
@@ -181,6 +181,3 @@ test/appium/tests/users.py
/artifacts
/.calva/
## component-tests
*.log
-47
View File
@@ -1,47 +0,0 @@
;; -*- mode: clojure -*-
;; vi: ft=clojure
{:width 105
:style [;; community style
;; https://github.com/kkinnear/zprint/blob/main/doc/reference.md#community
:community
;; no comma in map
:no-comma
:custom-justify
;; respect all newlines
;; https://github.com/kkinnear/zprint/blob/main/doc/reference.md#respect-nl
:respect-nl
;; respect blank line
;; https://github.com/kkinnear/zprint/blob/main/doc/reference.md#respect-bl
:respect-bl]
:fn-map
{"reg-sub" :arg1-pair
"list-comp" :binding
"defview" :arg1-body
"letsubs" :binding
"testing" :arg1-body
"deftest-sub" :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?]]}]
"set!" "reset!"
"assoc-when" "assoc"
"assoc-some" "assoc"
"conj-when" "conj"
"conj-some" "conj"}
: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}}
:map {:justify? true :justify {:max-variance 1000}}
:pair {:justify? true :justify {:max-variance 1000}}}}}
+5 -17
View File
@@ -285,20 +285,16 @@ endif
# Tests
#--------------
lint: export TARGET := default
lint: export TARGET := clojure
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 && \
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
TARGETS=$$(git diff --diff-filter=d --cached --name-only src && echo src) && \
clojure -Scp "$$CLASS_PATH" -m cljfmt.main check --indents indentation.edn $$TARGETS
lint-fix: export TARGET := default
lint-fix: export TARGET := clojure
lint-fix: ##@test Run code style checks and fix issues
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
clojure -Scp "$$CLASS_PATH" -m cljfmt.main fix src --indents indentation.edn
shadow-server: export TARGET := clojure
shadow-server:##@ Start shadow-cljs in server mode for watching
@@ -309,14 +305,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
+1 -1
View File
@@ -93,6 +93,6 @@ To use a physical iPhone your device UDID must be added to provisioning profiles
- Invite your Apple account to be Developer in Status team.
3. Run a build in XCode using the project from `status-mobile/ios` directory.
- You might see error: `Select a development team in the Signing & Capabilities editor`
- Select `Status Research & Development GmbH` as the development team and rebuild again.
- Select `STATUS HOLDINGS PTE. LTD.` as the development team and rebuild again.
Once build finishes Status should start on your iPhone with its logs in terminal running `make run-metro`.
@@ -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.
+9 -47
View File
@@ -274,6 +274,15 @@ test macros `deftest` and `is`, which are ubiquitous in the Clojure community.
(:require [cljs-time.coerce :as time.coerce]))
```
### Namespace aliases
Prefer the following namespace aliases:
```clojure
[clojure.string :as string]
[taoensso.timbre :as log]
```
### Javascript interop
Use [binaryage/oops](https://github.com/binaryage/cljs-oops) macros instead of
@@ -337,53 +346,6 @@ keywords and concatenating them into a single string.
(i18n/label :t/biometric-auth-error {:code error-code})
```
### Tests
#### Subscription tests
Test [layer-3 subscriptions](https://day8.github.io/re-frame/subscriptions/) by
actually subscribing to them, so reframe's signal graph gets validated too.
```clojure
;; bad
(defn user-recipes
[[current-user all-recipes location]]
...)
(re-frame/reg-sub
:user/recipes
:<- [:current-user]
:<- [:all-recipes]
:<- [:location]
user-recipes)
(deftest user-recipes-test
(testing "builds list of recipes"
(let [current-user {...}
all-recipes {...}
location [...]]
(is (= expected (recipes [current-user all-recipes location]))))))
;; good
(require '[status-im.test-helpers :as h])
(re-frame/reg-sub
:user/recipes
:<- [:current-user]
:<- [:all-recipes]
:<- [:location]
(fn [[current-user all-recipes location]]
...))
(h/deftest-sub :user/recipes
[sub-name]
(testing "builds list of recipes"
(swap! rf-db/app-db assoc
:current-user {...}
:all-recipes {...}
:location [...])
(is (= expected (rf/sub [sub-name])))))
```
## Project Structure
First, the bird's-eye view with some example ClojureScript files:
+1
View File
@@ -0,0 +1 @@
{letsubs [[:inner 0]]}
+23 -46
View File
@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 52;
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
@@ -319,6 +319,7 @@
FBF6AB84EBC2085D28C3CDEF /* Pods-Status-StatusImPR.debug.xcconfig */,
BF23313BEFBC84C6BB7E7677 /* Pods-Status-StatusImPR.release.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
@@ -400,12 +401,12 @@
TargetAttributes = {
00E356ED1AD99517003FC87E = {
CreatedOnToolsVersion = 6.2;
DevelopmentTeam = 8B5X2M6H2Y;
DevelopmentTeam = DTX7Z4U3YA;
ProvisioningStyle = Manual;
TestTargetID = 13B07F861A680F5B00A75B9A;
};
13B07F861A680F5B00A75B9A = {
DevelopmentTeam = 8B5X2M6H2Y;
DevelopmentTeam = DTX7Z4U3YA;
LastSwiftMigration = 1140;
ProvisioningStyle = Manual;
SystemCapabilities = {
@@ -424,7 +425,7 @@
};
};
3AAD2AB924A3A60E0075D594 = {
DevelopmentTeam = 8B5X2M6H2Y;
DevelopmentTeam = DTX7Z4U3YA;
ProvisioningStyle = Manual;
};
};
@@ -756,7 +757,7 @@
BUNDLE_LOADER = "$(TEST_HOST)";
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
DEVELOPMENT_TEAM = 8B5X2M6H2Y;
DEVELOPMENT_TEAM = DTX7Z4U3YA;
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
@@ -767,11 +768,7 @@
);
INFOPLIST_FILE = StatusImTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = "$(inherited)";
PRODUCT_BUNDLE_IDENTIFIER = im.status.ethereum;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -794,18 +791,14 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "iPhone Distribution";
COPY_PHASE_STRIP = NO;
DEVELOPMENT_TEAM = 8B5X2M6H2Y;
DEVELOPMENT_TEAM = DTX7Z4U3YA;
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
);
INFOPLIST_FILE = StatusImTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = "$(inherited)";
PRODUCT_BUNDLE_IDENTIFIER = im.status.ethereum;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -825,12 +818,12 @@
BUNDLE_ID_SUFFIX = .debug;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = StatusIm/StatusIm.entitlements;
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
CUSTOM_PRODUCT_NAME = "Status Debug";
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 8B5X2M6H2Y;
DEVELOPMENT_TEAM = DTX7Z4U3YA;
EXCLUDED_ARCHS = "";
"EXCLUDED_ARCHS[sdk=iphoneos*]" = x86_64;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -864,11 +857,7 @@
);
INFOPLIST_FILE = StatusIm/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = (
/usr/lib/swift,
"$(inherited)",
"@executable_path/Frameworks",
);
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)",
@@ -899,12 +888,12 @@
BUNDLE_ID_SUFFIX = "";
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = StatusIm/StatusIm.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_IDENTITY = "iPhone Distribution";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CUSTOM_PRODUCT_NAME = Status;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 8B5X2M6H2Y;
DEVELOPMENT_TEAM = DTX7Z4U3YA;
EXCLUDED_ARCHS = "";
FRAMEWORK_SEARCH_PATHS = (
"$(PROJECT_DIR)/../modules/react-native-status/ios/RCTStatus",
@@ -930,11 +919,7 @@
);
INFOPLIST_FILE = StatusIm/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = (
/usr/lib/swift,
"$(inherited)",
"@executable_path/Frameworks",
);
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)",
@@ -968,7 +953,7 @@
CODE_SIGN_STYLE = Manual;
CUSTOM_PRODUCT_NAME = "Status Debug";
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 8B5X2M6H2Y;
DEVELOPMENT_TEAM = DTX7Z4U3YA;
FRAMEWORK_SEARCH_PATHS = (
"$(PROJECT_DIR)/../modules/react-native-status/ios/RCTStatus",
"$(inherited)",
@@ -999,11 +984,7 @@
);
INFOPLIST_FILE = StatusImPR/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = (
/usr/lib/swift,
"$(inherited)",
"@executable_path/Frameworks",
);
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)",
@@ -1015,7 +996,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = im.status.ethereum.pr;
PRODUCT_NAME = "Status PR";
PROVISIONING_PROFILE_SPECIFIER = "match Development im.status.ethereum.pr";
PROVISIONING_PROFILE_SPECIFIER = "match AdHoc im.status.ethereum.pr";
SWIFT_OBJC_BRIDGING_HEADER = "StatusIm-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
@@ -1032,12 +1013,12 @@
BUNDLE_ID_SUFFIX = "";
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = StatusImPR/StatusImPR.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_IDENTITY = "iPhone Distribution";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CUSTOM_PRODUCT_NAME = Status;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 8B5X2M6H2Y;
DEVELOPMENT_TEAM = DTX7Z4U3YA;
FRAMEWORK_SEARCH_PATHS = (
"$(PROJECT_DIR)/../modules/react-native-status/ios/RCTStatus",
"$(inherited)",
@@ -1062,11 +1043,7 @@
);
INFOPLIST_FILE = StatusImPR/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = (
/usr/lib/swift,
"$(inherited)",
"@executable_path/Frameworks",
);
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)",
@@ -1119,7 +1096,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
DEVELOPMENT_TEAM = 8B5X2M6H2Y;
DEVELOPMENT_TEAM = DTX7Z4U3YA;
ENABLE_BITCODE = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
@@ -1202,7 +1179,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = YES;
DEVELOPMENT_TEAM = 8B5X2M6H2Y;
DEVELOPMENT_TEAM = DTX7Z4U3YA;
ENABLE_BITCODE = NO;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -940,52 +940,6 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
StatusThreadPoolExecutor.getInstance().execute(r);
}
@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);
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable runnableTask = new Runnable() {
@Override
public void run() {
String res = Statusgo.getConnectionStringForBootstrappingAnotherDevice(jsonConfig.toString());
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(runnableTask);
}
@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);
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable runnableTask = new Runnable() {
@Override
public void run() {
String res = Statusgo.inputConnectionStringForBootstrapping(connectionString,jsonConfig.toString());
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(runnableTask);
}
@ReactMethod
public void hashTypedData(final String data, final Callback callback) {
Log.d(TAG, "hashTypedData");
@@ -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,40 +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]);
}
//////////////////////////////////////////////////////////////////// hashTypedData
RCT_EXPORT_METHOD(hashTypedData:(NSString *)data
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -348,6 +328,7 @@ RCT_EXPORT_METHOD(hashTypedData:(NSString *)data
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// hashTypedDataV4
RCT_EXPORT_METHOD(hashTypedDataV4:(NSString *)data
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -357,6 +338,7 @@ RCT_EXPORT_METHOD(hashTypedDataV4:(NSString *)data
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// sendTransactionWithSignature
RCT_EXPORT_METHOD(sendTransactionWithSignature:(NSString *)txArgsJSON
signature:(NSString *)signature
callback:(RCTResponseSenderBlock)callback) {
@@ -367,6 +349,7 @@ RCT_EXPORT_METHOD(sendTransactionWithSignature:(NSString *)txArgsJSON
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// multiAccountImportMnemonic
RCT_EXPORT_METHOD(multiAccountImportMnemonic:(NSString *)json
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -376,6 +359,7 @@ RCT_EXPORT_METHOD(multiAccountImportMnemonic:(NSString *)json
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// multiAccountDeriveAddresses
RCT_EXPORT_METHOD(multiAccountDeriveAddresses:(NSString *)json
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
@@ -395,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];
@@ -467,6 +452,7 @@ RCT_EXPORT_METHOD(multiAccountDeriveAddresses:(NSString *)json
}
//////////////////////////////////////////////////////////////////// prepareDirAndUpdateConfig
RCT_EXPORT_METHOD(prepareDirAndUpdateConfig:(NSString *)keyUID
config:(NSString *)config
callback:(RCTResponseSenderBlock)callback) {
@@ -478,6 +464,7 @@ RCT_EXPORT_METHOD(prepareDirAndUpdateConfig:(NSString *)keyUID
callback(@[updatedConfig]);
}
//////////////////////////////////////////////////////////////////// saveAccountAndLogin
RCT_EXPORT_METHOD(saveAccountAndLogin:(NSString *)multiaccountData
password:(NSString *)password
settings:(NSString *)settings
@@ -494,6 +481,7 @@ RCT_EXPORT_METHOD(saveAccountAndLogin:(NSString *)multiaccountData
NSLog(@"%@", result);
}
//////////////////////////////////////////////////////////////////// saveAccountAndLoginWithKeycard
RCT_EXPORT_METHOD(saveAccountAndLoginWithKeycard:(NSString *)multiaccountData
password:(NSString *)password
settings:(NSString *)settings
@@ -514,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;
}
@@ -555,6 +543,7 @@ RCT_EXPORT_METHOD(saveAccountAndLoginWithKeycard:(NSString *)multiaccountData
}
}
//////////////////////////////////////////////////////////////////// login
RCT_EXPORT_METHOD(login:(NSString *)accountData
password:(NSString *)password) {
#if DEBUG
@@ -566,6 +555,7 @@ RCT_EXPORT_METHOD(login:(NSString *)accountData
NSLog(@"%@", result);
}
//////////////////////////////////////////////////////////////////// loginWithConfig
RCT_EXPORT_METHOD(loginWithConfig:(NSString *)accountData
password:(NSString *)password
configJSON:(NSString *)configJSON) {
@@ -578,6 +568,7 @@ RCT_EXPORT_METHOD(loginWithConfig:(NSString *)accountData
NSLog(@"%@", result);
}
//////////////////////////////////////////////////////////////////// loginWithKeycard
RCT_EXPORT_METHOD(loginWithKeycard:(NSString *)accountData
password:(NSString *)password
chatKey:(NSString *)chatKey) {
@@ -592,6 +583,7 @@ RCT_EXPORT_METHOD(loginWithKeycard:(NSString *)accountData
NSLog(@"%@", result);
}
//////////////////////////////////////////////////////////////////// logout
RCT_EXPORT_METHOD(logout) {
#if DEBUG
NSLog(@"Logout() method called");
@@ -601,6 +593,7 @@ RCT_EXPORT_METHOD(logout) {
NSLog(@"%@", result);
}
//////////////////////////////////////////////////////////////////// openAccounts
RCT_EXPORT_METHOD(openAccounts:(RCTResponseSenderBlock)callback) {
#if DEBUG
NSLog(@"OpenAccounts() method called");
@@ -614,6 +607,7 @@ RCT_EXPORT_METHOD(openAccounts:(RCTResponseSenderBlock)callback) {
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// verityAccountPassword
RCT_EXPORT_METHOD(verify:(NSString *)address
password:(NSString *)password
callback:(RCTResponseSenderBlock)callback) {
@@ -630,6 +624,7 @@ RCT_EXPORT_METHOD(verify:(NSString *)address
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// verifyDatabasePassword
RCT_EXPORT_METHOD(verifyDatabasePassword:(NSString *)keyUID
password:(NSString *)password
callback:(RCTResponseSenderBlock)callback) {
@@ -640,6 +635,7 @@ RCT_EXPORT_METHOD(verifyDatabasePassword:(NSString *)keyUID
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// changeDatabasePassword
RCT_EXPORT_METHOD(reEncryptDbAndKeystore:(NSString *)keyUID
currentPassword:(NSString *)currentPassword
newPassword:(NSString *)newPassword
@@ -652,6 +648,7 @@ RCT_EXPORT_METHOD(reEncryptDbAndKeystore:(NSString *)keyUID
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// convertToKeycardAccount
RCT_EXPORT_METHOD(convertToKeycardAccount:(NSString *)keyUID
accountData:(NSString *)accountData
settings:(NSString *)settings
@@ -666,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) {
@@ -679,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
@@ -691,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
@@ -703,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
@@ -717,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
@@ -731,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
@@ -743,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
@@ -755,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");
@@ -767,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");
@@ -960,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]);
}
@@ -998,7 +995,7 @@ RCT_EXPORT_METHOD(deactivateKeepAwake)
});
}
#pragma mark - deviceinfo
//// deviceinfo
- (bool) is24Hour
{
+181 -259
View File
@@ -51,45 +51,6 @@
}
},
{
"path": "borkdude/edamame/0.0.11-alpha.28/edamame-0.0.11-alpha.28",
"host": "https://repo.clojars.org",
"pom": {
"sha1": "488f403591739aab5a68ce6e7f82c0e855c23fd6",
"sha256": "1cw712kzza733g55s02ql35q630zd8j8nmnpivcczjkg240jans4"
},
"jar": {
"sha1": "371f3e232e7fdb04b7b2044825eb9822280e8f93",
"sha256": "1iyxq6jypjd875cgdyllknh27mx5fnkyjz80mr927d5i94r0ywj4"
}
},
{
"path": "borkdude/sci.impl.reflector/0.0.1/sci.impl.reflector-0.0.1",
"host": "https://repo.clojars.org",
"pom": {
"sha1": "eb3aff6c7db85d91f7e05b98e06d1354a4fce36c",
"sha256": "1bvr7cvpbvqi7swypzpbfrig16zipwvmg4m47y2x5chs5czwxv15"
},
"jar": {
"sha1": "33dfc86102e0ea400498cbca47572459c1c43b00",
"sha256": "0a5gxmj8kzc01y9bn7l4x7c1v5q9wcbvw5hdr525d3ylsyl6xfkw"
}
},
{
"path": "borkdude/sci/0.2.1-alpha.1/sci-0.2.1-alpha.1",
"host": "https://repo.clojars.org",
"pom": {
"sha1": "516cb8e3a8e430d59d9c3e7a51f9f859e3b249e0",
"sha256": "1nrww29m90q1avaahw7i5418c18crc3cidhbg3ml7h7vjjplsl9y"
},
"jar": {
"sha1": "96e39dcbc3fb3a41c6bf2e628303cf0f534b28a3",
"sha256": "1cjvz85ls982rmx90igv4mcfaa53fj5z29df9mf4jplyig3l9dxb"
}
},
{
"path": "cider/cider-nrepl/0.25.3/cider-nrepl-0.25.3",
"host": "https://repo.clojars.org",
@@ -117,15 +78,15 @@
},
{
"path": "clj-stacktrace/clj-stacktrace/0.2.8/clj-stacktrace-0.2.8",
"path": "cljfmt/cljfmt/0.6.7/cljfmt-0.6.7",
"host": "https://repo.clojars.org",
"pom": {
"sha1": "c5a47e6858344c9fd42eecafc7920a7a18d6126d",
"sha256": "1az0fs9k5xvzl8bz564apd77kqc83nl8sxsrvmswmbmkamvy4hd3"
"sha1": "3d7ac9112908c9c9349adc056d1d06ae2cd33c21",
"sha256": "1q46q43mykn61f032y5734j84gcjca41pwd3iik9xghnzmphy2mn"
},
"jar": {
"sha1": "b0654b98763199ee57182526465d823492d1cc3f",
"sha256": "1029wd82qyxv6ji9pd38m32nqia56h6845lqv4ym4dsz5i201g9a"
"sha1": "91e8afa2c19db72acdef7f881aad905871ba6e69",
"sha256": "0ara83gaqnafjgr2aqz5zqmywdsmhsz483xkkxpiwbn9lfqvidmh"
}
},
@@ -143,15 +104,15 @@
},
{
"path": "cljsjs/react-dom-server/17.0.1-0/react-dom-server-17.0.1-0",
"path": "cljsjs/react/17.0.1-0/react-17.0.1-0",
"host": "https://repo.clojars.org",
"pom": {
"sha1": "ff154f318e1f63061b5bd42d9a38fc8547360cb4",
"sha256": "166ynlrph7rx3nql0c85c37vcbcsnwbjamk71hw74rz451pgnncr"
"sha1": "d5d3681281585d981375fe7bb29362bfcd0d4449",
"sha256": "0rskh6mfq5h0l0wybs68w9v585a6zlnnfgrvsf5k3h51fk2p28da"
},
"jar": {
"sha1": "d5c53eca9f3884402c76268b82e3828223dcbf90",
"sha256": "1530hwqpxzxfjm6dy77whqwjzzx79jpnd9xvnkxy1lnk7l7lj494"
"sha1": "726da8a9305a865df90d81e9a8d58f87ff0ee8bd",
"sha256": "0bfmvw8gk98vdf9azll6041r1jaz1pmb0dyb35cfc8dr3mmr4ag7"
}
},
@@ -169,15 +130,15 @@
},
{
"path": "cljsjs/react/17.0.1-0/react-17.0.1-0",
"path": "cljsjs/react-dom-server/17.0.1-0/react-dom-server-17.0.1-0",
"host": "https://repo.clojars.org",
"pom": {
"sha1": "d5d3681281585d981375fe7bb29362bfcd0d4449",
"sha256": "0rskh6mfq5h0l0wybs68w9v585a6zlnnfgrvsf5k3h51fk2p28da"
"sha1": "ff154f318e1f63061b5bd42d9a38fc8547360cb4",
"sha256": "166ynlrph7rx3nql0c85c37vcbcsnwbjamk71hw74rz451pgnncr"
},
"jar": {
"sha1": "726da8a9305a865df90d81e9a8d58f87ff0ee8bd",
"sha256": "0bfmvw8gk98vdf9azll6041r1jaz1pmb0dyb35cfc8dr3mmr4ag7"
"sha1": "d5c53eca9f3884402c76268b82e3828223dcbf90",
"sha256": "1530hwqpxzxfjm6dy77whqwjzzx79jpnd9xvnkxy1lnk7l7lj494"
}
},
@@ -311,6 +272,32 @@
}
},
{
"path": "com/googlecode/java-diff-utils/diffutils/1.3.0/diffutils-1.3.0",
"host": "https://repo1.maven.org/maven2",
"pom": {
"sha1": "7d5e372ff32c90095800f96d8308c41af0285a41",
"sha256": "1dj7p0m0kwnjvy1y3kq63zb6bw0azjf79i5xbccmy3wv63b1vqrg"
},
"jar": {
"sha1": "7e060dd5b19431e6d198e91ff670644372f60fbd",
"sha256": "1hn5plp7iyl626h8mrra56sysfm2qannj1dapr1m5afwkb24vfk1"
}
},
{
"path": "com/googlecode/json-simple/json-simple/1.1.1/json-simple-1.1.1",
"host": "https://repo1.maven.org/maven2",
"pom": {
"sha1": "5e902aae26ac5c36f6420f689f43333129dd69e2",
"sha256": "10vzlnl8vbjv2jqf818wdb7kgy9c6qjka7fjmmi3vdpg1mcn6pv6"
},
"jar": {
"sha1": "c9ad4a0850ab676c5c64461a05ca524cdfff59f1",
"sha256": "170rflxnqnah0265ik2aylmxkshyqbf2zas9bp2l32xqj9l6jsaf"
}
},
{
"path": "com/google/elemental2/elemental2-core/1.0.0-RC1/elemental2-core-1.0.0-RC1",
"host": "https://repo1.maven.org/maven2",
@@ -441,19 +428,6 @@
}
},
{
"path": "com/googlecode/json-simple/json-simple/1.1.1/json-simple-1.1.1",
"host": "https://repo1.maven.org/maven2",
"pom": {
"sha1": "5e902aae26ac5c36f6420f689f43333129dd69e2",
"sha256": "10vzlnl8vbjv2jqf818wdb7kgy9c6qjka7fjmmi3vdpg1mcn6pv6"
},
"jar": {
"sha1": "c9ad4a0850ab676c5c64461a05ca524cdfff59f1",
"sha256": "170rflxnqnah0265ik2aylmxkshyqbf2zas9bp2l32xqj9l6jsaf"
}
},
{
"path": "com/ibm/icu/icu4j/66.1/icu4j-66.1",
"host": "https://repo1.maven.org/maven2",
@@ -467,6 +441,58 @@
}
},
{
"path": "commons-codec/commons-codec/1.10/commons-codec-1.10",
"host": "https://repo1.maven.org/maven2",
"pom": {
"sha1": "44b9477418d2942d45550f7e7c66c16262062d0e",
"sha256": "1yscxabk7i59vgfjg7c1y3prj39h1d8prnwgxbisc4ni29qdpf5x"
},
"jar": {
"sha1": "4b95f4897fa13f2cd904aee711aeafc0c5295cd8",
"sha256": "0scm6321zz76dc3bs8sy2qyami755lz4lq5455gl67bi9slxyha2"
}
},
{
"path": "commons-fileupload/commons-fileupload/1.4/commons-fileupload-1.4",
"host": "https://repo1.maven.org/maven2",
"pom": {
"sha1": "65112009d674333c1acfafb4e198ff250d710764",
"sha256": "007nyd66fqp3fbrmnsbfp1fpkhmr2lk33qmkp3salqld3xd7qlc8"
},
"jar": {
"sha1": "f95188e3d372e20e7328706c37ef366e5d7859b0",
"sha256": "1xyyl54sfxsdcwxdyq6b0azmr31b4dwqns850jjkw9a9dwrh5v54"
}
},
{
"path": "commons-io/commons-io/2.6/commons-io-2.6",
"host": "https://repo1.maven.org/maven2",
"pom": {
"sha1": "5060835593e5b6ed18c82fc2e782f0a3c30a00b1",
"sha256": "0q4a6fp6xkyd86ikymkyv2plhf9vj8aqvggxg9d1yad2jcw8c8qc"
},
"jar": {
"sha1": "815893df5f31da2ece4040fe0a12fd44b577afaf",
"sha256": "04v5fg53jl9gbn6pyz3l7kbpxv0xjzyasnw6yd1a3hhacq2d6xzq"
}
},
{
"path": "compojure/compojure/1.5.2/compojure-1.5.2",
"host": "https://repo.clojars.org",
"pom": {
"sha1": "9e8da477b6682094d56802cb155291a2acb829bd",
"sha256": "036z64iprypccz03iq7lqxvw99xjh4xlsfmfwbs37pmhfnfmbdnx"
},
"jar": {
"sha1": "0b5258d0616ffc5f64c2b6d95f09de56d24df439",
"sha256": "1s2k05lwnlm9a66mxnsss437i9gp70dny8y2rlfkl090s6mdqsaf"
}
},
{
"path": "com/taoensso/encore/2.105.0/encore-2.105.0",
"host": "https://repo.clojars.org",
@@ -545,58 +571,6 @@
}
},
{
"path": "commons-codec/commons-codec/1.10/commons-codec-1.10",
"host": "https://repo1.maven.org/maven2",
"pom": {
"sha1": "44b9477418d2942d45550f7e7c66c16262062d0e",
"sha256": "1yscxabk7i59vgfjg7c1y3prj39h1d8prnwgxbisc4ni29qdpf5x"
},
"jar": {
"sha1": "4b95f4897fa13f2cd904aee711aeafc0c5295cd8",
"sha256": "0scm6321zz76dc3bs8sy2qyami755lz4lq5455gl67bi9slxyha2"
}
},
{
"path": "commons-fileupload/commons-fileupload/1.4/commons-fileupload-1.4",
"host": "https://repo1.maven.org/maven2",
"pom": {
"sha1": "65112009d674333c1acfafb4e198ff250d710764",
"sha256": "007nyd66fqp3fbrmnsbfp1fpkhmr2lk33qmkp3salqld3xd7qlc8"
},
"jar": {
"sha1": "f95188e3d372e20e7328706c37ef366e5d7859b0",
"sha256": "1xyyl54sfxsdcwxdyq6b0azmr31b4dwqns850jjkw9a9dwrh5v54"
}
},
{
"path": "commons-io/commons-io/2.6/commons-io-2.6",
"host": "https://repo1.maven.org/maven2",
"pom": {
"sha1": "5060835593e5b6ed18c82fc2e782f0a3c30a00b1",
"sha256": "0q4a6fp6xkyd86ikymkyv2plhf9vj8aqvggxg9d1yad2jcw8c8qc"
},
"jar": {
"sha1": "815893df5f31da2ece4040fe0a12fd44b577afaf",
"sha256": "04v5fg53jl9gbn6pyz3l7kbpxv0xjzyasnw6yd1a3hhacq2d6xzq"
}
},
{
"path": "compojure/compojure/1.5.2/compojure-1.5.2",
"host": "https://repo.clojars.org",
"pom": {
"sha1": "9e8da477b6682094d56802cb155291a2acb829bd",
"sha256": "036z64iprypccz03iq7lqxvw99xjh4xlsfmfwbs37pmhfnfmbdnx"
},
"jar": {
"sha1": "0b5258d0616ffc5f64c2b6d95f09de56d24df439",
"sha256": "1s2k05lwnlm9a66mxnsss437i9gp70dny8y2rlfkl090s6mdqsaf"
}
},
{
"path": "crypto-equality/crypto-equality/1.0.0/crypto-equality-1.0.0",
"host": "https://repo.clojars.org",
@@ -675,19 +649,6 @@
}
},
{
"path": "hashp/hashp/0.2.1/hashp-0.2.1",
"host": "https://repo.clojars.org",
"pom": {
"sha1": "32530b9106a64fd5442d2aa27c6da4175d1f1440",
"sha256": "1z94k8c52r87rq3nhc0mqazcxff6d4phi8gy1bgndhiz4nq1mia7"
},
"jar": {
"sha1": "fb501db2eb4a028c1875382d58c17c381aec47b8",
"sha256": "1lwirzb60zvksvkb3arpwi4fk1xnp0x5ljmrca5vf7dq25lmwmsd"
}
},
{
"path": "hiccup/hiccup/1.0.5/hiccup-1.0.5",
"host": "https://repo.clojars.org",
@@ -818,32 +779,6 @@
}
},
{
"path": "mvxcvi/arrangement/1.2.0/arrangement-1.2.0",
"host": "https://repo.clojars.org",
"pom": {
"sha1": "4f19bd291595870162eadd35456a0b2e76444a64",
"sha256": "1qqnl05nwcjnzhlgplcsbckrp8pqvf8n6bf8kr8rz7lz2q4px3vb"
},
"jar": {
"sha1": "036e640bb9e14c2aa95589a45a018eb8358ce3f6",
"sha256": "1m3v1rkpdmv7aym5vq2swh0mf9mg7xppdr3vrl4g6qm3s42c24b7"
}
},
{
"path": "mvxcvi/puget/1.3.1/puget-1.3.1",
"host": "https://repo.clojars.org",
"pom": {
"sha1": "07104dc20a13c7e255fffee4f16de9a6873c187f",
"sha256": "1129rb3qksg7n85j03k6d3sr6868g724097vxids1r4wqj2phxkf"
},
"jar": {
"sha1": "a4f7dcf71ccd8d69d4ecd2b19f3d671dfc169308",
"sha256": "029znh7p8f1h91gri4jhzwqsa0rnn3i34yr3kav55npym2sw5sfp"
}
},
{
"path": "net/cgrand/macrovich/0.2.1/macrovich-0.2.1",
"host": "https://repo.clojars.org",
@@ -949,15 +884,15 @@
},
{
"path": "org/clojure/core.rrb-vector/0.1.2/core.rrb-vector-0.1.2",
"path": "org/clojure/core.rrb-vector/0.1.1/core.rrb-vector-0.1.1",
"host": "https://repo1.maven.org/maven2",
"pom": {
"sha1": "e9336ac820c5a7e07fe0aa431df981cbab6db3e3",
"sha256": "07q9qmxc7ggaxh27imgs34svn4j269rhslbnrs63ahrqzk5bmqlf"
"sha1": "3231642aa1dcf628c864a5f208cd293fbd6a385a",
"sha256": "18kk5sds5lg8r2kidhz9qpgyrvggkj8j4sgfdsmyyl93w3f16lnp"
},
"jar": {
"sha1": "0404feea925608b921b56acd11d3b187a0d33fe4",
"sha256": "13hkx1285f2imqlj6wbgyxki2yg8rmfr49iq1zijxm1cgfx8xyai"
"sha1": "aafb7677ec1e9f344fc834bbbdb91e8ba02af474",
"sha256": "0cqyy1vqrhilgwrdxsibd7360ch3hhwjnbbnzsak38v6i6mg66xl"
}
},
@@ -1000,19 +935,6 @@
}
},
{
"path": "org/clojure/google-closure-library-third-party/0.0-20191016-6ae1f72f/google-closure-library-third-party-0.0-20191016-6ae1f72f",
"host": "https://repo1.maven.org/maven2",
"pom": {
"sha1": "1aa0b32fd70e6644dcb06b88b55630d6bbb6f188",
"sha256": "1wq6gvhlvy3mdhj1cb0w62kclqd7x42dqd0275l40rlla5q5h1z1"
},
"jar": {
"sha1": "4fc15daa4fd1b150e8c6d32755796634446dd86a",
"sha256": "1ykw162gng0lcbxn2f387vv1l4d7hfwyayhxgzqqgi9jx211fzw5"
}
},
{
"path": "org/clojure/google-closure-library/0.0-20191016-6ae1f72f/google-closure-library-0.0-20191016-6ae1f72f",
"host": "https://repo1.maven.org/maven2",
@@ -1026,6 +948,19 @@
}
},
{
"path": "org/clojure/google-closure-library-third-party/0.0-20191016-6ae1f72f/google-closure-library-third-party-0.0-20191016-6ae1f72f",
"host": "https://repo1.maven.org/maven2",
"pom": {
"sha1": "1aa0b32fd70e6644dcb06b88b55630d6bbb6f188",
"sha256": "1wq6gvhlvy3mdhj1cb0w62kclqd7x42dqd0275l40rlla5q5h1z1"
},
"jar": {
"sha1": "4fc15daa4fd1b150e8c6d32755796634446dd86a",
"sha256": "1ykw162gng0lcbxn2f387vv1l4d7hfwyayhxgzqqgi9jx211fzw5"
}
},
{
"path": "org/clojure/spec.alpha/0.2.176/spec.alpha-0.2.176",
"host": "https://repo1.maven.org/maven2",
@@ -1052,19 +987,6 @@
}
},
{
"path": "org/clojure/tools.analyzer.jvm/1.1.0/tools.analyzer.jvm-1.1.0",
"host": "https://repo1.maven.org/maven2",
"pom": {
"sha1": "3f21bdbd549557232eb0bf0965b2beea34004d52",
"sha256": "0cyyhf82pn4mjw4whr4sx3cnxrgsk862dq92gn79yawxr6842i51"
},
"jar": {
"sha1": "027d2ebe594eb5f46d6bcea62593e184937b3011",
"sha256": "0kcfcn12dmyrm7sk1jkrlcb7ws8kazfqziv62mfid62xc095gayz"
}
},
{
"path": "org/clojure/tools.analyzer/1.0.0/tools.analyzer-1.0.0",
"host": "https://repo1.maven.org/maven2",
@@ -1078,6 +1000,19 @@
}
},
{
"path": "org/clojure/tools.analyzer.jvm/1.1.0/tools.analyzer.jvm-1.1.0",
"host": "https://repo1.maven.org/maven2",
"pom": {
"sha1": "3f21bdbd549557232eb0bf0965b2beea34004d52",
"sha256": "0cyyhf82pn4mjw4whr4sx3cnxrgsk862dq92gn79yawxr6842i51"
},
"jar": {
"sha1": "027d2ebe594eb5f46d6bcea62593e184937b3011",
"sha256": "0kcfcn12dmyrm7sk1jkrlcb7ws8kazfqziv62mfid62xc095gayz"
}
},
{
"path": "org/clojure/tools.cli/1.0.194/tools.cli-1.0.194",
"host": "https://repo1.maven.org/maven2",
@@ -1143,19 +1078,6 @@
}
},
{
"path": "org/graalvm/js/js-scriptengine/20.1.0/js-scriptengine-20.1.0",
"host": "https://repo1.maven.org/maven2",
"pom": {
"sha1": "020bac4e8232f0b051e3e6b8033f67653cf9c685",
"sha256": "1avd7kmwh0i6nkcirwl358a6lkpqwara0f5a7qiw1fjivc81qmzq"
},
"jar": {
"sha1": "a4accb1ced82da8d61bd2928f435de8af548dc2b",
"sha256": "03001wa3xs08lw2rz27w5s0fyspyhkiiarqsrq9yf8gfysmnik2s"
}
},
{
"path": "org/graalvm/js/js/20.1.0/js-20.1.0",
"host": "https://repo1.maven.org/maven2",
@@ -1169,6 +1091,19 @@
}
},
{
"path": "org/graalvm/js/js-scriptengine/20.1.0/js-scriptengine-20.1.0",
"host": "https://repo1.maven.org/maven2",
"pom": {
"sha1": "020bac4e8232f0b051e3e6b8033f67653cf9c685",
"sha256": "1avd7kmwh0i6nkcirwl358a6lkpqwara0f5a7qiw1fjivc81qmzq"
},
"jar": {
"sha1": "a4accb1ced82da8d61bd2928f435de8af548dc2b",
"sha256": "03001wa3xs08lw2rz27w5s0fyspyhkiiarqsrq9yf8gfysmnik2s"
}
},
{
"path": "org/graalvm/regex/regex/20.1.0/regex-20.1.0",
"host": "https://repo1.maven.org/maven2",
@@ -1299,6 +1234,19 @@
}
},
{
"path": "org/ow2/asm/asm/7.1/asm-7.1",
"host": "https://repo1.maven.org/maven2",
"pom": {
"sha1": "7e40eb6619fd20bd7d98bf775bfdd810aec87ac7",
"sha256": "101i3pyrq082kjyz413hfqwkjjsyfnk9kx3rqr38ln56mlp78a8g"
},
"jar": {
"sha1": "fa29aa438674ff19d5e1386d2c3527a0267f291e",
"sha256": "1pnlb1ick32bihpzc599xl9ppd07qhls6pm0xaqwrj9cdlmzmcja"
}
},
{
"path": "org/ow2/asm/asm-analysis/7.1/asm-analysis-7.1",
"host": "https://repo1.maven.org/maven2",
@@ -1351,19 +1299,6 @@
}
},
{
"path": "org/ow2/asm/asm/7.1/asm-7.1",
"host": "https://repo1.maven.org/maven2",
"pom": {
"sha1": "7e40eb6619fd20bd7d98bf775bfdd810aec87ac7",
"sha256": "101i3pyrq082kjyz413hfqwkjjsyfnk9kx3rqr38ln56mlp78a8g"
},
"jar": {
"sha1": "fa29aa438674ff19d5e1386d2c3527a0267f291e",
"sha256": "1pnlb1ick32bihpzc599xl9ppd07qhls6pm0xaqwrj9cdlmzmcja"
}
},
{
"path": "org/wildfly/client/wildfly-client-config/1.0.1.Final/wildfly-client-config-1.0.1.Final",
"host": "https://repo1.maven.org/maven2",
@@ -1416,6 +1351,19 @@
}
},
{
"path": "reagent/reagent/1.0.0/reagent-1.0.0",
"host": "https://repo.clojars.org",
"pom": {
"sha1": "93ad5ccb0101108fb0843a3f2c50da28f8fd8336",
"sha256": "1l4xzgc4wbd8b29mhj5j336cf8w1zccaia0w413zf3bnj6jf5m38"
},
"jar": {
"sha1": "a1dd4d40425bb2e1afad0fe72780b43ff394ccfa",
"sha256": "1g607s1ymns85923bvhwrsj0y5czyqqq1jmxx0a58fnsm1b318fa"
}
},
{
"path": "re-com/re-com/2.8.0/re-com-2.8.0",
"host": "https://repo.clojars.org",
@@ -1429,6 +1377,19 @@
}
},
{
"path": "refactor-nrepl/refactor-nrepl/2.5.0/refactor-nrepl-2.5.0",
"host": "https://repo.clojars.org",
"pom": {
"sha1": "0bce30b420249ba7e4b90cbb3e046b4bb5416389",
"sha256": "0zmg5qc8d55pry7832isiwd2q237znfjqjpxchd2hvlpalh5qnva"
},
"jar": {
"sha1": "6bc3441afc94f7ca024e41a864ca75e05df7e207",
"sha256": "0w8hax99y98l53mixxzx2ja0vcnhjv8dnsaz1zj3vqk775ns5w6i"
}
},
{
"path": "re-frame/re-frame/0.12.0/re-frame-0.12.0",
"host": "https://repo.clojars.org",
@@ -1468,32 +1429,6 @@
}
},
{
"path": "reagent/reagent/1.0.0/reagent-1.0.0",
"host": "https://repo.clojars.org",
"pom": {
"sha1": "93ad5ccb0101108fb0843a3f2c50da28f8fd8336",
"sha256": "1l4xzgc4wbd8b29mhj5j336cf8w1zccaia0w413zf3bnj6jf5m38"
},
"jar": {
"sha1": "a1dd4d40425bb2e1afad0fe72780b43ff394ccfa",
"sha256": "1g607s1ymns85923bvhwrsj0y5czyqqq1jmxx0a58fnsm1b318fa"
}
},
{
"path": "refactor-nrepl/refactor-nrepl/2.5.0/refactor-nrepl-2.5.0",
"host": "https://repo.clojars.org",
"pom": {
"sha1": "0bce30b420249ba7e4b90cbb3e046b4bb5416389",
"sha256": "0zmg5qc8d55pry7832isiwd2q237znfjqjpxchd2hvlpalh5qnva"
},
"jar": {
"sha1": "6bc3441afc94f7ca024e41a864ca75e05df7e207",
"sha256": "0w8hax99y98l53mixxzx2ja0vcnhjv8dnsaz1zj3vqk775ns5w6i"
}
},
{
"path": "rewrite-clj/rewrite-clj/0.6.1/rewrite-clj-0.6.1",
"host": "https://repo.clojars.org",
@@ -1508,15 +1443,15 @@
},
{
"path": "rewrite-cljs/rewrite-cljs/0.4.5/rewrite-cljs-0.4.5",
"path": "rewrite-cljs/rewrite-cljs/0.4.4/rewrite-cljs-0.4.4",
"host": "https://repo.clojars.org",
"pom": {
"sha1": "378cd53218027b9a93640dcf64c040665f6c9c6b",
"sha256": "1qrhrgzhqdyqizh0ngnr4zgj9xkfzq0qhsws4pxxra3qc9ikl9cq"
"sha1": "4e7a2542517a5b0231631305461603605bea1592",
"sha256": "050imgkzw5q7200klwrkd4v32xxy1f4aghs8g4ma5dbg4rl4pgcd"
},
"jar": {
"sha1": "c35be115c39dadc71a4de3f584aa8ca295e11257",
"sha256": "1dnw0jhr1hhqz80w3z5d8qdbaqdvgzg4fyhv6lffa51c9ca1dsr1"
"sha1": "dd14a371d0d09eedccb39f01e3edabefb2d1e64f",
"sha256": "0hb07wg71i2bjn8wzs8q6w1nb3jb2s36nhr9kv9mc0gyrr6im244"
}
},
@@ -1648,18 +1583,5 @@
"sha1": "09af0b348e6253dcf9fd567d0d22ffebdea46176",
"sha256": "1qg2iyblykfkzmplc2c46916b9m0h5ad6lxmvrk5qn3pdxqr8vw0"
}
},
{
"path": "zprint/zprint/1.1.1/zprint-1.1.1",
"host": "https://repo.clojars.org",
"pom": {
"sha1": "d7225bdc4978d3a8eac8613187cddd2efb9e39d2",
"sha256": "0yyy31h88zww238c2v4zi9va3pjp7ii76zjggglakd3wm1fdbnck"
},
"jar": {
"sha1": "3bd9bbedb188a66ccf72c1e22819e8e423a6757a",
"sha256": "1bvrarxw0dqvxlhj6gdwrv6mklzh4p79537g293fqlm07f2knfph"
}
}
]
+17 -23
View File
@@ -2,16 +2,13 @@ args4j/args4j/2.0.26/args4j-2.0.26.jar
bidi/bidi/2.1.6/bidi-2.1.6.jar
binaryage/env-config/0.2.2/env-config-0.2.2.jar
binaryage/oops/0.7.0/oops-0.7.0.jar
borkdude/edamame/0.0.11-alpha.28/edamame-0.0.11-alpha.28.jar
borkdude/sci.impl.reflector/0.0.1/sci.impl.reflector-0.0.1.jar
borkdude/sci/0.2.1-alpha.1/sci-0.2.1-alpha.1.jar
cider/cider-nrepl/0.25.3/cider-nrepl-0.25.3.jar
cider/piggieback/0.4.1/piggieback-0.4.1.jar
clj-stacktrace/clj-stacktrace/0.2.8/clj-stacktrace-0.2.8.jar
cljfmt/cljfmt/0.6.7/cljfmt-0.6.7.jar
cljs-bean/cljs-bean/1.3.0/cljs-bean-1.3.0.jar
cljsjs/react-dom-server/17.0.1-0/react-dom-server-17.0.1-0.jar
cljsjs/react-dom/17.0.1-0/react-dom-17.0.1-0.jar
cljsjs/react/17.0.1-0/react-17.0.1-0.jar
cljsjs/react-dom/17.0.1-0/react-dom-17.0.1-0.jar
cljsjs/react-dom-server/17.0.1-0/react-dom-server-17.0.1-0.jar
clout/clout/2.1.2/clout-2.1.2.jar
com/andrewmcveigh/cljs-time/0.5.2/cljs-time-0.5.2.jar
com/bhauman/cljs-test-display/0.1.1/cljs-test-display-0.1.1.jar
@@ -22,6 +19,8 @@ com/cognitect/transit-js/0.8.846/transit-js-0.8.846.jar
com/fasterxml/jackson/core/jackson-core/2.8.7/jackson-core-2.8.7.jar
com/google/code/findbugs/jsr305/3.0.1/jsr305-3.0.1.jar
com/google/code/gson/gson/2.7/gson-2.7.jar
com/googlecode/java-diff-utils/diffutils/1.3.0/diffutils-1.3.0.jar
com/googlecode/json-simple/json-simple/1.1.1/json-simple-1.1.1.jar
com/google/elemental2/elemental2-core/1.0.0-RC1/elemental2-core-1.0.0-RC1.jar
com/google/errorprone/error_prone_annotations/2.3.1/error_prone_annotations-2.3.1.jar
com/google/guava/guava/25.1-jre/guava-25.1-jre.jar
@@ -32,25 +31,23 @@ com/google/jsinterop/base/1.0.0/base-1.0.0.jar
com/google/jsinterop/jsinterop-annotations/1.0.2/jsinterop-annotations-1.0.2.jar
com/google/protobuf/protobuf-java/3.11.1/protobuf-java-3.11.1.jar
com/google/re2j/re2j/1.3/re2j-1.3.jar
com/googlecode/json-simple/json-simple/1.1.1/json-simple-1.1.1.jar
com/ibm/icu/icu4j/66.1/icu4j-66.1.jar
commons-codec/commons-codec/1.10/commons-codec-1.10.jar
commons-fileupload/commons-fileupload/1.4/commons-fileupload-1.4.jar
commons-io/commons-io/2.6/commons-io-2.6.jar
compojure/compojure/1.5.2/compojure-1.5.2.jar
com/taoensso/encore/2.105.0/encore-2.105.0.jar
com/taoensso/timbre/4.10.0/timbre-4.10.0.jar
com/taoensso/truss/1.5.0/truss-1.5.0.jar
com/taoensso/tufte/2.1.0/tufte-2.1.0.jar
com/wsscode/pathom/2.2.31/pathom-2.2.31.jar
com/wsscode/spec-inspec/1.0.0-alpha2/spec-inspec-1.0.0-alpha2.jar
commons-codec/commons-codec/1.10/commons-codec-1.10.jar
commons-fileupload/commons-fileupload/1.4/commons-fileupload-1.4.jar
commons-io/commons-io/2.6/commons-io-2.6.jar
compojure/compojure/1.5.2/compojure-1.5.2.jar
crypto-equality/crypto-equality/1.0.0/crypto-equality-1.0.0.jar
crypto-random/crypto-random/1.2.0/crypto-random-1.2.0.jar
day8/re-frame/test/0.1.5/test-0.1.5.jar
edn-query-language/eql/0.0.9/eql-0.0.9.jar
expound/expound/0.8.5/expound-0.8.5.jar
fipp/fipp/0.6.23/fipp-0.6.23.jar
hashp/hashp/0.2.1/hashp-0.2.1.jar
hiccup/hiccup/1.0.5/hiccup-1.0.5.jar
hickory/hickory/0.7.1/hickory-0.7.1.jar
http-kit/http-kit/2.2.0/http-kit-2.2.0.jar
@@ -61,8 +58,6 @@ javax/servlet/servlet-api/2.5/servlet-api-2.5.jar
javax/xml/bind/jaxb-api/2.3.0/jaxb-api-2.3.0.jar
medley/medley/0.8.2/medley-0.8.2.jar
mvxcvi/alphabase/1.0.0/alphabase-1.0.0.jar
mvxcvi/arrangement/1.2.0/arrangement-1.2.0.jar
mvxcvi/puget/1.3.1/puget-1.3.1.jar
net/cgrand/macrovich/0.2.1/macrovich-0.2.1.jar
nrepl/nrepl/0.7.0/nrepl-0.7.0.jar
org/checkerframework/checker-qual/2.0.0/checker-qual-2.0.0.jar
@@ -71,23 +66,23 @@ org/clojure/clojurescript/1.10.773/clojurescript-1.10.773.jar
org/clojure/core.async/1.3.610/core.async-1.3.610.jar
org/clojure/core.cache/1.0.207/core.cache-1.0.207.jar
org/clojure/core.memoize/1.0.236/core.memoize-1.0.236.jar
org/clojure/core.rrb-vector/0.1.2/core.rrb-vector-0.1.2.jar
org/clojure/core.rrb-vector/0.1.1/core.rrb-vector-0.1.1.jar
org/clojure/core.specs.alpha/0.2.44/core.specs.alpha-0.2.44.jar
org/clojure/data.json/1.0.0/data.json-1.0.0.jar
org/clojure/data.priority-map/1.0.0/data.priority-map-1.0.0.jar
org/clojure/google-closure-library-third-party/0.0-20191016-6ae1f72f/google-closure-library-third-party-0.0-20191016-6ae1f72f.jar
org/clojure/google-closure-library/0.0-20191016-6ae1f72f/google-closure-library-0.0-20191016-6ae1f72f.jar
org/clojure/google-closure-library-third-party/0.0-20191016-6ae1f72f/google-closure-library-third-party-0.0-20191016-6ae1f72f.jar
org/clojure/spec.alpha/0.2.176/spec.alpha-0.2.176.jar
org/clojure/test.check/1.1.0/test.check-1.1.0.jar
org/clojure/tools.analyzer.jvm/1.1.0/tools.analyzer.jvm-1.1.0.jar
org/clojure/tools.analyzer/1.0.0/tools.analyzer-1.0.0.jar
org/clojure/tools.analyzer.jvm/1.1.0/tools.analyzer.jvm-1.1.0.jar
org/clojure/tools.cli/1.0.194/tools.cli-1.0.194.jar
org/clojure/tools.logging/0.4.1/tools.logging-0.4.1.jar
org/clojure/tools.macro/0.1.5/tools.macro-0.1.5.jar
org/clojure/tools.reader/1.3.3/tools.reader-1.3.3.jar
org/codehaus/mojo/animal-sniffer-annotations/1.14/animal-sniffer-annotations-1.14.jar
org/graalvm/js/js-scriptengine/20.1.0/js-scriptengine-20.1.0.jar
org/graalvm/js/js/20.1.0/js-20.1.0.jar
org/graalvm/js/js-scriptengine/20.1.0/js-scriptengine-20.1.0.jar
org/graalvm/regex/regex/20.1.0/regex-20.1.0.jar
org/graalvm/sdk/graal-sdk/20.1.0/graal-sdk-20.1.0.jar
org/graalvm/truffle/truffle-api/20.1.0/truffle-api-20.1.0.jar
@@ -98,23 +93,23 @@ org/jboss/xnio/xnio-api/3.8.0.Final/xnio-api-3.8.0.Final.jar
org/jboss/xnio/xnio-nio/3.8.0.Final/xnio-nio-3.8.0.Final.jar
org/jsoup/jsoup/1.9.2/jsoup-1.9.2.jar
org/msgpack/msgpack/0.6.12/msgpack-0.6.12.jar
org/ow2/asm/asm/7.1/asm-7.1.jar
org/ow2/asm/asm-analysis/7.1/asm-analysis-7.1.jar
org/ow2/asm/asm-commons/7.1/asm-commons-7.1.jar
org/ow2/asm/asm-tree/7.1/asm-tree-7.1.jar
org/ow2/asm/asm-util/7.1/asm-util-7.1.jar
org/ow2/asm/asm/7.1/asm-7.1.jar
org/wildfly/client/wildfly-client-config/1.0.1.Final/wildfly-client-config-1.0.1.Final.jar
org/wildfly/common/wildfly-common/1.5.2.Final/wildfly-common-1.5.2.Final.jar
prismatic/schema/1.1.7/schema-1.1.7.jar
quoin/quoin/0.1.2/quoin-0.1.2.jar
reagent/reagent/1.0.0/reagent-1.0.0.jar
re-com/re-com/2.8.0/re-com-2.8.0.jar
refactor-nrepl/refactor-nrepl/2.5.0/refactor-nrepl-2.5.0.jar
re-frame/re-frame/0.12.0/re-frame-0.12.0.jar
re-frisk-remote/re-frisk-remote/1.6.0/re-frisk-remote-1.6.0.jar
re-frisk/sente/1.15.0/sente-1.15.0.jar
reagent/reagent/1.0.0/reagent-1.0.0.jar
refactor-nrepl/refactor-nrepl/2.5.0/refactor-nrepl-2.5.0.jar
rewrite-clj/rewrite-clj/0.6.1/rewrite-clj-0.6.1.jar
rewrite-cljs/rewrite-cljs/0.4.5/rewrite-cljs-0.4.5.jar
rewrite-cljs/rewrite-cljs/0.4.4/rewrite-cljs-0.4.4.jar
ring-cors/ring-cors/0.1.8/ring-cors-0.1.8.jar
ring/ring-codec/1.1.2/ring-codec-1.1.2.jar
ring/ring-core/1.8.1/ring-core-1.8.1.jar
@@ -125,4 +120,3 @@ thheller/shadow-cljs/2.11.16/shadow-cljs-2.11.16-aot.jar
thheller/shadow-cljsjs/0.0.21/shadow-cljsjs-0.0.21.jar
thheller/shadow-util/0.7.0/shadow-util-0.7.0.jar
viebel/codox-klipse-theme/0.0.1/codox-klipse-theme-0.0.1.jar
zprint/zprint/1.1.1/zprint-1.1.1.jar
-2
View File
@@ -17,8 +17,6 @@ in mkShell {
lsof # used in start-react-native.sh
# build specific utilities
clojure maven watchman
# lint specific utilities
clj-kondo zprint clojure-lsp
# other nice to have stuff
yarn nodejs python27
] # and some special cases
+1 -1
View File
@@ -23,7 +23,7 @@ let
# for calling clojure targets in CI or Makefile
clojure = mkShell {
buildInputs = with pkgs; [ clojure flock maven openjdk ];
buildInputs = with pkgs; [ clojure flock maven openjdk clj-kondo ];
inputsFrom = [ nodejs-sh ];
# CLASSPATH from clojure deps with 'src' appended to find local sources.
shellHook = with pkgs; ''
-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: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 377 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 738 KiB

+107 -110
View File
@@ -1,120 +1,117 @@
;; shadow-cljs configuration
{:source-paths ["src" "test/cljs"]
{:source-paths ["src" "test/cljs"]
:dependencies [[reagent "1.0.0"]
[re-frame "0.12.0"]
[binaryage/oops "0.7.0"]
[com.andrewmcveigh/cljs-time "0.5.2"]
[status-im/timbre "4.10.0-2-status"]
[com.taoensso/encore "2.105.0"]
[hickory "0.7.1"]
[cljs-bean "1.3.0"]
[com.cognitect/transit-cljs "0.8.248"]
[mvxcvi/alphabase "1.0.0"]
;; dev dependencies
[refactor-nrepl "2.5.0"]
[cider/cider-nrepl "0.25.3"]
[cider/piggieback "0.4.1"]
[re-frisk-remote "1.6.0"]
;; routing
[bidi "2.1.6"]
;; test dependencies
[day8.re-frame/test "0.1.5"]
[com.taoensso/tufte "2.1.0"]]
:dependencies [[reagent "1.0.0"]
[re-frame "0.12.0"]
[binaryage/oops "0.7.0"]
[com.andrewmcveigh/cljs-time "0.5.2"]
[status-im/timbre "4.10.0-2-status"]
[com.taoensso/encore "2.105.0"]
[hickory "0.7.1"]
[cljs-bean "1.3.0"]
[com.cognitect/transit-cljs "0.8.248"]
[mvxcvi/alphabase "1.0.0"]
;; dev dependencies
[refactor-nrepl "2.5.0"]
[cider/cider-nrepl "0.25.3"]
[cider/piggieback "0.4.1"]
[re-frisk-remote "1.6.0"]
;; routing
[bidi "2.1.6"]
;; test dependencies
[day8.re-frame/test "0.1.5"]
[com.taoensso/tufte "2.1.0"]
;; linting
[cljfmt/cljfmt "0.6.7"]]
;; port and middleware for repl in development
:nrepl {:port 7888
:middleware [cider.piggieback/wrap-cljs-repl
refactor-nrepl.middleware/wrap-refactor]}
:nrepl {:port 7888
:middleware [cider.piggieback/wrap-cljs-repl
refactor-nrepl.middleware/wrap-refactor]}
;; shadow-cljs web interface
:http {:port 3449
:host "0.0.0.0"}
:http {:port 3449
:host "0.0.0.0"}
:cache-blockers #{status-im.utils.js-resources status-im.ui.components.icons.icons}
:builds
{:mobile
{:target :react-native
:output-dir "app"
:init-fn status-im2.setup.core/init
;; When false, the Shadow-CLJS watcher won't automatically refresh
;; the target files (a.k.a hot reload). When false, you can manually
;; reload by calling `shadow.cljs.devtools.api/watch-compile-all!`.
:devtools {:autobuild #shadow/env ["SHADOW_AUTOBUILD_ENABLED" :default true :as :bool]}
:dev {:devtools {:after-load status-im2.setup.hot-reload/reload
:build-notify status-im2.setup.hot-reload/build-notify
:preloads [re-frisk-remote.preload]}
: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 {:output-feature-set :es5
:closure-defines
{re-frame.trace/trace-enabled? true}
:source-map false
:infer-externs true}
;; if you want to use a real device, set your local ip
;; in the SHADOW_HOST env variable to make sure that
;; it will use the right interface
:local-ip #shadow/env "SHADOW_HOST"}
:chunks {:fleets status-im.fleet.default-fleet/default-fleets
:chats status-im.chat.default-chats/default-chats}
:release
{: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 {:output-feature-set :es6
;;disable for android build as there
;;is an intermittent warning with deftype
:warnings-as-errors false
:infer-externs :auto
:static-fns true
:fn-invoke-direct true
:optimizations :advanced
:js-options {:js-provider :closure}}}}
;; the tests are ran with node, react-native dependencies are mocked
;; by using node --require override.js, which uses the node-library
;; produced by the target :mocks below and redefines node require
;; function to use the mocks instead of the rn libraries
:test
{:output-to "target/test/test.js"
: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-im.chat.models-test$"
:main
status-im.test-runner/main
;; set :ui-driven to true to let shadow-cljs inject node-repl
: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}}
:builds {:mobile
{:target :react-native
:output-dir "app"
:init-fn status-im2.setup.core/init
;; When false, the Shadow-CLJS watcher won't automatically refresh
;; the target files (a.k.a hot reload). When false, you can manually
;; reload by calling `shadow.cljs.devtools.api/watch-compile-all!`.
:devtools {:autobuild #shadow/env ["SHADOW_AUTOBUILD_ENABLED" :default true :as :bool]}
:dev {:devtools {:after-load status-im2.setup.hot-reload/reload
:build-notify status-im2.setup.hot-reload/build-notify
:preloads [re-frisk-remote.preload]}
: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 {:output-feature-set :es5
:closure-defines
{re-frame.trace/trace-enabled? true}
:source-map false
:infer-externs true}
;; if you want to use a real device, set your local ip
;; in the SHADOW_HOST env variable to make sure that
;; it will use the right interface
:local-ip #shadow/env "SHADOW_HOST"}
:chunks {:fleets status-im.fleet.default-fleet/default-fleets
:chats status-im.chat.default-chats/default-chats}
:release
{: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 {:output-feature-set :es6
;;disable for android build as there
;;is an intermittent warning with deftype
:warnings-as-errors false
:infer-externs :auto
:static-fns true
:fn-invoke-direct true
:optimizations :advanced
:js-options {:js-provider :closure}}}}
;; the tests are ran with node, react-native dependencies are mocked
;; by using node --require override.js, which uses the node-library
;; produced by the target :mocks below and redefines node require
;; function to use the mocks instead of the rn libraries
:test
{:output-to "target/test/test.js"
:output-dir "target/test"
:optimizations :simple
:target :node-test
:ns-regexp "status-im.chat.models-test$"
:main status-im.test-runner/main
;; set :ui-driven to true to let shadow-cljs inject node-repl
: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}}
;; mock.js-dependencies is mocking the react-native libraries
;; we build it as a node library so that it can be required by
;; override.js
:mocks
{:target :node-library
:exports {:mocks mocks.js-dependencies/mock}
:output-to "target/mocks/mocks.js"
:output-dir "target/mocks"
:compiler-options {:optimizations :simple
:source-map false}}
:component-test {:target :npm-module
:entries [quo2.core-spec]
:ns-regexp "-component-spec$"
:output-dir "component-spec"
:compiler-options {:warnings-as-errors false
:static-fns false
: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
;; override.js
:mocks
{:target :node-library
:exports {:mocks mocks.js-dependencies/mock}
:output-to "target/mocks/mocks.js"
:output-dir "target/mocks"
:compiler-options {:optimizations :simple
:source-map false}}
:component-test {:target :npm-module
:entries [quo2.core-spec]
:ns-regexp "-component-spec$"
:output-dir "component-spec"
:compiler-options {:warnings-as-errors false
:static-fns false
:infer-externs true}}}}
-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);
}
);
}
+2 -24
View File
@@ -4,20 +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 {}}})
@@ -125,9 +111,7 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return (
(def react-native-shake #js {})
(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}})
:SvgXml #js {:render identity}})
(def react-native-webview #js {:default {}})
(def react-native-audio-toolkit #js {:MediaStates {}})
(def net-info #js {})
@@ -223,10 +207,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 {}
:State #js {:BEGAN nil
:ACTIVE nil
@@ -303,8 +284,6 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return (
(def shell-worklets #js {})
(def record-audio-worklets #js {})
;; Update i18n_resources.cljs
(defn mock [module]
(case module
@@ -352,7 +331,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/record_audio_worklets.js" record-audio-worklets
"./fleets.js" default-fleets
"./chats.js" default-chats
"@walletconnect/client" wallet-connect-client
+1 -1
View File
@@ -34,7 +34,7 @@
(def touchable-opacity (reagent/adapt-react-class (.-TouchableOpacity ^js rn)))
(def touchable-highlight (reagent/adapt-react-class (.-TouchableHighlight ^js rn)))
(def touchable-without-feedback (reagent/adapt-react-class (.-TouchableWithoutFeedback ^js rn)))
(def text-input (reagent/adapt-react-class (.-TextInput ^js rn)))
(def text-input (reagent/adapt-react-class (.-TextInput ^js rn)))
(def keyboard-avoiding-view-class (reagent/adapt-react-class (.-KeyboardAvoidingView ^js rn)))
@@ -2,7 +2,7 @@
(:require [react-native.core :as rn]
[quo2.foundations.colors :as colors]
[quo2.components.markdown.text :as text]
[clojure.string :as string]))
[clojure.string :as clojure-string]))
(def circle-sizes {:small 20
:medium 32
@@ -29,10 +29,10 @@
(let [circle-size (size circle-sizes)
small? (= size :small)
f-name-initial (-> f-name
string/upper-case
clojure-string/upper-case
(subs 0 1))
l-name-initial (-> l-name
string/upper-case
clojure-string/upper-case
(subs 0 1))
circle-color (colors/custom-color color 50 20)
text-color (colors/custom-color-by-theme color 50 60)]
+2 -2
View File
@@ -1,7 +1,7 @@
(ns quo2.components.code.snippet
(:require ["react-native" :as react-native]
[cljs-bean.core :as bean]
[clojure.string :as string]
[clojure.string :as str]
[oops.core :as oops]
[react-native.core :as rn]
[quo2.theme :as theme]
@@ -52,7 +52,7 @@
:style (text-style (get-in node [:properties :className]))}]
(render-nodes children))
;; Remove newlines as we already render each line separately.
(-> value string/trim-newline)))
(-> value str/trim-newline)))
nodes))
(defn- code-block [{:keys [rows line-number-width]}]
@@ -28,9 +28,13 @@
[community-stats {:icon :i/group
:members-count "629.2K" ;;TODO here should be formatted value, use money/format-members from outside this component
:icon-color icon-color}]
[community-stats {:icon :i/lightning
[community-stats {:icon :i/lightning
:members-count "112.1K"
:icon-color icon-color}]]))
:icon-color icon-color}]
(when (= type :card-view)
[community-stats {:icon :i/placeholder
:count 4
:icon-color icon-color}])]))
(defn community-tags [tags]
[rn/view (style/community-tags-container)
@@ -1,34 +0,0 @@
(ns quo2.components.counter.--tests--.counter-component-spec
(:require ["@testing-library/react-native" :as rtl]
[quo2.components.counter.counter :as counter]
[reagent.core :as reagent]))
(defn render-counter
([]
(render-counter {} nil))
([opts value]
(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))))
(js/global.test "renders counter with a string value"
(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))))
(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))))
+1 -2
View File
@@ -38,8 +38,7 @@
1 16
2 20
28)]
[rn/view {:test-ID :counter-component
:accessible true
[rn/view {:accessible true
:accessibility-label accessibility-label
:style (cond-> (merge
{:align-items :center
-18
View File
@@ -1,18 +0,0 @@
(ns quo2.components.dividers.date
(:require [react-native.core :as rn]
[quo2.components.separator :as separator]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]))
(defn date [value]
[rn/view {:margin-vertical 16
:padding-right 8
:padding-left 62}
[text/text {:weight :medium
:accessibility-label :divider-date-text
:size :label
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)
:text-transform :capitalize
:margin-bottom 4}}
value]
[separator/separator]])
@@ -1,63 +0,0 @@
(ns quo2.components.drawers.--tests--.action-drawers-component-spec
(:require ["@testing-library/react-native" :as rtl]
[quo2.components.drawers.action-drawers :as action-drawer]
[reagent.core :as reagent]))
(defn render-action-drawer
([options]
(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))))
(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))))
(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)))))
(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))))
(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))))
@@ -9,16 +9,14 @@
colors/danger-50
(colors/theme-colors colors/neutral-50 colors/neutral-40)))
(def divider
(defn divider []
[rn/view {:style {:border-top-width 1
:border-top-color (colors/theme-colors
colors/neutral-10 colors/neutral-90)
:margin-top 8
:margin-bottom 7
:align-items :center
:flex-direction :row}
:accessible true
:accessibility-label :divider}])
:flex-direction :row}}])
(defn action [{:keys [icon
label
@@ -26,24 +24,21 @@
right-icon
danger?
on-press
add-divider?
accessibility-label] :as action-props}]
add-divider?] :as action-props}]
(when action-props
[:<> {:key label}
(when add-divider? divider)
[rn/touchable-highlight {:accessibility-label accessibility-label
:style {:border-radius 12
(when add-divider? [divider])
[rn/touchable-highlight {:style {:border-radius 12
:height (if sub-label 58 50)
:margin-horizontal 8}
:underlay-color (colors/theme-colors colors/neutral-5 colors/neutral-90)
:on-press on-press}
[rn/view {:style
{:height (if sub-label 58 50)
:margin-horizontal 12
:flex-direction :row}}
[rn/view {:accessibility-label :left-icon-for-action
:accessible true
:style
[rn/view {:style
{:height 20
:margin-top :auto
:margin-bottom :auto
@@ -74,9 +69,7 @@
{:height 20
:margin-top :auto
:margin-bottom :auto
:width 20}
:accessible true
:accessibility-label :right-icon-for-action}
:width 20}}
[icon/icon right-icon
{:color (get-icon-color danger?)
:size 20}]])]]]))
+6 -6
View File
@@ -1,6 +1,6 @@
(ns quo2.components.icons.icons
(:require [clojure.java.io :as io]
[clojure.string :as string]))
[clojure.string :as cstr]))
(def icon-path "./resources/images/icons2/")
@@ -13,17 +13,17 @@
(fn [el]
(let [s (combine-path path el)
k (-> el
(string/replace "_" "-")
(string/replace " " "-")
(string/lower-case)
(cstr/replace "_" "-")
(cstr/replace " " "-")
(cstr/lower-case)
(str size))]
[k `(js/require ~s)])))
(defn get-files [path]
(->> (io/file path)
file-seq
(filter #(string/ends-with? % "png"))
(map #(first (string/split (.getName %) #"@")))
(filter #(cstr/ends-with? % "png"))
(map #(first (cstr/split (.getName %) #"@")))
distinct))
(defn get-icons [size]
@@ -1,11 +1,11 @@
(ns quo2.components.messages.system-message
(:require [quo2.components.avatars.icon-avatar :as icon-avatar]
(:require [react-native.core :as rn]
[react-native.reanimated :as reanimated]
[quo2.theme :as theme]
[quo2.components.avatars.icon-avatar :as icon-avatar]
[quo2.components.avatars.user-avatar :as user-avatar]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]
[quo2.theme :as theme]
[react-native.core :as rn]
[react-native.reanimated :as reanimated]
[utils.string :as utils]))
(def themes-landed {:pinned colors/primary-50-opa-5
@@ -60,19 +60,19 @@
:flex-direction :row}
[rn/view {:align-items :center
:flex-direction :row}
[sm-icon {:icon :main-icons/delete
[sm-icon {:icon :main-icons/delete16
:color :danger
:opacity 5}]
[text/text {:size :paragraph-2
:style {:color (get-color :text)
:margin-right 5}}
(or (get labels label) label (:message-deleted labels))]
(or label (:message-deleted labels))]
[sm-timestamp timestamp-str]]])
(defmethod sm-render :added [{:keys [state mentions timestamp-str labels]}]
[rn/view {:align-items :center
:flex-direction :row}
[sm-icon {:icon :main-icons/add-user
[sm-icon {:icon :main-icons/add-user16
:color :primary
:opacity (if (= state :landed) 0 5)}]
[sm-user-avatar (:image (first mentions))]
@@ -94,7 +94,7 @@
[rn/view {:flex-direction :row
:flex 1
:align-items :center}
[sm-icon {:icon :main-icons/pin
[sm-icon {:icon :main-icons/pin16
:color :primary
:opacity (if (= state :landed) 0 5)}]
[rn/view {:flex-direction :column
+4 -6
View File
@@ -30,12 +30,10 @@
(defn left-section-view [{:keys [on-press icon accessibility-label type] :or {type :grey}}
put-middle-section-on-left?]
[rn/view {:style (when put-middle-section-on-left? {:margin-right 5})}
[button/button {:on-press on-press
:icon true
:type type
:size 32
:accessibility-label accessibility-label}
[rn/view {:style (merge
{:accessibility-label accessibility-label}
(when put-middle-section-on-left? {:margin-right 5}))}
[button/button {:on-press on-press :icon true :type type :size 32}
icon]])
(defn- mid-section-comp
@@ -64,8 +64,7 @@
(map (fn [s]
[rn/view {:style {:margin-right 4
:margin-top 0}}
[text/text {:size :paragraph-2
:style {:color colors/white}}
[text/text {:size :paragraph-2}
s]])
(string/split detail #"\s+"))
[[rn/view {:margin-right 4
@@ -1,106 +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)}])]])})))
@@ -1,82 +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 :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,212 +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,553 +0,0 @@
(ns quo2.components.record-audio.record-audio.view
(:require [quo.react :refer [memo effect!]]
[react-native.core :as rn]
[quo2.foundations.colors :as colors]
[quo2.components.icon :as icons]
[quo2.components.buttons.button :as button]
[react-native.reanimated :as reanimated]
[reagent.core :as reagent]
[cljs-bean.core :as bean]
[quo2.components.record-audio.record-audio.style :as style]
[status-im.utils.utils :as utils]
[oops.core :as oops]))
(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?]])))])
@@ -5,80 +5,10 @@
(defn render-toggle
([]
(render-toggle {}))
([opts]
(rtl/render (reagent/as-element [selectors/toggle opts]))))
(defn render-checkbox
([]
(render-checkbox {}))
([opts]
(rtl/render (reagent/as-element [selectors/checkbox opts]))))
(defn render-checkbox-prefill
([]
(render-checkbox-prefill {}))
([opts]
(rtl/render (reagent/as-element [selectors/checkbox-prefill opts]))))
(defn render-radio
([]
(render-radio {}))
([opts]
(rtl/render (reagent/as-element [selectors/radio opts]))))
(rtl/render (reagent/as-element [selectors/toggle {:disabled? (:disabled? false)}]))))
(js/global.test "default render of toggle component"
(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)))))
(js/global.test "default render of radio component"
(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)))))
(js/global.test "default render of checkbox component"
(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)))))
(js/global.test "default render of checkbox-prefill component"
(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)))))
(.toBeTruthy))))
+3 -6
View File
@@ -43,8 +43,7 @@
(colors/alpha colors/neutral-20 (if disabled? 0.3 1))
(colors/alpha colors/neutral-70 (if disabled? 0.3 1))))})
:accessibility-label (str "checkbox-" (if @checked? "on" "off"))
:accessibility-role :checkbox
:testID "checkbox-prefill-component"}
:accessibility-role :checkbox}
(when @checked?
[rn/view {:style
{:height 20
@@ -83,8 +82,7 @@
:border-color (if @checked? :none
(get-color @checked? disabled? blurred-background?))}
:accessibility-label (str "checkbox-" (if @checked? "on" "off"))
:accessibility-role :checkbox
:testID "checkbox-component"}
:accessibility-role :checkbox}
(when @checked?
[rn/view {:style
{:height 20
@@ -110,8 +108,7 @@
(colors/theme-colors colors/white
(colors/alpha colors/neutral-80 0.4)))})
:accessibility-label (str "radio-" (if @checked? "on" "off"))
:accessibility-role :checkbox
:testID "radio-component"}
:accessibility-role :checkbox}
[rn/view {:style
{:margin-left :auto
@@ -1,63 +0,0 @@
(ns quo2.components.settings.privacy-option
(:require [react-native.core :as rn]
[quo2.components.icon :as icons]
[quo2.components.selectors.selectors :as selectors]
[quo2.components.settings.style :as style]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]))
(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))})
+1 -1
View File
@@ -158,7 +158,7 @@
:on-press (fn [id]
(reset! active-tab-id id)
(when scroll-on-press?
(.scrollToIndex ^js @flat-list-ref
(.scrollToIndex @flat-list-ref
#js {:animated true
:index index
:viewPosition 0.5}))
+1 -16
View File
@@ -15,10 +15,8 @@
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.divider-label
quo2.components.dividers.date
quo2.components.dividers.new-messages
quo2.components.drawers.action-drawers
quo2.components.dropdowns.dropdown
@@ -36,18 +34,13 @@
quo2.components.tags.tags
quo2.components.tags.context-tags
quo2.components.tabs.tabs
quo2.components.notifications.toast
quo2.components.tabs.account-selector
quo2.components.navigation.floating-shell-button
quo2.components.tags.status-tags
quo2.components.navigation.page-nav
quo2.components.selectors.disclaimer
quo2.components.selectors.selectors
quo2.components.settings.privacy-option
quo2.components.loaders.skeleton
quo2.components.notifications.count-down-circle))
quo2.components.selectors.selectors))
(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)
@@ -74,8 +67,6 @@
(def page-nav quo2.components.navigation.page-nav/page-nav)
(def disclaimer quo2.components.selectors.disclaimer/disclaimer)
(def checkbox quo2.components.selectors.selectors/checkbox)
(def skeleton quo2.components.loaders.skeleton/skeleton)
;;;; AVATAR
(def account-avatar quo2.components.avatars.account-avatar/account-avatar)
(def channel-avatar quo2.components.avatars.channel-avatar/channel-avatar)
@@ -94,12 +85,10 @@
(def community-title quo2.components.community.community-view/community-title)
(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)
;;;; DIVIDERS
(def divider-label quo2.components.dividers.divider-label/divider-label)
(def new-messages quo2.components.dividers.new-messages/new-messages)
(def divider-date quo2.components.dividers.date/date)
;;;; LIST ITEMS
(def channel-list-item quo2.components.list-items.channel/list-item)
@@ -110,7 +99,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)
;;;; SETTINGS
(def privacy-option quo2.components.settings.privacy-option/card)
+1 -3
View File
@@ -1,4 +1,2 @@
(ns quo2.core-spec
(:require [quo2.components.selectors.--tests--.selectors-component-spec]
[quo2.components.counter.--tests--.counter-component-spec]
[quo2.components.drawers.--tests--.action-drawers-component-spec]))
(:require [quo2.components.selectors.--tests--.selectors-component-spec]))
-25
View File
@@ -13,20 +13,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
@@ -63,7 +49,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))
@@ -82,16 +67,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-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
@@ -111,11 +91,6 @@
(def white-opa-90 (alpha white 0.9))
(def white-opa-95 (alpha white 0.95))
;;;;Black
;;Solid
(def black "#000000")
;;;;Primary
;;Solid
+1 -19
View File
@@ -1,12 +1,9 @@
(ns react-native.core
(:require ["react" :as react]
(:require [reagent.core :as reagent]
["react-native" :as react-native]
[cljs-bean.core :as bean]
["@react-native-community/blur" :as blur]
[oops.core :as oops]
[react-native.flat-list :as flat-list]
[react-native.section-list :as section-list]
[reagent.core :as reagent]
[react-native.platform :as platform]))
(def app-state ^js (.-AppState ^js react-native))
@@ -60,9 +57,6 @@
(def status-bar (.-StatusBar ^js react-native))
(defn status-bar-height []
(.-currentHeight ^js status-bar))
(defn hw-back-add-listener [callback]
(.addEventListener (.-BackHandler ^js react-native) "hardwareBackPress" callback))
@@ -76,15 +70,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"))))))
-4
View File
@@ -5,9 +5,5 @@
(def os (when platform (.-OS ^js platform)))
(def version (when platform (.-Version ^js platform)))
(def android? (= os "android"))
(def ios? (= os "ios"))
(def low-device? (and android? (< version 29)))
+4 -33
View File
@@ -1,15 +1,10 @@
(ns react-native.reanimated
(:require ["react-native" :as rn]
[reagent.core :as reagent]
[clojure.string :as string]
["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)
:refer (useSharedValue useAnimatedStyle withTiming withDelay withSpring withRepeat Easing Keyframe)]))
;; Animated Components
(def create-animated-component (comp reagent/adapt-react-class (.-createAnimatedComponent reanimated)))
@@ -30,7 +25,6 @@
(def with-spring withSpring)
(def key-frame Keyframe)
(def with-repeat withRepeat)
(def cancel-animation cancelAnimation)
;; Easings
(def bezier (.-bezier ^js Easing))
@@ -60,12 +54,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
@@ -87,21 +75,4 @@
(defn animate-shared-value-with-repeat [anim val duration easing number-of-repetitions reverse?]
(set-shared-value anim (with-repeat (with-timing val (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))))
"easing" (get easings easing))) number-of-repetitions reverse?)))
+1 -1
View File
@@ -7,7 +7,7 @@
(defn consumer [component]
[consumer-raw
(fn [^js insets]
(fn [insets]
(reagent/as-element
[component (js->clj insets :keywordize-keys true)]))])
View File
-7
View File
@@ -1,7 +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,10 +1,10 @@
(ns status-im2.contexts.activity-center.events
(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-im2.common.json-rpc.events :as json-rpc]
[status-im2.contexts.activity-center.notification-types :as types]
[taoensso.timbre :as log]
[utils.re-frame :as urf]))
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.utils.fx :as fx]
[taoensso.timbre :as log]))
(def defaults
{:filter-status :unread
@@ -16,20 +16,19 @@
;;;; Navigation
(urf/defn open-activity-center
(fx/defn open-activity-center
{:events [:activity-center/open]}
[_]
(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}}]))
(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
(urf/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)
@@ -69,16 +68,14 @@
db-notifications
new-notifications))
(urf/defn notifications-reconcile
(fx/defn notifications-reconcile
{:events [:activity-center.notifications/reconcile]}
[{:keys [db]} new-notifications]
(when (seq new-notifications)
{:db (update-in db
[:activity-center :notifications]
update-notifications
new-notifications)}))
{:db (update-in db [:activity-center :notifications]
update-notifications new-notifications)}))
(urf/defn notifications-reconcile-from-response
(fx/defn notifications-reconcile-from-response
{:events [:activity-center/reconcile-notifications-from-response]}
[cofx response]
(->> response
@@ -90,78 +87,72 @@
(defn- get-notification
[db notification-id]
(->> (get-in db
[:activity-center
:notifications
(get-in db [:activity-center :filter :type])
(get-in db [:activity-center :filter :status])
:data])
(->> (get-in db [:activity-center
:notifications
(get-in db [:activity-center :filter :type])
(get-in db [:activity-center :filter :status])
:data])
(filter #(= notification-id (:id %)))
first))
(urf/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-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
%])}]}))
(urf/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
(urf/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-success #(rf/dispatch [:activity-center/reconcile-notifications-from-response %])
:on-error #(rf/dispatch [:activity-center/process-notification-failure
notification-id
:contact-verification/decline
%])}]})
(urf/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-success #(rf/dispatch [:activity-center/reconcile-notifications-from-response %])
:on-error #(rf/dispatch [:activity-center/process-notification-failure
notification-id
:contact-verification/reply
%])}]})
(urf/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-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
%])}]})
(urf/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-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
@@ -180,60 +171,51 @@
(def ^:const status-unread 2)
(def ^:const status-all 3)
(defn status
[filter-status]
(defn status [filter-status]
(case filter-status
:unread status-unread
:all status-all
99))
(urf/defn notifications-fetch
[{:keys [db]} {:keys [cursor per-page filter-type filter-status reset-data?]}]
(fx/defn notifications-fetch
[{:keys [db]} {:keys [cursor 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 (defaults :notifications-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 %])}]}))
(urf/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
(get-in db
[:activity-center :filter :type]
(get-in db [:activity-center :filter :type]
(defaults :filter-type)))
filter-status (or filter-status
(get-in db
[:activity-center :filter :status]
(get-in db [:activity-center :filter :status]
(defaults :filter-status)))]
(urf/merge cofx
{:db (-> db
(assoc-in [:activity-center :filter :type] filter-type)
(assoc-in [:activity-center :filter :status] filter-status))}
(notifications-fetch {:cursor start-or-end-cursor
:filter-type filter-type
:filter-status filter-status
:reset-data? true}))))
(fx/merge cofx
{:db (-> db
(assoc-in [:activity-center :filter :type] filter-type)
(assoc-in [:activity-center :filter :status] filter-status))}
(notifications-fetch {:cursor start-or-end-cursor
:filter-type filter-type
:filter-status filter-status
:reset-data? true}))))
(urf/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])
{:keys [cursor]} (get-in db [:activity-center :notifications type status])]
(when (valid-cursor? cursor)
(notifications-fetch cofx
{:cursor cursor
:filter-type type
:filter-status status
:reset-data? false}))))
(notifications-fetch cofx {:cursor cursor
:filter-type type
:filter-status status
:reset-data? false}))))
(urf/defn notifications-fetch-success
(fx/defn notifications-fetch-success
{:events [:activity-center.notifications/fetch-success]}
[{:keys [db]}
filter-type
@@ -249,38 +231,7 @@
(constantly processed)
#(concat %1 processed))))}))
(urf/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
are marked as read.
If this relationship ever changes, we will probably need to change the backend
to explicitly support fetching notifications for 'pending' contact requests."
{:events [:activity-center.notifications/fetch-unread-contact-requests]}
[cofx]
(notifications-fetch cofx
{:cursor start-or-end-cursor
:filter-status :unread
:filter-type types/contact-request
:per-page 20
:reset-data? true}))
(urf/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 #()}]})
(urf/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)})
(urf/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)
@@ -0,0 +1,518 @@
(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.notification-types :as types]
[status-im.constants :as constants]
[status-im.ethereum.json-rpc :as json-rpc]
status-im.events
[status-im.test-helpers :as h]
[status-im2.setup.config :as config]))
(def notification-id "0x1")
(defn setup []
(h/register-helper-events)
(rf/dispatch [:setup/app-started]))
(defn test-log-on-failure
[{:keys [before-test notification-id event action]}]
(rf-test/run-test-sync
(setup)
(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)
(is (= {:args [(str "Failed to " action)
{:notification-id notification-id
:error :fake-error}]
:level :warn}
(last @logs)))))))
;;;; Misc
(deftest mark-as-read-test
(testing "does nothing if the notification ID cannot be found in the app db"
(rf-test/run-test-sync
(setup)
(let [spy-queue (atom [])]
(h/spy-fx spy-queue ::json-rpc/call)
(let [notifications {types/one-to-one-chat
{:all {:cursor "" :data [{:id notification-id
:read false
:type types/one-to-one-chat}]}
:unread {:cursor "" :data []}}}]
(rf/dispatch [:test/assoc-in [:activity-center]
{:notifications notifications
:filter {:type types/one-to-one-chat
:status :all}}])
(rf/dispatch [:activity-center.notifications/mark-as-read "0x666"])
(is (= [] @spy-queue))
(is (= notifications (get-in (h/db) [:activity-center :notifications])))))))
(testing "marks notifications as read and updates app db"
(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))
(rf/dispatch [:test/assoc-in [:activity-center]
{:notifications {types/one-to-one-chat
{:all {:cursor "" :data [notif-3 notif-2 notif-1]}
:unread {:cursor "" :data [notif-3 notif-2]}}}
:filter {:type types/one-to-one-chat
:status :unread}}])
(rf/dispatch [:activity-center.notifications/mark-as-read (:id notif-2)])
(is (= {types/one-to-one-chat
{:all {:cursor "" :data [notif-3 new-notif-2 notif-1]}
:unread {:cursor "" :data [notif-3]}}
types/no-type
{:all {:data [new-notif-2]}
:unread {:data []}}}
(get-in (h/db) [:activity-center :notifications])))
(rf/dispatch [:activity-center.notifications/mark-as-read (:id notif-3)])
(is (= {types/one-to-one-chat
{:all {:cursor "" :data [new-notif-3 new-notif-2 notif-1]}
:unread {:cursor "" :data []}}
types/no-type
{:all {:data [new-notif-3 new-notif-2]}
:unread {:data []}}}
(get-in (h/db) [:activity-center :notifications]))))))
(testing "logs on failure"
(test-log-on-failure
{:notification-id notification-id
:event [:activity-center.notifications/mark-as-read notification-id]
:action :notification/mark-as-read
:before-test (fn []
(rf/dispatch [:test/assoc-in [:activity-center]
{:notifications {types/one-to-one-chat
{:all {:cursor "" :data [{:id notification-id
:read false
:type types/one-to-one-chat}]}
:unread {:cursor "" :data []}}}
:filter {:type types/one-to-one-chat
:status :all}}]))})))
;;;; Contact verification
(def contact-verification-rpc-response
{:activityCenterNotifications
[{:accepted false
:author "0x04d03f"
:chatId "0x04d03f"
:contactVerificationStatus constants/contact-verification-status-pending
:dismissed false
:id notification-id
:message {}
:name "0x04d03f"
:read true
:timestamp 1666647286000
:type types/contact-verification}]})
(def contact-verification-expected-notification
{:accepted false
:author "0x04d03f"
:chat-id "0x04d03f"
:contact-verification-status constants/contact-verification-status-pending
:dismissed false
:id notification-id
:last-message nil
:message {:command-parameters nil
:content {:chat-id nil
:ens-name nil
:image nil
:line-count nil
:links nil
:parsed-text nil
:response-to nil
:rtl? nil
:sticker nil
:text nil}
:outgoing false
:outgoing-status nil
:quoted-message nil}
:name "0x04d03f"
:read true
:reply-message nil
:timestamp 1666647286000
:type types/contact-verification})
(defn test-contact-verification-event
[{:keys [event expected-rpc-call]}]
(rf-test/run-test-sync
(setup)
(let [spy-queue (atom [])]
(h/stub-fx-with-callbacks ::json-rpc/call :on-success (constantly contact-verification-rpc-response))
(h/spy-fx spy-queue ::json-rpc/call)
(rf/dispatch event)
(is (= {types/no-type
{:all {:data [contact-verification-expected-notification]}
:unread {:data []}}
types/contact-verification
{:all {:data [contact-verification-expected-notification]}
:unread {:data []}}}
(get-in (h/db) [:activity-center :notifications])))
(is (= expected-rpc-call
(-> @spy-queue
(get-in [0 :args 0])
(select-keys [:method :params])))))))
(deftest contact-verification-decline-test
(with-redefs [config/new-activity-center-enabled? true]
(testing "declines notification and reconciles"
(test-contact-verification-event
{:event [:activity-center.contact-verification/decline notification-id]
:expected-rpc-call {:method "wakuext_declineContactVerificationRequest"
:params [notification-id]}}))
(testing "logs on failure"
(test-log-on-failure
{:notification-id notification-id
:event [:activity-center.contact-verification/decline notification-id]
:action :contact-verification/decline}))))
(deftest contact-verification-reply-test
(with-redefs [config/new-activity-center-enabled? true]
(testing "sends reply and reconciles"
(let [reply "any answer"]
(test-contact-verification-event
{:event [:activity-center.contact-verification/reply notification-id reply]
:expected-rpc-call {:method "wakuext_acceptContactVerificationRequest"
:params [notification-id reply]}})))
(testing "logs on failure"
(test-log-on-failure
{:notification-id notification-id
:event [:activity-center.contact-verification/reply notification-id "any answer"]
:action :contact-verification/reply}))))
(deftest contact-verification-mark-as-trusted-test
(with-redefs [config/new-activity-center-enabled? true]
(testing "marks notification as trusted and reconciles"
(test-contact-verification-event
{:event [:activity-center.contact-verification/mark-as-trusted notification-id]
:expected-rpc-call {:method "wakuext_verifiedTrusted"
:params [{:id notification-id}]}}))
(testing "logs on failure"
(test-log-on-failure
{:notification-id notification-id
:event [:activity-center.contact-verification/mark-as-trusted notification-id]
:action :contact-verification/mark-as-trusted}))))
(deftest contact-verification-mark-as-untrustworthy-test
(with-redefs [config/new-activity-center-enabled? true]
(testing "marks notification as untrustworthy and reconciles"
(test-contact-verification-event
{:event [:activity-center.contact-verification/mark-as-untrustworthy notification-id]
:expected-rpc-call {:method "wakuext_verifiedUntrustworthy"
:params [{:id notification-id}]}}))
(testing "logs on failure"
(test-log-on-failure
{:notification-id notification-id
:event [:activity-center.contact-verification/mark-as-untrustworthy notification-id]
:action :contact-verification/mark-as-untrustworthy}))))
;;;; Notification reconciliation
(deftest notifications-reconcile-test
(with-redefs [config/new-activity-center-enabled? true]
(testing "does nothing when there are no new notifications"
(rf-test/run-test-sync
(setup)
(let [notifications {types/one-to-one-chat
{:all {:cursor ""
:data [{:id "0x1"
:read true
:type types/one-to-one-chat}
{:id "0x2"
:read false
:type types/one-to-one-chat}]}
:unread {:cursor ""
:data [{:id "0x3"
:read false
:type types/one-to-one-chat}]}}
types/private-group-chat
{:unread {:cursor ""
:data [{:id "0x4"
:read false
:type types/private-group-chat}]}}}]
(rf/dispatch [:test/assoc-in [:activity-center :notifications] notifications])
(rf/dispatch [:activity-center.notifications/reconcile nil])
(is (= notifications (get-in (h/db) [:activity-center :notifications]))))))
(testing "removes dismissed or accepted notifications"
(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}
notif-4 {:id "0x4" :read false :type types/private-group-chat}
notif-5 {:id "0x5" :read true :type types/private-group-chat}
notif-6 {:id "0x6" :read false :type types/private-group-chat}]
(rf/dispatch [:test/assoc-in [:activity-center :notifications]
{types/one-to-one-chat
{:all {:cursor "" :data [notif-1 notif-2]}
:unread {:cursor "" :data [notif-3]}}
types/private-group-chat
{:unread {:cursor "" :data [notif-4 notif-6]}}}])
(rf/dispatch [:activity-center.notifications/reconcile
[(assoc notif-1 :dismissed true)
(assoc notif-3 :accepted true)
(assoc notif-4 :dismissed true)
notif-5]])
(is (= {types/no-type
{:all {:data [notif-5]}
:unread {:data []}}
types/one-to-one-chat
{:all {:cursor "" :data [notif-2]}
:unread {:cursor "" :data []}}
types/private-group-chat
{:all {:data [notif-5]}
:unread {:cursor "" :data [notif-6]}}}
(get-in (h/db) [:activity-center :notifications]))))))
(testing "replaces old notifications with newly arrived ones"
(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}
notif-6 {:id "0x6" :read false :type types/private-group-chat}
new-notif-1 (assoc notif-1 :last-message {})
new-notif-4 (assoc notif-4 :author "0xabc")]
(rf/dispatch [:test/assoc-in [:activity-center :notifications]
{types/no-type
{:all {:cursor "" :data [notif-1]}
:unread {:cursor "" :data [notif-4 notif-6]}}
types/one-to-one-chat
{:all {:cursor "" :data [notif-1]}}
types/private-group-chat
{:unread {:cursor "" :data [notif-4 notif-6]}}}])
(rf/dispatch [:activity-center.notifications/reconcile [new-notif-1 new-notif-4 notif-6]])
(is (= {types/no-type
{:all {:cursor "" :data [notif-6 new-notif-4 new-notif-1]}
:unread {:cursor "" :data [notif-6 new-notif-4]}}
types/one-to-one-chat
{:all {:cursor "" :data [new-notif-1]}
:unread {:data []}}
types/private-group-chat
{:all {:data [notif-6 new-notif-4]}
:unread {:cursor "" :data [notif-6 new-notif-4]}}}
(get-in (h/db) [:activity-center :notifications]))))))
(testing "reconciles notifications that switched their read/unread status"
(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)]
(rf/dispatch [:test/assoc-in [:activity-center :notifications]
{types/one-to-one-chat
{:all {:cursor "" :data [notif-1]}}}])
(rf/dispatch [:activity-center.notifications/reconcile [new-notif-1]])
(is (= {types/no-type
{:all {:data [new-notif-1]}
:unread {:data [new-notif-1]}}
types/one-to-one-chat
{:all {:cursor "" :data [new-notif-1]}
:unread {:data [new-notif-1]}}}
(get-in (h/db) [:activity-center :notifications]))))))
;; 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"
(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}
notif-3 {:id "0x3" :read false :type types/one-to-one-chat :timestamp 50}
notif-4 {:id "0x4" :read false :type types/one-to-one-chat :timestamp 100}
notif-5 {:id "0x5" :read false :type types/one-to-one-chat :timestamp 100}
new-notif-1 (assoc notif-1 :last-message {})
new-notif-4 (assoc notif-4 :last-message {})]
(rf/dispatch [:test/assoc-in [:activity-center :notifications]
{types/one-to-one-chat
{:all {:cursor "" :data [notif-1 notif-2]}
:unread {:cursor "" :data [notif-3 notif-4 notif-5]}}}])
(rf/dispatch [:activity-center.notifications/reconcile [new-notif-1 new-notif-4]])
(is (= {types/no-type
{:all {:data [new-notif-4 new-notif-1]}
:unread {:data [new-notif-4]}}
types/one-to-one-chat
{:all {:cursor "" :data [new-notif-4 notif-2 new-notif-1]}
:unread {:cursor "" :data [notif-5 new-notif-4 notif-3]}}}
(get-in (h/db) [:activity-center :notifications]))))))))
;;;; Notifications fetching and pagination
(deftest notifications-fetch-test
(with-redefs [config/new-activity-center-enabled? true]
(testing "fetches first page"
(rf-test/run-test-sync
(setup)
(let [spy-queue (atom [])]
(h/stub-fx-with-callbacks
::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)
(rf/dispatch [:activity-center.notifications/fetch-first-page
{:filter-type types/one-to-one-chat}])
(is (= :unread (get-in (h/db) [:activity-center :filter :status])))
(is (= "" (get-in @spy-queue [0 :args 0 :params 0]))
"Should be called with empty cursor when fetching first page")
(is (= {types/one-to-one-chat
{:unread {:cursor "10"
:data [{:chat-id "0x9"
:chat-name nil
:chat-type types/one-to-one-chat
:group-chat false
:id "0x1"
:public? false
:last-message nil
:message nil
:read false
:reply-message nil
:type types/one-to-one-chat}]}}}
(get-in (h/db) [:activity-center :notifications]))))))
(testing "does not fetch next page when pagination cursor reached the end"
(rf-test/run-test-sync
(setup)
(let [spy-queue (atom [])]
(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]
types/one-to-one-chat])
(rf/dispatch [:test/assoc-in [:activity-center :notifications types/one-to-one-chat :unread :cursor]
""])
(rf/dispatch [:activity-center.notifications/fetch-next-page])
(is (= [] @spy-queue)))))
;; The cursor can be nil sometimes because the reconciliation doesn't care
;; 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"
(rf-test/run-test-sync
(setup)
(let [spy-queue (atom [])]
(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]
types/one-to-one-chat])
(rf/dispatch [:test/assoc-in [:activity-center :notifications types/one-to-one-chat :unread :cursor]
nil])
(rf/dispatch [:activity-center.notifications/fetch-next-page])
(is (= [] @spy-queue)))))
(testing "fetches next page when pagination cursor is not empty"
(rf-test/run-test-sync
(setup)
(let [spy-queue (atom [])]
(h/stub-fx-with-callbacks
::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)
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
:unread])
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
types/mention])
(rf/dispatch [:test/assoc-in [:activity-center :notifications types/mention :unread :cursor]
"10"])
(rf/dispatch [:activity-center.notifications/fetch-next-page])
(is (= "wakuext_activityCenterNotificationsBy" (get-in @spy-queue [0 :args 0 :method])))
(is (= "10" (get-in @spy-queue [0 :args 0 :params 0]))
"Should be called with current cursor")
(is (= {types/mention
{:unread {:cursor ""
:data [{:chat-id "0x9"
:chat-name nil
:chat-type 3
:id "0x1"
:last-message nil
:message nil
:read false
:reply-message nil
:type types/mention}]}}}
(get-in (h/db) [:activity-center :notifications]))))))
(testing "does not fetch next page while it is still loading"
(rf-test/run-test-sync
(setup)
(let [spy-queue (atom [])]
(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]
types/one-to-one-chat])
(rf/dispatch [:test/assoc-in [:activity-center :notifications types/one-to-one-chat :all :cursor]
"10"])
(rf/dispatch [:test/assoc-in [:activity-center :notifications types/one-to-one-chat :all :loading?]
true])
(rf/dispatch [:activity-center.notifications/fetch-next-page])
(is (= [] @spy-queue)))))
(testing "resets loading flag after an error"
(rf-test/run-test-sync
(setup)
(let [spy-queue (atom [])]
(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)
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
:unread])
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
types/one-to-one-chat])
(rf/dispatch [:test/assoc-in [:activity-center :notifications types/one-to-one-chat :unread :cursor]
""])
(rf/dispatch [:activity-center.notifications/fetch-first-page])
(is (nil? (get-in (h/db) [:activity-center :notifications types/one-to-one-chat :unread :loading?])))
(is (= [:activity-center.notifications/fetch-error
types/one-to-one-chat
:unread
:fake-error]
(:args (last @spy-queue)))))))))
@@ -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)
+25 -27
View File
@@ -1,19 +1,19 @@
(ns status-im.add-new.core
(:require [clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.add-new.db :as db]
[status-im.chat.models :as chat]
[status-im.contact.core :as contact]
[status-im.ethereum.core :as ethereum]
[status-im.ethereum.ens :as ens]
[status-im.ethereum.stateofus :as stateofus]
[status-im.i18n.i18n :as i18n]
[status-im.router.core :as router]
[status-im.utils.db :as utils.db]
[status-im.add-new.db :as db]
[status-im.utils.random :as random]
[status-im.utils.utils :as utils]
[status-im.utils.fx :as fx]
[status-im.chat.models :as chat]
[status-im.i18n.i18n :as i18n]
[status-im.contact.core :as contact]
[status-im.router.core :as router]
[status-im2.navigation.events :as navigation]
[utils.re-frame :as rf]))
[status-im.ethereum.stateofus :as stateofus]
[status-im.utils.db :as utils.db]))
(re-frame/reg-fx
:resolve-public-key
@@ -24,19 +24,19 @@
;;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)))]
(when (or (not id) (= id @resolve-last-id))
(if ens-error
{:db (assoc-in db [:contacts/new-identity :state] :error)}
(let [new-identity (utils/safe-trim new-identity-raw)
(let [new-identity (utils/safe-trim new-identity-raw)
is-public-key? (and (string? new-identity)
(utils.db/valid-public-key? new-identity))
is-ens? (and (not is-public-key?)
(ens/valid-eth-name-prefix? new-identity))
error (db/validate-pub-key db new-identity)]
is-ens? (and (not is-public-key?)
(ens/valid-eth-name-prefix? new-identity))
error (db/validate-pub-key db new-identity)]
(reset! resolve-last-id nil)
(merge {:db (assoc db
:contacts/new-identity
@@ -61,27 +61,25 @@
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}]
(let [public-key? (and (string? data)
(string/starts-with? data "0x"))
chat-key (cond
(= type :private-chat) chat-id
(= type :contact) public-key
(and (= type :undefined)
public-key?)
data)
[{:keys [db] :as cofx} {:keys [type public-key chat-id data ens-name]} {:keys [new-contact? nickname] :as opts}]
(let [public-key? (and (string? data)
(string/starts-with? data "0x"))
chat-key (cond
(= type :private-chat) chat-id
(= type :contact) public-key
(and (= type :undefined)
public-key?) data)
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 +91,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)
+2 -2
View File
@@ -1,6 +1,6 @@
(ns status-im.async-storage.transit
(:require [cognitect.transit :as transit]
[taoensso.timbre :as log]))
[taoensso.timbre :as timbre]))
(def reader (transit/reader :json))
(def writer (transit/writer :json))
@@ -8,4 +8,4 @@
(defn clj->transit [o] (transit/write writer o))
(defn transit->clj [o] (try (transit/read reader o)
(catch :default e
(log/error e))))
(timbre/error e))))
+6 -6
View File
@@ -1,23 +1,23 @@
(ns status-im.backup.core
(:require [re-frame.core :as re-frame]
[status-im.ethereum.json-rpc :as json-rpc]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.utils.fx :as fx]))
(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)
{:db (assoc db :backup/performing-backup true)
::json-rpc/call [{:method "wakuext_backupData"
:params []
:on-error #(do
+41 -52
View File
@@ -4,27 +4,24 @@
[status-im.i18n.i18n :as i18n]
[status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im2.navigation.events :as navigation]
[utils.re-frame :as rf]))
[status-im.utils.fx :as fx]))
(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})")
(defn valid-address?
[address]
(defn valid-address? [address]
(re-matches address-regex address))
(defn- build
[id bootnode-name address chain]
(defn- build [id bootnode-name address chain]
{:address address
:chain chain
:id (string/replace id "-" "")
:name bootnode-name})
(rf/defn fetch
[cofx id]
(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,17 +35,16 @@
: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
(set-input :id id)
(set-input :url (str address))
(set-input :name (str name)))]
name]} (fetch cofx id)
fxs (fx/merge cofx
(set-input :id id)
(set-input :url (str address))
(set-input :name (str name)))]
(assoc fxs :dispatch [:navigate-to :edit-bootnode])))
(defn custom-bootnodes-in-use?
@@ -56,75 +52,68 @@
(let [network (:networks/current-network db)]
(get-in db [:multiaccount :custom-bootnodes-enabled? network])))
(rf/defn delete
[{{:keys [multiaccount] :as db} :db :as cofx} id]
(let [network (:networks/current-network db)
(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
(:custom-bootnodes new-multiaccount)
:custom-bootnodes (:custom-bootnodes new-multiaccount)
{:success-event (when (custom-bootnodes-in-use? cofx)
[:multiaccounts.update.callback/save-settings-success])}))))
(rf/defn upsert
{:events [:bootnodes.ui/save-pressed]
(fx/defn upsert
{:events [:bootnodes.ui/save-pressed]
:interceptors [(re-frame/inject-cofx :random-id-generator)]}
[{{:bootnodes/keys [manage] :keys [multiaccount] :as db} :db
random-id-generator :random-id-generator
:as cofx}]
random-id-generator :random-id-generator :as cofx}]
(let [{:keys [name id url]} manage
network (:networks/current-network db)
bootnode (build
(or (:value id) (random-id-generator))
(:value name)
(:value url)
network)
new-bootnodes (assoc-in
(:custom-bootnodes multiaccount)
[network (:id bootnode)]
bootnode)]
network (:networks/current-network db)
bootnode (build
(or (:value id) (random-id-generator))
(:value name)
(:value url)
network)
new-bootnodes (assoc-in
(:custom-bootnodes multiaccount)
[network (:id bootnode)]
bootnode)]
(rf/merge cofx
(fx/merge cofx
{:db (dissoc db :bootnodes/manage)
:dispatch [:navigate-back]}
(multiaccounts.update/multiaccount-update
:custom-bootnodes
new-bootnodes
:custom-bootnodes new-bootnodes
{: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)
(let [current-network (:networks/current-network db)
bootnodes-settings (get-in db [:multiaccount :custom-bootnodes-enabled?])]
(multiaccounts.update/multiaccount-update
cofx
:custom-bootnodes-enabled?
(assoc bootnodes-settings current-network value)
:custom-bootnodes-enabled? (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]))
: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)
:content (i18n/label :t/delete-bootnode-are-you-sure)
{:ui/show-confirmation {:title (i18n/label :t/delete-bootnode-title)
:content (i18n/label :t/delete-bootnode-are-you-sure)
:confirm-button-text (i18n/label :t/delete-bootnode)
:on-accept #(re-frame/dispatch [:bootnodes.ui/delete-confirmed
bootnode-id])}})
: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)))
+9 -9
View File
@@ -1,15 +1,15 @@
(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
:bottom-sheet/show? true
:bottom-sheet/view view
:bottom-sheet/options options)})
:db (assoc db
:bottom-sheet/show? true
: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,8 +17,8 @@
{:view view
:options options}))
(rf/defn hide-bottom-sheet
(fx/defn hide-bottom-sheet
{:events [:bottom-sheet/hide]}
[{:keys [db]}]
{:hide-bottom-sheet nil
:db (assoc db :bottom-sheet/show? false)})
:db (assoc db :bottom-sheet/show? false)})
+152 -195
View File
@@ -1,65 +1,62 @@
(ns status-im.browser.core
(:require ["eth-phishing-detect" :as eth-phishing-detect]
[clojure.string :as string]
(:require [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-im2.navigation.events :as navigation]
[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]
[status-im.signing.core :as signing]
[status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im.bottom-sheet.core :as bottom-sheet]
[status-im.browser.webview-ref :as webview-ref]
["eth-phishing-detect" :as eth-phishing-detect]
[utils.debounce :as debounce]
[utils.re-frame :as rf]))
[status-im.browser.eip3085 :as eip3085]
[status-im.browser.eip3326 :as eip3326]))
(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)})
(defn get-current-browser
[db]
(defn get-current-browser [db]
(get-in db [:browser/browsers (get-in db [:browser/options :browser-id])]))
(defn get-current-url
[{:keys [history history-index]
:or {history-index 0}}]
(defn get-current-url [{:keys [history history-index]
:or {history-index 0}}]
(when history
(nth history history-index)))
(defn secure?
[{:keys [error? dapp?]} {:keys [url]}]
(defn secure? [{:keys [error? dapp?]} {:keys [url]}]
(or dapp?
(and (not error?)
(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]
{: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)
@@ -68,116 +65,106 @@
:params [browser-id]
:on-success #()})})
(defn update-dapp-name
[{:keys [name] :as browser}]
(defn update-dapp-name [{:keys [name] :as browser}]
(assoc browser :dapp? false :name (or name (i18n/label :t/browser))))
(defn check-if-phishing-url
[{:keys [history history-index] :as browser}]
(defn check-if-phishing-url [{:keys [history history-index] :as browser}]
(let [history-host (http/url-host (try (nth history history-index) (catch js/Error _)))]
(cond-> browser history-host (assoc :unsafe? (eth-phishing-detect history-host)))))
(defn resolve-ens-contenthash-callback
[url]
(defn resolve-ens-contenthash-callback [url]
(if (not (string/blank? url))
(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))
host (http/url-host current-url)]
host (http/url-host current-url)]
(if (and (not resolved-url) (ens/is-valid-eth-name? host))
{:db (update db :browser/options assoc :resolving? true)
{:db (update db :browser/options assoc :resolving? true)
:browser/resolve-ens-contenthash {:chain-id (ethereum/chain-id db)
:ens-name host
: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])]
{: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]
{: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])
(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]}]
(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))
host (http/url-host (get-current-url browser))]
host (http/url-host (get-current-url browser))]
(update-browser cofx (assoc browser :ignore-unsafe host))))
(defn can-go-forward?
[{:keys [history-index history]}]
(defn can-go-forward? [{: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)]
@@ -186,81 +173,73 @@
new-index (dec (count new-history))]
(update-browser cofx
(assoc browser
:history new-history
: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
{:db (-> (update db
:browser/options
(fx/merge cofx
{:db (-> (update db :browser/options
assoc
:url (str gateway path)
:resolving? false)
:url (str gateway path)
: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
{:url (get-current-url (get-current-browser db))
:resolving? false
:error? true}))
(update-browser-options cofx {:url (get-current-url (get-current-browser db))
: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]
(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])
(let [browser (get-current-browser (:db cofx))
options (get-in cofx [:db :browser/options])
current-url (:url options)]
(when (and browser
(not (string/blank? url))
(not= "about:blank" url)
(not= current-url url)
(not= (str current-url "/") url))
(when (and browser (not (string/blank? url)) (not= "about:blank" url) (not= current-url url) (not= (str current-url "/") url))
(let [resolved-ens (first (filter (fn [v]
(not= (.indexOf ^js url (second v)) -1))
(:resolved-ens options)))
resolved-url (if resolved-ens
(http/normalize-url (string/replace url
(second resolved-ens)
(first resolved-ens)))
(http/normalize-url (string/replace url (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)
@@ -268,61 +247,59 @@
(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"
{:events [:browser.ui/url-submitted]}
[cofx url]
(let [browser (get-current-browser (:db cofx))
(let [browser (get-current-browser (:db cofx))
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"
{:events [:browser.ui/open-url]}
[{:keys [db] :as cofx} url]
(let [normalized-url (http/normalize-and-decode-url url)
browser {:browser-id (random/id)
:history-index 0
:history [normalized-url]}]
browser {:browser-id (random/id)
:history-index 0
:history [normalized-url]}]
(if (links/universal-link? normalized-url)
{:dispatch [:universal-links/handle-url normalized-url]}
(rf/merge cofx
{:db (assoc db
:browser/options
(fx/merge cofx
{:db (assoc db :browser/options
{:browser-id (:browser-id browser)})}
(navigation/change-tab :browser)
(navigation/set-stack-root :browser-stack :browser)
(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
{:db (assoc db
:browser/options
(fx/merge cofx
{:db (assoc db :browser/options
{:browser-id browser-id})}
(update-browser browser)
(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
@@ -330,7 +307,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
@@ -359,24 +336,21 @@
(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})
(defn web3-sign-message?
[method]
(defn web3-sign-message? [method]
(#{constants/web3-sign-typed-data constants/web3-sign-typed-data-v3 constants/web3-sign-typed-data-v4
constants/web3-personal-sign
constants/web3-eth-sign constants/web3-keycard-sign-typed-data}
method))
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])
typed? (and (not= constants/web3-personal-sign method)
(not= constants/web3-eth-sign method))]
typed? (and (not= constants/web3-personal-sign method) (not= constants/web3-eth-sign method))]
(if (or message? (= constants/web3-send-transaction method))
(let [[address data] (cond (and (= method constants/web3-keycard-sign-typed-data)
(not (vector? params)))
@@ -385,38 +359,34 @@
message? (normalize-sign-message-params params typed?)
:else [nil nil])]
(when (or (not message?) (and address data))
(signing/sign cofx
(merge
(if message?
{:message {:address address
:data data
:v4 (= constants/web3-sign-typed-data-v4 method)
:typed? typed?
:pinless? (= method constants/web3-keycard-sign-typed-data)
:from dapps-address}}
{:tx-obj (-> params
first
(update :from #(or % dapps-address))
(dissoc :gasPrice))})
{:on-result [:browser.dapp/transaction-on-result message-id id]
:on-error [:browser.dapp/transaction-on-error message-id]}))))
(signing/sign cofx (merge
(if message?
{:message {:address address
:data data
:v4 (= constants/web3-sign-typed-data-v4 method)
:typed? typed?
:pinless? (= method constants/web3-keycard-sign-typed-data)
:from dapps-address}}
{:tx-obj (-> params
first
(update :from #(or % dapps-address))
(dissoc :gasPrice))})
{:on-result [:browser.dapp/transaction-on-result message-id id]
:on-error [:browser.dapp/transaction-on-error message-id]}))))
(cond
(#{"eth_accounts" "eth_coinbase"} method)
(send-to-bridge
cofx
{:type constants/web3-send-async-callback
:messageId message-id
:result {:jsonrpc "2.0"
:id (int id)
:result (if (= method "eth_coinbase") dapps-address [dapps-address])}})
(send-to-bridge cofx {:type constants/web3-send-async-callback
:messageId message-id
:result {:jsonrpc "2.0"
:id (int id)
:result (if (= method "eth_coinbase") dapps-address [dapps-address])}})
(= method "personal_ecRecover")
{:signing.fx/recover-message {:params {:message (first params)
{:signing.fx/recover-message {:params {:message (first params)
:signature (second params)}
:on-completed #(re-frame/dispatch
[:browser.callback/call-rpc
{:type constants/web3-send-async-callback
:messageId message-id
:result (types/json->clj %)}])}}
:on-completed #(re-frame/dispatch [:browser.callback/call-rpc
{:type constants/web3-send-async-callback
:messageId message-id
:result (types/json->clj %)}])}}
(= method "wallet_switchEthereumChain")
(eip3326/handle-switch-ethereum-chain cofx dapp-name id message-id (first params))
@@ -431,8 +401,7 @@
:error %1
:result %2}])]}))))
(rf/defn handle-no-permissions
[cofx {:keys [method id]} message-id]
(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
(send-to-bridge cofx
@@ -451,48 +420,41 @@
"keycard_signTypedData"
"eth_signTypedData" "personal_sign" "personal_ecRecover"})
(defn has-permissions?
[{:dapps/keys [permissions]} dapp-name method]
(defn has-permissions? [{:dapps/keys [permissions]} dapp-name method]
(boolean
(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
db)
url-original (get-current-url
browser)
data (types/json->clj
message)
(let [browser (get-current-browser db)
url-original (get-current-url browser)
data (types/json->clj message)
{{:keys [url]} :navState :keys [type permission payload messageId params]} data
{:keys [dapp? name]} browser
dapp-name (if dapp?
name
(http/url-host
url-original))]
{:keys [dapp? name]} browser
dapp-name (if dapp? name (http/url-host url-original))]
(cond
(and (= type constants/history-state-changed)
(not= "about:blank" url))
@@ -512,13 +474,10 @@
(re-frame/reg-fx
:browser/send-to-bridge
(fn [message]
(let
[^js webview @webview-ref/webview-ref
msg
(str
"(function() { var __send = function() { if (ReactNativeWebView.onMessage) { ReactNativeWebView.onMessage('"
(types/clj->json message)
"');} else {setTimeout(__send, 0)}}; __send();})();")]
(let [^js webview @webview-ref/webview-ref
msg (str "(function() { var __send = function() { if (ReactNativeWebView.onMessage) { ReactNativeWebView.onMessage('"
(types/clj->json message)
"');} else {setTimeout(__send, 0)}}; __send();})();")]
(when (and message webview)
(.injectJavaScript webview msg)))))
@@ -549,61 +508,59 @@
(fn []
(status/clear-web-data)))
(defn share-link
[url]
(defn share-link [url]
(let [link (links/generate-link :browse :external url)
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)
(multiaccounts.update/multiaccount-update :dapps-address address {})
#(when (= (:view-id db) :browser)
(merge (navigation/navigate-back %)
{:dispatch [:browser.ui/browser-item-selected
(get-in db [:browser/options :browser-id])]}))))
{: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))
{}
bookmarks)
stored-bookmarks (get-in db [:bookmarks/bookmarks])]
stored-bookmarks (get-in db [:bookmarks/bookmarks])]
{:db (assoc-in db [:bookmarks/bookmarks] (merge stored-bookmarks changed-bookmarks))}))
+43 -50
View File
@@ -1,76 +1,69 @@
;reference https://eips.ethereum.org/EIPS/eip-3085 EIP-3085: Wallet Add Ethereum Chain RPC Method
;(`wallet_addEthereumChain`)
;reference https://eips.ethereum.org/EIPS/eip-3085 EIP-3085: Wallet Add Ethereum Chain RPC Method (`wallet_addEthereumChain`)
(ns status-im.browser.eip3085
(:require [clojure.string :as string]
(:require [status-im.constants :as constants]
[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.utils.fx :as fx]
[status-im.network.core :as network]
[status-im.ui.screens.browser.eip3085.sheet :as sheet]
[status-im.utils.random :as random]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
[status-im.utils.random :as random]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.ui.screens.browser.eip3085.sheet :as sheet]))
(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
{:browser/send-to-bridge {:type constants/web3-send-async-callback
:messageId messageId
:result {:jsonrpc "2.0"
:id (int id)
:result nil}}})
:result {:jsonrpc "2.0"
: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])
{: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]})
: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
{:browser/send-to-bridge {:type constants/web3-send-async-callback
:messageId message-id
:error {:code 4001
:message "User rejected the request."}}
:dispatch [:bottom-sheet/hide]})
:error {:code 4001
: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
{:keys [chainId blockExplorerUrls chainName iconUrls nativeCurrency rpcUrls] :as params}]
(let [manage {:name {:value chainName}
:symbol {:value (:symbol nativeCurrency)}
:url {:value (first rpcUrls)}
dapp-name id message-id {:keys [chainId blockExplorerUrls chainName iconUrls nativeCurrency rpcUrls] :as params}]
(let [manage {:name {:value chainName}
:symbol {:value (:symbol nativeCurrency)}
:url {:value (first rpcUrls)}
:network-id {:value chainId}
:chain {:value :custom}}]
:chain {:value :custom}}]
(if (network/valid-manage? manage)
(let [{:keys [name url chain network-id symbol]} manage
random-id (string/replace (random/id) "-" "")
network (network/new-network random-id
(:value name)
(:value symbol)
(:value url)
(:value chain)
(:value network-id))
new-networks (assoc networks random-id network)
params (assoc params
:new-networks new-networks
:id id
:new-network network)]
random-id (string/replace (random/id) "-" "")
network (network/new-network random-id
(:value name)
(:value symbol)
(:value url)
(:value chain)
(:value network-id))
new-networks (assoc networks random-id network)
params (assoc params :new-networks new-networks :id id :new-network network)]
(if (network/chain-id-available? networks network)
{:dispatch [:bottom-sheet/show-sheet
{:content (fn []
[sheet/permissions-panel dapp-name message-id params])}]}
{:dispatch [:bottom-sheet/show-sheet {:content (fn []
[sheet/permissions-panel dapp-name message-id params])}]}
(send-success-call-to-bridge cofx id message-id)))
{:browser/send-to-bridge {:type constants/web3-send-async-callback
{:browser/send-to-bridge {:type constants/web3-send-async-callback
:messageId message-id
:error {:code -32602
:message "invalid network parameters"}}})))
:error {:code -32602
:message "invalid network parameters"}}})))
+26 -37
View File
@@ -1,54 +1,43 @@
;reference https://eips.ethereum.org/EIPS/eip-3326 EIP-3326: Wallet Switch Ethereum Chain RPC Method
;(`wallet_switchEthereumChain`)
;reference https://eips.ethereum.org/EIPS/eip-3326 EIP-3326: Wallet Switch Ethereum Chain RPC Method (`wallet_switchEthereumChain`)
(ns status-im.browser.eip3326
(:require [status-im.constants :as constants]
[status-im.utils.fx :as fx]
[status-im.ethereum.core :as ethereum]
[status-im.ui.screens.browser.eip3326.sheet :as sheet]
[utils.re-frame :as rf]))
[status-im.ui.screens.browser.eip3326.sheet :as sheet]))
(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
{:browser/send-to-bridge {:type constants/web3-send-async-callback
:messageId message-id
:error {:code 4001
:message "User rejected the request."}}
:dispatch [:bottom-sheet/hide]})
:error {:code 4001
: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)
networks (vals (get-in db [:networks/networks]))
exist-chain-ids (set (map ethereum/network->chain-id networks))
(let [target-chain-id (js/parseInt chainId 16)
networks (vals (get-in db [:networks/networks]))
exist-chain-ids (set (map ethereum/network->chain-id networks))
current-chain-id (ethereum/chain-id db)]
(if (exist-chain-ids target-chain-id)
(if (= current-chain-id target-chain-id)
{:browser/send-to-bridge {:type constants/web3-send-async-callback
{:browser/send-to-bridge {:type constants/web3-send-async-callback
:messageId message-id
:result {:jsonrpc "2.0"
:id (int id)
:result nil}}}
(let [target-network (first (filter #(= (ethereum/network->chain-id %1) target-chain-id)
networks))
:result {:jsonrpc "2.0"
:id (int id)
:result nil}}}
(let [target-network (first (filter #(= (ethereum/network->chain-id %1) target-chain-id) networks))
target-network-id (:id target-network)
current-network (ethereum/current-network db)
network-from (:name current-network)
network-to (:name target-network)
params (assoc params
:target-network-id target-network-id
:network-from network-from
:network-to network-to)]
{:dispatch [:bottom-sheet/show-sheet
{:content (fn []
[sheet/permissions-panel dapp-name message-id params])}]}))
{:browser/send-to-bridge {:type constants/web3-send-async-callback
current-network (ethereum/current-network db)
network-from (:name current-network)
network-to (:name target-network)
params (assoc params :target-network-id target-network-id :network-from network-from :network-to network-to)]
{:dispatch [:bottom-sheet/show-sheet {:content (fn []
[sheet/permissions-panel dapp-name message-id params])}]}))
{:browser/send-to-bridge {:type constants/web3-send-async-callback
:messageId message-id
:error
{:code 4902
:message
(str
"Unrecognized chain ID: "
target-chain-id
". Try adding the chain using wallet_addEthereumChain first.")}}})))
:error {:code 4902
:message (str "Unrecognized chain ID: " target-chain-id ". Try adding the chain using wallet_addEthereumChain first.")}}})))
+51 -57
View File
@@ -2,53 +2,51 @@
(:require [status-im.constants :as constants]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.i18n.i18n :as i18n]
[status-im.qr-scanner.core :as qr-scanner]
[status-im2.navigation.events :as navigation]
[utils.re-frame :as rf]))
[status-im.qr-scanner.core :as qr-scanner]
[status-im.utils.fx :as fx]))
(declare process-next-permission)
(declare send-response-to-bridge)
(def supported-permissions
{constants/dapp-permission-qr-code {:yield-control? true
:allowed? true}
constants/dapp-permission-contact-code {:type :profile
:title (i18n/label :t/wants-to-access-profile)
:description (i18n/label :t/your-contact-code)
:icon :main-icons/profile}
constants/dapp-permission-web3 {:type :wallet
:title (i18n/label :t/dapp-would-like-to-connect-wallet)
:description (i18n/label :t/allowing-authorizes-this-dapp)
:icon :main-icons/wallet}})
{constants/dapp-permission-qr-code {:yield-control? true
:allowed? true}
constants/dapp-permission-contact-code {:type :profile
:title (i18n/label :t/wants-to-access-profile)
:description (i18n/label :t/your-contact-code)
:icon :main-icons/profile}
constants/dapp-permission-web3 {:type :wallet
:title (i18n/label :t/dapp-would-like-to-connect-wallet)
: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]
{:requested-permission permission
:message-id message-id
:dapp-name dapp-name
:yield-control? yield-control?})})
{:db (assoc-in db [:browser/options :show-permission]
{:requested-permission permission
:message-id message-id
:dapp-name dapp-name
:yield-control? yield-control?})})
(defn get-permission-data
[cofx allowed-permission]
(defn get-permission-data [cofx allowed-permission]
(let [multiaccount (get-in cofx [:db :multiaccount])]
(get {constants/dapp-permission-contact-code (:public-key multiaccount)
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]
@@ -59,7 +57,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?
@@ -67,28 +65,28 @@
(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]
{: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
(fx/merge cofx
{:db (update-in db [:dapps/permissions] dissoc dapp)
::json-rpc/call [{:method "permissions_deleteDappPermissions"
:params [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)
@@ -97,7 +95,7 @@
: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"
@@ -107,47 +105,46 @@
{:db db}
(let [pending-permissions (get-in db [:browser/options :pending-permissions])
next-permission (last pending-permissions)
new-cofx (update-in cofx [:db :browser/options :pending-permissions] butlast)]
new-cofx (update-in cofx [:db :browser/options :pending-permissions] butlast)]
(when-let [{:keys [yield-control? permission message-id allowed? params]} next-permission]
(if (and yield-control? allowed?)
(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)
(let [{:keys [requested-permission message-id dapp-name]} (get-in db [:browser/options :show-permission])]
(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"
@@ -159,18 +156,15 @@
(not supported-permission)
(send-response-to-bridge cofx permission message-id false nil)
(and (or permission-allowed? (:allowed? supported-permission))
(not (:yield-control? supported-permission)))
(and (or permission-allowed? (:allowed? supported-permission)) (not (:yield-control? supported-permission)))
(send-response-to-bridge cofx permission message-id true (get-permission-data cofx permission))
:else
(process-next-permission (update-in cofx
[:db :browser/options :pending-permissions]
conj
{:permission permission
:allowed? (or permission-allowed?
(:allowed? supported-permission))
:yield-control? (:yield-control? supported-permission)
:params params
:message-id message-id})
(process-next-permission (update-in cofx [:db :browser/options :pending-permissions]
conj {:permission permission
:allowed? (or permission-allowed?
(:allowed? supported-permission))
:yield-control? (:yield-control? supported-permission)
:params params
:message-id message-id})
dapp-name))))
+147 -165
View File
@@ -1,33 +1,29 @@
(ns status-im.chat.models
(:require [clojure.set :as set]
[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.loading :as loading]
(:require [re-frame.core :as re-frame]
[taoensso.timbre :as log]
[status-im.multiaccounts.model :as multiaccounts.model]
[status-im.chat.models.message-list :as message-list]
[status-im.constants :as constants]
[status-im.data-store.chats :as chats-store]
[status-im.mailserver.core :as mailserver]
[status-im.data-store.contacts :as contacts-store]
[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]
[quo.design-system.colors :as colors]
[status-im.constants :as constants]
[status-im.utils.clocks :as utils.clocks]
[status-im.utils.types :as types]
[status-im.utils.fx :as fx]
[status-im.utils.utils :as utils]
[status-im2.contexts.chat.messages.message.delete-message-for-me.events :as delete-for-me]
[status-im2.contexts.chat.messages.message.delete-message.events :as delete-message]
[status-im2.navigation.events :as navigation]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
[status-im.utils.types :as types]
[status-im.add-new.db :as new-public-chat.db]
[status-im.chat.models.loading :as loading]
[status-im.ui.screens.chat.state :as chat.state]
[clojure.set :as set]
[status-im2.navigation.events :as navigation]))
(defn chats
[]
(defn chats []
(:chats (types/json->clj (js/require "./chats.js"))))
(defn- get-chat
[cofx chat-id]
(defn- get-chat [cofx chat-id]
(get-in cofx [:db :chats chat-id]))
(defn multi-user-chat?
@@ -51,8 +47,7 @@
([cofx chat-id]
(community-chat? (get-chat cofx chat-id))))
(defn active-chat?
[cofx chat-id]
(defn active-chat? [cofx chat-id]
(let [chat (get-chat cofx chat-id)]
(:active chat)))
@@ -88,56 +83,52 @@
(defn- create-new-chat
[chat-id {:keys [db now]}]
(let [name (get-in db [:contacts/contacts chat-id :name])]
{:chat-id chat-id
:name (or name "")
:color (rand-nth colors/chat-colors)
:chat-type constants/one-to-one-chat-type
:group-chat false
:timestamp now
:contacts #{chat-id}
:last-clock-value 0}))
{:chat-id chat-id
:name (or name "")
:color (rand-nth colors/chat-colors)
:chat-type constants/one-to-one-chat-type
:group-chat false
:timestamp now
:contacts #{chat-id}
:last-clock-value 0}))
(defn map-chats
[{:keys [db] :as cofx}]
(defn map-chats [{:keys [db] :as cofx}]
(fn [val]
(assoc
(merge
(or (get (:chats db) (:chat-id val))
(create-new-chat (:chat-id val) cofx))
val)
:invitation-admin
(:invitation-admin val))))
:invitation-admin (:invitation-admin val))))
(defn filter-chats
[db]
(defn filter-chats [db]
(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}]
: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]}
(reduce
(fn [acc {:keys [chat-id profile-public-key timeline? community-id active muted] :as chat}]
(if (not (or active muted))
(update acc :removed-chats conj chat-id)
(cond-> acc
(and (not profile-public-key) (not timeline?) (not community-id) active)
(update :chats-home-list conj chat-id)
:always
(assoc-in [:all-chats chat-id] chat))))
{:all-chats {}
:chats-home-list #{}
:removed-chats #{}}
(map (map-chats cofx) chats))]
(rf/merge
(reduce (fn [acc {:keys [chat-id profile-public-key timeline? community-id active muted] :as chat}]
(if (not (or active muted))
(update acc :removed-chats conj chat-id)
(cond-> acc
(and (not profile-public-key) (not timeline?) (not community-id) active)
(update :chats-home-list conj chat-id)
:always
(assoc-in [:all-chats chat-id] chat))))
{:all-chats {}
:chats-home-list #{}
:removed-chats #{}}
(map (map-chats cofx) chats))]
(fx/merge
cofx
(merge {:db (-> db
(update :chats merge all-chats)
@@ -150,66 +141,62 @@
(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?
deleted-at-clock-value]}
(get-in db [:chats chat-id])
deleted-at-clock-value]} (get-in db [:chats chat-id])
last-message-clock-value (if (and public? remove-chat?)
0
(or (:clock-value last-message)
deleted-at-clock-value
(utils.clocks/send 0)))]
{:db (-> db
(assoc-in [:messages chat-id] {})
(update-in [:message-lists] dissoc chat-id)
(update :chats
(fn [chats]
(if (contains? chats chat-id)
(update chats
chat-id
merge
{:last-message nil
:unviewed-messages-count 0
:unviewed-mentions-count 0
:deleted-at-clock-value last-message-clock-value})
chats))))}))
{:db (-> db
(assoc-in [:messages chat-id] {})
(update-in [:message-lists] dissoc chat-id)
(update :chats (fn [chats]
(if (contains? chats chat-id)
(update chats chat-id merge
{:last-message nil
:unviewed-messages-count 0
:unviewed-mentions-count 0
: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}]
(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 %)}]}
: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}]
{: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 %)}]}
: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
@@ -219,52 +206,50 @@
(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)
@@ -277,11 +262,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))
@@ -293,68 +278,65 @@
#(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)))
(let [chat (chats-store/<-rpc (first (:chats response)))
contact-rpc (first (:contacts response))
contact (when contact-rpc (contacts-store/<-rpc contact-rpc))]
{:db (cond-> db
contact
(assoc-in [:contacts/contacts chat-id] contact)
:always
(assoc-in [:chats chat-id] chat)
:always
(update :chats-home-list conj chat-id))
contact (when contact-rpc (contacts-store/<-rpc contact-rpc))]
{:db (cond-> db
contact
(assoc-in [:contacts/contacts chat-id] contact)
:always
(assoc-in [:chats chat-id] chat)
:always
(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}]
{::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 %)}]}))
:on-error #(log/error "failed to create one-to-on chat" chat-id %)}]}))
(defn profile-chat-topic
[public-key]
(defn profile-chat-topic [public-key]
(str "@" public-key))
(defn my-profile-chat-topic
[db]
(defn my-profile-chat-topic [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
(assoc-in [:chats chat-id] (chats-store/<-rpc (first (:chats response))))
(update :chats-home-list conj chat-id))
{:db (-> db
(assoc-in [:chats chat-id] (chats-store/<-rpc (first (:chats response))))
(update :chats-home-list conj chat-id))
:dispatch [:chat.ui/navigate-to-chat chat-id]})
(rf/defn create-public-chat-go
[_ chat-id]
{::json-rpc/call [{:method "wakuext_createPublicChat"
:params [{:id chat-id}]
(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 %)}]})
: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]
@@ -367,10 +349,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
@@ -380,19 +362,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}]
{::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 %)}]})))
: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]}]
@@ -406,40 +388,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? %])
{: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
@@ -450,19 +432,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)
@@ -471,44 +453,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]
{::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 %])}]})
: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]
{::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 %])}]})
: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
@@ -519,7 +501,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,12 +1,10 @@
(ns status-im2.contexts.chat.messages.message.delete-message-for-me.events
(:require
;; TODO move to status-im2
[status-im.chat.models.message-list :as message-list]
[status-im.utils.datetime :as datetime]
[status-im2.common.json-rpc.events :as json-rpc]
[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]
@@ -23,7 +21,7 @@
(update-in db
[:messages chat-id message-id]
assoc
:deleted-for-me? true
:deleted-for-me? true
:deleted-for-me-undoable-till (+ (datetime/timestamp)
undo-time-limit-ms))))
@@ -41,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]
@@ -50,13 +48,12 @@
(message-list/rebuild-message-list
{:db (update-db-delete-locally db chat-id message-id undo-time-limit-ms)}
chat-id)
:utils/dispatch-later
[{:dispatch [:chat.ui/delete-message-for-me-and-sync
{:chat-id chat-id
:message-id message-id}]
:ms undo-time-limit-ms}])))
:utils/dispatch-later [{:dispatch [:chat.ui/delete-message-for-me-and-sync
{:chat-id chat-id
: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])
@@ -64,7 +61,7 @@
{: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])
@@ -72,22 +69,22 @@
::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 %])}]}))
(defn- filter-pending-sync-messages
"traverse all messages find not yet synced deleted-for-me? messages"
:on-error #(log/error "failed to delete message for me " %)
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response
%])}]}))
(defn- chats-reducer
"traverse all messages find not yet synced deleted-for-me? messages, generate dispatch vector"
[acc chat-id messages]
(->> messages
(filter (fn [[_ {:keys [deleted-for-me? deleted-for-me-undoable-till]}]]
(and deleted-for-me? deleted-for-me-undoable-till)))
(map (fn [message] {:chat-id chat-id :message-id (first message)}))
(concat acc)))
(reduce-kv
(fn [inner-acc message-id {:keys [deleted-for-me? deleted-for-me-undoable-till]}]
(if (and deleted-for-me? deleted-for-me-undoable-till)
(conj inner-acc [:chat.ui/delete-message-for-me-and-sync chat-id message-id])
inner-acc))
acc
messages))
(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))))
[{:keys [db]}]
{:dispatch-n (reduce-kv chats-reducer [] (:messages db))})
@@ -0,0 +1,97 @@
(ns status-im.chat.models.delete-message-for-me-test
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.chat.models.delete-message-for-me :as
delete-message-for-me]
[status-im.utils.datetime :as datetime]))
(defonce mid "message-id")
(defonce cid "chat-id")
(deftest delete-for-me
(with-redefs [datetime/timestamp (constantly 1)]
(let [db {:messages {cid {mid {:id mid :whisper-timestamp 1}}}}
message {:message-id mid :chat-id cid}]
(testing "delete for me"
(let [result-message (get-in (delete-message-for-me/delete {:db db} message 1000)
[:db :messages cid mid])]
(is (= (:id result-message) mid))
(is (true? (:deleted-for-me? result-message)))
(is (= (:deleted-for-me-undoable-till result-message) 1001))))
(testing "should return nil if message not in db"
(is (= (delete-message-for-me/delete {:db {:messages []}} message 1000)
nil))))))
(deftest undo-delete-for-me
(let [db {:messages {cid {mid {:id mid :whisper-timestamp 1}}}}
message {:message-id mid :chat-id cid}]
(testing "undo delete for me in time"
(let [db (update-in db
[:messages cid mid]
assoc
:deleted-for-me? true
:deleted-for-me-undoable-till
(+ (datetime/timestamp) 1000))
result-message (get-in (delete-message-for-me/undo {:db db} message)
[:db :messages cid mid])]
(is (= (:id result-message) mid))
(is (nil? (:deleted-for-me? result-message)))
(is (nil? (:deleted-for-me-undoable-till result-message)))))
(testing "remain deleted for me when undo delete for me late"
(let [db (update-in db
[:messages cid mid]
assoc
:deleted-for-me? true
:deleted-for-me-undoable-till (- (datetime/timestamp) 1000))
result-message (get-in (delete-message-for-me/undo {:db db} message) [:db :messages cid mid])]
(is (= (:id result-message) mid))
(is (nil? (:deleted-for-me-undoable-till result-message)))
(is (true? (:deleted-for-me? result-message)))))
(testing "should return nil if message not in db"
(is (= (delete-message-for-me/undo {:db {:messages []}} message)
nil)))))
(deftest delete-for-me-and-sync
(let [db {:messages {cid {mid {:id mid}}}}
message {:message-id mid :chat-id cid}]
(testing "delete for me and sync"
(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 (:status-im.ethereum.json-rpc/call effects)]
(is (= result-db expected-db))
(is (= (count rpc-calls) 1))
(is (= (-> rpc-calls
first
:method)
"wakuext_deleteMessageForMeAndSync"))
(is (= (-> rpc-calls
first
:params
count)
2))
(is (= (-> rpc-calls
first
:params
first)
cid))
(is (= (-> rpc-calls
first
:params
second)
mid))))
(testing "delete for me and sync, should clean undo timer"
(let [expected-db {:messages {cid {mid {:id mid}}}}
effects (delete-message-for-me/delete-and-sync
{:db (update-in db
[:messages cid mid
:deleted-for-me-undoable-till]
(constantly (datetime/timestamp)))}
message)
result-db (:db effects)]
(is (= result-db expected-db))))
(testing "should return nil if message not in db"
(is (= (delete-message-for-me/delete-and-sync {:db {:messages []}}
message)
nil)))))
+101 -118
View File
@@ -1,53 +1,49 @@
(ns status-im.chat.models.images
(:require ["@react-native-community/cameraroll" :as CameraRoll]
(:require [re-frame.core :as re-frame]
[status-im.utils.fx :as fx]
["@react-native-community/cameraroll" :as CameraRoll]
["react-native-blob-util" :default ReactNativeBlobUtil]
[clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.chat.models :as chat]
[status-im.i18n.i18n :as i18n]
[status-im.utils.types :as types]
[status-im.utils.config :as config]
[status-im.ui.components.permissions :as permissions]
[status-im.ui.components.react :as react]
[status-im.utils.config :as config]
[status-im.utils.fs :as fs]
[status-im.utils.image-processing :as image-processing]
[status-im.utils.platform :as platform]
[status-im.utils.types :as types]
[status-im.utils.utils :as utils]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
[clojure.string :as string]
[status-im.i18n.i18n :as i18n]
[status-im.utils.utils :as utils]
[status-im.utils.platform :as platform]
[status-im.utils.fs :as fs]
[status-im.chat.models :as chat]))
(def maximum-image-size-px 2000)
(defn- resize-and-call
[uri cb]
(defn- resize-and-call [uri cb]
(react/image-get-size
uri
(fn [width height]
(let [resize? (> (max width height) maximum-image-size-px)]
(image-processing/resize
uri
(if resize? maximum-image-size-px width)
(if resize? maximum-image-size-px height)
60
(fn [^js resized-image]
(let [path (.-path resized-image)
path (if (string/starts-with? path "file") path (str "file://" path))]
(cb path)))
#(log/error "could not resize image" %))))))
uri
(fn [width height]
(let [resize? (> (max width height) maximum-image-size-px)]
(image-processing/resize
uri
(if resize? maximum-image-size-px width)
(if resize? maximum-image-size-px height)
60
(fn [^js resized-image]
(let [path (.-path resized-image)
path (if (string/starts-with? path "file") path (str "file://" path))]
(cb path)))
#(log/error "could not resize image" %))))))
(defn result->id
[^js result]
(defn result->id [^js result]
(if platform/ios?
(.-localIdentifier result)
(.-path result)))
(def temp-image-url (str (fs/cache-dir) "/StatusIm_Image.jpeg"))
(defn download-image-http
[base64-uri on-success]
(-> (.config ReactNativeBlobUtil
(clj->js {:trusty platform/ios?
:path temp-image-url}))
(defn download-image-http [base64-uri on-success]
(-> (.config ReactNativeBlobUtil (clj->js {:trusty platform/ios?
:path temp-image-url}))
(.fetch "GET" base64-uri)
(.then #(on-success (.path %)))
(.catch #(log/error "could not save image"))))
@@ -55,72 +51,66 @@
(defn save-to-gallery [path] (.save CameraRoll path))
(re-frame/reg-fx
::save-image-to-gallery
(fn [base64-uri]
(if platform/ios?
(-> (download-image-http base64-uri save-to-gallery)
(.catch #(utils/show-popup (i18n/label :t/error)
(i18n/label :t/external-storage-denied))))
(permissions/request-permissions
{:permissions [:write-external-storage]
:on-allowed #(download-image-http base64-uri save-to-gallery)
:on-denied (fn []
(utils/set-timeout
#(utils/show-popup (i18n/label :t/error)
(i18n/label :t/external-storage-denied))
50))}))))
::save-image-to-gallery
(fn [base64-uri]
(if platform/ios?
(-> (download-image-http base64-uri save-to-gallery)
(.catch #(utils/show-popup (i18n/label :t/error)
(i18n/label :t/external-storage-denied))))
(permissions/request-permissions
{:permissions [:write-external-storage]
:on-allowed #(download-image-http base64-uri save-to-gallery)
:on-denied (fn []
(utils/set-timeout
#(utils/show-popup (i18n/label :t/error)
(i18n/label :t/external-storage-denied))
50))}))))
(re-frame/reg-fx
::chat-open-image-picker-camera
(fn [current-chat-id]
(react/show-image-picker-camera
#(re-frame/dispatch [:chat.ui/image-captured current-chat-id (.-path %)])
{})))
::chat-open-image-picker-camera
(fn [current-chat-id]
(react/show-image-picker-camera
#(re-frame/dispatch [:chat.ui/image-captured current-chat-id (.-path %)]) {})))
(re-frame/reg-fx
::chat-open-image-picker
(fn [chat-id]
(react/show-image-picker
(fn [^js images]
;; NOTE(Ferossgp): Because we can't highlight the already selected images inside
;; gallery, we just clean previous state and set all newly picked images
(when (and platform/ios? (pos? (count images)))
(re-frame/dispatch [:chat.ui/clear-sending-images chat-id]))
(doseq [^js result (if platform/ios?
(take config/max-images-batch images)
[images])]
(resize-and-call (.-path result)
#(re-frame/dispatch [:chat.ui/image-selected chat-id (result->id result) %]))))
;; NOTE(Ferossgp): On android you cannot set max limit on images, when a user
;; selects too many images the app crashes.
{:media-type "photo"
:multiple platform/ios?})))
::chat-open-image-picker
(fn [chat-id]
(react/show-image-picker
(fn [^js images]
;; NOTE(Ferossgp): Because we can't highlight the already selected images inside
;; gallery, we just clean previous state and set all newly picked images
(when (and platform/ios? (pos? (count images)))
(re-frame/dispatch [:chat.ui/clear-sending-images chat-id]))
(doseq [^js result (if platform/ios?
(take config/max-images-batch images)
[images])]
(resize-and-call (.-path result)
#(re-frame/dispatch [:chat.ui/image-selected chat-id (result->id result) %]))))
;; NOTE(Ferossgp): On android you cannot set max limit on images, when a user
;; selects too many images the app crashes.
{:media-type "photo"
:multiple platform/ios?})))
(re-frame/reg-fx
::image-selected
(fn [[uri chat-id]]
(resize-and-call
uri
#(re-frame/dispatch [:chat.ui/image-selected chat-id uri %]))))
::image-selected
(fn [[uri chat-id]]
(resize-and-call
uri
#(re-frame/dispatch [:chat.ui/image-selected chat-id uri %]))))
(re-frame/reg-fx
::camera-roll-get-photos
(fn [[num end-cursor]]
(permissions/request-permissions
{:permissions [:read-external-storage]
:on-allowed (fn []
(-> (if end-cursor
(.getPhotos
CameraRoll
#js {:first num :after end-cursor :assetType "Photos" :groupTypes "All"})
(.getPhotos CameraRoll
#js {:first num :assetType "Photos" :groupTypes "All"}))
(.then #(let [response (types/js->clj %)]
(re-frame/dispatch [:on-camera-roll-get-photos (:edges response)
(:page_info response) end-cursor])))
(.catch #(log/warn "could not get camera roll photos"))))})))
::camera-roll-get-photos
(fn [[num end-cursor]]
(permissions/request-permissions
{:permissions [:read-external-storage]
:on-allowed (fn []
(-> (if end-cursor
(.getPhotos CameraRoll #js {:first num :after end-cursor :assetType "Photos" :groupTypes "All"})
(.getPhotos CameraRoll #js {:first num :assetType "Photos" :groupTypes "All"}))
(.then #(re-frame/dispatch [:on-camera-roll-get-photos (:edges (types/js->clj %)) (:page_info (types/js->clj %)) end-cursor]))
(.catch #(log/warn "could not get camera roll photos"))))})))
(rf/defn image-captured
(fx/defn image-captured
{:events [:chat.ui/image-captured]}
[{:keys [db]} chat-id uri]
(let [current-chat-id (or chat-id (:current-chat-id db))
@@ -129,61 +119,54 @@
(not (get images uri)))
{::image-selected [uri current-chat-id]})))
(rf/defn on-end-reached
{:events [:camera-roll/on-end-reached]}
[_ end-cursor loading? has-next-page?]
(when (and (not loading?) has-next-page?)
(re-frame/dispatch [:chat.ui/camera-roll-loading-more true])
(re-frame/dispatch [:chat.ui/camera-roll-get-photos 20 end-cursor])))
(rf/defn camera-roll-get-photos
(fx/defn camera-roll-get-photos
{:events [:chat.ui/camera-roll-get-photos]}
[_ num end-cursor]
{::camera-roll-get-photos [num end-cursor]})
(rf/defn camera-roll-loading-more
(fx/defn camera-roll-loading-more
{:events [:chat.ui/camera-roll-loading-more]}
[{:keys [db]} is-loading]
{:db (assoc db :camera-roll/loading-more is-loading)})
{:db (assoc db :camera-roll-loading-more is-loading)})
(rf/defn on-camera-roll-get-photos
(fx/defn on-camera-roll-get-photos
{:events [:on-camera-roll-get-photos]}
[{:keys [db] :as cofx} photos page-info end-cursor]
(let [photos_x (when end-cursor (:camera-roll/photos db))]
(let [photos_x (when end-cursor (:camera-roll-photos db))]
{:db (-> db
(assoc :camera-roll/photos (concat photos_x (map #(get-in % [:node :image :uri]) photos)))
(assoc :camera-roll/end-cursor (:end_cursor page-info))
(assoc :camera-roll/has-next-page (:has_next_page page-info))
(assoc :camera-roll/loading-more false))}))
(assoc :camera-roll-photos (concat photos_x (map #(get-in % [:node :image :uri]) photos)))
(assoc :camera-roll-end-cursor (:end_cursor page-info))
(assoc :camera-roll-has-next-page (:has_next_page page-info))
(assoc :camera-roll-loading-more false))}))
(rf/defn clear-sending-images
(fx/defn clear-sending-images
{:events [:chat.ui/clear-sending-images]}
[{:keys [db]} current-chat-id]
{:db (update-in db [:chat/inputs current-chat-id :metadata] assoc :sending-image {})})
(rf/defn cancel-sending-image
(fx/defn cancel-sending-image
{:events [:chat.ui/cancel-sending-image]}
[{:keys [db] :as cofx} chat-id]
(let [current-chat-id (or chat-id (:current-chat-id db))]
(clear-sending-images cofx current-chat-id)))
(rf/defn cancel-sending-image-timeline
(fx/defn cancel-sending-image-timeline
{:events [:chat.ui/cancel-sending-image-timeline]}
[{:keys [db] :as cofx}]
(cancel-sending-image cofx (chat/my-profile-chat-topic db)))
(rf/defn image-selected
(fx/defn image-selected
{:events [:chat.ui/image-selected]}
[{:keys [db]} current-chat-id original uri]
{:db (update-in db [:chat/inputs current-chat-id :metadata :sending-image original] merge {:uri uri})})
(rf/defn image-unselected
(fx/defn image-unselected
{:events [:chat.ui/image-unselected]}
[{:keys [db]} original]
(let [current-chat-id (:current-chat-id db)]
{:db (update-in db [:chat/inputs current-chat-id :metadata :sending-image] dissoc original)}))
(rf/defn chat-open-image-picker
(fx/defn chat-open-image-picker
{:events [:chat.ui/open-image-picker]}
[{:keys [db]} chat-id]
(let [current-chat-id (or chat-id (:current-chat-id db))
@@ -191,12 +174,12 @@
(when (< (count images) config/max-images-batch)
{::chat-open-image-picker current-chat-id})))
(rf/defn chat-open-image-picker-timeline
(fx/defn chat-open-image-picker-timeline
{:events [:chat.ui/open-image-picker-timeline]}
[{:keys [db] :as cofx}]
(chat-open-image-picker cofx (chat/my-profile-chat-topic db)))
(rf/defn chat-show-image-picker-camera
(fx/defn chat-show-image-picker-camera
{:events [:chat.ui/show-image-picker-camera]}
[{:keys [db]} chat-id]
(let [current-chat-id (or chat-id (:current-chat-id db))
@@ -204,12 +187,12 @@
(when (< (count images) config/max-images-batch)
{::chat-open-image-picker-camera current-chat-id})))
(rf/defn chat-show-image-picker-camera-timeline
(fx/defn chat-show-image-picker-camera-timeline
{:events [:chat.ui/show-image-picker-camera-timeline]}
[{:keys [db] :as cofx}]
(chat-show-image-picker-camera cofx (chat/my-profile-chat-topic db)))
(rf/defn camera-roll-pick
(fx/defn camera-roll-pick
{:events [:chat.ui/camera-roll-pick]}
[{:keys [db]} uri chat-id]
(let [current-chat-id (or chat-id (:current-chat-id db))
@@ -221,12 +204,12 @@
(not (get images uri)))
{::image-selected [uri current-chat-id]}))))
(rf/defn camera-roll-pick-timeline
(fx/defn camera-roll-pick-timeline
{:events [:chat.ui/camera-roll-pick-timeline]}
[{:keys [db] :as cofx} uri]
(camera-roll-pick cofx uri (chat/my-profile-chat-topic db)))
(rf/defn save-image-to-gallery
(fx/defn save-image-to-gallery
{:events [:chat.ui/save-image-to-gallery]}
[_ base64-uri]
{::save-image-to-gallery base64-uri})
+115 -124
View File
@@ -1,20 +1,20 @@
(ns status-im.chat.models.input
(:require ["emojilib" :as emojis]
[clojure.string :as string]
(:require [clojure.string :as string]
[goog.object :as object]
[re-frame.core :as re-frame]
[taoensso.timbre :as log]
[status-im.chat.constants :as chat.constants]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.chat.models :as chat]
[status-im.chat.models.mentions :as mentions]
[status-im.chat.models.message :as chat.message]
[status-im.chat.models.message-content :as message-content]
[status-im.constants :as constants]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.i18n.i18n :as i18n]
[status-im.utils.datetime :as datetime]
[status-im.utils.utils :as utils]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
[status-im.utils.fx :as fx]
["emojilib" :as emojis]
[status-im.chat.models.mentions :as mentions]
[status-im.utils.utils :as utils]))
(defn text->emoji
"Replaces emojis in a specified `text`"
@@ -26,7 +26,7 @@
(.-char ^js emoji-map)
original))))
(rf/defn set-chat-input-text
(fx/defn set-chat-input-text
"Set input text for current-chat. Takes db and input text and cofx
as arguments and returns new fx. Always clear all validation messages."
{:events [:chat.ui/set-chat-input-text]}
@@ -34,26 +34,24 @@
(let [current-chat-id (or chat-id (:current-chat-id db))]
{:db (assoc-in db [:chat/inputs current-chat-id :input-text] (text->emoji new-input))}))
(rf/defn set-timeline-input-text
(fx/defn set-timeline-input-text
{:events [:chat.ui/set-timeline-input-text]}
[{db :db} new-input]
{:db (assoc-in db
[:chat/inputs (chat/my-profile-chat-topic db) :input-text]
(text->emoji new-input))})
{:db (assoc-in db [:chat/inputs (chat/my-profile-chat-topic db) :input-text]
(text->emoji new-input))})
(rf/defn select-mention
(fx/defn select-mention
{:events [:chat.ui/select-mention]}
[{:keys [db] :as cofx} text-input-ref {:keys [alias name searched-text match] :as user}]
(let [chat-id (:current-chat-id db)
new-text (mentions/new-input-text-with-mention cofx user)
at-sign-idx (get-in db [:chats/mentions chat-id :mentions :at-sign-idx])
cursor (+ at-sign-idx (count name) 2)]
(rf/merge
(fx/merge
cofx
{:db (-> db
(assoc-in [:chats/cursor chat-id] cursor)
(assoc-in [:chats/mention-suggestions chat-id] nil))
:set-text-input-value [chat-id new-text text-input-ref]}
{:db (-> db
(assoc-in [:chats/cursor chat-id] cursor)
(assoc-in [:chats/mention-suggestions chat-id] nil))}
(set-chat-input-text new-text chat-id)
;; NOTE(rasom): Some keyboards do not react on selection property passed to
;; text input (specifically Samsung keyboard with predictive text set on).
@@ -75,77 +73,78 @@
:end end}))
(mentions/recheck-at-idxs {alias user}))))
(defn- start-cooldown
[{:keys [db]} cooldowns]
(defn- start-cooldown [{:keys [db]} cooldowns]
{:dispatch-later [{:dispatch [:chat/disable-cooldown]
:ms (chat.constants/cooldown-periods-ms
cooldowns
chat.constants/default-cooldown-period-ms)}]
:show-cooldown-warning nil
:db (assoc db
:chat/cooldowns (if
(=
chat.constants/cooldown-reset-threshold
cooldowns)
0
cooldowns)
:chat/cooldowns (if (= chat.constants/cooldown-reset-threshold cooldowns)
0
cooldowns)
:chat/spam-messages-frequency 0
:chat/cooldown-enabled? true)})
:chat/cooldown-enabled? true)})
(rf/defn process-cooldown
(fx/defn process-cooldown
"Process cooldown to protect against message spammers"
[{{:keys [chat/last-outgoing-message-sent-at
chat/cooldowns
chat/spam-messages-frequency
current-chat-id]
:as db}
:db
:as cofx}]
current-chat-id] :as db} :db :as cofx}]
(when (chat/public-chat? cofx current-chat-id)
(let [spamming-fast? (< (- (datetime/timestamp) last-outgoing-message-sent-at)
(+ chat.constants/spam-interval-ms (* 1000 cooldowns)))
spamming-frequently? (= chat.constants/spam-message-frequency-threshold
spam-messages-frequency)]
(let [spamming-fast? (< (- (datetime/timestamp) last-outgoing-message-sent-at)
(+ chat.constants/spam-interval-ms (* 1000 cooldowns)))
spamming-frequently? (= chat.constants/spam-message-frequency-threshold spam-messages-frequency)]
(cond-> {:db (assoc db
:chat/last-outgoing-message-sent-at (datetime/timestamp)
:chat/spam-messages-frequency (if spamming-fast?
(inc spam-messages-frequency)
0))}
:chat/spam-messages-frequency (if spamming-fast?
(inc spam-messages-frequency)
0))}
(and spamming-fast? spamming-frequently?)
(start-cooldown (inc cooldowns))))))
(rf/defn reply-to-message
(fx/defn reply-to-message
"Sets reference to previous chat message and focuses on input"
{:events [:chat.ui/reply-to-message]}
[{:keys [db] :as cofx} message]
(let [current-chat-id (:current-chat-id db)]
(rf/merge cofx
(fx/merge cofx
{:db (-> db
(assoc-in [:chat/inputs current-chat-id :metadata :responding-to-message]
message)
(assoc-in [:chat/inputs current-chat-id :metadata :editing-message] nil)
(update-in [:chat/inputs current-chat-id :metadata]
dissoc
:sending-image))})))
dissoc :sending-image))})))
(rf/defn edit-message
(fx/defn edit-message
"Sets reference to previous chat message and focuses on input"
{:events [:chat.ui/edit-message]}
[{:keys [db] :as cofx} message]
(let [current-chat-id (:current-chat-id db)
text (get-in message [:content :text])]
text (get-in message [:content :text])]
{:dispatch [:chat.ui.input/set-chat-input-text text current-chat-id]
:db (-> db
(assoc-in [:chat/inputs current-chat-id :metadata :editing-message]
message)
(assoc-in [:chat/inputs current-chat-id :metadata :responding-to-message] nil)
(update-in [:chat/inputs current-chat-id :metadata]
dissoc
:sending-image))}))
:set-input-text [current-chat-id text]
:db (-> db
(assoc-in [:chat/inputs current-chat-id :metadata :editing-message]
message)
(assoc-in [:chat/inputs current-chat-id :metadata :responding-to-message] nil)
(update-in [:chat/inputs current-chat-id :metadata]
dissoc :sending-image))}))
(rf/defn show-contact-request-input
(fx/defn soft-delete-message
"Does't delete from db, this is a soft delete"
{:events [:chat.ui/soft-delete-message]}
[{:keys [db] :as cofx} {:keys [message-id chat-id]}]
{::json-rpc/call [{:method "wakuext_deleteMessageAndSend"
:params [message-id]
:js-response true
:on-error #(log/error "failed to delete message message " %)
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]})
(fx/defn show-contact-request-input
"Sets reference to previous chat message and focuses on input"
{:events [:chat.ui/send-contact-request]}
[{:keys [db] :as cofx}]
@@ -157,10 +156,9 @@
nil)
(assoc-in [:chat/inputs current-chat-id :metadata :editing-message] nil)
(update-in [:chat/inputs current-chat-id :metadata]
dissoc
:sending-image))}))
dissoc :sending-image))}))
(rf/defn cancel-message-reply
(fx/defn cancel-message-reply
"Cancels stage message reply"
{:events [:chat.ui/cancel-message-reply]}
[{:keys [db]}]
@@ -172,16 +170,16 @@
(when-not (string/blank? input-text)
(let [{:keys [message-id]}
(get-in db [:chat/inputs current-chat-id :metadata :responding-to-message])
preferred-name (get-in db [:multiaccount :preferred-name])
emoji? (message-content/emoji-only-content? {:text input-text
:response-to message-id})]
preferred-name (get-in db [:multiaccount :preferred-name])
emoji? (message-content/emoji-only-content? {:text input-text
:response-to message-id})]
{:chat-id current-chat-id
:content-type (if emoji?
constants/content-type-emoji
constants/content-type-text)
:text input-text
:response-to message-id
:ens-name preferred-name})))
:text input-text
:response-to message-id
:ens-name preferred-name})))
(defn build-image-messages
[{db :db} chat-id]
@@ -193,9 +191,8 @@
:text (i18n/label :t/update-to-see-image {"locale" "en"})})
images)))
(rf/defn clean-input
[{:keys [db] :as cofx} current-chat-id]
(rf/merge cofx
(fx/defn clean-input [{:keys [db] :as cofx} current-chat-id]
(fx/merge cofx
{:db (-> db
(assoc-in [:chat/inputs current-chat-id :metadata :sending-contact-request] nil)
(assoc-in [:chat/inputs current-chat-id :metadata :sending-image] nil)
@@ -203,137 +200,131 @@
(assoc-in [:chat/inputs current-chat-id :metadata :responding-to-message] nil))}
(set-chat-input-text nil current-chat-id)))
(rf/defn cancel-message-edit
(fx/defn cancel-message-edit
"Cancels stage message edit"
{:events [:chat.ui/cancel-message-edit]}
[{:keys [db] :as cofx}]
(let [current-chat-id (:current-chat-id db)]
(rf/merge cofx
{:set-text-input-value [current-chat-id ""]}
(fx/merge cofx
{:set-input-text [current-chat-id ""]}
(clean-input current-chat-id)
(mentions/clear-mentions)
(mentions/clear-cursor))))
(rf/defn send-messages
[{:keys [db] :as cofx} input-text current-chat-id]
(fx/defn send-messages [{:keys [db] :as cofx} input-text current-chat-id]
(let [image-messages (build-image-messages cofx current-chat-id)
text-message (build-text-message cofx input-text current-chat-id)
messages (keep identity (conj image-messages text-message))]
text-message (build-text-message cofx input-text current-chat-id)
messages (keep identity (conj image-messages text-message))]
(when (seq messages)
(rf/merge cofx
(fx/merge cofx
(clean-input (:current-chat-id db))
(process-cooldown)
(chat.message/send-messages messages)))))
(rf/defn send-my-status-message
(fx/defn send-my-status-message
"when not empty, proceed by sending text message with public key topic"
{:events [:profile.ui/send-my-status-message]}
[{db :db :as cofx}]
(let [current-chat-id (chat/my-profile-chat-topic db)
(let [current-chat-id (chat/my-profile-chat-topic db)
{:keys [input-text]} (get-in db [:chat/inputs current-chat-id])
image-messages (build-image-messages cofx current-chat-id)
text-message (build-text-message cofx input-text current-chat-id)
messages (keep identity (conj image-messages text-message))]
image-messages (build-image-messages cofx current-chat-id)
text-message (build-text-message cofx input-text current-chat-id)
messages (keep identity (conj image-messages text-message))]
(when (seq messages)
(rf/merge cofx
(fx/merge cofx
(clean-input current-chat-id)
(chat.message/send-messages messages)))))
(rf/defn send-audio-message
(fx/defn send-audio-message
[cofx audio-path duration current-chat-id]
(when-not (string/blank? audio-path)
(chat.message/send-message
cofx
{:chat-id current-chat-id
:content-type constants/content-type-audio
:audio-path audio-path
:audio-duration-ms duration
:text (i18n/label :t/update-to-listen-audio {"locale" "en"})})))
(chat.message/send-message cofx {:chat-id current-chat-id
:content-type constants/content-type-audio
:audio-path audio-path
:audio-duration-ms duration
:text (i18n/label :t/update-to-listen-audio {"locale" "en"})})))
(rf/defn send-sticker-message
(fx/defn send-sticker-message
[cofx {:keys [hash packID pack]} current-chat-id]
(when-not (or (string/blank? hash) (and (string/blank? packID) (string/blank? pack)))
(chat.message/send-message cofx
{:chat-id current-chat-id
:content-type constants/content-type-sticker
:sticker {:hash hash
(chat.message/send-message cofx {:chat-id current-chat-id
:content-type constants/content-type-sticker
:sticker {:hash hash
:pack (int (if (string/blank? packID) pack packID))}
:text (i18n/label :t/update-to-see-sticker {"locale" "en"})})))
:text (i18n/label :t/update-to-see-sticker {"locale" "en"})})))
(rf/defn send-edited-message
[{:keys [db] :as cofx} text {:keys [message-id quoted-message]}]
(rf/merge
(fx/defn send-edited-message [{:keys [db] :as cofx} text {:keys [message-id quoted-message]}]
(fx/merge
cofx
{::json-rpc/call [{:method "wakuext_editMessage"
:params [{:id message-id
:text text
:content-type (if (message-content/emoji-only-content?
{:text text :response-to quoted-message})
constants/content-type-emoji
constants/content-type-text)}]
{::json-rpc/call [{:method "wakuext_editMessage"
:params [{:id message-id
:text text
:content-type (if (message-content/emoji-only-content? {:text text :response-to quoted-message})
constants/content-type-emoji
constants/content-type-text)}]
:js-response true
:on-error #(log/error "failed to edit message " %)
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]}
:on-error #(log/error "failed to edit message " %)
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]}
(cancel-message-edit)
(process-cooldown)))
(rf/defn send-current-message
(fx/defn send-current-message
"Sends message from current chat input"
{:events [:chat.ui/send-current-message]}
[{{:keys [current-chat-id] :as db} :db :as cofx}]
(let [{:keys [input-text metadata]} (get-in db [:chat/inputs current-chat-id])
editing-message (:editing-message metadata)
input-text-with-mentions (mentions/check-mentions cofx input-text)]
(rf/merge cofx
editing-message (:editing-message metadata)
input-text-with-mentions (mentions/check-mentions cofx input-text)]
(fx/merge cofx
(if editing-message
(send-edited-message input-text-with-mentions editing-message)
(send-messages input-text-with-mentions current-chat-id))
(mentions/clear-mentions)
(mentions/clear-cursor))))
(rf/defn send-contact-request
(fx/defn send-contact-request
{:events [:contacts/send-contact-request]}
[{:keys [db] :as cofx} public-key message]
(rf/merge cofx
(fx/merge cofx
{:chat.ui/clear-inputs nil
:chat.ui/clear-inputs-old nil
::json-rpc/call [{:method "wakuext_sendContactRequest"
::json-rpc/call [{:method "wakuext_sendContactRequest"
:js-response true
:params [{:id public-key :message message}]
:on-error #(log/warn "failed to send a contact request" %)
:on-success #(re-frame/dispatch [:transport/message-sent %])}]}
:params [{:id public-key :message message}]
:on-error #(log/warn "failed to send a contact request" %)
:on-success #(re-frame/dispatch [:transport/message-sent %])}]}
(mentions/clear-mentions)
(mentions/clear-cursor)
(clean-input (:current-chat-id db))
(process-cooldown)))
(rf/defn cancel-contact-request
(fx/defn cancel-contact-request
"Cancels contact request"
{:events [:chat.ui/cancel-contact-request]}
[{:keys [db] :as cofx}]
(let [current-chat-id (:current-chat-id db)]
(rf/merge cofx
(fx/merge cofx
{:db (assoc-in db [:chat/inputs current-chat-id :metadata :sending-contact-request] nil)}
(mentions/clear-mentions)
(mentions/clear-cursor)
(clean-input (:current-chat-id db))
(process-cooldown))))
(rf/defn chat-send-sticker
(fx/defn chat-send-sticker
{:events [:chat/send-sticker]}
[{{:keys [current-chat-id] :as db} :db :as cofx} {:keys [hash packID pack] :as sticker}]
(rf/merge
(fx/merge
cofx
{:db (update db
:stickers/recent-stickers
(fn [recent]
(conj (remove #(= hash (:hash %)) recent) sticker)))
{:db (update db
:stickers/recent-stickers
(fn [recent]
(conj (remove #(= hash (:hash %)) recent) sticker)))
::json-rpc/call [{:method "stickers_addRecent"
:params [(int (if (string/blank? packID) pack packID)) hash]
:on-success #()}]}
(send-sticker-message sticker current-chat-id)))
(rf/defn chat-send-audio
(fx/defn chat-send-audio
{:events [:chat/send-audio]}
[{{:keys [current-chat-id]} :db :as cofx} audio-path duration]
(send-audio-message cofx audio-path duration current-chat-id))
+29 -37
View File
@@ -1,15 +1,15 @@
(ns status-im.chat.models.link-preview
(:require [re-frame.core :as re-frame]
[status-im.communities.core :as models.communities]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.utils.fx :as fx]
[status-im.multiaccounts.update.core :as multiaccounts.update]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.communities.core :as models.communities]
[taoensso.timbre :as log]))
(rf/defn enable
(fx/defn enable
{:events [::enable]}
[{{:keys [multiaccount]} :db :as cofx} site enabled?]
(rf/merge cofx
(fx/merge cofx
(multiaccounts.update/multiaccount-update
:link-previews-enabled-sites
(if enabled?
@@ -17,10 +17,10 @@
(disj (get multiaccount :link-previews-enabled-sites #{}) site))
{})))
(rf/defn enable-all
(fx/defn enable-all
{:events [::enable-all]}
[{{:keys [multiaccount]} :db :as cofx} link-previews-whitelist enabled?]
(rf/merge cofx
(fx/merge cofx
(multiaccounts.update/multiaccount-update
:link-previews-enabled-sites
(if enabled?
@@ -28,42 +28,37 @@
#{})
{})))
(defn community-resolved
[db community-id]
(defn community-resolved [db community-id]
(update db :communities/resolve-community-info dissoc community-id))
(defn community-failed-to-resolve
[db community-id]
(defn community-failed-to-resolve [db community-id]
(update db :communities/resolve-community-info dissoc community-id))
(defn community-resolving
[db community-id]
(defn community-resolving [db community-id]
(assoc-in db [:communities/resolve-community-info community-id] true))
(rf/defn handle-community-failed-to-resolve
(fx/defn handle-community-failed-to-resolve
{:events [::community-failed-to-resolve]}
[{:keys [db]} community-id]
{:db (community-failed-to-resolve db community-id)})
(defn community-link
[id]
(defn community-link [id]
(str "https://join.status.im/c/" id))
(rf/defn handle-community-resolved
(fx/defn handle-community-resolved
{:events [::community-resolved]}
[{:keys [db] :as cofx} community-id community]
(rf/merge cofx
(fx/merge cofx
(cond-> {:db (community-resolved db community-id)}
(some? community)
(assoc :dispatch
[::cache-link-preview-data
(community-link community-id) community]))
(assoc :dispatch [::cache-link-preview-data
(community-link community-id) community]))
(models.communities/handle-community community)))
(rf/defn resolve-community-info
(fx/defn resolve-community-info
{:events [::resolve-community-info]}
[{:keys [db]} community-id]
{:db (community-resolving db community-id)
{:db (community-resolving db community-id)
::json-rpc/call [{:method "wakuext_requestCommunityInfoFromMailserver"
:params [community-id]
:on-success #(re-frame/dispatch [::community-resolved community-id %])
@@ -71,18 +66,17 @@
(re-frame/dispatch [::community-failed-to-resolve community-id])
(log/error "Failed to request community info from mailserver"))}]})
(rf/defn load-link-preview-data
(fx/defn load-link-preview-data
{:events [::load-link-preview-data]}
[cofx link]
{::json-rpc/call [{:method "wakuext_getLinkPreviewData"
:params [link]
:on-success #(re-frame/dispatch [::cache-link-preview-data link %])
:on-error #(re-frame/dispatch
[::cache-link-preview-data
link
{:error (str "Can't get preview data for " link)}])}]})
:on-error #(re-frame/dispatch [::cache-link-preview-data
link
{:error (str "Can't get preview data for " link)}])}]})
(rf/defn cache-link-preview-data
(fx/defn cache-link-preview-data
{:events [::cache-link-preview-data]}
[{{:keys [multiaccount]} :db :as cofx} site data]
(multiaccounts.update/optimistic
@@ -96,25 +90,23 @@
(community-link id)
community]))
(rf/defn should-suggest-link-preview
(fx/defn should-suggest-link-preview
{:events [::should-suggest-link-preview]}
[{:keys [db] :as cofx} enabled?]
(multiaccounts.update/multiaccount-update
cofx
:link-preview-request-enabled
(boolean enabled?)
:link-preview-request-enabled (boolean enabled?)
{}))
(rf/defn request-link-preview-whitelist
(fx/defn request-link-preview-whitelist
[_]
{::json-rpc/call [{:method "wakuext_getLinkPreviewWhitelist"
:params []
:on-success #(re-frame/dispatch [::link-preview-whitelist-received %])
:on-error #(log/error "Failed to get link preview whitelist")}]})
(rf/defn save-link-preview-whitelist
(fx/defn save-link-preview-whitelist
{:events [::link-preview-whitelist-received]}
[{:keys [db]} whitelist]
{:db (assoc db
:link-previews-whitelist
{:db (assoc db :link-previews-whitelist
whitelist)})
+47 -56
View File
@@ -1,25 +1,25 @@
(ns status-im.chat.models.loading
(:require [re-frame.core :as re-frame]
[status-im.chat.models.message-list :as message-list]
[status-im.constants :as constants]
[status-im.data-store.chats :as data-store.chats]
[status-im.data-store.messages :as data-store.messages]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im2.contexts.activity-center.events :as activity-center]
[status-im.utils.fx :as fx]
[status-im.chat.models.message-list :as message-list]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.chat.models.pin-message :as models.pin-message]
[status-im.notifications-center.core :as notification-center]))
(defn cursor->clock-value
[^js cursor]
(js/parseInt (.substring cursor 51 64)))
(defn clock-value->cursor
[clock-value]
(defn clock-value->cursor [clock-value]
(str "000000000000000000000000000000000000000000000000000"
clock-value
"0x0000000000000000000000000000000000000000000000000000000000000000"))
(rf/defn update-chats-in-app-db
(fx/defn update-chats-in-app-db
{:events [:chats-list/load-success]}
[{:keys [db]} ^js new-chats-js]
(let [{:keys [all-chats chats-home-list]}
@@ -31,28 +31,27 @@
(update :chats-home-list conj chat-id)
:always
(assoc-in [:all-chats chat-id] chat))))
{:all-chats {}
{:all-chats {}
:chats-home-list #{}}
new-chats-js)]
{:db (assoc db
:chats all-chats
{:db (assoc db :chats all-chats
:chats-home-list chats-home-list
:chats/loading? false)}))
:chats/loading? false)}))
(rf/defn load-chat-success
(fx/defn load-chat-success
{:events [:chats-list/load-chat-success]}
[{:keys [db]} ^js chat]
(let [{:keys [chat-id] :as chat} (data-store.chats/<-rpc chat)]
{:db (update-in db [:chats chat-id] merge chat)}))
(rf/defn load-chat
(fx/defn load-chat
[_ chat-id]
{::json-rpc/call [{:method "wakuext_chat"
:params [chat-id]
{::json-rpc/call [{:method "wakuext_chat"
:params [chat-id]
:on-success #(re-frame/dispatch [:chats-list/load-chat-success %])
:on-error #(log/error "failed to fetch chats" 0 -1 %)}]})
:on-error #(log/error "failed to fetch chats" 0 -1 %)}]})
(rf/defn handle-failed-loading-messages
(fx/defn handle-failed-loading-messages
{:events [::failed-loading-messages]}
[{:keys [db]} current-chat-id _ err]
(log/error "failed loading messages" current-chat-id err)
@@ -61,55 +60,52 @@
(defn mark-chat-all-read
[db chat-id]
(update-in db
[:chats chat-id]
assoc
(update-in db [:chats chat-id] assoc
:unviewed-messages-count 0
:unviewed-mentions-count 0
:highlight false))
:highlight false))
(rf/defn handle-mark-all-read-successful
(fx/defn handle-mark-all-read-successful
{:events [::mark-all-read-successful]}
[cofx]
(activity-center/notifications-fetch-unread-count cofx))
(notification-center/get-activity-center-notifications-count cofx))
(rf/defn handle-mark-all-read-in-community-successful
(fx/defn handle-mark-all-read-in-community-successful
{:events [::mark-all-read-in-community-successful]}
[{:keys [db] :as cofx} chat-ids]
(rf/merge cofx
(fx/merge cofx
{:db (reduce mark-chat-all-read db chat-ids)}
(activity-center/notifications-fetch-unread-count)))
(notification-center/get-activity-center-notifications-count)))
(rf/defn handle-mark-all-read
(fx/defn handle-mark-all-read
{:events [:chat.ui/mark-all-read-pressed :chat/mark-all-as-read]}
[{db :db} chat-id]
{:db (mark-chat-all-read db chat-id)
:clear-message-notifications [[chat-id]
(get-in db [:multiaccount :remote-push-notifications-enabled?])]
::json-rpc/call [{:method "wakuext_markAllRead"
:params [chat-id]
:on-success #(re-frame/dispatch [::mark-all-read-successful])}]})
{:db (mark-chat-all-read db chat-id)
:clear-message-notifications [[chat-id]
(get-in db [:multiaccount :remote-push-notifications-enabled?])]
::json-rpc/call [{:method "wakuext_markAllRead"
:params [chat-id]
:on-success #(re-frame/dispatch [::mark-all-read-successful])}]})
(rf/defn handle-mark-mark-all-read-in-community
(fx/defn handle-mark-mark-all-read-in-community
{:events [:chat.ui/mark-all-read-in-community-pressed]}
[{db :db} community-id]
(let [community-chat-ids (map #(str community-id %)
(keys (get-in db [:communities community-id :chats])))]
{:clear-message-notifications [community-chat-ids
(get-in db [:multiaccount :remote-push-notifications-enabled?])]
::json-rpc/call [{:method "wakuext_markAllReadInCommunity"
:params [community-id]
:on-success #(re-frame/dispatch
[::mark-all-read-in-community-successful %])}]}))
{:clear-message-notifications [community-chat-ids
(get-in db [:multiaccount :remote-push-notifications-enabled?])]
::json-rpc/call [{:method "wakuext_markAllReadInCommunity"
:params [community-id]
:on-success #(re-frame/dispatch [::mark-all-read-in-community-successful %])}]}))
(rf/defn messages-loaded
(fx/defn messages-loaded
"Loads more messages for current chat"
{:events [::messages-loaded]}
[{db :db} chat-id session-id {:keys [cursor messages]}]
(when-not (and (get-in db [:pagination-info chat-id :messages-initialized?])
(not= session-id
(get-in db [:pagination-info chat-id :messages-initialized?])))
(let [already-loaded-messages (get-in db [:messages chat-id])
(let [already-loaded-messages (get-in db [:messages chat-id])
;; We remove those messages that are already loaded, as we might get some duplicates
{:keys [all-messages new-messages senders contacts]}
(reduce (fn [{:keys [all-messages] :as acc}
@@ -129,11 +125,8 @@
:contacts {}
:new-messages []}
messages)
current-clock-value (get-in db
[:pagination-info chat-id
:cursor-clock-value])
clock-value (when cursor
(cursor->clock-value cursor))]
current-clock-value (get-in db [:pagination-info chat-id :cursor-clock-value])
clock-value (when cursor (cursor->clock-value cursor))]
{:dispatch [:chat/add-senders-to-chat-users (vals senders)]
:db (-> db
(update-in [:pagination-info chat-id :cursor-clock-value]
@@ -142,9 +135,7 @@
%))
(update-in [:pagination-info chat-id :cursor]
#(if (or (empty? cursor)
(not current-clock-value)
(< clock-value current-clock-value))
#(if (or (empty? cursor) (not current-clock-value) (< clock-value current-clock-value))
cursor
%))
(assoc-in [:pagination-info chat-id :loading-messages?] false)
@@ -154,7 +145,7 @@
(empty? cursor))
(update :contacts/contacts merge contacts))})))
(rf/defn load-more-messages
(fx/defn load-more-messages
{:events [:chat.ui/load-more-messages]}
[{:keys [db]} chat-id first-request]
(when-let [session-id (get-in db [:pagination-info chat-id :messages-initialized?])]
@@ -173,17 +164,17 @@
#(re-frame/dispatch [::messages-loaded chat-id session-id %])
#(re-frame/dispatch [::failed-loading-messages chat-id session-id %]))))))))
(rf/defn load-more-messages-for-current-chat
(fx/defn load-more-messages-for-current-chat
{:events [:chat.ui/load-more-messages-for-current-chat]}
[{:keys [db] :as cofx}]
(load-more-messages cofx (:current-chat-id db) false))
(rf/defn load-messages
(fx/defn load-messages
[{:keys [db now] :as cofx} chat-id]
(when-not (get-in db [:pagination-info chat-id :messages-initialized?])
(rf/merge cofx
{:db (assoc-in db [:pagination-info chat-id :messages-initialized?] now)
(fx/merge cofx
{:db (assoc-in db [:pagination-info chat-id :messages-initialized?] now)
:utils/dispatch-later [{:ms 50 :dispatch [:chat.ui/mark-all-read-pressed chat-id]}
(when-not (get-in cofx [:db :chats chat-id :public?])
{:ms 100 :dispatch [:pin-message/load-pin-messages chat-id]})]}
{:ms 100 :dispatch [::models.pin-message/load-pin-messages chat-id]})]}
(load-more-messages chat-id true))))
+185 -231
View File
@@ -1,26 +1,23 @@
(ns status-im.chat.models.mentions
(:require [clojure.string :as string]
[quo.react :as react]
[quo.react-native :as rn]
[re-frame.core :as re-frame]
[status-im.constants :as constants]
[status-im.utils.fx :as fx]
[status-im.contact.db :as contact.db]
[status-im.multiaccounts.core :as multiaccounts]
[status-im.native-module.core :as status]
[status-im.utils.platform :as platform]
[status-im.utils.utils :as utils]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
[status-im.utils.utils :as utils]
[status-im.native-module.core :as status]
[quo.react-native :as rn]
[quo.react :as react]
[status-im.multiaccounts.core :as multiaccounts]))
(def at-sign "@")
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn re-pos
[re s]
(loop [res []
s s
last-idx 0]
(defn re-pos [re s]
(loop [res [] s s last-idx 0]
(if-let [m (.exec re s)]
(let [new-idx (.-index m)
idx (+ last-idx new-idx)
@@ -28,8 +25,7 @@
(recur (conj res [idx c]) (subs s (inc new-idx)) (inc idx)))
res)))
(defn check-style-tag
[text idxs idx]
(defn check-style-tag [text idxs idx]
(let [[pos c] (get idxs idx)
[pos2 c2] (get idxs (inc idx))
[pos3 c3] (get idxs (+ 2 idx))
@@ -67,8 +63,7 @@
(doall
((fnil concat []) checked new-idxs)))))))
(defn apply-style-tag
[data idx pos c len start? end?]
(defn apply-style-tag [data idx pos c len start? end?]
(let [was-started? (get data c)
tripple-tilde? (and (= "~" c) (= 3 len))]
(cond
@@ -92,9 +87,8 @@
start?
{:data (-> data
(assoc c
{:len len
:idx pos})
(assoc c {:len len
:idx pos})
(clear-pending-at-signs pos))
:next-idx (+ idx len)}
@@ -102,8 +96,7 @@
{:data data
:next-idx (+ idx len)})))
(defn code-tag-len
[idxs idx]
(defn code-tag-len [idxs idx]
(let [[pos c] (get idxs idx)
[pos2 c2] (get idxs (inc idx))
[pos3 c3] (get idxs (+ 2 idx))]
@@ -154,8 +147,7 @@
(string/blank? (subs text (first prev-newlines) (dec pos)))))
(recur (-> data
(dissoc :newline "*" "_" "~" "`")
(assoc ">" {:idx pos}))
(inc idx))
(assoc ">" {:idx pos})) (inc idx))
(recur data (inc idx))))
at-sign?
@@ -163,7 +155,7 @@
(inc idx))
code-tag?
(let [len (code-tag-len idxs idx)
(let [len (code-tag-len idxs idx)
{:keys [data next-idx]}
(apply-style-tag data idx pos c len true true)]
(recur data next-idx))
@@ -175,7 +167,7 @@
(apply-style-tag data idx pos c len can-be-start? can-be-end?)]
(recur data next-idx))
:else (recur data (inc idx)))))))))
:else (recur data (inc idx)))))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -209,8 +201,7 @@
:ens-verified ens-verified
:public-key public-key})))
(defn mentionable-contacts
[contacts]
(defn mentionable-contacts [contacts]
(reduce
(fn [acc [key contact]]
(let [mentionable-contact (add-searchable-phrases-to-contact contact false)]
@@ -220,49 +211,38 @@
{}
contacts))
(defn mentionable-contacts-from-identites
[contacts my-public-key identities]
(defn mentionable-contacts-from-identites [contacts my-public-key identities]
(reduce (fn [acc identity]
(let [contact (multiaccounts/contact-by-identity
contacts
identity)
contacts identity)
contact (if (string/blank? (:alias contact))
(assoc contact
:alias
(assoc contact :alias
(get-in contact [:names :three-words-name]))
contact)
mentionable-contact (add-searchable-phrases-to-contact
contact
true)]
(if (nil? mentionable-contact)
acc
(assoc acc identity mentionable-contact))))
contact true)]
(if (nil? mentionable-contact) acc
(assoc acc identity mentionable-contact))))
{}
(remove #(= my-public-key %) identities)))
(defn get-mentionable-users
[chat all-contacts current-multiaccount community-members]
(let [{:keys [name preferred-name public-key]} current-multiaccount
(defn get-mentionable-users [chat all-contacts current-multiaccount community-members]
(let [{:keys [name preferred-name public-key]} current-multiaccount
{:keys [chat-id users contacts chat-type]} chat
mentionable-contacts (mentionable-contacts all-contacts)
mentionable-users (assoc users
public-key
{:alias name
:name (or preferred-name name)
:public-key public-key})]
mentionable-contacts (mentionable-contacts all-contacts)
mentionable-users (assoc users public-key {:alias name
:name (or preferred-name name)
:public-key public-key})]
(cond
(= chat-type constants/private-group-chat-type)
(merge mentionable-users
(mentionable-contacts-from-identites all-contacts public-key contacts))
(= chat-type constants/one-to-one-chat-type)
(assoc mentionable-users
chat-id
(get mentionable-contacts
chat-id
(-> chat-id
contact.db/public-key->new-contact
contact.db/enrich-contact)))
(assoc mentionable-users chat-id (get mentionable-contacts chat-id
(-> chat-id
contact.db/public-key->new-contact
contact.db/enrich-contact)))
(= chat-type constants/community-chat-type)
(mentionable-contacts-from-identites
@@ -273,14 +253,13 @@
(= chat-type constants/public-chat-type)
(merge mentionable-users (select-keys mentionable-contacts (keys mentionable-users)))
:else mentionable-users)))
:else mentionable-users)))
(def ending-chars "[\\s\\.,;:]")
(def ending-chars-regex (re-pattern ending-chars))
(def word-regex (re-pattern (str "^[\\w\\d]*" ending-chars "|^[\\S]*$")))
(defn mentioned?
[{:keys [alias name]} text]
(defn mentioned? [{:keys [alias name]} text]
(let [lcase-name (string/lower-case name)
lcase-alias (string/lower-case alias)
regex (re-pattern
@@ -293,8 +272,7 @@
lcase-text (string/lower-case text)]
(re-find regex lcase-text)))
(defn get-user-suggestions
[users searched-text]
(defn get-user-suggestions [users searched-text]
(reduce
(fn [acc [k {:keys [alias name nickname searchable-phrases] :as user}]]
(if-let [match
@@ -323,11 +301,9 @@
(string/lower-case name)
searched-text)
name))]
(assoc acc
k
(assoc user
:match match
:searched-text searched-text))
(assoc acc k (assoc user
:match match
:searched-text searched-text))
acc))
{}
users))
@@ -348,25 +324,21 @@
text))
user-suggestions (get-user-suggestions users searched-text)
user-suggestions-cnt (count user-suggestions)]
(cond
(zero? user-suggestions-cnt)
nil
(cond (zero? user-suggestions-cnt)
nil
(and (= 1 user-suggestions-cnt)
(mentioned? (second (first user-suggestions))
(subs text (inc mention-key-idx))))
(second (first user-suggestions))
(and (= 1 user-suggestions-cnt)
(mentioned? (second (first user-suggestions))
(subs text (inc mention-key-idx))))
(second (first user-suggestions))
(> user-suggestions-cnt 1)
(let [word-len (count word)
text-len (count text)
next-word-start (+ next-word-idx word-len)]
(when (> text-len next-word-start)
(match-mention text
users
mention-key-idx
next-word-start
new-words))))))))
(> user-suggestions-cnt 1)
(let [word-len (count word)
text-len (count text)
next-word-start (+ next-word-idx word-len)]
(when (> text-len next-word-start)
(match-mention text users mention-key-idx
next-word-start new-words))))))))
(defn replace-mentions
([text users]
@@ -388,25 +360,18 @@
(let [new-text (string/join
[(subs text 0 (inc mention-key-idx))
public-key
(subs text
(+ (inc mention-key-idx)
(count match)))])]
(recur new-text
users
(rest idxs)
(subs text (+ (inc mention-key-idx)
(count match)))])]
(recur new-text users (rest idxs)
(+ diff (- (count text) (count new-text)))))))))))))
(defn check-mentions
[{:keys [db]} text]
(defn check-mentions [{:keys [db]} text]
(let [current-chat-id (:current-chat-id db)
chat (get-in db [:chats current-chat-id])
all-contacts (:contacts/contacts db)
current-multiaccount (:multiaccount db)
community-members (get-in db [:communities (:community-id chat) :members])
mentionable-users (get-mentionable-users chat
all-contacts
current-multiaccount
community-members)]
mentionable-users (get-mentionable-users chat all-contacts current-multiaccount community-members)]
(replace-mentions text mentionable-users)))
(defn get-at-sign-idxs
@@ -431,7 +396,7 @@
{:from idx
:checked? false})
new-idxs)
(let [diff (- new-text-len old-text-len)
(let [diff (- new-text-len old-text-len)
{:keys [state added?]}
(->> at-idxs
(keep (fn [{:keys [from to] :as entry}]
@@ -441,7 +406,7 @@
(>= from old-end)
(assoc entry
:from (+ from diff)
:to (+ to diff))
:to (+ to diff))
;; starts and end before change
(and
@@ -466,22 +431,20 @@
(> from last-new-idx)
(not added?))
{:state (conj
(into state
(map (fn [idx]
{:from idx
:checked? false})
new-idxs))
(into state (map (fn [idx]
{:from idx
:checked? false})
new-idxs))
entry)
:added? true}
(update acc :state conj entry)))
{:state []}))]
(if added?
state
(into state
(map (fn [idx]
{:from idx
:checked? false})
new-idxs)))))))
(into state (map (fn [idx]
{:from idx
:checked? false})
new-idxs)))))))
(defn check-entry
[text {:keys [from checked?] :as entry} mentionable-users]
@@ -490,14 +453,14 @@
(let [{user-match :match}
(match-mention (str text "@") mentionable-users from)]
(if user-match
{:from from
:to (+ from (count user-match))
{:from from
:to (+ from (count user-match))
:checked? true
:mention? true}
{:from from
:to (count text)
{:from from
:to (count text)
:checked? true
:mention false}))))
:mention false}))))
(defn check-idx-for-mentions
[text idxs mentionable-users]
@@ -526,7 +489,7 @@
(assoc-in [last-idx :to] (dec (count text)))
(assoc-in [last-idx :checked?] false)))))))
(rf/defn on-text-input
(fx/defn on-text-input
{:events [::on-text-input]}
[{:keys [db]} {:keys [previous-text start end] :as args}]
(log/debug "[mentions] on-text-input args" args)
@@ -536,34 +499,31 @@
(if platform/android?
previous-text
(subs previous-text start end))
chat-id (:current-chat-id db)
previous-state (get-in db [:chats/mentions chat-id :mentions])
new-state (-> previous-state
(merge args)
(assoc :previous-text normalized-previous-text))
new-at-idxs (calc-at-idxs new-state)
new-state (assoc new-state :at-idxs new-at-idxs)]
chat-id (:current-chat-id db)
previous-state (get-in db [:chats/mentions chat-id :mentions])
new-state (-> previous-state
(merge args)
(assoc :previous-text normalized-previous-text))
new-at-idxs (calc-at-idxs new-state)
new-state (assoc new-state :at-idxs new-at-idxs)]
(log/debug "[mentions] on-text-input state" new-state)
{:db (assoc-in db [:chats/mentions chat-id :mentions] new-state)}))
(defn calculate-input
[text [first-idx :as idxs]]
(defn calculate-input [text [first-idx :as idxs]]
(if-not first-idx
[[:text text]]
(let [idx-cnt (count idxs)
(let [idx-cnt (count idxs)
last-from (get-in idxs [(dec idx-cnt) :from])]
(reduce
(fn [acc {:keys [from to next-at-idx mention?]}]
(cond
(and mention? next-at-idx)
(into acc
[[:mention (subs text from (inc to))]
[:text (subs text (inc to) next-at-idx)]])
(into acc [[:mention (subs text from (inc to))]
[:text (subs text (inc to) next-at-idx)]])
(and mention? (= last-from from))
(into acc
[[:mention (subs text from (inc to))]
[:text (subs text (inc to))]])
(into acc [[:mention (subs text from (inc to))]
[:text (subs text (inc to))]])
:else
(conj acc [:text (subs text from (inc to))])))
@@ -573,82 +533,79 @@
[[:text (subs text 0 first-from)]]))
idxs))))
(rf/defn recheck-at-idxs
(fx/defn recheck-at-idxs
[{:keys [db]} mentionable-users]
(let [chat-id (:current-chat-id db)
text (get-in db [:chat/inputs chat-id :input-text])
state (get-in db [:chats/mentions chat-id :mentions])
new-at-idxs (check-idx-for-mentions
text
(:at-idxs state)
mentionable-users)
(let [chat-id (:current-chat-id db)
text (get-in db [:chat/inputs chat-id :input-text])
state (get-in db [:chats/mentions chat-id :mentions])
new-at-idxs (check-idx-for-mentions
text
(:at-idxs state)
mentionable-users)
calculated-input (calculate-input text new-at-idxs)]
(log/debug "[mentions] new-at-idxs" new-at-idxs calculated-input)
{:db (-> db
(update-in
[:chats/mentions chat-id :mentions]
assoc
:at-idxs
new-at-idxs)
:at-idxs new-at-idxs)
(assoc-in [:chat/inputs-with-mentions chat-id] calculated-input))}))
(rf/defn calculate-suggestions
(fx/defn calculate-suggestions
{:events [::calculate-suggestions]}
[{:keys [db]} mentionable-users]
(let [chat-id (:current-chat-id db)
text (get-in db [:chat/inputs chat-id :input-text])
(let [chat-id (:current-chat-id db)
text (get-in db [:chat/inputs chat-id :input-text])
{:keys [new-text at-idxs start end] :as state}
(get-in db [:chats/mentions chat-id :mentions])
new-text (or new-text text)]
new-text (or new-text text)]
(log/info "[mentions] calculate suggestions"
"state"
state)
"state" state)
(if-not (seq at-idxs)
{:db (-> db
(assoc-in [:chats/mention-suggestions chat-id] nil)
(assoc-in [:chats/mentions chat-id :mentions :at-idxs] nil)
(assoc-in [:chat/inputs-with-mentions chat-id] [[:text text]]))}
(let [new-at-idxs (check-idx-for-mentions
text
at-idxs
mentionable-users)
(let [new-at-idxs (check-idx-for-mentions
text
at-idxs
mentionable-users)
calculated-input (calculate-input text new-at-idxs)
addition? (<= start end)
end (if addition?
(+ start (count new-text))
start)
at-sign-idx (string/last-index-of text at-sign start)
searched-text (string/lower-case (subs text (inc at-sign-idx) end))
addition? (<= start end)
end (if addition?
(+ start (count new-text))
start)
at-sign-idx (string/last-index-of text at-sign start)
searched-text (string/lower-case (subs text (inc at-sign-idx) end))
mentions
(when (and (not (> at-sign-idx start))
(not (> (- end at-sign-idx) 100)))
(get-user-suggestions mentionable-users searched-text))]
(log/debug "[mentions] mention check"
"addition" addition?
"at-sign-idx" at-sign-idx
"start" start
"end" end
"addition" addition?
"at-sign-idx" at-sign-idx
"start" start
"end" end
"searched-text" (pr-str searched-text)
"mentions" (count mentions))
"mentions" (count mentions))
(log/debug "[mentions] new-at-idxs" new-at-idxs calculated-input)
{:db (-> db
(update-in [:chats/mentions chat-id :mentions]
assoc
:at-sign-idx at-sign-idx
:at-idxs new-at-idxs
:at-idxs new-at-idxs
:mention-end end)
(assoc-in [:chat/inputs-with-mentions chat-id] calculated-input)
(assoc-in [:chats/mention-suggestions chat-id] mentions))}))))
(defn new-input-text-with-mention
[{:keys [db]} {:keys [name]}]
(let [chat-id (:current-chat-id db)
text (get-in db [:chat/inputs chat-id :input-text])
(let [chat-id (:current-chat-id db)
text (get-in db [:chat/inputs chat-id :input-text])
{:keys [mention-end at-sign-idx]}
(get-in db [:chats/mentions chat-id :mentions])]
(log/debug "[mentions] clear suggestions"
"state"
new-input-text-with-mention)
"state" new-input-text-with-mention)
(string/join
[(subs text 0 (inc at-sign-idx))
name
@@ -659,36 +616,36 @@
" "))
(subs text mention-end)])))
(rf/defn clear-suggestions
(fx/defn clear-suggestions
[{:keys [db]}]
(log/debug "[mentions] clear suggestions")
(let [chat-id (:current-chat-id db)]
{:db (update db :chats/mention-suggestions dissoc chat-id)}))
(rf/defn clear-mentions
(fx/defn clear-mentions
[{:keys [db] :as cofx}]
(log/debug "[mentions] clear mentions")
(let [chat-id (:current-chat-id db)]
(rf/merge
(fx/merge
cofx
{:db (-> db
(update-in [:chats/mentions chat-id] dissoc :mentions)
(update :chat/inputs-with-mentions dissoc chat-id))}
(clear-suggestions))))
(rf/defn clear-cursor
(fx/defn clear-cursor
{:events [::clear-cursor]}
[{:keys [db]}]
(log/debug "[mentions] clear cursor")
{:db
(update db :chats/cursor dissoc (:current-chat-id db))})
(rf/defn check-selection
(fx/defn check-selection
{:events [::on-selection-change]}
[{:keys [db] :as cofx}
{:keys [start end] :as selection}
mentionable-users]
(let [chat-id (:current-chat-id db)
(let [chat-id (:current-chat-id db)
{:keys [mention-end at-idxs]}
(get-in db [:chats/mentions chat-id :mentions])]
(when (seq at-idxs)
@@ -698,13 +655,12 @@
(<= (dec end) to))
idx))
at-idxs)
(rf/merge
(fx/merge
cofx
{:db (update-in db
[:chats/mentions chat-id :mentions]
{:db (update-in db [:chats/mentions chat-id :mentions]
assoc
:start end
:end end
:start end
:end end
:new-text "")}
(calculate-suggestions mentionable-users))
(clear-suggestions cofx)))))
@@ -717,28 +673,26 @@
(rn/find-node-handle (react/current-ref ref))
cursor))))
(rf/defn reset-text-input-cursor
(fx/defn reset-text-input-cursor
[_ ref cursor]
{::reset-text-input-cursor [ref cursor]})
(defn is-valid-terminating-character?
[c]
(defn is-valid-terminating-character? [c]
(case c
"\t" true ; tab
"\t" true ; tab
"\n" true ; newline
"\f" true ; new page
"\r" true ; carriage return
" " true ; whitespace
"," true
"." true
":" true
";" true
" " true ; whitespace
"," true
"." true
":" true
";" true
false))
(def hex-reg #"[0-9a-f]")
(defn is-public-key-character?
[c]
(defn is-public-key-character? [c]
(.test hex-reg c))
(def mention-length 133)
@@ -756,7 +710,7 @@
current-text
current-mention
current-mention-length]} character]
(let [is-pk-character (is-public-key-character? character)
(let [is-pk-character (is-public-key-character? character)
is-termination-character (is-valid-terminating-character? character)]
(cond
;; It's a valid mention.
@@ -766,13 +720,13 @@
(and (= current-mention-length mention-length)
is-termination-character)
{:current-mention-length 0
:current-mention ""
:current-text character
:text (cond-> text
(seq current-text)
(conj [:text current-text])
:always
(conj [:mention current-mention]))}
:current-mention ""
:current-text character
:text (cond-> text
(seq current-text)
(conj [:text current-text])
:always
(conj [:mention current-mention]))}
;; It's either a pk character, or the `x` in the pk
@@ -785,9 +739,9 @@
(and (= 2 current-mention-length)
(= "x" character)))
{:current-mention-length (inc current-mention-length)
:current-text current-text
:current-mention (str current-mention character)
:text text}
:current-text current-text
:current-mention (str current-mention character)
:text text}
;; The beginning of a mention, discard the @ sign
@@ -796,29 +750,29 @@
(= "@" character)
{:current-mention-length 1
:current-mention ""
:current-text current-text
:text text}
:current-mention ""
:current-text current-text
:text text}
;; Not a mention character, but we were following a mention
;; discard everything up to know an count as text
(and (not is-pk-character)
(pos? current-mention-length))
{:current-mention-length 0
:current-text (str current-text "@" current-mention character)
:current-mention ""
:text text}
:current-text (str current-text "@" current-mention character)
:current-mention ""
:text text}
;; Just a normal text character
:else
{:current-mention-length 0
:current-mention ""
:current-text (str current-text character)
:text text})))
:current-mention ""
:current-text (str current-text character)
:text text})))
{:current-mention-length 0
:current-text ""
:current-mention ""
:text []}
:current-text ""
:current-mention ""
:text []}
text)]
;; Process any remaining mention/text
(cond-> text
@@ -834,32 +788,32 @@
[m]
(reduce (fn [{:keys [start end at-idxs at-sign-idx mention-end]} [t text]]
(if (= :mention t)
(let [new-mention {:checked? true
:mention? true
:from mention-end
:to (+ start (count text))}
(let [new-mention {:checked? true
:mention? true
:from mention-end
:to (+ start (count text))}
has-previous? (seq at-idxs)]
{:new-text (last text)
{:new-text (last text)
:previous-text ""
:start (+ start (count text))
:end (+ end (count text))
:at-idxs (cond-> at-idxs
has-previous?
(-> pop
(conj (assoc (peek at-idxs) :next-at-idx mention-end)))
:always
(conj new-mention))
:at-sign-idx mention-end
:mention-end (+ mention-end (count text))})
{:new-text (last text)
:start (+ start (count text))
:end (+ end (count text))
:at-idxs (cond-> at-idxs
has-previous?
(-> pop
(conj (assoc (peek at-idxs) :next-at-idx mention-end)))
:always
(conj new-mention))
:at-sign-idx mention-end
:mention-end (+ mention-end (count text))})
{:new-text (last text)
:previous-text ""
:start (+ start (count text))
:end (+ end (count text))
:at-idxs at-idxs
:at-sign-idx at-sign-idx
:mention-end (+ mention-end (count text))}))
{:start -1
:end -1
:at-idxs []
:start (+ start (count text))
:end (+ end (count text))
:at-idxs at-idxs
:at-sign-idx at-sign-idx
:mention-end (+ mention-end (count text))}))
{:start -1
:end -1
:at-idxs []
:mention-end 0}
m))
+72 -91
View File
@@ -1,21 +1,20 @@
(ns status-im.chat.models.message
(:require [clojure.string :as string]
(:require [status-im.chat.models :as chat-model]
[re-frame.core :as re-frame]
[status-im.chat.models :as chat-model]
[status-im.chat.models.loading :as chat.loading]
[status-im.chat.models.mentions :as mentions]
[status-im.chat.models.message-list :as message-list]
[status-im.constants :as constants]
[status-im.data-store.messages :as data-store.messages]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.transport.message.protocol :as protocol]
[status-im.ui.screens.chat.state :as view.state]
[status-im.utils.gfycat.core :as gfycat]
[status-im.utils.platform :as platform]
[status-im.utils.types :as types]
[status-im2.contexts.chat.messages.message.delete-message.events :as delete-message]
[status-im.utils.fx :as fx]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
[status-im.chat.models.mentions :as mentions]
[clojure.string :as string]
[status-im.utils.types :as types]
[status-im.ui.screens.chat.state :as view.state]
[status-im.chat.models.loading :as chat.loading]
[status-im.utils.platform :as platform]
[status-im.utils.gfycat.core :as gfycat]))
(defn- message-loaded?
[db chat-id message-id]
@@ -25,8 +24,7 @@
[db chat-id clock-value]
(>= (get-in db [:chats chat-id :deleted-at-clock-value]) clock-value))
(defn add-timeline-message
[acc chat-id message-id message]
(defn add-timeline-message [acc chat-id message-id message]
(-> acc
(update-in [:db :messages chat-id] assoc message-id message)
(update-in [:db :message-lists chat-id] message-list/add message)))
@@ -37,16 +35,14 @@
;; TODO this is too expensive, probably we could mark message somehow and just hide it in the UI
(message-list/rebuild-message-list {:db (update-in db [:messages chat-id] dissoc message-id)} chat-id))
(rf/defn add-senders-to-chat-users
(fx/defn add-senders-to-chat-users
{:events [:chat/add-senders-to-chat-users]}
[{:keys [db]} messages]
(reduce (fn [acc {:keys [chat-id alias name identicon from]}]
(let [alias (if (string/blank? alias)
(gfycat/generate-gfy from)
alias)]
(update-in acc
[:db :chats chat-id :users]
assoc
(update-in acc [:db :chats chat-id :users] assoc
from
(mentions/add-searchable-phrases
{:alias alias
@@ -57,8 +53,7 @@
{:db db}
messages))
(defn timeline-message?
[db chat-id]
(defn timeline-message? [db chat-id]
(and
(get-in db [:pagination-info constants/timeline-chat-id :messages-initialized?])
(or
@@ -66,13 +61,11 @@
(when-let [pub-key (get-in db [:chats chat-id :profile-public-key])]
(get-in db [:contacts/contacts pub-key :added])))))
(defn get-timeline-message
[db chat-id message-js]
(defn get-timeline-message [db chat-id message-js]
(when (timeline-message? db chat-id)
(data-store.messages/<-rpc (types/js->clj message-js))))
(defn add-message
[{:keys [db] :as acc} message-js chat-id message-id cursor-clock-value]
(defn add-message [{:keys [db] :as acc} message-js chat-id message-id cursor-clock-value]
(let [{:keys [replace from clock-value] :as message}
(data-store.messages/<-rpc (types/js->clj message-js))]
(if (message-loaded? db chat-id message-id)
@@ -88,9 +81,8 @@
(update-in [:db :message-lists chat-id] message-list/add message)
(or (not cursor-clock-value) (< clock-value cursor-clock-value))
(update-in [:db :pagination-info chat-id]
assoc
:cursor (chat.loading/clock-value->cursor clock-value)
(update-in [:db :pagination-info chat-id] assoc
:cursor (chat.loading/clock-value->cursor clock-value)
:cursor-clock-value clock-value)
;;conj sender for add-sender-to-chat-users
@@ -101,12 +93,11 @@
;;TODO this is expensive
(hide-message chat-id replace)))))
(defn reduce-js-messages
[{:keys [db] :as acc} ^js message-js]
(let [chat-id (.-localChatId message-js)
clock-value (.-clock message-js)
message-id (.-id message-js)
current-chat-id (:current-chat-id db)
(defn reduce-js-messages [{:keys [db] :as acc} ^js message-js]
(let [chat-id (.-localChatId message-js)
clock-value (.-clock message-js)
message-id (.-id message-js)
current-chat-id (:current-chat-id db)
cursor-clock-value (get-in db [:pagination-info current-chat-id :cursor-clock-value])]
;;ignore not opened chats and earlier clock
(if (and (get-in db [:pagination-info chat-id :messages-initialized?])
@@ -118,30 +109,26 @@
(add-message acc message-js chat-id message-id cursor-clock-value)
;; Not in the current view, set all-loaded to false
;; and offload to db and update cursor if necessary
;;TODO if we'll offload messages , it will conflict with end reached, so probably if we reached
;;the end of visible area,
;; we need to drop other messages with (< clock-value cursor-clock-value) from response-js so we
;; don't update
;;TODO if we'll offload messages , it will conflict with end reached, so probably if we reached the end of visible area,
;; we need to drop other messages with (< clock-value cursor-clock-value) from response-js so we don't update
;; :cursor-clock-value because it will be changed when we loadMore message
{:db (cond-> (assoc-in db [:pagination-info chat-id :all-loaded?] false)
(> clock-value cursor-clock-value)
;;TODO cut older messages from messages-list
(update-in [:pagination-info chat-id]
assoc
:cursor (chat.loading/clock-value->cursor clock-value)
(update-in [:pagination-info chat-id] assoc
:cursor (chat.loading/clock-value->cursor clock-value)
:cursor-clock-value clock-value))})
acc)))
(defn receive-many
[{:keys [db]} ^js response-js]
(let [messages-js ^js (.splice (.-messages response-js) 0 (if platform/low-device? 3 10))
(defn receive-many [{:keys [db]} ^js response-js]
(let [messages-js ^js (.splice (.-messages response-js) 0 (if platform/low-device? 3 10))
{:keys [db senders]}
(reduce reduce-js-messages
{:db db :chats #{} :senders {} :transactions #{}}
messages-js)]
;;we want to render new messages as soon as possible
;;so we dispatch later all other events which can be handled async
{:db db
{:db db
:utils/dispatch-later
(concat [{:ms 20 :dispatch [:process-response response-js]}]
(when (and (:current-chat-id db) (= "active" (:app-state db)))
@@ -149,8 +136,7 @@
(when (seq senders)
[{:ms 100 :dispatch [:chat/add-senders-to-chat-users (vals senders)]}]))}))
(defn reduce-js-statuses
[db ^js message-js]
(defn reduce-js-statuses [db ^js message-js]
(let [chat-id (.-localChatId message-js)
profile-initialized (get-in db [:pagination-info chat-id :messages-initialized?])
timeline-message (timeline-message? db chat-id)
@@ -168,66 +154,66 @@
(update-in [:message-lists constants/timeline-chat-id] message-list/add message)))
db)))
(rf/defn process-statuses
(fx/defn process-statuses
{:events [:process-statuses]}
[{:keys [db]} statuses]
{:db (reduce reduce-js-statuses db statuses)})
(rf/defn update-db-message-status
(fx/defn update-db-message-status
[{:keys [db] :as cofx} chat-id message-id status]
(when (get-in db [:messages chat-id message-id])
(rf/merge cofx
(fx/merge cofx
{:db (assoc-in db
[:messages chat-id message-id :outgoing-status]
status)})))
[:messages chat-id message-id :outgoing-status]
status)})))
(rf/defn update-message-status
(fx/defn update-message-status
[{:keys [db] :as cofx} chat-id message-id status]
(rf/merge cofx
(fx/merge cofx
(update-db-message-status chat-id message-id status)))
(rf/defn resend-message
(fx/defn resend-message
[{:keys [db] :as cofx} chat-id message-id]
(rf/merge cofx
{::json-rpc/call [{:method "wakuext_reSendChatMessage"
:params [message-id]
(fx/merge cofx
{::json-rpc/call [{:method "wakuext_reSendChatMessage"
:params [message-id]
:on-success #(log/debug "re-sent message successfully")
:on-error #(log/error "failed to re-send message" %)}]}
:on-error #(log/error "failed to re-send message" %)}]}
(update-message-status chat-id message-id :sending)))
(rf/defn send-message
(fx/defn delete-message
"Deletes chat message, rebuild message-list"
{:events [:chat.ui/delete-message]}
[{:keys [db] :as cofx} chat-id message-id]
(fx/merge cofx
{:db (update-in db [:messages chat-id] dissoc message-id)}
(data-store.messages/delete-message message-id)
(message-list/rebuild-message-list chat-id)))
(fx/defn send-message
[cofx message]
(protocol/send-chat-messages cofx [message]))
(rf/defn send-messages
(fx/defn send-messages
[cofx messages]
(protocol/send-chat-messages cofx messages))
(rf/defn handle-removed-messages
(fx/defn handle-removed-messages
{:events [::handle-removed-messages]}
[{:keys [db] :as cofx} removed-messages]
(let [mark-as-deleted-fx (->> removed-messages
(map #(assoc % :message-id (:messageId %)))
(group-by :chatId)
(mapv (fn [[chat-id messages]]
(delete-message/delete-messages-localy messages chat-id))))
mark-as-seen-fx (mapv
(fn [removed-message]
(let [chat-id (:chatId removed-message)
message-id (:messageId removed-message)]
(data-store.messages/mark-messages-seen chat-id
[message-id]
#(re-frame/dispatch
[:chat/decrease-unviewed-count
chat-id %3]))))
removed-messages)
(let [mark-as-seen-fx (mapv (fn [removed-message]
(let [chat-id (:chatId removed-message)
message-id (:messageId removed-message)]
(data-store.messages/mark-messages-seen chat-id
[message-id]
#(re-frame/dispatch [:chat/decrease-unviewed-count chat-id %3])))) removed-messages)
remove-messages-fx (fn [{:keys [db]}]
{:dispatch [:activity-center.notifications/fetch-unread-count]})]
(apply rf/merge
cofx
(-> mark-as-deleted-fx
(concat mark-as-seen-fx)
(conj remove-messages-fx)))))
{:db (reduce (fn [acc current]
(update-in acc [:messages (:chatId current)] dissoc (:messageId current)))
db removed-messages)
:dispatch-n [[:get-activity-center-notifications]
[:get-activity-center-notifications-count]]})]
(apply fx/merge cofx (conj mark-as-seen-fx remove-messages-fx))))
(comment
(handle-removed-messages
@@ -237,20 +223,15 @@
:m4 {:chat-id :c2 :message-id :m4}}}}}
[:m1 :m3]))
(defn remove-cleared-message
[messages cleared-at]
(into {}
(remove #(let [message-clock (:clock-value (second %))]
(<= message-clock cleared-at))
messages)))
(defn remove-cleared-message [messages cleared-at]
(into {} (remove #(let [message-clock (:clock-value (second %))]
(<= message-clock cleared-at))
messages)))
(rf/defn handle-cleared-histories-messages
(fx/defn handle-cleared-histories-messages
{:events [::handle-cleared-hisotories-messages]}
[{:keys [db]} cleared-histories]
{:db (reduce (fn [acc current]
(update-in acc
[:messages (:chatId current)]
remove-cleared-message
(:clearedAt current)))
(update-in acc [:messages (:chatId current)] remove-cleared-message (:clearedAt current)))
db
cleared-histories)})
+33 -44
View File
@@ -2,25 +2,22 @@
(:require ["functional-red-black-tree" :as rb-tree]
[status-im.constants :as constants]
[status-im.utils.datetime :as time]
[utils.re-frame :as rf]))
[status-im.utils.fx :as fx]))
(defn- add-datemark
[{:keys [whisper-timestamp] :as msg}]
(defn- add-datemark [{:keys [whisper-timestamp] :as msg}]
;;TODO this is slow
(assoc msg :datemark (time/day-relative whisper-timestamp)))
(defn- add-timestamp
[{:keys [whisper-timestamp] :as msg}]
(defn- add-timestamp [{:keys [whisper-timestamp] :as msg}]
(assoc msg :timestamp-str (time/timestamp->time whisper-timestamp)))
(defn prepare-message
[{:keys [message-id
clock-value
message-type
from
outgoing
whisper-timestamp
deleted-for-me?]}]
(defn prepare-message [{:keys [message-id
clock-value
message-type
from
outgoing
whisper-timestamp
deleted-for-me?]}]
(-> {:whisper-timestamp whisper-timestamp
:from from
:one-to-one? (= constants/message-type-one-to-one message-type)
@@ -72,8 +69,7 @@
the most recent message, similarly :first-in-group? is the most recent message
in a group."
[{:keys [system-message?
one-to-one? outgoing]
:as current-message}
one-to-one? outgoing] :as current-message}
{:keys [outgoing-seen?] :as previous-message}
next-message]
(let [last-in-group? (or (nil? next-message)
@@ -91,7 +87,7 @@
(not system-message?)
(not outgoing)
(not one-to-one?))
:display-photo? (display-photo? current-message))))
:display-photo? (display-photo? current-message))))
(defn update-next-message
"Update next message in the list, we set :first? to false, and check if it
@@ -99,28 +95,26 @@
[current-message next-message]
(assoc
next-message
:first? false
:first? false
:first-outgoing? (and
(not (:first-outgoing? current-message))
(:first-outgoing? next-message))
:outgoing-seen? (:outgoing-seen? current-message)
:outgoing-seen? (:outgoing-seen? current-message)
:first-in-group?
(not (same-group? current-message next-message))))
(defn update-previous-message
"If this is a new group, we mark the previous as the last one in the group"
[current-message
{:keys [one-to-one?
system-message?
outgoing]
:as previous-message}]
[current-message {:keys [one-to-one?
system-message?
outgoing] :as previous-message}]
(let [last-in-group? (not (same-group? current-message previous-message))]
(assoc previous-message
:display-username? (and last-in-group?
(not system-message?)
(not outgoing)
(not one-to-one?))
:last-in-group? last-in-group?)))
:last-in-group? last-in-group?)))
(defn get-prev-element
"Get previous item in the iterator, and wind it back to the initial state"
@@ -141,11 +135,11 @@
(defn update-message
"Update the message and siblings with positional info"
[^js tree message]
(let [^js iter (.find tree message)
^js previous-message (when (.-hasPrev iter)
(get-prev-element iter))
^js next-message (when (.-hasNext iter)
(get-next-element iter))
(let [^js iter (.find tree message)
^js previous-message (when (.-hasPrev iter)
(get-prev-element iter))
^js next-message (when (.-hasNext iter)
(get-next-element iter))
^js message-with-pos-data (add-group-info message previous-message next-message)]
(cond-> (.update iter message-with-pos-data)
next-message
@@ -163,9 +157,9 @@
(let [iter (.find tree prepared-message)]
(if (not iter)
tree
(let [^js new-tree (.remove iter)
^js next-message (when (.-hasNext iter)
(get-next-element iter))]
(let [^js new-tree (.remove iter)
^js next-message (when (.-hasNext iter)
(get-next-element iter))]
(if (not next-message)
new-tree
(update-message new-tree next-message))))))
@@ -179,26 +173,21 @@
(let [^js tree (.insert old-message-list prepared-message prepared-message)]
(update-message tree prepared-message)))
(defn add
[message-list message]
(defn add [message-list message]
(insert-message (or message-list (rb-tree compare-fn)) (prepare-message message)))
(defn add-many
[message-list messages]
(defn add-many [message-list messages]
(reduce add
message-list
messages))
(defn ->seq
[^js message-list]
(defn ->seq [^js message-list]
(if message-list
(array-seq (.-values message-list))
[]))
;; NOTE(performance): this is too expensive, probably we could mark message somehow and just hide it in
;; the UI
(rf/defn rebuild-message-list
;;TODO this is too expensive, probably we could mark message somehow and just hide it in the UI
(fx/defn rebuild-message-list
[{:keys [db]} chat-id]
{:db (assoc-in db
[:message-lists chat-id]
(add-many nil (vals (get-in db [:messages chat-id]))))})
{:db (assoc-in db [:message-lists chat-id]
(add-many nil (vals (get-in db [:messages chat-id]))))})
@@ -1,7 +1,9 @@
(ns status-im.chat.models.message-test
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.chat.models.message :as message]
[status-im.chat.models.message-list :as models.message-list]
[status-im.chat.models.loading :as loading]
[status-im.utils.datetime :as time]
[status-im.ui.screens.chat.state :as view.state]))
(deftest add-received-message-test
@@ -128,3 +130,54 @@
{:chats {"a" {:deleted-at-clock-value 1}}}
"a"
0))))
(deftest delete-message
(with-redefs [time/day-relative (constantly "day-relative")
time/timestamp->time (constantly "timestamp")]
(let [cofx1 {:db {:messages {"chat-id" {0 {:message-id 0
:content "a"
:clock-value 0
:whisper-timestamp 0
:timestamp 0}
1 {:message-id 1
:content "b"
:clock-value 1
:whisper-timestamp 1
:timestamp 1}}}
:message-lists {"chat-id" [{:something :something}]}
:chats {"chat-id" {}}}}
cofx2 {:db {:messages {"chat-id" {0 {:message-id 0
:content "a"
:clock-value 0
:whisper-timestamp 1
:timestamp 1}}}
:message-list {"chat-id" [{:something :something}]}
:chats {"chat-id" {}}}}
fx1 (message/delete-message cofx1 "chat-id" 1)
fx2 (message/delete-message cofx2 "chat-id" 0)]
(testing "Deleting message deletes it along with all references"
(is (= '(0)
(keys (get-in fx1 [:db :messages "chat-id"]))))
(is (= [{:one-to-one? false
:message-id 0
:whisper-timestamp 0
:type :message
:display-photo? true
:system-message? false
:last-in-group? true
:datemark "day-relative"
:clock-value 0
:first-in-group? true
:from nil
:first-outgoing? false
:outgoing-seen? false
:timestamp-str "timestamp"
:first? true
:display-username? true
:outgoing false}]
(models.message-list/->seq
(get-in fx1 [:db :message-lists "chat-id"]))))
(is (= {}
(get-in fx2 [:db :messages "chat-id"])))
(is (= nil
(get-in fx2 [:db :message-lists "chat-id"])))))))
+115
View File
@@ -0,0 +1,115 @@
(ns status-im.chat.models.pin-message
(:require [status-im.chat.models.message-list :as message-list]
[status-im.constants :as constants]
[status-im.data-store.pin-messages :as data-store.pin-messages]
[status-im.utils.fx :as fx]
[taoensso.timbre :as log]
[status-im.transport.message.protocol :as protocol]
[re-frame.core :as re-frame]))
(fx/defn handle-failed-loading-pin-messages
{:events [::failed-loading-pin-messages]}
[{:keys [db]} current-chat-id _ err]
(log/error "failed loading pin messages" current-chat-id err)
(when current-chat-id
{:db (assoc-in db [:pagination-info current-chat-id :loading-pin-messages?] false)}))
(fx/defn pin-messages-loaded
{:events [::pin-messages-loaded]}
[{db :db} chat-id {:keys [cursor pinned-messages]}]
(let [all-messages (reduce (fn [acc {:keys [message-id] :as message}]
(assoc acc message-id message))
{}
pinned-messages)
messages-id-list (map :message-id pinned-messages)]
{:db (-> db
(assoc-in [:pagination-info chat-id :loading-pin-messages?] false)
(assoc-in [:pin-messages chat-id] all-messages)
(assoc-in [:pin-message-lists chat-id] (message-list/add-many nil (vals all-messages)))
(assoc-in [:pagination-info chat-id :all-pin-loaded?]
(empty? cursor)))}))
(fx/defn receive-signal
[{:keys [db] :as cofx} pin-messages]
(let [{:keys [chat-id]} (first pin-messages)]
(when (= chat-id (db :current-chat-id))
(let [{:keys [chat-id]} (first pin-messages)
already-loaded-pin-messages (get-in db [:pin-messages chat-id] {})
already-loaded-messages (get-in db [:messages chat-id] {})
all-messages (reduce (fn [acc {:keys [message_id pinned from]}]
;; Add to or remove from pinned message list, and normalizing pinned-by property
(let [current-message (get already-loaded-messages message_id)
current-message-pin (merge current-message
{:pinned pinned
:pinned-by from})]
(cond-> acc
(nil? pinned)
(dissoc message_id)
(and (some? pinned) (some? current-message))
(assoc message_id current-message-pin))))
already-loaded-pin-messages
pin-messages)]
{:db (-> db
(assoc-in [:pin-messages chat-id] all-messages)
(assoc-in [:pin-message-lists chat-id]
(message-list/add-many nil (vals all-messages))))}))))
(fx/defn load-more-pin-messages
[{:keys [db]} chat-id first-request]
(let [not-all-loaded? (not (get-in db [:pagination-info chat-id :all-loaded?]))
not-loading-pin-messages? (not (get-in db [:pagination-info chat-id :loading-pin-messages?]))]
(when not-loading-pin-messages?
(fx/merge
{:db (assoc-in db [:pagination-info chat-id :loading-pin-messages?] true)}
(data-store.pin-messages/pinned-message-by-chat-id-rpc
chat-id
nil
constants/default-number-of-pin-messages
#(re-frame/dispatch [::pin-messages-loaded chat-id %])
#(re-frame/dispatch [::failed-loading-pin-messages chat-id %]))))))
(fx/defn send-pin-message
"Pin message, rebuild pinned messages list"
{:events [::send-pin-message]}
[{:keys [db] :as cofx} {:keys [chat-id message-id pinned] :as pin-message}]
(let [current-public-key (get-in db [:multiaccount :public-key])
message (merge pin-message {:pinned-by current-public-key})
preferred-name (get-in db [:multiaccount :preferred-name])]
(fx/merge cofx
{:db (cond-> db
pinned
(->
(update-in [:pin-message-lists chat-id] message-list/add message)
(assoc-in [:pin-messages chat-id message-id] message))
(not pinned)
(->
(update-in [:pin-message-lists chat-id] message-list/remove-message pin-message)
(update-in [:pin-messages chat-id] dissoc message-id)))}
(data-store.pin-messages/send-pin-message {:chat-id (pin-message :chat-id)
:message_id (pin-message :message-id)
:pinned (pin-message :pinned)})
(when pinned
(protocol/send-chat-messages [{:chat-id (pin-message :chat-id)
:content-type constants/content-type-system-text
:text "pinned a message"
:response-to (pin-message :message-id)
:ens-name preferred-name}])))))
(fx/defn load-pin-messages
{:events [::load-pin-messages]}
[{:keys [db] :as cofx} chat-id]
(load-more-pin-messages cofx chat-id true))
(fx/defn show-pin-limit-modal
{:events [::show-pin-limit-modal]}
[{:keys [db] :as cofx} chat-id]
(fx/merge
{:db (assoc-in db [:pin-modal chat-id] true)}))
(fx/defn hide-pin-limit-modal
{:events [::hide-pin-limit-modal]}
[{:keys [db] :as cofx} chat-id]
(fx/merge
{:db (assoc-in db [:pin-modal chat-id] false)}))
+22 -33
View File
@@ -1,36 +1,28 @@
(ns status-im.chat.models.reactions
(:require [re-frame.core :as re-frame]
[status-im.constants :as constants]
[status-im.data-store.reactions :as data-store.reactions]
[status-im.transport.message.protocol :as message.protocol]
(:require [status-im.constants :as constants]
[re-frame.core :as re-frame]
[status-im.utils.fx :as fx]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
[status-im.transport.message.protocol :as message.protocol]
[status-im.data-store.reactions :as data-store.reactions]))
(defn update-reaction
[acc retracted chat-id message-id emoji-id emoji-reaction-id reaction]
(defn update-reaction [acc retracted chat-id message-id emoji-id emoji-reaction-id reaction]
;; NOTE(Ferossgp): For a better performance, better to not keep in db all retracted reactions
;; retraction will always come after the reaction so there shouldn't be a conflict
(if retracted
(update-in acc [chat-id message-id emoji-id] dissoc emoji-reaction-id)
(assoc-in acc [chat-id message-id emoji-id emoji-reaction-id] reaction)))
(defn process-reactions
[chats]
(defn process-reactions [chats]
(fn [reactions new-reactions]
;; TODO(Ferossgp): handling own reaction in subscription could be expensive,
;; for better performance we can here separate own reaction into 2 maps
(reduce
(fn [acc
{:keys [chat-id message-id emoji-id emoji-reaction-id retracted]
:as reaction}]
(fn [acc {:keys [chat-id message-id emoji-id emoji-reaction-id retracted]
:as reaction}]
(cond-> (update-reaction acc retracted chat-id message-id emoji-id emoji-reaction-id reaction)
(get-in chats [chat-id :profile-public-key])
(update-reaction retracted
constants/timeline-chat-id
message-id
emoji-id
emoji-reaction-id
reaction)))
(update-reaction retracted constants/timeline-chat-id message-id emoji-id emoji-reaction-id reaction)))
reactions
new-reactions)))
@@ -40,12 +32,12 @@
(get-in db [:chats chat-id])]
(>= deleted-at-clock-value clock-value)))
(rf/defn receive-signal
(fx/defn receive-signal
[{:keys [db] :as cofx} reactions]
(let [reactions (filter (partial earlier-than-deleted-at? cofx) reactions)]
{:db (update db :reactions (process-reactions (:chats db)) reactions)}))
(rf/defn load-more-reactions
(fx/defn load-more-reactions
{:events [:load-more-reactions]}
[{:keys [db]} cursor chat-id]
(when-let [session-id (get-in db [:pagination-info chat-id :messages-initialized?])]
@@ -56,7 +48,7 @@
#(re-frame/dispatch [::reactions-loaded chat-id session-id %])
#(log/error "failed loading reactions" chat-id %))))
(rf/defn reactions-loaded
(fx/defn reactions-loaded
{:events [::reactions-loaded]}
[{db :db}
chat-id
@@ -72,36 +64,33 @@
;; Send reactions
(rf/defn send-emoji-reaction
(fx/defn send-emoji-reaction
{:events [::send-emoji-reaction]}
[{{:keys [current-chat-id]} :db :as cofx} reaction]
(message.protocol/send-reaction cofx
(update reaction :chat-id #(or % current-chat-id))))
(rf/defn send-retract-emoji-reaction
(fx/defn send-retract-emoji-reaction
{:events [::send-emoji-reaction-retraction]}
[{{:keys [current-chat-id]} :db :as cofx} reaction]
(message.protocol/send-retract-reaction cofx
(update reaction :chat-id #(or % current-chat-id))))
(rf/defn receive-one
(fx/defn receive-one
{:events [::receive-one]}
[{:keys [db]} reaction]
{:db (update db :reactions (process-reactions (:chats db)) [reaction])})
(defn message-reactions
[current-public-key reactions]
(defn message-reactions [current-public-key reactions]
(reduce
(fn [acc [emoji-id reactions]]
(if (pos? (count reactions))
(let [own (first (filter (fn [[_ {:keys [from]}]]
(= from current-public-key))
reactions))]
(conj acc
{:emoji-id emoji-id
:own (boolean (seq own))
:emoji-reaction-id (:emoji-reaction-id (second own))
:quantity (count reactions)}))
(= from current-public-key)) reactions))]
(conj acc {:emoji-id emoji-id
:own (boolean (seq own))
:emoji-reaction-id (:emoji-reaction-id (second own))
:quantity (count reactions)}))
acc))
[]
reactions))
+5 -5
View File
@@ -1,14 +1,14 @@
;;this ns is needed because of cycled deps
(ns status-im.chat.models.transport
(:require [status-im.chat.models.message :as chat.message]
(:require [status-im.utils.fx :as fx]
[status-im.transport.message.core :as transport.message]
[utils.re-frame :as rf]))
[status-im.chat.models.message :as chat.message]))
(rf/defn chat-ui-resend-message
(fx/defn chat-ui-resend-message
{:events [:chat.ui/resend-message]}
[{:keys [db] :as cofx} chat-id message-id]
(let [message (get-in db [:messages chat-id message-id])]
(rf/merge
(fx/merge
cofx
(transport.message/set-message-envelope-hash chat-id message-id (:message-type message))
(chat.message/resend-message chat-id message-id))))
(chat.message/resend-message chat-id message-id))))
+5 -4
View File
@@ -93,7 +93,8 @@
(let [res (chat/navigate-to-chat-nav2 {:db db} chat-id false)]
(is (nil? (get-in res [:db :pagination-info chat-id :all-loaded?])))))))
(deftest camera-roll-loading-more-test
(let [cofx {:db {:camera-roll/loading-more false}}]
(is (= {:db {:camera-roll/loading-more true}}
(images/camera-roll-loading-more cofx true)))))
(deftest camera-roll-loading-more
(let [cofx {:db {:camera-roll-loading-more false}}]
(is (= true (images/camera-roll-loading-more cofx true)))))
+20 -20
View File
@@ -2,44 +2,44 @@
(:require [re-frame.core :as re-frame]
[status-im.ethereum.core :as ethereum]
[status-im.ethereum.json-rpc :as json-rpc]
[utils.re-frame :as rf]))
[status-im.utils.fx :as fx]))
(rf/defn handle-prepare-accept-request-address-for-transaction
(fx/defn handle-prepare-accept-request-address-for-transaction
{:events [::prepare-accept-request-address-for-transaction]}
[{:keys [db]} message]
{:db (assoc db
:commands/select-account
{:message message
:from (ethereum/get-default-account (:multiaccount/accounts db))})
{:db (assoc db
:commands/select-account
{:message message
:from (ethereum/get-default-account (:multiaccount/accounts db))})
:show-select-acc-sheet nil})
(rf/defn set-selected-account
(fx/defn set-selected-account
{:events [::set-selected-account]}
[{:keys [db]} _ account]
{:db (-> (assoc-in db [:commands/select-account :from] account)
(assoc :bottom-sheet/show? false))})
(rf/defn handle-accept-request-address-for-transaction
(fx/defn handle-accept-request-address-for-transaction
{:events [::accept-request-address-for-transaction]}
[{:keys [db]} message-id address]
{:db (dissoc db :commands/select-account)
::json-rpc/call [{:method "wakuext_acceptRequestAddressForTransaction"
:params [message-id address]
{:db (dissoc db :commands/select-account)
::json-rpc/call [{:method "wakuext_acceptRequestAddressForTransaction"
:params [message-id address]
:js-response true
:on-success #(re-frame/dispatch [:transport/message-sent %])}]})
:on-success #(re-frame/dispatch [:transport/message-sent %])}]})
(rf/defn handle-decline-request-address-for-transaction
(fx/defn handle-decline-request-address-for-transaction
{:events [::decline-request-address-for-transaction]}
[_ message-id]
{::json-rpc/call [{:method "wakuext_declineRequestAddressForTransaction"
:params [message-id]
{::json-rpc/call [{:method "wakuext_declineRequestAddressForTransaction"
:params [message-id]
:js-response true
:on-success #(re-frame/dispatch [:transport/message-sent %])}]})
:on-success #(re-frame/dispatch [:transport/message-sent %])}]})
(rf/defn handle-decline-request-transaction
(fx/defn handle-decline-request-transaction
{:events [::decline-request-transaction]}
[cofx message-id]
{::json-rpc/call [{:method "wakuext_declineRequestTransaction"
:params [message-id]
{::json-rpc/call [{:method "wakuext_declineRequestTransaction"
:params [message-id]
:js-response true
:on-success #(re-frame/dispatch [:transport/message-sent %])}]})
:on-success #(re-frame/dispatch [:transport/message-sent %])}]})
+157 -203
View File
@@ -1,24 +1,25 @@
(ns status-im.communities.core
(:require [clojure.set :as clojure.set]
[clojure.string :as string]
[clojure.walk :as walk]
[quo.design-system.colors :as colors]
[re-frame.core :as re-frame]
[status-im.async-storage.core :as async-storage]
[status-im.bottom-sheet.core :as bottom-sheet]
[status-im.constants :as constants]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.ui.components.emoji-thumbnail.styles :as emoji-thumbnail-styles]
[status-im.utils.universal-links.core :as universal-links]
[status-im2.contexts.activity-center.events :as activity-center]
[status-im2.navigation.events :as navigation]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
(:require
[re-frame.core :as re-frame]
[clojure.walk :as walk]
[clojure.string :as string]
[clojure.set :as clojure.set]
[taoensso.timbre :as log]
[status-im.async-storage.core :as async-storage]
[status-im.utils.fx :as fx]
[status-im.constants :as constants]
[status-im.bottom-sheet.core :as bottom-sheet]
[status-im.utils.universal-links.core :as universal-links]
[status-im.ethereum.json-rpc :as json-rpc]
[quo.design-system.colors :as colors]
[status-im2.navigation.events :as navigation]
[status-im.utils.handlers :refer [>evt]]
[status-im.ui.components.emoji-thumbnail.styles :as emoji-thumbnail-styles]
[status-im.notifications-center.core :as notification-center]))
(def crop-size 1000)
(defn universal-link
[community-id]
(defn universal-link [community-id]
(str (:external universal-links/domains)
"/c/"
community-id))
@@ -27,22 +28,18 @@
[{:name "Status"
:id constants/status-community-id}])
(defn <-request-to-join-community-rpc
[r]
(clojure.set/rename-keys r
{:communityId :community-id
:publicKey :public-key
:chatId :chat-id}))
(defn <-request-to-join-community-rpc [r]
(clojure.set/rename-keys r {:communityId :community-id
:publicKey :public-key
:chatId :chat-id}))
(defn <-requests-to-join-community-rpc
[requests]
(defn <-requests-to-join-community-rpc [requests]
(reduce (fn [acc r]
(assoc acc (:id r) (<-request-to-join-community-rpc r)))
{}
requests))
(defn <-chats-rpc
[chats]
(defn <-chats-rpc [chats]
(reduce-kv (fn [acc k v]
(assoc acc
(name k)
@@ -53,8 +50,7 @@
{}
chats))
(defn <-categories-rpc
[categ]
(defn <-categories-rpc [categ]
(reduce-kv (fn [acc k v]
(assoc acc
(name k)
@@ -62,8 +58,7 @@
{}
categ))
(defn <-rpc
[c]
(defn <-rpc [c]
(-> c
(clojure.set/rename-keys {:canRequestAccess :can-request-access?
:canManageUsers :can-manage-users?
@@ -75,28 +70,25 @@
(update :chats <-chats-rpc)
(update :categories <-categories-rpc)))
(defn fetch-community-id-input
[{:keys [db]}]
(defn fetch-community-id-input [{:keys [db]}]
(:communities/community-id-input db))
(rf/defn handle-request-to-join
[{:keys [db]} r]
(fx/defn handle-request-to-join [{:keys [db]} r]
(let [{:keys [id community-id] :as request} (<-request-to-join-community-rpc r)]
{:db (assoc-in db [:communities/requests-to-join community-id id] request)}))
(rf/defn handle-removed-chats
[{:keys [db]} chat-ids]
(fx/defn handle-removed-chats [{:keys [db]} chat-ids]
{:db (reduce (fn [db chat-id]
(update db :chats dissoc chat-id))
db
chat-ids)})
(rf/defn handle-community
(fx/defn handle-community
[{:keys [db]} {:keys [id] :as community}]
(when id
{:db (assoc-in db [:communities id] (<-rpc community))}))
(rf/defn handle-communities
(fx/defn handle-communities
{:events [::fetched]}
[{:keys [db]} communities]
{:db (reduce (fn [db {:keys [id] :as community}]
@@ -104,36 +96,34 @@
db
communities)})
(rf/defn handle-response
[_ response-js]
(fx/defn handle-response [_ response-js]
{:dispatch [:sanitize-messages-and-process-response response-js]})
(rf/defn left
(fx/defn left
{:events [::left]}
[cofx response-js]
(rf/merge cofx
(fx/merge cofx
(handle-response cofx response-js)
(navigation/pop-to-root-tab :chat-stack)
(activity-center/notifications-fetch-unread-count)))
(notification-center/get-activity-center-notifications-count)))
(rf/defn joined
(fx/defn joined
{:events [::joined ::requested-to-join]}
[cofx response-js]
(handle-response cofx response-js))
(rf/defn export
(fx/defn export
{:events [::export-pressed]}
[cofx community-id]
{::json-rpc/call [{:method "wakuext_exportCommunity"
:params [community-id]
:on-success #(re-frame/dispatch [:show-popover
{:view :export-community
:community-key %}])
:on-success #(re-frame/dispatch [:show-popover {:view :export-community
:community-key %}])
:on-error #(do
(log/error "failed to export community" community-id %)
(re-frame/dispatch [::failed-to-export %]))}]})
(rf/defn import-community
(fx/defn import-community
{:events [::import]}
[cofx community-key]
{::json-rpc/call [{:method "wakuext_importCommunity"
@@ -144,7 +134,7 @@
(log/error "failed to import community" %)
(re-frame/dispatch [::failed-to-import %]))}]})
(rf/defn join
(fx/defn join
{:events [:communities/join]}
[cofx community-id]
{::json-rpc/call [{:method "wakuext_joinCommunity"
@@ -155,7 +145,7 @@
(log/error "failed to join community" community-id %)
(re-frame/dispatch [::failed-to-join %]))}]})
(rf/defn request-to-join
(fx/defn request-to-join
{:events [::request-to-join]}
[cofx community-id]
{::json-rpc/call [{:method "wakuext_requestToJoinCommunity"
@@ -166,7 +156,7 @@
(log/error "failed to request to join community" community-id %)
(re-frame/dispatch [::failed-to-request-to-join %]))}]})
(rf/defn leave
(fx/defn leave
{:events [:communities/leave]}
[{:keys [db]} community-id]
(let [community-chat-ids (map #(str community-id %)
@@ -178,13 +168,10 @@
:js-response true
:on-success #(re-frame/dispatch [::left %])
:on-error #(do
(log/error "failed to leave community"
community-id
%)
(log/error "failed to leave community" community-id %)
(re-frame/dispatch [::failed-to-leave %]))}]}))
(rf/defn fetch
[_]
(fx/defn fetch [_]
{::json-rpc/call [{:method "wakuext_communities"
:params []
:on-success #(re-frame/dispatch [::fetched %])
@@ -192,7 +179,7 @@
(log/error "failed to fetch communities" %)
(re-frame/dispatch [::failed-to-fetch %]))}]})
(rf/defn chat-created
(fx/defn chat-created
{:events [::chat-created]}
[_ community-id user-pk]
{::json-rpc/call [{:method "wakuext_sendChatMessage"
@@ -206,7 +193,7 @@
:on-error
#(log/error "failed to send a message" %)}]})
(rf/defn invite-users
(fx/defn invite-users
{:events [::invite-people-confirmation-pressed]}
[cofx user-pk contacts]
(let [community-id (fetch-community-id-input cofx)
@@ -222,7 +209,7 @@
:on-error #(do
(log/error "failed to invite-user community" %)
(re-frame/dispatch [::failed-to-invite-people %]))}]})))
(rf/defn share-community
(fx/defn share-community
{:events [::share-community-confirmation-pressed]}
[cofx user-pk contacts]
(let [community-id (fetch-community-id-input cofx)
@@ -239,7 +226,7 @@
(log/error "failed to invite-user community" %)
(re-frame/dispatch [::failed-to-share-community %]))}]})))
(rf/defn create
(fx/defn create
{:events [::create-confirmation-pressed]}
[{:keys [db]}]
(let [{:keys [name description membership image]} (get db :communities/create)]
@@ -261,7 +248,7 @@
(log/error "failed to create community" %)
(re-frame/dispatch [::failed-to-create-community %]))}]})))
(rf/defn edit
(fx/defn edit
{:events [::edit-confirmation-pressed]}
[{:keys [db]}]
(let [{:keys [id name description membership new-image color]} (get db :communities/create)]
@@ -281,10 +268,10 @@
(log/error "failed to edit community" %)
(re-frame/dispatch [::failed-to-edit-community %]))}]}))
(rf/defn create-channel
(fx/defn create-channel
{:events [::create-channel-confirmation-pressed]}
[{:keys [db] :as cofx}]
(let [community-id (fetch-community-id-input cofx)
(let [community-id (fetch-community-id-input cofx)
{:keys [name description color emoji]} (get db :communities/create-channel)]
{::json-rpc/call [{:method "wakuext_createCommunityChat"
:params [community-id
@@ -292,8 +279,7 @@
:description description
:color color
:emoji emoji}
:permissions {:access
constants/community-channel-access-no-membership}}]
:permissions {:access constants/community-channel-access-no-membership}}]
:js-response true
:on-success #(re-frame/dispatch [::community-channel-created %])
:on-error #(do
@@ -302,11 +288,10 @@
(def community-chat-id-length 68)
(defn to-community-chat-id
[chat-id]
(defn to-community-chat-id [chat-id]
(subs chat-id community-chat-id-length))
(rf/defn edit-channel
(fx/defn edit-channel
{:events [::edit-channel-confirmation-pressed]}
[{:keys [db] :as cofx}]
(let [{:keys [name description color community-id emoji edit-channel-id category-id position]}
@@ -320,171 +305,161 @@
:emoji emoji}
:category_id category-id
:position position
:permissions {:access
constants/community-channel-access-no-membership}}]
:permissions {:access constants/community-channel-access-no-membership}}]
:js-response true
:on-success #(re-frame/dispatch [::community-channel-edited %])
:on-error #(do
(log/error "failed to edit community channel" %)
(re-frame/dispatch [::failed-to-edit-community-channel %]))}]}))
(defn require-membership?
[permissions]
(defn require-membership? [permissions]
(not= constants/community-no-membership-access (:access permissions)))
(defn can-post?
[community _ local-chat-id]
(defn can-post? [community _ local-chat-id]
(let [chat-id (to-community-chat-id local-chat-id)]
(get-in community [:chats chat-id :can-post?])))
(rf/defn reset-community-id-input
[{:keys [db]} id]
(fx/defn reset-community-id-input [{:keys [db]} id]
{:db (assoc db :communities/community-id-input id)})
(rf/defn reset-channel-info
[{:keys [db]}]
(fx/defn reset-channel-info [{:keys [db]}]
{:db (assoc db :communities/create-channel {})})
(rf/defn invite-people-pressed
(fx/defn invite-people-pressed
{:events [:communities/invite-people-pressed]}
[cofx id]
(rf/merge cofx
(fx/merge cofx
(reset-community-id-input id)
(bottom-sheet/hide-bottom-sheet)
(navigation/open-modal :invite-people-community {:invite? true})))
(rf/defn share-community-pressed
(fx/defn share-community-pressed
{:events [:communities/share-community-pressed]}
[cofx id]
(rf/merge cofx
(fx/merge cofx
(reset-community-id-input id)
(bottom-sheet/hide-bottom-sheet)
(navigation/open-modal :invite-people-community {})))
(rf/defn create-channel-pressed
(fx/defn create-channel-pressed
{:events [::create-channel-pressed]}
[{:keys [db] :as cofx} id]
(rf/merge cofx
(fx/merge cofx
(reset-community-id-input id)
(reset-channel-info)
(rf/dispatch [::create-channel-fields
(rand-nth emoji-thumbnail-styles/emoji-picker-default-thumbnails)])
(>evt [::create-channel-fields (rand-nth emoji-thumbnail-styles/emoji-picker-default-thumbnails)])
(navigation/open-modal :create-community-channel {:community-id id})))
(rf/defn edit-channel-pressed
(fx/defn edit-channel-pressed
{:events [::edit-channel-pressed]}
[{:keys [db] :as cofx} community-id chat-name description color emoji chat-id category-id position]
(let [{:keys [color emoji]} (if (string/blank? emoji)
(rand-nth emoji-thumbnail-styles/emoji-picker-default-thumbnails)
{:color color :emoji emoji})]
(rf/merge cofx
{:db (assoc db
:communities/create-channel
{:name chat-name
:description description
:color color
:community-id community-id
:emoji emoji
:edit-channel-id chat-id
:category-id category-id
:position position})}
(fx/merge cofx
{:db (assoc db :communities/create-channel {:name chat-name
:description description
:color color
:community-id community-id
:emoji emoji
:edit-channel-id chat-id
:category-id category-id
:position position})}
(navigation/open-modal :edit-community-channel nil))))
(rf/defn community-created
(fx/defn community-created
{:events [::community-created]}
[cofx response-js]
(rf/merge cofx
(fx/merge cofx
(navigation/navigate-back)
(handle-response response-js)))
(rf/defn community-edited
(fx/defn community-edited
{:events [::community-edited]}
[cofx response-js]
(rf/merge cofx
(fx/merge cofx
(navigation/navigate-back)
(handle-response response-js)))
(rf/defn open-create-community
(fx/defn open-create-community
{:events [::open-create-community]}
[{:keys [db] :as cofx}]
(rf/merge cofx
(fx/merge cofx
{:db (assoc db :communities/create {:membership constants/community-no-membership-access})}
(navigation/navigate-to :community-create nil)))
(rf/defn open-edit-community
(fx/defn open-edit-community
{:events [::open-edit-community]}
[{:keys [db] :as cofx} id]
(let [{:keys [name description images permissions color]} (get-in db [:communities id])
{:keys [access]} permissions]
(rf/merge cofx
{:db (assoc db
:communities/create
{:id id
:name name
:description description
:image (get-in images [:large :uri])
:membership access
:color color
:editing? true})}
{:keys [access]} permissions]
(fx/merge cofx
{:db (assoc db :communities/create {:id id
:name name
:description description
:image (get-in images [:large :uri])
:membership access
:color color
:editing? true})}
(navigation/navigate-to :community-edit nil))))
(rf/defn community-imported
(fx/defn community-imported
{:events [::community-imported]}
[cofx response-js]
(rf/merge cofx
(fx/merge cofx
(navigation/navigate-back)
(handle-response response-js)))
(rf/defn people-invited
(fx/defn people-invited
{:events [::people-invited]}
[cofx response-js]
(rf/merge cofx
(fx/merge cofx
(navigation/navigate-back)
(handle-response response-js)))
(rf/defn community-channel-created
(fx/defn community-channel-created
{:events [::community-channel-created]}
[cofx response-js]
(rf/merge cofx
(fx/merge cofx
(navigation/navigate-back)
(handle-response response-js)))
(rf/defn community-channel-edited
(fx/defn community-channel-edited
{:events [::community-channel-edited]}
[cofx response-js]
(rf/merge cofx
(fx/merge cofx
(navigation/navigate-back)
(handle-response response-js)))
(rf/defn create-field
(fx/defn create-field
{:events [::create-field]}
[{:keys [db]} field value]
{:db (assoc-in db [:communities/create field] value)})
(rf/defn remove-field
(fx/defn remove-field
{:events [::remove-field]}
[{:keys [db]} field]
{:db (update-in db [:communities/create] dissoc field)})
(rf/defn create-channel-field
(fx/defn create-channel-field
{:events [::create-channel-field]}
[{:keys [db]} field value]
{:db (assoc-in db [:communities/create-channel field] value)})
(rf/defn create-channel-fields
(fx/defn create-channel-fields
{:events [::create-channel-fields]}
[{:keys [db]} field-values]
{:db (update-in db [:communities/create-channel] merge field-values)})
(rf/defn member-banned
(fx/defn member-banned
{:events [::member-banned]}
[cofx response-js]
(rf/merge cofx
(fx/merge cofx
(bottom-sheet/hide-bottom-sheet)
(handle-response response-js)
(activity-center/notifications-fetch-unread-count)))
(notification-center/get-activity-center-notifications-count)))
(rf/defn member-ban
(fx/defn member-ban
{:events [::member-ban]}
[cofx community-id public-key]
{::json-rpc/call [{:method "wakuext_banUserFromCommunity"
@@ -492,43 +467,35 @@
:user public-key}]
:js-response true
:on-success #(re-frame/dispatch [::member-banned %])
:on-error #(log/error "failed to ban user from community"
community-id
public-key
%)}]})
:on-error #(log/error "failed to ban user from community" community-id public-key %)}]})
(rf/defn member-kicked
(fx/defn member-kicked
{:events [::member-kicked]}
[cofx response-js]
(rf/merge cofx
(fx/merge cofx
(bottom-sheet/hide-bottom-sheet)
(handle-response response-js)))
(rf/defn member-kick
(fx/defn member-kick
{:events [::member-kick]}
[cofx community-id public-key]
{::json-rpc/call [{:method "wakuext_removeUserFromCommunity"
:params [community-id public-key]
:js-response true
:on-success #(re-frame/dispatch [::member-kicked %])
:on-error #(log/error "failed to remove user from community"
community-id
public-key
%)}]})
:on-error #(log/error "failed to remove user from community" community-id public-key %)}]})
(rf/defn delete-community
(fx/defn delete-community
{:events [::delete-community]}
[cofx community-id]
(log/error "Community delete is not yet implemented"))
(rf/defn requests-to-join-fetched
(fx/defn requests-to-join-fetched
{:events [::requests-to-join-fetched]}
[{:keys [db]} community-id requests]
{:db (assoc-in db
[:communities/requests-to-join community-id]
(<-requests-to-join-community-rpc requests))})
{:db (assoc-in db [:communities/requests-to-join community-id] (<-requests-to-join-community-rpc requests))})
(rf/defn fetch-requests-to-join
(fx/defn fetch-requests-to-join
{:events [::fetch-requests-to-join]}
[cofx community-id]
{::json-rpc/call [{:method "wakuext_pendingRequestsToJoinForCommunity"
@@ -536,56 +503,48 @@
:on-success #(re-frame/dispatch [::requests-to-join-fetched community-id %])
:on-error #(log/error "failed to fetch requests-to-join" community-id %)}]})
(defn fetch-requests-to-join!
[community-id]
(defn fetch-requests-to-join! [community-id]
(re-frame/dispatch [::fetch-requests-to-join community-id]))
(rf/defn request-to-join-accepted
(fx/defn request-to-join-accepted
{:events [::request-to-join-accepted]}
[{:keys [db] :as cofx} community-id request-id response-js]
(rf/merge cofx
(fx/merge cofx
{:db (update-in db [:communities/requests-to-join community-id] dissoc request-id)}
(handle-response response-js)))
(rf/defn request-to-join-declined
(fx/defn request-to-join-declined
{:events [::request-to-join-declined]}
[{:keys [db] :as cofx} community-id request-id response-js]
(rf/merge cofx
(fx/merge cofx
{:db (update-in db [:communities/requests-to-join community-id] dissoc request-id)}
(handle-response response-js)))
(rf/defn accept-request-to-join-pressed
(fx/defn accept-request-to-join-pressed
{:events [:communities.ui/accept-request-to-join-pressed]}
[cofx community-id request-id]
{::json-rpc/call [{:method "wakuext_acceptRequestToJoinCommunity"
:params [{:id request-id}]
:js-response true
:on-success #(re-frame/dispatch [::request-to-join-accepted community-id request-id
%])
:on-error #(log/error "failed to accept requests-to-join"
community-id
request-id
%)}]})
:on-success #(re-frame/dispatch [::request-to-join-accepted community-id request-id %])
:on-error #(log/error "failed to accept requests-to-join" community-id request-id %)}]})
(rf/defn decline-request-to-join-pressed
(fx/defn decline-request-to-join-pressed
{:events [:communities.ui/decline-request-to-join-pressed]}
[cofx community-id request-id]
{::json-rpc/call [{:method "wakuext_declineRequestToJoinCommunity"
:params [{:id request-id}]
:js-response true
:on-success #(re-frame/dispatch [::request-to-join-declined community-id request-id
%])
:on-error #(log/error "failed to decline requests-to-join"
community-id
request-id)}]})
:on-success #(re-frame/dispatch [::request-to-join-declined community-id request-id %])
:on-error #(log/error "failed to decline requests-to-join" community-id request-id)}]})
(rf/defn switch-communities-enabled
(fx/defn switch-communities-enabled
{:events [:multiaccounts.ui/switch-communities-enabled]}
[{:keys [db]} enabled?]
{::async-storage/set! {:communities-enabled? enabled?}
:db (assoc db :communities/enabled? enabled?)})
(rf/defn create-category
(fx/defn create-category
{:events [::create-category-confirmation-pressed]}
[_ community-id category-title chat-ids]
{::json-rpc/call [{:method "wakuext_createCommunityCategory"
@@ -598,13 +557,12 @@
(re-frame/dispatch [:sanitize-messages-and-process-response %]))
:on-error #(log/error "failed to create community category" %)}]})
(rf/defn remove-chat-from-category
(fx/defn remove-chat-from-category
{:events [:remove-chat-from-community-category]}
[{:keys [db]} community-id id categoryID]
(let [category (get-in db [:communities community-id :categories categoryID])
category-chats (map :id
(filter #(and (= (:categoryID %) categoryID) (not= id (:id %)))
(vals (get-in db [:communities community-id :chats]))))]
category-chats (map :id (filter #(and (= (:categoryID %) categoryID) (not= id (:id %)))
(vals (get-in db [:communities community-id :chats]))))]
{::json-rpc/call [{:method "wakuext_editCommunityCategory"
:params [{:communityId community-id
:categoryId categoryID
@@ -614,7 +572,7 @@
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
:on-error #(log/error "failed to remove chat from community" %)}]}))
(rf/defn delete-community-chat
(fx/defn delete-community-chat
{:events [:delete-community-chat]}
[_ community-id chat-id]
{::json-rpc/call [{:method "wakuext_deleteCommunityChat"
@@ -623,7 +581,7 @@
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
:on-error #(log/error "failed to delete community chat" %)}]})
(rf/defn delete-category
(fx/defn delete-category
{:events [:delete-community-category]}
[_ community-id category-id]
{::json-rpc/call [{:method "wakuext_deleteCommunityCategory"
@@ -633,7 +591,7 @@
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
:on-error #(log/error "failed to delete community category" %)}]})
(rf/defn change-category
(fx/defn change-category
{:events [::change-category-confirmation-pressed]}
[cofx community-id category-id {:keys [id position categoryID]}]
(if (not (string/blank? category-id))
@@ -647,11 +605,11 @@
(re-frame/dispatch [:navigate-back])
(re-frame/dispatch [:sanitize-messages-and-process-response %]))
:on-error #(log/error "failed to change community category" %)}]}
(rf/merge cofx
(fx/merge cofx
(navigation/navigate-back)
(remove-chat-from-category community-id id categoryID))))
(rf/defn reorder-category-chat
(fx/defn reorder-category-chat
{:events [::reorder-community-category-chat]}
[_ community-id category-id chat-id new-position]
{::json-rpc/call [{:method "wakuext_reorderCommunityChat"
@@ -663,7 +621,7 @@
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
:on-error #(log/error "failed to reorder community category chat" %)}]})
(rf/defn reorder-category
(fx/defn reorder-category
{:events [::reorder-community-category]}
[_ community-id category-id new-position]
{::json-rpc/call [{:method "wakuext_reorderCommunityCategories"
@@ -674,18 +632,17 @@
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
:on-error #(log/error "failed to reorder community category" %)}]})
(defn category-hash
[public-key community-id category-id]
(defn category-hash [public-key community-id category-id]
(hash (str public-key community-id category-id)))
(rf/defn store-category-state
(fx/defn store-category-state
{:events [::store-category-state]}
[{:keys [db]} community-id category-id state-open?]
(let [public-key (get-in db [:multiaccount :public-key])
hash (category-hash public-key community-id category-id)]
{::async-storage/set! {hash state-open?}}))
(rf/defn update-category-states-in-db
(fx/defn update-category-states-in-db
{:events [::category-states-loaded]}
[{:keys [db]} community-id hashes states]
(let [public-key (get-in db [:multiaccount :public-key])
@@ -695,11 +652,10 @@
state (get states hash)
category-updated (assoc category :state state)]
(assoc acc category-id category-updated)))
{}
categories-old)]
{} categories-old)]
{:db (update-in db [:communities community-id :categories] merge categories)}))
(rf/defn load-category-states
(fx/defn load-category-states
{:events [:communities/load-category-states]}
[{:keys [db]} community-id]
(let [public-key (get-in db [:multiaccount :public-key])
@@ -713,27 +669,26 @@
(-> acc
(assoc-in [:hashes category-id] hash)
(update :keys #(conj % hash)))))
{}
categories)]
{} categories)]
{::async-storage/get {:keys keys
:cb #(re-frame/dispatch
[::category-states-loaded community-id hashes %])}}))
(rf/defn navigate-to-community
(fx/defn navigate-to-community
{:events [:communities/navigate-to-community]}
[cofx community-id]
(rf/merge cofx
(fx/merge cofx
(navigation/pop-to-root-tab :shell-stack)
(navigation/navigate-to-nav2 :community community-id true)))
(rf/defn member-role-updated
(fx/defn member-role-updated
{:events [:community.member/role-updated]}
[cofx response-js]
(rf/merge cofx
(fx/merge cofx
(bottom-sheet/hide-bottom-sheet)
(handle-response response-js)))
(rf/defn add-role-to-member
(fx/defn add-role-to-member
{:events [:community.member/add-role]}
[cofx community-id public-key role-id]
{::json-rpc/call [{:method "wakuext_addRoleToMember"
@@ -747,7 +702,7 @@
:public-key public-key
:role-id role-id})}]})
(rf/defn remove-role-from-member
(fx/defn remove-role-from-member
{:events [:community.member/remove-role]}
[_ community-id public-key role-id]
{::json-rpc/call [{:method "wakuext_removeRoleFromMember"
@@ -755,9 +710,8 @@
:user public-key
:role role-id}]}
:on-success #(re-frame/dispatch [:community.member/role-updated %])
:on-error
#(log/error "failed to remove role from member"
{:error %
:community-id community-id
:public-key public-key
:role-id role-id})]})
:on-error #(log/error "failed to remove role from member"
{:error %
:community-id community-id
:public-key public-key
:role-id role-id})]})
+2 -7
View File
@@ -197,13 +197,8 @@
(def ^:const sticker-pack-status-pending 2)
(def ^:const sticker-pack-status-owned 3)
(def ^:const delete-message-for-me-undo-time-limit-ms 4000)
(def ^:const community-member-role-all 1)
(def ^:const community-member-role-manage-users 2)
(def ^:const community-member-role-moderator 3)
(def local-pairing-connection-string-identifier
"If any string begins with cs we know its a connection string.
This is useful when we read QR codes we know it is a connection string if it begins with this identifier.
An example of a connection string is -> cs2:5vd6J6:Jfc:27xMmHKEYwzRGXcvTtuiLZFfXscMx4Mz8d9wEHUxDj4p7:EG7Z13QScfWBJNJ5cprszzDQ5fBVsYMirXo8MaQFJvpF:3 "
"cs")
+35 -38
View File
@@ -4,12 +4,12 @@
[status-im.contact.db :as contact.db]
[status-im.data-store.chats :as chats-store]
[status-im.data-store.contacts :as contacts-store]
[status-im.utils.types :as types]
[status-im2.contexts.activity-center.events :as activity-center]
[status-im2.navigation.events :as navigation]
[utils.re-frame :as rf]))
[status-im.utils.types :as types]
[status-im.notifications-center.core :as notification-center]
[status-im.utils.fx :as fx]))
(rf/defn clean-up-chat
(fx/defn clean-up-chat
[{:keys [db] :as cofx}
public-key
{:keys [chat-id
@@ -21,63 +21,60 @@
(when (= from public-key)
message-id))
(get-in db [:messages chat-id]))
db (-> db
;; remove messages
(update-in [:messages chat-id]
#(apply dissoc % removed-messages-ids))
(update-in [:chats chat-id]
assoc
:unviewed-messages-count unviewed-messages-count
:unviewed-mentions-count unviewed-mentions-count
:last-message last-message))]
{:db (assoc-in db
[:message-lists chat-id]
(message-list/add-many nil (vals (get-in db [:messages chat-id]))))}))
db (-> db
;; remove messages
(update-in [:messages chat-id]
#(apply dissoc % removed-messages-ids))
(update-in [:chats chat-id]
assoc
:unviewed-messages-count unviewed-messages-count
:unviewed-mentions-count unviewed-mentions-count
:last-message last-message))]
{:db (assoc-in db [:message-lists chat-id]
(message-list/add-many nil (vals (get-in db [:messages chat-id]))))}))
(rf/defn contact-blocked
(fx/defn contact-blocked
{:events [::contact-blocked]}
[{:keys [db] :as cofx} {:keys [public-key]} chats]
(let [fxs (when chats
(map #(->> (chats-store/<-rpc %)
(clean-up-chat public-key))
(types/js->clj chats)))]
(apply rf/merge
(clean-up-chat public-key)) (types/js->clj chats)))]
(apply fx/merge
cofx
{:db (-> db
(update :chats dissoc public-key)
(update :chats-home-list disj public-key)
(assoc-in [:contacts/contacts public-key :added] false))
{:db (-> db
(update :chats dissoc public-key)
(update :chats-home-list disj public-key)
(assoc-in [:contacts/contacts public-key :added] false))
:clear-message-notifications
[[public-key] (get-in db [:multiaccount :remote-push-notifications-enabled?])]}
(activity-center/notifications-fetch-unread-count)
(notification-center/get-activity-center-notifications-count)
fxs)))
(rf/defn block-contact
(fx/defn block-contact
{:events [:contact.ui/block-contact-confirmed]}
[{:keys [db] :as cofx} public-key]
(let [contact (-> (contact.db/public-key->contact
(:contacts/contacts db)
public-key)
(assoc :blocked true
:added false))
(let [contact (-> (contact.db/public-key->contact
(:contacts/contacts db)
public-key)
(assoc :blocked true
:added false))
from-one-to-one-chat? (not (get-in db [:chats (:current-chat-id db) :group-chat]))]
(rf/merge cofx
(fx/merge cofx
{:db (-> db
;; add the contact to blocked contacts
(update :contacts/blocked (fnil conj #{}) public-key)
;; update the contact in contacts list
(assoc-in [:contacts/contacts public-key] contact))}
(contacts-store/block
public-key
#(do (re-frame/dispatch [::contact-blocked contact (.-chats %)])
(re-frame/dispatch [:sanitize-messages-and-process-response %])
(re-frame/dispatch [:hide-popover])))
public-key #(do (re-frame/dispatch [::contact-blocked contact (.-chats %)])
(re-frame/dispatch [:sanitize-messages-and-process-response %])
(re-frame/dispatch [:hide-popover])))
;; reset navigation to avoid going back to non existing one to one chat
(if from-one-to-one-chat?
(navigation/pop-to-root-tab :chat-stack)
(navigation/navigate-back)))))
(rf/defn contact-unblocked
(fx/defn contact-unblocked
{:events [::contact-unblocked]}
[{:keys [db]} contact-id]
(let [contact (-> (get-in db [:contacts/contacts contact-id])
@@ -86,7 +83,7 @@
(update :contacts/blocked disj contact-id)
(assoc-in [:contacts/contacts contact-id] contact))}))
(rf/defn unblock-contact
(fx/defn unblock-contact
{:events [:contact.ui/unblock-contact-pressed]}
[cofx contact-id]
(contacts-store/unblock
+11 -8
View File
@@ -1,29 +1,32 @@
(ns status-im.contact.chat
(:require [re-frame.core :as re-frame]
[status-im2.navigation.events :as navigation]
[status-im.utils.fx :as fx]
[status-im.chat.models :as chat]
[status-im.contact.core :as contact]
[status-im2.navigation.events :as navigation]
[utils.re-frame :as rf]))
[status-im.notifications-center.core :as notification-center]))
(rf/defn send-message-pressed
(fx/defn send-message-pressed
{:events [:contact.ui/send-message-pressed]
:interceptors [(re-frame/inject-cofx :random-id-generator)]}
[cofx {:keys [public-key ens-name]}]
(chat/start-chat cofx public-key ens-name))
(fx/merge cofx
(chat/start-chat public-key ens-name)
(notification-center/accept-all-activity-center-notifications-from-chat public-key)))
(rf/defn contact-code-submitted
(fx/defn contact-code-submitted
{:events [:contact.ui/contact-code-submitted]
:interceptors [(re-frame/inject-cofx :random-id-generator)]}
[{{:contacts/keys [new-identity]} :db :as cofx} new-contact? nickname]
(let [{:keys [public-key ens-name]} new-identity]
(rf/merge cofx
(fx/merge cofx
#(if new-contact?
(contact/add-contact % public-key nickname ens-name)
(chat/start-chat % public-key ens-name))
#(when new-contact?
(navigation/navigate-back %)))))
(rf/defn pinned-messages-pressed
{:events [:contact.ui/pinned-messages-pressed]}
(fx/defn pinned-messages-pressed
{:events [:contact.ui/pinned-messages-pressed]}
[cofx public-key]
(chat/navigate-to-user-pinned-messages cofx public-key))
+46 -51
View File
@@ -1,44 +1,42 @@
(ns status-im.contact.core
(:require [re-frame.core :as re-frame]
[status-im.constants :as constants]
[status-im.contact.block :as contact.block]
[status-im.contact.db :as contact.db]
[status-im.data-store.contacts :as contacts-store]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im2.navigation.events :as navigation]
[status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im.utils.fx :as fx]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
[status-im.constants :as constants]
[status-im.contact.block :as contact.block]))
(rf/defn load-contacts
(fx/defn load-contacts
{:events [::contacts-loaded]}
[{:keys [db] :as cofx} all-contacts]
(let [contacts-list (map #(vector (:public-key %)
(if (empty? (:address %))
(dissoc % :address)
%))
(let [contacts-list (map #(vector (:public-key %) (if (empty? (:address %))
(dissoc % :address)
%))
all-contacts)
contacts (into {} contacts-list)]
contacts (into {} contacts-list)]
{:db (cond-> (-> db
(update :contacts/contacts #(merge contacts %))
(assoc :contacts/blocked (contact.db/get-blocked-contacts all-contacts))))}))
(defn build-contact
[{{:keys [multiaccount]
:contacts/keys [contacts]}
:db} public-key]
[{{:keys [multiaccount]
:contacts/keys [contacts]} :db} public-key]
(cond-> (contact.db/public-key->contact contacts public-key)
(= public-key (:public-key multiaccount))
(assoc :name (:name multiaccount))))
(rf/defn ensure-contacts
(fx/defn ensure-contacts
[{:keys [db]} contacts chats]
(let [events
(reduce
(fn [acc {:keys [public-key] :as contact}]
(let [added (:added contact)
was-added (contact.db/added? db public-key)
blocked (:blocked contact)
(let [added (:added contact)
was-added (contact.db/added? db public-key)
blocked (:blocked contact)
was-blocked (contact.db/blocked? db public-key)]
(cond-> acc
(and added (not was-added))
@@ -50,11 +48,10 @@
(and blocked (not was-blocked))
(conj [::contact.block/contact-blocked contact chats]))))
[[:offload-messages constants/timeline-chat-id]
[:activity-center.notifications/fetch-unread-count]]
[:get-activity-center-notifications-count]]
contacts)]
(merge
{:db (update db
:contacts/contacts
{:db (update db :contacts/contacts
#(reduce (fn [acc {:keys [public-key] :as contact}]
(-> acc
(update public-key merge contact)
@@ -71,15 +68,15 @@
:profile-image identicon
:address address}))
(rf/defn send-contact-request
(fx/defn send-contact-request
{:events [::send-contact-request]}
[{:keys [db] :as cofx} public-key]
(let [{:keys [name profile-image]} (own-info db)]
{::json-rpc/call [{:method "wakuext_sendContactUpdate"
:params [public-key name profile-image]
{::json-rpc/call [{:method "wakuext_sendContactUpdate"
:params [public-key name profile-image]
:on-success #(log/debug "contact request sent" public-key)}]}))
(rf/defn add-contact
(fx/defn add-contact
"Add a contact and set pending to false"
{:events [:contact.ui/add-to-contact-pressed]}
[{:keys [db] :as cofx} public-key nickname ens-name]
@@ -93,62 +90,60 @@
(re-frame/dispatch [:sanitize-messages-and-process-response %])
(re-frame/dispatch [:offload-messages constants/timeline-chat-id])))))
(rf/defn remove-contact
(fx/defn remove-contact
"Remove a contact from current account's contact list"
{:events [:contact.ui/remove-contact-pressed]}
[{:keys [db]} {:keys [public-key]}]
{:db (-> db
(assoc-in [:contacts/contacts public-key :added] false)
(assoc-in [:contacts/contacts public-key :contact-request-state]
constants/contact-request-state-none))
::json-rpc/call [{:method "wakuext_removeContact"
:params [public-key]
{:db (-> db
(assoc-in [:contacts/contacts public-key :added] false)
(assoc-in [:contacts/contacts public-key :contact-request-state] constants/contact-request-state-none))
::json-rpc/call [{:method "wakuext_removeContact"
:params [public-key]
:on-success #(log/debug "contact removed successfully")}
{:method "wakuext_retractContactRequest"
:params [{:contactId public-key}]
{:method "wakuext_retractContactRequest"
:params [{:contactId public-key}]
:on-success #(log/debug "contact removed successfully")}]
:dispatch [:offload-messages constants/timeline-chat-id]})
:dispatch [:offload-messages constants/timeline-chat-id]})
(rf/defn accept-contact-request
(fx/defn accept-contact-request
{:events [:contact-requests.ui/accept-request]}
[{:keys [db]} id]
{::json-rpc/call [{:method "wakuext_acceptContactRequest"
:params [{:id id}]
{::json-rpc/call [{:method "wakuext_acceptContactRequest"
:params [{:id id}]
:js-response true
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]})
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]})
(rf/defn decline-contact-request
(fx/defn decline-contact-request
{:events [:contact-requests.ui/decline-request]}
[{:keys [db]} id]
{::json-rpc/call [{:method "wakuext_dismissContactRequest"
:params [{:id id}]
{::json-rpc/call [{:method "wakuext_dismissContactRequest"
:params [{:id id}]
:js-response true
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]})
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]})
(rf/defn initialize-contacts
[cofx]
(fx/defn initialize-contacts [cofx]
(contacts-store/fetch-contacts-rpc cofx #(re-frame/dispatch [::contacts-loaded %])))
(rf/defn open-contact-toggle-list
(fx/defn open-contact-toggle-list
{:events [:contact.ui/start-group-chat-pressed]}
[{:keys [db] :as cofx}]
(rf/merge cofx
(fx/merge cofx
{:db (assoc db
:group/selected-contacts #{}
:new-chat-name "")}
:new-chat-name "")}
(navigation/navigate-to-cofx :contact-toggle-list nil)))
(rf/defn update-nickname
(fx/defn update-nickname
{:events [:contacts/update-nickname]}
[{:keys [db] :as cofx} public-key nickname]
(rf/merge cofx
(fx/merge cofx
(contacts-store/set-nickname
public-key
nickname
#(re-frame/dispatch [:sanitize-messages-and-process-response %]))
(navigation/navigate-back)))
(rf/defn switch-mutual-contact-requests-enabled
(fx/defn switch-mutual-contact-requests-enabled
{:events [:multiaccounts.ui/switch-mutual-contact-requests-enabled]}
[cofx enabled?]
(multiaccounts.update/multiaccount-update
@@ -157,7 +152,7 @@
enabled?
nil))
(rf/defn set-search-query
(fx/defn set-search-query
{:events [:contacts/set-search-query]}
[{:keys [db] :as cofx} value]
{:db (assoc db :contacts/search-query value)})
+4 -4
View File
@@ -1,6 +1,6 @@
(ns status-im.contact.db
(:require [clojure.set :as clojure.set]
[clojure.string :as string]
[clojure.string :as clojure.string]
[status-im.ethereum.core :as ethereum]
[status-im.utils.gfycat.core :as gfycat]
[status-im.utils.identicon :as identicon]
@@ -40,8 +40,8 @@
(sort (fn [c1 c2]
(let [name1 (or (:name c1) (:address c1) (:public-key c1))
name2 (or (:name c2) (:address c2) (:public-key c2))]
(compare (string/lower-case name1)
(string/lower-case name2))))
(compare (clojure.string/lower-case name1)
(clojure.string/lower-case name2))))
(vals contacts)))
(defn filter-dapps
@@ -71,7 +71,7 @@
(->> members
(map #(or (get all-contacts %)
(public-key->new-contact %)))
(sort-by (comp string/lower-case #(or (:name %) (:alias %))))
(sort-by (comp clojure.string/lower-case #(or (:name %) (:alias %))))
(map #(if (get admins (:public-key %))
(assoc % :admin? true)
%)))))
+6 -8
View File
@@ -1,18 +1,16 @@
(ns status-im.currency.core
(:require [status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im.wallet.prices :as prices]
[utils.re-frame :as rf]))
[status-im.utils.fx :as fx]
[status-im.wallet.prices :as prices]))
(defn get-currency
[db]
(defn get-currency [db]
(get-in db [:multiaccount :currency] :usd))
(rf/defn set-currency
(fx/defn set-currency
{:events [:wallet.settings.ui/currency-selected]}
[{:keys [db] :as cofx} currency]
(rf/merge cofx
(fx/merge cofx
(multiaccounts.update/multiaccount-update
:currency
currency
:currency currency
{})
(prices/update-prices)))
+16 -12
View File
@@ -1,8 +1,10 @@
(ns status-im.data-store.activities
(:require [clojure.set :as set]
[quo.design-system.colors :as colors]
[status-im.constants :as constants]
[status-im2.contexts.activity-center.notification-types :as notification-types]
[status-im.data-store.messages :as messages]))
[status-im.activity-center.notification-types :as notification-types]
[status-im.data-store.messages :as messages]
[status-im2.setup.config :as config]))
(defn- rpc->type [{:keys [type name] :as chat}]
(case type
@@ -33,13 +35,15 @@
chat))
(defn <-rpc [item]
(-> item
rpc->type
(set/rename-keys {:lastMessage :last-message
:replyMessage :reply-message
:chatId :chat-id
:contactVerificationStatus :contact-verification-status})
(update :last-message #(when % (messages/<-rpc %)))
(update :message #(when % (messages/<-rpc %)))
(update :reply-message #(when % (messages/<-rpc %)))
(dissoc :chatId)))
(cond-> (-> item
rpc->type
(set/rename-keys {:lastMessage :last-message
:replyMessage :reply-message
:chatId :chat-id
:contactVerificationStatus :contact-verification-status})
(update :last-message #(when % (messages/<-rpc %)))
(update :message #(when % (messages/<-rpc %)))
(update :reply-message #(when % (messages/<-rpc %)))
(dissoc :chatId))
(not config/new-activity-center-enabled?)
(assoc :color (rand-nth colors/chat-colors))))
+77 -75
View File
@@ -1,8 +1,9 @@
(ns status-im.data-store.activities-test
(:require [cljs.test :refer [deftest is testing]]
[status-im.constants :as constants]
[status-im2.contexts.activity-center.notification-types :as notification-types]
[status-im.data-store.activities :as store]))
[status-im.activity-center.notification-types :as notification-types]
[status-im.data-store.activities :as store]
[status-im2.setup.config :as config]))
(def chat-id
"0x04c66155")
@@ -18,81 +19,82 @@
:replyMessage {}})
(deftest <-rpc-test
(testing "renames keys"
(is (= {:name chat-name
:chat-id chat-id
:contact-verification-status constants/contact-verification-status-pending}
(-> raw-notification
store/<-rpc
(dissoc :last-message :message :reply-message)))))
(with-redefs [config/new-activity-center-enabled? true]
(testing "renames keys"
(is (= {:name chat-name
:chat-id chat-id
:contact-verification-status constants/contact-verification-status-pending}
(-> raw-notification
store/<-rpc
(dissoc :last-message :message :reply-message)))))
(testing "transforms messages from RPC response"
(is (= {:last-message {:quoted-message nil
:outgoing-status nil
:command-parameters nil
:content {:sticker nil
:rtl? nil
:ens-name nil
:parsed-text nil
:response-to nil
:chat-id nil
:image nil
:line-count nil
:links nil
:text nil}
:outgoing false}
:message nil
:reply-message {:quoted-message nil
:outgoing-status nil
:command-parameters nil
:content {:sticker nil
:rtl? nil
:ens-name nil
:parsed-text nil
:response-to nil
:chat-id nil
:image nil
:line-count nil
:links nil
:text nil}
:outgoing false}}
(-> raw-notification
store/<-rpc
(select-keys [:last-message :message :reply-message])))))
(testing "transforms messages from RPC response"
(is (= {:last-message {:quoted-message nil
:outgoing-status nil
:command-parameters nil
:content {:sticker nil
:rtl? nil
:ens-name nil
:parsed-text nil
:response-to nil
:chat-id nil
:image nil
:line-count nil
:links nil
:text nil}
:outgoing false}
:message nil
:reply-message {:quoted-message nil
:outgoing-status nil
:command-parameters nil
:content {:sticker nil
:rtl? nil
:ens-name nil
:parsed-text nil
:response-to nil
:chat-id nil
:image nil
:line-count nil
:links nil
:text nil}
:outgoing false}}
(-> raw-notification
store/<-rpc
(select-keys [:last-message :message :reply-message])))))
(testing "augments notification based on its type"
(is (= {:chat-name chat-name
:chat-type constants/private-group-chat-type
:name chat-name}
(-> raw-notification
(assoc :type notification-types/reply)
store/<-rpc
(select-keys [:name :chat-type :chat-name :public? :group-chat]))))
(testing "augments notification based on its type"
(is (= {:chat-name chat-name
:chat-type constants/private-group-chat-type
:name chat-name}
(-> raw-notification
(assoc :type notification-types/reply)
store/<-rpc
(select-keys [:name :chat-type :chat-name :public? :group-chat]))))
(is (= {:chat-name chat-name
:chat-type constants/private-group-chat-type
:name chat-name}
(-> raw-notification
(assoc :type notification-types/mention)
store/<-rpc
(select-keys [:name :chat-type :chat-name :public? :group-chat]))))
(is (= {:chat-name chat-name
:chat-type constants/private-group-chat-type
:name chat-name}
(-> raw-notification
(assoc :type notification-types/mention)
store/<-rpc
(select-keys [:name :chat-type :chat-name :public? :group-chat]))))
(is (= {:chat-name chat-name
:chat-type constants/private-group-chat-type
:group-chat true
:name chat-name
:public? false}
(-> raw-notification
(assoc :type notification-types/private-group-chat)
store/<-rpc
(select-keys [:name :chat-type :chat-name :public? :group-chat]))))
(is (= {:chat-name chat-name
:chat-type constants/private-group-chat-type
:group-chat true
:name chat-name
:public? false}
(-> raw-notification
(assoc :type notification-types/private-group-chat)
store/<-rpc
(select-keys [:name :chat-type :chat-name :public? :group-chat]))))
(is (= {:chat-name chat-name
:chat-type constants/one-to-one-chat-type
:group-chat false
:name chat-name
:public? false}
(-> raw-notification
(assoc :type notification-types/one-to-one-chat)
store/<-rpc
(select-keys [:name :chat-type :chat-name :public? :group-chat]))))))
(is (= {:chat-name chat-name
:chat-type constants/one-to-one-chat-type
:group-chat false
:name chat-name
:public? false}
(-> raw-notification
(assoc :type notification-types/one-to-one-chat)
store/<-rpc
(select-keys [:name :chat-type :chat-name :public? :group-chat])))))))
+41 -48
View File
@@ -1,34 +1,31 @@
(ns status-im.data-store.chats
(:require [clojure.set :as clojure.set]
[status-im.constants :as constants]
[status-im.data-store.messages :as messages]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.utils.types :as types]
[status-im.constants :as constants]
[status-im.utils.fx :as fx]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
[status-im.utils.types :as types]))
(defn rpc->type
[{:keys [chat-type name] :as chat}]
(defn rpc->type [{:keys [chat-type name] :as chat}]
(cond
(or (= constants/public-chat-type chat-type)
(= constants/profile-chat-type chat-type)
(= constants/timeline-chat-type chat-type))
(assoc chat
:chat-name (str "#" name)
:public? true
:group-chat true
:timeline? (= constants/timeline-chat-type chat-type))
(= constants/timeline-chat-type chat-type)) (assoc chat
:chat-name (str "#" name)
:public? true
:group-chat true
:timeline? (= constants/timeline-chat-type chat-type))
(= constants/community-chat-type chat-type) (assoc chat
:chat-name name
:chat-name name
:group-chat true)
(= constants/private-group-chat-type chat-type) (assoc chat
:chat-name name
:public? false
:chat-name name
:public? false
:group-chat true)
:else (assoc chat :public? false :group-chat false)))
(defn members-reducer
[acc member]
(defn members-reducer [acc member]
(cond-> acc
(:admin member)
(update :admins conj (:id member))
@@ -37,48 +34,45 @@
:always
(update :contacts conj (:id member))))
(defn- unmarshal-members
[{:keys [members chat-type] :as chat}]
(defn- unmarshal-members [{:keys [members chat-type] :as chat}]
(cond
(= constants/public-chat-type chat-type) (assoc chat
:contacts #{}
:admins #{}
:members-joined #{})
(= constants/public-chat-type chat-type) (assoc chat
:contacts #{}
:admins #{}
:members-joined #{})
(= constants/private-group-chat-type chat-type) (merge chat
(reduce members-reducer
{:admins #{}
{:admins #{}
:members-joined #{}
:contacts #{}}
:contacts #{}}
members))
:else
(assoc chat
:contacts #{(:id chat)}
:admins #{}
:contacts #{(:id chat)}
:admins #{}
:members-joined #{})))
(defn <-rpc
[chat]
(defn <-rpc [chat]
(-> chat
(clojure.set/rename-keys {:id :chat-id
:communityId :community-id
:syncedFrom :synced-from
:syncedTo :synced-to
(clojure.set/rename-keys {:id :chat-id
:communityId :community-id
:syncedFrom :synced-from
:syncedTo :synced-to
:membershipUpdateEvents :membership-update-events
:deletedAtClockValue :deleted-at-clock-value
:chatType :chat-type
:unviewedMessagesCount :unviewed-messages-count
:unviewedMentionsCount :unviewed-mentions-count
:lastMessage :last-message
:lastClockValue :last-clock-value
:invitationAdmin :invitation-admin
:profile :profile-public-key})
:deletedAtClockValue :deleted-at-clock-value
:chatType :chat-type
:unviewedMessagesCount :unviewed-messages-count
:unviewedMentionsCount :unviewed-mentions-count
:lastMessage :last-message
:lastClockValue :last-clock-value
:invitationAdmin :invitation-admin
:profile :profile-public-key})
rpc->type
unmarshal-members
(update :last-message #(when % (messages/<-rpc %)))
(dissoc :members)))
(defn <-rpc-js
[^js chat]
(defn <-rpc-js [^js chat]
(-> {:name (.-name chat)
:description (.-description chat)
:color (.-color chat)
@@ -109,10 +103,9 @@
rpc->type
unmarshal-members))
(rf/defn fetch-chats-rpc
[_ {:keys [on-success]}]
{::json-rpc/call [{:method "wakuext_chatsPreview"
:params []
(fx/defn fetch-chats-rpc [_ {:keys [on-success]}]
{::json-rpc/call [{:method "wakuext_chatsPreview"
:params []
:js-response true
:on-success #(on-success ^js %)
:on-error #(log/error "failed to fetch chats" 0 -1 %)}]})
:on-success #(on-success ^js %)
:on-error #(log/error "failed to fetch chats" 0 -1 %)}]})
+35 -42
View File
@@ -1,11 +1,10 @@
(ns status-im.data-store.contacts
(:require [clojure.set :as clojure.set]
[status-im.ethereum.json-rpc :as json-rpc]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
[status-im.utils.fx :as fx]
[taoensso.timbre :as log]))
(defn <-rpc
[contact]
(defn <-rpc [contact]
(-> contact
(clojure.set/rename-keys
{:id :public-key
@@ -18,54 +17,48 @@
:lastENSClockValue :last-ens-clock-value
:lastUpdated :last-updated
:localNickname :nickname})
(assoc :mutual?
(and (:added contact)
(:hasAddedUs contact)))))
(assoc :mutual? (and (:added contact)
(:hasAddedUs contact)))))
(rf/defn fetch-contacts-rpc
(fx/defn fetch-contacts-rpc
[_ on-success]
{::json-rpc/call [{:method "wakuext_contacts"
:params []
{::json-rpc/call [{:method "wakuext_contacts"
:params []
:on-success #(on-success (map <-rpc %))
:on-error #(log/error "failed to fetch contacts" %)}]})
:on-error #(log/error "failed to fetch contacts" %)}]})
(rf/defn add
(fx/defn add
[_ public-key nickname ens-name on-success]
{::json-rpc/call [{:method "wakuext_addContact"
:params [{:id public-key :nickname nickname :ensName ens-name}]
{::json-rpc/call [{:method "wakuext_addContact"
:params [{:id public-key :nickname nickname :ensName ens-name}]
:js-response true
:on-success #(do
(log/info "saved contact" public-key "successfuly")
(when on-success
(on-success %)))
:on-error #(log/error "failed to add contact" public-key %)}]})
:on-success #(do
(log/info "saved contact" public-key "successfuly")
(when on-success
(on-success %)))
:on-error #(log/error "failed to add contact" public-key %)}]})
(rf/defn set-nickname
(fx/defn set-nickname
[_ public-key nickname on-success]
{::json-rpc/call [{:method "wakuext_setContactLocalNickname"
:params [{:id public-key :nickname nickname}]
{::json-rpc/call [{:method "wakuext_setContactLocalNickname"
:params [{:id public-key :nickname nickname}]
:js-response true
:on-success #(do
(log/debug "set contact nickname" public-key "successfuly" nickname)
(when on-success
(on-success %)))
:on-error #(log/error "failed to set contact nickname "
public-key
nickname
%)}]})
:on-success #(do
(log/debug "set contact nickname" public-key "successfuly" nickname)
(when on-success
(on-success %)))
:on-error #(log/error "failed to set contact nickname " public-key nickname %)}]})
(rf/defn block
[_ contact-id on-success]
{::json-rpc/call [{:method "wakuext_blockContact"
:params [contact-id]
(fx/defn block [_ contact-id on-success]
{::json-rpc/call [{:method "wakuext_blockContact"
:params [contact-id]
:js-response true
:on-success on-success
:on-error #(log/error "failed to block contact" % contact-id)}]})
:on-success on-success
:on-error #(log/error "failed to block contact" % contact-id)}]})
(rf/defn unblock
[_ contact-id on-success]
{::json-rpc/call [{:method "wakuext_unblockContact"
:params [contact-id]
:on-success on-success
(fx/defn unblock [_ contact-id on-success]
{::json-rpc/call [{:method "wakuext_unblockContact"
:params [contact-id]
:on-success on-success
:js-response true
:on-error #(log/error "failed to unblock contact" % contact-id)}]})
:on-error #(log/error "failed to unblock contact" % contact-id)}]})
+63 -79
View File
@@ -1,118 +1,102 @@
(ns status-im.data-store.messages
(:require [clojure.set :as clojure.set]
[status-im.ethereum.json-rpc :as json-rpc]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
[status-im.utils.fx :as fx]
[taoensso.timbre :as log]))
(defn ->rpc
[{:keys [content] :as message}]
(defn ->rpc [{:keys [content] :as message}]
(cond-> message
content
(assoc :text (:text content)
(assoc :text (:text content)
:sticker (:sticker content))
:always
(clojure.set/rename-keys {:chat-id :chat_id
(clojure.set/rename-keys {:chat-id :chat_id
:whisper-timestamp :whisperTimestamp
:community-id :communityId
:clock-value :clock})))
:community-id :communityId
:clock-value :clock})))
(defn <-rpc
[message]
(defn <-rpc [message]
(-> message
(clojure.set/rename-keys {:id :message-id
:whisperTimestamp :whisper-timestamp
:editedAt :edited-at
(clojure.set/rename-keys {:id :message-id
:whisperTimestamp :whisper-timestamp
:editedAt :edited-at
:contactVerificationState :contact-verification-state
:contactRequestState :contact-request-state
:commandParameters :command-parameters
:gapParameters :gap-parameters
:messageType :message-type
:localChatId :chat-id
:communityId :community-id
:contentType :content-type
:clock :clock-value
:quotedMessage :quoted-message
:outgoingStatus :outgoing-status
:audioDurationMs :audio-duration-ms
:deleted :deleted?
:deletedForMe :deleted-for-me?
:new :new?})
:contactRequestState :contact-request-state
:commandParameters :command-parameters
:gapParameters :gap-parameters
:messageType :message-type
:localChatId :chat-id
:communityId :community-id
:contentType :content-type
:clock :clock-value
:quotedMessage :quoted-message
:outgoingStatus :outgoing-status
:audioDurationMs :audio-duration-ms
:deletedForMe :deleted-for-me?
:new :new?})
(update :quoted-message
clojure.set/rename-keys
{:parsedText :parsed-text :communityId :community-id})
(update :quoted-message clojure.set/rename-keys {:parsedText :parsed-text :communityId :community-id})
(update :outgoing-status keyword)
(update :command-parameters
clojure.set/rename-keys
{:transactionHash :transaction-hash
:commandState :command-state})
(assoc :content {:chat-id (:chatId message)
:text (:text message)
:image (:image message)
:sticker (:sticker message)
:ens-name (:ensName message)
:line-count (:lineCount message)
:parsed-text (:parsedText message)
:links (:links message)
:rtl? (:rtl message)
:response-to (:responseTo message)}
(update :command-parameters clojure.set/rename-keys {:transactionHash :transaction-hash
:commandState :command-state})
(assoc :content {:chat-id (:chatId message)
:text (:text message)
:image (:image message)
:sticker (:sticker message)
:ens-name (:ensName message)
:line-count (:lineCount message)
:parsed-text (:parsedText message)
:links (:links message)
:rtl? (:rtl message)
:response-to (:responseTo message)}
:outgoing (boolean (:outgoingStatus message)))
(dissoc :ensName :chatId :text :rtl :responseTo :image :sticker :lineCount :parsedText :links)))
(defn messages-by-chat-id-rpc
[chat-id
cursor
limit
on-success
on-error]
(defn messages-by-chat-id-rpc [chat-id
cursor
limit
on-success
on-error]
{::json-rpc/call [{:method "wakuext_chatMessages"
:params [chat-id cursor limit]
:on-success (fn [result]
(on-success (update result :messages #(map <-rpc %))))
:on-error on-error}]})
:on-error on-error}]})
(defn mark-seen-rpc
[chat-id ids on-success]
{::json-rpc/call [{:method "wakuext_markMessagesSeen"
:params [chat-id ids]
(defn mark-seen-rpc [chat-id ids on-success]
{::json-rpc/call [{:method "wakuext_markMessagesSeen"
:params [chat-id ids]
:on-success #(do
(log/debug "successfully marked as seen" %)
(when on-success (on-success chat-id ids %)))
:on-error #(log/error "failed to get messages" %)}]})
:on-error #(log/error "failed to get messages" %)}]})
(defn delete-message-rpc
[id]
{::json-rpc/call [{:method "wakuext_deleteMessage"
:params [id]
(defn delete-message-rpc [id]
{::json-rpc/call [{:method "wakuext_deleteMessage"
:params [id]
:on-success #(log/debug "successfully deleted message" id)
:on-error #(log/error "failed to delete message" % id)}]})
:on-error #(log/error "failed to delete message" % id)}]})
(defn delete-messages-from-rpc
[author]
{::json-rpc/call [{:method "wakuext_deleteMessagesFrom"
:params [author]
(defn delete-messages-from-rpc [author]
{::json-rpc/call [{:method "wakuext_deleteMessagesFrom"
:params [author]
:on-success #(log/debug "successfully deleted messages from" author)
:on-error #(log/error "failed to delete messages from" % author)}]})
:on-error #(log/error "failed to delete messages from" % author)}]})
(defn delete-messages-by-chat-id-rpc
[chat-id]
{::json-rpc/call [{:method "wakuext_deleteMessagesByChatID"
:params [chat-id]
(defn delete-messages-by-chat-id-rpc [chat-id]
{::json-rpc/call [{:method "wakuext_deleteMessagesByChatID"
:params [chat-id]
:on-success #(log/debug "successfully deleted messages by chat-id" chat-id)
:on-error #(log/error "failed to delete messages by chat-id" % chat-id)}]})
:on-error #(log/error "failed to delete messages by chat-id" % chat-id)}]})
(rf/defn delete-message
[cofx id]
(fx/defn delete-message [cofx id]
(delete-message-rpc id))
(rf/defn delete-messages-from
[cofx author]
(fx/defn delete-messages-from [cofx author]
(delete-messages-from-rpc author))
(rf/defn mark-messages-seen
[cofx chat-id ids on-success]
(fx/defn mark-messages-seen [cofx chat-id ids on-success]
(mark-seen-rpc chat-id ids on-success))
(rf/defn delete-messages-by-chat-id
[cofx chat-id]
(fx/defn delete-messages-by-chat-id [cofx chat-id]
(delete-messages-by-chat-id-rpc chat-id))

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