Flow for sending multiple collectibles (#19386)

This commit is contained in:
Volodymyr Kozieiev 2024-03-28 15:14:17 +00:00 committed by GitHub
parent 3ee004afab
commit 4aee5af791
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 180 additions and 102 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 B

View File

@ -15,9 +15,9 @@
[{:keys [disabled? blur? on-press on-long-press type]} label]
(let [theme (quo.theme/use-theme-value)
[pressed? set-pressed?] (rn/use-state false)
on-press (rn/use-callback #(when on-press (on-press label)) [label])
on-press (rn/use-callback #(when on-press (on-press label)) [on-press label])
on-long-press (rn/use-callback #(when (fn? on-long-press) (on-long-press label))
[label])
[on-long-press label])
on-press-in (rn/use-callback #(set-pressed? true))
on-press-out (rn/use-callback #(set-pressed? false))
label-color (style/get-label-color disabled? theme blur?)

View File

@ -4,8 +4,7 @@
(def container
(merge (shadows/get 2)
{:flex 1
:align-items :center
{:align-items :center
:justify-content :center
:border-radius 16}))

View File

@ -55,7 +55,7 @@
{:label (i18n/label :t/cant-fetch-info)
:counter counter
:theme theme}]
[rn/view {:style {:flex 1}}
[rn/view
[rn/image
{:style (style/image square? (:aspect-ratio image-size))
:source image-src}]
@ -66,7 +66,7 @@
[:catn
[:props
[:map {:closed true}
[:image-src {:optional true} string?]
[:image-src {:optional true} [:maybe string?]]
[:container-style {:optional true} [:maybe :map]]
[:square? {:optional true} [:maybe boolean?]]
[:counter {:optional true} [:maybe string?]]

View File

@ -7,11 +7,12 @@
[react-native.core :as rn]))
(defn- view-internal
[{:keys [title networks status theme blur?] :or {status :default}}]
[{:keys [title networks status theme blur? container-style] :or {status :default}}]
[rn/view
{:style (style/container {:status status
:theme theme
:blur? blur?})}
{:style (merge (style/container {:status status
:theme theme
:blur? blur?})
container-style)}
[preview-list/view
{:type :network
:number (count networks)

View File

@ -12,32 +12,24 @@
(h/describe "Amount input component"
(h/test "Renders with default value"
(let [text-expected 0]
(render [amount-input/view {:init-value text-expected}])
(h/is-truthy (h/query-by-label-text :amount-input))
(h/is-equal (oops/oget (h/get-by-label-text :amount-input) "props" "value")
(str text-expected))))
(render [amount-input/view {:value text-expected}])
(h/is-truthy (h/query-by-label-text :amount-input))))
(h/test "When the value = minimum dec button is disabled"
(render [amount-input/view
{:init-value 0
:min-value 0}])
{:value 0
:min-value 0}])
(h/is-truthy
(oops/oget (h/get-by-label-text :amount-input-dec-button) "props" "accessibilityState" "disabled")))
(h/test "When the value = maximum inc button is disabled"
(render [amount-input/view
{:init-value 100
:max-value 100}])
{:value 100
:max-value 100}])
(h/is-truthy
(oops/oget (h/get-by-label-text :amount-input-inc-button) "props" "accessibilityState" "disabled")))
(h/test "Renders the error state"
(render [amount-input/view {:status :error}])
(render [amount-input/view {:status :error :value 10}])
(h/is-equal (colors/resolve-color :danger :light)
(oops/oget (h/get-by-label-text :amount-input) "props" "style" "color")))
(h/test "on-change-text function is fired"
(let [on-change-text (h/mock-fn)]
(render [amount-input/view {:on-change-text on-change-text}])
(h/fire-event :change-text (h/get-by-label-text :amount-input) "100")
(h/was-called on-change-text))))
(oops/oget (h/get-by-label-text :amount-input) "props" "style" "color"))))

View File

@ -1,9 +1,5 @@
(ns quo.components.wallet.amount-input.schema)
(def return-key-types
[:enum :done :go :next :search :send :none :previous :default
:emergency-call :google :join :route :yahoo])
(def ?schema
[:=>
[:catn
@ -11,11 +7,10 @@
[:map {:closed true}
[:status {:optional true} [:maybe [:enum :default :error]]]
[:theme :schema.common/theme]
[:on-change-text {:optional true} [:maybe fn?]]
[:on-inc-press {:optional true} [:maybe fn?]]
[:on-dec-press {:optional true} [:maybe fn?]]
[:container-style {:optional true} [:maybe :map]]
[:auto-focus? {:optional true} [:maybe :boolean]]
[:min-value {:optional true} [:maybe :int]]
[:max-value {:optional true} [:maybe :int]]
[:return-key-type {:optional true} [:maybe return-key-types]]
[:init-value {:optional true} [:maybe :int]]]]]
[:value [:maybe :int]]]]]
:any])

