Compare commits

..
Author SHA1 Message Date
Dario Gabriel Lipicar e03757b4d4 update go version 2024-10-17 10:19:16 -03:00
pavloburykh 46501d611d update go version 2024-10-16 16:31:43 +03:00
pavloburykh f0e69b1f45 update go verion to test go 5945 2024-10-16 13:09:36 +03:00
Tetiana Churikova 6a33c4723a e2e: balance test (#21440) 2024-10-16 11:26:57 +02:00
Lungu Cristian 6a26ebafa3 fix: clean up state after unmounting (#21435) 2024-10-16 13:27:01 +05:30
Mohsen 3c55fc1fa2 [#21335] feat: add dApp analytics (#21379) 2024-10-15 20:22:05 +03:00
flexsurfer 15a1a15c08 Keycard - Allow user to migrate existing Profile [Part 2] (#21408) 2024-10-15 16:48:47 +02:00
Mohsen 9e43aa04c7 [#21396] chore: rename crypto buy phrases (#21404) 2024-10-15 14:15:07 +03:00
flexsurfer 18f7d05a61 Feature/keycard create pin #21368 (#21413) 2024-10-14 10:33:18 +02:00
frank 70e325fd2b fix_: community sync (#21327)
https://github.com/status-im/status-go/compare/12c4e869...3f613a92
2024-10-12 09:02:33 +08:00
flexsurfer fba631c044 [#21420] Keycard - Auth with password (#21422) 2024-10-11 18:35:39 +02:00
richΛrdandPrem Chaitanya Prathi 598617cf2b chore: fix mismatched staging fleet peerID and take filter loop fix and updates from status-go (#21386)
https://github.com/status-im/status-go/compare/12c4e869...0f4f2f4c

Co-authored-by: Prem Chaitanya Prathi <chaitanyaprem@gmail.com>
2024-10-11 22:07:12 +08:00
Siddarth Kumar 66f177370a nix: fix sha256 of nixpkgs release 24-05 (#21416) 2024-10-11 15:16:25 +05:30
Volodymyr Kozieiev ad7d58788f Missed key added to token returned from assets list (#21402) 2024-10-10 11:55:03 +01:00
Alexander ad4ea0783f A black square background at the end of Swap confirmation screen (#21372) 2024-10-09 15:37:38 -03:00
Brian Sztamfater 5c44cb6399 fix(swap): display very small max values, fix scientific notation, handle decimal mismatch when changing tokens, display long numbers (#21388)
Signed-off-by: Brian Sztamfater <brian@status.im>
2024-10-09 13:49:45 -03:00
Ulises Manuel a9e0b3dd6b fix(wallet): Exception when navigating back from route generation (#21381)
- Slightly improved code style
2024-10-08 19:05:29 -06:00
Ulises Manuel 131da5f444 fix(community): Last message overlapped with "join community" button (#21370) 2024-10-08 18:50:29 -06:00
Brian Sztamfater 874ececc47 fix(swap): update max value when the account is switched (#21395)
Signed-off-by: Brian Sztamfater <brian@status.im>
2024-10-08 18:16:38 -03:00
Yevheniia Berdnyk 5f1d74a77a e2e: added tests for fallback flow 2024-10-08 17:49:27 +03:00
Brian Sztamfater 00b0d75fc4 fix(wallet): mercuryo on-ramp (#20962)
Signed-off-by: Brian Sztamfater <brian@status.im>
2024-10-08 09:43:56 -03:00
Siddarth Kumar 4b93c8ff43 nix: use multiple nixpkgs sources (#21374) 2024-10-08 17:51:30 +05:30
flexsurfer 80ad2b8fca [#21318] Keycard - Allow user to initiate Profile key pair migration on an empty Keycard (#21359) 2024-10-07 20:15:19 +02:00
Alexander a8bc93eb17 Account selection drawer with balance tag (#21356) 2024-10-07 14:57:23 -03:00
Parvesh Monu 9d3fc4286e fix syncing screen only shows paired label after the restart (#21315)
https://github.com/status-im/status-go/compare/effde33d...12c4e869
2024-10-07 22:35:46 +08:00
Siddarth Kumar 16fba373c9 nix: add preBuild phase for generating files (#21390)
https://github.com/status-im/status-go/compare/effde33d...ba37c32c

https://github.com/status-im/status-go/pull/5920

Signed-off-by: Jakub Sokołowski <jakub@status.im>
2024-10-07 17:19:37 +05:30
Yevheniia Berdnyk 9c3ff8c88c e2e: fixes for edit/delete message in tests 2024-10-04 17:04:18 +03:00
flexsurfer 2525620730 Update import-assets.md typo 2024-10-04 14:32:46 +02:00
Ulises Manuel 6dde1d7b74 fix(onboarding): Increase hit area for all selectors (#21355)
* Increase hit-slop for all selectors

* Remove unnecessary `:f>` wrapper
2024-10-03 13:35:47 -06:00
Sean Hagstrom 1691e2fe34 chore: remove run-tests from wallet-send event tests (#21373) 2024-10-03 12:56:45 +01:00
Parvesh Monu b536d20713 Add syncing notification to System Tab (#21314) 2024-10-03 07:11:10 +05:30
Ulises Manuel ca3720be0a fix(communities): token-gated community info text (#21338) 2024-10-02 14:57:45 -06:00
Ulises Manuel 63d0aaca2a feat(chats): Implement a simpler chat screen (#21313)
* Change `defonce ^:const` -> `def ^:const` in `status-im.contexts.chat.messenger.messages.list.view` to be more REPL friendly

* Remove animations from `status-im.contexts.chat.messenger.messages.pin.banner.style`

* Simplify placeholder for messages

* Replace shared-value with a ratom usage since no animations were performed.

* Fix the style for the new designs and remove now unnecessary animations

* Improve chat component implementation

    The previous implementation has two main problems:
        1. It is creating atoms and ratoms in non-form-2-components.
        2. Conditionally uses a hook: `rn/use-mount` in `lazy-chat-screen`.

    The new implementation fixes the previous problems, improves the readability and passes only the props needed to its children.

* Remove action buttons from the chat screen

* Remove now unused code

* Replace reanimated/view -> rn/view where no animations are made
2024-10-02 14:35:23 -06:00
Brian Sztamfater 3a4b58f32f feat(swap): remove feature flag (#21363)
Signed-off-by: Brian Sztamfater <brian@status.im>
2024-10-02 12:55:32 -03:00
Lungu Cristian d748ccdef9 Fix WalletConnect sessions disappearing (#21350)
* feat: moved disconnect logic to function

* ref: moved rpc calls to rpc ns

* ref: moved session approval logic to function

* fix: small fixes for sessions

* test: wallet-connect/on-session-delete event test

* test: added event tests for wc sessions

* fix: require sessions events ns

* fix: the wallet was loaded after wc sometimes
2024-10-02 18:16:20 +03:00
Lungu Cristian 97aff27980 Dapps Typed Data request improvements (#21333)
* feat: added type data flattening + tests

* feat: added typed data flattened ui

* ref: moved typed data utils into their own ns

* ref: small changes & formatting

* feat: show typed data view only for typed data requests

* ref: moved typed-data? into sub

* fix: tests and styling
2024-10-02 13:50:38 +03:00
Brian Sztamfater 0c3e836272 feat: disable swap and bridge options on testnet mode (#21343)
Signed-off-by: Brian Sztamfater <brian@status.im>
2024-09-30 13:13:15 -03:00
Mohsen 60fc4f1b9a [#21132] refactor: improve send transaction content in wc flow (#21266) 2024-09-30 15:11:56 +03:00
Siddarth Kumar b97e2df2fb chore: nuke personal INFURA token 2024-09-30 17:09:35 +05:30
Alexander f539bb5ca2 Swap flows (launch from home / launch from account) (#21269)
* Swap flows (launch from home / launch from account)

* Fixes

* Lint fix
2024-09-30 10:44:25 +02:00
144 changed files with 2984 additions and 1625 deletions
+1 -1
View File
@@ -19,5 +19,5 @@
Make sure to compress images before using into project.
```
make shell
./scrips/compress_image.sh image_path
./scripts/compress_image.sh image_path
```
-1
View File
@@ -51,7 +51,6 @@ in {
};
# Package version adjustments
gradle = super.gradle_8.override { java = super.openjdk17_headless; };
nodejs = super.nodejs_20;
ruby = super.ruby_3_1;
yarn = super.yarn.override { nodejs = super.nodejs_20; };
+18 -2
View File
@@ -6,8 +6,16 @@ let
# For testing local version of nixpkgs
#nixpkgsSrc = (import <nixpkgs> { }).lib.cleanSource "/home/jakubgs/work/nixpkgs";
# We use a commit from the unstable channel of nixpkgs for gradle 8.8
# We follow release 24-05 of nixpkgs
# https://github.com/NixOS/nixpkgs/releases/tag/24.05
nixpkgsSrc = builtins.fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/df27247e6f3e636c119e2610bf12d38b5e98cc79.tar.gz";
sha256 = "sha256:0bbvimk7xb7akrx106mmsiwf9nzxnssisqmqffla03zz51d0kz2n";
};
# FIXME: remove this additional source when nixpkgs includes gradle 8.8 in stable channel
# We use a commit from the unstable channel of nixpkgs for gradle 8.8
gradleNixpkgsSrc = builtins.fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/48d567fc7b299de90f70a48e4263e31f690ba03e.tar.gz";
sha256 = "sha256:0zynbk52khdfhg4qfv26h3r5156xff5p0cga2cin7b07i7lqminh";
};
@@ -21,6 +29,14 @@ let
# Override some packages and utilities
pkgsOverlay = import ./overlay.nix;
# FIXME: remove this additional source when nixpkgs includes gradle 8.8 in stable channel
gradleOverlay = final: prev: {
gradle = (import gradleNixpkgsSrc {
inherit (prev) system;
config = defaultConfig // config;
}).gradle_8.override { java = prev.openjdk17_headless; };
};
# Fix for lack of Android SDK for M1 Macs.
systemOverride = let
inherit (builtins) currentSystem getEnv;
@@ -35,5 +51,5 @@ in
(import nixpkgsSrc) {
config = defaultConfig // config;
system = systemOverride;
overlays = [ pkgsOverlay ];
overlays = [ pkgsOverlay gradleOverlay ];
}
+7 -1
View File
@@ -1,4 +1,5 @@
{ lib, stdenv, meta, source, buildGoPackage }:
{ lib, stdenv, meta, source, buildGoPackage,
go-bindata, mockgen, protoc-gen-go, protobuf3_20 }:
buildGoPackage {
pname = source.repo;
@@ -7,6 +8,10 @@ buildGoPackage {
inherit meta;
inherit (source) src goPackagePath;
nativeBuildInputs = [
go-bindata mockgen protoc-gen-go protobuf3_20
];
phases = ["unpackPhase" "configurePhase" "buildPhase"];
# https://pkg.go.dev/net#hdr-Name_Resolution
@@ -22,6 +27,7 @@ buildGoPackage {
preBuild = ''
pushd go/src/$goPackagePath
go run cmd/library/*.go > $NIX_BUILD_TOP/main.go
make generate SHELL=$SHELL GO111MODULE=on GO_GENERATE_CMD='go generate'
popd
'';
+12 -3
View File
@@ -1,5 +1,6 @@
{ callPackage, lib, buildGoPackage
, androidPkgs, openjdk, gomobile, xcodeWrapper, removeReferencesTo
, go-bindata, mockgen, protobuf3_20, protoc-gen-go
, meta
, source
, platform ? "android"
@@ -15,7 +16,7 @@ let
isAndroid = platform == "android";
enforceXCodeAvailable = callPackage ./enforceXCodeAvailable.nix { };
in buildGoPackage {
in buildGoPackage rec {
pname = source.repo;
version = "${source.cleanVersion}-${source.shortRev}-${platform}";
@@ -27,8 +28,9 @@ in buildGoPackage {
__noChroot = isIOS;
extraSrcPaths = [ gomobile ];
nativeBuildInputs = [ gomobile removeReferencesTo ]
++ optional isAndroid openjdk
nativeBuildInputs = [
gomobile removeReferencesTo go-bindata mockgen protoc-gen-go protobuf3_20
] ++ optional isAndroid openjdk
++ optional isIOS xcodeWrapper;
ldflags = goBuildLdFlags;
@@ -44,6 +46,13 @@ in buildGoPackage {
# TODO: try removing when go is upgraded to 1.22
GODEBUG = "netdns=cgo+2";
preBuild = ''
echo 'Generate static files'
pushd go/src/$goPackagePath
make generate SHELL=$SHELL GO111MODULE=on GO_GENERATE_CMD='go generate'
popd
'';
buildPhase = ''
runHook preBuild
echo -e "\nBuilding $pname for: ${concatStringsSep "," targets}"
+3 -3
View File
@@ -180,17 +180,17 @@
},
"shards.staging": {
"tcp/p2p/waku/boot": {
"boot-01.do-ams3.shards.staging": "/dns4/boot-01.do-ams3.shards.staging.status.im/tcp/30303/p2p/16Uiu2HAmEqqio4UR1SWqAc7KY19t6qyDvtmyjreZpzUBJvb4u65R",
"boot-01.do-ams3.shards.staging": "/dns4/boot-01.do-ams3.shards.staging.status.im/tcp/30303/p2p/16Uiu2HAmQE7FXQc6iZHdBzYfw3qCSDa9dLc1wsBJKoP4aZvztq2d",
"boot-01.gc-us-central1-a.shards.staging": "/dns4/boot-01.gc-us-central1-a.shards.staging.status.im/tcp/30303/p2p/16Uiu2HAmGAA54bBTE78MYidSy3P7Q9yAWFNTAEReJYD69VRvtL5r",
"boot-01.ac-cn-hongkong-c.shards.staging": "/dns4/boot-01.ac-cn-hongkong-c.shards.staging.status.im/tcp/30303/p2p/16Uiu2HAmNTpGnyZ8W1BK2sXEmgSCNWiyDKgRU3NBR2DXST2HzxRU"
},
"enr/p2p/waku/boot": {
"boot-01.do-ams3.shards.staging": "enr:-QEQuEDsh6FgAb_36cReaX7W4gWx_7_GNpsUki7bXMoMrrrWij5pDEyV3guR-urDW_6GJTAzpQiJV61F-CfNn_NxPbY-AYJpZIJ2NIJpcISPxvrpim11bHRpYWRkcnO4YAAtNihib290LTAxLmRvLWFtczMuc2hhcmRzLnN0YWdpbmcuc3RhdHVzLmltBnZfAC82KGJvb3QtMDEuZG8tYW1zMy5zaGFyZHMuc3RhZ2luZy5zdGF0dXMuaW0GAbveA4Jyc40AEAUAAQAgAEAAgAEAiXNlY3AyNTZrMaEDIH8BcuEzgnmwPQTu7BPYyg4u4om7K9qekKA2gT_H2wSDdGNwgnZfg3VkcIIjKIV3YWt1Mg0",
"boot-01.do-ams3.shards.staging": "enr:-QEQuEBuiQgFlJNcv255042zwyl4pOBOivakX8N30Dr9vaaEU2q8-7N4GVY4Hk87iEKELjlIXTpE9Wj6EQq1lrBuc7ayAYJpZIJ2NIJpcISPxvrpim11bHRpYWRkcnO4YAAtNihib290LTAxLmRvLWFtczMuc3RhdHVzLnN0YWdpbmcuc3RhdHVzLmltBnZfAC82KGJvb3QtMDEuZG8tYW1zMy5zdGF0dXMuc3RhZ2luZy5zdGF0dXMuaW0GAbveA4Jyc40AEAUAAQAgAEAAgAEAiXNlY3AyNTZrMaEDq-yGgpuoUG6NKkbIDRmrMiT-bEVzFlpWLEK_rF3yKUaDdGNwgnZfg3VkcIIjKIV3YWt1Mg0",
"boot-01.gc-us-central1-a.shards.staging": "enr:-QEiuECWCuk1CoefbPYUV_cff27t-471k_QhJ_MvgOUQmRE4vgI6h9fD4LgXy7TZNSFXeBBP9haWJB7lx8Wujx-LPmhrAYJpZIJ2NIJpcIRoxQVgim11bHRpYWRkcnO4cgA2NjFib290LTAxLmdjLXVzLWNlbnRyYWwxLWEuc2hhcmRzLnN0YWdpbmcuc3RhdHVzLmltBnZfADg2MWJvb3QtMDEuZ2MtdXMtY2VudHJhbDEtYS5zaGFyZHMuc3RhZ2luZy5zdGF0dXMuaW0GAbveA4Jyc40AEAUAAQAgAEAAgAEAiXNlY3AyNTZrMaEDNAvlGjekD1YV4WpmjwArGAH2g9kHFJnMRfgUhcIkoA2DdGNwgnZfg3VkcIIjKIV3YWt1Mg0",
"boot-01.ac-cn-hongkong-c.shards.staging": "enr:-QEiuEAcHWW7aNBtJhigTCAnKu9-H1b98SWpBJkGm3PAxth5QjJA0kdiabOfou4HgCD6RuyZEnFE5L8ymahouw3kWRiWAYJpZIJ2NIJpcIQvTKi6im11bHRpYWRkcnO4cgA2NjFib290LTAxLmFjLWNuLWhvbmdrb25nLWMuc2hhcmRzLnN0YWdpbmcuc3RhdHVzLmltBnZfADg2MWJvb3QtMDEuYWMtY24taG9uZ2tvbmctYy5zaGFyZHMuc3RhZ2luZy5zdGF0dXMuaW0GAbveA4Jyc40AEAUAAQAgAEAAgAEAiXNlY3AyNTZrMaEDkbgV7oqPNmFtX5FzSPi9WH8kkmrPB1R3n9xRXge91M-DdGNwgnZfg3VkcIIjKIV3YWt1Mg0"
},
"wss/p2p/waku/boot": {
"boot-01.do-ams3.shards.staging": "/dns4/boot-01.do-ams3.shards.staging.status.im/tcp/443/wss/p2p/16Uiu2HAmEqqio4UR1SWqAc7KY19t6qyDvtmyjreZpzUBJvb4u65R",
"boot-01.do-ams3.shards.staging": "/dns4/boot-01.do-ams3.shards.staging.status.im/tcp/443/wss/p2p/16Uiu2HAmQE7FXQc6iZHdBzYfw3qCSDa9dLc1wsBJKoP4aZvztq2d",
"boot-01.gc-us-central1-a.shards.staging": "/dns4/boot-01.gc-us-central1-a.shards.staging.status.im/tcp/443/wss/p2p/16Uiu2HAmGAA54bBTE78MYidSy3P7Q9yAWFNTAEReJYD69VRvtL5r",
"boot-01.ac-cn-hongkong-c.shards.staging": "/dns4/boot-01.ac-cn-hongkong-c.shards.staging.status.im/tcp/443/wss/p2p/16Uiu2HAmNTpGnyZ8W1BK2sXEmgSCNWiyDKgRU3NBR2DXST2HzxRU"
},
Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

@@ -1,47 +0,0 @@
import { useDerivedValue, withTiming, interpolate } from 'react-native-reanimated';
export function navigationHeaderOpacity(distanceFromListTop, isAllLoaded, isCalculationsComplete, startPosition) {
return useDerivedValue(function () {
'worklet';
const isCalculationsCompleteValue = isCalculationsComplete.value;
if (distanceFromListTop.value < startPosition && isAllLoaded.value) {
return isCalculationsCompleteValue ? withTiming(0) : 0;
} else {
return isCalculationsCompleteValue ? withTiming(1) : 1;
}
});
}
export function navigationHeaderPosition(distanceFromListTop, isAllLoaded, topBarHeight, startPosition) {
return useDerivedValue(function () {
'worklet';
return distanceFromListTop.value < startPosition && isAllLoaded.value ? withTiming(topBarHeight) : withTiming(0);
});
}
export function navigationButtonsCompleteOpacity(isCalculationComplete) {
return useDerivedValue(function () {
'worklet';
return isCalculationComplete.value ? withTiming(1) : 0;
});
}
export function interpolateNavigationViewOpacity(props) {
return useDerivedValue(function () {
'worklet';
const {
'all-loaded?': isAllLoaded,
'end-position': endPosition,
'start-position': startPosition,
'distance-from-list-top': distanceFromListTop,
} = props;
if (isAllLoaded.value) {
return interpolate(distanceFromListTop.value, [startPosition, endPosition], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
} else {
return 1;
}
});
}
@@ -1,15 +0,0 @@
import { useDerivedValue } from 'react-native-reanimated';
export function placeholderOpacity(isCalculationsComplete) {
return useDerivedValue(function () {
'worklet';
return isCalculationsComplete.value ? 0 : 1;
});
}
export function placeholderZIndex(isCalculationsComplete) {
return useDerivedValue(function () {
'worklet';
return isCalculationsComplete.value ? 0 : 2;
});
}
+4 -3
View File
@@ -256,9 +256,10 @@
{:events [:pairing/pair-and-sync]}
[cofx installation-id]
{:fx [[:json-rpc/call
[{:method "wakuext_enableInstallationAndSync"
:params [{:installationId installation-id}]
:on-success #(log/debug "successfully synced devices")}]]]})
[{:method "wakuext_enableInstallationAndSync"
:params [{:installationId installation-id}]
:js-response true
:on-success [:sanitize-messages-and-process-response]}]]]})
(rf/defn enable-installation-success
{:events [:pairing.callback/enable-installation-success]}
@@ -1,4 +1,5 @@
(ns quo.components.buttons.wallet-ctas.style)
(ns quo.components.buttons.wallet-ctas.style
(:require [quo.foundations.colors :as colors]))
(def inner-container
{:flex-direction :row
@@ -10,3 +11,9 @@
:width 77.75
:justify-content :center
:align-items :center})
(defn action-button-text
[theme disabled?]
(cond-> {:margin-top 4
:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)}
disabled? (assoc :opacity 0.5)))
@@ -3,28 +3,28 @@
[quo.components.buttons.wallet-button.view :as wallet-button]
[quo.components.buttons.wallet-ctas.style :as style]
[quo.components.markdown.text :as text]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[utils.i18n :as i18n]))
(defn action-button
[{:keys [icon text on-press theme accessibility-label]}]
[{:keys [icon text on-press theme accessibility-label disabled?]}]
[rn/view
{:style style/button-container
:accessibility-label accessibility-label}
[wallet-button/view
{:icon icon
:on-press on-press}]
{:icon icon
:disabled? disabled?
:on-press on-press}]
[text/text
{:weight :medium
:size :paragraph-2
:style {:margin-top 4
:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)}} text]])
:style (style/action-button-text theme disabled?)} text]])
(defn view
[{:keys [buy-action send-action receive-action bridge-action swap-action container-style]}]
[{:keys [buy-action send-action receive-action bridge-action swap-action bridge-disabled?
swap-disabled? container-style]}]
(let [theme (quo.theme/use-theme)]
[rn/view {:style container-style}
[rn/view {:style style/inner-container}
@@ -52,10 +52,12 @@
:text (i18n/label :t/swap)
:on-press swap-action
:theme theme
:disabled? swap-disabled?
:accessibility-label :swap}])
[action-button
{:icon :i/bridge
:text (i18n/label :t/bridge)
:on-press bridge-action
:theme theme
:disabled? bridge-disabled?
:accessibility-label :bridge}]]]))
@@ -65,7 +65,7 @@
:type type
:include-button? include-button?})
style)}
[icons/icon icon
[icons/icon (or icon :i/info)
{:color (style/get-color-by-type theme type :icon)
:no-color no-icon-color?
:size (or icon-size 16)
@@ -1,6 +1,7 @@
(ns quo.components.list-items.network-list.view
(:require
[clojure.string :as string]
[quo.components.icon :as icon]
[quo.components.list-items.network-list.style :as style]
[quo.components.markdown.text :as text]
[quo.theme :as quo.theme]
@@ -42,8 +43,8 @@
[:map {:closed true}
[:network-image :int]
[:label :string]
[:fiat-value :string]
[:token-value :string]
[:fiat-value {:optional true} [:maybe :string]]
[:token-value {:optional true} [:maybe :string]]
[:container-style {:optional true} [:maybe :map]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]
[:state {:optional true} [:enum :pressed :active :disabled :default]]
@@ -51,7 +52,7 @@
:any])
(defn- view-internal
[{:keys [on-press container-style state customization-color]
[{:keys [fiat-value token-value on-press container-style state customization-color]
:as props
:or {customization-color :blue}}]
(let [theme (quo.theme/use-theme)
@@ -67,6 +68,8 @@
:on-press (when-not (= state :disabled) on-press)
:accessibility-label :network-list}
[info props]
[values props]]))
(if (or token-value fiat-value)
[values props]
[icon/icon :i/chevron-right])]))
(def view (schema/instrument #'view-internal ?schema))
+1 -3
View File
@@ -4,12 +4,10 @@
[container-style]
(merge container-style
{:flex-direction :row
:flex 1
:align-items :flex-start}))
(def index
{:margin-left 5})
(def text-container
{:margin-left 8
:flex 1})
{:margin-left 8})
@@ -32,7 +32,8 @@
[rn/pressable
(when-not disabled?
{:on-press on-press
:allow-multiple-presses? true})
:allow-multiple-presses? true
:hit-slop 12})
[rn/view
{:style outer-styles
:needs-offscreen-alpha-compositing true
@@ -75,3 +75,16 @@
(def fiat-amount
{:color colors/neutral-50})
(def gradient-common
{:width 64
:position :absolute
:top 0
:bottom 0
:z-index 1})
(def gradient-start
(assoc gradient-common :left 0))
(def gradient-end
(assoc gradient-common :right 0))
+91 -32
View File
@@ -11,6 +11,7 @@
[quo.foundations.colors :as colors]
quo.theme
[react-native.core :as rn]
[react-native.linear-gradient :as linear-gradient]
[schema.core :as schema]))
(def ?schema
@@ -18,6 +19,7 @@
[:catn
[:props
[:map {:closed true}
[:get-ref {:optional true} [:maybe fn?]]
[:type {:optional true} [:maybe [:enum :pay :receive]]]
[:status {:optional true} [:maybe [:enum :default :typing :disabled :loading]]]
[:token {:optional true} [:maybe :string]]
@@ -42,26 +44,65 @@
[:container-style {:optional true} [:maybe :map]]]]]
:any])
(def icon-size 32)
(def container-padding (* 2 (:padding (style/row-1 false))))
(def max-cursor-position 5)
(defn view-internal
[{:keys [type status token value fiat-value show-approval-label? error? network-tag-props
approval-label-props default-value auto-focus? input-disabled? enable-swap?
currency-symbol on-change-text show-keyboard?
currency-symbol on-change-text show-keyboard? get-ref
container-style on-swap-press on-token-press on-max-press on-input-focus]}]
(let [theme (quo.theme/use-theme)
pay? (= type :pay)
disabled? (= status :disabled)
loading? (= status :loading)
typing? (= status :typing)
controlled-input? (some? value)
input-ref (rn/use-ref-atom nil)
set-input-ref (rn/use-callback (fn [ref] (reset! input-ref ref)) [])
focus-input (rn/use-callback (fn []
(some-> @input-ref
(oops/ocall "focus")))
[input-ref])]
(let [theme (quo.theme/use-theme)
pay? (= type :pay)
disabled? (= status :disabled)
loading? (= status :loading)
typing? (= status :typing)
controlled-input? (some? value)
[container-width
set-container-width] (rn/use-state)
[overflow?
set-overflow?] (rn/use-state false)
[cursor-close-to-start?
set-cursor-close-to-start?] (rn/use-state false)
[label-width
set-label-width] (rn/use-state 0)
input-ref (rn/use-ref-atom nil)
set-input-ref (rn/use-callback (fn [ref]
(reset! input-ref ref)
(when get-ref (get-ref ref)))
[])
focus-input (rn/use-callback (fn []
(some-> @input-ref
(oops/ocall "focus")))
[input-ref])
on-layout-container (rn/use-callback
(fn [e]
(let [width (oops/oget e "nativeEvent.layout.width")]
(set-container-width width))))
on-layout-text-input (rn/use-callback
(fn [e]
(let [width (oops/oget e "nativeEvent.layout.width")
max-width (- container-width
icon-size
container-padding
(* label-width 2))]
(set-overflow? (> width max-width))))
[container-width label-width])
on-layout-label (rn/use-callback
(fn [e]
(let [width (oops/oget e "nativeEvent.layout.width")]
(set-label-width width))))
on-selection-change (rn/use-callback
(fn [e]
(let [selection-start (oops/oget e
"nativeEvent.selection.start")]
(set-cursor-close-to-start?
(< selection-start max-cursor-position)))))]
[rn/view
{:style container-style
:accessibility-label :swap-input}
:accessibility-label :swap-input
:on-layout on-layout-container}
[rn/view {:style (style/content typing? theme)}
[rn/view
{:style (style/row-1 loading?)}
@@ -75,25 +116,43 @@
[rn/pressable
{:style style/input-container
:on-press focus-input}
[rn/text-input
(cond-> {:ref set-input-ref
:style (style/input disabled? error? theme)
:placeholder-text-color (colors/theme-colors colors/neutral-40
colors/neutral-50
theme)
:keyboard-type :numeric
:editable (not input-disabled?)
:auto-focus auto-focus?
:on-focus on-input-focus
:on-change-text on-change-text
:show-soft-input-on-focus show-keyboard?
:default-value default-value
:placeholder "0"}
controlled-input? (assoc :value value))]
[rn/view {:style {:flex-shrink 1}}
(when (and overflow? typing? (not cursor-close-to-start?))
[linear-gradient/linear-gradient
{:start {:x 0 :y 0}
:end {:x 1 :y 0}
:colors [(colors/theme-colors colors/white colors/neutral-100 theme)
(colors/theme-colors colors/white-opa-10 colors/neutral-100-opa-10 theme)]
:style style/gradient-start}])
(when (and overflow? disabled?)
[linear-gradient/linear-gradient
{:start {:x 0 :y 0}
:end {:x 1 :y 0}
:colors [(colors/theme-colors colors/white-opa-10 colors/neutral-100-opa-10 theme)
(colors/theme-colors colors/white colors/neutral-100 theme)]
:style style/gradient-end}])
[rn/text-input
(cond-> {:ref set-input-ref
:style (style/input disabled? error? theme)
:placeholder-text-color (colors/theme-colors colors/neutral-40
colors/neutral-50
theme)
:keyboard-type :numeric
:editable (not input-disabled?)
:auto-focus auto-focus?
:on-focus on-input-focus
:on-change-text on-change-text
:on-layout on-layout-text-input
:on-selection-change on-selection-change
:show-soft-input-on-focus show-keyboard?
:default-value default-value
:placeholder "0"}
controlled-input? (assoc :value value))]]
[text/text
{:size :paragraph-2
:weight :semi-bold
:style (style/token-symbol theme)}
{:size :paragraph-2
:weight :semi-bold
:style (style/token-symbol theme)
:on-layout on-layout-label}
token]]
(when (and pay? enable-swap?)
[buttons/button
@@ -96,7 +96,7 @@
(take 7)))
(defn recovery-phrase-screen
[{:keys [banner-offset initial-insets keypair title recovering-keypair? render-controls]}]
[{:keys [banner-offset initial-insets keypair title on-success render-controls]}]
(reagent/with-let [keyboard-shown? (reagent/atom false)
keyboard-show-listener (.addListener rn/keyboard
"keyboardDidShow"
@@ -120,14 +120,20 @@
(reset! seed-phrase new-phrase))
on-submit (fn []
(swap! seed-phrase clean-seed-phrase)
(if recovering-keypair?
(rf/dispatch [:wallet/seed-phrase-entered
(security/mask-data
@seed-phrase)
set-invalid-seed-phrase])
(rf/dispatch [:onboarding/seed-phrase-entered
(security/mask-data @seed-phrase)
set-invalid-seed-phrase])))]
(rf/dispatch
[:profile.recover/validate-recovery-phrase
(security/mask-data @seed-phrase)
{:on-success (fn [mnemonic key-uid]
(if on-success
(on-success
{:key-uid key-uid
:phrase mnemonic
:on-error
set-invalid-seed-phrase})
(rf/dispatch
[:onboarding/seed-phrase-validated
mnemonic key-uid])))
:on-error set-invalid-seed-phrase}]))]
(let [words-coll (mnemonic/passphrase->words @seed-phrase)
last-word (peek words-coll)
pick-suggested-word (fn [pressed-word]
@@ -191,7 +197,7 @@
[rn/view {:style style/keyboard-container}
[quo/predictive-keyboard
{:type suggestions-state
:blur? (not recovering-keypair?)
:blur? (not on-success)
:text suggestions-text
:words (keyboard-suggestions last-word)
:on-press pick-suggested-word}]])])
@@ -200,7 +206,7 @@
(.remove keyboard-hide-listener))))
(defn screen
[{:keys [title keypair navigation-icon recovering-keypair? render-controls]}]
[{:keys [title keypair navigation-icon on-success render-controls]}]
(let [[insets _] (rn/use-state (safe-area/get-insets))
banner-offset (rf/sub [:alert-banners/top-margin])]
[rn/view {:style style/full-layout}
@@ -209,21 +215,22 @@
{:margin-top (:top insets)
:background :blur
:icon-name (or navigation-icon
(if recovering-keypair? :i/close :i/arrow-left))
(if on-success :i/close :i/arrow-left))
:on-press #(rf/dispatch [:navigate-back])}]
[recovery-phrase-screen
{:title title
:keypair keypair
:render-controls render-controls
:banner-offset banner-offset
:initial-insets insets
:recovering-keypair? recovering-keypair?}]]]))
{:title title
:keypair keypair
:render-controls render-controls
:banner-offset banner-offset
:initial-insets insets
:on-success on-success}]]]))
(defn view
[]
(let [{:keys [recovering-keypair?]} (rf/sub [:get-screen-params])]
(let [{:keys [on-success]} (rf/sub [:get-screen-params])]
(rn/use-unmount
#(rf/dispatch [:onboarding/clear-navigated-to-enter-seed-phrase-from-screen]))
(when-not on-success
#(rf/dispatch [:onboarding/clear-navigated-to-enter-seed-phrase-from-screen])))
[screen
{:title (i18n/label :t/use-recovery-phrase)
:recovering-keypair? recovering-keypair?}]))
{:title (i18n/label :t/use-recovery-phrase)
:on-success on-success}]))
+1
View File
@@ -17,6 +17,7 @@
:generate-keys1 (js/require "../resources/images/ui2/generating-keys-1.png")
:ethereum-address (js/require "../resources/images/ui2/ethereum-address.png")
:use-keycard (js/require "../resources/images/ui2/keycard.png")
:check-your-keycard (js/require "../resources/images/ui2/check-your-keycard.png")
:onboarding-illustration (js/require "../resources/images/ui2/onboarding_illustration.png")
:qr-code (js/require "../resources/images/ui2/qr-code.png")
:keycard-logo (js/require "../resources/images/ui2/keycard-logo.png")
+1 -1
View File
@@ -10,7 +10,7 @@
(defn enabled? [v] (= "1" v))
(goog-define INFURA_TOKEN "62f3cee52dbb484198e7339837e263f3")
(goog-define INFURA_TOKEN "")
(goog-define POKT_TOKEN "3ef2018191814b7e1009b8d9")
(goog-define STATUS_BUILD_PROXY_USER "")
(goog-define STATUS_BUILD_PROXY_PASSWORD "")
+9
View File
@@ -619,3 +619,12 @@
(def router-error-code-not-enough-liquidity "WPP-038")
(def router-error-code-price-impact-too-high "WPP-039")
(def router-error-code-not-enough-native-balance "WR-002")
(def ^:const wallet-transaction-estimation
{:unknown 0
:less-than-one-minute 1
:less-than-three-minutes 2
:less-than-five-minutes 3
:more-than-five-minutes 4})
(def ^:const wallet-connect-transaction-refresh-interval-ms 10000)
@@ -45,3 +45,15 @@
#(rf/dispatch [:profile.login/login-with-biometric-if-available
(get-in db [:profile/login :key-uid])]))
:shell? true}]]]})))
(rf/reg-fx :effects.centralized-metrics/track
(fn [event]
(native-module/add-centralized-metric event)))
(rf/reg-event-fx
:centralized-metrics/track
(fn [{:keys [db]} [event-name data]]
(let [event-id (name event-name)]
(when (push-event? db)
{:fx [[:effects.centralized-metrics/track
(tracking/key-value-event event-id data)]]}))))
@@ -4,20 +4,20 @@
[react-native.platform :as platform]))
(defn key-value-event
[event-name val-key value]
[event-name event-value]
{:metric
{:eventName event-name
:platform platform/os
:appVersion build/app-short-version
:eventValue {val-key value}}})
:eventValue event-value}})
(defn user-journey-event
[action]
(key-value-event "user-journey" :action action))
(key-value-event "user-journey" {:action action}))
(defn navigation-event
[view-id]
(key-value-event "navigation" :viewId view-id))
(key-value-event "navigation" {:viewId view-id}))
(def ^:const app-started-event "app-started")
@@ -57,7 +57,7 @@
(user-journey-event app-started-event)
:centralized-metrics/toggle-centralized-metrics
(key-value-event "events.metrics-enabled" :enabled second-parameter)
(key-value-event "events.metrics-enabled" {:enabled second-parameter})
:set-view-id
(track-view-id-event second-parameter)
@@ -18,7 +18,7 @@
:platform platform-os
:appVersion app-version
:eventValue {val-key value}}}]
(is (= expected (tracking/key-value-event event-name val-key value))))))
(is (= expected (tracking/key-value-event event-name {val-key value}))))))
(deftest user-journey-event-test
(testing "creates correct user journey event"
@@ -17,7 +17,7 @@
user])
(defn view
[{:keys [layout-height]}]
[layout-height]
(let [suggestions (rf/sub [:chat/mention-suggestions])
suggestions? (seq suggestions)
theme (quo.theme/use-theme)
@@ -35,7 +35,7 @@
:default-value default-value}])))
(defn view
[props]
[layout-height]
(let [theme (quo.theme/use-theme)
bottom (safe-area/get-bottom)
input-ref (rn/use-ref-atom nil)
@@ -43,7 +43,7 @@
(rf/dispatch [:chat/set-input-ref value])
(reset! input-ref value)))]
[rn/view {:style {:margin-bottom bottom}}
[mentions/view props]
[mentions/view layout-height]
[quo/separator]
[rn/view {:style {:padding-horizontal 20 :padding-top 20}}
[:<>
@@ -3,15 +3,4 @@
;;;; Navigation
(def ^:const top-bar-height 56)
(def ^:const pinned-banner-height 40)
(def ^:const header-container-top-margin 48)
(def ^:const header-container-radius 20)
(def ^:const header-animation-distance 20)
(def ^:const content-animation-start-position-android 130)
(def ^:const content-animation-start-position-ios 124)
;; Note - We should also consider height of bio for banner animation starting position
;; Todo - Should be updated once New-profile implemation is complete
(def ^:const pinned-banner-animation-start-position 148)
(def ^:const default-extrapolation-option
{:extrapolateLeft "clamp"
:extrapolateRight "clamp"})
@@ -11,25 +11,23 @@
(defn view
[{:keys [contact-id]}]
(let [customization-color (rf/sub [:profile/customization-color])
[primary-name _] (rf/sub [:contacts/contact-two-names-by-identity
contact-id])
{:keys [contact-request-state community-id]} (rf/sub [:chats/current-chat-chat-view])
chat-type (rf/sub [:chats/chat-type])
community-chat? (= chat-type :community-chat)
joined (when community-chat?
(rf/sub [:communities/community-joined
community-id]))
pending? (when community-chat?
(rf/sub [:communities/my-pending-request-to-join
community-id]))
contact-request-send? (or (not contact-request-state)
(= contact-request-state
constants/contact-request-state-none))
contact-request-received? (= contact-request-state
constants/contact-request-state-received)
contact-request-pending? (= contact-request-state
constants/contact-request-state-sent)]
(let [customization-color (rf/sub [:profile/customization-color])
[primary-name _] (rf/sub [:contacts/contact-two-names-by-identity contact-id])
{:keys [contact-request-state
community-id]} (rf/sub [:chats/current-chat-chat-view])
chat-type (rf/sub [:chats/chat-type])
community-chat? (= chat-type :community-chat)
joined (when community-chat?
(rf/sub [:communities/community-joined community-id]))
pending? (when community-chat?
(rf/sub [:communities/my-pending-request-to-join community-id]))
contact-request-send? (or (not contact-request-state)
(= contact-request-state
constants/contact-request-state-none))
contact-request-received? (= contact-request-state
constants/contact-request-state-received)
contact-request-pending? (= contact-request-state
constants/contact-request-state-sent)]
[rn/view {:style style/container}
[quo/permission-context
{:blur? true
@@ -1,11 +1,11 @@
(ns status-im.contexts.chat.messenger.messages.list.style
(:require
[quo.foundations.colors :as colors]
[quo.foundations.shadows :as shadows]
[react-native.reanimated :as reanimated]
[react-native.safe-area :as safe-area]
[status-im.contexts.chat.messenger.messages.constants :as messages.constants]))
(def permission-context-height 46)
(def distance-from-last-message 4)
(defn keyboard-avoiding-container
[theme]
@@ -13,70 +13,13 @@
:background-color (colors/theme-colors colors/white colors/neutral-95 theme)
:z-index 2})
(defn background-container
[background-color background-opacity top-margin]
(reanimated/apply-animations-to-style
{:opacity background-opacity}
{:background-color background-color
:position :absolute
:top 0
:left 0
:right 0
:height (+ top-margin messages.constants/header-container-radius)}))
(def permission-context-sheet {:flex 3}) ; Pushes composer to bottom
(defn header-bottom-container
[bottom top-margin]
(reanimated/apply-animations-to-style
{:bottom bottom}
{:margin-top top-margin}))
(defn header-bottom-part
[theme]
{:background-color (colors/theme-colors colors/white colors/neutral-95 theme)
:padding-horizontal 20
:border-radius 20})
(defn header-bottom-shadow
[theme]
(assoc
(shadows/get 2 theme :inverted)
:left 0
:right 0
:height 40
:position :absolute
:border-radius 20))
(defn header-image
[scale top left theme]
(reanimated/apply-animations-to-style
{:transform [{:scale scale}]
:top top
:left left}
{:position :absolute
:border-width 4
:border-radius 50
:background-color (colors/theme-colors colors/white colors/neutral-95 theme)
:border-color (colors/theme-colors colors/white colors/neutral-95 theme)}))
(defn user-name-container
[top left]
(reanimated/apply-animations-to-style
{:top top
:left left}
{:z-index -1}))
(def user-name
{:align-items :center
:flex-direction :row
:margin-top 52})
(defn bio-and-actions
[top]
(reanimated/apply-animations-to-style
{:top top}
{:row-gap 16}))
(defn permission-context-sheet
[margin-bottom?]
{:flex 3 ;; Pushes composer to bottom
:margin-bottom (when margin-bottom? permission-context-height)})
(defn list-paddings
[add-padding-bottom?]
(let [{:keys [top bottom]} (safe-area/get-insets)]
;; WARNING: the flat-list is reversed, so the paddings are applied inverted
{:padding-top (if add-padding-bottom?
(+ distance-from-last-message permission-context-height bottom)
distance-from-last-message)
:padding-bottom (+ top messages.constants/top-bar-height)}))
@@ -1,6 +1,5 @@
(ns status-im.contexts.chat.messenger.messages.list.view
(:require
[clojure.string :as string]
[legacy.status-im.ui.screens.chat.group :as chat.group]
[oops.core :as oops]
[quo.core :as quo]
@@ -11,11 +10,9 @@
[react-native.hooks :as hooks]
[react-native.platform :as platform]
[react-native.reanimated :as reanimated]
[react-native.safe-area :as safe-area]
[status-im.common.home.actions.view :as home.actions]
[status-im.constants :as constants]
[status-im.contexts.chat.messenger.composer.constants :as composer.constants]
[status-im.contexts.chat.messenger.messages.constants :as messages.constants]
[status-im.contexts.chat.messenger.messages.content.view :as message]
[status-im.contexts.chat.messenger.messages.list.state :as state]
[status-im.contexts.chat.messenger.messages.list.style :as style]
@@ -25,8 +22,7 @@
[utils.re-frame :as rf]
[utils.worklets.chat.messenger.messages :as worklets]))
(defonce ^:const distance-from-last-message 4)
(defonce ^:const loading-indicator-page-loading-height 100)
(def ^:const loading-indicator-page-loading-height 100)
(defn list-key-fn [{:keys [message-id value]}] (or message-id value))
(defn list-ref [ref] (reset! state/messages-list-ref ref))
@@ -60,44 +56,17 @@
on-loaded])
(if platform/low-device? 700 100)))))
(defn- contact-icon
[{:keys [ens-verified added?]} theme]
(when (or ens-verified added?)
[rn/view
{:style {:margin-left 4}}
(if ens-verified
[quo/icon :i/verified
{:no-color true
:size 20
:color (colors/theme-colors
(colors/custom-color :success 50)
(colors/custom-color :success 60)
theme)}]
(when added?
[quo/icon :i/contact
{:no-color true
:size 20
:color (colors/theme-colors colors/primary-50 colors/primary-60 theme)}]))]))
(defn- skeleton-list-props
[content parent-height animated?]
{:content content
:parent-height parent-height
:animated? animated?})
(defn loading-view
[chat-id {:keys [window-height]}]
[{:keys [chat-id window-height]}]
(let [messages (rf/sub [:chats/raw-chat-messages-stream chat-id])
loading-first-page? (= (count messages) 0)
top-spacing (if loading-first-page?
0
(+ messages.constants/top-bar-height (safe-area/get-top)))
parent-height (if loading-first-page?
window-height
loading-indicator-page-loading-height)]
[rn/view {:padding-top top-spacing}
;; Don't use animated loading skeleton https://github.com/status-im/status-mobile/issues/17426
[quo/skeleton-list (skeleton-list-props :messages parent-height false)]]))
[quo/skeleton-list
{:content :messages
:parent-height parent-height
:animated? false}]))
(defn header-height
[{:keys [insets able-to-send-message? images reply edit link-previews? input-content-height]}]
@@ -125,84 +94,6 @@
(+ (:bottom insets)))
(- 70 (:bottom insets))))
(defn list-header
[insets able-to-send-message?]
(let [header-data {:insets insets
:able-to-send-message? able-to-send-message?
:input-content-height (:input-content-height (rf/sub [:chats/current-chat-input]))
:images (rf/sub [:chats/sending-image])
:reply (rf/sub [:chats/reply-message])
:edit (rf/sub [:chats/edit-message])
:link-previews? (or (rf/sub [:chats/link-previews?])
(rf/sub [:chats/status-link-previews?]))}]
[rn/view {:style {:height (header-height header-data)}}]))
(defn list-footer-avatar
[{:keys [distance-from-list-top display-name online? profile-picture theme group-chat color
emoji chat-type chat-name empty-chat?]}]
(let [scale (reanimated/interpolate
distance-from-list-top
[0 (if empty-chat? 0 messages.constants/header-container-top-margin)]
[1 0.4]
messages.constants/default-extrapolation-option)
top (reanimated/interpolate
distance-from-list-top
[0 (if empty-chat? 0 messages.constants/header-container-top-margin)]
[-44 -12]
messages.constants/default-extrapolation-option)
left (reanimated/interpolate
distance-from-list-top
[0 (if empty-chat? 0 messages.constants/header-container-top-margin)]
[16 -8]
messages.constants/default-extrapolation-option)
community-channel? (= chat-type constants/community-chat-type)]
[reanimated/view
{:style (style/header-image scale top left theme)}
(cond
community-channel?
[quo/channel-avatar
{:size :size-80
:full-name chat-name
:customization-color color
:emoji (when-not (string/blank? emoji)
(string/trim emoji))}]
group-chat
[quo/group-avatar
{:customization-color color
:size :size-80
:picture profile-picture
:chat-name chat-name}]
:else
[quo/user-avatar
{:full-name display-name
:online? online?
:profile-picture profile-picture
:size :big}])]))
(defn chat-display-name
[{:keys [distance-from-list-top display-name contact theme empty-chat?]}]
(let [top (reanimated/interpolate
distance-from-list-top
[0 (if empty-chat? 0 messages.constants/header-container-top-margin)]
[0 -35]
messages.constants/default-extrapolation-option)
left (reanimated/interpolate
distance-from-list-top
[0 (if empty-chat? 0 messages.constants/header-container-top-margin)]
[0 40]
messages.constants/default-extrapolation-option)]
[reanimated/view
{:style (style/user-name-container top left)}
[rn/view
{:style style/user-name}
[quo/text
{:weight :semi-bold
:size :heading-1
:style {:flex-shrink 1}
:number-of-lines 1}
display-name]
[contact-icon contact theme]]]))
(defn actions
[chat-id cover-bg-color]
(let [latest-pin-text (rf/sub [:chats/last-pinned-message-text chat-id])
@@ -236,101 +127,12 @@
chat-type
muted?)))}]}]))
(defn bio-and-actions
[{:keys [distance-from-list-top bio chat-id customization-color empty-chat? description]}]
(let [has-bio (seq (or bio description))
top (reanimated/interpolate
distance-from-list-top
[0 (if empty-chat? 0 messages.constants/header-container-top-margin)]
[(if has-bio 8 16) (if has-bio -28 -20)]
messages.constants/default-extrapolation-option)]
[reanimated/view
{:style (style/bio-and-actions top)}
(when has-bio
[quo/text (or bio description)])
[actions chat-id customization-color]]))
(defn footer-component
[{:keys [chat distance-from-list-top customization-color]}]
(let [theme (quo.theme/use-theme)
{:keys [chat-id chat-name emoji chat-type
group-chat color description
empty-chat?]} chat
display-name (cond
(= chat-type constants/one-to-one-chat-type)
(first (rf/sub [:contacts/contact-two-names-by-identity chat-id]))
(= chat-type constants/community-chat-type)
(str "# " chat-name)
:else (str emoji chat-name))
{:keys [bio]} (rf/sub [:contacts/contact-by-identity chat-id])
online? (rf/sub [:visibility-status-updates/online? chat-id])
contact (when-not group-chat
(rf/sub [:contacts/contact-by-address chat-id]))
photo-path (if group-chat
(rf/sub [:chats/group-chat-image chat-id])
(rf/sub [:chats/photo-path chat-id]))
top-margin (+ (safe-area/get-top)
messages.constants/top-bar-height
messages.constants/header-container-top-margin
32)
background-color (colors/theme-colors
(colors/resolve-color customization-color theme 20)
(colors/resolve-color customization-color theme 40)
theme)
bottom (reanimated/interpolate
distance-from-list-top
[0 messages.constants/header-container-top-margin]
[32 -4]
messages.constants/default-extrapolation-option)
background-opacity (reanimated/interpolate
distance-from-list-top
[messages.constants/header-container-top-margin
(+ messages.constants/header-animation-distance
messages.constants/header-container-top-margin)]
[1 0]
messages.constants/default-extrapolation-option)]
[:<>
[reanimated/view
{:style (style/background-container background-color background-opacity top-margin)}]
[reanimated/view {:style (style/header-bottom-container bottom top-margin)}
[rn/view {:style (style/header-bottom-shadow theme)}]
[rn/view {:style (style/header-bottom-part theme)}
[list-footer-avatar
{:distance-from-list-top distance-from-list-top
:display-name display-name
:online? online?
:theme theme
:profile-picture photo-path
:group-chat group-chat
:color color
:emoji emoji
:chat-type chat-type
:chat-name chat-name
:empty-chat? empty-chat?}]
[chat-display-name
{:distance-from-list-top distance-from-list-top
:display-name display-name
:theme theme
:contact contact
:group-chat group-chat
:empty-chat? empty-chat?}]
[bio-and-actions
{:distance-from-list-top distance-from-list-top
:bio bio
:chat-id chat-id
:customization-color customization-color
:description description
:empty-chat? empty-chat?}]]]]))
(defn list-footer
[props]
(let [chat-id (get-in props [:chat :chat-id])
loading-messages? (rf/sub [:chats/loading-messages? chat-id])
(defn more-messages-loader
[{:keys [chat-id] :as props}]
(let [loading-messages? (rf/sub [:chats/loading-messages? chat-id])
all-loaded? (rf/sub [:chats/all-loaded? chat-id])]
[:<>
(if (or loading-messages? (not all-loaded?))
[loading-view chat-id props]
[footer-component props])]))
(when (or loading-messages? (not all-loaded?))
[loading-view props])))
(defn list-group-chat-header
[{:keys [chat-id invitation-admin]}]
@@ -359,8 +161,7 @@
(reset! content-height content-height-new)))))
(defn on-layout
[{:keys [event layout-height distance-atom distance-from-list-top
chat-screen-layout-calculations-complete?]}]
[{:keys [event layout-height distance-atom distance-from-list-top on-layout-done?]}]
(let [layout-height-new (oops/oget event "nativeEvent.layout.height")
change (- layout-height-new @layout-height)
new-distance (- @distance-atom change)]
@@ -368,8 +169,7 @@
(reanimated/set-shared-value distance-from-list-top new-distance)
(reset! distance-atom new-distance)
(reset! layout-height layout-height-new))
(when-not (reanimated/get-shared-value chat-screen-layout-calculations-complete?)
(reanimated/set-shared-value chat-screen-layout-calculations-complete? true))))
(reset! on-layout-done? true)))
(defn on-scroll-fn
[distance-atom layout-height-atom]
@@ -379,29 +179,21 @@
(reset! distance-atom new-distance)))
(defn messages-list-content
[{:keys [insets distance-from-list-top content-height layout-height distance-atom
chat-screen-layout-calculations-complete? chat-list-scroll-y]}]
(let [theme (quo.theme/use-theme)
{:keys [chat-type chat-id] :as chat} (rf/sub [:chats/current-chat-chat-view])
one-to-one-chat? (= chat-type constants/one-to-one-chat-type)
community-channel? (= constants/community-chat-type chat-type)
{contact-customization-color
:customization-color} (when one-to-one-chat?
(rf/sub [:contacts/contact-by-identity chat-id]))
customization-color (cond community-channel?
(or (:color chat)
(rf/sub [:communities/community-color
(:community-id chat)]))
one-to-one-chat? contact-customization-color
:else (or (:color chat) :turquoise))
{:keys [keyboard-shown]} (hooks/use-keyboard)
{window-height :height} (rn/get-window)
context (rf/sub [:chats/current-chat-message-list-view-context])
able-to-send-message? (:able-to-send-message? context)
messages (rf/sub [:chats/raw-chat-messages-stream chat-id])
margin-bottom? (and community-channel? (not able-to-send-message?))
recording? (rf/sub [:chats/recording?])]
[rn/view {:style (style/permission-context-sheet margin-bottom?)}
[{:keys [insets distance-from-list-top layout-height chat-list-scroll-y on-layout-done?]}]
(let [content-height (rn/use-ref-atom 0)
distance-atom (rn/use-ref-atom 0)
theme (quo.theme/use-theme)
{:keys [keyboard-shown]} (hooks/use-keyboard)
{:keys [chat-type chat-id]
:as chat} (rf/sub [:chats/current-chat-chat-view])
community-channel? (= constants/community-chat-type chat-type)
{window-height :height} (rn/get-window)
context (rf/sub [:chats/current-chat-message-list-view-context])
able-to-send-message? (:able-to-send-message? context)
messages (rf/sub [:chats/raw-chat-messages-stream chat-id])
add-padding-bottom? (and community-channel? (not able-to-send-message?))
recording? (rf/sub [:chats/recording?])]
[rn/view {:style style/permission-context-sheet}
[rn/view {:style {:flex-shrink 1}} ;; Keeps flat list on top
[reanimated/flat-list
{:key-fn list-key-fn
@@ -409,12 +201,9 @@
:bounces false
:header (when (= (:chat-type chat) constants/private-group-chat-type)
[list-group-chat-header chat])
:footer [list-footer
{:theme theme
:chat chat
:window-height window-height
:distance-from-list-top distance-from-list-top
:customization-color customization-color}]
:footer [more-messages-loader
{:chat-id chat-id
:window-height window-height}]
:data messages
:render-data {:theme theme
:context context
@@ -444,15 +233,14 @@
:style {:background-color (colors/theme-colors colors/white
colors/neutral-95
theme)}
:content-container-style {:padding-top distance-from-last-message}
:content-container-style (style/list-paddings add-padding-bottom?)
:inverted true
:on-layout #(on-layout
{:event %
:layout-height layout-height
:distance-atom distance-atom
{:event %
:layout-height layout-height
:distance-atom distance-atom
:distance-from-list-top distance-from-list-top
:chat-screen-layout-calculations-complete?
chat-screen-layout-calculations-complete?})
:on-layout-done? on-layout-done?})
:scroll-enabled (not recording?)
:content-inset-adjustment-behavior :never
:scroll-indicator-insets {:right 1}}]]]))
@@ -1,56 +1,43 @@
(ns status-im.contexts.chat.messenger.messages.navigation.style
(:require
[quo.foundations.colors :as colors]
[react-native.reanimated :as reanimated]))
(:require [quo.foundations.colors :as colors]
[status-im.contexts.chat.messenger.messages.constants :as messages.constants]))
(defn navigation-view
[navigation-view-height pinned-banner-height]
[navigation-view-height]
{:top 0
:left 0
:right 0
:position :absolute
:pointer-events :box-none
:height (+ navigation-view-height pinned-banner-height)
:height (+ navigation-view-height messages.constants/pinned-banner-height)
:z-index 1})
(defn animated-background-view
[background-opacity navigation-view-height]
(reanimated/apply-animations-to-style
{:opacity background-opacity}
{:height navigation-view-height
:top 0
:left 0
:right 0
:overflow :hidden
:position :absolute}))
(defn background
[navigation-view-height]
{:height navigation-view-height
:top 0
:left 0
:right 0
:overflow :hidden
:position :absolute})
(defn header-container
[top-insets top-bar-height]
[top-insets]
{:margin-top top-insets
:flex-direction :row
:padding-horizontal 20
:overflow :hidden
:height top-bar-height
:height messages.constants/top-bar-height
:align-items :center})
(defn button-animation-container
[opacity-value]
(reanimated/apply-animations-to-style
{:opacity opacity-value}
{}))
;;;; Content
(defn header-content-container
[header-opacity header-position]
(reanimated/apply-animations-to-style
{:transform [{:translate-y header-position}]
:opacity header-opacity}
{:flex-direction :row
:align-items :center
:flex 1
:margin-horizontal 12
:height 40}))
(def header-content-container
{:flex-direction :row
:align-items :center
:flex 1
:margin-horizontal 12
:height 40})
(def header-text-container
{:margin-left 8})
@@ -7,7 +7,6 @@
[re-frame.db]
[react-native.core :as rn]
[react-native.platform :as platform]
[react-native.reanimated :as reanimated]
[react-native.safe-area :as safe-area]
[status-im.common.home.actions.view :as actions]
[status-im.constants :as constants]
@@ -15,44 +14,26 @@
[status-im.contexts.chat.messenger.messages.navigation.style :as style]
[status-im.contexts.chat.messenger.messages.pin.banner.view :as pin.banner]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.worklets.chat.messenger.messages :as messages.worklets]
[utils.worklets.chat.messenger.navigation :as worklets]))
[utils.re-frame :as rf]))
(defn header-content-container
[{:keys [chat distance-from-list-top all-loaded? chat-screen-layout-calculations-complete?]}]
(let [theme (quo.theme/use-theme)
{:keys [chat-id group-chat chat-type chat-name
emoji color]} chat
display-name (cond
(= chat-type constants/one-to-one-chat-type)
(first (rf/sub
[:contacts/contact-two-names-by-identity
chat-id]))
(= chat-type constants/community-chat-type)
(str "# " chat-name)
:else (str emoji chat-name))
online? (when-not group-chat (rf/sub [:visibility-status-updates/online? chat-id]))
photo-path (if group-chat
(rf/sub [:chats/group-chat-image chat-id])
(rf/sub [:chats/photo-path chat-id]))
header-opacity (worklets/navigation-header-opacity
distance-from-list-top
all-loaded?
chat-screen-layout-calculations-complete?
(if platform/ios?
messages.constants/content-animation-start-position-ios
messages.constants/content-animation-start-position-android))
header-position (worklets/navigation-header-position
distance-from-list-top
all-loaded?
messages.constants/top-bar-height
(if platform/ios?
messages.constants/content-animation-start-position-ios
messages.constants/content-animation-start-position-android))
community-channel? (= chat-type constants/community-chat-type)]
[reanimated/view
{:style (style/header-content-container header-opacity header-position)}
[{:keys [chat-id group-chat chat-type chat-name emoji color] :as _chat}]
(let [theme (quo.theme/use-theme)
display-name (cond
(= chat-type constants/one-to-one-chat-type)
(first (rf/sub [:contacts/contact-two-names-by-identity chat-id]))
(= chat-type constants/community-chat-type)
(str "# " chat-name)
:else
(str emoji chat-name))
online? (when-not group-chat (rf/sub [:visibility-status-updates/online? chat-id]))
photo-path (if group-chat
(rf/sub [:chats/group-chat-image chat-id])
(rf/sub [:chats/photo-path chat-id]))
community-channel? (= chat-type constants/community-chat-type)]
[rn/view {:style style/header-content-container}
(cond
community-channel?
[quo/channel-avatar
@@ -90,30 +71,11 @@
(i18n/label
(if online? :t/online :t/offline))])]]))
(defn animated-background-and-pinned-banner
[{:keys [chat-id navigation-view-height distance-from-list-top all-loaded?]}]
(let [theme (quo.theme/use-theme)
animation-distance messages.constants/header-animation-distance
props {:distance-from-list-top distance-from-list-top
:all-loaded? all-loaded?}
background-opacity (worklets/interpolate-navigation-view-opacity
(assoc props
:start-position
messages.constants/header-container-top-margin
:end-position
(+ animation-distance
messages.constants/header-container-top-margin)))
banner-opacity (worklets/interpolate-navigation-view-opacity
(assoc props
:start-position
(+ navigation-view-height
messages.constants/pinned-banner-animation-start-position)
:end-position
(+ animation-distance
navigation-view-height
messages.constants/pinned-banner-animation-start-position)))]
(defn header-background
[{:keys [chat-id navigation-view-height]}]
(let [theme (quo.theme/use-theme)]
[:<>
[reanimated/view {:style (style/animated-background-view background-opacity navigation-view-height)}
[rn/view {:style (style/background navigation-view-height)}
[quo/blur
{:style {:flex 1}
:blur-amount 20
@@ -121,58 +83,37 @@
:overlay-color (colors/theme-colors colors/white-70-blur colors/neutral-95-opa-70-blur theme)
:blur-radius (if platform/ios? 20 10)}]]
[pin.banner/banner
{:chat-id chat-id
:banner-opacity banner-opacity
:top-offset navigation-view-height}]]))
{:chat-id chat-id
:top-offset navigation-view-height}]]))
(defn view
[{:keys [distance-from-list-top chat-screen-layout-calculations-complete?]}]
[]
(let [{:keys [chat-id chat-type]
:as chat} (rf/sub [:chats/current-chat-chat-view])
all-loaded? (reanimated/use-shared-value false)
all-loaded-sub (rf/sub [:chats/all-loaded? chat-id])
top-insets (safe-area/get-top)
top-bar-height messages.constants/top-bar-height
navigation-view-height (+ top-bar-height top-insets)
navigation-buttons-opacity (worklets/navigation-buttons-complete-opacity
chat-screen-layout-calculations-complete?)
reached-threshold? (messages.worklets/use-messages-scrolled-to-threshold
distance-from-list-top
top-bar-height)
button-background (if reached-threshold? :photo :blur)]
(rn/use-effect (fn [] (reanimated/set-shared-value all-loaded? all-loaded-sub))
[all-loaded-sub])
[rn/view
{:style (style/navigation-view navigation-view-height messages.constants/pinned-banner-height)}
[animated-background-and-pinned-banner
:as chat} (rf/sub [:chats/current-chat-chat-view])
top-insets (safe-area/get-top)
navigation-view-height (+ top-insets messages.constants/top-bar-height)]
[rn/view {:style (style/navigation-view navigation-view-height)}
[header-background
{:chat-id chat-id
:navigation-view-height navigation-view-height
:distance-from-list-top distance-from-list-top
:all-loaded? all-loaded?}]
[rn/view {:style (style/header-container top-insets top-bar-height)}
[reanimated/view {:style (style/button-animation-container navigation-buttons-opacity)}
[quo/button
{:icon-only? true
:type :grey
:background button-background
:size 32
:accessibility-label :back-button
:on-press #(rf/dispatch [:navigate-back])}
(if (= chat-type constants/community-chat-type) :i/arrow-left :i/close)]]
[header-content-container
{:chat chat
:distance-from-list-top distance-from-list-top
:all-loaded? all-loaded?
:chat-screen-layout-calculations-complete? chat-screen-layout-calculations-complete?}]
[reanimated/view {:style (style/button-animation-container navigation-buttons-opacity)}
[quo/button
{:icon-only? true
:type :grey
:background button-background
:size 32
:accessibility-label :options-button
:on-press (fn []
(rf/dispatch [:dismiss-keyboard])
(rf/dispatch [:show-bottom-sheet
{:content (fn [] [actions/chat-actions chat true])}]))}
:i/options]]]]))
:navigation-view-height navigation-view-height}]
[rn/view {:style (style/header-container top-insets)}
[quo/button
{:icon-only? true
:type :grey
:background :blur
:size 32
:accessibility-label :back-button
:on-press #(rf/dispatch [:navigate-back])}
(if (= chat-type constants/community-chat-type) :i/arrow-left :i/close)]
[header-content-container chat]
[quo/button
{:icon-only? true
:type :grey
:background :blur
:size 32
:accessibility-label :options-button
:on-press (fn []
(rf/dispatch [:dismiss-keyboard])
(rf/dispatch [:show-bottom-sheet
{:content (fn [] [actions/chat-actions chat true])}]))}
:i/options]]]))
@@ -1,6 +1,5 @@
(ns status-im.contexts.chat.messenger.messages.pin.banner.style
(:require
[react-native.reanimated :as reanimated]
[status-im.contexts.chat.messenger.messages.constants :as messages.constants]))
(def container
@@ -10,8 +9,6 @@
:right 0
:height messages.constants/pinned-banner-height})
(defn container-animated-style
[top-offset banner-opacity]
(reanimated/apply-animations-to-style
{:opacity banner-opacity}
(assoc container :top top-offset)))
(defn container-with-top-offset
[top-offset]
(assoc container :top top-offset))
@@ -2,30 +2,26 @@
(:require
[quo.core :as quo]
[quo.theme]
[react-native.core :as rn]
[react-native.platform :as platform]
[react-native.reanimated :as reanimated]
[status-im.contexts.chat.messenger.messages.pin.banner.style :as style]
[utils.re-frame :as rf]))
(defn f-banner
[{:keys [chat-id banner-opacity top-offset]} latest-pin-text pins-count]
(let [theme (quo.theme/use-theme)]
[reanimated/view {:style (style/container-animated-style top-offset banner-opacity)}
[quo/blur
{:style style/container
:blur-radius (if platform/ios? 20 10)
:blur-type theme
:blur-amount 20}]
[quo/banner
{:latest-pin-text latest-pin-text
:pins-count pins-count
:on-press (fn []
(rf/dispatch [:dismiss-keyboard])
(rf/dispatch [:pin-message/show-pins-bottom-sheet chat-id]))}]]))
(defn banner
[{:keys [chat-id] :as props}]
(let [latest-pin-text (rf/sub [:chats/last-pinned-message-text chat-id])
[{:keys [chat-id top-offset]}]
(let [theme (quo.theme/use-theme)
latest-pin-text (rf/sub [:chats/last-pinned-message-text chat-id])
pins-count (rf/sub [:chats/pin-messages-count chat-id])]
(when (> pins-count 0)
[:f> f-banner props latest-pin-text pins-count])))
[rn/view {:style (style/container-with-top-offset top-offset)}
[quo/blur
{:style style/container
:blur-radius (if platform/ios? 20 10)
:blur-type theme
:blur-amount 20}]
[quo/banner
{:latest-pin-text latest-pin-text
:pins-count pins-count
:on-press (fn []
(rf/dispatch [:dismiss-keyboard])
(rf/dispatch [:pin-message/show-pins-bottom-sheet chat-id]))}]])))
@@ -7,7 +7,7 @@
[utils.worklets.chat.messenger.composer :as worklets]))
(defn button
[{:keys [chat-list-scroll-y]}]
[chat-list-scroll-y]
(let [{window-height :height} (rn/get-window)
scroll-down-button-opacity (worklets/scroll-down-button-opacity
chat-list-scroll-y
@@ -19,57 +19,50 @@
[utils.re-frame :as rf]))
(defn- footer
[props]
[layout-height]
(let [current-chat-id (rf/sub [:chats/current-chat-id])
able-to-send-message? (rf/sub [:chats/able-to-send-message?])]
(when-not (string/blank? current-chat-id)
(if able-to-send-message?
[composer/view props]
[composer/view layout-height]
[contact-requests.bottom-drawer/view {:contact-id current-chat-id}]))))
(defn- chat-screen
[{:keys [insets] :as props}]
[on-layout-done?]
(let [theme (quo.theme/use-theme)
layout-height (rn/use-ref-atom 0)
distance-from-list-top (reanimated/use-shared-value 0)
chat-list-scroll-y (reanimated/use-shared-value 0)
alert-banners-top-margin (rf/sub [:alert-banners/top-margin])
chat-exist? (rf/sub [:chats/current-chat-exist?])]
(when chat-exist?
[rn/keyboard-avoiding-view
{:style (style/keyboard-avoiding-container theme)
:keyboard-vertical-offset (- (if platform/ios? alert-banners-top-margin 0) (:bottom insets))}
[:<>
[list.view/messages-list-content props]
[scroll-to-bottom/button props]]
[messages.navigation/view props]
[footer props]])))
(defn lazy-chat-screen
[chat-screen-layout-calculations-complete? *screen-loaded?*]
(let [screen-loaded? (rf/sub [:shell/chat-screen-loaded?])
distance-from-list-top (reanimated/use-shared-value 0)
chat-list-scroll-y (reanimated/use-shared-value 0)
props {:insets (safe-area/get-insets)
:content-height (atom 0)
:layout-height (atom 0)
:distance-atom (atom 0)
:distance-from-list-top distance-from-list-top
:chat-list-scroll-y chat-list-scroll-y
:chat-screen-layout-calculations-complete?
chat-screen-layout-calculations-complete?}]
(when *screen-loaded?*
(rn/use-mount #(reset! *screen-loaded?* true)))
(when-not (if *screen-loaded?* @*screen-loaded?* screen-loaded?)
(reanimated/set-shared-value chat-screen-layout-calculations-complete? false)
(reanimated/set-shared-value distance-from-list-top 0)
(reanimated/set-shared-value chat-list-scroll-y 0))
(when (if *screen-loaded?* @*screen-loaded?* screen-loaded?)
[chat-screen props])))
insets (safe-area/get-insets)]
[rn/keyboard-avoiding-view
{:style (style/keyboard-avoiding-container theme)
:keyboard-vertical-offset (- (if platform/ios? alert-banners-top-margin 0)
(:bottom insets))}
[:<>
[list.view/messages-list-content
{:insets insets
:distance-from-list-top distance-from-list-top
:chat-list-scroll-y chat-list-scroll-y
:layout-height layout-height
:on-layout-done? on-layout-done?}]
[scroll-to-bottom/button chat-list-scroll-y]]
[messages.navigation/view]
[footer layout-height]]))
(defn chat
[]
(let [chat-screen-layout-calculations-complete? (reanimated/use-shared-value false)
jump-to-enabled? (ff/enabled? ::ff/shell.jump-to)
*screen-loaded?* (when-not jump-to-enabled?
(reagent/atom false))]
[:<>
[lazy-chat-screen chat-screen-layout-calculations-complete? *screen-loaded?*]
[placeholder.view/view chat-screen-layout-calculations-complete?]]))
(let [on-layout-done? (reagent/atom false)
first-render-done? (reagent/atom false)]
(fn []
(let [chat-exists? (rf/sub [:chats/current-chat-exist?])
jump-to-enabled? (ff/enabled? ::ff/shell.jump-to)
screen-loaded-for-jump-to? (rf/sub [:shell/chat-screen-loaded?])
screen-loaded? (if jump-to-enabled?
screen-loaded-for-jump-to?
@first-render-done?)]
(rn/use-mount #(reset! first-render-done? true))
[:<>
(when (and chat-exists? screen-loaded?)
[chat-screen on-layout-done?])
[placeholder.view/view on-layout-done?]]))))
@@ -1,16 +1,15 @@
(ns status-im.contexts.chat.messenger.placeholder.style
(:require [quo.foundations.colors :as colors]
[react-native.reanimated :as reanimated]))
[react-native.safe-area :as safe-area]))
(defn container
[top opacity z-index theme]
(reanimated/apply-animations-to-style
{:opacity opacity
:z-index z-index}
{:position :absolute
:padding-top top
:top 0
:left 0
:right 0
:bottom 0
:background-color (colors/theme-colors colors/white colors/neutral-95 theme)}))
[theme on-layout-done?]
{:position :absolute
:padding-top (safe-area/get-top)
:top 0
:left 0
:right 0
:bottom 0
:background-color (colors/theme-colors colors/white colors/neutral-95 theme)
:opacity (if on-layout-done? 0 1)
:z-index (if on-layout-done? 0 2)})
@@ -3,11 +3,8 @@
[quo.core :as quo]
[quo.theme]
[react-native.core :as rn]
[react-native.reanimated :as reanimated]
[react-native.safe-area :as safe-area]
[status-im.contexts.chat.messenger.placeholder.style :as style]
[utils.re-frame :as rf]
[utils.worklets.chat.messenger.placeholder :as worklets]))
[utils.re-frame :as rf]))
(defn- loading-skeleton
[]
@@ -17,13 +14,10 @@
:animated? false}])
(defn view
[chat-screen-layout-calculations-complete?]
[on-layout-done?]
(let [theme (quo.theme/use-theme)
top (safe-area/get-top)
chat-exist? (rf/sub [:chats/current-chat-exist?])
opacity (worklets/placeholder-opacity chat-screen-layout-calculations-complete?)
z-index (worklets/placeholder-z-index chat-screen-layout-calculations-complete?)]
[reanimated/view {:style (style/container top opacity z-index theme)}
chat-exist? (rf/sub [:chats/current-chat-exist?])]
[rn/view {:style (style/container theme @on-layout-done?)}
(when-not chat-exist?
[quo/page-nav
{:icon-name :i/arrow-left
@@ -119,38 +119,29 @@
:icon-left :i/communities}
(i18n/label :t/request-to-join)])
(defn- info-button-handler
(defn token-gated-communities-info
[]
(rf/dispatch
[:show-bottom-sheet
{:content
(fn []
[quo/documentation-drawers
{:title (i18n/label :t/token-gated-communities)
:show-button? true
:button-label (i18n/label :t/read-more)
:button-icon :info}
[quo/text {:size :paragraph-2}
(i18n/label :t/token-gated-communities-info)]])}]))
[quo/documentation-drawers {:title (i18n/label :t/token-gated-communities)}
[quo/text {:size :paragraph-2}
(i18n/label :t/token-gated-communities-info)]])
(defn- token-requirements
[{:keys [id color role-permissions?]}]
(let [{:keys [can-request-access?
no-member-permission?
tokens
networks-not-supported?
highest-permission-role]} (rf/sub [:community/token-gated-overview id])
highest-role-text
(i18n/label
(communities.utils/role->translation-key highest-permission-role :t/member))
on-press (rn/use-callback (fn []
(if config/community-accounts-selection-enabled?
(rf/dispatch [:open-modal :community-account-selection-sheet
{:community-id id}])
(rf/dispatch [:open-modal :community-requests-to-join
{:id
id}])))
[id])]
(let [{:keys [can-request-access? no-member-permission? networks-not-supported?
highest-permission-role
tokens]} (rf/sub [:community/token-gated-overview id])
highest-role-text (i18n/label
(communities.utils/role->translation-key highest-permission-role :t/member))
on-press (rn/use-callback
(fn []
(if config/community-accounts-selection-enabled?
(rf/dispatch [:open-modal :community-account-selection-sheet
{:community-id id}])
(rf/dispatch [:open-modal :community-requests-to-join
{:id id}])))
[id])
on-press-info #(rf/dispatch
[:show-bottom-sheet {:content token-gated-communities-info}])]
(cond
networks-not-supported?
[network-not-supported]
@@ -160,13 +151,12 @@
:else
[quo/community-token-gating
{:role highest-role-text
:tokens tokens
{:role highest-role-text
:tokens tokens
:community-color color
:satisfied? can-request-access?
:on-press on-press
:on-press-info
info-button-handler}])))
:satisfied? can-request-access?
:on-press on-press
:on-press-info on-press-info}])))
(defn- join-community
[{:keys [id joined permissions] :as community}]
@@ -0,0 +1,35 @@
(ns status-im.contexts.keycard.authorise.view
(:require [quo.core :as quo]
[react-native.core :as rn]
[status-im.common.events-helper :as events-helper]
[status-im.common.standard-authentication.core :as standard-auth]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.security.core :as security]))
(defn view
[]
(let [profile-name (rf/sub [:profile/name])
profile-picture (rf/sub [:profile/image])
customization-color (rf/sub [:profile/customization-color])]
[:<>
[quo/page-nav
{:key :header
:background :blur
:icon-name :i/close
:on-press events-helper/navigate-back}]
[quo/page-top
{:title (i18n/label :t/authorise-with-password)
:description :context-tag
:context-tag {:full-name profile-name
:profile-picture profile-picture
:customization-color customization-color}}]
[rn/view {:style {:flex 1 :padding-horizontal 20 :justify-content :space-between}}
[quo/text (i18n/label :t/migrate-key-pair-authorise)]
[standard-auth/slide-button
{:size :size-48
:container-style {}
:customization-color customization-color
:track-text (i18n/label :t/slide-to-authorise)
:on-auth-success #(println "TBD" (security/safe-unmask-data %))
:auth-button-label (i18n/label :t/confirm)}]]]))
@@ -0,0 +1,36 @@
(ns status-im.contexts.keycard.check.view
(:require [quo.core :as quo]
[react-native.core :as rn]
[status-im.common.events-helper :as events-helper]
[status-im.common.resources :as resources]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn view
[]
[:<>
[quo/page-nav
{:key :header
:background :blur
:icon-name :i/arrow-left
:on-press events-helper/navigate-back}]
[quo/page-top
{:title (i18n/label :t/check-keycard)
:description :text
:description-text (i18n/label :t/see-keycard-ready)}]
[rn/view {:style {:flex 1 :align-items :center :justify-content :center}}
[rn/image
{:resize-mode :contain
:source (resources/get-image :check-your-keycard)}]]
[quo/divider-label (i18n/label :t/tips-scan-keycard)]
[rn/view {:style {:padding-horizontal 10}}
[quo/markdown-list
{:container-style {:padding-vertical 10}
:description (i18n/label :t/remove-phone-case)}]
[quo/markdown-list
{:container-style {:padding-bottom 25}
:description (i18n/label :t/keep-card-steady)}]]
[quo/button
{:on-press #(rf/dispatch [:keycard/connect])
:container-style {:margin-horizontal 20}}
(i18n/label :t/ready-to-scan)]])
+8 -1
View File
@@ -10,10 +10,15 @@
(defonce ^:private active-listeners (atom []))
(defn register-card-events
(defn unregister-card-events
[]
(doseq [listener @active-listeners]
(keycard/remove-event-listener listener))
(reset! active-listeners nil))
(defn register-card-events
[]
(unregister-card-events)
(reset! active-listeners
[(keycard/on-card-connected #(rf/dispatch [:keycard/on-card-connected]))
(keycard/on-card-disconnected #(rf/dispatch [:keycard/on-card-disconnected]))
@@ -23,7 +28,9 @@
(keycard/on-nfc-timeout #(rf/dispatch [:keycard.ios/on-nfc-timeout])))
(keycard/on-nfc-enabled #(rf/dispatch [:keycard/on-check-nfc-enabled-success true]))
(keycard/on-nfc-disabled #(rf/dispatch [:keycard/on-check-nfc-enabled-success false]))]))
(rf/reg-fx :effects.keycard/register-card-events register-card-events)
(rf/reg-fx :effects.keycard/unregister-card-events unregister-card-events)
(defn check-nfc-enabled
[]
@@ -0,0 +1,36 @@
(ns status-im.contexts.keycard.empty.view
(:require [quo.core :as quo]
[react-native.core :as rn]
[status-im.common.events-helper :as events-helper]
[status-im.common.resources :as resources]
[status-im.contexts.keycard.sheets.migrate.view :as sheets.migrate]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn view
[]
[:<>
[quo/page-nav
{:key :header
:background :blur
:icon-name :i/arrow-left
:on-press events-helper/navigate-back}]
[quo/page-top
{:title (i18n/label :t/keycard-empty)
:description :text
:description-text (i18n/label :t/what-to-do)}]
[rn/view {:style {:padding-horizontal 28 :padding-top 20}}
[quo/small-option-card
{:variant :main
:title (i18n/label :t/import-key-pair-keycard)
:subtitle (i18n/label :t/use-keycard-login-sign)
:button-label (i18n/label :t/import-profile-key-pair)
:accessibility-label :get-keycard
:image (resources/get-image :generate-keys)
:on-press #(rf/dispatch [:show-bottom-sheet
{:theme :dark
:content (fn [] [sheets.migrate/view])}])}]]
[quo/information-box
{:type :default
:style {:margin-top 32 :margin-horizontal 28}}
(i18n/label :t/empty-card-info)]])
@@ -0,0 +1,42 @@
(ns status-im.contexts.keycard.error.view
(:require [quo.core :as quo]
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[status-im.common.events-helper :as events-helper]
[utils.re-frame :as rf]))
(def titles
{:keycard/error.keycard-wrong {:title "Keycard is not empty"
:description "You cant use it to store new keys right now"}
:keycard/error.keycard-unpaired {:title "Keycard is full"
:description "All pairing slots are occupied"}
:keycard/error.keycard-frozen {:title "Keycard is locked"
:description "You cant use it right now"}
:keycard/error.keycard-locked {:title "Keycard is locked"
:description "You cant use it right now"}})
(defn view
[]
(let [{:keys [top bottom]} (safe-area/get-insets)
error (rf/sub [:keycard/application-info-error])
{:keys [title description]} (get titles error)]
[quo/overlay
{:type :shell
:container-style {:padding-top top
:padding-bottom bottom}}
[quo/page-nav
{:key :header
:background :blur
:icon-name :i/arrow-left
:on-press events-helper/navigate-back}]
[quo/page-top
{:title title
:description :text
:description-text description}]
[rn/view {:height 226}]
[rn/view {:padding-horizontal 20}
[quo/info-message
{:container-style {:padding-top 15}
:icon :i/info
:size :default}
"To unlock or factory reset the Keycard, please use the Status desktop app. If you'd like this features on mobile, feel free to upvote them and discuss in the Status community."]]]))
+52 -11
View File
@@ -1,8 +1,8 @@
(ns status-im.contexts.keycard.events
(:require [re-frame.core :as rf]
status-im.contexts.keycard.login.events
status-im.contexts.keycard.nfc-sheet.events
status-im.contexts.keycard.pin.events
status-im.contexts.keycard.sheet.events
status-im.contexts.keycard.sign.events
[status-im.contexts.keycard.utils :as keycard.utils]
[taoensso.timbre :as log]))
@@ -14,7 +14,9 @@
(rf/reg-event-fx :keycard.ios/on-nfc-user-cancelled
(fn [{:keys [db]}]
(log/debug "[keycard] nfc user cancelled")
{:db (assoc-in db [:keycard :pin :status] nil)
{:db (-> db
(assoc-in [:keycard :pin :status] nil)
(assoc-in [:keycard :on-nfc-cancelled-event-vector] nil))
:fx [(when-let [on-nfc-cancelled-event-vector (get-in db [:keycard :on-nfc-cancelled-event-vector])]
[:dispatch on-nfc-cancelled-event-vector])]}))
@@ -42,12 +44,6 @@
{:db (assoc-in db [:keycard :card-connected?] false)
:fx [[:dispatch-later [{:ms 500 :dispatch [:keycard.ios/start-nfc]}]]]}))
(rf/reg-event-fx :keycard/get-application-info
(fn [_ [{:keys [on-success on-failure]}]]
(log/debug "[keycard] get-application-info")
{:effects.keycard/get-application-info {:on-success on-success
:on-failure on-failure}}))
(rf/reg-event-fx :keycard/on-retrieve-pairings-success
(fn [{:keys [db]} [pairings]]
{:db (assoc-in db [:keycard :pairings] pairings)
@@ -60,7 +56,7 @@
(rf/reg-event-fx :keycard/on-action-with-pin-error
(fn [{:keys [db]} [error]]
(log/debug "[keycard] get keys error: " error)
(log/debug "[keycard] on-action-with-pin-error: " error)
(let [tag-was-lost? (keycard.utils/tag-lost? (:error error))
pin-retries-count (keycard.utils/pin-retries (:error error))]
(if tag-was-lost?
@@ -69,7 +65,52 @@
{:effects.utils/show-popup {:title "wrong-keycard"}}
{:db (-> db
(assoc-in [:keycard :application-info :pin-retry-counter] pin-retries-count)
(update-in [:keycard :pin] assoc :status :error))
:fx [[:dispatch [:keycard/hide-connection-sheet]]
(assoc-in [:keycard :pin :status] :error))
:fx [[:dispatch [:keycard/disconnect]]
(when (zero? pin-retries-count)
[:effects.utils/show-popup {:title "frozen-keycard"}])]})))))
(rf/reg-event-fx :keycard/on-get-application-info-success
(fn [{:keys [db]} [application-info {:keys [key-uid on-success-fx]}]]
(if-let [error (keycard.utils/validate-application-info key-uid application-info)]
(case error
:keycard/error.not-keycard
{:fx [[:dispatch [:keycard/disconnect]]
[:dispatch [:open-modal :screen/keycard.not-keycard]]]}
:keycard/error.keycard-blank
{:fx [[:dispatch [:keycard/disconnect]]
[:dispatch [:open-modal :screen/keycard.empty]]]}
{:db (assoc-in db [:keycard :application-info-error] error)
:fx [[:dispatch [:keycard/disconnect]]
[:dispatch [:open-modal :screen/keycard.error]]]})
{:db (-> db
(assoc-in [:keycard :application-info] application-info)
(assoc-in [:keycard :pin :status] :verifying))
:fx on-success-fx})))
(rf/reg-event-fx :keycard/get-application-info
(fn [_ [{:keys [on-success on-failure]}]]
{:effects.keycard/get-application-info {:on-success on-success :on-failure on-failure}}))
(rf/reg-event-fx :keycard/cancel-connection
(fn [{:keys [db]}]
{:db (-> db
(assoc-in [:keycard :on-card-connected-event-vector] nil)
(assoc-in [:keycard :on-nfc-cancelled-event-vector] nil))}))
(rf/reg-event-fx :keycard/disconnect
(fn [_ _]
{:fx [[:dispatch [:keycard/cancel-connection]]
[:dispatch [:keycard/hide-connection-sheet]]]}))
(rf/reg-event-fx :keycard/connect
(fn [{:keys [db]} [args]]
(let [connected? (get-in db [:keycard :card-connected?])
event-vector [:keycard/get-application-info
{:on-success #(rf/dispatch [:keycard/on-get-application-info-success % args])}]]
{:db (assoc-in db [:keycard :on-card-connected-event-vector] event-vector)
:fx [[:dispatch
[:keycard/show-connection-sheet
{:on-cancel-event-vector [:keycard/cancel-connection]}]]
(when connected?
[:dispatch event-vector])]})))
@@ -1,7 +1,5 @@
(ns status-im.contexts.keycard.login.events
(:require [status-im.contexts.keycard.utils :as keycard.utils]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
(:require [utils.re-frame :as rf]))
(rf/reg-event-fx :keycard.login/on-get-keys-success
(fn [{:keys [db]} [data]]
@@ -16,7 +14,7 @@
:password encryption-public-key
:key-uid key-uid
:name (:name profile)))
:fx [[:dispatch [:keycard/hide-connection-sheet]]
:fx [[:dispatch [:keycard/disconnect]]
[:effects.keycard/login-with-keycard
{:password encryption-public-key
:whisper-private-key whisper-private-key
@@ -33,36 +31,8 @@
:password encryption-public-key
:key-uid key-uid
:name (:name profile)))
:fx [[:dispatch [:keycard/hide-connection-sheet]]
:fx [[:dispatch [:keycard/disconnect]]
[:effects.keycard/login-with-keycard
{:password encryption-public-key
:whisper-private-key whisper-private-key
:key-uid key-uid}]]}))))
(rf/reg-event-fx :keycard.login/on-get-application-info-success
(fn [{:keys [db]} [application-info {:keys [key-uid on-read-fx]}]]
(let [error (keycard.utils/validate-application-info key-uid application-info)]
(if error
{:effects.utils/show-popup {:title (str error)}}
{:db (-> db
(assoc-in [:keycard :application-info] application-info)
(assoc-in [:keycard :pin :status] :verifying))
:fx on-read-fx}))))
(rf/reg-event-fx :keycard.login/cancel-reading-card
(fn [{:keys [db]}]
{:db (assoc-in db [:keycard :on-card-connected-event-vector] nil)}))
(rf/reg-event-fx :keycard/read-card
(fn [{:keys [db]} [args]]
(let [connected? (get-in db [:keycard :card-connected?])
event-vector [:keycard/get-application-info
{:on-success #(rf/dispatch [:keycard.login/on-get-application-info-success %
args])}]]
(log/debug "[keycard] proceed-to-login")
{:db (assoc-in db [:keycard :on-card-connected-event-vector] event-vector)
:fx [[:dispatch
[:keycard/show-connection-sheet
{:on-cancel-event-vector [:keycard.login/cancel-reading-card]}]]
(when connected?
[:dispatch event-vector])]})))
@@ -1,18 +1,16 @@
(ns status-im.contexts.keycard.sheet.events
(ns status-im.contexts.keycard.nfc-sheet.events
(:require [re-frame.core :as rf]
[react-native.platform :as platform]
[taoensso.timbre :as log]))
(rf/reg-event-fx :keycard/show-connection-sheet-component
(fn [{:keys [db]} [{:keys [on-cancel-event-vector]}]]
{:db (assoc-in db [:keycard :connection-sheet-opts] {:on-close #(rf/dispatch on-cancel-event-vector)})
:fx [[:dismiss-keyboard true]
[:show-nfc-sheet nil]]}))
(rf/reg-event-fx :keycard/show-connection-sheet
(fn [_ [args]]
(fn [{:keys [db]} [{:keys [on-cancel-event-vector]} :as args]]
(if platform/android?
{:dispatch [:keycard/show-connection-sheet-component args]}
{:db (assoc-in db
[:keycard :connection-sheet-opts]
{:on-close #(rf/dispatch on-cancel-event-vector)})
:fx [[:dismiss-keyboard true]
[:show-nfc-sheet nil]]}
{:effects.keycard.ios/start-nfc
{:on-success
(fn []
@@ -1,8 +1,9 @@
(ns status-im.contexts.keycard.sheet.view
(ns status-im.contexts.keycard.nfc-sheet.view
(:require [quo.foundations.colors :as colors]
quo.theme
[react-native.core :as rn]
[status-im.common.resources :as resources]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn connect-keycard
@@ -18,13 +19,13 @@
:padding-vertical 30
:background-color (colors/theme-colors colors/white colors/neutral-95 theme)}}
[rn/text {:style {:font-size 26 :color "#9F9FA5" :margin-bottom 36}}
"Ready to Scan"]
(i18n/label :t/ready-to-scan)]
[rn/image
{:source (resources/get-image :nfc-prompt)}]
[rn/text {:style {:font-size 16 :color :white :margin-vertical 36}}
(if connected?
"Connected. Dont move your card."
"Hold your phone near a Status Keycard")]
(i18n/label :t/connected-dont-move)
(i18n/label :t/hold-phone-near-keycard))]
[rn/pressable
{:on-press (fn []
(when on-close (on-close))
@@ -33,4 +34,4 @@
[rn/view
{:style {:background-color "#8E8E93" :flex 1 :align-items :center :padding 18 :border-radius 10}}
[rn/text {:style {:color :white :font-size 16}}
"Cancel"]]]]]))
(i18n/label :t/cancel)]]]]]))
@@ -0,0 +1,28 @@
(ns status-im.contexts.keycard.not-keycard.view
(:require [quo.core :as quo]
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[status-im.common.events-helper :as events-helper]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn view
[]
(let [{:keys [top bottom]} (safe-area/get-insets)]
[quo/overlay
{:type :shell
:container-style {:padding-top top
:padding-bottom bottom}}
[quo/page-nav
{:key :header
:background :blur
:icon-name :i/arrow-left
:on-press events-helper/navigate-back}]
[quo/page-top
{:title (i18n/label :t/oops-not-keycard)
:description :text
:description-text (i18n/label :t/make-sure-keycard)}]
[rn/view {:flex 1}]
[rn/view {:padding-horizontal 20}
[quo/button {:on-press #(rf/dispatch [:keycard/connect])}
(i18n/label :t/try-again)]]]))
@@ -0,0 +1,55 @@
(ns status-im.contexts.keycard.pin.create.view
(:require [clojure.string :as string]
[quo.core :as quo]
[react-native.core :as rn]
[status-im.constants :as constants]
[utils.i18n :as i18n]))
(defn view
[{:keys [on-complete]}]
(let [[pin set-pin] (rn/use-state "")
[first-pin set-first-pin] (rn/use-state "")
[error? set-error] (rn/use-state false)
[stage set-stage] (rn/use-state :create)
pin-empty? (string/blank? pin)
on-delete (rn/use-callback
(fn []
(set-error false)
(if (= (count pin) 1)
(do
(set-pin "")
(set-stage :create))
(when (and pin (pos? (count pin)))
(set-pin (.slice pin 0 -1)))))
[pin])
on-press (rn/use-callback
(fn [new-symbol]
(let [new-pin (str pin new-symbol)]
(when (<= (count new-pin) constants/pincode-length)
(set-pin new-pin)
(when (= constants/pincode-length (count new-pin))
(if (= :repeat stage)
(if (= first-pin new-pin)
(on-complete new-pin)
(set-error true))
(do
(set-pin "")
(set-first-pin new-pin)
(set-stage :repeat)))))))
[pin stage first-pin])]
[rn/view {:style {:padding-bottom 12 :flex 1}}
[quo/drawer-top
{:title (if (= :create stage)
(i18n/label :t/create-keycard-pin)
(i18n/label :t/repeat-keycard-pin))}]
[rn/view {:style {:flex 1 :justify-content :center :align-items :center :padding-vertical 34}}
[quo/pin-input
{:blur? false
:number-of-pins constants/pincode-length
:number-of-filled-pins (count pin)
:error? error?
:info (when error? (i18n/label :t/pin-not-match))}]]
[quo/numbered-keyboard
{:delete-key? (not pin-empty?)
:on-delete on-delete
:on-press on-press}]]))
@@ -0,0 +1,38 @@
(ns status-im.contexts.keycard.sheets.migrate.view
(:require [quo.core :as quo]
[react-native.core :as rn]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn view
[]
(let [profile-name (rf/sub [:profile/name])
profile-picture (rf/sub [:profile/image])
customization-color (rf/sub [:profile/customization-color])
recovery-phrase-backed-up? (rf/sub [:profile/recovery-phrase-backed-up?])]
[:<>
[quo/drawer-top
{:type :context-tag
:context-tag-type :default
:title (i18n/label :t/migrate-key-pair-keycard)
:full-name profile-name
:profile-picture profile-picture
:customization-color customization-color}]
[rn/view {:style {:padding-horizontal 20}}
[quo/text {}
(i18n/label :t/migrate-key-pair-keycard-default-key {:name profile-name})]
[quo/information-box
{:type :default
:style {:margin-top 20 :margin-bottom 12}}
(i18n/label :t/migrate-key-pair-keycard-info)]]
[quo/bottom-actions
{:actions :two-actions
:button-one-label (i18n/label :t/continue)
:button-one-props {:on-press #(if recovery-phrase-backed-up?
(rf/dispatch [:open-modal :screen/use-recovery-phrase
{:on-success (fn [])}])
(rf/dispatch [:open-modal :screen/backup-recovery-phrase
{:on-success (fn [])}]))}
:button-two-label (i18n/label :t/cancel)
:button-two-props {:type :grey
:on-press #(rf/dispatch [:hide-bottom-sheet])}}]]))
+14 -13
View File
@@ -14,16 +14,17 @@
path (get-in db [:wallet :accounts current-address :path])
key-uid (get-in db [:profile/profile :key-uid])]
{:fx [[:dispatch
[:keycard/read-card
{:key-uid key-uid
:on-read-fx [[:effects.keycard/sign
{:pin pin-text
:path path
:hash (utils.address/naked-address tx-hash)
:on-success
#(do
(rf/dispatch [:keycard/hide-connection-sheet])
(rf/dispatch
[:wallet/proceed-with-transactions-signatures
(get-signature-map tx-hash %)]))
:on-failure #(rf/dispatch [:keycard/on-action-with-pin-error %])}]]}]]]})))
[:keycard/connect
{:key-uid key-uid
:on-success-fx [[:effects.keycard/sign
{:pin pin-text
:path path
:hash (utils.address/naked-address tx-hash)
:on-success
#(do
(rf/dispatch [:keycard/disconnect])
(rf/dispatch
[:wallet/proceed-with-transactions-signatures
(get-signature-map tx-hash %)]))
:on-failure #(rf/dispatch [:keycard/on-action-with-pin-error
%])}]]}]]]})))
+9 -6
View File
@@ -18,28 +18,31 @@
(defn validate-application-info
[profile-key-uid {:keys [key-uid paired? pin-retry-counter puk-retry-counter] :as application-info}]
(let [profile-mismatch? (or (nil? profile-key-uid) (not= profile-key-uid key-uid))]
(log/debug "[keycard] login-with-keycard"
(log/debug "[keycard]" "login-with-keycard"
"empty application info" (empty? application-info)
"no key-uid" (empty? key-uid)
"profile-mismatch?" profile-mismatch?
"no pairing" paired?)
(cond
(empty? application-info)
:not-keycard
:keycard/error.not-keycard
(empty? (:key-uid application-info))
:keycard-blank
:keycard/error.keycard-blank
profile-mismatch?
:keycard-wrong
:keycard/error.keycard-wrong
(not paired?)
:keycard-unpaired
:keycard/error.keycard-unpaired
(and (zero? pin-retry-counter)
(or (nil? puk-retry-counter)
(pos? puk-retry-counter)))
nil
:keycard/error.keycard-frozen
(zero? puk-retry-counter)
:keycard/error.keycard-locked
:else
nil)))
@@ -58,7 +58,7 @@
(reset! shell.state/screen-height height)
(async-storage/set-item! :screen-height height))))
(defn f-view
(defn view
[dark-overlay?]
(let [view-id (rf/sub [:view-id])
animate? (not dark-overlay?)
@@ -94,5 +94,3 @@
:blur-radius (if platform/android? 25 10)
:blur-type :transparent
:overlay-color :transparent}])]))
(defn view [dark-overlay?] [:f> f-view dark-overlay?])
@@ -131,15 +131,6 @@
[:open-modal :screen/onboarding.enable-biometrics]
[:open-modal :screen/onboarding.enable-notifications])}))
(rf/defn seed-phrase-entered
{:events [:onboarding/seed-phrase-entered]}
[_ seed-phrase on-error]
{:multiaccount/validate-mnemonic [seed-phrase
(fn [mnemonic key-uid]
(re-frame/dispatch [:onboarding/seed-phrase-validated
mnemonic key-uid]))
on-error]})
(rf/defn seed-phrase-validated
{:events [:onboarding/seed-phrase-validated]}
[{:keys [db]} seed-phrase key-uid]
@@ -1,4 +1,4 @@
(ns status-im.contexts.wallet.add-account.create-account.new-keypair.backup-recovery-phrase.style
(ns status-im.contexts.profile.backup-recovery-phrase.style
(:require
[quo.foundations.colors :as colors]
[react-native.platform :as platform]))
@@ -1,4 +1,4 @@
(ns status-im.contexts.wallet.add-account.create-account.new-keypair.backup-recovery-phrase.view
(ns status-im.contexts.profile.backup-recovery-phrase.view
(:require
[clojure.string :as string]
[native-module.core :as native-module]
@@ -6,8 +6,7 @@
[quo.theme :as quo.theme]
[react-native.core :as rn]
[reagent.core :as reagent]
[status-im.contexts.wallet.add-account.create-account.new-keypair.backup-recovery-phrase.style :as
style]
[status-im.contexts.profile.backup-recovery-phrase.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.security.core :as security]))
@@ -45,20 +44,20 @@
(defn view
[]
(let [step-labels [:t/backup-step-1 :t/backup-step-2 :t/backup-step-3
:t/backup-step-4]
checked? (reagent/atom
{:0 false
:1 false
:2 false
:3 false})
revealed? (reagent/atom false)
customization-color (rf/sub [:profile/customization-color])
seed-phrase (reagent/atom [])
random-phrase (reagent/atom [])]
(let [step-labels [:t/backup-step-1 :t/backup-step-2 :t/backup-step-3
:t/backup-step-4]
checked? (reagent/atom
{:0 false
:1 false
:2 false
:3 false})
revealed? (reagent/atom false)
customization-color (rf/sub [:profile/customization-color])
{:keys [on-success]} (rf/sub [:get-screen-params])
seed-phrase (reagent/atom [])
random-phrase (reagent/atom [])]
(fn []
(let [theme (quo.theme/use-theme)]
(rn/use-mount
(fn []
(native-module/get-random-mnemonic #(reset! seed-phrase (string/split % #"\s")))
@@ -107,10 +106,10 @@
:button-one-label (i18n/label :t/i-have-written)
:button-one-props {:disabled? (some false? (vals @checked?))
:customization-color customization-color
:on-press #(rf/dispatch [:wallet/store-new-seed-phrase
{:seed-phrase (security/mask-data
@seed-phrase)
:random-phrase @random-phrase}])}}]
:on-press #(on-success {:masked-seed-phrase
(security/mask-data
@seed-phrase)
:random-phrase @random-phrase})}}]
[quo/text
{:size :paragraph-2
:style (style/description-text theme)}
@@ -102,8 +102,6 @@
[:dispatch-later [{:ms 1500 :dispatch [:profile.login/non-critical-initialization]}]]
[:dispatch [:network/check-expensive-connection]]
[:profile.settings/get-profile-picture key-uid]
(when (ff/enabled? ::ff/wallet.wallet-connect)
[:dispatch [:wallet-connect/init]])
(when notifications-enabled?
[:effects/push-notifications-enable])]})))
@@ -242,12 +242,13 @@
{:on-complete
(fn [pin-text]
(rf/dispatch
[:keycard/read-card
{:key-uid key-uid
:on-read-fx [[:effects.keycard/get-keys
{:pin pin-text
:on-success #(rf/dispatch [:keycard.login/on-get-keys-success %])
:on-failure #(rf/dispatch [:keycard/on-action-with-pin-error %])}]]}]))}]
[:keycard/connect
{:key-uid key-uid
:on-success-fx [[:effects.keycard/get-keys
{:pin pin-text
:on-success #(rf/dispatch [:keycard.login/on-get-keys-success %])
:on-failure #(rf/dispatch [:keycard/on-action-with-pin-error
%])}]]}]))}]
[password-input])]
(when-not keycard-pairing
[quo/button
@@ -1,9 +1,31 @@
(ns status-im.contexts.profile.recover.effects
(:require
[native-module.core :as native-module]
[utils.re-frame :as rf]))
[promesa.core :as promesa]
[utils.re-frame :as rf]
[utils.security.core :as security]
[utils.transforms :as transforms]))
(rf/reg-fx :effects.profile/restore-and-login
(fn [request]
;;"node.login" signal will be triggered as a callback
(native-module/restore-account-and-login request)))
(defn validate-mnemonic
[mnemonic]
(-> mnemonic
(security/safe-unmask-data)
(native-module/validate-mnemonic)
(promesa/then (fn [result]
(let [{:keys [keyUID]} (transforms/json->clj result)]
{:key-uid keyUID})))))
(rf/reg-fx :effects.profile/validate-recovery-phrase
(fn [[mnemonic on-success on-error]]
(-> (validate-mnemonic mnemonic)
(promesa/then (fn [{:keys [key-uid]}]
(when (fn? on-success)
(on-success mnemonic key-uid))))
(promesa/catch (fn [error]
(when (and error (fn? on-error))
(on-error error)))))))
@@ -24,3 +24,7 @@
:imagePath (profile.config/strip-file-prefix image-path)
:customizationColor (or color constants/profile-default-color)
:fetchBackup true)}))
(rf/reg-event-fx :profile.recover/validate-recovery-phrase
(fn [_ [phrase {:keys [on-success on-error]}]]
{:effects.profile/validate-recovery-phrase [phrase on-success on-error]}))
@@ -1,6 +1,7 @@
(ns status-im.contexts.profile.settings.list-items
(:require [status-im.common.not-implemented :as not-implemented]
[status-im.config :as config]
[status-im.feature-flags :as ff]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
@@ -54,9 +55,9 @@
:image :icon
:blur? true
:action :arrow})
(when config/show-not-implemented-features?
(when (ff/enabled? ::ff/keycard.migrate-profile)
{:title (i18n/label :t/keycard)
:on-press not-implemented/alert
:on-press #(rf/dispatch [:open-modal :screen/settings.keycard])
:image-props :i/keycard
:image :icon
:blur? true
@@ -49,13 +49,14 @@
(defn view
[]
(let [theme (quo.theme/use-theme)
insets (safe-area/get-insets)
customization-color (rf/sub [:profile/customization-color])
scroll-y (reanimated/use-shared-value 0)
profile (rf/sub [:profile/profile])
full-name (profile.utils/displayed-name profile)
on-scroll (rn/use-callback #(scroll-handler % scroll-y))]
(let [theme (quo.theme/use-theme)
insets (safe-area/get-insets)
customization-color (rf/sub [:profile/customization-color])
scroll-y (reanimated/use-shared-value 0)
profile (rf/sub [:profile/profile])
recovery-phrase-backed-up? (rf/sub [:profile/recovery-phrase-backed-up?])
full-name (profile.utils/displayed-name profile)
on-scroll (rn/use-callback #(scroll-handler % scroll-y))]
[quo/overlay {:type :shell}
[rn/view
{:style (style/navigation-wrapper {:customization-color customization-color
@@ -79,7 +80,7 @@
profile)}}])}]}]]
[rn/flat-list
{:header [settings.header/view {:scroll-y scroll-y}]
:data (settings.items/items (boolean (seq (:mnemonic profile))))
:data (settings.items/items (not recovery-phrase-backed-up?))
:shows-vertical-scroll-indicator false
:render-fn settings-category-view
:get-item-layout get-item-layout
@@ -0,0 +1,45 @@
(ns status-im.contexts.settings.keycard.view
(:require [quo.core :as quo]
[quo.foundations.colors :as colors]
[react-native.core :as rn]
[status-im.common.events-helper :as events-helper]
[status-im.common.resources :as resources]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn view
[]
(let [keycard-profile? (rf/sub [:keycard/keycard-profile?])]
[:<>
[quo/page-nav
{:key :header
:background :blur
:icon-name :i/arrow-left
:on-press events-helper/navigate-back}]
[quo/page-top
{:title (i18n/label :t/keycard)}]
(if keycard-profile?
[:<>]
[rn/view {:style {:padding-horizontal 28 :padding-top 20}}
[quo/small-option-card
{:variant :main
:title (i18n/label :t/get-keycard)
:subtitle (i18n/label :t/secure-wallet-card)
:button-label (i18n/label :t/buy-keycard)
:accessibility-label :get-keycard
:image (resources/get-image :generate-keys)
:on-press #()}]
[rn/view {:style {:margin-top 24}}
[quo/text
{:style {:margin-bottom 12
:color colors/white-opa-70}
:size :paragraph-2
:weight :medium}
(i18n/label :t/own-keycard)]]
[quo/small-option-card
{:variant :icon
:title (i18n/label :t/setup-keycard)
:subtitle (i18n/label :t/ready-keycard)
:accessibility-label :setup-keycard
:image (resources/get-image :use-keycard)
:on-press #(rf/dispatch [:open-modal :screen/keycard.check])}]])]))
@@ -160,7 +160,7 @@
(defn wallet-validate-seed-phrase
[_ [seed-phrase on-success on-error]]
{:fx [[:multiaccount/validate-mnemonic [seed-phrase on-success on-error]]]})
{:fx [[:effects.profile/validate-recovery-phrase [seed-phrase on-success on-error]]]})
(rf/reg-event-fx :wallet/validate-seed-phrase wallet-validate-seed-phrase)
@@ -101,7 +101,7 @@
seed-phrase-masked (security/mask-data "seed phrase")
on-success #(prn "success")
on-error #(prn "error")
expected {:fx [[:multiaccount/validate-mnemonic
expected {:fx [[:effects.profile/validate-recovery-phrase
[seed-phrase-masked on-success on-error]]]}]
(is (= expected
(sut/wallet-validate-seed-phrase
@@ -28,7 +28,8 @@
;; TODO: Replace with correct enum values once status-go implements them.
(def ^:const tx 66612)
(def ^:const system 66614)
(def ^:const system
#{new-installation-received new-installation-created})
(def ^:const membership
"Membership is like a logical group of notifications with different types, i.e.
+20 -18
View File
@@ -26,7 +26,8 @@
(let [selected-tab (or (rf/sub [:wallet/account-tab]) first-tab-id)
{:keys [name color formatted-balance
watch-only?]} (rf/sub [:wallet/current-viewing-account])
customization-color (rf/sub [:profile/customization-color])]
customization-color (rf/sub [:profile/customization-color])
testnet-mode? (rf/sub [:profile/test-networks-enabled?])]
(hot-reload/use-safe-unmount (fn []
(rf/dispatch [:wallet/close-account-page])
(rf/dispatch [:wallet/clean-current-viewing-account])))
@@ -46,23 +47,24 @@
[quo/wallet-graph {:time-frame :empty}])
(when (not watch-only?)
[quo/wallet-ctas
{:container-style style/cta-buttons
:send-action (fn []
(rf/dispatch [:wallet/clean-send-data])
(rf/dispatch [:wallet/wizard-navigate-forward
{:start-flow? true
:flow-id :wallet-send-flow}]))
:receive-action #(rf/dispatch [:open-modal :screen/wallet.share-address
{:status :receive}])
:buy-action #(rf/dispatch [:show-bottom-sheet
{:content buy-token/view}])
:bridge-action (fn []
(rf/dispatch [:wallet/clean-send-data])
(rf/dispatch [:wallet/start-bridge]))
:swap-action (when (ff/enabled? ::ff/wallet.swap)
(fn []
(rf/dispatch [:wallet.tokens/get-token-list])
(rf/dispatch [:open-modal :screen/wallet.swap-select-asset-to-pay])))}])
{:container-style style/cta-buttons
:send-action (fn []
(rf/dispatch [:wallet/clean-send-data])
(rf/dispatch [:wallet/wizard-navigate-forward
{:start-flow? true
:flow-id :wallet-send-flow}]))
:receive-action #(rf/dispatch [:open-modal :screen/wallet.share-address
{:status :receive}])
:buy-action #(rf/dispatch [:show-bottom-sheet
{:content buy-token/view}])
:bridge-action (fn []
(rf/dispatch [:wallet/clean-send-data])
(rf/dispatch [:wallet/start-bridge]))
:swap-action (fn []
(rf/dispatch [:wallet.tokens/get-token-list])
(rf/dispatch [:open-modal :screen/wallet.swap-select-asset-to-pay]))
:bridge-disabled? testnet-mode?
:swap-disabled? testnet-mode?}])
[quo/tabs
{:style style/tabs
:size 32
@@ -61,16 +61,6 @@
(rf/reg-event-fx :wallet/seed-phrase-validated seed-phrase-validated)
(defn seed-phrase-entered
[_ [seed-phrase on-error]]
{:fx [[:multiaccount/validate-mnemonic
[seed-phrase
(fn [mnemonic key-uid]
(rf/dispatch [:wallet/seed-phrase-validated mnemonic key-uid on-error]))
on-error]]]})
(rf/reg-event-fx :wallet/seed-phrase-entered seed-phrase-entered)
(defn store-account-generated-with-mnemonic
[{:keys [db]} [{:keys [new-account-data keypair-name]}]]
(let [new-account (update new-account-data :mnemonic security/mask-data)]
@@ -16,13 +16,21 @@
[[{:icon :i/add
:accessibility-label :generate-new-keypair
:label (i18n/label :t/generate-new-keypair)
:on-press #(rf/dispatch [:navigate-to :screen/wallet.backup-recovery-phrase])}
:on-press #(rf/dispatch [:navigate-to :screen/backup-recovery-phrase
{:on-success (fn [{:keys [masked-seed-phrase random-phrase]}]
(rf/dispatch [:wallet/store-new-seed-phrase
{:seed-phrase masked-seed-phrase
:random-phrase
random-phrase}]))}])}
{:icon :i/seed
:accessibility-label :import-using-phrase
:label (i18n/label :t/import-using-phrase)
:add-divider? true
:on-press #(rf/dispatch [:navigate-to :screen/wallet.enter-seed-phrase
{:recovering-keypair? true}])}
:on-press (fn []
(rf/dispatch [:navigate-to :screen/use-recovery-phrase
{:on-success (fn [{:keys [key-uid phrase on-error]}]
(rf/dispatch [:wallet/seed-phrase-validated
phrase key-uid on-error]))}]))}
(when (ff/enabled? ::ff/wallet.import-private-key)
{:icon :i/key
:accessibility-label :import-private-key
@@ -0,0 +1,72 @@
(ns status-im.contexts.wallet.buy-crypto.events
(:require [re-frame.core :as rf]
[react-native.core :as rn]
[status-im.contexts.wallet.sheets.buy-network-selection.view :as buy-network-selection]
[status-im.contexts.wallet.sheets.select-asset.view :as select-asset]
[utils.i18n :as i18n]))
(rf/reg-event-fx :wallet.buy-crypto/select-provider
(fn [{:keys [db]} [{:keys [account provider recurrent?]}]]
(let [swap-network (get-in db [:wallet :ui :swap :network])]
{:db (-> db
(assoc-in [:wallet :ui :buy-crypto :account] account)
(assoc-in [:wallet :ui :buy-crypto :provider] provider)
(assoc-in [:wallet :ui :buy-crypto :recurrent?] recurrent?))
:fx [(if swap-network
[:dispatch [:wallet.buy-crypto/select-network swap-network]]
[:dispatch
[:show-bottom-sheet
{:content
(fn []
[buy-network-selection/view
{:provider provider
:account account}])}]])]})))
(rf/reg-event-fx :wallet.buy-crypto/select-network
(fn [{:keys [db]} [network]]
(let [provider (get-in db [:wallet :ui :buy-crypto :provider])
network-chain-id (:chain-id network)
supported-tokens (:supported-tokens provider)
supported-tokens-on-network (set (map :symbol
(filter (fn [token]
(= (:chain-id token) network-chain-id))
supported-tokens)))]
{:db (assoc-in db [:wallet :ui :buy-crypto :network] network)
:fx [[:dispatch
[:show-bottom-sheet
{:content
(fn []
[select-asset/view
{:title (i18n/label :t/select-asset-to-buy)
:network network
:provider provider
:on-select (fn [token]
(rf/dispatch [:wallet/get-crypto-on-ramp-url {:token token}]))
:hide-token-fn (fn [_ token]
(not (contains? supported-tokens-on-network
(:symbol token))))}])}]]]})))
(rf/reg-event-fx
:wallet/get-crypto-on-ramp-url
(fn [{:keys [db]} [{:keys [token]}]]
(let [account (get-in db [:wallet :ui :buy-crypto :account])
provider (get-in db [:wallet :ui :buy-crypto :provider])
network (get-in db [:wallet :ui :buy-crypto :network])
recurrent? (get-in db [:wallet :ui :buy-crypto :recurrent?])
provider-id (:id provider)
parameters {:symbol (:symbol token)
:destAddress (:address account)
:chainID (:chain-id network)
:isRecurrent recurrent?}]
{:fx [[:json-rpc/call
[{:method "wallet_getCryptoOnRampURL"
:params [provider-id parameters]
:on-success (fn [url]
(rf/dispatch [:navigate-back])
(rf/dispatch [:wallet.buy-crypto/clean-all])
(rn/open-url url))
:on-error [:wallet/log-rpc-error {:event :wallet/get-crypto-on-ramps}]}]]]})))
(rf/reg-event-fx :wallet.buy-crypto/clean-all
(fn [{:keys [db]}]
{:db (update-in db [:wallet :ui] dissoc :buy-crypto)}))
@@ -8,19 +8,20 @@
[utils.re-frame :as rf]))
(defn get-bottom-sheet-args
[switcher-type]
[switcher-type params]
(case switcher-type
:account-options {:content account-options/view
:hide-handle? true}
:select-account {:content select-account/view}
:select-account {:content (fn []
[select-account/view params])}
nil))
(defn- on-dapps-press
[switcher-type]
(rf/dispatch [:show-bottom-sheet (get-bottom-sheet-args switcher-type)]))
[switcher-type params]
(rf/dispatch [:show-bottom-sheet (get-bottom-sheet-args switcher-type params)]))
(defn view
[{:keys [type on-press accessibility-label icon-name switcher-type margin-top]
[{:keys [type on-press accessibility-label icon-name switcher-type margin-top params]
:or {icon-name :i/close
accessibility-label :top-bar
switcher-type :account-options
@@ -45,6 +46,6 @@
(when-not sending-collectible?
{:content-type :account-switcher
:customization-color color
:on-press #(on-dapps-press switcher-type)
:on-press #(on-dapps-press switcher-type params)
:emoji emoji
:type (when watch-only? :watch-only)})]}]))
@@ -32,9 +32,11 @@
:selected)}]))
(defn view
[{:keys [content-container-style search-text on-token-press preselected-token-symbol]
[{:keys [content-container-style search-text on-token-press preselected-token-symbol chain-ids]
:or {content-container-style {:padding-horizontal 8}}}]
(let [filtered-tokens (rf/sub [:wallet/current-viewing-account-tokens-filtered {:query search-text}])
(let [filtered-tokens (rf/sub [:wallet/current-viewing-account-tokens-filtered
{:query search-text
:chain-ids chain-ids}])
currency (rf/sub [:profile/currency])
currency-symbol (rf/sub [:profile/currency-symbol])]
[gesture/flat-list
@@ -1,6 +1,5 @@
(ns status-im.contexts.wallet.common.token-value.view
(:require [quo.core :as quo]
[status-im.common.not-implemented :as not-implemented]
[status-im.contexts.wallet.send.utils :as send-utils]
[status-im.contexts.wallet.sheets.buy-token.view :as buy-token]
[status-im.feature-flags :as ff]
@@ -18,15 +17,15 @@
:right-icon :i/external})
(defn- action-send
[send-params entry-point]
[{:keys [disabled?] :as params} entry-point]
{:icon :i/send
:accessibility-label :send
:label (i18n/label :t/send)
:disabled? (:disabled? send-params)
:disabled? disabled?
:on-press (fn []
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch [:wallet/clean-send-data])
(rf/dispatch [:wallet/set-token-to-send send-params entry-point]))})
(rf/dispatch [:wallet/set-token-to-send params entry-point]))})
(defn- action-receive
[selected-account?]
@@ -38,21 +37,27 @@
#(rf/dispatch [:open-modal :screen/share-shell {:initial-tab :wallet}]))})
(defn- action-bridge
[bridge-params]
[{:keys [bridge-disabled? testnet-mode?] :as params}]
{:icon :i/bridge
:accessibility-label :bridge
:label (i18n/label :t/bridge)
:disabled? (:bridge-disabled? bridge-params)
:disabled? (or testnet-mode? bridge-disabled?)
:on-press (fn []
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch [:wallet/bridge-select-token bridge-params]))})
(rf/dispatch [:wallet/bridge-select-token params]))})
(defn- action-swap
[]
[{:keys [token asset-to-receive token-symbol testnet-mode?]}]
{:icon :i/swap
:accessibility-label :swap
:label (i18n/label :t/swap)
:on-press #(not-implemented/alert)})
:disabled? testnet-mode?
:on-press (fn []
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch [:wallet.swap/start
{:asset-to-pay (or token {:symbol token-symbol})
:asset-to-receive asset-to-receive
:open-new-screen? true}]))})
(defn- action-manage-tokens
[watch-only?]
@@ -71,47 +76,41 @@
(defn token-value-drawer
[token watch-only? entry-point]
(let [token-symbol (:token token)
token-data (first (rf/sub [:wallet/current-viewing-account-tokens-filtered
{:query token-symbol}]))
fiat-unformatted-value (get-in token [:values :fiat-unformatted-value])
has-balance? (money/greater-than fiat-unformatted-value (money/bignumber "0"))
selected-account? (rf/sub [:wallet/current-viewing-account-address])
token-owners (rf/sub [:wallet/operable-addresses-with-token-symbol token-symbol])
send-params (if selected-account?
{:token token-data
:stack-id :screen/wallet.accounts
:disabled? (not has-balance?)
:start-flow? true
:owners token-owners}
{:token-symbol token-symbol
:stack-id :wallet-stack
:start-flow? true
:owners token-owners})
bridge-params (if selected-account?
{:token token-data
:bridge-disabled? (or (not has-balance?)
(send-utils/bridge-disabled? token-symbol))
:stack-id :screen/wallet.accounts
:start-flow? true
:owners token-owners}
{:token-symbol token-symbol
:bridge-disabled? (send-utils/bridge-disabled? token-symbol)
:stack-id :wallet-stack
:start-flow? true
:owners token-owners})]
(let [token-symbol (:token token)
token-data (first (rf/sub [:wallet/current-viewing-account-tokens-filtered
{:query token-symbol}]))
selected-account (rf/sub [:wallet/current-viewing-account-address])
token-owners (rf/sub [:wallet/operable-addresses-with-token-symbol token-symbol])
testnet-mode? (rf/sub [:profile/test-networks-enabled?])
receive-token-symbol (if (= token-symbol "SNT") "ETH" "SNT")
asset-to-receive (rf/sub [:wallet/token-by-symbol receive-token-symbol])
params (cond-> {:start-flow? true
:owners token-owners
:testnet-mode? testnet-mode?
:asset-to-receive asset-to-receive}
selected-account
(assoc :token token-data
:stack-id :screen/wallet.accounts
:has-balance? (-> (get-in token [:values :fiat-unformatted-value])
(money/greater-than (money/bignumber "0"))))
(not selected-account)
(assoc :token-symbol token-symbol
:stack-id :wallet-stack))]
[quo/action-drawer
[(cond->> [(when (ff/enabled? ::ff/wallet.assets-modal-manage-tokens)
(action-manage-tokens watch-only?))
(when (ff/enabled? ::ff/wallet.assets-modal-hide)
(action-hide))]
(not watch-only?) (concat [(action-buy)
(when (seq token-owners)
(action-send send-params entry-point))
(action-receive selected-account?)
(when (ff/enabled? ::ff/wallet.swap) (action-swap))
(when (seq (seq token-owners))
(action-bridge bridge-params))]))]]))
(not watch-only?)
(concat [(action-buy)
(when (seq token-owners)
(action-send params entry-point))
(action-receive selected-account)
(action-swap params)
(when (seq token-owners)
(action-bridge (assoc params
:bridge-disabled?
(send-utils/bridge-disabled? token-symbol))))]))]]))
(defn view
[item _ _ {:keys [watch-only? entry-point]}]
@@ -15,13 +15,17 @@
[full-name]
(first (string/split full-name #" ")))
(defn cut-fiat-balance-to-two-decimals
[balance]
(defn cut-fiat-balance
[balance decimals]
(let [valid-balance? (and balance
(or (number? balance) (.-toFixed balance)))]
(as-> balance $
(if valid-balance? $ 0)
(.toFixed $ 2))))
(.toFixed $ decimals))))
(defn cut-fiat-balance-to-two-decimals
[balance]
(cut-fiat-balance balance 2))
(defn prettify-balance
[currency-symbol balance]
@@ -151,6 +155,10 @@
[accounts address]
(some #(when (= (:address %) address) %) accounts))
(defn get-default-account
[accounts]
(some #(when (:default-account? %) %) accounts))
(defn calculate-token-fiat-value
"Returns the token fiat value for provided raw balance"
[{:keys [currency balance token]}]
@@ -493,3 +501,13 @@
:available-balance (calculate-total-token-balance token)
:total-balance (calculate-total-token-balance token chain-ids)))
tokens))
(defn estimated-time-format
"Formats the estimated time for a transaction"
[estimated-time]
(condp = estimated-time
(:unknown constants/wallet-transaction-estimation) ">5"
(:less-than-one-minute constants/wallet-transaction-estimation) "<1"
(:less-than-three-minutes constants/wallet-transaction-estimation) "1-3"
(:less-than-five-minutes constants/wallet-transaction-estimation) "3-5"
">5"))
+2 -21
View File
@@ -4,6 +4,7 @@
[native-module.core :as native-module]
[promesa.core :as promesa]
[status-im.common.json-rpc.events :as json-rpc]
[status-im.contexts.profile.recover.effects :as profile.recover.effects]
[taoensso.timbre :as log]
[utils.re-frame :as rf]
[utils.security.core :as security]
@@ -26,26 +27,6 @@
:paths paths}
on-success))))
(defn validate-mnemonic
[mnemonic]
(-> mnemonic
(security/safe-unmask-data)
(native-module/validate-mnemonic)
(promesa/then (fn [result]
(let [{:keys [keyUID]} (transforms/json->clj result)]
{:key-uid keyUID})))))
(rf/reg-fx
:multiaccount/validate-mnemonic
(fn [[mnemonic on-success on-error]]
(-> (validate-mnemonic mnemonic)
(promesa/then (fn [{:keys [key-uid]}]
(when (fn? on-success)
(on-success mnemonic key-uid))))
(promesa/catch (fn [error]
(when (and error (fn? on-error))
(on-error error)))))))
(defn create-account-from-private-key
[private-key]
(-> private-key
@@ -81,7 +62,7 @@
(defn import-missing-keypair-by-seed-phrase
[keypair-key-uid seed-phrase password]
(-> (validate-mnemonic seed-phrase)
(-> (profile.recover.effects/validate-mnemonic seed-phrase)
(promesa/then
(fn [{:keys [key-uid]}]
(if (not= keypair-key-uid key-uid)
+4 -3
View File
@@ -345,8 +345,7 @@
:Prod
data-store/rpc->network)
data)}]
{:fx [(when (ff/enabled? ::ff/wallet.swap)
[:dispatch [:wallet.tokens/get-token-list]])]
{:fx [[:dispatch [:wallet.tokens/get-token-list]]]
:db (assoc-in db [:wallet :networks] network-data)})))
(rf/reg-event-fx
@@ -478,7 +477,9 @@
[:dispatch [:wallet/get-ethereum-chains]]
[:dispatch [:wallet/get-accounts]]
[:dispatch [:wallet/get-keypairs]]
[:dispatch [:wallet/get-saved-addresses]]]}))
[:dispatch [:wallet/get-saved-addresses]]
(when (ff/enabled? ::ff/wallet.wallet-connect)
[:dispatch-later [{:ms 500 :dispatch [:wallet-connect/init]}]])]}))
(rf/reg-event-fx :wallet/share-account
(fn [_ [{:keys [content title]}]]
@@ -1,6 +1,6 @@
(ns status-im.contexts.wallet.send.events-test
(:require
[cljs.test :refer-macros [is testing run-tests]]
[cljs.test :refer-macros [is testing]]
matcher-combinators.test
[re-frame.db :as rf-db]
[status-im.contexts.wallet.common.utils.networks :as network-utils]
@@ -710,5 +710,3 @@
:stack-id stack-id
:start-flow? start-flow?
:netork-details network-details}])))))))
(run-tests)
@@ -121,7 +121,7 @@
[quo/alert-banner
{:action? true
:text (i18n/label :t/not-enough-assets-to-pay-gas-fees)
:button-text (i18n/label :t/buy-eth)
:button-text (i18n/label :t/add-eth)
:on-button-press #(rf/dispatch [:show-bottom-sheet
{:content buy-token/view}])}])
@@ -158,16 +158,14 @@
active-screen? (= view-id current-screen-id)
bottom (safe-area/get-bottom)
[crypto-currency? set-crypto-currency] (rn/use-state initial-crypto-currency?)
on-navigate-back on-navigate-back
handle-on-confirm (fn [amount]
(rf/dispatch [:wallet/set-token-amount-to-send
{:amount amount
:stack-id current-screen-id}]))
{fiat-currency :currency} (rf/sub [:profile/profile])
{token-symbol :symbol
{token-symbol :symbol
token-networks :networks
:as
token} (rf/sub [:wallet/wallet-send-token])
:as token} (rf/sub [:wallet/wallet-send-token])
send-from-locked-amounts (rf/sub [:wallet/wallet-send-from-locked-amounts])
{:keys [total-balance]
:as token-by-symbol} (rf/sub [:wallet/token-by-symbol
@@ -196,10 +194,8 @@
(utils/cut-crypto-decimals-to-fit-usd-cents
token-balance
usd-conversion-rate)
(-> (money/crypto->fiat
token-balance
conversion-rate)
utils/cut-fiat-balance-to-two-decimals))
(utils/cut-fiat-balance-to-two-decimals
(money/crypto->fiat token-balance conversion-rate)))
input-value (controlled-input/input-value input-state)
valid-input? (not (or (controlled-input/empty-value? input-state)
(controlled-input/input-error input-state)))
@@ -389,7 +385,8 @@
:button-one-label (if should-try-again?
(i18n/label :t/try-again)
button-one-label)
:button-one-props (merge (when-not should-try-again? button-one-props)
:button-one-props (merge (when-not should-try-again?
button-one-props)
{:disabled? (or loading-routes?
(and (not should-try-again?) confirm-disabled?))
:on-press (cond
@@ -10,8 +10,7 @@
[input-amount/view
{:current-screen-id :screen/wallet.send-input-amount
:button-one-label (i18n/label :t/review-send)
:enabled-from-chain-ids (rf/sub
[:wallet/wallet-send-enabled-from-chain-ids])
:enabled-from-chain-ids (rf/sub [:wallet/wallet-send-enabled-from-chain-ids])
:from-enabled-networks (rf/sub [:wallet/wallet-send-enabled-networks])
:on-navigate-back (fn []
(rf/dispatch-sync [:wallet/stop-and-clean-suggested-routes])
@@ -0,0 +1,28 @@
(ns status-im.contexts.wallet.sheets.buy-network-selection.style)
(defn network-list-container
[mainnet?]
{:margin-horizontal 8
:padding-vertical (when mainnet? 8)})
(def header-container
{:height 62
:padding-horizontal 20})
(def context-tag
{:margin-top 0})
(def divider-label
{:padding-top 0
:padding-bottom 0})
(def subheader-container
{:flex-direction :row
:align-items :center
:margin-top 4})
(def on
{:margin-right 4})
(def to
{:margin-horizontal 4})
@@ -0,0 +1,78 @@
(ns status-im.contexts.wallet.sheets.buy-network-selection.view
(:require [quo.core :as quo]
[quo.foundations.resources :as quo.resources]
[react-native.core :as rn]
[status-im.constants :as constants]
[status-im.contexts.wallet.sheets.buy-network-selection.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- network-item
[{:keys [network on-select-network]}]
(let [{:keys [network-name]} network
mainnet? (= network-name constants/mainnet-network-name)]
[quo/network-list
{:label (name network-name)
:network-image (quo.resources/get-network network-name)
:on-press #(on-select-network network)
:container-style (style/network-list-container mainnet?)}]))
(defn- subheader
[{:keys [provider account]}]
[rn/view {:style style/subheader-container}
[quo/text
{:size :paragraph-2
:weight :medium
:style style/on}
(i18n/label :t/on-uppercase)]
[quo/context-tag
{:type :network
:network-logo (:logo-url provider)
:network-name (:name provider)
:size 24
:container-style style/context-tag}]
[quo/text
{:size :paragraph-2
:weight :medium
:style style/to}
(i18n/label :t/to)]
[quo/context-tag
{:type :account
:account-name (:name account)
:emoji (:emoji account)
:size 24
:container-style style/context-tag}]])
(defn view
[]
(let [provider (rf/sub [:wallet/wallet-buy-crypto-provider])
account (rf/sub [:wallet/wallet-buy-crypto-account])
network-details (rf/sub [:wallet/network-details])
mainnet-network (first network-details)
layer-2-networks (rest network-details)
render-fn (rn/use-callback (fn [network]
[network-item
{:network network
:on-select-network
(fn [network]
(rf/dispatch [:wallet.buy-crypto/select-network
network]))}]))]
[:<>
[rn/view {:style style/header-container}
[quo/text
{:size :heading-2
:weight :semi-bold}
(i18n/label :t/select-network-for-buying)]
[subheader
{:provider provider
:account account}]]
(when mainnet-network
[network-item
{:network mainnet-network
:on-select-network #(rf/dispatch [:wallet.buy-crypto/select-network mainnet-network])}])
[quo/divider-label {:container-style style/divider-label}
(i18n/label :t/layer-2)]
[rn/flat-list
{:data (vec layer-2-networks)
:render-fn render-fn
:scroll-enabled false}]]))
@@ -7,11 +7,20 @@
[utils.re-frame :as rf]))
(defn- crypto-on-ramp-item
[{:keys [name description fees logo-url site-url recurrent-site-url]} _ _ {:keys [tab]}]
(let [open-url (rn/use-callback (fn []
(rf/dispatch [:open-url
(if (= tab :recurrent) recurrent-site-url site-url)]))
[site-url recurrent-site-url tab])]
[{:keys [name description fees logo-url site-url recurrent-site-url urls-need-parameters]
:as provider}
_
_
{:keys [account tab]}]
(let [open-url (rn/use-callback
(fn []
(if urls-need-parameters
(rf/dispatch [:wallet.buy-crypto/select-provider
{:account account
:provider provider
:recurrent? (= tab :recurrent)}])
(rn/open-url (if (= tab :recurrent) recurrent-site-url site-url))))
[site-url recurrent-site-url tab])]
[quo/settings-item
{:title name
:description :text
@@ -39,13 +48,14 @@
(rn/use-mount (fn []
(rf/dispatch [:wallet/get-crypto-on-ramps])))
(let [crypto-on-ramps (rf/sub [:wallet/crypto-on-ramps])
account (rf/sub [:wallet/current-viewing-account-or-default])
[selected-tab set-selected-tab] (rn/use-state initial-tab)
[min-height set-min-height] (rn/use-state 0)
on-layout (rn/use-callback
#(set-min-height
(oops/oget % :nativeEvent :layout :height)))]
[:<>
[quo/drawer-top {:title (or title (i18n/label :t/buy-assets))}]
[quo/drawer-top {:title (or title (i18n/label :t/ways-to-buy-assets))}]
[quo/segmented-control
{:size 32
:container-style style/tabs
@@ -58,5 +68,6 @@
(:one-time crypto-on-ramps))
:on-layout on-layout
:style (style/list-container min-height)
:render-data {:tab selected-tab}
:render-data {:tab selected-tab
:account account}
:render-fn crypto-on-ramp-item}]]))
@@ -2,30 +2,60 @@
(:require [quo.core :as quo]
quo.theme
[react-native.gesture :as gesture]
[status-im.contexts.wallet.common.utils :as wallet.utils]
[status-im.contexts.wallet.sheets.select-account.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- render-account-item
[{:keys [color address] :as account} _ _ {:keys [selected-account-address]}]
[quo/account-item
{:type :default
:account-props (assoc account :customization-color color)
:customization-color color
:state (if (= address selected-account-address) :selected :default)
:on-press (fn []
(rf/dispatch [:wallet/switch-current-viewing-account address])
(rf/dispatch [:hide-bottom-sheet]))}])
(defn list-item
[{:keys [account selected-account-address]}]
(let [{:keys [color address]} account]
[quo/account-item
{:type :default
:account-props (assoc account :customization-color color)
:customization-color color
:state (if (= address selected-account-address) :selected :default)
:on-press (fn []
(rf/dispatch [:wallet/switch-current-viewing-account address])
(rf/dispatch [:hide-bottom-sheet]))}]))
(defn list-item-with-balance
[{:keys [account selected-account-address asset-symbol network]}]
(let [{:keys [color address tokens]} account
token (->> tokens
(filter #(= (:symbol %) asset-symbol))
first)
token-balance (get-in token [:balances-per-chain (:chain-id network) :balance])]
[quo/account-item
{:type (if (= address selected-account-address) :default :tag)
:token-props {:symbol asset-symbol
:value (wallet.utils/cut-fiat-balance (or (js/parseFloat token-balance) 0)
4)}
:account-props (assoc account :customization-color color)
:customization-color color
:state (if (= address selected-account-address) :selected :default)
:on-press (fn []
(rf/dispatch [:wallet/switch-current-viewing-account address])
(rf/dispatch [:hide-bottom-sheet]))}]))
(defn view
[]
[{:keys [show-account-balances? asset-symbol network]}]
(let [selected-account-address (rf/sub [:wallet/current-viewing-account-address])
accounts (rf/sub [:wallet/operable-accounts])]
[:<>
[quo/drawer-top {:title (i18n/label :t/select-account)}]
[gesture/flat-list
{:data accounts
:render-fn render-account-item
:render-fn (fn [account _ _ {:keys [selected-account-address]}]
(if show-account-balances?
[list-item-with-balance
{:account account
:selected-account-address selected-account-address
:asset-symbol asset-symbol
:network network}]
[list-item
{:account account
:selected-account-address selected-account-address}]))
:render-data {:selected-account-address selected-account-address}
:content-container-style style/list-container
:shows-vertical-scroll-indicator false}]]))
@@ -51,14 +51,31 @@
:network-name full-name
:type :network}])
(defn- provider-view
[{:keys [name logo-url]}]
[quo/context-tag
{:size 24
:network-logo logo-url
:network-name name
:type :network}])
(defn- subheader-view
[{:keys [account network]}]
[{:keys [provider account network]}]
[rn/view {:style style/subheader-container}
[quo/text
{:size :paragraph-2
:weight :medium}
(i18n/label :t/select-asset-on)]
[account-view account]
(if provider
[provider-view provider]
[account-view account])
(when provider
[:<>
[quo/text
{:size :paragraph-2
:weight :medium}
(i18n/label :t/to)]
[account-view account]])
[quo/text
{:size :paragraph-2
:weight :medium}
@@ -66,9 +83,8 @@
[network-view network]])
(defn view
[{:keys [title on-select hide-token-fn disable-token-fn]}]
(let [account (rf/sub [:wallet/current-viewing-account])
{:keys [network]} (rf/sub [:wallet/swap])
[{:keys [title network provider on-select hide-token-fn disable-token-fn]}]
(let [account (rf/sub [:wallet/current-viewing-account-or-default])
[search-text set-search-text] (rn/use-state "")
window-height (:height (rn/get-window))]
[rn/safe-area-view {:style (style/container window-height)}
@@ -81,8 +97,9 @@
{:title title
:title-accessibility-label :title-label}]
[subheader-view
{:account account
:network network}]
{:account account
:network network
:provider provider}]
[assets-view
{:search-text search-text
:on-change-text #(set-search-text %)
+30 -23
View File
@@ -14,22 +14,30 @@
[utils.number :as number]))
(rf/reg-event-fx :wallet.swap/start
(fn [{:keys [db]} [{:keys [asset-to-pay asset-to-receive network]}]]
(let [asset-to-receive' (or asset-to-receive
(swap-utils/select-asset-to-receive
(:wallet db)
(:profile/profile db)
asset-to-pay))
network' (or network
(swap-utils/select-network asset-to-pay))]
(fn [{:keys [db]} [{:keys [network asset-to-receive open-new-screen?] :as data}]]
(let [{:keys [wallet]} db
test-networks-enabled? (get-in db [:profile/profile :test-networks-enabled?])
account (swap-utils/wallet-account wallet)
asset-to-pay (if (get-in data [:asset-to-pay :networks])
(:asset-to-pay data)
(swap-utils/select-asset-to-pay-by-symbol
{:wallet wallet
:account account
:test-networks-enabled? test-networks-enabled?
:token-symbol (get-in data [:asset-to-pay :symbol])}))
network' (or network
(swap-utils/select-network asset-to-pay))]
{:db (-> db
(assoc-in [:wallet :ui :swap :asset-to-pay] asset-to-pay)
(assoc-in [:wallet :ui :swap :asset-to-receive] asset-to-receive')
(assoc-in [:wallet :ui :swap :asset-to-receive] asset-to-receive)
(assoc-in [:wallet :ui :swap :network] network'))
:fx (if network'
[[:dispatch
[:navigate-to-within-stack
[:screen/wallet.setup-swap :screen/wallet.swap-select-asset-to-pay]]]
[[:dispatch [:wallet/switch-current-viewing-account (:address account)]]
[:dispatch
(if open-new-screen?
[:navigate-to :screen/wallet.setup-swap]
[:navigate-to-within-stack
[:screen/wallet.setup-swap :screen/wallet.swap-select-asset-to-pay]])]
[:dispatch [:wallet.swap/set-default-slippage]]]
[[:dispatch
[:show-bottom-sheet
@@ -41,8 +49,9 @@
(rf/dispatch
[:wallet.swap/start
{:asset-to-pay asset-to-pay
:asset-to-receive asset-to-receive'
:network network}]))}])}]]])})))
:asset-to-receive asset-to-receive
:network network
:open-new-screen? open-new-screen?}]))}])}]]])})))
(rf/reg-event-fx :wallet.swap/select-asset-to-pay
(fn [{:keys [db]} [{:keys [token]}]]
@@ -61,11 +70,7 @@
(rf/reg-event-fx :wallet.swap/select-asset-to-receive
(fn [{:keys [db]} [{:keys [token]}]]
{:db (update-in db
[:wallet :ui :swap]
#(-> %
(assoc :asset-to-receive token)
(assoc :loading-swap-proposal? true)))}))
{:db (assoc-in db [:wallet :ui :swap :asset-to-receive] token)}))
(rf/reg-event-fx :wallet.swap/set-default-slippage
(fn [{:keys [db]}]
@@ -126,8 +131,6 @@
:amount amount
:amount-hex amount-in-hex
:loading-swap-proposal? true)
:always
(dissoc :error-response)
clean-approval-transaction?
(dissoc :approval-transaction-id :approved-amount :swap-proposal)))
:json-rpc/call [{:method "wallet_getSuggestedRoutesAsync"
@@ -201,6 +204,12 @@
(fn [{:keys [db]}]
{:db (update-in db [:wallet :ui] dissoc :swap)}))
(rf/reg-event-fx
:wallet/on-swap-done
(fn [_]
{:fx [[:dispatch [:wallet/select-account-tab :activity]]
[:dispatch [:wallet/clean-swap]]]}))
(rf/reg-event-fx :wallet/swap-transaction
(fn [{:keys [db]} [sha3-pwd]]
(let [wallet-address (get-in db
@@ -295,8 +304,6 @@
:screen/wallet.swap-set-spending-cap
:screen/wallet.swap-confirmation)])
(when-not approval-required?
(rf/dispatch [:wallet/select-account-tab :activity])
(rf/dispatch [:wallet/clean-swap])
(debounce/debounce-and-dispatch
[:toasts/upsert
{:id :swap-transaction-pending
@@ -20,8 +20,15 @@
(defn- assets-view
[search-text on-change-text]
(let [on-token-press (fn [token]
(rf/dispatch [:wallet.swap/start {:asset-to-pay token}]))]
(let [snt-token (rf/sub [:wallet/token-by-symbol "SNT"])
eth-token (rf/sub [:wallet/token-by-symbol "ETH"])
on-token-press (fn [token]
(let [pay-token-symbol (:symbol token)
asset-to-receive (if (= pay-token-symbol "SNT") eth-token snt-token)]
(rf/dispatch [:wallet.swap/start
{:asset-to-pay token
:asset-to-receive asset-to-receive
:open-new-screen? false}])))]
[:<>
[search-input search-text on-change-text]
[asset-list/view
@@ -10,8 +10,12 @@
:height 36
:background-color :transparent})
(def floating-button-page-content-container
{:margin-bottom 168})
(def scroll-view-container
{:padding-bottom 150})
{:flex 1
:padding-bottom 20})
(def content-container
{:padding-top 12
@@ -56,5 +60,5 @@
(defn footer-container
[theme]
{:margin-bottom -10
:background-color (colors/theme-colors colors/white colors/neutral-100 theme)})
{:margin-bottom -12
:background-color (colors/theme-colors colors/white colors/neutral-95 theme)})
@@ -254,6 +254,7 @@
[rn/view {:style style/container}
[floating-button-page/view
{:footer-container-padding 0
:content-container-style style/floating-button-page-content-container
:header [quo/page-nav
{:icon-name :i/close
:on-press events-helper/navigate-back
@@ -1,27 +1,29 @@
(ns status-im.contexts.wallet.swap.setup-swap.view
(:require [clojure.string :as string]
[quo.core :as quo]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[react-native.platform :as platform]
[react-native.safe-area :as safe-area]
[status-im.common.controlled-input.utils :as controlled-input]
[status-im.common.events-helper :as events-helper]
[status-im.constants :as constants]
[status-im.contexts.wallet.common.account-switcher.view :as account-switcher]
[status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.sheets.buy-token.view :as buy-token]
[status-im.contexts.wallet.sheets.select-asset.view :as select-asset]
[status-im.contexts.wallet.sheets.slippage-settings.view :as slippage-settings]
[status-im.contexts.wallet.swap.setup-swap.style :as style]
[status-im.contexts.wallet.swap.utils :as swap-utils]
[utils.debounce :as debounce]
[utils.i18n :as i18n]
[utils.money :as money]
[utils.number :as number]
[utils.re-frame :as rf]
[utils.string :as utils.string]))
(:require
[clojure.string :as string]
[oops.core :as oops]
[quo.core :as quo]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[react-native.platform :as platform]
[react-native.safe-area :as safe-area]
[status-im.common.controlled-input.utils :as controlled-input]
[status-im.common.events-helper :as events-helper]
[status-im.constants :as constants]
[status-im.contexts.wallet.common.account-switcher.view :as account-switcher]
[status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.sheets.buy-token.view :as buy-token]
[status-im.contexts.wallet.sheets.select-asset.view :as select-asset]
[status-im.contexts.wallet.sheets.slippage-settings.view :as slippage-settings]
[status-im.contexts.wallet.swap.setup-swap.style :as style]
[status-im.contexts.wallet.swap.utils :as swap-utils]
[utils.debounce :as debounce]
[utils.i18n :as i18n]
[utils.money :as money]
[utils.number :as number]
[utils.re-frame :as rf]
[utils.string :as utils.string]))
(def ^:private default-text-for-unfocused-input "0.00")
@@ -85,7 +87,7 @@
[{:keys [input-state on-max-press on-input-focus on-token-press on-approve-press input-focused?]}]
(let [account-color (rf/sub [:wallet/current-viewing-account-color])
network (rf/sub [:wallet/swap-network])
asset-to-pay (rf/sub [:wallet/swap-asset-to-pay])
pay-token-symbol (:symbol (rf/sub [:wallet/swap-asset-to-pay]))
currency (rf/sub [:profile/currency])
loading-swap-proposal? (rf/sub [:wallet/swap-loading-swap-proposal?])
swap-proposal (rf/sub [:wallet/swap-proposal-without-fees])
@@ -96,14 +98,19 @@
approval-transaction-id (rf/sub [:wallet/swap-approval-transaction-id])
approved-amount (rf/sub [:wallet/swap-approved-amount])
error-response (rf/sub [:wallet/swap-error-response])
asset-to-pay (rf/sub [:wallet/token-by-symbol pay-token-symbol])
overlay-shown? (boolean (:sheets (rf/sub [:bottom-sheet])))
input-ref (rn/use-ref-atom nil)
set-input-ref (rn/use-callback (fn [ref] (reset! input-ref ref)))
pay-input-num-value (controlled-input/value-numeric input-state)
pay-input-amount (controlled-input/input-value input-state)
pay-token-symbol (:symbol asset-to-pay)
pay-token-decimals (:decimals asset-to-pay)
pay-token-balance-selected-chain (get-in asset-to-pay
[:balances-per-chain
(:chain-id network) :balance]
0)
pay-token-balance-selected-chain (number/convert-to-whole-number
(get-in asset-to-pay
[:balances-per-chain
(:chain-id network) :raw-balance]
0)
pay-token-decimals)
pay-token-fiat-value (str
(utils/calculate-token-fiat-value
{:currency currency
@@ -111,10 +118,15 @@
:token asset-to-pay}))
available-crypto-limit (money/bignumber
pay-token-balance-selected-chain)
display-decimals (min pay-token-decimals
constants/min-token-decimals-to-display)
available-crypto-limit-display (number/remove-trailing-zeroes
(.toFixed available-crypto-limit
(min pay-token-decimals
constants/min-token-decimals-to-display)))
(.toFixed available-crypto-limit display-decimals))
available-crypto-limit-display (if (and (= available-crypto-limit-display "0")
(money/greater-than available-crypto-limit
(money/bignumber 0)))
(number/small-number-threshold display-decimals)
available-crypto-limit-display)
approval-amount-required-num (when approval-amount-required
(str (number/hex->whole approval-amount-required
pay-token-decimals)))
@@ -122,8 +134,7 @@
(money/greater-than
(money/bignumber pay-input-amount)
available-crypto-limit))
(money/equal-to (money/bignumber
available-crypto-limit-display)
(money/equal-to available-crypto-limit
(money/bignumber 0)))
valid-pay-input? (and
(not (string/blank?
@@ -137,12 +148,20 @@
:valid-input? valid-pay-input?
:clean-approval-transaction? true}))
[pay-input-amount])]
(rn/use-unmount #(rf/dispatch [:wallet/on-swap-done]))
(rn/use-effect
(fn []
(request-fetch-swap-proposal))
[pay-input-amount])
(rn/use-effect
(fn []
(when-not overlay-shown?
(some-> @input-ref
(oops/ocall "focus"))))
[overlay-shown?])
[quo/swap-input
{:type :pay
{:get-ref set-input-ref
:type :pay
:error? pay-input-error?
:token pay-token-symbol
:customization-color :blue
@@ -156,7 +175,7 @@
:else :disabled)
:currency-symbol currency-symbol
:on-token-press on-token-press
:on-max-press #(on-max-press (str available-crypto-limit))
:on-max-press #(on-max-press (str pay-token-balance-selected-chain))
:on-input-focus on-input-focus
:value pay-input-amount
:fiat-value pay-token-fiat-value
@@ -239,17 +258,15 @@
(merge {:action? true
:on-button-press #(rf/dispatch [:show-bottom-sheet
{:content buy-token/view}])
:button-text (i18n/label :t/buy-assets)})
:button-text (i18n/label :t/add-assets)})
(= error-response-code
constants/router-error-code-not-enough-native-balance)
(merge {:action? true
:on-button-press #(rf/dispatch
[:show-bottom-sheet
{:content (fn []
[buy-token/view
{:title (i18n/label
:t/buy-ethereum)}])}])
:button-text (i18n/label :t/buy-eth)}))]
[buy-token/view])}])
:button-text (i18n/label :t/add-eth)}))]
(when (or pay-input-error? error-response)
[quo/alert-banner props])))
@@ -274,23 +291,28 @@
(defn- pay-token-bottom-sheet
[]
(let [asset-to-receive (rf/sub [:wallet/swap-asset-to-receive])]
(let [asset-to-receive (rf/sub [:wallet/swap-asset-to-receive])
network (rf/sub [:wallet/swap-network])]
[select-asset/view
{:title (i18n/label :t/select-asset-to-pay)
:network network
:on-select (fn [token]
(rf/dispatch [:wallet.swap/select-asset-to-pay {:token token}]))
:hide-token-fn (fn [type {:keys [available-balance]}]
(and (= type constants/swap-tokens-my)
(= available-balance 0)))
:hide-token-fn (fn [type {:keys [balances-per-chain]}]
(let [balance (get-in balances-per-chain [(:chain-id network) :balance] "0")]
(and (= type constants/swap-tokens-my)
(= balance "0"))))
:disable-token-fn (fn [_ token]
(= (:symbol token)
(:symbol asset-to-receive)))}]))
(defn- receive-token-bottom-sheet
[]
(let [asset-to-pay (rf/sub [:wallet/swap-asset-to-pay])]
(let [asset-to-pay (rf/sub [:wallet/swap-asset-to-pay])
network (rf/sub [:wallet/swap-network])]
[select-asset/view
{:title (i18n/label :t/select-asset-to-receive)
:network network
:on-select (fn [token]
(rf/dispatch [:wallet.swap/select-asset-to-receive {:token token}]))
:disable-token-fn (fn [_ token]
@@ -310,10 +332,12 @@
network (rf/sub [:wallet/swap-network])
pay-input-amount (controlled-input/input-value pay-input-state)
pay-token-decimals (:decimals asset-to-pay)
pay-token-balance-selected-chain (get-in asset-to-pay
[:balances-per-chain
(:chain-id network) :balance]
0)
pay-token-balance-selected-chain (number/convert-to-whole-number
(get-in asset-to-pay
[:balances-per-chain
(:chain-id network) :raw-balance]
0)
pay-token-decimals)
pay-input-error? (and (not (string/blank? pay-input-amount))
(money/greater-than
(money/bignumber pay-input-amount)
@@ -415,13 +439,17 @@
(when (and swap-amount refetch-interval)
(js/clearTimeout @refetch-interval)
(reset! refetch-interval nil))
(if (and swap-amount (not= swap-amount pay-input-amount))
(set-pay-input-state
(fn [input-state]
(controlled-input/set-input-value
input-state
swap-amount)))
(refetch-swap-proposal)))))
(cond (and swap-amount (not= swap-amount pay-input-amount))
(set-pay-input-state
(fn [input-state]
(controlled-input/set-input-value
input-state
swap-amount)))
(and pay-input-amount
(not (number/valid-decimal-count? pay-input-amount (:decimals asset-to-pay))))
(set-pay-input-state controlled-input/delete-all)
:else
(refetch-swap-proposal)))))
[asset-to-pay])
(rn/use-effect
refetch-swap-proposal
@@ -431,7 +459,10 @@
{:on-press on-close
:icon-name :i/arrow-left
:margin-top (safe-area/get-top)
:switcher-type :select-account}]
:switcher-type :select-account
:params {:show-account-balances? true
:asset-symbol (:symbol asset-to-pay)
:network network}}]
[rn/view {:style style/inputs-container}
[pay-token-input
{:input-state pay-input-state
@@ -453,7 +484,7 @@
:on-token-press #(rf/dispatch [:show-bottom-sheet {:content receive-token-bottom-sheet}])
:on-input-focus #(set-pay-input-focused? false)}]]
[rn/view {:style style/footer-container}
[alert-banner {:pay-input-error? pay-input-error?}]
(when-not loading-swap-proposal? [alert-banner {:pay-input-error? pay-input-error?}])
(when (or loading-swap-proposal? swap-proposal)
[transaction-details])
[action-button {:on-press on-review-swap-press}]]
+50 -11
View File
@@ -30,22 +30,61 @@
:else
(i18n/label :t/something-went-wrong-please-try-again-later)))
(defn select-asset-to-receive
[wallet profile asset-to-pay]
(let [wallet-address (get-in wallet [:current-viewing-account-address])
account (-> wallet
:accounts
vals
(utils/get-account-by-address wallet-address))
test-networks-enabled? (get profile :test-networks-enabled?)
networks (-> (get-in wallet
[:wallet :networks (if test-networks-enabled? :test :prod)])
(network-utils/sorted-networks-with-details))]
(defn- first-operable-account
[accounts]
(->> accounts
vals
(remove :watch-only?)
(filter :operable?)
(sort-by :position)
first))
(defn wallet-account
"Picks the account that's gonna be used for the swap operation.
It's gonna be either the preselected account defined by
`[:wallet :current-viewing-account-address]` in `db`
or the first operable account."
[wallet]
(if-let [wallet-address (get wallet :current-viewing-account-address)]
(-> wallet
:accounts
vals
(utils/get-account-by-address wallet-address))
(-> wallet :accounts first-operable-account)))
(defn select-asset-to-pay-by-symbol
"Selects an asset to pay by token symbol.
It's used for cases when only token symbol is available and the information
about token needs to be extracted from the database.
That happens when token is being selected on the home screen and
it basically indicates that no account pre-selection was made."
[{:keys [wallet account test-networks-enabled? token-symbol]}]
(let [networks (-> (get-in wallet [:networks (if test-networks-enabled? :test :prod)])
(network-utils/sorted-networks-with-details))
token (->> account
:tokens
(filter #(= token-symbol (:symbol %)))
first)]
(assoc token :networks (network-utils/network-list token networks))))
(defn select-default-asset-to-receive
"Selects an asset to receive if it was not provided explicitly.
The principle of how the asset is being selected is simple: we get the
whole list, remove the currently selected `asset-to-pay` from it, and choose
the first one of what's left."
[{:keys [wallet account test-networks-enabled? asset-to-pay]}]
(let [networks (-> (get-in wallet [:networks (if test-networks-enabled? :test :prod)])
(network-utils/sorted-networks-with-details))]
(->> (utils/tokens-with-balance (:tokens account) networks nil)
(remove #(= (:symbol %) (:symbol asset-to-pay)))
first)))
(defn select-network
"Chooses the network.
Usually user needs to do the selection first and if the selection was done
then the list of networks for the defined token will always contain
one entry. Otherwise `nil` will be returned from here which will serve
as an indicator that the network selector needs to be displayed."
[{:keys [networks]}]
(when (= (count networks) 1)
(first networks)))
@@ -34,9 +34,8 @@
(defn tokens-by-symbol
[props]
(tokens-by-key (assoc props
:key-fn
(fn [{:keys [address] :as token}]
(if (get-in token [:communityData :id])
address
(:symbol token))))))
(tokens-by-key
(assoc props
:key-fn
(fn [{:keys [chainId address] :as token}]
(str chainId "-" (if (get-in token [:communityData :id]) address (:symbol token)))))))
@@ -75,7 +75,7 @@
:image nil})
:by-symbol '({:address "0x0000000000000000000000000000000000000000"
:decimals 18
:key "ETH"
:key "1-ETH"
:community-id nil
:symbol "ETH"
:sources ["native"]
@@ -86,7 +86,7 @@
:image nil}
{:address "0x0000000000000000000000000000000000000001"
:decimals 18
:key "SNT"
:key "1-SNT"
:community-id nil
:symbol "SNT"
:sources ["native"]
@@ -174,7 +174,7 @@
:image nil})
:by-symbol '({:address "0x0000000000000000000000000000000000000000"
:decimals 18
:key "ETH"
:key "1-ETH"
:community-id nil
:symbol "ETH"
:sources ["native"]
@@ -185,7 +185,7 @@
:image nil}
{:address "0x0000000000000000000000000000000000000001"
:decimals 18
:key "SNT"
:key "1-SNT"
:community-id nil
:symbol "SNT"
:sources ["native" "second"]
@@ -196,7 +196,7 @@
:image nil}
{:address "0x0000000000000000000000000000000000000002"
:decimals 18
:key "ANOTHER1"
:key "1-ANOTHER1"
:community-id nil
:symbol "ANOTHER1"
:sources ["second"]
@@ -253,7 +253,7 @@
:image nil})
:by-symbol '({:address "0x0000000000000000000000000000000000000000"
:decimals 18
:key "ETH"
:key "1-ETH"
:community-id nil
:symbol "ETH"
:sources ["native"]
@@ -264,7 +264,7 @@
:image nil}
{:address "0x0000000000000000000000000000000000000000"
:decimals 18
:key "ANOTHER1"
:key "1-ANOTHER1"
:community-id nil
:symbol "ANOTHER1"
:sources ["native" "second"]
@@ -21,8 +21,8 @@
{:fx [[:effects.wallet-connect/init
{:on-success #(rf/dispatch [:wallet-connect/on-init-success %])
:on-fail #(rf/dispatch [:wallet-connect/on-init-fail %])}]]})
;; NOTE: when offline, fetching persistent sessions only
{:fx [[:dispatch [:wallet-connect/fetch-persisted-sessions]]]}))))
;; NOTE: when offline, fetching persistent sessions without initializing WC
{:fx [[:dispatch [:wallet-connect/get-sessions]]]}))))
(rf/reg-event-fx
:wallet-connect/on-init-success
@@ -30,7 +30,7 @@
(log/info "WalletConnect SDK initialisation successful")
{:db (assoc db :wallet-connect/web3-wallet web3-wallet)
:fx [[:dispatch [:wallet-connect/register-event-listeners]]
[:dispatch [:wallet-connect/fetch-persisted-sessions]]]}))
[:dispatch [:wallet-connect/get-sessions]]]}))
(rf/reg-event-fx
:wallet-connect/reload-on-network-change
@@ -1,13 +1,15 @@
(ns status-im.contexts.wallet.wallet-connect.events.effects
(:require
[promesa.core :as promesa]
[re-frame.core :as rf]
[react-native.wallet-connect :as wallet-connect]
[status-im.config :as config]
[status-im.constants :as constants]
[status-im.contexts.wallet.wallet-connect.utils.sessions :as sessions]
[status-im.contexts.wallet.wallet-connect.utils.signing :as signing]
[status-im.contexts.wallet.wallet-connect.utils.transactions :as transactions]
[status-im.contexts.wallet.wallet-connect.utils.typed-data :as typed-data]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.security.core :as security]))
(rf/reg-fx
@@ -41,37 +43,21 @@
(rf/reg-fx
:effects.wallet-connect/disconnect
(fn [{:keys [web3-wallet topic reason on-success on-fail]}]
(-> (wallet-connect/disconnect-session {:web3-wallet web3-wallet
:topic topic
:reason reason})
(fn [{:keys [web3-wallet topic on-success on-fail]}]
(-> (sessions/disconnect web3-wallet topic)
(promesa/then on-success)
(promesa/catch on-fail))))
(rf/reg-fx
:effects.wallet-connect/approve-session
(fn [{:keys [web3-wallet proposal networks accounts on-success on-fail]}]
(let [{:keys [params id]} proposal
approved-namespaces (->> {:eip155
{:chains networks
:accounts accounts
:methods constants/wallet-connect-supported-methods
:events constants/wallet-connect-supported-events}}
(wallet-connect/build-approved-namespaces
params))]
(-> (wallet-connect/approve-session
{:web3-wallet web3-wallet
:id id
:approved-namespaces approved-namespaces})
(promesa/then on-success)
(promesa/catch on-fail)))))
(rf/reg-fx
:effects.wallet-connect/fetch-active-sessions
(fn [{:keys [web3-wallet on-success on-fail]}]
(-> (wallet-connect/get-active-sessions web3-wallet)
(promesa/then on-success)
(promesa/catch on-fail))))
(fn [{:keys [web3-wallet proposal-request session-networks address on-success on-fail]}]
(-> (sessions/approve
{:web3-wallet web3-wallet
:proposal-request proposal-request
:address address
:session-networks session-networks})
(promesa/then (partial rf/call-continuation on-success))
(promesa/catch (partial rf/call-continuation on-fail)))))
(rf/reg-fx
:effects.wallet-connect/sign-message
@@ -86,8 +72,8 @@
(signing/eth-sign password address data)
(signing/personal-sign password address data))
(promesa/then on-success)
(promesa/catch on-error)))))
(promesa/then (partial rf/call-continuation on-success))
(promesa/catch (partial rf/call-continuation on-error))))))
(rf/reg-fx
:effects.wallet-connect/prepare-transaction
@@ -106,8 +92,8 @@
tx-hash
tx-args
chain-id)
(promesa/then on-success)
(promesa/catch on-error))))
(promesa/then (partial rf/call-continuation on-success))
(promesa/catch (partial rf/call-continuation on-error)))))
(rf/reg-fx
:effects.wallet-connect/send-transaction
@@ -117,19 +103,19 @@
tx-hash
tx-args
chain-id)
(promesa/then on-success)
(promesa/catch on-error))))
(promesa/then (partial rf/call-continuation on-success))
(promesa/catch (partial rf/call-continuation on-error)))))
(rf/reg-fx
:effects.wallet-connect/sign-typed-data
(fn [{:keys [password address data version chain-id on-success on-error]}]
(-> (signing/eth-sign-typed-data (security/safe-unmask-data password)
address
data
chain-id
version)
(promesa/then on-success)
(promesa/catch on-error))))
(-> (typed-data/sign (security/safe-unmask-data password)
address
data
chain-id
version)
(promesa/then (partial rf/call-continuation on-success))
(promesa/catch (partial rf/call-continuation on-error)))))
(rf/reg-fx
:effects.wallet-connect/respond-session-request
@@ -155,3 +141,10 @@
:reason reason})
(promesa/then on-success)
(promesa/catch on-error)))))
(rf/reg-fx
:effects.wallet-connect/get-sessions
(fn [{:keys [web3-wallet addresses online? on-success on-error]}]
(-> (sessions/get-sessions web3-wallet addresses online?)
(promesa/then on-success)
(promesa/catch on-error))))
@@ -109,7 +109,6 @@
(fn [{:keys [db]} [address]]
{:db (assoc-in db [:wallet-connect/current-proposal :address] address)}))
(rf/reg-event-fx
:wallet-connect/approve-session
(fn [{:keys [db]}]
@@ -119,33 +118,62 @@
(map networks/chain-id->eip155)
vec)
current-address (get-in db [:wallet-connect/current-proposal :address])
accounts (-> (partial networks/format-eip155-address current-address)
(map session-networks))
network-status (:network/status db)
expiry (get-in current-proposal [:params :expiryTimestamp])]
expired? (-> current-proposal
(get-in [:params :expiryTimestamp])
uri/timestamp-expired?)]
(if (= network-status :online)
{:db (assoc-in db [:wallet-connect/current-proposal :response-sent?] true)
:fx [(if (uri/timestamp-expired? expiry)
:fx [(if expired?
[:dispatch
[:toasts/upsert
{:id :wallet-connect-proposal-expired
:type :negative
:text (i18n/label :t/wallet-connect-proposal-expired)}]]
[:effects.wallet-connect/approve-session
{:web3-wallet web3-wallet
:proposal current-proposal
:networks session-networks
:accounts accounts
:on-success (fn [approved-session]
(log/info "Wallet Connect session approved")
(rf/dispatch [:wallet-connect/reset-current-session-proposal])
(rf/dispatch [:wallet-connect/persist-session
approved-session]))
:on-fail (fn [error]
(log/error "Wallet Connect session approval failed"
{:error error
:event :wallet-connect/approve-session})
(rf/dispatch
[:wallet-connect/reset-current-session-proposal]))}])
{:web3-wallet web3-wallet
:proposal-request current-proposal
:session-networks session-networks
:address current-address
:on-success [:wallet-connect/approve-session-success]
:on-fail [:wallet-connect/approve-session-error]}])
[:dispatch [:dismiss-modal :screen/wallet.wallet-connect-session-proposal]]]}
{:fx [[:dispatch [:wallet-connect/no-internet-toast]]]}))))
(rf/reg-event-fx :wallet-connect/approve-session-success
(fn [{:keys [db]} [session]]
(let [total-connected-dapps (data-store/get-total-connected-dapps db)]
{:fx [[:dispatch [:wallet-connect/on-new-session session]]
[:dispatch [:wallet-connect/reset-current-session-proposal]]
[:dispatch [:wallet-connect/redirect-to-dapp (data-store/get-dapp-redirect-url session)]]
[:dispatch
[:centralized-metrics/track :metric/dapp-session-proposal
{:action :approved
:total_connected_dapps total-connected-dapps}]]]})))
(rf/reg-event-fx :wallet-connect/approve-session-error
(fn [_ [error]]
(log/error "Wallet Connect session approval failed"
{:error error
:event :wallet-connect/approve-session})
{:fx [[:dispatch [:wallet-connect/reset-current-session-proposal]]]}))
(rf/reg-event-fx
:wallet-connect/reject-session-proposal
(fn [{:keys [db]} [proposal]]
(let [web3-wallet (get db :wallet-connect/web3-wallet)
{:keys [request response-sent?]} (:wallet-connect/current-proposal db)
networks (networks/get-proposal-networks (or proposal request))
rejected? (nil? proposal)]
{:fx (concat
(when-not response-sent?
[[:effects.wallet-connect/reject-session-proposal
{:web3-wallet web3-wallet
:proposal (or proposal request)
:on-success #(log/info "Wallet Connect session proposal rejected")
:on-error #(log/error "Wallet Connect unable to reject session proposal")}]
[:dispatch
[:centralized-metrics/track :metric/dapp-session-proposal
{:action (if rejected? :rejected :not_supported)
:networks networks}]]])
[[:dispatch [:wallet-connect/reset-current-session-proposal]]])})))
@@ -2,15 +2,15 @@
(:require [cljs-bean.core :as bean]
[clojure.string :as string]
[native-module.core :as native-module]
[re-frame.core :as rf]
[status-im.constants :as constants]
[status-im.contexts.wallet.wallet-connect.utils.data-store :as
data-store]
[status-im.contexts.wallet.wallet-connect.utils.networks :as networks]
[status-im.contexts.wallet.wallet-connect.utils.signing :as signing]
[status-im.contexts.wallet.wallet-connect.utils.transactions :as transactions]
[status-im.contexts.wallet.wallet-connect.utils.typed-data :as typed-data]
[taoensso.timbre :as log]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.transforms :as transforms]))
(rf/reg-event-fx
@@ -37,7 +37,9 @@
(assoc-in [:wallet-connect/current-request :response-sent?] false))
:fx [(condp = method
constants/wallet-connect-eth-send-transaction-method
[:dispatch [:wallet-connect/process-eth-send-transaction]]
[:dispatch
[:wallet-connect/process-eth-send-transaction
{:on-success (fn [] (rf/dispatch [:wallet-connect/show-request-modal]))}]]
constants/wallet-connect-eth-sign-method
[:dispatch [:wallet-connect/process-eth-sign]]
@@ -94,22 +96,25 @@
:raw-data prepared-tx
:transaction tx
:chain-id chain-id
:display-data display-data)
:fx [[:dispatch [:wallet-connect/show-request-modal]]]})))
:display-data display-data)})))
(rf/reg-event-fx
:wallet-connect/process-eth-send-transaction
(fn [{:keys [db]}]
(fn [{:keys [db]} [{:keys [on-success]}]]
(let [event (data-store/get-db-current-request-event db)
tx (-> event data-store/get-request-params first)
chain-id (-> event
(get-in [:params :chainId])
networks/eip155->chain-id)]
{:fx [[:effects.wallet-connect/prepare-transaction
{:tx tx
:chain-id chain-id
:on-success #(rf/dispatch [:wallet-connect/prepare-transaction-success % chain-id])
:on-error #(rf/dispatch [:wallet-connect/on-processing-error %])}]]})))
(when tx
{:fx [[:effects.wallet-connect/prepare-transaction
{:tx tx
:chain-id chain-id
:on-success (fn [data]
(rf/dispatch [:wallet-connect/prepare-transaction-success data chain-id])
(when on-success
(rf/call-continuation on-success)))
:on-error #(rf/dispatch [:wallet-connect/on-processing-error %])}]]}))))
(rf/reg-event-fx
:wallet-connect/process-eth-sign-transaction
@@ -130,16 +135,13 @@
(fn [{:keys [db]}]
(try
(let [[address raw-data] (data-store/get-db-current-request-params db)
parsed-raw-data (transforms/js-parse raw-data)
session-chain-id (-> (data-store/get-db-current-request-event db)
(get-in [:params :chainId])
networks/eip155->chain-id)
data-chain-id (-> parsed-raw-data
transforms/js->clj
signing/typed-data-chain-id)
parsed-data (-> parsed-raw-data
(transforms/js-dissoc :types :primaryType)
(transforms/js-stringify 2))]
typed-data (-> raw-data
transforms/js-parse
transforms/js->clj)
data-chain-id (typed-data/get-chain-id typed-data)]
(if (and data-chain-id
(not= session-chain-id data-chain-id))
{:fx [[:dispatch
@@ -150,7 +152,7 @@
[:wallet-connect/current-request]
assoc
:address (string/lower-case address)
:display-data (or parsed-data raw-data)
:display-data (typed-data/flatten-typed-data typed-data)
:raw-data raw-data)
:fx [[:dispatch [:wallet-connect/show-request-modal]]]}))
(catch js/Error err
@@ -51,8 +51,8 @@
:address address
:data raw-data
:rpc-method rpc-method
:on-error #(rf/dispatch [:wallet-connect/on-sign-error %])
:on-success #(rf/dispatch [:wallet-connect/finish-session-request %])}]]})))
:on-error [:wallet-connect/on-sign-error]
:on-success [:wallet-connect/finish-session-request]}]]})))
(rf/reg-event-fx
:wallet-connect/respond-sign-typed-data
@@ -65,8 +65,8 @@
:data raw-data
:chain-id chain-id
:version typed-data-version
:on-error #(rf/dispatch [:wallet-connect/on-sign-error %])
:on-success #(rf/dispatch [:wallet-connect/finish-session-request %])}]]})))
:on-error [:wallet-connect/on-sign-error]
:on-success [:wallet-connect/finish-session-request]}]]})))
(rf/reg-event-fx
:wallet-connect/respond-send-transaction-data
@@ -79,8 +79,8 @@
:chain-id chain-id
:tx-hash tx-hash
:tx-args tx-args
:on-error #(rf/dispatch [:wallet-connect/on-sign-error %])
:on-success #(rf/dispatch [:wallet-connect/finish-session-request %])}]]})))
:on-error [:wallet-connect/on-sign-error]
:on-success [:wallet-connect/finish-session-request]}]]})))
(rf/reg-event-fx
:wallet-connect/respond-sign-transaction-data
@@ -93,8 +93,8 @@
:chain-id chain-id
:tx-hash tx-hash
:tx-params tx-args
:on-error #(rf/dispatch [:wallet-connect/on-sign-error %])
:on-success #(rf/dispatch [:wallet-connect/finish-session-request %])}]]})))
:on-error [:wallet-connect/on-sign-error]
:on-success [:wallet-connect/finish-session-request]}]]})))
(rf/reg-event-fx
:wallet-connect/on-sign-error
@@ -161,22 +161,15 @@
(rf/reg-event-fx
:wallet-connect/finish-session-request
(fn [_ [result]]
{:fx [[:dispatch [:wallet-connect/send-response {:result result}]]
[:dispatch [:wallet-connect/dismiss-request-modal]]]}))
(rf/reg-event-fx
:wallet-connect/reject-session-proposal
(fn [{:keys [db]} [proposal]]
(let [web3-wallet (get db :wallet-connect/web3-wallet)
{:keys [request response-sent?]} (:wallet-connect/current-proposal db)]
{:fx [(when-not response-sent?
[:effects.wallet-connect/reject-session-proposal
{:web3-wallet web3-wallet
:proposal (or proposal request)
:on-success #(log/info "Wallet Connect session proposal rejected")
:on-error #(log/error "Wallet Connect unable to reject session proposal")}])
[:dispatch [:wallet-connect/reset-current-session-proposal]]]})))
(fn [{:keys [db]} [result]]
(let [event (get-in db [:wallet-connect/current-request :event])
method (data-store/get-request-method event)]
{:fx [[:dispatch
[:centralized-metrics/track :metric/dapp-session-response
{:method method
:approved true}]]
[:dispatch [:wallet-connect/send-response {:result result}]]
[:dispatch [:wallet-connect/dismiss-request-modal]]]})))
;; NOTE: Currently we only reject a session if the user dismissed a modal
;; without accepting the session first.
@@ -189,9 +182,17 @@
(rf/reg-event-fx
:wallet-connect/on-request-modal-dismissed
(fn [{:keys [db]}]
{:fx [(when-not (get-in db [:wallet-connect/current-request :response-sent?])
[:dispatch
[:wallet-connect/send-response
{:error (wallet-connect/get-sdk-error
constants/wallet-connect-user-rejected-error-key)}]])
[:dispatch [:wallet-connect/reset-current-request]]]}))
(let [{:keys [response-sent? event]} (get db :wallet-connect/current-request)
method (data-store/get-request-method event)]
{:fx (concat
(when-not response-sent?
[[:dispatch
[:centralized-metrics/track :metric/dapp-session-response
{:method method
:approved false}]]
[:dispatch
[:wallet-connect/send-response
{:request event
:error (wallet-connect/get-sdk-error
constants/wallet-connect-user-rejected-error-key)}]]])
[[:dispatch [:wallet-connect/reset-current-request]]])})))

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