Compare commits

..
Author SHA1 Message Date
Yevheniia Berdnyk f8772b4fa1 e2e: added gathering Status.log for failed tests 2024-09-28 16:09:06 +03:00
144 changed files with 1625 additions and 2984 deletions
+1 -1
View File
@@ -19,5 +19,5 @@
Make sure to compress images before using into project.
```
make shell
./scripts/compress_image.sh image_path
./scrips/compress_image.sh image_path
```
+1
View File
@@ -51,6 +51,7 @@ 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; };
+2 -18
View File
@@ -6,16 +6,8 @@ let
# For testing local version of nixpkgs
#nixpkgsSrc = (import <nixpkgs> { }).lib.cleanSource "/home/jakubgs/work/nixpkgs";
# 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 {
nixpkgsSrc = builtins.fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/48d567fc7b299de90f70a48e4263e31f690ba03e.tar.gz";
sha256 = "sha256:0zynbk52khdfhg4qfv26h3r5156xff5p0cga2cin7b07i7lqminh";
};
@@ -29,14 +21,6 @@ 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;
@@ -51,5 +35,5 @@ in
(import nixpkgsSrc) {
config = defaultConfig // config;
system = systemOverride;
overlays = [ pkgsOverlay gradleOverlay ];
overlays = [ pkgsOverlay ];
}
+1 -7
View File
@@ -1,5 +1,4 @@
{ lib, stdenv, meta, source, buildGoPackage,
go-bindata, mockgen, protoc-gen-go, protobuf3_20 }:
{ lib, stdenv, meta, source, buildGoPackage }:
buildGoPackage {
pname = source.repo;
@@ -8,10 +7,6 @@ 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
@@ -27,7 +22,6 @@ 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
'';
+3 -12
View File
@@ -1,6 +1,5 @@
{ callPackage, lib, buildGoPackage
, androidPkgs, openjdk, gomobile, xcodeWrapper, removeReferencesTo
, go-bindata, mockgen, protobuf3_20, protoc-gen-go
, meta
, source
, platform ? "android"
@@ -16,7 +15,7 @@ let
isAndroid = platform == "android";
enforceXCodeAvailable = callPackage ./enforceXCodeAvailable.nix { };
in buildGoPackage rec {
in buildGoPackage {
pname = source.repo;
version = "${source.cleanVersion}-${source.shortRev}-${platform}";
@@ -28,9 +27,8 @@ in buildGoPackage rec {
__noChroot = isIOS;
extraSrcPaths = [ gomobile ];
nativeBuildInputs = [
gomobile removeReferencesTo go-bindata mockgen protoc-gen-go protobuf3_20
] ++ optional isAndroid openjdk
nativeBuildInputs = [ gomobile removeReferencesTo ]
++ optional isAndroid openjdk
++ optional isIOS xcodeWrapper;
ldflags = goBuildLdFlags;
@@ -46,13 +44,6 @@ in buildGoPackage rec {
# 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/16Uiu2HAmQE7FXQc6iZHdBzYfw3qCSDa9dLc1wsBJKoP4aZvztq2d",
"boot-01.do-ams3.shards.staging": "/dns4/boot-01.do-ams3.shards.staging.status.im/tcp/30303/p2p/16Uiu2HAmEqqio4UR1SWqAc7KY19t6qyDvtmyjreZpzUBJvb4u65R",
"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:-QEQuEBuiQgFlJNcv255042zwyl4pOBOivakX8N30Dr9vaaEU2q8-7N4GVY4Hk87iEKELjlIXTpE9Wj6EQq1lrBuc7ayAYJpZIJ2NIJpcISPxvrpim11bHRpYWRkcnO4YAAtNihib290LTAxLmRvLWFtczMuc3RhdHVzLnN0YWdpbmcuc3RhdHVzLmltBnZfAC82KGJvb3QtMDEuZG8tYW1zMy5zdGF0dXMuc3RhZ2luZy5zdGF0dXMuaW0GAbveA4Jyc40AEAUAAQAgAEAAgAEAiXNlY3AyNTZrMaEDq-yGgpuoUG6NKkbIDRmrMiT-bEVzFlpWLEK_rF3yKUaDdGNwgnZfg3VkcIIjKIV3YWt1Mg0",
"boot-01.do-ams3.shards.staging": "enr:-QEQuEDsh6FgAb_36cReaX7W4gWx_7_GNpsUki7bXMoMrrrWij5pDEyV3guR-urDW_6GJTAzpQiJV61F-CfNn_NxPbY-AYJpZIJ2NIJpcISPxvrpim11bHRpYWRkcnO4YAAtNihib290LTAxLmRvLWFtczMuc2hhcmRzLnN0YWdpbmcuc3RhdHVzLmltBnZfAC82KGJvb3QtMDEuZG8tYW1zMy5zaGFyZHMuc3RhZ2luZy5zdGF0dXMuaW0GAbveA4Jyc40AEAUAAQAgAEAAgAEAiXNlY3AyNTZrMaEDIH8BcuEzgnmwPQTu7BPYyg4u4om7K9qekKA2gT_H2wSDdGNwgnZfg3VkcIIjKIV3YWt1Mg0",
"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/16Uiu2HAmQE7FXQc6iZHdBzYfw3qCSDa9dLc1wsBJKoP4aZvztq2d",
"boot-01.do-ams3.shards.staging": "/dns4/boot-01.do-ams3.shards.staging.status.im/tcp/443/wss/p2p/16Uiu2HAmEqqio4UR1SWqAc7KY19t6qyDvtmyjreZpzUBJvb4u65R",
"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.

Before

Width:  |  Height:  |  Size: 31 KiB

@@ -0,0 +1,47 @@
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;
}
});
}
@@ -0,0 +1,15 @@
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;
});
}
+3 -4
View File
@@ -256,10 +256,9 @@
{:events [:pairing/pair-and-sync]}
[cofx installation-id]
{:fx [[:json-rpc/call
[{:method "wakuext_enableInstallationAndSync"
:params [{:installationId installation-id}]
:js-response true
:on-success [:sanitize-messages-and-process-response]}]]]})
[{:method "wakuext_enableInstallationAndSync"
:params [{:installationId installation-id}]
:on-success #(log/debug "successfully synced devices")}]]]})
(rf/defn enable-installation-success
{:events [:pairing.callback/enable-installation-success]}
@@ -1,5 +1,4 @@
(ns quo.components.buttons.wallet-ctas.style
(:require [quo.foundations.colors :as colors]))
(ns quo.components.buttons.wallet-ctas.style)
(def inner-container
{:flex-direction :row
@@ -11,9 +10,3 @@
: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 disabled?]}]
[{:keys [icon text on-press theme accessibility-label]}]
[rn/view
{:style style/button-container
:accessibility-label accessibility-label}
[wallet-button/view
{:icon icon
:disabled? disabled?
:on-press on-press}]
{:icon icon
:on-press on-press}]
[text/text
{:weight :medium
:size :paragraph-2
:style (style/action-button-text theme disabled?)} text]])
:style {:margin-top 4
:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)}} text]])
(defn view
[{:keys [buy-action send-action receive-action bridge-action swap-action bridge-disabled?
swap-disabled? container-style]}]
[{:keys [buy-action send-action receive-action bridge-action swap-action container-style]}]
(let [theme (quo.theme/use-theme)]
[rn/view {:style container-style}
[rn/view {:style style/inner-container}
@@ -52,12 +52,10 @@
: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 (or icon :i/info)
[icons/icon icon
{:color (style/get-color-by-type theme type :icon)
:no-color no-icon-color?
:size (or icon-size 16)
@@ -1,7 +1,6 @@
(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]
@@ -43,8 +42,8 @@
[:map {:closed true}
[:network-image :int]
[:label :string]
[:fiat-value {:optional true} [:maybe :string]]
[:token-value {:optional true} [:maybe :string]]
[:fiat-value :string]
[:token-value :string]
[:container-style {:optional true} [:maybe :map]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]
[:state {:optional true} [:enum :pressed :active :disabled :default]]
@@ -52,7 +51,7 @@
:any])
(defn- view-internal
[{:keys [fiat-value token-value on-press container-style state customization-color]
[{:keys [on-press container-style state customization-color]
:as props
:or {customization-color :blue}}]
(let [theme (quo.theme/use-theme)
@@ -68,8 +67,6 @@
:on-press (when-not (= state :disabled) on-press)
:accessibility-label :network-list}
[info props]
(if (or token-value fiat-value)
[values props]
[icon/icon :i/chevron-right])]))
[values props]]))
(def view (schema/instrument #'view-internal ?schema))
+3 -1
View File
@@ -4,10 +4,12 @@
[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})
{:margin-left 8
:flex 1})
@@ -32,8 +32,7 @@
[rn/pressable
(when-not disabled?
{:on-press on-press
:allow-multiple-presses? true
:hit-slop 12})
:allow-multiple-presses? true})
[rn/view
{:style outer-styles
:needs-offscreen-alpha-compositing true
@@ -75,16 +75,3 @@
(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))
+32 -91
View File
@@ -11,7 +11,6 @@
[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
@@ -19,7 +18,6 @@
[: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]]
@@ -44,65 +42,26 @@
[: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? get-ref
currency-symbol on-change-text show-keyboard?
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)
[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)))))]
(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])]
[rn/view
{:style container-style
:accessibility-label :swap-input
:on-layout on-layout-container}
:accessibility-label :swap-input}
[rn/view {:style (style/content typing? theme)}
[rn/view
{:style (style/row-1 loading?)}
@@ -116,43 +75,25 @@
[rn/pressable
{:style style/input-container
:on-press focus-input}
[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))]]
[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))]
[text/text
{:size :paragraph-2
:weight :semi-bold
:style (style/token-symbol theme)
:on-layout on-layout-label}
{:size :paragraph-2
:weight :semi-bold
:style (style/token-symbol theme)}
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 on-success render-controls]}]
[{:keys [banner-offset initial-insets keypair title recovering-keypair? render-controls]}]
(reagent/with-let [keyboard-shown? (reagent/atom false)
keyboard-show-listener (.addListener rn/keyboard
"keyboardDidShow"
@@ -120,20 +120,14 @@
(reset! seed-phrase new-phrase))
on-submit (fn []
(swap! seed-phrase clean-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}]))]
(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])))]
(let [words-coll (mnemonic/passphrase->words @seed-phrase)
last-word (peek words-coll)
pick-suggested-word (fn [pressed-word]
@@ -197,7 +191,7 @@
[rn/view {:style style/keyboard-container}
[quo/predictive-keyboard
{:type suggestions-state
:blur? (not on-success)
:blur? (not recovering-keypair?)
:text suggestions-text
:words (keyboard-suggestions last-word)
:on-press pick-suggested-word}]])])
@@ -206,7 +200,7 @@
(.remove keyboard-hide-listener))))
(defn screen
[{:keys [title keypair navigation-icon on-success render-controls]}]
[{:keys [title keypair navigation-icon recovering-keypair? 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}
@@ -215,22 +209,21 @@
{:margin-top (:top insets)
:background :blur
:icon-name (or navigation-icon
(if on-success :i/close :i/arrow-left))
(if recovering-keypair? :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
:on-success on-success}]]]))
{:title title
:keypair keypair
:render-controls render-controls
:banner-offset banner-offset
:initial-insets insets
:recovering-keypair? recovering-keypair?}]]]))
(defn view
[]
(let [{:keys [on-success]} (rf/sub [:get-screen-params])]
(let [{:keys [recovering-keypair?]} (rf/sub [:get-screen-params])]
(rn/use-unmount
(when-not on-success
#(rf/dispatch [:onboarding/clear-navigated-to-enter-seed-phrase-from-screen])))
#(rf/dispatch [:onboarding/clear-navigated-to-enter-seed-phrase-from-screen]))
[screen
{:title (i18n/label :t/use-recovery-phrase)
:on-success on-success}]))
{:title (i18n/label :t/use-recovery-phrase)
:recovering-keypair? recovering-keypair?}]))
-1
View File
@@ -17,7 +17,6 @@
: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 "")
(goog-define INFURA_TOKEN "62f3cee52dbb484198e7339837e263f3")
(goog-define POKT_TOKEN "3ef2018191814b7e1009b8d9")
(goog-define STATUS_BUILD_PROXY_USER "")
(goog-define STATUS_BUILD_PROXY_PASSWORD "")
-9
View File
@@ -619,12 +619,3 @@
(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,15 +45,3 @@
#(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 event-value]
[event-name val-key value]
{:metric
{:eventName event-name
:platform platform/os
:appVersion build/app-short-version
:eventValue event-value}})
:eventValue {val-key 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
[layout-height]
[{:keys [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
[layout-height]
[props]
(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 layout-height]
[mentions/view props]
[quo/separator]
[rn/view {:style {:padding-horizontal 20 :padding-top 20}}
[:<>
@@ -3,4 +3,15 @@
;;;; 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,23 +11,25 @@
(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]
[react-native.safe-area :as safe-area]
[quo.foundations.shadows :as shadows]
[react-native.reanimated :as reanimated]
[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,13 +13,70 @@
:background-color (colors/theme-colors colors/white colors/neutral-95 theme)
:z-index 2})
(def permission-context-sheet {:flex 3}) ; Pushes composer to bottom
(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)}))
(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)}))
(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)})
@@ -1,5 +1,6 @@
(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]
@@ -10,9 +11,11 @@
[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]
@@ -22,7 +25,8 @@
[utils.re-frame :as rf]
[utils.worklets.chat.messenger.messages :as worklets]))
(def ^:const loading-indicator-page-loading-height 100)
(defonce ^:const distance-from-last-message 4)
(defonce ^: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))
@@ -56,17 +60,44 @@
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
[{:keys [chat-id window-height]}]
[chat-id {:keys [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)]
[quo/skeleton-list
{:content :messages
:parent-height parent-height
:animated? false}]))
[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)]]))
(defn header-height
[{:keys [insets able-to-send-message? images reply edit link-previews? input-content-height]}]
@@ -94,6 +125,84 @@
(+ (: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])
@@ -127,12 +236,101 @@
chat-type
muted?)))}]}]))
(defn more-messages-loader
[{:keys [chat-id] :as props}]
(let [loading-messages? (rf/sub [:chats/loading-messages? chat-id])
(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])
all-loaded? (rf/sub [:chats/all-loaded? chat-id])]
(when (or loading-messages? (not all-loaded?))
[loading-view props])))
[:<>
(if (or loading-messages? (not all-loaded?))
[loading-view chat-id props]
[footer-component props])]))
(defn list-group-chat-header
[{:keys [chat-id invitation-admin]}]
@@ -161,7 +359,8 @@
(reset! content-height content-height-new)))))
(defn on-layout
[{:keys [event layout-height distance-atom distance-from-list-top on-layout-done?]}]
[{:keys [event layout-height distance-atom distance-from-list-top
chat-screen-layout-calculations-complete?]}]
(let [layout-height-new (oops/oget event "nativeEvent.layout.height")
change (- layout-height-new @layout-height)
new-distance (- @distance-atom change)]
@@ -169,7 +368,8 @@
(reanimated/set-shared-value distance-from-list-top new-distance)
(reset! distance-atom new-distance)
(reset! layout-height layout-height-new))
(reset! on-layout-done? true)))
(when-not (reanimated/get-shared-value chat-screen-layout-calculations-complete?)
(reanimated/set-shared-value chat-screen-layout-calculations-complete? true))))
(defn on-scroll-fn
[distance-atom layout-height-atom]
@@ -179,21 +379,29 @@
(reset! distance-atom new-distance)))
(defn messages-list-content
[{: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}
[{: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?)}
[rn/view {:style {:flex-shrink 1}} ;; Keeps flat list on top
[reanimated/flat-list
{:key-fn list-key-fn
@@ -201,9 +409,12 @@
:bounces false
:header (when (= (:chat-type chat) constants/private-group-chat-type)
[list-group-chat-header chat])
:footer [more-messages-loader
{:chat-id chat-id
:window-height window-height}]
:footer [list-footer
{:theme theme
:chat chat
:window-height window-height
:distance-from-list-top distance-from-list-top
:customization-color customization-color}]
:data messages
:render-data {:theme theme
:context context
@@ -233,14 +444,15 @@
:style {:background-color (colors/theme-colors colors/white
colors/neutral-95
theme)}
:content-container-style (style/list-paddings add-padding-bottom?)
:content-container-style {:padding-top distance-from-last-message}
: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
:on-layout-done? on-layout-done?})
:chat-screen-layout-calculations-complete?
chat-screen-layout-calculations-complete?})
:scroll-enabled (not recording?)
:content-inset-adjustment-behavior :never
:scroll-indicator-insets {:right 1}}]]]))
@@ -1,43 +1,56 @@
(ns status-im.contexts.chat.messenger.messages.navigation.style
(:require [quo.foundations.colors :as colors]
[status-im.contexts.chat.messenger.messages.constants :as messages.constants]))
(:require
[quo.foundations.colors :as colors]
[react-native.reanimated :as reanimated]))
(defn navigation-view
[navigation-view-height]
[navigation-view-height pinned-banner-height]
{:top 0
:left 0
:right 0
:position :absolute
:pointer-events :box-none
:height (+ navigation-view-height messages.constants/pinned-banner-height)
:height (+ navigation-view-height pinned-banner-height)
:z-index 1})
(defn background
[navigation-view-height]
{:height navigation-view-height
:top 0
:left 0
:right 0
:overflow :hidden
:position :absolute})
(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 header-container
[top-insets]
[top-insets top-bar-height]
{:margin-top top-insets
:flex-direction :row
:padding-horizontal 20
:overflow :hidden
:height messages.constants/top-bar-height
:height top-bar-height
:align-items :center})
(defn button-animation-container
[opacity-value]
(reanimated/apply-animations-to-style
{:opacity opacity-value}
{}))
;;;; Content
(def header-content-container
{:flex-direction :row
:align-items :center
:flex 1
:margin-horizontal 12
:height 40})
(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-text-container
{:margin-left 8})
@@ -7,6 +7,7 @@
[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]
@@ -14,26 +15,44 @@
[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.re-frame :as rf]
[utils.worklets.chat.messenger.messages :as messages.worklets]
[utils.worklets.chat.messenger.navigation :as worklets]))
(defn header-content-container
[{: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}
[{: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)}
(cond
community-channel?
[quo/channel-avatar
@@ -71,11 +90,30 @@
(i18n/label
(if online? :t/online :t/offline))])]]))
(defn header-background
[{:keys [chat-id navigation-view-height]}]
(let [theme (quo.theme/use-theme)]
(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)))]
[:<>
[rn/view {:style (style/background navigation-view-height)}
[reanimated/view {:style (style/animated-background-view background-opacity navigation-view-height)}
[quo/blur
{:style {:flex 1}
:blur-amount 20
@@ -83,37 +121,58 @@
: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
:top-offset navigation-view-height}]]))
{:chat-id chat-id
:banner-opacity banner-opacity
: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])
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
: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
{:chat-id chat-id
: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]]]))
: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]]]]))
@@ -1,5 +1,6 @@
(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
@@ -9,6 +10,8 @@
:right 0
:height messages.constants/pinned-banner-height})
(defn container-with-top-offset
[top-offset]
(assoc container :top top-offset))
(defn container-animated-style
[top-offset banner-opacity]
(reanimated/apply-animations-to-style
{:opacity banner-opacity}
(assoc container :top top-offset)))
@@ -2,26 +2,30 @@
(: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 top-offset]}]
(let [theme (quo.theme/use-theme)
latest-pin-text (rf/sub [:chats/last-pinned-message-text chat-id])
[{:keys [chat-id] :as props}]
(let [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)
[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]))}]])))
[:f> f-banner props latest-pin-text pins-count])))
@@ -7,7 +7,7 @@
[utils.worklets.chat.messenger.composer :as worklets]))
(defn button
[chat-list-scroll-y]
[{:keys [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,50 +19,57 @@
[utils.re-frame :as rf]))
(defn- footer
[layout-height]
[props]
(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 layout-height]
[composer/view props]
[contact-requests.bottom-drawer/view {:contact-id current-chat-id}]))))
(defn- chat-screen
[on-layout-done?]
[{:keys [insets] :as props}]
(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])
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]]))
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])))
(defn chat
[]
(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?]]))))
(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?]]))
@@ -1,15 +1,16 @@
(ns status-im.contexts.chat.messenger.placeholder.style
(:require [quo.foundations.colors :as colors]
[react-native.safe-area :as safe-area]))
[react-native.reanimated :as reanimated]))
(defn container
[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)})
[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)}))
@@ -3,8 +3,11 @@
[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.re-frame :as rf]
[utils.worklets.chat.messenger.placeholder :as worklets]))
(defn- loading-skeleton
[]
@@ -14,10 +17,13 @@
:animated? false}])
(defn view
[on-layout-done?]
[chat-screen-layout-calculations-complete?]
(let [theme (quo.theme/use-theme)
chat-exist? (rf/sub [:chats/current-chat-exist?])]
[rn/view {:style (style/container theme @on-layout-done?)}
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)}
(when-not chat-exist?
[quo/page-nav
{:icon-name :i/arrow-left
@@ -119,29 +119,38 @@
:icon-left :i/communities}
(i18n/label :t/request-to-join)])
(defn token-gated-communities-info
(defn- info-button-handler
[]
[quo/documentation-drawers {:title (i18n/label :t/token-gated-communities)}
[quo/text {:size :paragraph-2}
(i18n/label :t/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)]])}]))
(defn- token-requirements
[{:keys [id color role-permissions?]}]
(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}])]
(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])]
(cond
networks-not-supported?
[network-not-supported]
@@ -151,12 +160,13 @@
: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 on-press-info}])))
:satisfied? can-request-access?
:on-press on-press
:on-press-info
info-button-handler}])))
(defn- join-community
[{:keys [id joined permissions] :as community}]
@@ -1,35 +0,0 @@
(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)}]]]))
@@ -1,36 +0,0 @@
(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)]])
+1 -8
View File
@@ -10,15 +10,10 @@
(defonce ^:private active-listeners (atom []))
(defn unregister-card-events
(defn register-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]))
@@ -28,9 +23,7 @@
(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
[]
@@ -1,36 +0,0 @@
(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)]])
@@ -1,42 +0,0 @@
(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."]]]))
+11 -52
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,9 +14,7 @@
(rf/reg-event-fx :keycard.ios/on-nfc-user-cancelled
(fn [{:keys [db]}]
(log/debug "[keycard] nfc user cancelled")
{:db (-> db
(assoc-in [:keycard :pin :status] nil)
(assoc-in [:keycard :on-nfc-cancelled-event-vector] nil))
{:db (assoc-in db [:keycard :pin :status] nil)
:fx [(when-let [on-nfc-cancelled-event-vector (get-in db [:keycard :on-nfc-cancelled-event-vector])]
[:dispatch on-nfc-cancelled-event-vector])]}))
@@ -44,6 +42,12 @@
{: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)
@@ -56,7 +60,7 @@
(rf/reg-event-fx :keycard/on-action-with-pin-error
(fn [{:keys [db]} [error]]
(log/debug "[keycard] on-action-with-pin-error: " error)
(log/debug "[keycard] get keys 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?
@@ -65,52 +69,7 @@
{:effects.utils/show-popup {:title "wrong-keycard"}}
{:db (-> db
(assoc-in [:keycard :application-info :pin-retry-counter] pin-retries-count)
(assoc-in [:keycard :pin :status] :error))
:fx [[:dispatch [:keycard/disconnect]]
(update-in [:keycard :pin] assoc :status :error))
:fx [[:dispatch [:keycard/hide-connection-sheet]]
(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,5 +1,7 @@
(ns status-im.contexts.keycard.login.events
(:require [utils.re-frame :as rf]))
(:require [status-im.contexts.keycard.utils :as keycard.utils]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
(rf/reg-event-fx :keycard.login/on-get-keys-success
(fn [{:keys [db]} [data]]
@@ -14,7 +16,7 @@
:password encryption-public-key
:key-uid key-uid
:name (:name profile)))
:fx [[:dispatch [:keycard/disconnect]]
:fx [[:dispatch [:keycard/hide-connection-sheet]]
[:effects.keycard/login-with-keycard
{:password encryption-public-key
:whisper-private-key whisper-private-key
@@ -31,8 +33,36 @@
:password encryption-public-key
:key-uid key-uid
:name (:name profile)))
:fx [[:dispatch [:keycard/disconnect]]
:fx [[:dispatch [:keycard/hide-connection-sheet]]
[: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,28 +0,0 @@
(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)]]]))
@@ -1,55 +0,0 @@
(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}]]))
@@ -1,16 +1,18 @@
(ns status-im.contexts.keycard.nfc-sheet.events
(ns status-im.contexts.keycard.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 [{:keys [db]} [{:keys [on-cancel-event-vector]} :as args]]
(fn [_ [args]]
(if platform/android?
{:db (assoc-in db
[:keycard :connection-sheet-opts]
{:on-close #(rf/dispatch on-cancel-event-vector)})
:fx [[:dismiss-keyboard true]
[:show-nfc-sheet nil]]}
{:dispatch [:keycard/show-connection-sheet-component args]}
{:effects.keycard.ios/start-nfc
{:on-success
(fn []
@@ -1,9 +1,8 @@
(ns status-im.contexts.keycard.nfc-sheet.view
(ns status-im.contexts.keycard.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
@@ -19,13 +18,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}}
(i18n/label :t/ready-to-scan)]
"Ready to Scan"]
[rn/image
{:source (resources/get-image :nfc-prompt)}]
[rn/text {:style {:font-size 16 :color :white :margin-vertical 36}}
(if connected?
(i18n/label :t/connected-dont-move)
(i18n/label :t/hold-phone-near-keycard))]
"Connected. Dont move your card."
"Hold your phone near a Status Keycard")]
[rn/pressable
{:on-press (fn []
(when on-close (on-close))
@@ -34,4 +33,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}}
(i18n/label :t/cancel)]]]]]))
"Cancel"]]]]]))
@@ -1,38 +0,0 @@
(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])}}]]))
+13 -14
View File
@@ -14,17 +14,16 @@
path (get-in db [:wallet :accounts current-address :path])
key-uid (get-in db [:profile/profile :key-uid])]
{:fx [[:dispatch
[: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
%])}]]}]]]})))
[: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 %])}]]}]]]})))
+6 -9
View File
@@ -18,31 +18,28 @@
(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)
:keycard/error.not-keycard
:not-keycard
(empty? (:key-uid application-info))
:keycard/error.keycard-blank
:keycard-blank
profile-mismatch?
:keycard/error.keycard-wrong
:keycard-wrong
(not paired?)
:keycard/error.keycard-unpaired
:keycard-unpaired
(and (zero? pin-retry-counter)
(or (nil? puk-retry-counter)
(pos? puk-retry-counter)))
:keycard/error.keycard-frozen
(zero? puk-retry-counter)
:keycard/error.keycard-locked
nil
:else
nil)))
@@ -58,7 +58,7 @@
(reset! shell.state/screen-height height)
(async-storage/set-item! :screen-height height))))
(defn view
(defn f-view
[dark-overlay?]
(let [view-id (rf/sub [:view-id])
animate? (not dark-overlay?)
@@ -94,3 +94,5 @@
:blur-radius (if platform/android? 25 10)
:blur-type :transparent
:overlay-color :transparent}])]))
(defn view [dark-overlay?] [:f> f-view dark-overlay?])
@@ -131,6 +131,15 @@
[: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]
@@ -102,6 +102,8 @@
[: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,13 +242,12 @@
{:on-complete
(fn [pin-text]
(rf/dispatch
[: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
%])}]]}]))}]
[: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 %])}]]}]))}]
[password-input])]
(when-not keycard-pairing
[quo/button
@@ -1,31 +1,9 @@
(ns status-im.contexts.profile.recover.effects
(:require
[native-module.core :as native-module]
[promesa.core :as promesa]
[utils.re-frame :as rf]
[utils.security.core :as security]
[utils.transforms :as transforms]))
[utils.re-frame :as rf]))
(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,7 +24,3 @@
: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,7 +1,6 @@
(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]))
@@ -55,9 +54,9 @@
:image :icon
:blur? true
:action :arrow})
(when (ff/enabled? ::ff/keycard.migrate-profile)
(when config/show-not-implemented-features?
{:title (i18n/label :t/keycard)
:on-press #(rf/dispatch [:open-modal :screen/settings.keycard])
:on-press not-implemented/alert
:image-props :i/keycard
:image :icon
:blur? true
@@ -49,14 +49,13 @@
(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])
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))]
(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))]
[quo/overlay {:type :shell}
[rn/view
{:style (style/navigation-wrapper {:customization-color customization-color
@@ -80,7 +79,7 @@
profile)}}])}]}]]
[rn/flat-list
{:header [settings.header/view {:scroll-y scroll-y}]
:data (settings.items/items (not recovery-phrase-backed-up?))
:data (settings.items/items (boolean (seq (:mnemonic profile))))
:shows-vertical-scroll-indicator false
:render-fn settings-category-view
:get-item-layout get-item-layout
@@ -1,45 +0,0 @@
(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 [[:effects.profile/validate-recovery-phrase [seed-phrase on-success on-error]]]})
{:fx [[:multiaccount/validate-mnemonic [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 [[:effects.profile/validate-recovery-phrase
expected {:fx [[:multiaccount/validate-mnemonic
[seed-phrase-masked on-success on-error]]]}]
(is (= expected
(sut/wallet-validate-seed-phrase
@@ -28,8 +28,7 @@
;; TODO: Replace with correct enum values once status-go implements them.
(def ^:const tx 66612)
(def ^:const system
#{new-installation-received new-installation-created})
(def ^:const system 66614)
(def ^:const membership
"Membership is like a logical group of notifications with different types, i.e.
+18 -20
View File
@@ -26,8 +26,7 @@
(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])
testnet-mode? (rf/sub [:profile/test-networks-enabled?])]
customization-color (rf/sub [:profile/customization-color])]
(hot-reload/use-safe-unmount (fn []
(rf/dispatch [:wallet/close-account-page])
(rf/dispatch [:wallet/clean-current-viewing-account])))
@@ -47,24 +46,23 @@
[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 (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?}])
{: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])))}])
[quo/tabs
{:style style/tabs
:size 32
@@ -61,6 +61,16 @@
(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)]
@@ -1,4 +1,4 @@
(ns status-im.contexts.profile.backup-recovery-phrase.style
(ns status-im.contexts.wallet.add-account.create-account.new-keypair.backup-recovery-phrase.style
(:require
[quo.foundations.colors :as colors]
[react-native.platform :as platform]))
@@ -1,4 +1,4 @@
(ns status-im.contexts.profile.backup-recovery-phrase.view
(ns status-im.contexts.wallet.add-account.create-account.new-keypair.backup-recovery-phrase.view
(:require
[clojure.string :as string]
[native-module.core :as native-module]
@@ -6,7 +6,8 @@
[quo.theme :as quo.theme]
[react-native.core :as rn]
[reagent.core :as reagent]
[status-im.contexts.profile.backup-recovery-phrase.style :as style]
[status-im.contexts.wallet.add-account.create-account.new-keypair.backup-recovery-phrase.style :as
style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.security.core :as security]))
@@ -44,20 +45,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])
{:keys [on-success]} (rf/sub [:get-screen-params])
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])
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")))
@@ -106,10 +107,10 @@
:button-one-label (i18n/label :t/i-have-written)
:button-one-props {:disabled? (some false? (vals @checked?))
:customization-color customization-color
:on-press #(on-success {:masked-seed-phrase
(security/mask-data
@seed-phrase)
:random-phrase @random-phrase})}}]
:on-press #(rf/dispatch [:wallet/store-new-seed-phrase
{:seed-phrase (security/mask-data
@seed-phrase)
:random-phrase @random-phrase}])}}]
[quo/text
{:size :paragraph-2
:style (style/description-text theme)}
@@ -16,21 +16,13 @@
[[{:icon :i/add
:accessibility-label :generate-new-keypair
:label (i18n/label :t/generate-new-keypair)
: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}]))}])}
:on-press #(rf/dispatch [:navigate-to :screen/wallet.backup-recovery-phrase])}
{:icon :i/seed
:accessibility-label :import-using-phrase
:label (i18n/label :t/import-using-phrase)
:add-divider? 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]))}]))}
:on-press #(rf/dispatch [:navigate-to :screen/wallet.enter-seed-phrase
{:recovering-keypair? true}])}
(when (ff/enabled? ::ff/wallet.import-private-key)
{:icon :i/key
:accessibility-label :import-private-key
@@ -1,72 +0,0 @@
(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,20 +8,19 @@
[utils.re-frame :as rf]))
(defn get-bottom-sheet-args
[switcher-type params]
[switcher-type]
(case switcher-type
:account-options {:content account-options/view
:hide-handle? true}
:select-account {:content (fn []
[select-account/view params])}
:select-account {:content select-account/view}
nil))
(defn- on-dapps-press
[switcher-type params]
(rf/dispatch [:show-bottom-sheet (get-bottom-sheet-args switcher-type params)]))
[switcher-type]
(rf/dispatch [:show-bottom-sheet (get-bottom-sheet-args switcher-type)]))
(defn view
[{:keys [type on-press accessibility-label icon-name switcher-type margin-top params]
[{:keys [type on-press accessibility-label icon-name switcher-type margin-top]
:or {icon-name :i/close
accessibility-label :top-bar
switcher-type :account-options
@@ -46,6 +45,6 @@
(when-not sending-collectible?
{:content-type :account-switcher
:customization-color color
:on-press #(on-dapps-press switcher-type params)
:on-press #(on-dapps-press switcher-type)
:emoji emoji
:type (when watch-only? :watch-only)})]}]))
@@ -32,11 +32,9 @@
:selected)}]))
(defn view
[{:keys [content-container-style search-text on-token-press preselected-token-symbol chain-ids]
[{:keys [content-container-style search-text on-token-press preselected-token-symbol]
:or {content-container-style {:padding-horizontal 8}}}]
(let [filtered-tokens (rf/sub [:wallet/current-viewing-account-tokens-filtered
{:query search-text
:chain-ids chain-ids}])
(let [filtered-tokens (rf/sub [:wallet/current-viewing-account-tokens-filtered {:query search-text}])
currency (rf/sub [:profile/currency])
currency-symbol (rf/sub [:profile/currency-symbol])]
[gesture/flat-list
@@ -1,5 +1,6 @@
(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]
@@ -17,15 +18,15 @@
:right-icon :i/external})
(defn- action-send
[{:keys [disabled?] :as params} entry-point]
[send-params entry-point]
{:icon :i/send
:accessibility-label :send
:label (i18n/label :t/send)
:disabled? disabled?
:disabled? (:disabled? send-params)
:on-press (fn []
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch [:wallet/clean-send-data])
(rf/dispatch [:wallet/set-token-to-send params entry-point]))})
(rf/dispatch [:wallet/set-token-to-send send-params entry-point]))})
(defn- action-receive
[selected-account?]
@@ -37,27 +38,21 @@
#(rf/dispatch [:open-modal :screen/share-shell {:initial-tab :wallet}]))})
(defn- action-bridge
[{:keys [bridge-disabled? testnet-mode?] :as params}]
[bridge-params]
{:icon :i/bridge
:accessibility-label :bridge
:label (i18n/label :t/bridge)
:disabled? (or testnet-mode? bridge-disabled?)
:disabled? (:bridge-disabled? bridge-params)
:on-press (fn []
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch [:wallet/bridge-select-token params]))})
(rf/dispatch [:wallet/bridge-select-token bridge-params]))})
(defn- action-swap
[{:keys [token asset-to-receive token-symbol testnet-mode?]}]
[]
{:icon :i/swap
:accessibility-label :swap
:label (i18n/label :t/swap)
: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}]))})
:on-press #(not-implemented/alert)})
(defn- action-manage-tokens
[watch-only?]
@@ -76,41 +71,47 @@
(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}]))
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))]
(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})]
[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 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))))]))]]))
(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))]))]]))
(defn view
[item _ _ {:keys [watch-only? entry-point]}]
@@ -15,17 +15,13 @@
[full-name]
(first (string/split full-name #" ")))
(defn cut-fiat-balance
[balance decimals]
(defn cut-fiat-balance-to-two-decimals
[balance]
(let [valid-balance? (and balance
(or (number? balance) (.-toFixed balance)))]
(as-> balance $
(if valid-balance? $ 0)
(.toFixed $ decimals))))
(defn cut-fiat-balance-to-two-decimals
[balance]
(cut-fiat-balance balance 2))
(.toFixed $ 2))))
(defn prettify-balance
[currency-symbol balance]
@@ -155,10 +151,6 @@
[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]}]
@@ -501,13 +493,3 @@
: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"))
+21 -2
View File
@@ -4,7 +4,6 @@
[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]
@@ -27,6 +26,26 @@
: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
@@ -62,7 +81,7 @@
(defn import-missing-keypair-by-seed-phrase
[keypair-key-uid seed-phrase password]
(-> (profile.recover.effects/validate-mnemonic seed-phrase)
(-> (validate-mnemonic seed-phrase)
(promesa/then
(fn [{:keys [key-uid]}]
(if (not= keypair-key-uid key-uid)
+3 -4
View File
@@ -345,7 +345,8 @@
:Prod
data-store/rpc->network)
data)}]
{:fx [[:dispatch [:wallet.tokens/get-token-list]]]
{:fx [(when (ff/enabled? ::ff/wallet.swap)
[:dispatch [:wallet.tokens/get-token-list]])]
:db (assoc-in db [:wallet :networks] network-data)})))
(rf/reg-event-fx
@@ -477,9 +478,7 @@
[:dispatch [:wallet/get-ethereum-chains]]
[:dispatch [:wallet/get-accounts]]
[:dispatch [:wallet/get-keypairs]]
[:dispatch [:wallet/get-saved-addresses]]
(when (ff/enabled? ::ff/wallet.wallet-connect)
[:dispatch-later [{:ms 500 :dispatch [:wallet-connect/init]}]])]}))
[:dispatch [:wallet/get-saved-addresses]]]}))
(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]]
[cljs.test :refer-macros [is testing run-tests]]
matcher-combinators.test
[re-frame.db :as rf-db]
[status-im.contexts.wallet.common.utils.networks :as network-utils]
@@ -710,3 +710,5 @@
: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/add-eth)
:button-text (i18n/label :t/buy-eth)
:on-button-press #(rf/dispatch [:show-bottom-sheet
{:content buy-token/view}])}])
@@ -158,14 +158,16 @@
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
@@ -194,8 +196,10 @@
(utils/cut-crypto-decimals-to-fit-usd-cents
token-balance
usd-conversion-rate)
(utils/cut-fiat-balance-to-two-decimals
(money/crypto->fiat token-balance conversion-rate)))
(-> (money/crypto->fiat
token-balance
conversion-rate)
utils/cut-fiat-balance-to-two-decimals))
input-value (controlled-input/input-value input-state)
valid-input? (not (or (controlled-input/empty-value? input-state)
(controlled-input/input-error input-state)))
@@ -385,8 +389,7 @@
: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,7 +10,8 @@
[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])
@@ -1,28 +0,0 @@
(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})
@@ -1,78 +0,0 @@
(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,20 +7,11 @@
[utils.re-frame :as rf]))
(defn- crypto-on-ramp-item
[{: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])]
[{: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])]
[quo/settings-item
{:title name
:description :text
@@ -48,14 +39,13 @@
(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/ways-to-buy-assets))}]
[quo/drawer-top {:title (or title (i18n/label :t/buy-assets))}]
[quo/segmented-control
{:size 32
:container-style style/tabs
@@ -68,6 +58,5 @@
(:one-time crypto-on-ramps))
:on-layout on-layout
:style (style/list-container min-height)
:render-data {:tab selected-tab
:account account}
:render-data {:tab selected-tab}
:render-fn crypto-on-ramp-item}]]))
@@ -2,60 +2,30 @@
(: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 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- 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 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 (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-fn render-account-item
:render-data {:selected-account-address selected-account-address}
:content-container-style style/list-container
:shows-vertical-scroll-indicator false}]]))
@@ -51,31 +51,14 @@
: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 [provider account network]}]
[{:keys [account network]}]
[rn/view {:style style/subheader-container}
[quo/text
{:size :paragraph-2
:weight :medium}
(i18n/label :t/select-asset-on)]
(if provider
[provider-view provider]
[account-view account])
(when provider
[:<>
[quo/text
{:size :paragraph-2
:weight :medium}
(i18n/label :t/to)]
[account-view account]])
[account-view account]
[quo/text
{:size :paragraph-2
:weight :medium}
@@ -83,8 +66,9 @@
[network-view network]])
(defn view
[{:keys [title network provider on-select hide-token-fn disable-token-fn]}]
(let [account (rf/sub [:wallet/current-viewing-account-or-default])
[{:keys [title on-select hide-token-fn disable-token-fn]}]
(let [account (rf/sub [:wallet/current-viewing-account])
{:keys [network]} (rf/sub [:wallet/swap])
[search-text set-search-text] (rn/use-state "")
window-height (:height (rn/get-window))]
[rn/safe-area-view {:style (style/container window-height)}
@@ -97,9 +81,8 @@
{:title title
:title-accessibility-label :title-label}]
[subheader-view
{:account account
:network network
:provider provider}]
{:account account
:network network}]
[assets-view
{:search-text search-text
:on-change-text #(set-search-text %)
+23 -30
View File
@@ -14,30 +14,22 @@
[utils.number :as number]))
(rf/reg-event-fx :wallet.swap/start
(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))]
(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))]
{: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 [: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
[: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
@@ -49,9 +41,8 @@
(rf/dispatch
[:wallet.swap/start
{:asset-to-pay asset-to-pay
:asset-to-receive asset-to-receive
:network network
:open-new-screen? open-new-screen?}]))}])}]]])})))
:asset-to-receive asset-to-receive'
:network network}]))}])}]]])})))
(rf/reg-event-fx :wallet.swap/select-asset-to-pay
(fn [{:keys [db]} [{:keys [token]}]]
@@ -70,7 +61,11 @@
(rf/reg-event-fx :wallet.swap/select-asset-to-receive
(fn [{:keys [db]} [{:keys [token]}]]
{:db (assoc-in db [:wallet :ui :swap :asset-to-receive] token)}))
{:db (update-in db
[:wallet :ui :swap]
#(-> %
(assoc :asset-to-receive token)
(assoc :loading-swap-proposal? true)))}))
(rf/reg-event-fx :wallet.swap/set-default-slippage
(fn [{:keys [db]}]
@@ -131,6 +126,8 @@
: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"
@@ -204,12 +201,6 @@
(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
@@ -304,6 +295,8 @@
: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,15 +20,8 @@
(defn- assets-view
[search-text on-change-text]
(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}])))]
(let [on-token-press (fn [token]
(rf/dispatch [:wallet.swap/start {:asset-to-pay token}]))]
[:<>
[search-input search-text on-change-text]
[asset-list/view
@@ -10,12 +10,8 @@
:height 36
:background-color :transparent})
(def floating-button-page-content-container
{:margin-bottom 168})
(def scroll-view-container
{:flex 1
:padding-bottom 20})
{:padding-bottom 150})
(def content-container
{:padding-top 12
@@ -60,5 +56,5 @@
(defn footer-container
[theme]
{:margin-bottom -12
:background-color (colors/theme-colors colors/white colors/neutral-95 theme)})
{:margin-bottom -10
:background-color (colors/theme-colors colors/white colors/neutral-100 theme)})
@@ -254,7 +254,6 @@
[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,29 +1,27 @@
(ns status-im.contexts.wallet.swap.setup-swap.view
(: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]))
(: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]))
(def ^:private default-text-for-unfocused-input "0.00")
@@ -87,7 +85,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])
pay-token-symbol (:symbol (rf/sub [:wallet/swap-asset-to-pay]))
asset-to-pay (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])
@@ -98,19 +96,14 @@
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 (number/convert-to-whole-number
(get-in asset-to-pay
[:balances-per-chain
(:chain-id network) :raw-balance]
0)
pay-token-decimals)
pay-token-balance-selected-chain (get-in asset-to-pay
[:balances-per-chain
(:chain-id network) :balance]
0)
pay-token-fiat-value (str
(utils/calculate-token-fiat-value
{:currency currency
@@ -118,15 +111,10 @@
: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 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)
(.toFixed available-crypto-limit
(min pay-token-decimals
constants/min-token-decimals-to-display)))
approval-amount-required-num (when approval-amount-required
(str (number/hex->whole approval-amount-required
pay-token-decimals)))
@@ -134,7 +122,8 @@
(money/greater-than
(money/bignumber pay-input-amount)
available-crypto-limit))
(money/equal-to available-crypto-limit
(money/equal-to (money/bignumber
available-crypto-limit-display)
(money/bignumber 0)))
valid-pay-input? (and
(not (string/blank?
@@ -148,20 +137,12 @@
: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
{:get-ref set-input-ref
:type :pay
{:type :pay
:error? pay-input-error?
:token pay-token-symbol
:customization-color :blue
@@ -175,7 +156,7 @@
:else :disabled)
:currency-symbol currency-symbol
:on-token-press on-token-press
:on-max-press #(on-max-press (str pay-token-balance-selected-chain))
:on-max-press #(on-max-press (str available-crypto-limit))
:on-input-focus on-input-focus
:value pay-input-amount
:fiat-value pay-token-fiat-value
@@ -258,15 +239,17 @@
(merge {:action? true
:on-button-press #(rf/dispatch [:show-bottom-sheet
{:content buy-token/view}])
:button-text (i18n/label :t/add-assets)})
:button-text (i18n/label :t/buy-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])}])
:button-text (i18n/label :t/add-eth)}))]
[buy-token/view
{:title (i18n/label
:t/buy-ethereum)}])}])
:button-text (i18n/label :t/buy-eth)}))]
(when (or pay-input-error? error-response)
[quo/alert-banner props])))
@@ -291,28 +274,23 @@
(defn- pay-token-bottom-sheet
[]
(let [asset-to-receive (rf/sub [:wallet/swap-asset-to-receive])
network (rf/sub [:wallet/swap-network])]
(let [asset-to-receive (rf/sub [:wallet/swap-asset-to-receive])]
[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 [balances-per-chain]}]
(let [balance (get-in balances-per-chain [(:chain-id network) :balance] "0")]
(and (= type constants/swap-tokens-my)
(= balance "0"))))
:hide-token-fn (fn [type {:keys [available-balance]}]
(and (= type constants/swap-tokens-my)
(= available-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])
network (rf/sub [:wallet/swap-network])]
(let [asset-to-pay (rf/sub [:wallet/swap-asset-to-pay])]
[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]
@@ -332,12 +310,10 @@
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 (number/convert-to-whole-number
(get-in asset-to-pay
[:balances-per-chain
(:chain-id network) :raw-balance]
0)
pay-token-decimals)
pay-token-balance-selected-chain (get-in asset-to-pay
[:balances-per-chain
(:chain-id network) :balance]
0)
pay-input-error? (and (not (string/blank? pay-input-amount))
(money/greater-than
(money/bignumber pay-input-amount)
@@ -439,17 +415,13 @@
(when (and swap-amount refetch-interval)
(js/clearTimeout @refetch-interval)
(reset! refetch-interval nil))
(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)))))
(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)))))
[asset-to-pay])
(rn/use-effect
refetch-swap-proposal
@@ -459,10 +431,7 @@
{:on-press on-close
:icon-name :i/arrow-left
:margin-top (safe-area/get-top)
:switcher-type :select-account
:params {:show-account-balances? true
:asset-symbol (:symbol asset-to-pay)
:network network}}]
:switcher-type :select-account}]
[rn/view {:style style/inputs-container}
[pay-token-input
{:input-state pay-input-state
@@ -484,7 +453,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}
(when-not loading-swap-proposal? [alert-banner {:pay-input-error? pay-input-error?}])
[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}]]
+11 -50
View File
@@ -30,61 +30,22 @@
:else
(i18n/label :t/something-went-wrong-please-try-again-later)))
(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))]
(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))]
(->> (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,8 +34,9 @@
(defn tokens-by-symbol
[props]
(tokens-by-key
(assoc props
:key-fn
(fn [{:keys [chainId address] :as token}]
(str chainId "-" (if (get-in token [:communityData :id]) address (:symbol token)))))))
(tokens-by-key (assoc props
:key-fn
(fn [{:keys [address] :as token}]
(if (get-in token [:communityData :id])
address
(:symbol token))))))
@@ -75,7 +75,7 @@
:image nil})
:by-symbol '({:address "0x0000000000000000000000000000000000000000"
:decimals 18
:key "1-ETH"
:key "ETH"
:community-id nil
:symbol "ETH"
:sources ["native"]
@@ -86,7 +86,7 @@
:image nil}
{:address "0x0000000000000000000000000000000000000001"
:decimals 18
:key "1-SNT"
:key "SNT"
:community-id nil
:symbol "SNT"
:sources ["native"]
@@ -174,7 +174,7 @@
:image nil})
:by-symbol '({:address "0x0000000000000000000000000000000000000000"
:decimals 18
:key "1-ETH"
:key "ETH"
:community-id nil
:symbol "ETH"
:sources ["native"]
@@ -185,7 +185,7 @@
:image nil}
{:address "0x0000000000000000000000000000000000000001"
:decimals 18
:key "1-SNT"
:key "SNT"
:community-id nil
:symbol "SNT"
:sources ["native" "second"]
@@ -196,7 +196,7 @@
:image nil}
{:address "0x0000000000000000000000000000000000000002"
:decimals 18
:key "1-ANOTHER1"
:key "ANOTHER1"
:community-id nil
:symbol "ANOTHER1"
:sources ["second"]
@@ -253,7 +253,7 @@
:image nil})
:by-symbol '({:address "0x0000000000000000000000000000000000000000"
:decimals 18
:key "1-ETH"
:key "ETH"
:community-id nil
:symbol "ETH"
:sources ["native"]
@@ -264,7 +264,7 @@
:image nil}
{:address "0x0000000000000000000000000000000000000000"
:decimals 18
:key "1-ANOTHER1"
:key "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 without initializing WC
{:fx [[:dispatch [:wallet-connect/get-sessions]]]}))))
;; NOTE: when offline, fetching persistent sessions only
{:fx [[:dispatch [:wallet-connect/fetch-persisted-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/get-sessions]]]}))
[:dispatch [:wallet-connect/fetch-persisted-sessions]]]}))
(rf/reg-event-fx
:wallet-connect/reload-on-network-change
@@ -1,15 +1,13 @@
(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
@@ -43,21 +41,37 @@
(rf/reg-fx
:effects.wallet-connect/disconnect
(fn [{:keys [web3-wallet topic on-success on-fail]}]
(-> (sessions/disconnect web3-wallet topic)
(fn [{:keys [web3-wallet topic reason on-success on-fail]}]
(-> (wallet-connect/disconnect-session {:web3-wallet web3-wallet
:topic topic
:reason reason})
(promesa/then on-success)
(promesa/catch on-fail))))
(rf/reg-fx
:effects.wallet-connect/approve-session
(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)))))
(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))))
(rf/reg-fx
:effects.wallet-connect/sign-message
@@ -72,8 +86,8 @@
(signing/eth-sign password address data)
(signing/personal-sign password address data))
(promesa/then (partial rf/call-continuation on-success))
(promesa/catch (partial rf/call-continuation on-error))))))
(promesa/then on-success)
(promesa/catch on-error)))))
(rf/reg-fx
:effects.wallet-connect/prepare-transaction
@@ -92,8 +106,8 @@
tx-hash
tx-args
chain-id)
(promesa/then (partial rf/call-continuation on-success))
(promesa/catch (partial rf/call-continuation on-error)))))
(promesa/then on-success)
(promesa/catch on-error))))
(rf/reg-fx
:effects.wallet-connect/send-transaction
@@ -103,19 +117,19 @@
tx-hash
tx-args
chain-id)
(promesa/then (partial rf/call-continuation on-success))
(promesa/catch (partial rf/call-continuation on-error)))))
(promesa/then on-success)
(promesa/catch on-error))))
(rf/reg-fx
:effects.wallet-connect/sign-typed-data
(fn [{:keys [password address data version chain-id on-success 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)))))
(-> (signing/eth-sign-typed-data (security/safe-unmask-data password)
address
data
chain-id
version)
(promesa/then on-success)
(promesa/catch on-error))))
(rf/reg-fx
:effects.wallet-connect/respond-session-request
@@ -141,10 +155,3 @@
: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,6 +109,7 @@
(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]}]
@@ -118,62 +119,33 @@
(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)
expired? (-> current-proposal
(get-in [:params :expiryTimestamp])
uri/timestamp-expired?)]
expiry (get-in current-proposal [:params :expiryTimestamp])]
(if (= network-status :online)
{:db (assoc-in db [:wallet-connect/current-proposal :response-sent?] true)
:fx [(if expired?
:fx [(if (uri/timestamp-expired? expiry)
[: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-request current-proposal
:session-networks session-networks
:address current-address
:on-success [:wallet-connect/approve-session-success]
:on-fail [:wallet-connect/approve-session-error]}])
{: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]))}])
[: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,9 +37,7 @@
(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
{:on-success (fn [] (rf/dispatch [:wallet-connect/show-request-modal]))}]]
[:dispatch [:wallet-connect/process-eth-send-transaction]]
constants/wallet-connect-eth-sign-method
[:dispatch [:wallet-connect/process-eth-sign]]
@@ -96,25 +94,22 @@
:raw-data prepared-tx
:transaction tx
:chain-id chain-id
:display-data display-data)})))
:display-data display-data)
:fx [[:dispatch [:wallet-connect/show-request-modal]]]})))
(rf/reg-event-fx
:wallet-connect/process-eth-send-transaction
(fn [{:keys [db]} [{:keys [on-success]}]]
(fn [{:keys [db]}]
(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)]
(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 %])}]]}))))
{: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 %])}]]})))
(rf/reg-event-fx
:wallet-connect/process-eth-sign-transaction
@@ -135,13 +130,16 @@
(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)
typed-data (-> raw-data
transforms/js-parse
transforms/js->clj)
data-chain-id (typed-data/get-chain-id typed-data)]
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))]
(if (and data-chain-id
(not= session-chain-id data-chain-id))
{:fx [[:dispatch
@@ -152,7 +150,7 @@
[:wallet-connect/current-request]
assoc
:address (string/lower-case address)
:display-data (typed-data/flatten-typed-data typed-data)
:display-data (or parsed-data raw-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 [:wallet-connect/on-sign-error]
:on-success [:wallet-connect/finish-session-request]}]]})))
:on-error #(rf/dispatch [:wallet-connect/on-sign-error %])
:on-success #(rf/dispatch [: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 [:wallet-connect/on-sign-error]
:on-success [:wallet-connect/finish-session-request]}]]})))
:on-error #(rf/dispatch [:wallet-connect/on-sign-error %])
:on-success #(rf/dispatch [: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 [:wallet-connect/on-sign-error]
:on-success [:wallet-connect/finish-session-request]}]]})))
:on-error #(rf/dispatch [:wallet-connect/on-sign-error %])
:on-success #(rf/dispatch [: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 [:wallet-connect/on-sign-error]
:on-success [:wallet-connect/finish-session-request]}]]})))
:on-error #(rf/dispatch [:wallet-connect/on-sign-error %])
:on-success #(rf/dispatch [:wallet-connect/finish-session-request %])}]]})))
(rf/reg-event-fx
:wallet-connect/on-sign-error
@@ -161,15 +161,22 @@
(rf/reg-event-fx
:wallet-connect/finish-session-request
(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]]]})))
(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]]]})))
;; NOTE: Currently we only reject a session if the user dismissed a modal
;; without accepting the session first.
@@ -182,17 +189,9 @@
(rf/reg-event-fx
:wallet-connect/on-request-modal-dismissed
(fn [{:keys [db]}]
(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]]])})))
{: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]]]}))

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