View File

@ -12,6 +12,7 @@
(defn input-text
[theme type]
{:padding 0
:align :center
:color (if (= type :error)
(colors/resolve-color :danger theme)
(colors/theme-colors colors/neutral-100 colors/white theme))})

View File

@ -6,7 +6,6 @@
[quo.components.wallet.amount-input.style :as style]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[reagent.core :as reagent]
[schema.core :as schema]))
(defn- amount-button
@ -21,60 +20,35 @@
:on-press on-press}
icon])
(defn- process-amount
[input-value min-value max-value]
(let [parsed-input-value (parse-double input-value)]
(cond
(nil? parsed-input-value) min-value
(>= input-value max-value) max-value
(<= input-value min-value) min-value
:else parsed-input-value)))
(defn- view-internal
[{:keys [init-value]}]
(let [init-value (or init-value 0)
value (reagent/atom init-value)
on-dec-press #(swap! value dec)
on-inc-press #(swap! value inc)]
(fn [{:keys [theme status min-value max-value auto-focus?
return-key-type container-style on-change-text]}]
(let [min-value (or min-value 0)
max-value (or max-value 999999999)]
[rn/view
{:style (merge style/container container-style)}
[amount-button
{:theme theme
:accessibility-label :amount-input-dec-button
:icon :i/remove
:on-press on-dec-press
:disabled? (>= min-value @value)}]
[rn/view {:style style/input-container}
[rn/text-input
{:style
(text/text-style
{:size :heading-1
:weight :semi-bold
:align :center
:style (style/input-text theme (or status :default))})
:accessibility-label :amount-input
:editable true
:auto-focus (or auto-focus? false)
:value (str @value)
:keyboard-appearance (quo.theme/theme-value :light :dark theme)
:return-key-type (or return-key-type :done)
:input-mode :numeric
:on-change-text (fn [input-value]
(let [processed-amount (process-amount input-value min-value max-value)]
(reset! value processed-amount)
(when on-change-text
(on-change-text processed-amount))
(reagent/flush)))}]] ; Fixes the input flickering issue when typing.
[amount-button
{:theme theme
:icon :i/add
:accessibility-label :amount-input-inc-button
:on-press on-inc-press
:disabled? (>= @value max-value)}]]))))
[{:keys [on-inc-press on-dec-press theme status value min-value max-value
container-style]
:or {value 0
min-value 0
max-value 999999999}}]
[rn/view
{:style (merge style/container container-style)}
[amount-button
{:theme theme
:accessibility-label :amount-input-dec-button
:icon :i/remove
:on-press on-dec-press
:disabled? (>= min-value value)}]
[rn/view {:style style/input-container}
[text/text
{:number-of-lines 1
:accessibility-label :amount-input
:weight :semi-bold
:size :heading-1
:align-self :center
:style (style/input-text theme (or status :default))}
value]]
[amount-button
{:theme theme
:icon :i/add
:accessibility-label :amount-input-inc-button
:on-press on-inc-press
:disabled? (>= value max-value)}]])
(def view
(quo.theme/with-theme

View File

@ -9,7 +9,7 @@
:type :number}
{:key :min-value
:type :number}
{:key :init-value
{:key :value
:type :number}
{:type :select
:key :status
@ -18,12 +18,18 @@
(defn view
[]
(let [state (reagent/atom {:max-value 10000
:min-value 0
:init-value 1
:status :default})]
(let [state (reagent/atom {:max-value 10000
:min-value 0
:value 1
:status :default})
on-inc-press (fn [] (swap! state #(assoc % :value (inc (:value %)))))
on-dec-press (fn [] (swap! state #(assoc % :value (dec (:value %)))))]
(fn []
[preview/preview-container
{:state state
:descriptor descriptor}
[quo/amount-input @state]])))
[quo/amount-input
(merge
@state
{:on-dec-press on-dec-press
:on-inc-press on-inc-press})]])))

View File

@ -0,0 +1,9 @@
(ns status-im.contexts.wallet.collectible.utils)
(defn collectible-balance
[collectible]
(-> collectible
:ownership
first
:balance
js/parseInt))

View File

@ -101,16 +101,24 @@
:amount
(when (= transaction-type :collecible) :tx-type))})))
(rf/reg-event-fx :wallet/send-select-collectible
(fn [{:keys [db]} [{:keys [collectible stack-id]}]]
(rf/reg-event-fx :wallet/send-collectibles-amount
(fn [{:keys [db]} [{:keys [collectible stack-id amount]}]]
{:db (-> db
(update-in [:wallet :ui :send] dissoc :token)
(assoc-in [:wallet :ui :send :collectible] collectible)
(assoc-in [:wallet :ui :send :tx-type] :collectible)
(assoc-in [:wallet :ui :send :amount] 1))
:fx [[:dispatch [:wallet/get-suggested-routes {:amount 1}]]
(assoc-in [:wallet :ui :send :amount] amount))
:fx [[:dispatch [:wallet/get-suggested-routes {:amount amount}]]
[:navigate-to-within-stack [:screen/wallet.transaction-confirmation stack-id]]]}))
(rf/reg-event-fx :wallet/select-collectibles-amount
(fn [{:keys [db]} [{:keys [collectible stack-id]}]]
{:db (-> db
(update-in [:wallet :ui :send] dissoc :token)
(assoc-in [:wallet :ui :send :collectible] collectible)
(assoc-in [:wallet :ui :send :tx-type] :collectible))
:fx [[:navigate-to-within-stack [:screen/wallet.select-collectible-amount stack-id]]]}))
(rf/reg-event-fx :wallet/send-select-amount
(fn [{:keys [db]} [{:keys [amount stack-id start-flow?]}]]
{:db (assoc-in db [:wallet :ui :send :amount] amount)

View File

@ -5,6 +5,7 @@
[quo.theme :as quo.theme]
[react-native.core :as rn]
[reagent.core :as reagent]
[status-im.contexts.wallet.collectible.utils :as utils]
[status-im.contexts.wallet.common.account-switcher.view :as account-switcher]
[status-im.contexts.wallet.common.asset-list.view :as asset-list]
[status-im.contexts.wallet.common.collectibles-tab.view :as collectibles-tab]
@ -34,9 +35,15 @@
{:collectibles collectibles
:filtered? search-performed?
:on-end-reached #(rf/dispatch [:wallet/request-collectibles-for-current-viewing-account])
:on-collectible-press #(rf/dispatch [:wallet/send-select-collectible
{:collectible %
:stack-id :screen/wallet.select-asset}])}]))
:on-collectible-press #(let [collectibles-count (utils/collectible-balance %)]
(if (> collectibles-count 1)
(rf/dispatch [:wallet/select-collectibles-amount
{:collectible %
:stack-id :screen/wallet.select-asset}])
(rf/dispatch [:wallet/send-collectibles-amount
{:collectible %
:amount 1
:stack-id :screen/wallet.select-asset}])))}]))
(defn- tab-view
[search-text selected-tab on-change-text]
(let [unfiltered-collectibles (rf/sub [:wallet/current-viewing-account-collectibles])

View File

@ -0,0 +1,13 @@
(ns status-im.contexts.wallet.send.select-collectible-amount.style)
(def collectible-container
{:margin-vertical 12
:margin-horizontal 56})
(def network-tags-container
{:align-self :center})
(def amount-input-container
{:padding-left 20
:padding-right 20
:margin-vertical 12})

View File

@ -0,0 +1,67 @@
(ns status-im.contexts.wallet.send.select-collectible-amount.view
(:require
[quo.core :as quo]
[react-native.core :as rn]
[status-im.contexts.wallet.collectible.utils :as utils]
[status-im.contexts.wallet.common.account-switcher.view :as account-switcher]
[status-im.contexts.wallet.send.select-collectible-amount.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn view
[]
(let [on-close (rn/use-callback #(rf/dispatch [:navigate-back]))
[value set-value] (rn/use-state 1)
inc-value (rn/use-callback (fn [] (set-value (inc value)))
[value])
dec-value (rn/use-callback (fn [] (set-value (dec value)))
[value])
add-digit (rn/use-callback (fn [digit]
(set-value (+ (js/parseInt digit)
(* value 10))))
[value])
delete-digit (rn/use-callback (fn []
(set-value (Math/floor (/ value 10))))
[value])
send-transaction-data (rf/sub [:wallet/wallet-send])
collectible (:collectible send-transaction-data)
balance (utils/collectible-balance collectible)
preview-uri (get-in collectible [:preview-url :uri])
incorrect-value? (or (< value 1) (> value balance))]
[rn/view
[account-switcher/view
{:icon-name :i/arrow-left
:on-press on-close
:switcher-type :select-account}]
[quo/expanded-collectible
{:image-src preview-uri
:square? true
:container-style style/collectible-container}]
[quo/network-tags
{:title (i18n/label :t/max {:number balance})
:status (if incorrect-value? :error :default)
:container-style style/network-tags-container}]
[quo/amount-input
{:max-value (if (integer? balance) balance 0)
:min-value 1
:value value
:on-inc-press inc-value
:on-dec-press dec-value
:container-style style/amount-input-container
:status (if incorrect-value? :error :default)}]
[quo/bottom-actions
{:actions :one-action
:button-one-props {:on-press #(rf/dispatch [:wallet/send-collectibles-amount
{:collectible collectible
:amount value
:stack-id :screen/wallet.select-asset}])
:disabled? incorrect-value?}
:button-one-label (i18n/label :t/confirm)}]
[quo/numbered-keyboard
{:left-action :none
:delete-key? true
:on-press add-digit
:on-delete delete-digit}]]))

View File

@ -77,6 +77,7 @@
[status-im.contexts.wallet.send.save-address.view :as wallet-save-address]
[status-im.contexts.wallet.send.select-address.view :as wallet-select-address]
[status-im.contexts.wallet.send.select-asset.view :as wallet-select-asset]
[status-im.contexts.wallet.send.select-collectible-amount.view :as wallet-select-collectible-amount]
[status-im.contexts.wallet.send.send-amount.view :as wallet-send-input-amount]
[status-im.contexts.wallet.send.transaction-confirmation.view :as wallet-transaction-confirmation]
[status-im.contexts.wallet.send.transaction-progress.view :as wallet-transaction-progress]
@ -428,6 +429,10 @@
{:name :screen/wallet.transaction-confirmation
:component wallet-transaction-confirmation/view}
{:name :screen/wallet.select-collectible-amount
:options {:insets {:top? true}}
:component wallet-select-collectible-amount/view}
{:name :screen/wallet.transaction-progress
:component wallet-transaction-progress/view}

View File

@ -2561,5 +2561,6 @@
"address-count": {
"one": "1 address",
"other": "{{count}} addresses"
}
},
"max": "Max: {{number}}"
}