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
157 changed files with 1957 additions and 3361 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: 999 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

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}
@@ -48,16 +48,14 @@
:accessibility-label :receive}]
(when swap-action
[action-button
{:icon :i/transaction
{:icon :i/swap
: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
@@ -60,8 +60,7 @@
(defn- view-internal
[{:keys [type account-props token-props networks? values]}]
(let [theme (quo.theme/use-theme)
address (or (:address account-props) (:address token-props))]
(let [theme (quo.theme/use-theme)]
[rn/view
{:style (style/container networks? theme)}
[rn/view
@@ -90,13 +89,12 @@
(get-in account-props [:status-account :name])]
[rn/view
{:style (style/dot-divider theme)}]])
(when address
[text/text
{:size (when (not= type :account) :paragraph-2)
:weight (when (= type :account) :semi-bold)
:style {:color (when (not= type :account)
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme))}}
address])]]]
[text/text
{:size (when (not= type :account) :paragraph-2)
:weight (when (= type :account) :semi-bold)
:style {:color (when (not= type :account)
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme))}}
(or (:address account-props) (:address token-props))]]]]
(when networks?
[:<>
[rn/view
@@ -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
@@ -17,7 +17,7 @@
(def transaction-icon
{:send :i/send
:swap :i/transaction
:swap :i/swap
:bridge :i/bridge})
(defn transaction-header
@@ -23,7 +23,7 @@
(def transaction-icon
{:receive :i/receive
:send :i/send
:swap :i/transaction
:swap :i/swap
:bridge :i/bridge
:buy :i/buy
:destroy :i/destroy
@@ -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]
@@ -1,7 +1,6 @@
(ns status-im.contexts.onboarding.syncing.progress.style
(:require
[quo.foundations.colors :as colors]
[react-native.safe-area :as safe-area]))
[quo.foundations.colors :as colors]))
(def absolute-fill
{:position :absolute
@@ -18,7 +17,6 @@
:bottom 0
:left 0
:right 0
:padding-top (safe-area/get-top)
:padding-bottom 20
:background-color (when-not in-onboarding? colors/neutral-80-opa-80-blur)})
@@ -15,14 +15,16 @@
(defn page-title
[pairing-progress?]
[quo/page-top
{:title (i18n/label (if pairing-progress?
:t/sync-devices-title
:t/sync-devices-error-title))
:description :text
:description-text (i18n/label (if pairing-progress?
:t/sync-devices-sub-title
:t/sync-devices-error-sub-title))}])
[quo/text-combinations
{:container-style {:margin-top 56 :margin-horizontal 20}
:title (i18n/label (if pairing-progress?
:t/sync-devices-title
:t/sync-devices-error-title))
:description (i18n/label (if pairing-progress?
:t/sync-devices-sub-title
:t/sync-devices-error-sub-title))
:title-accessibility-label :progress-screen-title
:description-accessibility-label :progress-screen-sub-title}])
(defn- navigate-to-enter-seed-phrase
[]
@@ -40,41 +42,32 @@
(defn try-again-button
[profile-color logged-in?]
(let [number-of-actions (if logged-in? :one-action :two-actions)
try-again-label (i18n/label :t/try-again)
try-again-props {:type (if logged-in? :primary :grey)
:accessibility-label :try-again-later-button
:customization-color profile-color
:container-style {:flex 1}
:size 40
:on-press #(try-again logged-in?)}
recovery-phrase-label (i18n/label :t/recovery-phrase)
recovery-phrase-props {:type :primary
:accessibility-label :try-seed-phrase-button
:customization-color profile-color
:container-style {:flex 1}
:size 40
:on-press navigate-to-enter-seed-phrase}
buttons (if logged-in?
{:button-one-label try-again-label
:button-one-props try-again-props}
{:button-one-label recovery-phrase-label
:button-one-props recovery-phrase-props
:button-two-label try-again-label
:button-two-props try-again-props})]
[quo/bottom-actions
(assoc buttons
:actions number-of-actions
:blur? true)]))
[quo/bottom-actions
{:actions (if logged-in? :one-action :two-actions)
:blur? true
:button-one-label (i18n/label :t/recovery-phrase)
:button-one-props {:type :primary
:accessibility-label :try-seed-phrase-button
:customization-color profile-color
:container-style {:flex 1}
:size 40
:on-press navigate-to-enter-seed-phrase}
(if logged-in? :button-one-label :button-two-label)
(i18n/label :t/try-again)
(if logged-in? :button-one-props :button-two-props)
{:type (if logged-in? :primary :grey)
:accessibility-label :try-again-later-button
:customization-color profile-color
:container-style {:flex 1}
:size 40
:on-press #(try-again logged-in?)}}])
(defn- illustration
[pairing-progress?]
[rn/image
{:resize-mode :contain
:style (style/page-illustration (:width (rn/get-window)))
:source (resources/get-image (if pairing-progress?
:syncing-devices
:syncing-wrong))}])
:source (resources/get-image (if pairing-progress? :syncing-devices :syncing-wrong))}])
(defn view
[in-onboarding?]
@@ -90,7 +83,7 @@
[quo/page-nav {:type :no-title :background :blur}]
[page-title pairing-progress?]
[illustration pairing-progress?]
(when-not pairing-progress?
(when-not (pairing-progress pairing-status)
[try-again-button profile-color logged-in?])]))
(defn view-onboarding
@@ -25,7 +25,7 @@
:title "floating button page"
:description "press right icon to swap button type"
:text-align :left
:right-side [{:icon-name :i/transaction
:right-side [{:icon-name :i/swap
:on-press #(swap! slide? not)}]
:background :blur
:icon-name :i/close
@@ -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
@@ -6,21 +6,24 @@
[react-native.platform :as platform]
[status-im.constants :as constants]
[status-im.contexts.settings.wallet.saved-addresses.sheets.remove-address.view :as remove-address]
[status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.common.utils.networks :as network-utils]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn view
[{:keys [name full-address chain-short-names address customization-color] :as opts}]
(let [open-send-flow (rn/use-callback
[{:keys [name full-address chain-short-names address] :as opts}]
(let [[_ splitted-address] (network-utils/split-network-full-address address)
open-send-flow (rn/use-callback
(fn []
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch [:pop-to-root :shell-stack])
(js/setTimeout #(rf/dispatch [:wallet/select-send-address
{:address full-address
:recipient
{:label name
:customization-color
customization-color
{:label
(utils/get-shortened-address
splitted-address)
:recipient-type :saved-address}
:stack-id :wallet-select-address
:start-flow? true}])
@@ -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/transaction
[]
{: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]}]]
+118 -133
View File
@@ -28,108 +28,95 @@
{:fx [[:dispatch [:wallet/stop-get-suggested-routes]]
[:dispatch [:wallet/clean-suggested-routes]]]}))
(defn- add-not-enough-assets-data
[send-data chosen-route]
(-> send-data
(assoc :route chosen-route
:loading-suggested-routes? false
:suggested-routes {:best []}
:enough-assets? false)
(update :sender-network-values send-utils/reset-loading-network-amounts-to-zero)
(update :receiver-network-values send-utils/reset-loading-network-amounts-to-zero)))
(rf/reg-event-fx
:wallet/suggested-routes-success
(fn [{:keys [db]} [suggested-routes-data enough-assets?]]
(if-not enough-assets?
{:db (update-in db [:wallet :ui :send] add-not-enough-assets-data (:best suggested-routes-data))}
(let [chosen-route (:best suggested-routes-data)
{:keys [token collectible token-display-name
receiver-networks
receiver-network-values
sender-network-values tx-type
disabled-from-chain-ids
from-locked-amounts]} (get-in db [:wallet :ui :send])
token-decimals (if collectible 0 (:decimals token))
native-token? (and token (= token-display-name "ETH"))
routes-available? (pos? (count chosen-route))
token-networks (:networks token)
token-networks-ids (when token-networks
(map #(:chain-id %) token-networks))
from-network-amounts-by-chain (send-utils/network-amounts-by-chain
{:route chosen-route
:token-decimals token-decimals
:native-token? native-token?
:receiver? false})
to-network-amounts-by-chain (send-utils/network-amounts-by-chain
{:route chosen-route
:token-decimals token-decimals
:native-token? native-token?
:receiver? true})
sender-possible-chain-ids (map :chain-id sender-network-values)
sender-network-values (if routes-available?
(send-utils/network-amounts
{:network-values
(if (= tx-type :tx/bridge)
from-network-amounts-by-chain
(send-utils/add-zero-values-to-network-values
from-network-amounts-by-chain
sender-possible-chain-ids))
:disabled-chain-ids disabled-from-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:from-locked-amounts from-locked-amounts
:tx-type tx-type
:receiver? false})
(rf/reg-event-fx :wallet/suggested-routes-success
(fn [{:keys [db]} [suggested-routes-data]]
(let [chosen-route (:best suggested-routes-data)
{:keys [token collectible token-display-name
receiver-networks
receiver-network-values
sender-network-values tx-type
disabled-from-chain-ids
from-locked-amounts]} (get-in db [:wallet :ui :send])
token-decimals (if collectible 0 (:decimals token))
native-token? (and token (= token-display-name "ETH"))
routes-available? (pos? (count chosen-route))
token-networks (:networks token)
token-networks-ids (when token-networks
(map #(:chain-id %) token-networks))
from-network-amounts-by-chain (send-utils/network-amounts-by-chain
{:route chosen-route
:token-decimals token-decimals
:native-token? native-token?
:receiver? false})
to-network-amounts-by-chain (send-utils/network-amounts-by-chain
{:route chosen-route
:token-decimals token-decimals
:native-token? native-token?
:receiver? true})
sender-possible-chain-ids (map :chain-id sender-network-values)
sender-network-values (if routes-available?
(send-utils/network-amounts
{:network-values
(if (= tx-type :tx/bridge)
from-network-amounts-by-chain
(send-utils/add-zero-values-to-network-values
from-network-amounts-by-chain
sender-possible-chain-ids))
:disabled-chain-ids disabled-from-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:from-locked-amounts from-locked-amounts
:tx-type tx-type
:receiver? false})
(send-utils/reset-loading-network-amounts-to-zero
sender-network-values))
receiver-network-values (if routes-available?
(send-utils/network-amounts
{:network-values to-network-amounts-by-chain
:disabled-chain-ids disabled-from-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:tx-type tx-type
:receiver? true})
(cond->
(send-utils/reset-loading-network-amounts-to-zero
sender-network-values))
receiver-network-values (if routes-available?
(send-utils/network-amounts
{:network-values to-network-amounts-by-chain
:disabled-chain-ids disabled-from-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:tx-type tx-type
:receiver? true})
(cond->
(send-utils/reset-loading-network-amounts-to-zero
receiver-network-values)
receiver-network-values)
(not= tx-type :tx/bridge)
send-utils/safe-add-type-edit))
network-links (when routes-available?
(send-utils/network-links chosen-route
sender-network-values
receiver-network-values))]
{:db (update-in db
[:wallet :ui :send]
assoc
:suggested-routes suggested-routes-data
:route chosen-route
:from-values-by-chain from-network-amounts-by-chain
:to-values-by-chain to-network-amounts-by-chain
:sender-network-values sender-network-values
:receiver-network-values receiver-network-values
:network-links network-links
:loading-suggested-routes? false
:enough-assets? true)}))))
(not= tx-type :tx/bridge)
send-utils/safe-add-type-edit))
network-links (when routes-available?
(send-utils/network-links chosen-route
sender-network-values
receiver-network-values))]
{:db (update-in db
[:wallet :ui :send]
assoc
:suggested-routes suggested-routes-data
:route chosen-route
:from-values-by-chain from-network-amounts-by-chain
:to-values-by-chain to-network-amounts-by-chain
:sender-network-values sender-network-values
:receiver-network-values receiver-network-values
:network-links network-links
:loading-suggested-routes? false)})))
(rf/reg-event-fx
:wallet/suggested-routes-error
(rf/reg-event-fx :wallet/suggested-routes-error
(fn [{:keys [db]} [error-message]]
{:db (-> db
(update-in [:wallet :ui :send] dissoc :route)
(update-in [:wallet :ui :send :sender-network-values]
send-utils/reset-loading-network-amounts-to-zero)
(update-in [:wallet :ui :send :receiver-network-values]
send-utils/reset-loading-network-amounts-to-zero)
(assoc-in [:wallet :ui :send :loading-suggested-routes?] false)
(assoc-in [:wallet :ui :send :suggested-routes] {:best []}))
:fx [[:dispatch
[:toasts/upsert
{:id :send-transaction-error
:type :negative
:text error-message}]]]}))
(let [cleaned-sender-network-values (-> (get-in db [:wallet :ui :send :sender-network-values])
(send-utils/reset-loading-network-amounts-to-zero))
cleaned-receiver-network-values (-> (get-in db [:wallet :ui :send :receiver-network-values])
(send-utils/reset-loading-network-amounts-to-zero))]
{:db (-> db
(update-in [:wallet :ui :send] dissoc :route)
(assoc-in [:wallet :ui :send :sender-network-values] cleaned-sender-network-values)
(assoc-in [:wallet :ui :send :receiver-network-values] cleaned-receiver-network-values)
(assoc-in [:wallet :ui :send :loading-suggested-routes?] false)
(assoc-in [:wallet :ui :send :suggested-routes] {:best []}))
:fx [[:dispatch
[:toasts/upsert
{:id :send-transaction-error
:type :negative
:text error-message}]]]})))
(rf/reg-event-fx :wallet/clean-send-address
(fn [{:keys [db]}]
@@ -391,7 +378,8 @@
from-locked-amounts bridge-to-chain-id]
:or {token updated-token}} (get-in db [:wallet :ui :send])
test-networks-enabled? (get-in db [:profile/profile :test-networks-enabled?])
networks (get-in db [:wallet :networks (if test-networks-enabled? :test :prod)])
networks ((if test-networks-enabled? :test :prod)
(get-in db [:wallet :networks]))
network-chain-ids (map :chain-id networks)
token-decimal (when token (:decimals token))
token-id (utils/format-token-id token collectible)
@@ -521,43 +509,40 @@
[routes]
(map data-store/new->old-route-path routes))
(def ^:private best-routes-fix
(comp ->old-route-paths
remove-invalid-bonder-fees-routes
remove-multichain-routes))
(def ^:private candidates-fix
(comp ->old-route-paths remove-invalid-bonder-fees-routes))
(defn- fix-routes
[data]
(-> data
(data-store/rpc->suggested-routes)
(update :best best-routes-fix)
(update :candidates candidates-fix)))
(rf/reg-event-fx
:wallet/handle-suggested-routes
(fn [{:keys [db]} [data]]
(let [{send :send swap? :swap} (-> db :wallet :ui)
skip-processing-routes? (:skip-processing-suggested-routes? send)]
(when (or swap? (not skip-processing-routes?))
(let [{error-code :code
:as error} (:ErrorResponse data)
enough-assets? (not (and (:Best data) (= error-code "WR-002")))
failure? (and error enough-assets? (not swap?))
error-message (if (zero? error-code) "An error occurred" (:details error))]
(when failure?
(log/error "failed to get suggested routes (async)"
{:event :wallet/handle-suggested-routes
:error error-message}))
{:fx [[:dispatch
(cond
(and failure? swap?) [:wallet/swap-proposal-error error-message]
failure? [:wallet/suggested-routes-error error-message]
swap? [:wallet/swap-proposal-success (fix-routes data)]
:else [:wallet/suggested-routes-success (fix-routes data)
enough-assets?])]]})))))
(let [swap? (get-in db [:wallet :ui :swap])
{:keys [code details] :as error-response} (-> data :ErrorResponse)
skip-processing-suggested-routes? (get-in db
[:wallet :ui :send
:skip-processing-suggested-routes?])
process-response? (and (not swap?)
(not skip-processing-suggested-routes?))]
(if (and (not swap?) error-response)
(let [error-message (if (= code "0") "An error occurred" details)]
(log/error "failed to get suggested routes (async)"
{:event :wallet/handle-suggested-routes
:error error-message})
{:fx [(cond
swap?
[:dispatch [:wallet/swap-proposal-error error-message]]
process-response?
[:dispatch [:wallet/suggested-routes-error error-message]])]})
(let [best-routes-fix (comp ->old-route-paths
remove-invalid-bonder-fees-routes
remove-multichain-routes)
candidates-fix (comp ->old-route-paths
remove-invalid-bonder-fees-routes)
routes (-> data
(data-store/rpc->suggested-routes)
(update :best best-routes-fix)
(update :candidates candidates-fix))]
{:fx [(cond
swap?
[:dispatch [:wallet/swap-proposal-success routes]]
process-response?
[:dispatch [:wallet/suggested-routes-success routes]])]})))))
(rf/reg-event-fx :wallet/add-authorized-transaction
(fn [{:keys [db]} [transaction]]
@@ -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)
@@ -41,8 +41,6 @@
:mixedcase-address "0x7bcDfc75c431"
:public-key "0x04371e2d9d66b82f056bc128064"
:removed false}
:wallet/current-viewing-account-color :purple
:wallet/wallet-send-enough-assets? true
:wallet/wallet-send-token {:symbol :eth
:networks [{:source 879
:short-name "eth"
@@ -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}])}])
@@ -140,26 +140,6 @@
:else (rf/dispatch [:wallet/stop-and-clean-suggested-routes])))
(defn- get-fee-formatted
[route]
(when-let [native-currency-symbol (-> route first :from :native-currency-symbol)]
(rf/sub [:wallet/wallet-send-fee-fiat-formatted native-currency-symbol])))
(defn- insufficient-asset-amount?
[{:keys [token-symbol owned-eth-token input-state no-routes-found? limit-exceeded?
sender-network-values enough-assets?]}]
(let [eth-selected? (= token-symbol (string/upper-case constants/mainnet-short-name))
zero-owned-eth? (money/equal-to (:total-balance owned-eth-token) 0)
input-at-max-owned-amount? (money/equal-to
(controlled-input/value-bn input-state)
(controlled-input/upper-limit-bn input-state))
exceeded-input? (if eth-selected?
input-at-max-owned-amount?
zero-owned-eth?)]
(and (or no-routes-found? limit-exceeded?)
(seq sender-network-values)
(or exceeded-input? (not enough-assets?)))))
(defn view
;; crypto-decimals, limit-crypto and initial-crypto-currency? args are needed
;; for component tests only
@@ -174,129 +154,147 @@
enabled-from-chain-ids :enabled-from-chain-ids
from-enabled-networks :from-enabled-networks
:or {initial-crypto-currency? true}}]
(let [view-id (rf/sub [:view-id])
active-screen? (= view-id current-screen-id)
bottom (safe-area/get-bottom)
[crypto-currency?
set-crypto-currency] (rn/use-state initial-crypto-currency?)
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
(let [view-id (rf/sub [:view-id])
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-networks :networks
:as token} (rf/sub [:wallet/wallet-send-token])
send-from-locked-amounts (rf/sub [:wallet/wallet-send-from-locked-amounts])
: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
(str token-symbol)
enabled-from-chain-ids])
token-balance (or default-limit-crypto total-balance)
usd-conversion-rate (utils/token-usd-price token)
currency (rf/sub [:profile/currency])
conversion-rate (-> token
:market-values-per-currency
currency
:price)
token-decimals (-> token
utils/token-usd-price
utils/one-cent-value
utils/calc-max-crypto-decimals)
[input-state set-input-state] (rn/use-state controlled-input/init-state)
clear-input! #(set-input-state controlled-input/delete-all)
currency-symbol (rf/sub [:profile/currency-symbol])
loading-routes? (rf/sub [:wallet/wallet-send-loading-suggested-routes?])
route (rf/sub [:wallet/wallet-send-route])
on-confirm (or default-on-confirm handle-on-confirm)
crypto-decimals (or token-decimals default-crypto-decimals)
max-limit (if crypto-currency?
(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)))
input-value (controlled-input/input-value input-state)
valid-input? (not (or (controlled-input/empty-value? input-state)
(controlled-input/input-error input-state)))
amount-in-crypto (if crypto-currency?
input-value
(number/remove-trailing-zeroes
(.toFixed (/ input-value conversion-rate)
crypto-decimals)))
total-amount-receiver (rf/sub [:wallet/total-amount true])
amount-text (str (number/remove-trailing-zeroes
(.toFixed total-amount-receiver
(min token-decimals 6)))
" "
token-symbol)
show-select-asset-sheet #(rf/dispatch
[:show-bottom-sheet
{:content (fn []
[select-asset-bottom-sheet
clear-input!])}])
sender-network-values (rf/sub [:wallet/wallet-send-sender-network-values])
receiver-network-values (rf/sub [:wallet/wallet-send-receiver-network-values])
tx-type (rf/sub [:wallet/wallet-send-tx-type])
unsupported-token-in-receiver? (and (not= tx-type :tx/bridge)
(->> receiver-network-values
(remove #(= (:type %) :add))
(every? #(= (:type %) :not-available))))
suggested-routes (rf/sub [:wallet/wallet-send-suggested-routes])
routes (when suggested-routes
(or (:best suggested-routes) []))
no-routes-found? (and
(every-network-value-is-zero? sender-network-values)
(some? routes)
(not loading-routes?)
(not unsupported-token-in-receiver?))
receiver-networks (rf/sub [:wallet/wallet-send-receiver-networks])
receiver-preferred-networks (rf/sub [:wallet/wallet-send-receiver-preferred-networks])
receiver-preferred-network? (set receiver-preferred-networks)
sending-to-unpreferred-networks? (some (comp not receiver-preferred-network?)
receiver-networks)
input-error (controlled-input/input-error input-state)
limit-exceeded? (controlled-input/upper-limit-exceeded? input-state)
current-address (rf/sub [:wallet/current-viewing-account-address])
current-color (rf/sub [:wallet/current-viewing-account-color])
enough-assets? (rf/sub [:wallet/wallet-send-enough-assets?])
owned-eth-token (rf/sub [:wallet/token-by-symbol
(string/upper-case constants/mainnet-short-name)
enabled-from-chain-ids])
not-enough-asset? (insufficient-asset-amount?
{:enough-assets? enough-assets?
:token-symbol token-symbol
:owned-eth-token owned-eth-token
:input-state input-state
:no-routes-found? no-routes-found?
:limit-exceeded? limit-exceeded?
:sender-network-values sender-network-values})
should-try-again? (and (not limit-exceeded?)
no-routes-found?
(not not-enough-asset?))
show-no-routes? (and (or no-routes-found? limit-exceeded?)
(not-empty sender-network-values)
(not not-enough-asset?))
confirm-disabled? (or (nil? route)
(empty? route)
(not valid-input?))
fee-formatted (when (or (not confirm-disabled?) not-enough-asset?)
(get-fee-formatted route))
request-fetch-routes (fn [bounce-duration-ms]
(fetch-routes
{:amount amount-in-crypto
:valid-input? valid-input?
:bounce-duration-ms bounce-duration-ms
:token token
:reset-amounts-to-zero? (and limit-exceeded?
(some? routes))}))
swap-between-fiat-and-crypto (fn []
(if crypto-currency?
(set-input-state
#(controlled-input/->fiat % conversion-rate))
(set-input-state
#(controlled-input/->crypto % conversion-rate)))
(set-crypto-currency (not crypto-currency?)))]
:as token-by-symbol} (rf/sub [:wallet/token-by-symbol
(str token-symbol)
enabled-from-chain-ids])
token-balance (or default-limit-crypto total-balance)
usd-conversion-rate (utils/token-usd-price token)
currency (rf/sub [:profile/currency])
conversion-rate (-> token
:market-values-per-currency
currency
:price)
token-decimals (-> token
utils/token-usd-price
utils/one-cent-value
utils/calc-max-crypto-decimals)
[input-state set-input-state] (rn/use-state controlled-input/init-state)
clear-input! #(set-input-state controlled-input/delete-all)
currency-symbol (rf/sub [:profile/currency-symbol])
loading-routes? (rf/sub
[:wallet/wallet-send-loading-suggested-routes?])
route (rf/sub [:wallet/wallet-send-route])
on-confirm (or default-on-confirm handle-on-confirm)
crypto-decimals (or token-decimals default-crypto-decimals)
max-limit (if crypto-currency?
(utils/cut-crypto-decimals-to-fit-usd-cents
token-balance
usd-conversion-rate)
(-> (money/crypto->fiat
token-balance
conversion-rate)
utils/cut-fiat-balance-to-two-decimals))
input-value (controlled-input/input-value input-state)
valid-input? (not (or (controlled-input/empty-value? input-state)
(controlled-input/input-error input-state)))
confirm-disabled? (or (nil? route)
(empty? route)
(not valid-input?))
amount-in-crypto (if crypto-currency?
input-value
(number/remove-trailing-zeroes
(.toFixed (/ input-value conversion-rate)
crypto-decimals)))
total-amount-receiver (rf/sub [:wallet/total-amount true])
amount-text (str (number/remove-trailing-zeroes
(.toFixed total-amount-receiver
(min token-decimals 6)))
" "
token-symbol)
first-route (first route)
native-currency-symbol (when-not confirm-disabled?
(get-in first-route
[:from :native-currency-symbol]))
fee-formatted (when native-currency-symbol
(rf/sub [:wallet/wallet-send-fee-fiat-formatted
native-currency-symbol]))
show-select-asset-sheet #(rf/dispatch
[:show-bottom-sheet
{:content (fn []
[select-asset-bottom-sheet
clear-input!])}])
sender-network-values (rf/sub
[:wallet/wallet-send-sender-network-values])
receiver-network-values (rf/sub
[:wallet/wallet-send-receiver-network-values])
tx-type (rf/sub [:wallet/wallet-send-tx-type])
token-not-supported-in-receiver-networks? (and (not= tx-type :tx/bridge)
(->> receiver-network-values
(remove #(= (:type %) :add))
(every? #(= (:type %) :not-available))))
suggested-routes (rf/sub [:wallet/wallet-send-suggested-routes])
routes (when suggested-routes
(or (:best suggested-routes) []))
no-routes-found? (and
(every-network-value-is-zero?
sender-network-values)
(not (nil? routes))
(not loading-routes?)
(not token-not-supported-in-receiver-networks?))
receiver-networks (rf/sub [:wallet/wallet-send-receiver-networks])
receiver-preferred-networks (rf/sub
[:wallet/wallet-send-receiver-preferred-networks])
receiver-preferred-networks-set (set receiver-preferred-networks)
sending-to-unpreferred-networks? (not (every? (fn [receiver-selected-network]
(contains?
receiver-preferred-networks-set
receiver-selected-network))
receiver-networks))
input-error (controlled-input/input-error input-state)
limit-exceeded? (controlled-input/upper-limit-exceeded? input-state)
should-try-again? (and (not limit-exceeded?) no-routes-found?)
current-address (rf/sub [:wallet/current-viewing-account-address])
owned-eth-token (rf/sub [:wallet/token-by-symbol
(string/upper-case
constants/mainnet-short-name)
enabled-from-chain-ids])
not-enough-asset? (and
(or no-routes-found? limit-exceeded?)
(not-empty sender-network-values)
(if (= token-symbol
(string/upper-case
constants/mainnet-short-name))
(money/equal-to
(controlled-input/value-bn input-state)
(controlled-input/upper-limit-bn input-state))
(money/equal-to (:total-balance
owned-eth-token)
0)))
show-no-routes? (and
(or no-routes-found? limit-exceeded?)
(not-empty sender-network-values)
(not not-enough-asset?))
request-fetch-routes (fn [bounce-duration-ms]
(fetch-routes
{:amount amount-in-crypto
:valid-input? valid-input?
:bounce-duration-ms bounce-duration-ms
:token token
:reset-amounts-to-zero? (and limit-exceeded?
(some? routes))}))
swap-between-fiat-and-crypto (fn []
(if crypto-currency?
(set-input-state
#(controlled-input/->fiat % conversion-rate))
(set-input-state
#(controlled-input/->crypto % conversion-rate)))
(set-crypto-currency (not crypto-currency?)))]
(rn/use-effect
(fn []
(when active-screen?
@@ -371,44 +369,38 @@
{:token token-by-symbol
:send-amount-in-crypto amount-in-crypto
:valid-input? valid-input?
:token-not-supported-in-receiver-networks? unsupported-token-in-receiver?
:token-not-supported-in-receiver-networks? token-not-supported-in-receiver-networks?
:current-screen-id current-screen-id
:request-fetch-routes request-fetch-routes}]
(when (and (not loading-routes?)
sender-network-values
unsupported-token-in-receiver?)
token-not-supported-in-receiver-networks?)
[token-not-available token-symbol receiver-networks token-networks])
(when not-enough-asset?
[not-enough-asset])
(when (or (and (not no-routes-found?) (or loading-routes? route))
not-enough-asset?)
(when (and (not no-routes-found?) (or loading-routes? route))
[estimated-fees
{:loading-routes? loading-routes?
:fees fee-formatted
:amount amount-text}])
(when show-no-routes?
[no-routes-found])
(cond
show-no-routes? [no-routes-found]
not-enough-asset? [not-enough-asset])
[quo/bottom-actions
{:actions :one-action
: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)
{:disabled? (or not-enough-asset?
loading-routes?
(and (not should-try-again?)
confirm-disabled?))
:on-press (cond
should-try-again?
#(rf/dispatch [:wallet/start-get-suggested-routes
{:amount amount-in-crypto
:updated-token token-by-symbol}])
sending-to-unpreferred-networks?
#(show-unpreferred-networks-alert on-confirm)
:else
#(on-confirm amount-in-crypto))
:customization-color current-color}
: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
should-try-again?
#(rf/dispatch [:wallet/start-get-suggested-routes
{:amount amount-in-crypto
:updated-token token-by-symbol}])
sending-to-unpreferred-networks?
#(show-unpreferred-networks-alert on-confirm)
:else
#(on-confirm amount-in-crypto))}
(when should-try-again?
{:type :grey}))}]
[quo/numbered-keyboard
@@ -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)})

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