diff --git a/resources/icons/exclamation_mark.svg b/resources/icons/exclamation_mark.svg index d0e7dd1c8f..47919eff22 100644 --- a/resources/icons/exclamation_mark.svg +++ b/resources/icons/exclamation_mark.svg @@ -1,3 +1,6 @@ - - - + + + + + + \ No newline at end of file diff --git a/src/status_im/constants.cljs b/src/status_im/constants.cljs index 34366daab6..c1d25bce46 100644 --- a/src/status_im/constants.cljs +++ b/src/status_im/constants.cljs @@ -33,6 +33,7 @@ {:type [{:id :inbound :label (i18n/label :t/incoming) :checked? true} {:id :outbound :label (i18n/label :t/outgoing) :checked? true} {:id :pending :label (i18n/label :t/pending) :checked? true} + {:id :failed :label (i18n/label :t/failed) :checked? true} ;; TODO(jeluard) Restore once we support postponing transaction #_{:id :postponed :label (i18n/label :t/postponed) :checked? true}]}}) diff --git a/src/status_im/translations/en.cljs b/src/status_im/translations/en.cljs index 1d74a9018f..8eda46e9aa 100644 --- a/src/status_im/translations/en.cljs +++ b/src/status_im/translations/en.cljs @@ -405,6 +405,7 @@ :incoming "Incoming" :outgoing "Outgoing" :pending "Pending" + :failed "Failed" :postponed "Postponed" ;;webview diff --git a/src/status_im/ui/components/colors.cljs b/src/status_im/ui/components/colors.cljs index 234949059f..179b2ec164 100644 --- a/src/status_im/ui/components/colors.cljs +++ b/src/status_im/ui/components/colors.cljs @@ -24,6 +24,7 @@ (def text-light-gray "#212121") ;; Used for labels (home items) (def cyan "#7adcfb") ;; Used by wallet transaction filtering icon (def photo-border-color "#ccd3d6") +(def green "#44d058") ;; icon for successful inboud transaction (def chat-colors ["#fa6565" "#7cda00" diff --git a/src/status_im/ui/components/icons/vector_icons.cljs b/src/status_im/ui/components/icons/vector_icons.cljs index 87c8f8a907..b0f031a236 100644 --- a/src/status_im/ui/components/icons/vector_icons.cljs +++ b/src/status_im/ui/components/icons/vector_icons.cljs @@ -52,7 +52,7 @@ :icons/delete (components.svg/slurp-svg "./resources/icons/delete.svg") :icons/dots-horizontal (components.svg/slurp-svg "./resources/icons/dots_horizontal.svg") :icons/dots-vertical (components.svg/slurp-svg "./resources/icons/dots_vertical.svg") - :icons/exclamation_mark (components.svg/slurp-svg "./resources/icons/exclamation_mark.svg") + :icons/exclamation-mark (components.svg/slurp-svg "./resources/icons/exclamation_mark.svg") :icons/filter (components.svg/slurp-svg "./resources/icons/filter.svg") :icons/fullscreen (components.svg/slurp-svg "./resources/icons/fullscreen.svg") :icons/group-big (components.svg/slurp-svg "./resources/icons/group_big.svg") diff --git a/src/status_im/ui/screens/wallet/transactions/styles.cljs b/src/status_im/ui/screens/wallet/transactions/styles.cljs index 6735efb694..06a0fb32d8 100644 --- a/src/status_im/ui/screens/wallet/transactions/styles.cljs +++ b/src/status_im/ui/screens/wallet/transactions/styles.cljs @@ -50,6 +50,9 @@ :ios {:padding-top 13} :android {:padding-top 14}}) +(def amount-text + {:color colors/text}) + (def tx-amount {:flex-grow 1 :flex-shrink 1 @@ -185,13 +188,17 @@ :margin-vertical 10 :height 2}) -(defn progress-bar-done [done] +(defn progress-bar-done [done failed?] {:flex done - :background-color colors/blue}) + :background-color (if failed? + colors/red + colors/blue)}) -(defn progress-bar-todo [todo] +(defn progress-bar-todo [todo failed?] {:flex todo - :background-color colors/blue + :background-color (if failed? + colors/red + colors/blue) :opacity 0.30}) (def details-confirmations-count @@ -199,6 +206,11 @@ :font-size 15 :margin-vertical 2}) +(def details-failed + {:color colors/red + :font-size 15 + :margin-vertical 2}) + (def details-confirmations-helper-text {:color colors/gray :font-size 14 diff --git a/src/status_im/ui/screens/wallet/transactions/subs.cljs b/src/status_im/ui/screens/wallet/transactions/subs.cljs index b641535448..8cbd71b223 100644 --- a/src/status_im/ui/screens/wallet/transactions/subs.cljs +++ b/src/status_im/ui/screens/wallet/transactions/subs.cljs @@ -78,16 +78,24 @@ (fn [{:keys [postponed]}] (when postponed {:title "Postponed" - :key :postponed - :data postponed}))) + :key :postponed + :data postponed}))) (reg-sub :wallet.transactions/pending-transactions-list :<- [:wallet.transactions/grouped-transactions] (fn [{:keys [pending]}] (when pending {:title "Pending" - :key :pending - :data pending}))) + :key :pending + :data pending}))) + +(reg-sub :wallet.transactions/failed-transactions-list + :<- [:wallet.transactions/grouped-transactions] + (fn [{:keys [failed]}] + (when failed + {:title "Failed" + :key :failed + :data failed}))) (defn group-transactions-by-date [transactions] (->> transactions @@ -101,8 +109,8 @@ (reg-sub :wallet.transactions/completed-transactions-list :<- [:wallet.transactions/grouped-transactions] - (fn [{:keys [inbound outbound]}] - (group-transactions-by-date (into inbound outbound)))) + (fn [{:keys [inbound outbound failed]}] + (group-transactions-by-date (concat inbound outbound failed)))) (reg-sub :wallet.transactions/transactions-history-list :<- [:wallet.transactions/postponed-transactions-list] diff --git a/src/status_im/ui/screens/wallet/transactions/views.cljs b/src/status_im/ui/screens/wallet/transactions/views.cljs index 7c27dc854e..f89499bcab 100644 --- a/src/status_im/ui/screens/wallet/transactions/views.cljs +++ b/src/status_im/ui/screens/wallet/transactions/views.cljs @@ -6,6 +6,7 @@ [status-im.ui.components.react :as react] [status-im.ui.components.status-bar.view :as status-bar] [status-im.ui.components.styles :as components.styles] + [status-im.ui.components.colors :as colors] [status-im.ui.components.toolbar.actions :as actions] [status-im.ui.components.toolbar.view :as toolbar] [status-im.ui.components.status-bar.view :as status-bar] @@ -33,6 +34,7 @@ [(history-action (not (all-checked? filter-data)))]]]) (defn- inbound? [type] (= :inbound type)) +(defn- failed? [type] (= :failed type)) (defn- transaction-icon [k background-color color] {:icon k @@ -41,12 +43,13 @@ (defn- transaction-type->icon [k] (case k - :inbound (transaction-icon :icons/arrow-left components.styles/color-green-3-light components.styles/color-green-3) - :outbound (transaction-icon :icons/arrow-right components.styles/color-blue4-transparent components.styles/color-blue4) - (:postponed :pending) (transaction-icon :icons/arrow-right components.styles/color-gray4-transparent components.styles/color-gray7) + :inbound (transaction-icon :icons/arrow-left (colors/alpha colors/green 0.2) colors/green) + :outbound (transaction-icon :icons/arrow-right (colors/alpha colors/blue 0.1) colors/blue) + :failed (transaction-icon :icons/exclamation-mark colors/gray-light colors/red) + (:postponed :pending) (transaction-icon :icons/arrow-right colors/gray-light colors/gray) (throw (str "Unknown transaction type: " k)))) -(defn render-transaction [{:keys [hash from-contact to-contact to from type value time-formatted symbol] :as transaction} network] +(defn render-transaction [{:keys [hash from-contact to-contact to from type value time-formatted symbol]} network] (let [[label contact address contact-accessibility-label address-accessibility-label] (if (inbound? type) @@ -62,7 +65,8 @@ [react/text {:style styles/tx-amount :ellipsize-mode "tail" :number-of-lines 1} - [react/text {:accessibility-label :amount-text} + [react/text {:accessibility-label :amount-text + :style styles/amount-text} (-> value (money/internal->formatted symbol decimals) money/to-fixed str)] " " [react/text {:accessibility-label :currency-text} @@ -170,16 +174,19 @@ (clojure.string/upper-case (name symbol))]] [react/text {:style styles/details-header-date} date]]]) -(defn progress-bar [progress] +(defn progress-bar [progress failed?] [react/view {:style styles/progress-bar} - [react/view {:style (styles/progress-bar-done progress)}] - [react/view {:style (styles/progress-bar-todo (- 100 progress))}]]) + [react/view {:style (styles/progress-bar-done progress failed?)}] + [react/view {:style (styles/progress-bar-todo (- 100 progress) failed?)}]]) -(defn details-confirmations [confirmations confirmations-progress] +(defn details-confirmations [confirmations confirmations-progress type] [react/view {:style styles/details-block} - [progress-bar confirmations-progress] - [react/text {:style styles/details-confirmations-count} - (str confirmations " " (i18n/label :t/confirmations))] + [progress-bar confirmations-progress (failed? type)] + (if (failed? type) + [react/text {:style styles/details-failed} + (i18n/label :t/failed)] + [react/text {:style styles/details-confirmations-count} + (str confirmations " " (i18n/label :t/confirmations))]) [react/text {:style styles/details-confirmations-helper-text} (i18n/label :t/confirmations-helper-text)]]) @@ -232,9 +239,9 @@ {:label (i18n/label :t/open-on-etherscan) :action #(.openURL react/linking url)}])]) (defview transaction-details [] - (letsubs [{:keys [hash url] :as transaction} [:wallet.transactions/transaction-details] - confirmations [:wallet.transactions.details/confirmations] - confirmations-progress [:wallet.transactions.details/confirmations-progress]] + (letsubs [{:keys [hash url type] :as transaction} [:wallet.transactions/transaction-details] + confirmations [:wallet.transactions.details/confirmations] + confirmations-progress [:wallet.transactions.details/confirmations-progress]] [react/view {:style components.styles/flex} [status-bar/status-bar] [toolbar/toolbar {} @@ -243,6 +250,6 @@ (when transaction [toolbar/actions (details-action hash url)])] [react/scroll-view {:style components.styles/main-container} [details-header transaction] - [details-confirmations confirmations confirmations-progress] + [details-confirmations confirmations confirmations-progress type] [react/view {:style styles/details-separator}] [details-list transaction]]])) diff --git a/src/status_im/utils/ethereum/erc20.cljs b/src/status_im/utils/ethereum/erc20.cljs index 060d25acfb..716f1950f4 100644 --- a/src/status_im/utils/ethereum/erc20.cljs +++ b/src/status_im/utils/ethereum/erc20.cljs @@ -84,35 +84,39 @@ (defn- parse-transaction-entries [current-block-number block-info chain direction transfers] (into {} - (for [transfer transfers] - (let [token (->> transfer :address (tokens/address->token chain))] - [(:transactionHash transfer) - {:block (-> block-info :number str) - :hash (:transactionHash transfer) - :symbol (:symbol token) - :from (-> transfer :topics second remove-padding) - :to (-> transfer :topics last remove-padding) - :value (-> transfer :data ethereum/hex->bignumber) - :type direction + (keep identity + (for [transfer transfers] + (if-let [token (->> transfer :address (tokens/address->token chain))] + [(:transactionHash transfer) + {:block (-> block-info :number str) + :hash (:transactionHash transfer) + :symbol (:symbol token) + :from (-> transfer :topics second remove-padding) + :to (-> transfer :topics last remove-padding) + :value (-> transfer :data ethereum/hex->bignumber) + :type direction - :confirmations (str (- current-block-number (-> transfer :blockNumber ethereum/hex->int))) + :confirmations (str (- current-block-number (-> transfer :blockNumber ethereum/hex->int))) - :gas-price nil - :nonce nil - :data nil + :gas-price nil + :nonce nil + :data nil - :gas-limit nil - :timestamp (-> block-info :timestamp (* 1000) str) + :gas-limit nil + :timestamp (-> block-info :timestamp (* 1000) str) - :gas-used nil + :gas-used nil - ;; NOTE(goranjovic) - metadata on the type of token in question: contains name, symbol, decimas, address. - :token token + ;; NOTE(goranjovic) - metadata on the type of token: contains name, symbol, decimas, address. + :token token - ;; NOTE(goranjovic) - just a flag we need when we merge this entry with the existing entry in - ;; the app, e.g. transaction info with gas details, or a previous transfer entry with old - ;; confirmations count. - :transfer true}])))) + ;; NOTE(goranjovic) - if an event has been emitted, we can say there was no error + :error? false + + ;; NOTE(goranjovic) - just a flag we need when we merge this entry with the existing entry in + ;; the app, e.g. transaction info with gas details, or a previous transfer entry with old + ;; confirmations count. + :transfer true}]))))) (defn add-block-info [web3 current-block-number chain direction result success-fn] (let [transfers-by-block (group-by :blockNumber result)] diff --git a/src/status_im/utils/transactions.cljs b/src/status_im/utils/transactions.cljs index 3c88d2d813..5b6be83af4 100644 --- a/src/status_im/utils/transactions.cljs +++ b/src/status_im/utils/transactions.cljs @@ -25,23 +25,26 @@ (str "https://" network-subdomain ".etherscan.io/api?module=account&action=txlist&address=0x" account "&startblock=0&endblock=99999999&sort=desc&apikey=" etherscan-api-key "?q=json"))) -(defn- format-transaction [account {:keys [value timeStamp blockNumber hash from to gas gasPrice gasUsed nonce confirmations input]}] - (let [inbound? (= (str "0x" account) to)] - {:value value +(defn- format-transaction [account {:keys [value timeStamp blockNumber hash from to gas gasPrice gasUsed nonce confirmations input isError]}] + (let [inbound? (= (str "0x" account) to) + error? (= "1" isError)] + {:value value ;; timestamp is in seconds, we convert it in ms - :timestamp (str timeStamp "000") - :symbol :ETH - :type (if inbound? :inbound :outbound) - :block blockNumber - :hash hash - :from from - :to to - :gas-limit gas - :gas-price gasPrice - :gas-used gasUsed - :nonce nonce + :timestamp (str timeStamp "000") + :symbol :ETH + :type (cond error? :failed + inbound? :inbound + :else :outbound) + :block blockNumber + :hash hash + :from from + :to to + :gas-limit gas + :gas-price gasPrice + :gas-used gasUsed + :nonce nonce :confirmations confirmations - :data input})) + :data input})) (defn- format-transactions-response [response account] (->> response