From b4866ff72885cab4d0e7f1f5ca162ad4804947c4 Mon Sep 17 00:00:00 2001 From: Ajay Sivan Date: Fri, 5 Jul 2024 15:14:38 +0530 Subject: [PATCH] Swap: Edit slippage drawer (#20554) --- .../images/icons2/20x20/percentage@2x.png | Bin 0 -> 781 bytes .../images/icons2/20x20/percentage@3x.png | Bin 0 -> 1284 bytes src/status_im/constants.cljs | 5 + .../sheets/slippage_settings/style.cljs | 13 ++ .../wallet/sheets/slippage_settings/view.cljs | 118 ++++++++++++++++++ .../contexts/wallet/swap/events.cljs | 26 ++-- .../wallet/swap/swap_proposal/style.cljs | 4 + .../wallet/swap/swap_proposal/view.cljs | 15 +++ src/status_im/navigation/screens.cljs | 5 + src/status_im/subs/wallet/swap.cljs | 4 + src/utils/number.cljs | 10 ++ src/utils/number_test.cljs | 14 +++ translations/en.json | 8 ++ 13 files changed, 213 insertions(+), 9 deletions(-) create mode 100644 resources/images/icons2/20x20/percentage@2x.png create mode 100644 resources/images/icons2/20x20/percentage@3x.png create mode 100644 src/status_im/contexts/wallet/sheets/slippage_settings/style.cljs create mode 100644 src/status_im/contexts/wallet/sheets/slippage_settings/view.cljs create mode 100644 src/status_im/contexts/wallet/swap/swap_proposal/style.cljs create mode 100644 src/status_im/contexts/wallet/swap/swap_proposal/view.cljs diff --git a/resources/images/icons2/20x20/percentage@2x.png b/resources/images/icons2/20x20/percentage@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..020f6d65661fac1bd1b7aa3e89c57a20c8c511a5 GIT binary patch literal 781 zcmV+o1M>WdP)RwGhqDr)7_m1Vdey;E)}&tz8#=&w9V9y;VPzNQemTqN&xkKH)oe5R(? zCsCheM0_3HnD!z8)R#dS(TZgvM z$76%Nxi5n^?5X(f(Uz}H-EPs6ogpefZn3ZpHncaUXO<7GgI9=sVDpa2sVYP3S;gsK zA)p@$D5BkB61usv@~m`-tqdF60pk<5W55m0oPLEGOVmT+e9G`rU`+v@~jeqX+ofkJWICEi7X*d zRGw8r&{QV|=8kuZ>0ci|MdjHxOP{{&D|Q&Uq@Q&Uq@4Ga$8Jm6mtwSZ_( zHSf9w;Judc`XdpBcp#J(c!O`91M)b36ubB=Zcgw3C?)V?zIiQyor1#$pT+P1OVH#& zz%aR{1)XUPJh1jBanpk(4Ol|Lo|3SCCwm#Gv3$?KLTdbG+!8!${=&YEJ`PZjwdRvo z^!2CK)x8zN7bt<%4#z7${x8`pPu8*46CxC|o(=vxsd{v-4HE0{xAieu+%0Hl-owIm zGMs|+ z!!t-txAmP(h?xk}xmR1!sc(Kv#`XV-r^}zBII>TP6!CijxAs45j7`jHg0O;ESwqu&TtM2u=kq zLRzLpiNUU`Sa}f+BCX}9GBGH^N(HVuF(|^yR62HJ<7>UobW^Lq#|nt6n#B`?pZL=q zviv+AC&ef6&Y8-uWQS8KoA(NO%9u4UIik?Bbw8LjnaO)a)DE#DWUwb{@@f| z9@36q7KqwZ0Mj;o`Z821@LiepN;gzynKiv2m@l_$WJJ|->VQbzA9##egLpMKL^ z!vU705u4478^gvi^m_}V-{S>lK%fpdZ5 zXewD4pwos<+E|%fln@Jv^xgjz7C04Hsj^vS9i#EgT^2MwvnqvCfy*TZtU%=?zldK1 zX9C+K2IQvxpSNU?RtTICpEt2!)}{x43k#eGYved&Ra7-=YHDg~YHDg~Z21ly?+{JsS_RGk0000 (count (second (string/split slippage "."))) + constants/max-slippage-decimal-places) + {:message (i18n/label :t/max-2-decimals) + :type :error} + (> slippage-value constants/max-recommended-slippage) + {:message (i18n/label :t/slippage-may-be-higher-than-necessary) + :type :warning}))) + +(defn- on-cancel + [] + (rf/dispatch [:hide-bottom-sheet])) + +(defn- update-string-on-keypress + [k s] + (if (= :i/backspace k) + (subs s 0 (dec (count s))) + (str s k))) + +(defn view + [] + (let [current-slippage (rf/sub [:wallet/swap-max-slippage]) + account-color (rf/sub [:wallet/current-viewing-account-color]) + [max-slippage set-max-slippage] (rn/use-state (str current-slippage)) + [error set-error] (rn/use-state nil) + [custom? set-custom?] (rn/use-state (not-any? #{current-slippage} + constants/slippages)) + handle-slippage-change (rn/use-callback + (fn [value] + (let [new-slippage (update-string-on-keypress value + max-slippage)] + (set-max-slippage new-slippage) + (set-error (validate-slippage new-slippage)))) + [max-slippage set-max-slippage set-error]) + on-select-slippage (rn/use-callback (fn [slippage] + (set-max-slippage (str slippage)) + (set-custom? (not slippage))) + [set-max-slippage set-custom?]) + save-disabled? (rn/use-memo (fn [] + (or (= max-slippage (str current-slippage)) + (= (:type error) :error) + (and custom? + (empty? max-slippage)))) + [max-slippage current-slippage error custom?]) + on-save (rn/use-callback (fn [] + (rf/dispatch [:wallet.swap/set-max-slippage + max-slippage]) + (rf/dispatch [:hide-bottom-sheet])) + [max-slippage])] + [:<> + [quo/drawer-top + {:title (i18n/label :t/slippage-settings) + :description (i18n/label :t/slippage-settings-description)}] + [rn/view {:style style/slippages} + (map (fn [slippage] + ^{:key slippage} + [quo/drawer-action + (cond-> {:title (str slippage "%") + :on-press #(on-select-slippage slippage)} + (= (str slippage) max-slippage) (assoc :state :selected))]) + constants/slippages) + [quo/drawer-action + (cond-> {:title (i18n/label :t/custom) + :action :input + :on-press #(on-select-slippage nil) + :input-props {:auto-focus true + :customization-color account-color + :placeholder (i18n/label :t/type-slippage) + :right-icon {:icon-name :i/percentage + :on-press identity + :style-fn style/percentage-icon} + :value max-slippage}} + custom? (assoc :state :selected))]] + (when (and custom? error) + [quo/info-message + {:status (:type error) + :size :default + :container-style style/info-message + :icon :i/alert} + (:message error)]) + [quo/bottom-actions + {:actions :two-actions + :button-one-label (i18n/label :t/save-changes) + :button-one-props {:disabled? save-disabled? + :customization-color account-color + :on-press on-save} + :button-two-label (i18n/label :t/cancel) + :button-two-props {:on-press on-cancel + :customization-color account-color + :type :grey} + :description :top + :context-tag-props {:size 24 + :type :token + :token "USDT" + :amount "99.97"} ;; will be replaced with real data later + :description-top-text (i18n/label :t/receive-at-least)}] + (when custom? + [quo/numbered-keyboard + {:left-action :dot + :delete-key? true + :on-press handle-slippage-change + :on-delete handle-slippage-change}])])) diff --git a/src/status_im/contexts/wallet/swap/events.cljs b/src/status_im/contexts/wallet/swap/events.cljs index c495b90c81..ff16116a4f 100644 --- a/src/status_im/contexts/wallet/swap/events.cljs +++ b/src/status_im/contexts/wallet/swap/events.cljs @@ -1,6 +1,8 @@ (ns status-im.contexts.wallet.swap.events (:require [re-frame.core :as rf] - [status-im.contexts.wallet.sheets.network-selection.view :as network-selection])) + [status-im.constants :as constants] + [status-im.contexts.wallet.sheets.network-selection.view :as network-selection] + [utils.number])) (rf/reg-event-fx :wallet.swap/start (fn [{:keys [_db]}] @@ -11,13 +13,10 @@ {:db (-> db (assoc-in [:wallet :ui :swap :asset-to-pay] token) (assoc-in [:wallet :ui :swap :network] network)) - :fx [(if network - [:dispatch - [:toasts/upsert - {:id :swap-error - :type :negative - :text "Not implemented yet"}]] - [:dispatch + :fx (if network + [[:dispatch [:navigate-to :screen/wallet.swap-propasal]] + [:dispatch [:wallet.swap/set-default-slippage]]] + [[:dispatch [:show-bottom-sheet {:content (fn [] [network-selection/view @@ -29,8 +28,17 @@ {:token token :network network :stack-id - :screen/wallet.swap-select-asset-to-pay}]))}])}]])]})) + :screen/wallet.swap-select-asset-to-pay}]))}])}]]])})) (rf/reg-event-fx :wallet.swap/clean-asset-to-pay (fn [{:keys [db]}] {:db (update-in db [:wallet :ui :swap] dissoc :asset-to-pay)})) + +(rf/reg-event-fx :wallet.swap/set-default-slippage + (fn [{:keys [db]}] + {:db + (assoc-in db [:wallet :ui :swap :max-slippage] constants/default-slippage)})) + +(rf/reg-event-fx :wallet.swap/set-max-slippage + (fn [{:keys [db]} [max-slippage]] + {:db (assoc-in db [:wallet :ui :swap :max-slippage] (utils.number/parse-float max-slippage))})) diff --git a/src/status_im/contexts/wallet/swap/swap_proposal/style.cljs b/src/status_im/contexts/wallet/swap/swap_proposal/style.cljs new file mode 100644 index 0000000000..47e48e5cd8 --- /dev/null +++ b/src/status_im/contexts/wallet/swap/swap_proposal/style.cljs @@ -0,0 +1,4 @@ +(ns status-im.contexts.wallet.swap.swap-proposal.style) + +(def container + {:flex 1}) diff --git a/src/status_im/contexts/wallet/swap/swap_proposal/view.cljs b/src/status_im/contexts/wallet/swap/swap_proposal/view.cljs new file mode 100644 index 0000000000..e5ffc0940e --- /dev/null +++ b/src/status_im/contexts/wallet/swap/swap_proposal/view.cljs @@ -0,0 +1,15 @@ +(ns status-im.contexts.wallet.swap.swap-proposal.view + (:require [quo.core :as quo] + [react-native.core :as rn] + [status-im.contexts.wallet.sheets.slippage-settings.view :as slippage-settings] + [status-im.contexts.wallet.swap.swap-proposal.style :as style] + [utils.re-frame :as rf])) + +(defn view + [] + (let [max-slippage (rf/sub [:wallet/swap-max-slippage])] + [rn/view {:style style/container} + [quo/button + {:on-press #(rf/dispatch [:show-bottom-sheet + {:content slippage-settings/view}])} + (str "Edit Slippage: " max-slippage "%")]])) diff --git a/src/status_im/navigation/screens.cljs b/src/status_im/navigation/screens.cljs index c31cd7cbee..f6c0dcadf1 100644 --- a/src/status_im/navigation/screens.cljs +++ b/src/status_im/navigation/screens.cljs @@ -116,6 +116,7 @@ [status-im.contexts.wallet.send.transaction-confirmation.view :as wallet-transaction-confirmation] [status-im.contexts.wallet.send.transaction-progress.view :as wallet-transaction-progress] [status-im.contexts.wallet.swap.select-asset-to-pay.view :as wallet-swap-select-asset-to-pay] + [status-im.contexts.wallet.swap.swap-proposal.view :as wallet-swap-propasal] [status-im.contexts.wallet.wallet-connect.modals.send-transaction.view :as wallet-connect-send-transaction] [status-im.contexts.wallet.wallet-connect.modals.sign-message.view :as wallet-connect-sign-message] @@ -519,6 +520,10 @@ :insets {:top? true}} :component wallet-swap-select-asset-to-pay/view} + {:name :screen/wallet.swap-propasal + :options {:insets {:top? true}} + :component wallet-swap-propasal/view} + {:name :scan-profile-qr-code :options (merge options/dark-screen diff --git a/src/status_im/subs/wallet/swap.cljs b/src/status_im/subs/wallet/swap.cljs index 93523c33d1..8e42bb96a1 100644 --- a/src/status_im/subs/wallet/swap.cljs +++ b/src/status_im/subs/wallet/swap.cljs @@ -52,3 +52,7 @@ fiat-value)] {:crypto (str crypto-formatted " " token-symbol) :fiat fiat-formatted}))) +(rf/reg-sub + :wallet/swap-max-slippage + :<- [:wallet/swap] + :-> :max-slippage) diff --git a/src/utils/number.cljs b/src/utils/number.cljs index 392498ca8e..6e5d758530 100644 --- a/src/utils/number.cljs +++ b/src/utils/number.cljs @@ -25,6 +25,16 @@ maybe-int default)))) +(defn parse-float + "Parses `n` as a float. Defaults to zero or `default` instead of NaN." + ([n] + (parse-float n 0)) + ([n default] + (let [maybe-float (js/parseFloat n 10)] + (if (js/Number.isNaN maybe-float) + default + maybe-float)))) + (defn value-in-range "Returns `num` if is in the range [`lower-bound` `upper-bound`] if `num` exceeds a given bound, then returns the bound exceeded." diff --git a/src/utils/number_test.cljs b/src/utils/number_test.cljs index 5cb964ee51..ebfd9a2abe 100644 --- a/src/utils/number_test.cljs +++ b/src/utils/number_test.cljs @@ -16,3 +16,17 @@ (is (= 6 (utils.number/parse-int "6" 0))) (is (= 6 (utils.number/parse-int "6.99" 0))) (is (= -6 (utils.number/parse-int "-6" 0))))) + +(deftest parse-float-test + (testing "defaults to zero" + (is (= 0 (utils.number/parse-float nil)))) + + (testing "accepts any other default value" + (is (= 3 (utils.number/parse-float "" 3))) + (is (= :invalid-float (utils.number/parse-float "" :invalid-float)))) + + (testing "valid numbers" + (is (= -6 (utils.number/parse-float "-6a"))) + (is (= 6 (utils.number/parse-float "6"))) + (is (= 6.99 (utils.number/parse-float "6.99" 0))) + (is (= -6.9 (utils.number/parse-float "-6.9" 0))))) diff --git a/translations/en.json b/translations/en.json index 186b35ba9e..cc12be4486 100644 --- a/translations/en.json +++ b/translations/en.json @@ -1296,6 +1296,7 @@ "reveal-qr-code": "Reveal QR code", "revoke-access": "Revoke access", "save": "Save", + "save-changes": "Save changes", "save-address": "Save address", "save-bio": "Save bio", "save-colour": "Save colour", @@ -1998,6 +1999,13 @@ "status-inactive-subtitle": "Hides your online status", "two-minutes": "two minutes", "swap": "Swap", + "slippage-settings": "Slippage settings", + "slippage-settings-description": "Your transaction will revert if the price changes more than the slippage percentage", + "slippage-may-be-higher-than-necessary": "Slippage may be higher than necessary", + "slippage-should-be-more-than-0": "Slippage should be more than 0", + "max-2-decimals": "Max. 2 decimals", + "type-slippage": "Type slippage", + "receive-at-least": "Receive at least", "select-token-to-swap": "Select token to Swap", "select-token-to-receive": "Select token to receive", "slide-to-request-to-join": "Slide to request to join",