diff --git a/src/status_im/ui/screens/events.cljs b/src/status_im/ui/screens/events.cljs index 1c45966ad6..d971aec86f 100644 --- a/src/status_im/ui/screens/events.cljs +++ b/src/status_im/ui/screens/events.cljs @@ -166,6 +166,7 @@ [:remove-old-discoveries!] [:set :accounts/creating-account? false] [:refresh-wallet] + [:refresh-transactions] [:get-fcm-token]]})) (register-handler-fx @@ -273,4 +274,3 @@ (fn [_ _] (notifications/get-fcm-token) {})) - diff --git a/src/status_im/ui/screens/wallet/events.cljs b/src/status_im/ui/screens/wallet/events.cljs index cb9e7a70e4..6adf80ba2c 100644 --- a/src/status_im/ui/screens/wallet/events.cljs +++ b/src/status_im/ui/screens/wallet/events.cljs @@ -2,6 +2,7 @@ (:require [re-frame.core :as re-frame :refer [dispatch reg-fx]] [status-im.utils.handlers :as handlers] [status-im.utils.prices :as prices] + [status-im.utils.transactions :as transactions] [status-im.ui.screens.wallet.db :as wallet.db] [status-im.components.status :as status] [taoensso.timbre :as log])) @@ -28,6 +29,14 @@ :on-success #(dispatch [success-event %]) :on-error #(dispatch [error-event %])}))) +(reg-fx + :get-transactions + (fn [{:keys [network account-id]}] + (transactions/get-transactions network + account-id + #(dispatch [:update-transactions-succes %]) + #(dispatch [:update-transactions-fail %])))) + (reg-fx :get-prices (fn [{:keys [from to success-event error-event]}] @@ -51,15 +60,13 @@ (handlers/register-handler-fx :refresh-wallet - (fn [{{:keys [web3 accounts/current-account-id] :as db} :db} [_ a]] + (fn [{{:keys [web3 accounts/current-account-id network] :as db} :db} [_ a]] {:get-balance {:web3 web3 :account-id current-account-id :success-event :update-balance :error-event :update-balance-fail} :dispatch [:load-prices] - :db (-> db - (assoc-in [:wallet :transactions] wallet.db/dummy-transaction-data) - (assoc-in [:wallet :balance-loading?] true))})) + :db (assoc-in db [:wallet :balance-loading?] true)})) (defn assoc-error-message [db err] (assoc-in db [:wallet :wallet/error] err)) @@ -69,6 +76,27 @@ (fn [db [_]] (update db :wallet dissoc :wallet/error))) +(handlers/register-handler-fx + :refresh-transactions + (fn [{{:keys [accounts/current-account-id network] :as db} :db} _] + {:get-transactions {:account-id current-account-id + :network network} + :db (assoc-in db [:wallet :transactions-loading?] true)})) + +(handlers/register-handler-db + :update-transactions-succes + (fn [db [_ transactions]] + (-> db + (assoc-in [:wallet :transactions] transactions) + (assoc-in [:wallet :transactions-loading?] false)))) + +(handlers/register-handler-db + :update-transactions-fail + (fn [db [_ err]] + (log/debug "Unable to get transactions: " err) + (-> (assoc-error-message db :error) + (assoc-in [:wallet :transactions-loading?] false)))) + (handlers/register-handler-db :update-balance (fn [db [_ balance]] diff --git a/src/status_im/ui/screens/wallet/history/views.cljs b/src/status_im/ui/screens/wallet/history/views.cljs index 40a2d7eb2c..73928e410d 100644 --- a/src/status_im/ui/screens/wallet/history/views.cljs +++ b/src/status_im/ui/screens/wallet/history/views.cljs @@ -47,25 +47,29 @@ [btn/primary-button {:text (i18n/label :t/transactions-sign) :on-press #(on-sign-transaction m)}] [btn/secondary-button {:text (i18n/label :t/delete) :on-press #(on-delete-transaction m)}]]) -(defn- unsigned? [state] (= "unsigned" state)) +(defn- unsigned? [type] (= "unsigned" type)) +(defn- inbound? [type] (= "inbound" type)) (defn- transaction-icon [k color] {:icon k :style (history-styles/transaction-icon-background color)}) (defn- transaction-type->icon [s] (case s - "incoming" (transaction-icon :icons/arrow-left :red) - "outgoing" (transaction-icon :icons/arrow-right :red) + "inbound" (transaction-icon :icons/arrow-left :red) + "outbound" (transaction-icon :icons/arrow-right :red) "postponed" (transaction-icon :icons/arrow-right :red) "unsigned" (transaction-icon :icons/dots-horizontal :red) "pending" (transaction-icon :icons/dots-horizontal :red) (throw (str "Unknown transaction type: " s)))) -(defn render-transaction [{:keys [to state] {:keys [value symbol]} :content :as m}] +(defn render-transaction [{:keys [to from type value symbol] :as m}] [list/item - [list/item-icon (transaction-type->icon state)] + [list/item-icon (transaction-type->icon type)] [list/item-content - (str value " " symbol) (str (i18n/label :t/to) " " to) - (if (unsigned? state) + (str value " " symbol) + (if to + (str (i18n/label :t/to) " " to) + (str (i18n/label :t/from) " " from)) + (when (unsigned? type) [action-buttons m])] [list/item-icon {:icon :icons/forward}]]) @@ -75,22 +79,14 @@ [rn/text {:style history-styles/empty-text} s]) (defview history-list [] - (letsubs [pending-transactions [:wallet/pending-transactions] - postponed-transactions [:wallet/postponed-transactions] - sent-transactions [:wallet/sent-transactions]] + (letsubs [transactions-history-list [:wallet/transactions-history-list] + transactions-loading? [:wallet/transactions-loading?]] [rn/scroll-view - [list/section-list {:sections [{:title "Postponed" - :key :postponed - :data postponed-transactions} - {:title "Pending" - :key :pending - :data pending-transactions} - ;; TODO(yenda) :sent transactions shouldbe grouped by day and have their :key / :title adapted - {:title "01 Sep" - :key :sent-sep - :data sent-transactions}] + [list/section-list {:sections transactions-history-list :render-fn render-transaction - :empty-component (empty-text (i18n/label :t/transactions-history-empty))}]])) + :empty-component (empty-text (i18n/label :t/transactions-history-empty)) + :on-refresh #(rf/dispatch [:refresh-transactions]) + :refreshing transactions-loading?}]])) (defview unsigned-list [transactions] [] @@ -187,12 +183,12 @@ [rn/swiper (merge tst/swiper {:index (get-tab-index tabs @view-id) :loop false}) - ;:ref #(reset! swiper %) - ;:on-momentum-scroll-end (on-scroll-end swiped? scroll-ended @view-id) + ;:ref #(reset! swiper %) + ;:on-momentum-scroll-end (on-scroll-end swiped? scroll-ended @view-id) (doall - (map-indexed (fn [index {screen :screen}] - (with-meta screen {:key index} )) tabs))]])) + (map-indexed (fn [index {screen :screen}] + (with-meta screen {:key index} )) tabs))]])) ;; TODO(yenda) must reflect selected wallet diff --git a/src/status_im/ui/screens/wallet/main/views.cljs b/src/status_im/ui/screens/wallet/main/views.cljs index 12baa516ac..2cda80568e 100644 --- a/src/status_im/ui/screens/wallet/main/views.cljs +++ b/src/status_im/ui/screens/wallet/main/views.cljs @@ -128,12 +128,12 @@ :refreshing (or prices-loading? balance-loading?)}]])) (defview wallet [] - (letsubs [eth-balance [:eth-balance] - portfolio-value [:portfolio-value] + (letsubs [eth-balance [:eth-balance] + portfolio-value [:portfolio-value] portfolio-change [:portfolio-change] - prices-loading? [:prices-loading?] - balance-loading? [:wallet/balance-loading?] - error-message [:wallet/error-message]] + prices-loading? [:prices-loading?] + balance-loading? [:wallet/balance-loading?] + error-message [:wallet/error-message]] [rn/view {:style wallet-styles/wallet-container} [toolbar-view] [rn/scroll-view diff --git a/src/status_im/ui/screens/wallet/subs.cljs b/src/status_im/ui/screens/wallet/subs.cljs index 045ffd51bb..3e197fe4fe 100644 --- a/src/status_im/ui/screens/wallet/subs.cljs +++ b/src/status_im/ui/screens/wallet/subs.cljs @@ -1,6 +1,8 @@ (ns status-im.ui.screens.wallet.subs (:require [re-frame.core :refer [reg-sub subscribe]] - [status-im.utils.money :as money])) + [clojure.string :as string] + [status-im.utils.money :as money] + [status-im.utils.datetime :as datetime])) (reg-sub :balance (fn [db] @@ -54,26 +56,38 @@ (fn [db] (get-in db [:wallet :balance-loading?]))) +(reg-sub :wallet/transactions-loading? + (fn [db] + (get-in db [:wallet :transactions-loading?]))) + (reg-sub :wallet/transactions (fn [db] (get-in db [:wallet :transactions]))) +(defn filter-transactions [type transactions] + (filter #(= (:type %) type) transactions)) + (reg-sub :wallet/unsigned-transactions :<- [:wallet/transactions] (fn [transactions] - (filter #(= (:state %) :unsigned) transactions))) + (filter-transactions :unsigned transactions))) -(reg-sub :wallet/pending-transactions +(defn mini-str-date->keyword [mini-str-date] + (keyword (str "sent-" (string/replace mini-str-date #" " "-")))) + +(reg-sub :wallet/transactions-history-list :<- [:wallet/transactions] (fn [transactions] - (filter #(= (:state %) :pending) transactions))) - -(reg-sub :wallet/postponed-transactions - :<- [:wallet/transactions] - (fn [transactions] - (filter #(= (:state %) :postponed) transactions))) - -(reg-sub :wallet/sent-transactions - :<- [:wallet/transactions] - (fn [transactions] - (filter #(= (:state %) :sent) transactions))) + (let [{:keys [postponed pending inbound outbound]} (group-by :type transactions) + transaction-history-list [{:title "Postponed" + :key :postponed + :data (or postponed [])} + {:title "Pending" + :key :pending + :data (or pending [])}] + completed-transactions (->> (into inbound outbound) + (group-by #(datetime/date->mini-str-date (:timestamp %))) + (map (fn [[k v]] {:title k + :key (mini-str-date->keyword k) + :data v})))] + (into transaction-history-list (or completed-transactions []))))) diff --git a/src/status_im/utils/datetime.cljs b/src/status_im/utils/datetime.cljs index 21887b148f..657eef8eb1 100644 --- a/src/status_im/utils/datetime.cljs +++ b/src/status_im/utils/datetime.cljs @@ -34,6 +34,11 @@ (before? date today) (label :t/datetime-yesterday) :else (today-format-fn local))))) +(defn date->mini-str-date [ms] + (unparse (formatter "dd MMM") (-> ms + from-long + (plus time-zone-offset)))) + (defn day-relative [ms] (when (pos? ms) (to-short-str ms #(label :t/datetime-today)))) diff --git a/src/status_im/utils/transactions.cljs b/src/status_im/utils/transactions.cljs new file mode 100644 index 0000000000..7a52c2af83 --- /dev/null +++ b/src/status_im/utils/transactions.cljs @@ -0,0 +1,37 @@ +(ns status-im.utils.transactions + (:require [status-im.utils.utils :as utils] + [status-im.utils.types :as types] + [status-im.utils.money :as money])) + +(defn get-transaction-url [network account] + (let [network (case network + "testnet" "ropsten" + "mainnet" "api")] + (str "https://" network ".etherscan.io/api?module=account&action=txlist&address=0x" + account "&startblock=0&endblock=99999999&sort=desc&apikey=YourApiKeyToken?q=json"))) + +(defn format-transaction [account {:keys [value to from timeStamp]}] + (let [transaction {:value (money/wei->ether value) + ;; timestamp is in seconds, we convert it in ms + :timestamp (str timeStamp "000") + :symbol "ETH"} + inbound? (= (str "0x" account) to)] + (if inbound? + (assoc transaction + :from from + :type :inbound) + (assoc transaction + :to to + :type :outbound)))) + +(defn format-transactions-response [response account] + (->> response + types/json->clj + :result + (map (partial format-transaction account)) + (sort-by :timestamp))) + +(defn get-transactions [network account on-success on-error] + (utils/http-get (get-transaction-url network account) + #(on-success (format-transactions-response % account)) + on-error))