Compare commits

..
Author SHA1 Message Date
tumanov-alex 6a40a7e3bb Init 2023-11-02 17:29:22 +01:00
257 changed files with 3779 additions and 3744 deletions
+1 -1
View File
@@ -1,2 +1,2 @@
{:hooks {:analyze-call {utils.i18n/label utils.i18n/label}}
{:hooks {:analyze-call {utils.i18n/label hooks.core/i18n-label}}
:linters {:status-im.linter/invalid-translation-keyword {:level :error}}}
+19
View File
@@ -0,0 +1,19 @@
(ns hooks.core
(:require [clj-kondo.hooks-api :as api]))
(defn i18n-label
"Verify call to `i18n/label` pass the translation keyword qualified with `t`."
[{:keys [node]}]
(let [[_ translation-key-node & _] (:children node)]
(when (and (api/keyword-node? translation-key-node)
(not= "t" (-> translation-key-node api/sexpr namespace)))
(api/reg-finding! (assoc (meta translation-key-node)
:message "Translation keyword should be qualified with \"t\""
:type :status-im.linter/invalid-translation-keyword)))))
(comment
;; Valid
(i18n-label {:node (api/parse-string "(i18n/label :t/foo)")})
;; Invalid
(i18n-label {:node (api/parse-string "(i18n/label :foo)")}))
-26
View File
@@ -1,26 +0,0 @@
(ns utils.i18n
(:require [clj-kondo.hooks-api :as hooks]))
(defn label
"Verify call to `utils.i18n/label` pass the translation keyword qualified with `t`."
[{:keys [node]}]
(let [[_ translation-key-node & _] (:children node)]
(when (and (hooks/keyword-node? translation-key-node)
(not= "t" (-> translation-key-node hooks/sexpr namespace)))
(hooks/reg-finding! (assoc (meta translation-key-node)
:message "Translation keyword should be qualified with \"t\""
:type :status-im.linter/invalid-translation-keyword)))))
(comment
;; Valid
(label {:node (hooks/parse-string "(i18n/label :t/foo {:var \"hello\"})")
:cljc false
:lang :cljs
:filename ""
:config {}
:ns ""
:context nil})
;; Invalid
(label {:node (hooks/parse-string "(i18n/label :foo)")})
)
-4
View File
@@ -190,7 +190,3 @@ test/appium/tests/users.py
## git hooks
lefthook.yml
## clj-kondo
/.clj-kondo/taoensso/*
/.clj-kondo/babashka/*
-1
View File
@@ -7,4 +7,3 @@ modules
result
target
component-spec
/app
+2 -4
View File
@@ -274,11 +274,11 @@ run-re-frisk: ##@run Start re-frisk server
# TODO: Migrate this to a Nix recipe, much the same way as nix/mobile/android/targets/release-android.nix
run-android: export TARGET := android
# INFO: If it's empty (no devices attached, parsing issues, script error) - for Nix it's the same as not set.
run-android: export ANDROID_ABI_INCLUDE ?= $(shell ./scripts/adb_devices_abis.sh)
run-android: export ANDROID_ABI_INCLUDE ?= $(shell ./scripts/adb_devices_arch.sh)
run-android: ##@run Build Android APK and start it on the device
npx react-native run-android --appIdSuffix debug
SIMULATOR=iPhone 13
SIMULATOR=iPhone 11 Pro
run-ios: export TARGET := ios
run-ios: export IOS_STATUS_GO_TARGETS := iossimulator/amd64
run-ios: ##@run Build iOS app and start it in a simulator/device
@@ -312,10 +312,8 @@ lint: export TARGET := clojure
lint: export CLJ_LINTER_PRINT_WARNINGS ?= false
lint: ##@test Run code style checks
@sh scripts/lint-re-frame-in-quo-components.sh && \
sh scripts/lint-direct-require-component-outside-quo.sh && \
clj-kondo --config .clj-kondo/config.edn --cache false --fail-level error --lint src $(if $(filter $(CLJ_LINTER_PRINT_WARNINGS),true),,| grep -v ': warning: ') && \
ALL_CLOJURE_FILES=$(call find_all_clojure_files) && \
scripts/lint_translations.clj && \
zprint '{:search-config? true}' -sfc $$ALL_CLOJURE_FILES && \
sh scripts/lint-trailing-newline.sh && \
node_modules/.bin/prettier --write .
-2
View File
@@ -26,8 +26,6 @@
[Creating a pixel perfect UI](pixel-perfection.md)
[Contributing to status-go](status-go-changes.md)
## Testing
[How to run local tests](testing.md)
+1 -1
View File
@@ -23,7 +23,7 @@ setups and runs the test suite once.
setups and runs the test suite and watches for code changes will then retrigger the test suite.
## Writing Tests
New test files will need their namespace added to either the file "src/quo/core_spec.cljs" or "src/status_im2/core_spec.cljs. These locations may update overtime but it is dependent on the entrypoint in shadow-cljs config discussed below.
New test files will need their namespace added to either the file "src/quo2/core_spec.cljs" or "src/status_im2/core_spec.cljs. These locations may update overtime but it is dependent on the entrypoint in shadow-cljs config discussed below.
### Best practices
+1 -1
View File
@@ -4,7 +4,7 @@
![](images/export-icons/export-icons.gif)
1. Export from figma 2 pngs 2x and 3x put them in `./resources/images/icons2`
1. Export from figma 2 pngs 2x and 3x put them in `./resources/images/icons`
2. if necessary, rename file so that filename contains only lower case chars, e.g. `"Icon-Name@2x.png"` should be renamed to `"icon_name@2x.png"`.
3. In the app `icon_name.png` still can be accessed as `icon-name`, so in order to use can add the next code:
```clojure
+22 -22
View File
@@ -76,7 +76,7 @@ It's important to name functional components with `f-` prefix.
### Component props and API scheme to match Figma as closely as possible
Ideally, the prop names for components (particularly in quo Design System)
Ideally, the prop names for components (particularly in quo2 Design System)
should match the Figma properties as best as possible. This makes it easier for
the developer using that component to configure it correctly for the screen it
is being used on and avoids unnecessary overwrites and adjustments being made.
@@ -134,7 +134,7 @@ For example if Figma has sizes `:small`, `:medium` and `:large`
Prefer to define styles in a separate file named `style.cljs`, colocated with
the source file. For a real example, see
[src/quo/components/record_audio/record_audio/style.cljs](../src/quo/components/record_audio/record_audio/style.cljs).
[src/quo2/components/record_audio/record_audio/style.cljs](../src/quo2/components/record_audio/record_audio/style.cljs).
```clojure
;; bad
@@ -467,9 +467,9 @@ Prefer the pure version of `:json-rpc/call` (no callbacks).
### Registering event handlers
Register events with `utils.re-frame/reg-event-fx` and follow [re-frame's best
Register events with `re-frame.core/reg-event-fx` and follow [re-frame's best
practice](https://github.com/day8/re-frame/blob/39adca93673f334dc751ee2d99d340b51a9cc6db/docs/FAQs/BestPractice.md#use-the-fx-effect)
so use only `:db` and `:fx` effects. `utils.re-frame/merge` and `utils.re-frame/defn` are deprecated and should not be
so use only `:db` and `:fx` effects. `rf/merge` is deprecated and should not be
used in the new code in `src/status_im2/`. Don't use
`re-frame.core/reg-event-db`.
@@ -528,27 +528,27 @@ due to performance constraints.
(:preferred-name multiaccount)))
```
### Requiring quo components
### Requiring quo2 components
Consume `quo` components from `quo.core`, unless the namespace is also inside
the `quo/` directory.
Consume `quo2` components from `quo2.core`, unless the namespace is also inside
the `quo2/` directory.
```clojure
;; bad
(ns my-namespace
(:require [quo.components.icon :as icon]))
(:require [quo2.components.icon :as icon]))
(icon/icon :i/verified)
;; good
(ns my-namespace
(:require [quo.core :as quo]))
(:require [quo2.core :as quo]))
(quo/icon :i/verified)
;; also good because both namespaces are inside quo/
(ns quo.components.tabs.account-selector
(:require [quo.components.markdown.text :as text]))
;; also good because both namespaces are inside quo2/
(ns quo2.components.tabs.account-selector
(:require [quo2.components.markdown.text :as text]))
```
### Require/import
@@ -615,12 +615,12 @@ Use the appropriate keyword qualification/namespace.
```clojure
;; bad
(require '[quo.components.icon :as icons])
(require '[quo2.components.icon :as icons])
(icons/icon :main-icons2/verified)
;; good
(require '[quo.core :as quo])
(quo/icon :i/verified)
(require '[quo2.core :as quo2])
(quo2/icon :i/verified)
```
### Translations
@@ -695,7 +695,7 @@ First, the bird's-eye view with some example ClojureScript files:
src
├── js/
├── mocks/
├── quo
├── quo2
│ ├── components/
│ ├── foundations/
│ └── theme.cljs
@@ -716,7 +716,7 @@ src
- `src/js`: Raw Javascript files, e.g. React Native Reanimated worklets.
- `src/mocks`: Plumbing configuration to be able to run tests.
- `src/quo/`: The component library for Status Mobile.
- `src/quo2/`: The component library for Status Mobile.
- `src/react_native/`: Contains only low-level constructs to help React Native
work in tandem with Clojure(Script).
- `src/status_im2/`: Directory where we try to be as strict as possible about
@@ -728,7 +728,7 @@ src
of the directory tree. Just like directories named `utils`, their directory
nesting level communicates their applicable limits.
- `src/status_im2/common/components/`: Contains reusable components that are not
part of the design system (quo).
part of the design system (quo2).
- `src/status_im2/contexts/`: Contains [bounded contexts](#glossary), like
`browser/`, `messaging/`, etc. As much as possible, _bounded contexts_ should
not directly require each other's namespaces.
@@ -743,9 +743,9 @@ directory nesting level precisely indicates its boundaries. For example, a
`contexts/user_settings/utils/datetime.cljs` file communicates that it should
only be used in the `user_settings` context.
### src/quo
### src/quo2
The `src/quo/` directory holds all components for the new design system. As
The `src/quo2/` directory holds all components for the new design system. As
much as possible, its sub-directories and component names should reflect the
same language used by designers.
@@ -753,14 +753,14 @@ Even though the directory lives alongside the rest of the codebase, we should
think of it as an external entity that abstracts away particular Status domain
knowledge.
Components inside `src/quo/` should not rely on re-frame, i.e. they should not
Components inside `src/quo2/` should not rely on re-frame, i.e. they should not
dispatch events or use subscriptions.
Example structure:
```
src
└── quo
└── quo2
├── components
│ └── dropdown
│ ├── style.cljs
+3 -9
View File
@@ -1,22 +1,16 @@
# Pixel Perfection
The Status Mobile team aims to align the design implementation perfectly according to a given screens respective Figma design spec.
Currently, the Figma Designs match up with both "iPhone 11 Pro" and "iPhone 13" screen size.
The Dimension reference for iPhone 11 Pro is 375 x 812 (width x height) and for iPhone 13 is 390 x 844.
Currently the Figma Designs match up with an "iPhone 11 Pro" screen size.
To test your implementation is correct you can take the following steps
- Open a new Figma file so that you have write privileges.
- Copy in the component or screen from the Figma file.
- Using the "iPhone 11 Pro" or the "iPhone 13" simulator (it is the default now) you take a screenshot of your component and paste it into your Figma file.
- To get the right size, set your screenshot to width according to your simulator:
- for iPhone 13 set 390
- for iPhone 11 Pro set 375
- Using the "iPhone 11 Pro" simulator (it is the default now) you take a screenshot of your component and paste it into your Figma file.
- To get the right size, set your screenshot to width 375px
![](images/pixel-perfection/layer-width.png)
+5 -5
View File
@@ -30,23 +30,23 @@ You need to have an emulator like [AVD](https://developer.android.com/studio/run
#### Running on a simulator
We highly recommend using either the `iPhone 11 Pro` or `iPhone 13` simulator as its screen dimensions match with our design.
We highly recommend using the `iPhone 11 Pro` simulator as its screen dimensions match with our design.
If you have Xcode `v12.x` (and above) installed in your system, you need to follow the below steps to add `iPhone 13` simulator:
If you have Xcode `v12.x` (and above) installed in your system, you need to follow the below steps to add `iPhone 11 Pro` simulator:
1. Open Xcode
2. Menu `>` Window `>` Devices and Simulators
3. Tap `+` button on bottom left
4. Select **Device Type** as `iPhone 13`
4. Select **Device Type** as `iPhone 11 Pro`
5. Leave the **Simulator Name** empty and tap on **Create**
##### NOTE ⚠️
Running `make run-ios` will target `iPhone 13` by default.
Running `make run-ios` will target `iPhone 11 Pro` by default.
If you need to run on any other simulator, you can specify the simulator type by adding the `SIMULATOR` flag:
```sh
make run-ios SIMULATOR="iPhone 13"
make run-ios SIMULATOR="iPhone 11 Pro"
```
#### Running on a physical device
-31
View File
@@ -1,31 +0,0 @@
# How to make changes to status-go
## Overview
To make changes to status-go, the best approach is to first identify the issue/feature and the changes to be made directly in status-go.
For any bugs we want to most likely write a failing test in status-go, and make sure that it passes, before we test on mobile.
Once you are quite confident that the status-go code is ready to be tested on mobile, you can test it locally by running the following command:
```
env STATUS_GO_SRC_OVERRIDE={your-status-go-directory} make run-{android/ios}
```
This will recompile status-go and run status-mobile on your device with that updated status-go version.
Once you have tested the status-go changes locally and you think it's ready for a PR:
1) Create a PR in status-go
2) Point your local status-mobile branch to that PR by running the script:
```
scripts/update-status-go.sh {status-go-branch-name}
```
This will update `status-go-version.json`
Commit updated status-go-version.json and create a status-mobile PR
Once both PRs have been approved and are good to merge, you can follow the [merge guidelines](./merging-pr-process.md)
+1 -1
View File
@@ -765,7 +765,7 @@ SPEC CHECKSUMS:
RNLanguages: 962e562af0d34ab1958d89bcfdb64fafc37c513e
RNPermissions: ad71dd4f767ec254f2cd57592fbee02afee75467
RNReactNativeHapticFeedback: 2566b468cc8d0e7bb2f84b23adc0f4614594d071
RNReanimated: 42f56dc5c032a11177b9ea12cdb57285318b432e
RNReanimated: 62e43ee6baafb9ba3d3af1857d7fd23a1d41bff0
RNShare: d82e10f6b7677f4b0048c23709bd04098d5aee6c
RNStaticSafeAreaInsets: 055ddbf5e476321720457cdaeec0ff2ba40ec1b8
RNSVG: 80584470ff1ffc7994923ea135a3e5ad825546b9
@@ -1194,6 +1194,21 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
return getPublicStorageDirectory().getAbsolutePath();
}
@ReactMethod(isBlockingSynchronousMethod = true)
public String generateAlias(final String seed) {
return Statusgo.generateAlias(seed);
}
@ReactMethod
public void generateAliasAsync(final String seed, final Callback callback) throws JSONException {
executeRunnableStatusGoMethod(() -> Statusgo.generateAlias(seed), callback);
}
@ReactMethod(isBlockingSynchronousMethod = true)
public String identicon(final String seed) {
return Statusgo.identicon(seed);
}
@ReactMethod(isBlockingSynchronousMethod = true)
public String encodeTransfer(final String to, final String value) {
return Statusgo.encodeTransfer(to, value);
@@ -1254,6 +1269,31 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
executeRunnableStatusGoMethod(() -> Statusgo.identicon(seed), callback);
}
@ReactMethod
public void generateAliasAndIdenticonAsync(final String seed, final Callback callback) {
Log.d(TAG, "generateAliasAndIdenticonAsync");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String resIdenticon = Statusgo.identicon(seed);
String resAlias = Statusgo.generateAlias(seed);
Log.d(TAG, resIdenticon);
Log.d(TAG, resAlias);
callback.invoke(resAlias, resIdenticon);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@Override
public @Nullable
Map<String, Object> getConstants() {
@@ -880,6 +880,10 @@ RCT_EXPORT_METHOD(callRPC:(NSString *)payload
});
}
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(generateAlias:(NSString *)publicKey) {
return StatusgoGenerateAlias(publicKey);
}
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(keystoreDir) {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *rootUrl =[[fileManager
@@ -939,6 +943,19 @@ RCT_EXPORT_METHOD(initLogging:(BOOL)enabled
callback(@[initResult]);
}
RCT_EXPORT_METHOD(generateAliasAsync:(NSString *)publicKey
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
NSLog(@"generateAliasAsync() method called");
#endif
NSString *result = StatusgoGenerateAlias(publicKey);
callback(@[result]);
}
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(identicon:(NSString *)publicKey) {
return StatusgoIdenticon(publicKey);
}
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(encodeTransfer:(NSString *)to
value:(NSString *)value) {
return StatusgoEncodeTransfer(to,value);
@@ -994,6 +1011,15 @@ RCT_EXPORT_METHOD(validateMnemonic:(NSString *)seed
callback(@[result]);
}
RCT_EXPORT_METHOD(identiconAsync:(NSString *)publicKey
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
NSLog(@"identiconAsync() method called");
#endif
NSString *result = StatusgoIdenticon(publicKey);
callback(@[result]);
}
RCT_EXPORT_METHOD(createAccountAndLogin:(NSString *)request) {
#if DEBUG
NSLog(@"createAccountAndLogin() method called");
@@ -1008,6 +1034,16 @@ RCT_EXPORT_METHOD(restoreAccountAndLogin:(NSString *)request) {
StatusgoRestoreAccountAndLogin(request);
}
RCT_EXPORT_METHOD(generateAliasAndIdenticonAsync:(NSString *)publicKey
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
NSLog(@"generateAliasAndIdenticonAsync() method called");
#endif
NSString *identiconResult = StatusgoIdenticon(publicKey);
NSString *aliasResult = StatusgoGenerateAlias(publicKey);
callback(@[aliasResult, identiconResult]);
}
RCT_EXPORT_METHOD(callPrivateRPC:(NSString *)payload
callback:(RCTResponseSenderBlock)callback) {
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@@ -354,6 +354,38 @@ void _StopCPUProfiling(const FunctionCallbackInfo<Value>& args) {
}
void _Identicon(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
if (args.Length() != 1) {
// Throw an Error that is passed back to JavaScript
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8Literal(isolate, "Wrong number of arguments for Identicon")));
return;
}
// Check the argument types
if (!args[0]->IsString()) {
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8Literal(isolate, "Wrong argument type for 'pk'")));
return;
}
String::Utf8Value arg0Obj(isolate, args[0]->ToString(context).ToLocalChecked());
char *arg0 = *arg0Obj;
// Call exported Go function, which returns a C string
char *c = Identicon(arg0);
Local<String> ret = String::NewFromUtf8(isolate, c).ToLocalChecked();
args.GetReturnValue().Set(ret);
delete c;
}
void _EncodeTransfer(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
@@ -832,6 +864,38 @@ void _CreateAccountAndLogin(const FunctionCallbackInfo<Value>& args) {
}
void _GenerateAlias(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
if (args.Length() != 1) {
// Throw an Error that is passed back to JavaScript
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8Literal(isolate, "Wrong number of arguments for GenerateAlias")));
return;
}
// Check the argument types
if (!args[0]->IsString()) {
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8Literal(isolate, "Wrong argument type for 'pk'")));
return;
}
String::Utf8Value arg0Obj(isolate, args[0]->ToString(context).ToLocalChecked());
char *arg0 = *arg0Obj;
// Call exported Go function, which returns a C string
char *c = GenerateAlias(arg0);
Local<String> ret = String::NewFromUtf8(isolate, c).ToLocalChecked();
args.GetReturnValue().Set(ret);
delete c;
}
void _ValidateMnemonic(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
@@ -1889,6 +1953,7 @@ void init(Local<Object> exports) {
NODE_SET_METHOD(exports, "multiAccountStoreAccount", _MultiAccountStoreAccount);
NODE_SET_METHOD(exports, "initKeystore", _InitKeystore);
NODE_SET_METHOD(exports, "stopCPUProfiling", _StopCPUProfiling);
NODE_SET_METHOD(exports, "identicon", _Identicon);
NODE_SET_METHOD(exports, "encodeTransfer", _EncodeTransfer);
NODE_SET_METHOD(exports, "encodeFunctionCall", _EncodeFunctionCall);
NODE_SET_METHOD(exports, "decodeParameters", _DecodeParameters);
@@ -1903,6 +1968,7 @@ void init(Local<Object> exports) {
NODE_SET_METHOD(exports, "resetChainData", _ResetChainData);
NODE_SET_METHOD(exports, "saveAccountAndLogin", _SaveAccountAndLogin);
NODE_SET_METHOD(exports, "createAccountAndLogin", _CreateAccountAndLogin);
NODE_SET_METHOD(exports, "generateAlias", _GenerateAlias);
NODE_SET_METHOD(exports, "validateMnemonic", _ValidateMnemonic);
NODE_SET_METHOD(exports, "multiformatSerializePublicKey", _MultiformatSerializePublicKey);
NODE_SET_METHOD(exports, "saveAccountAndLoginWithKeycard", _SaveAccountAndLoginWithKeycard);
+1 -1
View File
@@ -21,7 +21,7 @@ let
buildInputs = with pkgs; [
clojure flock maven openjdk
# lint specific utilities
babashka clj-kondo clojure-lsp ripgrep zprint
clj-kondo zprint clojure-lsp
];
# CLASSPATH from clojure deps with 'src' appended to find local sources.
shellHook = with pkgs; ''
+1 -2
View File
@@ -12,7 +12,7 @@
"@react-native-async-storage/async-storage": "^1.17.9",
"@react-native-camera-roll/camera-roll": "git+https://github.com/status-im/react-native-camera-roll.git#refs/tags/v5.1.1.1",
"@react-native-community/audio-toolkit": "git+https://github.com/tbenr/react-native-audio-toolkit.git#refs/tags/v2.0.3-status-v6",
"@react-native-community/blur": "git+https://github.com/status-im/react-native-blur.git#refs/tags/v4.3.3-status",
"@react-native-community/blur": "git+https://github.com/status-im/react-native-blur#refs/tags/v4.3.3-status",
"@react-native-community/clipboard": "^1.2.2",
"@react-native-community/hooks": "^3.0.0",
"@react-native-community/masked-view": "^0.1.6",
@@ -47,7 +47,6 @@
"react-native-image-crop-picker": "git+https://github.com/status-im/react-native-image-crop-picker.git#refs/tags/v0.36.2-status.0",
"react-native-image-resizer": "^1.2.3",
"react-native-image-viewing": "git+https://github.com/status-im/react-native-image-viewing.git#refs/tags/v0.2.1.status",
"react-native-intersection-observer": "^0.2.0",
"react-native-keychain": "git+https://github.com/status-im/react-native-keychain.git#refs/tags/v.3.0.0-5-status",
"react-native-languages": "^3.0.2",
"react-native-linear-gradient": "^2.8.0",
@@ -1,14 +0,0 @@
#!/usr/bin/env sh
# quo components namespace ends with style|utils|types|reaction-resource
# are not the component view, they are usually utils fns or styles
if rg --pcre2 --glob '!src/quo/**/*' 'quo\.components(\.[\w-]+)*\.[\w-]*(?<!style|utils|types|reaction-resource)(\s|\]|\n)' src; then
echo "Found above direct require of quo component outside src/quo/"
echo "For more info, check the style guide https://github.com/status-im/status-mobile/blob/develop/doc/new-guidelines.md#requiring-quo-components"
exit 1
elif [ $? -eq 1 ]; then
exit 0
else
exit $?
fi
-98
View File
@@ -1,98 +0,0 @@
#!/usr/bin/env bb
(ns lint-translations
(:require [babashka.pods :as pods]))
(pods/load-pod 'clj-kondo/clj-kondo "2023.09.07")
(require '[pod.borkdude.clj-kondo :as kondo])
(require '[cheshire.core :as json])
(require '[clojure.set :as set])
(def src-paths ["src"])
(def translation-file "translations/en.json")
;; set the following to true when solving https://github.com/status-im/status-mobile/issues/17811
(def flag-show-non-namespaced-translation-keys false)
(def flag-show-non-namespaced-translation-keys-occurrences false) ;; this makes the output super verbose!
;; set the following to true when solving https://github.com/status-im/status-mobile/issues/17813
;; and keep it permanently on after #17811 and #17813 have both been solved
(def flag-show-unused-translation-keys false)
(def flag-show-missing-translation-keys true)
(defn- safe-name
[x]
(when x (name x)))
(defn- ->keyword
[analysis-keyword]
(keyword (safe-name (:ns analysis-keyword)) (:name analysis-keyword)))
(defn- report-issues
[incorrect-usages]
(doseq [incorrect-usage incorrect-usages]
(->> incorrect-usage
((juxt :filename :row :reason ->keyword))
(apply format "%s:%s %s %s")
println)))
(defn- extract-translation-keys
[file]
(-> file slurp json/parse-string keys))
(def ^:private probably-unused-warning
(format "Probably unused translation key in %s:" translation-file))
(defn -main
[& _args]
(println "Linting translations...")
(let [result (kondo/run!
{:lint src-paths
:config {:output {:analysis {:keywords true}}}})
all-keywords (get-in result [:analysis :keywords])
used-translations (filter (comp (partial = 't) :ns) all-keywords)
file-translation-keys (apply sorted-set (extract-translation-keys translation-file))
missing-translations (remove (comp file-translation-keys :name) used-translations)
used-translation-keys (set (map :name used-translations))
possibly-unused-translation-keys (set/difference file-translation-keys used-translation-keys)
non-namespaced-translations (filter
(fn [kw]
(and (not (:ns kw))
(possibly-unused-translation-keys (:name kw))))
all-keywords)
unused-translation-keys (set/difference possibly-unused-translation-keys
(set (map :name non-namespaced-translations)))]
;; TODO (2023-11-06 akatov): delete the following once #17811 and #17813 have both been solved
(doseq [k (apply sorted-set (map :name non-namespaced-translations))]
(when flag-show-non-namespaced-translation-keys
(println "Probably non-namespaced key" k))
(when flag-show-non-namespaced-translation-keys-occurrences
(->> non-namespaced-translations
(filter #(= k (:name %)))
(map #(assoc % :reason "Possibly non-namespaced translation key"))
report-issues)))
(when flag-show-unused-translation-keys
(run! #(println probably-unused-warning %) unused-translation-keys))
(when flag-show-missing-translation-keys
(report-issues (map #(assoc % :reason "Undefined Translation Key") missing-translations)))
(if (and
(or (not flag-show-missing-translation-keys)
(empty? missing-translations))
(or (not flag-show-unused-translation-keys)
(empty? possibly-unused-translation-keys))
(or (not flag-show-non-namespaced-translation-keys)
(not flag-show-non-namespaced-translation-keys-occurrences)
(empty? unused-translation-keys)))
0
1)))
(when (= *file* (System/getProperty "babashka.file"))
(->> *command-line-args*
(apply -main)
System/exit))
-6
View File
@@ -300,11 +300,6 @@
{:clamp nil
:withPause (fn [])})
(def react-native-intersection-observer
#js
{:InView #js {}
:IOFlatList #js {}})
(def react-native-languages
(clj->js {:default {:language "en"
:addEventListener (fn [])
@@ -431,7 +426,6 @@
"react-native-transparent-video" react-native-transparent-video
"react-native-orientation-locker" react-native-orientation-locker
"react-native-gifted-charts" react-native-gifted-charts
"react-native-intersection-observer" react-native-intersection-observer
"../resources/data/emojis/en.json" (js/JSON.parse (slurp
"./resources/data/emojis/en.json"))
"../src/js/worklets/core.js" worklet-factory
+21 -1
View File
@@ -4,7 +4,8 @@
[clojure.string :as string]
[react-native.platform :as platform]
[taoensso.timbre :as log]
[utils.transforms :as types]))
[utils.transforms :as types]
[utils.validators :as validators]))
(defn status
[]
@@ -430,6 +431,19 @@
;; in unknown scenarios we also consider the device rooted to avoid degrading security
:else (callback true)))
(defn generate-gfycat
"Generate a 3 words random name based on the user public-key, synchronously"
[public-key]
(log/debug "[native-module] generate-gfycat")
(when (validators/valid-public-key? public-key)
(.generateAlias ^js (status) public-key)))
(defn identicon
"Generate a icon based on a string, synchronously"
[seed]
(log/debug "[native-module] identicon")
(.identicon ^js (status) seed))
(defn encode-transfer
[to-norm amount-hex]
(log/debug "[native-module] encode-transfer")
@@ -487,6 +501,12 @@
(log/debug "[native-module] to-checksum-address")
(.toChecksumAddress ^js (status) address))
(defn gfycat-identicon-async
"Generate an icon based on a string and 3 words random name asynchronously"
[seed callback]
(log/debug "[native-module] gfycat-identicon-async")
(.generateAliasAndIdenticonAsync ^js (status) seed callback))
(defn validate-mnemonic
"Validate that a mnemonic conforms to BIP39 dictionary/checksum standards"
[mnemonic callback]
@@ -2,7 +2,7 @@
(:require
[oops.core :as oops]
[quo.components.animated-header-flatlist.style :as style]
[quo.components.icon :as icon]
[quo.core :as quo]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]
@@ -73,11 +73,11 @@
{:active-opacity 1
:on-press back-button-on-press
:style (style/button-container {:left 20 :theme theme})}
[icon/icon :i/arrow-left {:size 20 :color (colors/theme-colors colors/black colors/white theme)}]]
[quo/icon :i/arrow-left {:size 20 :color (colors/theme-colors colors/black colors/white theme)}]]
[rn/touchable-opacity
{:active-opacity 1
:style (style/button-container {:right 20 :theme theme})}
[icon/icon :i/options {:size 20 :color (colors/theme-colors colors/black colors/white theme)}]]
[quo/icon :i/options {:size 20 :color (colors/theme-colors colors/black colors/white theme)}]]
[reanimated/blur-view
{:blurAmount 32
:blurType :light
@@ -0,0 +1,68 @@
(ns quo.components.avatars.wallet-user-avatar
(:require
[clojure.string :as string]
[quo.components.markdown.text :as text]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]))
(def circle-sizes
{:small 20
:medium 32
:large 48
:size-64 64
:x-large 80})
(def font-sizes
{:small :label
:medium :paragraph-2
:large :paragraph-1
:size-64 :heading-1
:x-large :heading-1})
(def font-weights
{:small :medium
:medium :semi-bold
:large :semi-bold
:size-64 :medium
:x-large :medium})
(defn- view-internal
"params, first name, last name, customization-color, size
and if it's dark or not!"
[{:keys [f-name l-name customization-color size theme monospace? uppercase?]
:or {f-name "John"
l-name "Doe"
size :x-large
uppercase? true}}]
(let [circle-size (size circle-sizes)
small? (= size :small)
f-name-initial (-> f-name
(#(if uppercase? (string/upper-case %) %))
(subs 0 1))
l-name-initial (-> l-name
(#(if uppercase? (string/upper-case %) %))
(subs 0 1))
circle-color (if customization-color
(colors/resolve-color customization-color theme 20)
(colors/theme-colors colors/neutral-80-opa-5 colors/white-opa-5 theme))
text-color (if customization-color
(colors/resolve-color customization-color theme)
(colors/theme-colors colors/neutral-80-opa-70 colors/white-opa-70 theme))]
[rn/view
{:style {:width circle-size
:height circle-size
:border-radius circle-size
:text-align :center
:justify-content :center
:align-items :center
:background-color circle-color}}
[text/text
{:size (size font-sizes)
:weight (if monospace? :monospace (size font-weights))
:style {:color text-color}}
(if small?
(str f-name-initial)
(str f-name-initial l-name-initial))]]))
(def wallet-user-avatar (quo.theme/with-theme view-internal))
@@ -1,16 +0,0 @@
(ns quo.components.avatars.wallet-user-avatar.component-spec
(:require [quo.components.avatars.wallet-user-avatar.view :as wallet-user-avatar]
[test-helpers.component :as h]))
(h/describe "wallet user avatar"
(h/describe "View internal"
(h/test "Renders by default even with no input parameters"
(h/render
[wallet-user-avatar/wallet-user-avatar {}])
(h/is-truthy (h/query-by-label-text :wallet-user-avatar)))
(h/test "Renders users initials when full name is provided"
(h/render
[wallet-user-avatar/wallet-user-avatar {:full-name "Jane Smith"}])
(h/is-truthy (h/get-by-text "JS")))))
@@ -1,27 +0,0 @@
(ns quo.components.avatars.wallet-user-avatar.style
(:require [quo.foundations.colors :as colors]))
(defn- circle-color
[customization-color]
(colors/custom-color customization-color 50 20))
(defn- text-color
[customization-color theme]
(colors/theme-colors
(colors/custom-color customization-color 50)
(colors/custom-color customization-color 60)
theme))
(defn container
[circle-size customization-color]
{:width circle-size
:height circle-size
:border-radius circle-size
:text-align :center
:justify-content :center
:align-items :center
:background-color (circle-color customization-color)})
(defn text
[customization-color theme]
{:color (text-color customization-color theme)})
@@ -1,57 +0,0 @@
(ns quo.components.avatars.wallet-user-avatar.view
(:require [quo.components.avatars.wallet-user-avatar.style :as style]
[quo.components.markdown.text :as text]
[quo.theme :as quo.theme]
[react-native.core :as rn]
utils.string))
(def properties
{:size-20 {:size 20
:font-size :label
:font-weight :medium}
:size-24 {:size 24
:font-size :label
:font-weight :semi-bold}
:size-32 {:size 32
:font-size :paragraph-2
:font-weight :semi-bold}
:size-48 {:size 48
:font-size :paragraph-1
:font-weight :semi-bold}
:size-64 {:size 64
:font-size :paragraph-1
:font-weight :medium}
:size-80 {:size 80
:font-size :heading-1
:font-weight :medium}})
(def smallest-possible (first (keys properties)))
(def second-smallest-possible (second (keys properties)))
(defn check-if-size-small
[size]
(or (= size smallest-possible)
(= size second-smallest-possible)))
(def biggest-possible (last (keys properties)))
(defn- view-internal
"Options:
:full-name - string (default: nil) - used to generate initials
:customization-color - keyword (default: nil) - color of the avatar
:size - keyword (default: last element of properties object) - size of the
avatar
:monospace? - boolean (default: false) - use monospace font"
[{:keys [full-name customization-color size theme monospace?]
:or {size biggest-possible}}]
(let [circle-size (:size (size properties))
small? (check-if-size-small size)]
[rn/view
{:style (style/container circle-size customization-color)}
[text/text
{:accessibility-label :wallet-user-avatar
:size (:font-size (size properties))
:weight (if monospace? :monospace (:font-weight (size properties)))
:style (style/text customization-color theme)}
(utils.string/get-initials full-name (if small? 1 2))]]))
(def wallet-user-avatar (quo.theme/with-theme view-internal))
@@ -1,18 +0,0 @@
(ns quo.components.community.community-stat.component-spec
(:require
[quo.core :as quo]
[test-helpers.component :as h]
utils.money))
(h/describe "Community Stat Component"
(h/test "default render"
(h/render [quo/community-stat
{:value "5000"
:icon :i/active-members
:accessibility-label :community-active-members}])
(h/is-truthy (h/query-by-label-text :community-active-members)))
(h/test "renders community stat with a value"
(h/render [quo/community-stat
{:value "5000"
:icon :i/members}])
(h/is-truthy (h/query-by-text (utils.money/format-amount "5000")))))
@@ -1,12 +0,0 @@
(ns quo.components.community.community-stat.style
(:require [quo.foundations.colors :as colors]))
(def container
{:flex-direction :row
:align-items :center
:height 22})
(defn text
[theme]
{:color (colors/theme-colors colors/black colors/white theme)
:margin-left 2})
@@ -1,26 +0,0 @@
(ns quo.components.community.community-stat.view
(:require [quo.components.community.community-stat.style :as style]
[quo.components.icon :as quo.icons]
[quo.components.markdown.text :as quo.text]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]
utils.money))
(defn view-internal
[{:keys [value icon theme style accessibility-label]}]
[rn/view
{:style (merge style/container style)
:accessibility-label accessibility-label}
[quo.icons/icon icon
{:size 16
:container-style {:align-items :center
:justify-content :center}
:resize-mode :center
:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)}]
[quo.text/text
{:size :paragraph-1
:weight :regular
:style (style/text theme)} (utils.money/format-amount value)]])
(def view (quo.theme/with-theme view-internal))
@@ -1,30 +1,52 @@
(ns quo.components.community.community-view
(:require
[quo.components.community.community-stat.view :as community-stat]
[quo.components.community.style :as style]
[quo.components.icon :as icons]
[quo.components.markdown.text :as text]
[quo.components.tags.permission-tag :as permission]
[quo.components.tags.tag :as tag]
[quo.foundations.colors :as colors]
[quo.theme :as theme]
[react-native.core :as rn]
[react-native.gesture :as gesture]))
[react-native.gesture :as gesture]
utils.money))
(defn community-stats
[{:keys [icon members-count icon-color accessibility-label]}]
[rn/view
{:accessibility-label accessibility-label
:style (style/stats-count-container)}
[rn/view {:margin-right 2}
[icons/icon icon
{:container-style {:align-items :center
:justify-content :center}
:resize-mode :center
:size 16
:color icon-color}]]
[text/text
{:weight :regular
:size :paragraph-2}
members-count]])
(defn community-stats-column
[{:keys [type members-count active-count]}]
[rn/view
(if (= type :card-view)
(style/card-stats-container)
(style/list-stats-container))
[community-stat/view
{:accessibility-label :stats-members-count
:icon :i/group
:value members-count
:style {:margin-right 12}}]
[community-stat/view
{:accessibility-label :stats-active-count
:icon :i/active-members
:value active-count}]])
[{:keys [type theme blur? members-count active-count]}]
(let [icon-color (if (and (= :dark theme) blur?)
colors/white-opa-40
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme))]
[rn/view
(if (= type :card-view)
(style/card-stats-container)
(style/list-stats-container))
[community-stats
{:accessibility-label :stats-members-count
:icon :i/group
:members-count (utils.money/format-amount members-count)
:icon-color icon-color}]
[community-stats
{:accessibility-label :stats-active-count
:icon :i/active-members
:members-count (utils.money/format-amount active-count)
:icon-color icon-color}]]))
(defn community-tags
[{:keys [tags container-style last-item-style]}]
+6
View File
@@ -17,6 +17,12 @@
(def detail-container
{:flex 1})
(defn stats-count-container
[]
{:flex-direction :row
:align-items :center
:margin-right 16})
(defn card-stats-container
[]
{:flex-direction :row})
@@ -7,9 +7,8 @@
[react-native.core :as rn]))
(def default-props
{:button-one-type :primary
:button-two-type :grey
:customization-color :blue})
{:button-one-type :primary
:button-two-type :grey})
(defn- view-internal
"Options:
@@ -24,7 +23,7 @@
:scroll? - bool (default false) - Whether the iOS Home Indicator should be rendered"
[props]
(let [{:keys [actions description button-one-label button-two-label
button-one-props button-two-props theme scroll? customization-color]}
button-one-props button-two-props theme scroll?]}
(merge default-props props)]
[:<>
[rn/view {:style style/buttons-container}
@@ -44,8 +43,7 @@
:container-style style/button-container
:background (when scroll? :blur)
:theme theme
:accessibility-label :button-one
:customization-color customization-color}
:accessibility-label :button-one}
button-one-props)
button-one-label]]
(when description
@@ -0,0 +1,20 @@
(ns quo.components.dropdowns.dropdown-input.view
(:require
[quo.components.dropdowns.dropdown.properties :as properties]
[quo.components.dropdowns.dropdown.style :as style]
[quo.components.icon :as icon]
[quo.components.markdown.text :as text]
[quo.foundations.customization-colors :as customization-colors]
[quo.theme :as theme]
[react-native.blur :as blur]
[react-native.core :as rn]))
(defn view-internal
[]
[rn/pressable
{:on-press nil}])
(def view
"Props:
- :on-press - function to call when pressed"
(theme/with-theme view-internal))
@@ -65,23 +65,22 @@
ens-regex address-regex
valid-ens-or-address?]}]
(let [on-change (fn [text]
(when (not= @value text)
(let [ens? (when ens-regex
(boolean (re-matches ens-regex text)))
address? (when address-regex
(boolean (re-matches address-regex text)))]
(if (> (count text) 0)
(reset! status :typing)
(reset! status :active))
(reset! value text)
(when on-change-text
(on-change-text text))
(when (and ens? on-detect-ens)
(reset! status :loading)
(on-detect-ens text))
(when (and address? on-detect-address)
(reset! status :loading)
(on-detect-address text)))))
(let [ens? (when ens-regex
(boolean (re-matches ens-regex text)))
address? (when address-regex
(boolean (re-matches address-regex text)))]
(if (> (count text) 0)
(reset! status :typing)
(reset! status :active))
(reset! value text)
(when on-change-text
(on-change-text text))
(when (and ens? on-detect-ens)
(reset! status :loading)
(on-detect-ens text))
(when (and address? on-detect-address)
(reset! status :loading)
(on-detect-address text))))
on-paste (fn []
(clipboard/get-string
(fn [clipboard]
@@ -91,8 +90,6 @@
on-clear (fn []
(reset! value "")
(reset! status (if @focused? :active :default))
(when on-change-text
(on-change-text ""))
(when on-clear
(on-clear)))
on-scan #(when on-scan
@@ -122,7 +119,6 @@
:auto-complete (when platform/ios? :none)
:auto-capitalize :none
:auto-correct false
:spell-check false
:keyboard-appearance (quo.theme/theme-value :light :dark theme)
:on-focus on-focus
:on-blur on-blur
@@ -23,8 +23,6 @@
return-key-type
size
theme
on-focus
on-blur
container-style]
:or {max-length 0
auto-focus false
@@ -50,14 +48,8 @@
:accessibility-label :profile-title-input
:keyboard-appearance (quo.theme/theme-value :light :dark theme)
:return-key-type return-key-type
:on-focus (fn []
(when (fn? on-focus)
(on-focus))
(reset! focused? true))
:on-blur (fn []
(when (fn? on-blur)
(on-blur))
(reset! focused? false))
:on-focus #(swap! focused? (constantly true))
:on-blur #(swap! focused? (constantly false))
:auto-focus auto-focus
:input-mode :text
:on-change-text on-change
@@ -31,5 +31,4 @@
:align-items :center})
(def account-container
{:margin-left 8
:padding-right 56})
{:margin-left 8})
@@ -1,13 +1,13 @@
(ns quo.components.list-items.address.view
(:require
[quo.components.avatars.wallet-user-avatar.view :as wallet-user-avatar]
[clojure.string :as string]
[quo.components.avatars.wallet-user-avatar :as wallet-user-avatar]
[quo.components.list-items.address.style :as style]
[quo.components.markdown.text :as text]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[reagent.core :as reagent]
[utils.address :as address]))
[reagent.core :as reagent]))
(defn- left-container
[{:keys [theme address networks blur?]}]
@@ -25,8 +25,7 @@
[text/text
{:size :paragraph-1
:weight :semi-bold
:style {:color (colors/resolve-color network theme)}}
(str (subs (name network) 0 3) ":")])
:style {:color (colors/resolve-color network)}} (str (subs (name network) 0 3) ":")])
networks)
[text/text
{:size :paragraph-1
@@ -34,7 +33,7 @@
:style {:color (if blur?
colors/white
(colors/theme-colors colors/neutral-100 colors/white theme))}}
(address/get-shortened-key address)]]]])
(string/replace address "x" "×")]]]])
(defn- internal-view
[]
@@ -61,7 +60,6 @@
[left-container
{:theme theme
:networks networks
:address address
:blur? blur?}]]))))
:address address}]]))))
(def view (quo.theme/with-theme internal-view))
@@ -148,6 +148,8 @@
(when (and members (= type :discover))
[community-view/community-stats-column
{:type :list-view
:theme theme
:blur? blur?
:members-count (:members-count members)
:active-count (:active-count members)}])]
[info-component
@@ -13,7 +13,7 @@
(h/render [saved-address/view])
(h/fire-event :on-press-in (h/get-by-label-text :container))
(h/wait-for #(h/has-style (h/query-by-label-text :container)
{:backgroundColor (colors/resolve-color :blue :light 5)})))
{:backgroundColor (colors/custom-color :blue 50 5)})))
(h/test "on-press-in changes state to :pressed with blur? enabled"
(h/render [saved-address/view {:blur? true}])
@@ -22,25 +22,24 @@
{:backgroundColor colors/white-opa-5})))
(h/test "on-press-out changes state to :active"
(h/render [saved-address/view {:active-state? true}])
(h/render [saved-address/view])
(h/fire-event :on-press-in (h/get-by-label-text :container))
(h/fire-event :on-press-out (h/get-by-label-text :container))
(h/wait-for #(h/has-style (h/query-by-label-text :container)
{:backgroundColor (colors/resolve-color :blue :light 10)})))
{:backgroundColor (colors/custom-color :blue 50 10)})))
(h/test "on-press-out changes state to :active with blur? enabled"
(h/render [saved-address/view
{:blur? true
:active-state? true}])
(h/render [saved-address/view {:blur? true}])
(h/fire-event :on-press-in (h/get-by-label-text :container))
(h/fire-event :on-press-out (h/get-by-label-text :container))
(h/wait-for #(h/has-style (h/query-by-label-text :container)
{:backgroundColor colors/white-opa-10})))
(h/test "on-press container calls on-press"
(h/test "on-press-out calls on-press"
(let [on-press (h/mock-fn)]
(h/render [saved-address/view {:on-press on-press}])
(h/fire-event :on-press (h/get-by-label-text :container))
(h/fire-event :on-press-in (h/get-by-label-text :container))
(h/fire-event :on-press-out (h/get-by-label-text :container))
(h/was-called on-press)))
(h/test "renders options button if type :action"
@@ -1,27 +1,26 @@
(ns quo.components.list-items.saved-address.view
(:require
[clojure.string :as string]
[quo.components.avatars.wallet-user-avatar.view :as wallet-user-avatar]
[quo.components.avatars.wallet-user-avatar :as wallet-user-avatar]
[quo.components.icon :as icon]
[quo.components.list-items.saved-address.style :as style]
[quo.components.markdown.text :as text]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[reagent.core :as reagent]
[utils.address :as address]))
[reagent.core :as reagent]))
(defn- left-container
[{:keys [blur? theme name ens address customization-color]}]
[{:keys [blur? theme name address customization-color]}]
(let [names (remove string/blank? (string/split name " "))
first-name (if (> (count names) 0) (first names) "")
last-name (if (> (count names) 1) (last names) "")]
[rn/view {:style style/left-container}
[wallet-user-avatar/wallet-user-avatar
{:size :medium
:f-name first-name
:l-name last-name
:customization-color customization-color}]
{:size :medium
:f-name first-name
:l-name last-name
:color customization-color}]
[rn/view {:style style/account-container}
[text/text
{:weight :semi-bold
@@ -33,7 +32,7 @@
{:size :paragraph-2
:weight :monospace
:style (style/account-address blur? theme)}
(or ens (address/get-shortened-key address))]]]]))
address]]]]))
(defn- internal-view
[]
@@ -43,39 +42,39 @@
on-press-in (fn []
(when-not (= @state :selected)
(reset! timer (js/setTimeout #(reset! state :pressed) 100))))]
(fn [{:keys [blur? user-props active-state? customization-color
(fn [{:keys [blur? user-props customization-color
on-press
on-options-press
theme]
:or {customization-color :blue
blur? false}}]
(let [on-press-out (fn []
(let [new-state (if (or (not active-state?) @active?) :default :active)]
(let [new-state (if @active? :default :active)]
(when @timer (js/clearTimeout @timer))
(reset! timer nil)
(reset! active? (= new-state :active))
(reset! state new-state)))]
(reset! state new-state)
(when on-press
(on-press))))]
[rn/pressable
{:style (style/container
{:state @state :blur? blur? :customization-color customization-color})
:on-press-in on-press-in
:on-press-out on-press-out
:on-press on-press
:accessibility-label :container}
[left-container
{:blur? blur?
:theme theme
:name (:name user-props)
:ens (:ens user-props)
:address (:address user-props)
:customization-color (or (:customization-color user-props) :blue)}]
(when on-options-press
[rn/pressable
{:accessibility-label :options-button
:on-press on-options-press}
[icon/icon :i/options
{:color (if blur?
colors/white-opa-70
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme))}]])]))))
:customization-color (:customization-color user-props)}]
[rn/pressable
{:accessibility-label :options-button
:on-press #(when on-options-press
(on-options-press))}
[icon/icon :i/options
{:color (if blur?
colors/white-opa-70
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme))}]]]))))
(def view (quo.theme/with-theme internal-view))
@@ -9,7 +9,6 @@
[quo.theme :as quo.theme]
[react-native.core :as rn]
[reagent.core :as reagent]
[utils.address :as address]
[utils.i18n :as i18n]))
(defn- account
@@ -31,7 +30,7 @@
{:size :paragraph-2
:weight :monospace
:style (style/account-address theme)}
(address/get-shortened-key address)]])
address]])
(defn- internal-view
[]
+2 -3
View File
@@ -27,9 +27,8 @@
{:size 20
:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}]
:checkbox
[selectors/view
{:type :checkbox
:checked? checked?
[selectors/checkbox
{:checked? checked?
:accessibility-label :user-list-toggle-check
:disabled? disabled?
:on-change (when on-check on-check)}]
+17 -15
View File
@@ -5,22 +5,23 @@
(def container
{:flex-wrap :nowrap
:flex-direction :row
:align-items :flex-end})
:align-items :center})
(def name-container
{:margin-right 8
:flex-direction :row
:align-items :flex-end
})
(defn middle-dot
(defn middle-dot-nickname
[theme]
{:color (colors/theme-colors colors/neutral-40 colors/neutral-50 theme)
:margin-horizontal 2})
:margin-horizontal 4})
(defn chat-key-text
[theme]
{:color (colors/theme-colors colors/neutral-40 colors/neutral-50 theme)})
{:color (colors/theme-colors colors/neutral-40 colors/neutral-50 theme)
:margin-left 8
:padding-top 1})
(defn middle-dot-chat-key
[theme]
{:color (colors/theme-colors colors/neutral-40 colors/neutral-50 theme)
:margin-left 4})
(defn primary-name
[muted? theme]
@@ -33,13 +34,14 @@
[theme]
{:padding-top 1
:flex-shrink 999999
:min-width 40
:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)})
(defn icon-container
[is-first?]
{:margin-left (if is-first? 4 2)
:margin-bottom 2})
(def icon-container
{:margin-left 4})
(defn time-text
[theme]
{:color (colors/theme-colors colors/neutral-40 colors/neutral-50 theme)})
{:color (colors/theme-colors colors/neutral-40 colors/neutral-50 theme)
:padding-top 1
:margin-left 8})
+39 -41
View File
@@ -14,48 +14,46 @@
muted? size theme]
:or {size 13}}]
[rn/view {:style (merge style/container style {:height (if (= size 15) 21.75 18.2)})}
[rn/view {:style style/name-container}
[text/text
{:weight :semi-bold
:size (if (= size 15) :paragraph-1 :paragraph-2)
:number-of-lines 1
:accessibility-label :author-primary-name
:style (style/primary-name muted? theme)}
primary-name]
(when (not (string/blank? secondary-name))
[:<>
[text/text
{:size :label
:number-of-lines 1
:style (style/middle-dot theme)}
middle-dot]
[text/text
{:weight :medium
:size :label
:number-of-lines 1
:accessibility-label :author-secondary-name
:style (style/secondary-name theme)}
secondary-name]])
(when contact?
[icons/icon :main-icons2/contact
{:size 12
:no-color true
:container-style (style/icon-container true)}])
(cond
verified?
[icons/icon :main-icons2/verified
{:size 12
:no-color true
:container-style (style/icon-container contact?)}]
untrustworthy?
[icons/icon :main-icons2/untrustworthy
{:size 12
:no-color true
:container-style (style/icon-container contact?)}])
]
[text/text
{:weight :semi-bold
:size (if (= size 15) :paragraph-1 :paragraph-2)
:number-of-lines 1
:accessibility-label :author-primary-name
:style (style/primary-name muted? theme)}
primary-name]
(when (not (string/blank? secondary-name))
[:<>
[text/text
{:size :label
:number-of-lines 1
:style (style/middle-dot-nickname theme)}
middle-dot]
[text/text
{:weight :medium
:size :label
:number-of-lines 1
:accessibility-label :author-secondary-name
:style (style/secondary-name theme)}
secondary-name]])
(when contact?
[icons/icon :main-icons2/contact
{:size 12
:no-color true
:container-style style/icon-container}])
(cond
verified?
[icons/icon :main-icons2/verified
{:size 12
:no-color true
:container-style style/icon-container}]
untrustworthy?
[icons/icon :main-icons2/untrustworthy
{:size 12
:no-color true
:container-style style/icon-container}])
(when (and (not verified?) short-chat-key)
[text/text
{:weight :monospace
{:monospace true
:size :label
:number-of-lines 1
:style (style/chat-key-text theme)}
@@ -65,7 +63,7 @@
{:monospace true
:size :label
:number-of-lines 1
:style (style/middle-dot theme)}
:style (style/middle-dot-chat-key theme)}
middle-dot])
(when time-str
[text/text
@@ -2,6 +2,11 @@
(:require
[quo.foundations.colors :as colors]))
(def height 56)
(def root-container
{:height height})
(defn container
[state theme]
{:padding-top 12
@@ -25,7 +25,7 @@
(defn- view-internal
[{:keys [theme container-style default-active state data on-press active-id]}]
[rn/view
{:style container-style
{:style (merge style/root-container container-style)
:accessibility-label :showcase-nav}
[rn/flat-list
{:data data
@@ -11,9 +11,8 @@
{:on-press on-change
:accessibility-label :disclaimer-touchable-opacity}
[rn/view {:style (merge container-style (style/container blur?))}
[selectors/view
{:type :checkbox
:accessibility-label accessibility-label
[selectors/checkbox
{:accessibility-label accessibility-label
:blur? blur?
:checked? checked?
:on-change on-change}]
@@ -6,27 +6,27 @@
(defn render-toggle
([]
(render-toggle {:type :toggle}))
(render-toggle {}))
([opts]
(h/render (reagent/as-element [selectors/view (assoc opts :type :toggle)]))))
(h/render (reagent/as-element [selectors/toggle opts]))))
(defn render-checkbox
([]
(render-checkbox {:type :checkbox}))
(render-checkbox {}))
([opts]
(h/render (reagent/as-element [selectors/view (assoc opts :type :checkbox)]))))
(h/render (reagent/as-element [selectors/checkbox opts]))))
(defn render-filled-checkbox
(defn render-checkbox-prefill
([]
(render-filled-checkbox {:type :filled-checkbox}))
(render-checkbox-prefill {}))
([opts]
(h/render (reagent/as-element [selectors/view (assoc opts :type :filled-checkbox)]))))
(h/render (reagent/as-element [selectors/checkbox-prefill opts]))))
(defn render-radio
([]
(render-radio {:type :radio}))
(render-radio {}))
([opts]
(h/render (reagent/as-element [selectors/view (assoc opts :type :radio)]))))
(h/render (reagent/as-element [selectors/radio opts]))))
(h/test "default render of toggle component"
(render-toggle)
@@ -58,12 +58,12 @@
(h/fire-event :press (h/get-by-test-id "checkbox-component"))
(h/was-called mock-fn)))
(h/test "default render of filled-checkbox component"
(render-filled-checkbox)
(h/is-truthy (h/get-by-test-id "filled-checkbox-component")))
(h/test "default render of checkbox-prefill component"
(render-checkbox-prefill)
(h/is-truthy (h/get-by-test-id "checkbox-prefill-component")))
(h/test "filled-checkbox component on change is working"
(h/test "checkbox-prefill component on change is working"
(let [mock-fn (h/mock-fn)]
(render-filled-checkbox {:on-change mock-fn})
(h/fire-event :press (h/get-by-test-id "filled-checkbox-component"))
(render-checkbox-prefill {:on-change mock-fn})
(h/fire-event :press (h/get-by-test-id "checkbox-prefill-component"))
(h/was-called mock-fn)))
@@ -6,7 +6,7 @@
[customization-color theme]
{:normal {:checked (colors/resolve-color customization-color theme)
:unchecked (colors/theme-colors colors/neutral-30 colors/neutral-80 theme)}
:blur {:checked (colors/theme-colors (colors/resolve-color customization-color theme)
:blur {:checked (colors/theme-colors (colors/custom-color customization-color 50)
colors/white-opa-70
theme)
:unchecked (colors/theme-colors colors/neutral-80-opa-20 colors/white-opa-10 theme)}})
@@ -15,7 +15,7 @@
[customization-color theme]
{:normal {:checked (colors/resolve-color customization-color theme)
:unchecked (colors/theme-colors colors/neutral-30 colors/neutral-70 theme)}
:blur {:checked (colors/theme-colors (colors/resolve-color customization-color theme)
:blur {:checked (colors/theme-colors (colors/custom-color customization-color 50)
colors/white
theme)
:unchecked (colors/theme-colors colors/neutral-80-opa-20 colors/white-opa-40 theme)}})
@@ -29,7 +29,7 @@
[customization-color theme]
{:normal {:checked (colors/resolve-color customization-color theme)
:unchecked (colors/theme-colors colors/white-opa-40 colors/neutral-80-opa-40 theme)}
:blur {:checked (colors/theme-colors (colors/resolve-color customization-color theme)
:blur {:checked (colors/theme-colors (colors/custom-color customization-color 50)
colors/white)
:unchecked colors/white-opa-5}})
@@ -38,7 +38,7 @@
{:normal (colors/theme-colors colors/neutral-30 colors/neutral-70 theme)
:blur (colors/theme-colors colors/neutral-80-opa-20 colors/white-opa-40 theme)})
(defn- filled-checkbox-background-color
(defn- checkbox-prefill-background-color
[theme]
{:normal (colors/theme-colors colors/neutral-30 colors/neutral-80 theme)
:blur (colors/theme-colors colors/neutral-80-opa-10 colors/white-opa-10 theme)})
@@ -119,16 +119,16 @@
(colors/theme-colors colors/white colors/neutral-100 theme)
colors/white)})
(defn filled-checkbox
(defn checkbox-prefill
[{:keys [disabled? blur? container-style theme]}]
(assoc container-style
:height 21
:width 21
:border-radius 6
:opacity (if disabled? 0.3 1)
:background-color (get-color (filled-checkbox-background-color theme) blur?)))
:background-color (get-color (checkbox-prefill-background-color theme) blur?)))
(defn filled-checkbox-check
(defn checkbox-prefill-check
[checked? _blur? theme]
{:size 20
:color (when checked? (colors/theme-colors colors/neutral-100 colors/white theme))})
@@ -11,7 +11,7 @@
(when checked-atom (swap! checked-atom not))
(when on-change (on-change (not checked?))))
(defn- base-selector
(defn- selector-internal
[{:keys [default-checked? checked?]}]
(let [controlled-component? (some? checked?)
internal-checked? (when-not controlled-component?
@@ -28,13 +28,14 @@
:container-style container-style
:customization-color customization-color
:theme theme})]
[rn/pressable
[rn/touchable-without-feedback
(when-not disabled?
{:on-press #(handle-press on-change internal-checked? actual-checked?)})
[rn/view
{:style outer-styles
:needs-offscreen-alpha-compositing true
:accessibility-label accessibility-label
:accessibility-role :checkbox
:testID test-id}
[rn/view
{:style (inner-style-fn {:theme theme
@@ -44,49 +45,39 @@
(when (and icon-style-fn actual-checked?)
[icons/icon :i/check-small (icon-style-fn actual-checked? blur? theme)])]]]))))
(defn- toggle
(def ^:private selector (quo.theme/with-theme selector-internal))
(defn toggle
[props]
[base-selector
[selector
(assoc props
:label-prefix "toggle"
:outer-style-fn style/toggle
:inner-style-fn style/toggle-inner)])
(defn- radio
(defn radio
[props]
[base-selector
[selector
(assoc props
:label-prefix "radio"
:outer-style-fn style/radio
:inner-style-fn style/radio-inner)])
(defn- checkbox
(defn checkbox
[props]
[base-selector
[selector
(assoc props
:label-prefix "checkbox"
:outer-style-fn style/checkbox
:inner-style-fn style/common-checkbox-inner
:icon-style-fn style/checkbox-check)])
(defn- filled-checkbox
(defn checkbox-prefill
[props]
[base-selector
[selector
(assoc props
:label-prefix "filled-checkbox"
:outer-style-fn style/filled-checkbox
:label-prefix "checkbox-prefill"
:outer-style-fn style/checkbox-prefill
:inner-style-fn style/common-checkbox-inner
:icon-style-fn style/filled-checkbox-check)])
:icon-style-fn style/checkbox-prefill-check)])
(defn view-internal
[{:keys [type]
:or {type :toggle}
:as props}]
(case type
:toggle [toggle props]
:radio [radio props]
:checkbox [checkbox props]
:filled-checkbox [filled-checkbox props]
nil))
(def view (quo.theme/with-theme view-internal))
@@ -29,9 +29,8 @@
[rn/view {:style style/card-footer-label-container}
[text/text {:size :paragraph-2} label]]
[rn/view {:style style/card-footer-toggle-container}
[selectors/view
{:type :toggle
:disabled? (not active?)
[selectors/toggle
{:disabled? (not active?)
:on-change on-toggle}]]]])
(defn- selection-indicator
@@ -97,7 +97,9 @@
{:type :outline
:size 24})
(:button-text action-props)]
:selector [selectors/view action-props]
:selector (if (= (:type action-props) :checkbox)
[selectors/checkbox action-props]
[selectors/toggle action-props])
nil)])
(defn- internal-view
+1 -1
View File
@@ -3,7 +3,7 @@
[quo.components.avatars.account-avatar.view :as account-avatar]
[quo.components.avatars.channel-avatar.view :as channel-avatar]
[quo.components.avatars.user-avatar.view :as user-avatar]
[quo.components.avatars.wallet-user-avatar.view :as wallet-avatar]
[quo.components.avatars.wallet-user-avatar :as wallet-avatar]
[quo.components.share.qr-code.style :as style]
[react-native.core :as rn]
[react-native.fast-image :as fast-image]))
@@ -7,10 +7,10 @@
(defn- colored-network-text
[theme network]
(let [{:keys [network-name short-name]} network]
(let [{:keys [name short-name]} network]
[text/text
{:size :paragraph-2
:style {:color (colors/resolve-color network-name theme)}}
:style {:color (colors/resolve-color name theme)}}
(str short-name ":")]))
(defn- view-internal
+2 -3
View File
@@ -48,9 +48,8 @@
[text/text {:weight :semi-bold}
(if (= type :default-keypair) (keypair-string full-name) full-name)]
(if (= action :selector)
[selectors/view
{:type :radio
:checked? selected?
[selectors/radio
{:checked? selected?
:blur? blur?
:customization-color customization-color}]
[rn/pressable {:on-press on-options-press}
+9 -7
View File
@@ -1,14 +1,13 @@
(ns quo.core
(:refer-clojure :exclude [filter])
(:require
quo.components.animated-header-flatlist.view
quo.components.avatars.account-avatar.view
quo.components.avatars.channel-avatar.view
quo.components.avatars.collection-avatar.view
quo.components.avatars.group-avatar.view
quo.components.avatars.icon-avatar
quo.components.avatars.user-avatar.view
quo.components.avatars.wallet-user-avatar.view
quo.components.avatars.wallet-user-avatar
quo.components.banners.banner.view
quo.components.browser.browser-input.view
quo.components.buttons.button.view
@@ -31,7 +30,6 @@
quo.components.community.channel-actions
quo.components.community.community-card-view
quo.components.community.community-list-view
quo.components.community.community-stat.view
quo.components.community.community-view
quo.components.community.icon
quo.components.community.token-gating
@@ -50,6 +48,7 @@
quo.components.drawers.permission-context.view
quo.components.dropdowns.dropdown.view
quo.components.dropdowns.network-dropdown.view
quo.components.dropdowns.dropdown-input.view
quo.components.empty-state.empty-state.view
quo.components.gradient.gradient-cover.view
[quo.components.graph.interactive-graph.view :as interactive-graph]
@@ -161,7 +160,7 @@
(def group-avatar quo.components.avatars.group-avatar.view/view)
(def icon-avatar quo.components.avatars.icon-avatar/icon-avatar)
(def user-avatar quo.components.avatars.user-avatar.view/user-avatar)
(def wallet-user-avatar quo.components.avatars.wallet-user-avatar.view/wallet-user-avatar)
(def wallet-user-avatar quo.components.avatars.wallet-user-avatar/wallet-user-avatar)
;;;; Banner
(def banner quo.components.banners.banner.view/view)
@@ -200,7 +199,7 @@
(def communities-membership-list-item
quo.components.community.community-list-view/communities-membership-list-item)
(def community-stats-column quo.components.community.community-view/community-stats-column)
(def community-stat quo.components.community.community-stat.view/view)
(def community-stats quo.components.community.community-view/community-stats)
(def community-tags quo.components.community.community-view/community-tags)
(def community-title quo.components.community.community-view/community-title)
(def permission-tag-container quo.components.community.community-view/permission-tag-container)
@@ -231,6 +230,7 @@
;;;; Dropdowns
(def dropdown quo.components.dropdowns.dropdown.view/view)
(def network-dropdown quo.components.dropdowns.network-dropdown.view/view)
(def dropdown-input quo.components.dropdowns.dropdown-input.view/view)
;;;; Empty State
(def empty-state quo.components.empty-state.empty-state.view/empty-state)
@@ -241,7 +241,6 @@
;;;; Header
(def header quo.components.header/header)
(def animated-header-list quo.components.animated-header-flatlist.view/animated-header-list)
;;;; Gradient
(def gradient-cover quo.components.gradient.gradient-cover.view/view)
@@ -331,7 +330,10 @@
(def reactions-selector quo.components.selectors.reactions-selector.view/view)
(def react quo.components.selectors.react.view/view)
(def react-selector quo.components.selectors.react-selector.view/view)
(def selectors quo.components.selectors.selectors.view/view)
(def checkbox quo.components.selectors.selectors.view/checkbox)
(def toggle quo.components.selectors.selectors.view/toggle)
(def radio quo.components.selectors.selectors.view/radio)
(def checkbox-prefill quo.components.selectors.selectors.view/checkbox-prefill)
;;;; Settings
(def account quo.components.settings.accounts.view/account)
-1
View File
@@ -15,7 +15,6 @@
[quo.components.calendar.calendar.component-spec]
[quo.components.calendar.calendar.month-picker.component-spec]
[quo.components.colors.color-picker.component-spec]
[quo.components.community.community-stat.component-spec]
[quo.components.counter.counter.component-spec]
[quo.components.counter.step.component-spec]
[quo.components.dividers.divider-label.component-spec]
+6 -8
View File
@@ -18,14 +18,12 @@
(log/error e))))
(defn set-item!
([k value] (set-item! k value identity))
([k value cb]
(-> ^js async-storage
(.setItem (str k)
(clj->transit value))
(.then (fn [_] (cb)))
(.catch (fn [error]
(log/error "[async-storage]" error))))))
[k value]
(-> ^js async-storage
(.setItem (str k)
(clj->transit value))
(.catch (fn [error]
(log/error "[async-storage]" error)))))
(defn set-item-factory
[]
@@ -1,12 +0,0 @@
(ns react-native.react-native-intersection-observer
(:require
["react-native-intersection-observer" :refer [InView IOFlatList]]
[react-native.flat-list :refer [base-list-props]]
[reagent.core :as reagent]))
(def view (reagent/adapt-react-class InView))
(def flat-list-comp (reagent/adapt-react-class IOFlatList))
(defn flat-list
[props]
[flat-list-comp (base-list-props props)])
+2 -3
View File
@@ -16,17 +16,16 @@
[status-im.ui.components.list-selection :as list-selection]
[status-im.utils.deprecated-types :as types]
[status-im.utils.random :as random]
[status-im.utils.universal-links.utils :as links]
[status-im2.constants :as constants]
[status-im2.navigation.events :as navigation]
[taoensso.timbre :as log]
[utils.address :as address]
[utils.debounce :as debounce]
[utils.ens.core :as utils.ens]
[utils.ethereum.chain :as chain]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.security.core :as security]
[utils.universal-links :as links]
[utils.url :as url]))
(rf/defn update-browser-option
@@ -91,7 +90,7 @@
(when (not error?)
(let [current-url (get-current-url (get-current-browser db))
host (url/url-host current-url)]
(if (and (not resolved-url) (utils.ens/is-valid-eth-name? host))
(if (and (not resolved-url) (ens/is-valid-eth-name? host))
{:db (update db :browser/options assoc :resolving? true)
:browser/resolve-ens-contenthash {:chain-id (chain/chain-id db)
:ens-name host
+30 -12
View File
@@ -8,9 +8,9 @@
[status-im.bottom-sheet.events :as bottom-sheet]
[status-im.ui.components.colors :as colors]
[status-im.utils.deprecated-types :as types]
[status-im.utils.universal-links.core :as universal-links]
[status-im2.common.muting.helpers :refer [format-mute-till]]
[status-im2.common.toasts.events :as toasts]
[status-im2.common.universal-links :as universal-links]
[status-im2.constants :as constants]
[status-im2.contexts.chat.events :as chat.events]
[status-im2.contexts.shell.activity-center.events :as activity-center]
@@ -94,17 +94,9 @@
(defn- handle-my-request
[db {:keys [community-id state deleted] :as request}]
(let [{:keys [name]} (get-in db [:communities community-id])]
(cond (and (= constants/community-request-to-join-state-pending state) (not deleted))
(assoc-in db [:communities/my-pending-requests-to-join community-id] request)
(and (= constants/community-request-to-join-state-accepted state) (not deleted))
(do (re-frame/dispatch [:toasts/upsert
{:icon :i/correct
:id :joined-community
:icon-color (:positive-01 @colors/theme)
:text (i18n/label :t/joined-community {:community name})}])
(update-in db [:communities/my-pending-requests-to-join] dissoc community-id))
:else (update-in db [:communities/my-pending-requests-to-join] dissoc community-id))))
(if (and (= constants/community-request-to-join-state-pending state) (not deleted))
(assoc-in db [:communities/my-pending-requests-to-join community-id] request)
(update-in db [:communities/my-pending-requests-to-join] dissoc community-id)))
(defn handle-admin-request
[db {:keys [id community-id deleted] :as request}]
@@ -228,6 +220,32 @@
(log/error "failed to import community" %)
(re-frame/dispatch [::failed-to-import %]))}]})
(rf/defn request-to-join
{:events [:communities/request-to-join]}
[_ community-id]
{:json-rpc/call [{:method "wakuext_requestToJoinCommunity"
:params [{:communityId community-id}]
:js-response true
:on-success #(re-frame/dispatch [:communities/requested-to-join %])
:on-error #(log/error "failed to request to join community" community-id)}]})
(rf/defn requested-to-join-with-password-error
{:events [:communities/requested-to-join-with-password-error]}
[{:keys [db]} error]
{:db (assoc-in db [:password-authentication :error] error)})
(rf/defn request-to-join-with-password
{:events [:communities/request-to-join-with-password]}
[_ community-id password]
{:json-rpc/call [{:method "wakuext_requestToJoinCommunity"
:params [{:communityId community-id :password password}]
:js-response true
:on-success #(re-frame/dispatch [:communities/requested-to-join %])
:on-error (fn [error]
(log/error "failed to request to join community" community-id error)
(re-frame/dispatch [:communities/requested-to-join-with-password-error
error]))}]})
(rf/defn get-user-requests-to-join
{:events [:communities/get-user-requests-to-join]}
[_]
+6
View File
@@ -11,3 +11,9 @@
:group/selected-contacts #{}
:new-chat-name "")}
(navigation/navigate-to :contact-toggle-list nil)))
(defn displayed-photo
[{:keys [images]}]
(or (:large images)
(:thumbnail images)
(first images)))
+14 -9
View File
@@ -2,12 +2,21 @@
(:require
[clojure.set :as set]
[clojure.string :as string]
[status-im.utils.gfycat.core :as gfycat]
[status-im2.constants :as constants]
[utils.address :as address]))
(defn public-key->new-contact
[public-key]
(let [alias (gfycat/generate-gfy public-key)]
{:alias alias
:name alias
:primary-name alias
:public-key public-key}))
(defn public-key-and-ens-name->new-contact
[public-key ens-name]
(let [contact {:public-key public-key}]
(let [contact (public-key->new-contact public-key)]
(if ens-name
(-> contact
(assoc :ens-name ens-name)
@@ -18,7 +27,8 @@
(defn public-key->contact
[contacts public-key]
(when public-key
(get contacts public-key {:public-key public-key})))
(or (get contacts public-key)
(public-key->new-contact public-key))))
(defn- contact-by-address
[[addr contact] address]
@@ -56,13 +66,8 @@
(assoc public-key current-contact))]
(->> members
(map #(or (get all-contacts %)
{:public-key %}))
(sort-by (comp string/lower-case
(fn [{:keys [primary-name name alias public-key]}]
(or primary-name
name
alias
public-key))))
(public-key->new-contact %)))
(sort-by (comp string/lower-case #(or (:primary-name %) (:name %) (:alias %))))
(map #(if (get admins (:public-key %))
(assoc % :admin? true)
%)))))
+48 -42
View File
@@ -1,49 +1,55 @@
(ns status-im.contact.db-test
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.contact.db :as contact.db]))
(:require
[cljs.test :refer-macros [deftest is testing]]
[status-im.contact.db :as contact.db]
[status-im.utils.gfycat.core :as gfycat]))
(deftest contacts-subs
(testing "get-all-contacts-in-group-chat"
(let
[chat-contact-ids
#{"0x04fcf40c526b09ff9fb22f4a5dbd08490ef9b64af700870f8a0ba2133f4251d5607ed83cd9047b8c2796576bc83fa0de23a13a4dced07654b8ff137fe744047917"
"0x04985040682b77a32bb4bb58268a0719bd24ca4d07c255153fe1eb2ccd5883669627bd1a092d7cc76e8e4b9104327667b19dcda3ac469f572efabe588c38c1985f"
"0x048a2f8b80c60f89a91b4c1316e56f75b087f446e7b8701ceca06a40142d8efe1f5aa36bd0fee9e248060a8d5207b43ae98bef4617c18c71e66f920f324869c09f"}
admins
#{"0x04fcf40c526b09ff9fb22f4a5dbd08490ef9b64af700870f8a0ba2133f4251d5607ed83cd9047b8c2796576bc83fa0de23a13a4dced07654b8ff137fe744047917"}
(with-redefs [gfycat/generate-gfy (constantly "generated")]
(let
[chat-contact-ids
#{"0x04fcf40c526b09ff9fb22f4a5dbd08490ef9b64af700870f8a0ba2133f4251d5607ed83cd9047b8c2796576bc83fa0de23a13a4dced07654b8ff137fe744047917"
"0x04985040682b77a32bb4bb58268a0719bd24ca4d07c255153fe1eb2ccd5883669627bd1a092d7cc76e8e4b9104327667b19dcda3ac469f572efabe588c38c1985f"
"0x048a2f8b80c60f89a91b4c1316e56f75b087f446e7b8701ceca06a40142d8efe1f5aa36bd0fee9e248060a8d5207b43ae98bef4617c18c71e66f920f324869c09f"}
admins
#{"0x04fcf40c526b09ff9fb22f4a5dbd08490ef9b64af700870f8a0ba2133f4251d5607ed83cd9047b8c2796576bc83fa0de23a13a4dced07654b8ff137fe744047917"}
contacts
{"0x04985040682b77a32bb4bb58268a0719bd24ca4d07c255153fe1eb2ccd5883669627bd1a092d7cc76e8e4b9104327667b19dcda3ac469f572efabe588c38c1985f"
{:last-updated 0
:name "User B"
:primary-name "User B"
:last-online 0
:public-key
"0x04985040682b77a32bb4bb58268a0719bd24ca4d07c255153fe1eb2ccd5883669627bd1a092d7cc76e8e4b9104327667b19dcda3ac469f572efabe588c38c1985f"}}
current-multiaccount
{:last-updated 0
:signed-up? true
:sharing-usage-data? false
:primary-name "User A"
:name "User A"
:public-key
"0x048a2f8b80c60f89a91b4c1316e56f75b087f446e7b8701ceca06a40142d8efe1f5aa36bd0fee9e248060a8d5207b43ae98bef4617c18c71e66f920f324869c09f"}]
(is
(=
(contact.db/get-all-contacts-in-group-chat chat-contact-ids
admins
contacts
current-multiaccount)
[{:admin? true
:public-key
"0x04fcf40c526b09ff9fb22f4a5dbd08490ef9b64af700870f8a0ba2133f4251d5607ed83cd9047b8c2796576bc83fa0de23a13a4dced07654b8ff137fe744047917"}
{:alias "User A"
:primary-name "User A"
:public-key
"0x048a2f8b80c60f89a91b4c1316e56f75b087f446e7b8701ceca06a40142d8efe1f5aa36bd0fee9e248060a8d5207b43ae98bef4617c18c71e66f920f324869c09f"}
contacts
{"0x04985040682b77a32bb4bb58268a0719bd24ca4d07c255153fe1eb2ccd5883669627bd1a092d7cc76e8e4b9104327667b19dcda3ac469f572efabe588c38c1985f"
{:last-updated 0
:name "User B"
:primary-name "User B"
:last-online 0
:public-key
"0x04985040682b77a32bb4bb58268a0719bd24ca4d07c255153fe1eb2ccd5883669627bd1a092d7cc76e8e4b9104327667b19dcda3ac469f572efabe588c38c1985f"}}
current-multiaccount
{:last-updated 0
:name "User B"
:primary-name "User B"
:last-online 0
:signed-up? true
:sharing-usage-data? false
:primary-name "User A"
:name "User A"
:public-key
"0x04985040682b77a32bb4bb58268a0719bd24ca4d07c255153fe1eb2ccd5883669627bd1a092d7cc76e8e4b9104327667b19dcda3ac469f572efabe588c38c1985f"}])))))
"0x048a2f8b80c60f89a91b4c1316e56f75b087f446e7b8701ceca06a40142d8efe1f5aa36bd0fee9e248060a8d5207b43ae98bef4617c18c71e66f920f324869c09f"}]
(is
(=
(contact.db/get-all-contacts-in-group-chat chat-contact-ids
admins
contacts
current-multiaccount)
[{:name "generated"
:primary-name "generated"
:alias "generated"
:admin? true
:public-key
"0x04fcf40c526b09ff9fb22f4a5dbd08490ef9b64af700870f8a0ba2133f4251d5607ed83cd9047b8c2796576bc83fa0de23a13a4dced07654b8ff137fe744047917"}
{:alias "User A"
:primary-name "User A"
:public-key
"0x048a2f8b80c60f89a91b4c1316e56f75b087f446e7b8701ceca06a40142d8efe1f5aa36bd0fee9e248060a8d5207b43ae98bef4617c18c71e66f920f324869c09f"}
{:last-updated 0
:name "User B"
:primary-name "User B"
:last-online 0
:public-key
"0x04985040682b77a32bb4bb58268a0719bd24ca4d07c255153fe1eb2ccd5883669627bd1a092d7cc76e8e4b9104327667b19dcda3ac469f572efabe588c38c1985f"}]))))))
@@ -1,9 +0,0 @@
(ns status-im.data-store.communities
(:require [clojure.set :as set]))
(defn rpc->channel-permissions
[rpc-channels-permissions]
(update-vals rpc-channels-permissions
(fn [{:keys [viewAndPostPermissions viewOnlyPermissions]}]
{:view-only (set/rename-keys viewOnlyPermissions {:satisfied :satisfied?})
:view-and-post (set/rename-keys viewAndPostPermissions {:satisfied :satisfied?})})))
@@ -1,23 +0,0 @@
(ns status-im.data-store.communities-test
(:require
[cljs.test :refer [deftest is]]
[status-im.data-store.communities :as sut]))
(def permissions
{"community-id-chat-1"
{:viewOnlyPermissions {:satisfied false
:permissions {:token-permission-id-01 {:criteria [false]}}}
:viewAndPostPermissions {:satisfied true :permissions {}}}
"community-id-chat-2"
{:viewOnlyPermissions {:satisfied true :permissions {}}
:viewAndPostPermissions {:satisfied true :permissions {}}}})
(deftest rpc->channel-permissions-test
(is (= {"community-id-chat-1"
{:view-only {:satisfied? false
:permissions {:token-permission-id-01 {:criteria [false]}}}
:view-and-post {:satisfied? true :permissions {}}}
"community-id-chat-2"
{:view-only {:satisfied? true :permissions {}}
:view-and-post {:satisfied? true :permissions {}}}}
(sut/rpc->channel-permissions permissions))))
+3 -4
View File
@@ -6,6 +6,7 @@
[re-frame.core :as re-frame]
[status-im.bottom-sheet.events :as bottom-sheet]
[status-im.ethereum.ens :as ens]
[status-im.ethereum.stateofus :as stateofus]
[status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im.utils.random :as random]
[status-im.wallet.utils :as wallet.utils]
@@ -13,8 +14,6 @@
[status-im2.navigation.events :as navigation]
[taoensso.timbre :as log]
[utils.datetime :as datetime]
[utils.ens.core :as utils.ens]
[utils.ens.stateofus :as stateofus]
[utils.ethereum.chain :as chain]
[utils.ethereum.eip.eip55 :as eip55]
[utils.re-frame :as rf]))
@@ -159,7 +158,7 @@
(cond
;; No address for a stateofus subdomain: it can be registered
(and (= response utils.ens/default-address) (not custom-domain?))
(and (= response ens/default-address) (not custom-domain?))
(re-frame/dispatch [::name-resolved username :available])
;; if we get an address back, we try to get the public key associated
@@ -206,7 +205,7 @@
(defn- valid-custom-domain?
[username]
(and (utils.ens/is-valid-eth-name? username)
(and (ens/is-valid-eth-name? username)
(stateofus/lower-case? username)))
(defn- valid-username?
@@ -1,4 +1,4 @@
(ns utils.ethereum.eip.eip681
(ns status-im.ethereum.eip681
"Utility function related to [EIP681](https://eips.ethereum.org/EIPS/eip-681)
This EIP standardize how ethereum payment request can be represented as URI (say to embed them in a QR code).
@@ -6,9 +6,11 @@
e.g. ethereum:0x1234@1/transfer?to=0x5678&value=1e18&gas=5000"
(:require
[clojure.string :as string]
[status-im.ethereum.ens :as ens]
[status-im.ethereum.tokens :as tokens]
[utils.address :as address]
[utils.ens.core :as utils.ens]
[utils.ethereum.chain :as chain]))
[utils.ethereum.chain :as chain]
[utils.money :as money]))
(def scheme "ethereum")
(def scheme-separator ":")
@@ -18,6 +20,7 @@
(def parameter-separator "&")
(def key-value-separator "=")
;;TODO(goranjovic) - rewrite all of these with something more readable than regex
(def uri-pattern
(re-pattern (str scheme scheme-separator "([^" query-separator "]*)(?:\\" query-separator "(.*))?")))
(def authority-path-pattern
@@ -68,10 +71,10 @@
(let [[_ authority-path query] (re-find uri-pattern s)]
(when authority-path
(let [[_ raw-address chain-id function-name] (re-find authority-path-pattern authority-path)]
(when (or (or (utils.ens/is-valid-eth-name? raw-address) (address/address? raw-address))
(when (or (or (ens/is-valid-eth-name? raw-address) (address/address? raw-address))
(when (string/starts-with? raw-address "pay-")
(let [pay-address (string/replace-first raw-address "pay-" "")]
(or (utils.ens/is-valid-eth-name? pay-address)
(or (ens/is-valid-eth-name? pay-address)
(address/address? pay-address)))))
(let [address (if (string/starts-with? raw-address "pay-")
(string/replace-first raw-address "pay-" "")
@@ -79,7 +82,7 @@
(when-let [arguments (parse-arguments function-name query)]
(let [contract-address (get-in arguments [:function-arguments :address])]
(if-not (or (not contract-address)
(or (utils.ens/is-valid-eth-name? contract-address)
(or (ens/is-valid-eth-name? contract-address)
(address/address? contract-address)))
nil
(merge {:address address
@@ -88,6 +91,31 @@
(chain/chain-keyword->chain-id :mainnet))}
arguments))))))))))))
(defn parse-eth-value
"Takes a map as returned by `parse-uri` and returns value as BigNumber"
[s]
(when (string? s)
(let [eth? (string/ends-with? s "ETH")
^js n (money/bignumber (string/replace s "ETH" ""))]
(if eth? (.times n 1e18) n))))
(defn extract-request-details
"Return a map encapsulating request details (with keys `value`, `address` and `symbol`) from a parsed URI.
Supports ethereum and erc20 token."
[{:keys [value address function-name function-arguments] :as details} all-tokens]
(when address
(merge details
(case function-name
nil
{:value (parse-eth-value value)
:symbol :ETH
:address address}
"transfer"
{:value (money/bignumber (:uint256 function-arguments))
:symbol (:symbol (tokens/address->token all-tokens address))
:address (:address function-arguments)}
nil))))
(defn- generate-query-string
[m]
(string/join parameter-separator
@@ -114,3 +142,12 @@
(let [native-parameters (dissoc parameters :function-name :function-arguments)]
(generate-query-string (merge function-arguments native-parameters))))
(str query-separator (generate-query-string parameters))))))))
(defn generate-erc20-uri
"Generate a EIP 681 URI encapsulating ERC20 token transfer"
[address {sym :symbol value :value :as m} all-tokens]
(when-let [token (tokens/symbol->token all-tokens sym)]
(generate-uri (:address token)
(merge (dissoc m :value :symbol)
{:function-name "transfer"
:function-arguments {:uint256 value :address address}}))))
@@ -1,7 +1,7 @@
(ns utils.ethereum.eip.eip681-test
(ns status-im.ethereum.eip681-test
(:require
[cljs.test :refer-macros [deftest is]]
[utils.ethereum.eip.eip681 :as eip681]
[status-im.ethereum.eip681 :as eip681]
[utils.money :as money]))
(deftest parse-uri
@@ -118,6 +118,27 @@
:symbol :STT
:decimals 18}}})
(deftest generate-erc20-uri
(is (= nil (eip681/generate-erc20-uri nil nil all-tokens)))
(is
(=
"ethereum:0x744d70fdbe2ba4cf95131626614a1763df805b9e/transfer?uint256=5&address=0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7"
(eip681/generate-erc20-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7"
{:symbol :SNT :value 5}
(:mainnet all-tokens))))
(is
(=
"ethereum:0x744d70fdbe2ba4cf95131626614a1763df805b9e/transfer?uint256=5&address=0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7&gas=10000&gasPrice=10000"
(eip681/generate-erc20-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7"
{:symbol :SNT :value 5 :gas 10000 :gasPrice 10000}
(:mainnet all-tokens))))
(is
(=
"ethereum:0x744d70fdbe2ba4cf95131626614a1763df805b9e/transfer?uint256=5&address=0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7"
(eip681/generate-erc20-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7"
{:symbol :SNT :chain-id 1 :value 5}
(:mainnet all-tokens)))))
(deftest generate-uri
(is (= nil (eip681/generate-uri nil nil)))
(is (= "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7"
@@ -157,3 +178,46 @@
"ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7@3/transfer?uint256=5&address=0xc55cF4B03948D7EBc8b9E8BAD92643703811d162"
{:keys [address] :as params} (eip681/parse-uri uri)]
(is (= uri (eip681/generate-uri address (dissoc params :address))))))
(deftest parse-eth-value
(is (= nil (eip681/parse-eth-value nil)))
(is (= nil (eip681/parse-eth-value 1)))
(is (= nil (eip681/parse-eth-value "NOT_NUMBER")))
(is (.equals (money/bignumber 1) (eip681/parse-eth-value "1")))
(is (.equals (money/bignumber 2.014e18) (eip681/parse-eth-value "2.014e18")))
(is (.equals (money/bignumber 1e18) (eip681/parse-eth-value "1ETH")))
(is (.equals (money/bignumber -1e18) (eip681/parse-eth-value "-1e18")))
(is (.equals (money/bignumber 1e18) (eip681/parse-eth-value "1E18")))
(is (.equals (money/bignumber "111122223333441239") (eip681/parse-eth-value "111122223333441239"))))
(deftest extract-request-details
(let [{value :value
sym :symbol
address :address}
(eip681/extract-request-details
{:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7"
:value "1ETH"}
{})]
(is (.equals (money/ether->wei (money/bignumber 1)) value))
(is (= :ETH sym))
(is (= "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" address)))
(is (= (eip681/extract-request-details
{:address "0x744d70fdbe2ba4cf95131626614a1763df805b9e" :chain-id 1 :function-name "unknown"}
{})
{:address "0x744d70fdbe2ba4cf95131626614a1763df805b9e" :chain-id 1 :function-name "unknown"}))
(let [{value :value
sym :symbol
address :address}
(eip681/extract-request-details
{:address "0x744d70fdbe2ba4cf95131626614a1763df805b9e"
:chain-id 1
:function-name "transfer"
:function-arguments {:uint256 1000 :address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7"}}
{"0x744d70fdbe2ba4cf95131626614a1763df805b9e" {:address
"0x744d70fdbe2ba4cf95131626614a1763df805b9e"
:name "Status Network Token"
:symbol :SNT
:decimals 18}})]
(is (.equals (money/bignumber 1000) value))
(is (= :SNT sym))
(is (= "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" address))))
+27 -4
View File
@@ -1,12 +1,35 @@
(ns status-im.ethereum.ens
(:require
[clojure.string :as string]
[native-module.core :as native-module]
[status-im2.common.json-rpc.events :as json-rpc]
[utils.ens.core :as utils.ens]))
[status-im2.common.json-rpc.events :as json-rpc]))
;; this is the addresses of ens registries for the different networks
(def ens-registries
{:mainnet "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e"
:goerli "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e"})
(def default-address "0x0000000000000000000000000000000000000000")
(def default-key
"0x0400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
(def default-hash "0x0000000000000000000000000000000000000000000000000000000000000000")
(defn valid-eth-name-prefix?
[prefix]
(not
(or (string/blank? prefix)
(string/ends-with? prefix ".")
(string/includes? prefix ".."))))
(defn is-valid-eth-name?
[ens-name]
(and ens-name
(string? ens-name)
(string/ends-with? ens-name ".eth")))
(defn address
[chain-id ens-name cb]
{:pre [(utils.ens/is-valid-eth-name? ens-name)]}
{:pre [(is-valid-eth-name? ens-name)]}
(json-rpc/call {:method "ens_addressOf"
:params [chain-id ens-name]
:on-success cb
@@ -14,7 +37,7 @@
(defn pubkey
([chain-id ens-name on-success on-error]
{:pre [(utils.ens/is-valid-eth-name? ens-name)]}
{:pre [(is-valid-eth-name? ens-name)]}
(json-rpc/call {:method "ens_publicKeyOf"
:params [chain-id ens-name]
:on-success on-success
@@ -1,7 +1,8 @@
(ns utils.ens.stateofus
(ns status-im.ethereum.stateofus
(:require
[clojure.string :as string]
[utils.ens.core :as utils.ens]))
[status-im.ethereum.ens :as ens]
[status-im2.config :as config]))
(def domain "stateofus.eth")
@@ -28,8 +29,9 @@
(def old-registrars
(merge
{:mainnet "0xDB5ac1a559b02E12F29fC0eC0e37Be8E046DEF49"
:goerli "0xD1f7416F91E7Eb93dD96A61F12FC092aD6B67B11"}))
{:mainnet "0xDB5ac1a559b02E12F29fC0eC0e37Be8E046DEF49"}
(when config/test-stateofus?
{:goerli "0xD1f7416F91E7Eb93dD96A61F12FC092aD6B67B11"})))
(defn get-cached-registrar
[chain]
@@ -49,6 +51,6 @@
[contact-identity]
(when (string? contact-identity)
(string/lower-case
(if (utils.ens/is-valid-eth-name? contact-identity)
(if (ens/is-valid-eth-name? contact-identity)
contact-identity
(subdomain contact-identity)))))
@@ -1,7 +1,7 @@
(ns utils.ens.stateofus-test
(ns status-im.ethereum.stateofus-test
(:require
[cljs.test :refer-macros [deftest is]]
[utils.ens.stateofus :as stateofus]))
[status-im.ethereum.stateofus :as stateofus]))
(deftest valid-username?
(is (false? (stateofus/valid-username? nil)))
+5 -8
View File
@@ -74,12 +74,9 @@
"blockNumber" blockNumber
"accounts" accounts)
(case type
"new-transfers" (new-transfers cofx blockNumber accounts)
"recent-history-fetching" (recent-history-fetching-started cofx accounts)
"recent-history-ready" (recent-history-fetching-ended cofx event)
"fetching-history-error" (fetching-error cofx event)
"non-archival-node-detected" (non-archival-node-detected cofx event)
"wallet-owned-collectibles-filtering-done" {:fx [[:dispatch
[:wallet/owned-collectibles-filtering-done
event]]]}
"new-transfers" (new-transfers cofx blockNumber accounts)
"recent-history-fetching" (recent-history-fetching-started cofx accounts)
"recent-history-ready" (recent-history-fetching-ended cofx event)
"fetching-history-error" (fetching-error cofx event)
"non-archival-node-detected" (non-archival-node-detected cofx event)
(log/warn ::unknown-wallet-event :type type :event event)))
+3 -5
View File
@@ -41,6 +41,7 @@
status-im.ui.screens.privacy-and-security-settings.events
[status-im.utils.dimensions :as dimensions]
status-im.utils.logging.core
[status-im.utils.universal-links.core :as universal-links]
[status-im.utils.utils :as utils]
status-im.visibility-status-popover.core
status-im.visibility-status-updates.core
@@ -52,7 +53,6 @@
status-im.wallet.custom-tokens.core
[status-im2.common.biometric.events :as biometric]
[status-im2.common.theme.core :as theme]
[status-im2.common.universal-links :as universal-links]
[status-im2.constants :as constants]
status-im2.contexts.chat.home.events
status-im2.contexts.communities.home.events
@@ -118,7 +118,7 @@
(let [current-theme-type (get-in cofx [:db :profile/profile :appearance])]
(when (and (multiaccounts.model/logged-in? db)
(= current-theme-type status-im2.constants/theme-type-system))
{:profile.settings/switch-theme-fx
{:multiaccounts.ui/switch-theme-fx
[(get-in db [:profile/profile :appearance])
(:view-id db) true]})))
@@ -158,9 +158,7 @@
(rf/defn on-going-in-background
[{:keys [db now]}]
{:db (-> db
(assoc :app-in-background-since now)
(dissoc :universal-links/handling))})
{:db (assoc db :app-in-background-since now)})
;; event not implemented
;; :dispatch-n [[:audio-recorder/on-background] [:audio-message/on-background]]
-1
View File
@@ -77,7 +77,6 @@
:on-success #(re-frame/dispatch [:chat-updated %])}]}))
(rf/defn create-from-link
{:events [:group-chats/create-from-link]}
[cofx {:keys [chat-id invitation-admin chat-name]}]
(if (get-in cofx [:db :chats chat-id])
{:dispatch [:chat/navigate-to-chat chat-id]}
+261
View File
@@ -0,0 +1,261 @@
(ns status-im.integration-test
(:require
[cljs.test :refer [deftest is]]
[clojure.string :as string]
[day8.re-frame.test :as rf-test]
[native-module.core :as native-module]
[re-frame.core :as rf]
status-im.events
[status-im.multiaccounts.logout.core :as logout]
status-im.subs.root ; so integration tests can run independently
[status-im.transport.core :as transport]
[status-im.utils.test :as utils.test]
[status-im2.constants :as constants]
status-im2.events
status-im2.navigation.core
status-im2.subs.root ; so integration tests can run independently
[taoensso.timbre :as log]))
(def password "testabc")
(def community {:membership 1 :name "foo" :description "bar"})
(def account-name "account-abc")
(utils.test/init!)
(defn initialize-app!
[]
(rf/dispatch [:app-started]))
(defn create-multiaccount!
[]
(rf/dispatch [:profile.create/create-and-login
{:display-name account-name :password password :color "blue"}]))
(defn assert-app-initialized
[]
(let [app-state @(rf/subscribe [:app-state])]
(is (= "active" app-state))))
(defn messenger-started
[]
@(rf/subscribe [:messenger/started?]))
(defn assert-messenger-started
[]
(is (messenger-started)))
(defn assert-community-created
[]
(is (= @(rf/subscribe [:communities/create]) community)))
(defn create-new-account!
[]
(rf/dispatch-sync [:wallet-legacy.accounts/start-adding-new-account {:type :generate}])
(rf/dispatch-sync [:set-in [:add-account :account :name] account-name])
(rf/dispatch [:wallet-legacy.accounts/add-new-account (native-module/sha3 password)]))
(defn assert-new-account-created
[]
(is (true? (some #(= (:name %) account-name)
@(rf/subscribe [:profile/wallet-accounts])))))
(defn logout!
[]
(rf/dispatch [:logout]))
(deftest initialize-app-test
(log/info "========= initialize-app-test ==================")
(rf-test/run-test-async
(rf/dispatch [:app-started])
(rf-test/wait-for
;; use initialize-view because it has the longest avg. time and
;; is dispatched by initialize-multiaccounts (last non-view event)
[:profile/get-profiles-overview-success]
(assert-app-initialized))))
(deftest create-account-test
(log/info "====== create-account-test ==================")
(rf-test/run-test-async
(initialize-app!) ; initialize app
(rf-test/wait-for
[:profile/get-profiles-overview-success]
(create-multiaccount!) ; create a multiaccount
(rf-test/wait-for ; wait for login
[::transport/messenger-started]
(assert-messenger-started)
(logout!)
(rf-test/wait-for [::logout/logout-method])))))
(deftest create-community-test
(log/info "====== create-community-test ==================")
(rf-test/run-test-async
(initialize-app!) ; initialize app
(rf-test/wait-for
[:profile/get-profiles-overview-success]
(create-multiaccount!) ; create a multiaccount
(rf-test/wait-for ; wait for login
[::transport/messenger-started]
(assert-messenger-started)
(rf/dispatch-sync [:legacy-only-for-e2e/open-create-community])
(doseq [[k v] (dissoc community :membership)]
(rf/dispatch-sync [:status-im.communities.core/create-field k v]))
(rf/dispatch [:status-im.communities.core/create-confirmation-pressed])
(rf-test/wait-for
[:status-im.communities.core/community-created]
(assert-community-created)
(logout!)
(rf-test/wait-for [::logout/logout-method]))))))
(deftest create-wallet-account-test
(log/info "====== create-wallet-account-test ==================")
(rf-test/run-test-async
(initialize-app!)
(rf-test/wait-for
[:profile/get-profiles-overview-success]
(create-multiaccount!) ; create a multiaccount
(rf-test/wait-for ; wait for login
[::transport/messenger-started]
(assert-messenger-started)
(create-new-account!) ; create a new account
(rf-test/wait-for
[:wallet-legacy.accounts/account-stored]
(assert-new-account-created) ; assert account was created
(logout!)
(rf-test/wait-for [::logout/logout-method]))))))
(deftest back-up-seed-phrase-test
(log/info "========= back-up-seed-phrase-test ==================")
(rf-test/run-test-async
(initialize-app!)
(rf-test/wait-for
[:profile/get-profiles-overview-success]
(create-multiaccount!)
(rf-test/wait-for
[::transport/messenger-started]
(assert-messenger-started)
(rf/dispatch-sync [:set-in [:my-profile/seed :step] :12-words]) ; display seed phrase to user
(rf/dispatch-sync [:my-profile/enter-two-random-words]) ; begin prompting user for seed words
(let [{:keys [mnemonic]} @(rf/subscribe [:profile/profile])
seed @(rf/subscribe [:my-profile/seed])
word1 (second (:first-word seed))
word2 (second (:second-word seed))]
(is (= 12 (count (string/split mnemonic #" ")))) ; assert 12-word seed phrase
(rf/dispatch-sync [:set-in [:my-profile/seed :word] word1])
(rf/dispatch-sync [:my-profile/set-step :second-word])
(rf/dispatch-sync [:set-in [:my-profile/seed :word] word2])
;; TODO: refactor (defn next-handler) & (defn enter-word) to improve test coverage?
(rf/dispatch [:my-profile/finish])
(rf-test/wait-for
[:my-profile/finish-success]
(is (nil? @(rf/subscribe [:mnemonic]))) ; assert seed phrase has been removed
(logout!)
(rf-test/wait-for [::logout/logout-method])))))))
(def multiaccount-name "Narrow Frail Lemming")
(def multiaccount-mnemonic
"tattoo ramp health green tongue universe style vapor become tape lava reason")
(def multiaccount-key-uid "0x694b8229524820a3a00a6e211141561d61b251ad99d6b65daf82a73c9a57697b")
(def chat-id
"0x0402905bed83f0bbf993cee8239012ccb1a8bc86907ead834c1e38476a0eda71414eed0e25f525f270592a2eebb01c9119a4ed6429ba114e51f5cb0a28dae1adfd")
(deftest one-to-one-chat-test
(log/info "========= one-to-one-chat-test ==================")
(rf-test/run-test-async
(initialize-app!)
(rf-test/wait-for
[:profile/get-profiles-overview-success]
(create-multiaccount!)
(rf-test/wait-for
[::transport/messenger-started]
(assert-messenger-started)
(rf/dispatch-sync [:chat.ui/start-chat chat-id]) ;; start a new chat
(rf-test/wait-for
[:chat/one-to-one-chat-created]
(rf/dispatch-sync [:chat/navigate-to-chat chat-id])
(is (= chat-id @(rf/subscribe [:chats/current-chat-id])))
(logout!)
(rf-test/wait-for [::logout/logout-method]))))))
(deftest delete-chat-test
(log/info "========= delete-chat-test ==================")
(rf-test/run-test-async
(initialize-app!)
(rf-test/wait-for
[:profile/get-profiles-overview-success]
(create-multiaccount!)
(rf-test/wait-for
[::transport/messenger-started]
(assert-messenger-started)
(rf/dispatch-sync [:chat.ui/start-chat chat-id]) ;; start a new chat
(rf-test/wait-for
[:chat/one-to-one-chat-created]
(rf/dispatch-sync [:chat/navigate-to-chat chat-id])
(is (= chat-id @(rf/subscribe [:chats/current-chat-id])))
(is @(rf/subscribe [:chats/chat chat-id]))
(rf/dispatch-sync [:chat.ui/show-remove-confirmation chat-id])
(rf/dispatch-sync [:chat.ui/remove-chat chat-id])
(logout!)
(rf-test/wait-for [::logout/logout-method]))))))
(deftest mute-chat-test
(log/info "========= mute-chat-test ==================")
(rf-test/run-test-async
(initialize-app!)
(rf-test/wait-for
[:profile/get-profiles-overview-success]
(create-multiaccount!)
(rf-test/wait-for
[::transport/messenger-started]
(assert-messenger-started)
(rf/dispatch-sync [:chat.ui/start-chat chat-id]) ;; start a new chat
(rf-test/wait-for
[:chat/one-to-one-chat-created]
(rf/dispatch-sync [:chat/navigate-to-chat chat-id])
(is (= chat-id @(rf/subscribe [:chats/current-chat-id])))
(is @(rf/subscribe [:chats/chat chat-id]))
(rf/dispatch-sync [:chat.ui/mute chat-id true constants/mute-till-unmuted])
(rf-test/wait-for
[:chat/mute-successfully]
(is @(rf/subscribe [:chats/muted chat-id]))
(rf/dispatch-sync [:chat.ui/mute chat-id false])
(rf-test/wait-for
[:chat/mute-successfully]
(is (not @(rf/subscribe [:chats/muted chat-id])))
(logout!)
(rf-test/wait-for [::logout/logout-method]))))))))
(deftest add-contact-test
(log/info "========= add-contact-test ==================")
(let
[compressed-key "zQ3shWj4WaBdf2zYKCkXe6PHxDxNTzZyid1i75879Ue9cX9gA"
public-key
"0x048a6773339d11ccf5fd81677b7e54daeec544a1287bd92b725047ad6faa9a9b9f8ea86ed5a226d2a994f5f46d0b43321fd8de7b7997a166e67905c8c73cd37cea"
primary-name "zQ3...9cX9gA"]
(rf-test/run-test-async
(initialize-app!)
(rf-test/wait-for
[:profile/get-profiles-overview-success]
(create-multiaccount!)
(rf-test/wait-for
[::transport/messenger-started]
(assert-messenger-started)
;; search for contact using compressed key
(rf/dispatch [:contacts/set-new-identity compressed-key])
(rf-test/wait-for
[:contacts/set-new-identity-success]
(let [new-identity @(rf/subscribe [:contacts/new-identity])]
(is (= public-key (:public-key new-identity)))
(is (= :valid (:state new-identity))))
;; click 'view profile' button
(rf/dispatch [:chat.ui/show-profile public-key])
(rf-test/wait-for
[:contacts/build-contact]
(rf-test/wait-for
[:contacts/contact-built]
(let [contact @(rf/subscribe [:contacts/current-contact])]
(is (= primary-name (:primary-name contact))))
(logout!)
(rf-test/wait-for [::logout/logout-method])))))))))
+10
View File
@@ -2,6 +2,7 @@
(:require
["@react-native-async-storage/async-storage" :default AsyncStorage]
["react-native" :refer (BackHandler)]
[native-module.core :as native-module]
[re-frame.core :as re-frame]
[status-im.keycard.card :as card]
[status-im.utils.deprecated-types :as types]
@@ -199,6 +200,15 @@
(fn [^js listener]
(.remove listener)))
(re-frame/reg-fx
:keycard/generate-name
(fn [{:keys [public-key on-success]}]
(native-module/gfycat-identicon-async
public-key
(fn [whisper-name photo-path]
(re-frame/dispatch
[on-success whisper-name photo-path])))))
(re-frame/reg-fx
:keycard/save-multiaccount-and-login
card/save-multiaccount-and-login)
+45 -24
View File
@@ -179,30 +179,37 @@
;; name might have been generated during recovery via passphrase
(get-in db [:intro-wizard :derived constants/path-whisper-keyword])
{:name name})]
(rf/merge cofx
{:db (-> db
(assoc-in [:keycard :setup-step] nil)
(dissoc :intro-wizard))}
(multiaccounts.create/on-multiaccount-created
{:recovered (or recovered (get-in db [:intro-wizard :recovering?]))
:derived {constants/path-wallet-root-keyword
{:public-key wallet-root-public-key
:address (eip55/address->checksum wallet-root-address)}
constants/path-whisper-keyword
{:public-key whisper-public-key
:address (eip55/address->checksum whisper-address)}
constants/path-default-wallet-keyword
{:public-key wallet-public-key
:address (eip55/address->checksum wallet-address)}}
:address address
:public-key public-key
:keycard-instance-uid instance-uid
:key-uid (address/normalized-hex key-uid)
:keycard-pairing pairing
:keycard-paired-on paired-on
:chat-key whisper-private-key}
encryption-public-key
{}))))
;; if a name is still `nil` we have to generate it before multiaccount's
;; creation otherwise spec validation will fail
(if (nil? name)
{:keycard/generate-name
{:public-key whisper-public-key
:on-success ::on-name-generated}}
(rf/merge cofx
{:db (-> db
(assoc-in [:keycard :setup-step] nil)
(dissoc :intro-wizard))}
(multiaccounts.create/on-multiaccount-created
{:recovered (or recovered (get-in db [:intro-wizard :recovering?]))
:derived {constants/path-wallet-root-keyword
{:public-key wallet-root-public-key
:address (eip55/address->checksum wallet-root-address)}
constants/path-whisper-keyword
{:public-key whisper-public-key
:address (eip55/address->checksum whisper-address)
:name name}
constants/path-default-wallet-keyword
{:public-key wallet-public-key
:address (eip55/address->checksum wallet-address)}}
:address address
:public-key public-key
:keycard-instance-uid instance-uid
:key-uid (address/normalized-hex key-uid)
:keycard-pairing pairing
:keycard-paired-on paired-on
:chat-key whisper-private-key}
encryption-public-key
{})))))
(rf/defn return-to-keycard-login
[{:keys [db] :as cofx}]
@@ -374,3 +381,17 @@
{:on-card-connected :keycard/load-recovering-key-screen
:handler (common/dispatch-event :keycard/import-multiaccount)}))
(rf/defn on-name-generated
{:events [::on-name-generated]
:interceptors [(re-frame/inject-cofx :random-guid-generator)
(re-frame/inject-cofx ::multiaccounts.create/get-signing-phrase)]}
[{:keys [db] :as cofx} whisper-name]
(rf/merge
cofx
{:db (update-in db
[:keycard :profile/profile]
(fn [multiacc]
(assoc multiacc
:recovered (get db :recovered-account?)
:name whisper-name)))}
(create-keycard-multiaccount)))
+10 -4
View File
@@ -265,10 +265,16 @@
constants/path-default-wallet]
(fn [result]
(let [derived-data (multiaccounts.create/normalize-derived-data-keys
(types/json->clj result))]
(reset! derived-acc
{:root-key root-data
:derived derived-data}))))))))
(types/json->clj result))
public-key (get-in derived-data [constants/path-whisper-keyword :public-key])]
(native-module/gfycat-identicon-async
public-key
(fn [name _]
(let [derived-data-extended
(update derived-data constants/path-whisper-keyword assoc :name name)]
(reset! derived-acc
{:root-key root-data
:derived derived-data-extended})))))))))))
(when (= password (get @state :password))
(swap! state assoc-in [:application-info :paired?] true)
(later #(on-success (str (rand-int 10000000))))))
+234
View File
@@ -0,0 +1,234 @@
(ns status-im.multiaccounts.core
(:require
[clojure.string :as string]
[native-module.core :as native-module]
[quo.foundations.colors :as colors]
[re-frame.core :as re-frame]
[react-native.platform :as platform]
[status-im.bottom-sheet.events :as bottom-sheet]
[status-im.contact.db :as contact.db]
[status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im.utils.gfycat.core :as gfycat]
[status-im2.common.theme.core :as theme]
[status-im2.constants :as constants]
[status-im2.contexts.shell.jump-to.utils :as shell.utils]
[status-im2.setup.hot-reload :as hot-reload]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
;; validate that the given mnemonic was generated from Status Dictionary
(re-frame/reg-fx
::validate-mnemonic
(fn [[passphrase callback]]
(native-module/validate-mnemonic passphrase callback)))
(defn displayed-name
"Use preferred name, display-name, name or alias in that order"
[{:keys [name display-name preferred-name alias public-key ens-verified primary-name]}]
(let [display-name (if (string/blank? display-name) nil display-name)
preferred-name (if (string/blank? preferred-name) nil preferred-name)
ens-name (or preferred-name
display-name
name)]
;; Preferred name is our own otherwise we make sure it's verified
(if (or preferred-name (and ens-verified name))
ens-name
(or display-name primary-name alias (gfycat/generate-gfy public-key)))))
(defn contact-by-identity
[contacts contact-identity]
(or (get contacts contact-identity)
(contact.db/public-key->new-contact contact-identity)))
(defn contact-two-names-by-identity
[contact profile contact-identity]
(let [{:keys [public-key preferred-name display-name]} profile
{:keys [primary-name secondary-name]} contact
me? (= public-key contact-identity)]
(if me?
[(cond
(not (string/blank? preferred-name)) preferred-name
(not (string/blank? display-name)) display-name
(not (string/blank? primary-name)) primary-name
:else (gfycat/generate-gfy contact-identity))]
[primary-name secondary-name])))
(defn displayed-photo
[{:keys [images]}]
(or (:thumbnail images)
(:large images)
(first images)))
(re-frame/reg-fx
::webview-debug-changed
(fn [value]
(when platform/android?
(native-module/toggle-webview-debug value))))
(re-frame/reg-fx
::blank-preview-flag-changed
(fn [flag]
(native-module/set-blank-preview-flag flag)))
(rf/defn confirm-wallet-set-up
{:events [:multiaccounts.ui/wallet-set-up-confirmed]}
[cofx]
(multiaccounts.update/multiaccount-update cofx
:wallet-set-up-passed?
true
{}))
(rf/defn switch-webview-debug
{:events [:multiaccounts.ui/switch-webview-debug]}
[{:keys [db] :as cofx} value]
(rf/merge cofx
{::webview-debug-changed value}
(multiaccounts.update/multiaccount-update
:webview-debug
(boolean value)
{})))
(rf/defn switch-preview-privacy-mode
{:events [:multiaccounts.ui/preview-privacy-mode-switched]}
[{:keys [db] :as cofx} private?]
(rf/merge cofx
{::blank-preview-flag-changed private?}
(multiaccounts.update/multiaccount-update
:preview-privacy?
(boolean private?)
{})))
(rf/defn switch-webview-permission-requests?
{:events [:multiaccounts.ui/webview-permission-requests-switched]}
[cofx enabled?]
(multiaccounts.update/multiaccount-update
cofx
:webview-allow-permission-requests?
(boolean enabled?)
{}))
(rf/defn switch-default-sync-period
{:events [:multiaccounts.ui/default-sync-period-switched]}
[cofx value]
(multiaccounts.update/multiaccount-update
cofx
:default-sync-period
value
{}))
(rf/defn switch-preview-privacy-mode-flag
[{:keys [db]}]
(let [private? (get-in db [:profile/profile :preview-privacy?])]
{::blank-preview-flag-changed private?}))
(re-frame/reg-fx
:multiaccounts.ui/switch-theme-fx
(fn [[theme-type view-id reload-ui?]]
(let [[theme status-bar-theme nav-bar-color]
;; Status bar theme represents status bar icons colors, so opposite to app theme
(if (or (= theme-type constants/theme-type-dark)
(and (= theme-type constants/theme-type-system)
(theme/device-theme-dark?)))
[:dark :light colors/neutral-100]
[:light :dark colors/white])]
(theme/set-theme theme)
(re-frame/dispatch [:change-shell-status-bar-style
(if (shell.utils/home-stack-open?) status-bar-theme :light)])
(when reload-ui?
(rf/dispatch [:dissmiss-all-overlays])
(hot-reload/reload)
(when-not (= view-id :shell-stack)
(re-frame/dispatch [:change-shell-nav-bar-color nav-bar-color]))))))
(rf/defn switch-appearance
{:events [:multiaccounts.ui/appearance-switched]}
[cofx theme]
(rf/merge cofx
{:multiaccounts.ui/switch-theme-fx [theme :appearance true]}
(multiaccounts.update/multiaccount-update :appearance theme {})))
(rf/defn switch-theme
{:events [:multiaccounts.ui/switch-theme]}
[cofx theme view-id]
(let [theme (or theme
(get-in cofx [:db :profile/profile :appearance])
constants/theme-type-dark)]
{:multiaccounts.ui/switch-theme-fx [theme view-id false]}))
(rf/defn switch-profile-picture-show-to
{:events [:multiaccounts.ui/profile-picture-show-to-switched]}
[cofx id]
(rf/merge cofx
{:json-rpc/call [{:method "wakuext_changeIdentityImageShowTo"
:params [id]
:on-success #(log/debug "picture settings changed successfully")}]}
(multiaccounts.update/optimistic :profile-pictures-show-to id)))
(rf/defn switch-appearance-profile
{:events [:multiaccounts.ui/appearance-profile-switched]}
[cofx id]
(multiaccounts.update/multiaccount-update cofx :profile-pictures-visibility id {}))
(defn clean-path
[path]
(if path
(string/replace-first path #"file://" "")
(log/warn "[native-module] Empty path was provided")))
(rf/defn save-profile-picture
{:events [::save-profile-picture]}
[cofx path ax ay bx by]
(let [key-uid (get-in cofx [:db :profile/profile :key-uid])]
(rf/merge cofx
{:json-rpc/call [{:method "multiaccounts_storeIdentityImage"
:params [key-uid (clean-path path) ax ay bx by]
;; NOTE: In case of an error we can show a toast error
:on-success #(re-frame/dispatch [::update-local-picture %])}]}
(bottom-sheet/hide-bottom-sheet-old))))
(rf/defn save-profile-picture-from-url
{:events [::save-profile-picture-from-url]}
[cofx url]
(let [key-uid (get-in cofx [:db :profile/profile :key-uid])]
(rf/merge cofx
{:json-rpc/call [{:method "multiaccounts_storeIdentityImageFromURL"
:params [key-uid url]
:on-error #(log/error "::save-profile-picture-from-url error" %)
:on-success #(re-frame/dispatch [::update-local-picture %])}]}
(bottom-sheet/hide-bottom-sheet-old))))
(comment
(re-frame/dispatch
[::save-profile-picture-from-url
"https://lh3.googleusercontent.com/XuKjNm3HydsaxbPkbpGs9YyCKhn5QQk5oDC8XF2jzmPyYXeZofxFtfUDZuQ3EVmacS_BlBKzbX2ypm37YNX3n1fDJA3WndeFcPsp7Z0=w600"]))
(rf/defn delete-profile-picture
{:events [::delete-profile-picture]}
[cofx name]
(let [key-uid (get-in cofx [:db :profile/profile :key-uid])]
(rf/merge cofx
{:json-rpc/call [{:method "multiaccounts_deleteIdentityImage"
:params [key-uid]
;; NOTE: In case of an error we could fallback to previous image in
;; UI with a toast error
:on-success #(log/info "[multiaccount] Delete profile image" %)}]}
(multiaccounts.update/optimistic :images nil)
(bottom-sheet/hide-bottom-sheet-old))))
(rf/defn get-profile-picture
[cofx]
(let [key-uid (get-in cofx [:db :profile/profile :key-uid])]
{:json-rpc/call [{:method "multiaccounts_getIdentityImages"
:params [key-uid]
:on-success #(re-frame/dispatch [::update-local-picture %])}]}))
(rf/defn store-profile-picture
{:events [::update-local-picture]}
[cofx pics]
(multiaccounts.update/optimistic cofx :images pics))
(comment
;; Test seed for Dim Venerated Yaffle, it's not here by mistake, this is just a test account
(native-module/validate-mnemonic
"rocket mixed rebel affair umbrella legal resemble scene virus park deposit cargo"
prn))
+10 -9
View File
@@ -2,6 +2,7 @@
(:require
[native-module.core :as native-module]
[re-frame.core :as re-frame]
[status-im.multiaccounts.core :as multiaccounts]
[status-im.wallet.core :as wallet]
[status-im2.common.keychain.events :as keychain]
[status-im2.db :as db]
@@ -29,15 +30,15 @@
[{:keys [db] :as cofx} {:keys [auth-method logout?]}]
(let [key-uid (get-in db [:profile/profile :key-uid])]
(rf/merge cofx
{:set-root :progress
:chat.ui/clear-inputs nil
:shell/reset-state nil
:hide-popover nil
::logout nil
:profile.settings/webview-debug-changed false
:keychain/clear-user-password key-uid
:profile/get-profiles-overview #(rf/dispatch
[:profile/get-profiles-overview-success %])}
{:set-root :progress
:chat.ui/clear-inputs nil
:shell/reset-state nil
:hide-popover nil
::logout nil
::multiaccounts/webview-debug-changed false
:keychain/clear-user-password key-uid
:profile/get-profiles-overview #(rf/dispatch
[:profile/get-profiles-overview-success %])}
(keychain/save-auth-method key-uid auth-method)
(wallet/clear-timeouts)
(initialize-app-db))))
@@ -26,6 +26,12 @@
constants/path-default-wallet]
(fn [result]
(let [derived-data (multiaccounts.create/normalize-derived-data-keys
(types/json->clj result))]
(re-frame/dispatch [success-event root-data derived-data])))))))))
(types/json->clj result))
public-key (get-in derived-data [constants/path-whisper-keyword :public-key])]
(native-module/gfycat-identicon-async
public-key
(fn [name _]
(let [derived-data-extended
(update derived-data constants/path-whisper-keyword assoc :name name)]
(re-frame/dispatch [success-event root-data derived-data-extended]))))))))))))
+2 -4
View File
@@ -1,9 +1,9 @@
(ns status-im.multiaccounts.update.core
(:require
[status-im.ethereum.ens :as ens]
[status-im.utils.deprecated-types :as types]
[status-im2.constants :as constants]
[taoensso.timbre :as log]
[utils.ens.core :as utils.ens]
[utils.re-frame :as rf]))
(rf/defn send-contact-update
@@ -23,9 +23,7 @@
(when-let [new-name (and account (or preferred-name display-name name))]
(rf/merge
cofx
{:db (assoc-in db
[:profile/profile :ens-name?]
(utils.ens/is-valid-eth-name? new-name))
{:db (assoc-in db [:profile/profile :ens-name?] (ens/is-valid-eth-name? new-name))
:json-rpc/call [{:method "multiaccounts_updateAccount"
:params [(assoc account :name new-name)]
:on-success #(log/debug "sent multiaccount update")}]}))))
+2 -2
View File
@@ -5,8 +5,8 @@
[status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im.ui.components.list-selection :as list-selection]
[status-im.ui.components.react :as react]
[utils.re-frame :as rf]
[utils.universal-links :as universal-links]))
[status-im.utils.universal-links.utils :as universal-links]
[utils.re-frame :as rf]))
(re-frame/reg-fx
:copy-to-clipboard
+5 -4
View File
@@ -2,6 +2,7 @@
(:require
[re-frame.core :as re-frame]
[status-im.group-chats.core :as group-chats]
[status-im.router.core :as router]
[status-im.utils.utils :as utils]
[status-im2.navigation.events :as navigation]
[taoensso.timbre :as log]
@@ -110,7 +111,7 @@
(rf/defn on-scan
{:events [::on-scan-success]}
[{:keys [db]} uri]
{:router/handle-uri {:chain (chain/chain-keyword db)
:chats (get db :chats)
:uri uri
:cb #(re-frame/dispatch [::match-scanned-value %])}})
{::router/handle-uri {:chain (chain/chain-keyword db)
:chats (get db :chats)
:uri uri
:cb #(re-frame/dispatch [::match-scanned-value %])}})
@@ -1,18 +1,18 @@
(ns status-im2.common.router
(ns status-im.router.core
(:require
[bidi.bidi :as bidi]
[clojure.string :as string]
[native-module.core :as native-module]
[re-frame.core :as re-frame]
[status-im.ethereum.eip681 :as eip681]
[status-im.ethereum.ens :as ens]
[status-im.ethereum.stateofus :as stateofus]
[status-im.utils.wallet-connect :as wallet-connect]
[status-im2.constants :as constants]
[status-im2.contexts.chat.events :as chat.events]
[taoensso.timbre :as log]
[utils.address :as address]
[utils.ens.core :as utils.ens]
[utils.ens.stateofus :as stateofus]
[utils.ethereum.chain :as chain]
[utils.ethereum.eip.eip681 :as eip681]
[utils.security.core :as security]
[utils.transforms :as transforms]
[utils.url :as url]
@@ -81,7 +81,7 @@
;; fragment is the one after `#`, usually user-id, ens-name, community-id
fragment (parse-fragment uri)
ens? (utils.ens/is-valid-eth-name? fragment)
ens? (ens/is-valid-eth-name? fragment)
{:keys [handler route-params] :as parsed}
(assoc (bidi/match-route routes uri-without-equal-in-path)
@@ -119,7 +119,7 @@
(defn match-contact-async
[chain {:keys [user-id ens-name]} callback]
(let [valid-public-key? (and (validators/valid-public-key? user-id)
(not= user-id utils.ens/default-key))
(not= user-id ens/default-key))
valid-compressed-key? (validators/valid-compressed-key? user-id)]
(cond
valid-public-key?
@@ -230,7 +230,7 @@
(let [{:keys [paths ens-names]}
(reduce (fn [acc path]
(let [address (get-in message path)]
(if (utils.ens/is-valid-eth-name? address)
(if (ens/is-valid-eth-name? address)
(-> acc
(update :paths conj path)
(update :ens-names conj address))
@@ -318,6 +318,9 @@
(url/url? uri)
(cb (match-browser-string uri))
(wallet-connect/url? uri)
(cb {:type :wallet-connect :data uri})
(string/starts-with? uri constants/local-pairing-connection-string-identifier)
(cb {:type :localpairing :data uri})
@@ -326,6 +329,6 @@
:data uri}))))
(re-frame/reg-fx
:router/handle-uri
::handle-uri
(fn [{:keys [chain chats uri cb]}]
(handle-uri chain chats uri cb)))
@@ -1,7 +1,7 @@
(ns status-im2.common.router-test
(ns status-im.router.core-test
(:require
[cljs.test :refer [are deftest]]
[status-im2.common.router :as router]))
[status-im.router.core :as router]))
(def public-key
"0x04fbce10971e1cd7253b98c7b7e54de3729ca57ce41a2bfb0d1c4e0a26f72c4b6913c3487fa1b4bb86125770f1743fb4459da05c1cbe31d938814cfaf36e252073")
-2
View File
@@ -5,10 +5,8 @@
[clojure.string :as string]
[shadow.test :as st]
[shadow.test.env :as env]
status-im.subs.root
status-im2.setup.i18n-resources
[status-im2.setup.interceptors :as interceptors]
status-im2.subs.root
[utils.re-frame :as rf]))
(defonce repl? (atom false))
+1 -1
View File
@@ -4,7 +4,7 @@
[status-im.pairing.core :as pairing]
[status-im.stickers.core :as stickers]
status-im.transport.shh
[status-im2.common.universal-links :as universal-links]
[status-im.utils.universal-links.core :as universal-links]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
@@ -6,13 +6,13 @@
[quo.theme :as theme]
[re-frame.core :as re-frame.core]
[react-native.core :as rn]
[status-im.ethereum.ens :as ens]
[status-im.multiaccounts.core :as multiaccounts]
[status-im.ui.components.chat-icon.styles :as styles]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.icons.icons :as icons]
[status-im.ui.screens.chat.photos :as photos]
[status-im.ui.screens.profile.visibility-status.utils :as visibility-status-utils]
[status-im2.contexts.profile.utils :as profile.utils]
[utils.ens.core :as utils.ens]))
[status-im.ui.screens.profile.visibility-status.utils :as visibility-status-utils]))
;;TODO REWORK THIS NAMESPACE
@@ -78,7 +78,7 @@
:indicator-color "#000000"
:color (get text-style :color)
:length 2
:ring? (not (utils.ens/is-valid-eth-name? full-name))
:ring? (not (ens/is-valid-eth-name? full-name))
:ring-width 2})}
{:size size}]
[photos/photo photo-path {:size size}])
@@ -161,14 +161,14 @@
(defn contact-icon-view
[contact {:keys [container] :as styles}]
[rn/view container
[photos/photo (profile.utils/photo contact) styles]])
[photos/photo (multiaccounts/displayed-photo contact) styles]])
(defn contact-icon-contacts-tab
[profile]
[{:keys [primary-name] :as contact}]
[rn/view styles/container-chat-list
[quo/user-avatar
{:full-name (profile.utils/displayed-name profile)
:profile-picture (profile.utils/photo profile)
{:full-name primary-name
:profile-picture (multiaccounts/displayed-photo contact)
:size :small
:status-indicator? false}]])
@@ -221,7 +221,7 @@
:indicator-color "#000000"
:color (get-in styles [:default-chat-icon-text :color])
:length 2
:ring? (not (utils.ens/is-valid-eth-name? name))
:ring? (not (ens/is-valid-eth-name? name))
:ring-width 2})}
photo-path)]
[rn/view (:container styles)
@@ -2,9 +2,9 @@
(:require
[re-frame.core :as re-frame]
[status-im.ui.components.react :as react]
[status-im.utils.universal-links.utils :as universal-links]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.universal-links :as universal-links]))
[utils.re-frame :as rf]))
(re-frame/reg-fx
::share
@@ -92,7 +92,7 @@
:container-margin-bottom 8
:on-press
#(re-frame/dispatch
[:profile.settings/change-webview-debug (not webview-debug)])
[:multiaccounts.ui/switch-webview-debug (not webview-debug)])
:accessory :switch
:active webview-debug}
{:size :small
@@ -11,7 +11,7 @@
(defn button
[label icon theme selected?]
[react/touchable-highlight
{:on-press #(re-frame/dispatch [:profile.settings/change-appearance theme])}
{:on-press #(re-frame/dispatch [:multiaccounts.ui/appearance-switched theme])}
[react/view
(merge {:align-items :center :padding 8 :border-radius 20}
(when selected?
@@ -3,6 +3,7 @@
[react-native.core :as rn]
[reagent.core :as reagent]
[status-im.communities.core :as communities]
[status-im.multiaccounts.core :as multiaccounts]
[status-im.ui.components.chat-icon.screen :as chat-icon]
[status-im.ui.components.core :as quo]
[status-im.ui.components.list.item :as list.item]
@@ -10,7 +11,6 @@
[status-im.ui.components.topbar :as topbar]
[status-im.ui.components.unviewed-indicator :as unviewed-indicator]
[status-im2.constants :as constants]
[status-im2.contexts.profile.utils :as profile.utils]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
@@ -69,7 +69,7 @@
:accessibility-label :member-item
:icon [chat-icon/profile-photo-plus-dot-view
{:public-key public-key
:photo-path (profile.utils/photo member)}]
:photo-path (multiaccounts/displayed-photo member)}]
:accessory (when (not= public-key my-public-key)
[quo/button
{:on-press
@@ -1,13 +1,13 @@
(ns status-im.ui.screens.contacts-list.views
(:require
[re-frame.core :as re-frame]
[status-im.multiaccounts.core :as multiaccounts]
[status-im.ui.components.chat-icon.screen :as chat-icon.screen]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.invite.views :as invite]
[status-im.ui.components.list.item :as list.item]
[status-im.ui.components.list.views :as list.views]
[status-im.ui.components.react :as react]
[status-im2.contexts.profile.utils :as profile.utils]
[utils.i18n :as i18n])
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
@@ -21,7 +21,7 @@
{:public-key public-key
:full-name primary-name
:customization-color (or customization-color :primary)
:photo-path (profile.utils/photo contact)}]
:photo-path (multiaccounts/displayed-photo contact)}]
:chevron true
:on-press #(re-frame/dispatch [:chat.ui/show-profile public-key])}]))

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