wallet UX error fixes and more frequent refreshes

fix wallet errors that were sticking when refresh occured
add separate errors for transaction history fetching with visual feedback
update wallet when going on wallet tab
update transaction list when opening transaction modal
This commit is contained in:
Eric Dvorsak 2017-09-07 19:29:45 +02:00 committed by Roman Volosovskyi
parent 97789feb6b
commit c1cac1eb7c
12 changed files with 219 additions and 166 deletions

View File

@ -20,6 +20,7 @@
(def color-gray6 "#212121")
(def color-gray7 "#9fa3b4")
(def color-gray8 "#6E777E")
(def color-gray9 "#E9EBEC")
(def color-dark "#49545d")
(def color-steel "#838b91")
(def color-white "white")

View File

@ -165,8 +165,8 @@
[:start-requesting-discoveries]
[:remove-old-discoveries!]
[:set :accounts/creating-account? false]
[:refresh-wallet]
[:refresh-transactions]
[:update-wallet]
[:update-transactions]
[:get-fcm-token]]}))
(register-handler-fx

View File

@ -5,6 +5,7 @@
[status-im.utils.transactions :as transactions]
[status-im.ui.screens.wallet.db :as wallet.db]
[status-im.components.status :as status]
[status-im.ui.screens.wallet.navigation]
[taoensso.timbre :as log]))
(defn get-balance [{:keys [web3 account-id on-success on-error]}]
@ -18,73 +19,72 @@
(on-error err))))
(on-error "web3 or account-id not available")))
(defn assoc-error-message [db error-type err]
(assoc-in db [:wallet :errors error-type] (or (when err (str err))
:unknown-error)))
(defn clear-error-message [db error-type]
(update-in db [:wallet :errors] dissoc error-type))
;; FX
(reg-fx
:get-balance
(fn [{:keys [web3 account-id success-event error-event]}]
(get-balance
{:web3 web3
(get-balance {:web3 web3
:account-id account-id
:on-success #(dispatch [success-event %])
:on-error #(dispatch [error-event %])})))
(reg-fx
:get-transactions
(fn [{:keys [network account-id]}]
(fn [{:keys [network account-id success-event error-event]}]
(transactions/get-transactions network
account-id
#(dispatch [:update-transactions-succes %])
#(dispatch [:update-transactions-fail %]))))
#(dispatch [success-event %])
#(dispatch [error-event %]))))
;; TODO(oskarth): At some point we want to get list of relevant assets to get prices for
(reg-fx
:get-prices
(fn [{:keys [from to success-event error-event]}]
(prices/get-prices
from
(prices/get-prices from
to
#(dispatch [success-event %])
#(dispatch [error-event %]))))
;; Handlers
;; TODO(oskarth): At some point we want to get list of relevant assets to get prices for
(handlers/register-handler-fx
:load-prices
(fn [{{:keys [wallet] :as db} :db} [_ a]]
{:get-prices {:from "ETH"
:to "USD"
:success-event :update-prices
:error-event :update-prices-fail}
:db (assoc db :prices-loading? true)}))
(handlers/register-handler-fx
:refresh-wallet
:update-wallet
(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
:success-event :update-balance-success
:error-event :update-balance-fail}
:dispatch [:load-prices]
:db (assoc-in db [:wallet :balance-loading?] true)}))
(defn assoc-error-message [db err]
(assoc-in db [:wallet :wallet/error] err))
(handlers/register-handler-db
:wallet/clear-error-message
(fn [db [_]]
(update db :wallet dissoc :wallet/error)))
:get-prices {:from "ETH"
:to "USD"
:success-event :update-prices-success
:error-event :update-prices-fail}
:db (-> db
(clear-error-message :price-update)
(clear-error-message :balance-update)
(assoc-in [:wallet :balance-loading?] true)
(assoc :prices-loading? true))}))
(handlers/register-handler-fx
:refresh-transactions
:update-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)}))
:network network
:success-event :update-transactions-success
:error-event :update-transactions-fail}
:db (-> db
(clear-error-message :transaction-update)
(assoc-in [:wallet :transactions-loading?] true))}))
(handlers/register-handler-db
:update-transactions-succes
:update-transactions-success
(fn [db [_ transactions]]
(-> db
(assoc-in [:wallet :transactions] transactions)
@ -94,31 +94,36 @@
:update-transactions-fail
(fn [db [_ err]]
(log/debug "Unable to get transactions: " err)
(-> (assoc-error-message db :error)
(-> db
(assoc-error-message :transactions-update err)
(assoc-in [:wallet :transactions-loading?] false))))
(handlers/register-handler-db
:update-balance
:update-balance-success
(fn [db [_ balance]]
(-> db
(assoc-in [:wallet :balance] balance)
(assoc-in [:wallet :balance-loading?] false))))
(handlers/register-handler-db
:update-prices
(fn [db [_ prices]]
(assoc db :prices prices :prices-loading? false)))
(handlers/register-handler-db
:update-balance-fail
(fn [db [_ err]]
(log/debug "Unable to get balance: " err)
(-> (assoc-error-message db :error)
(-> db
(assoc-error-message :balance-update err)
(assoc-in [:wallet :balance-loading?] false))))
(handlers/register-handler-db
:update-prices-success
(fn [db [_ prices]]
(assoc db
:prices prices
:prices-loading? false)))
(handlers/register-handler-db
:update-prices-fail
(fn [db [_ err]]
(log/debug "Unable to get prices: " err)
(-> (assoc-error-message db :error)
(-> db
(assoc-error-message :prices-update err)
(assoc :prices-loading? false))))

View File

@ -1,14 +1,29 @@
(ns status-im.ui.screens.wallet.history.styles
(:require [status-im.components.styles :as st]))
(:require [status-im.components.styles :as styles]))
(def error-container
{:align-self :center
:justify-content :center
:border-radius 4
:padding-vertical 4
:flex-direction :row
:background-color styles/color-gray9})
(def error-message
{:color styles/color-black
:padding-top 3
:padding-right 10
:font-size 13})
(def wallet-transactions-container
{:flex 1
:background-color st/color-white})
:background-color styles/color-white})
(def main-section
{:flex 1
:position :relative
:background-color st/color-white})
:background-color styles/color-white})
(def empty-text
{:text-align :center
@ -24,11 +39,11 @@
{:flex 1
:flex-direction :column
:justify-content :center
:background-color st/color-gray-transparent})
:background-color styles/color-gray-transparent})
(def sign-all-popup
{:align-self :flex-start
:background-color st/color-white
:background-color styles/color-white
:margin-horizontal 12
:border-radius 8})
@ -38,7 +53,7 @@
:margin-horizontal 12
:text-align :center
:padding-vertical 9
:background-color st/color-light-gray})
:background-color styles/color-light-gray})
(def sign-all-popup-text
{:margin-top 8
@ -64,4 +79,4 @@
(defn transaction-icon-background [color]
{:border-radius 32
:background-color st/color-blue4-transparent})
:background-color styles/color-blue4-transparent})

View File

@ -1,18 +1,18 @@
(ns status-im.ui.screens.wallet.history.views
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [reagent.core :as r]
[re-frame.core :as rf]
[status-im.components.button.view :as btn]
[status-im.components.checkbox.view :as chk]
[status-im.components.react :as rn]
(:require [re-frame.core :as re-frame]
[reagent.core :as reagent]
[status-im.components.button.view :as button]
[status-im.components.checkbox.view :as checkbox]
[status-im.components.list.views :as list]
[status-im.components.tabs.styles :as tst]
[status-im.components.react :as react]
[status-im.components.tabs.styles :as tabs.styles]
[status-im.components.tabs.views :as tabs]
[status-im.components.toolbar-new.actions :as act]
[status-im.components.toolbar-new.view :as toolbar]
[status-im.ui.screens.wallet.history.styles :as history-styles]
[status-im.utils.utils :as utils]
[status-im.i18n :as i18n]))
[status-im.i18n :as i18n]
[status-im.ui.screens.wallet.history.styles :as history.styles]
[status-im.ui.screens.wallet.views :as wallet.views]
[status-im.utils.utils :as utils])
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
(defn on-sign-transaction
[m]
@ -25,13 +25,13 @@
(utils/show-popup "TODO" "Delete Transaction"))
(defn unsigned-action []
[toolbar/text-action #(rf/dispatch [:navigate-to-modal :wallet-transactions-sign-all])
[toolbar/text-action #(re-frame/dispatch [:navigate-to-modal :wallet-transactions-sign-all])
(i18n/label :t/transactions-sign-all)])
(def history-action
{:icon :icons/filter
:icon-opts {}
:handler #(utils/show-popup "TODO" "Not implemented") #_(rf/dispatch [:navigate-to-modal :wallet-transactions-sign-all])})
:handler #(utils/show-popup "TODO" "Not implemented") #_(re-frame/dispatch [:navigate-to-modal :wallet-transactions-sign-all])})
(defn toolbar-view [view-id]
[toolbar/toolbar2 {}
@ -43,14 +43,14 @@
[history-action]])])
(defn action-buttons [m]
[rn/view {:style history-styles/action-buttons}
[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)}]])
[react/view {:style history.styles/action-buttons}
[button/primary-button {:text (i18n/label :t/transactions-sign) :on-press #(on-sign-transaction m)}]
[button/secondary-button {:text (i18n/label :t/delete) :on-press #(on-delete-transaction m)}]])
(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-icon [k color] {:icon k :style (history.styles/transaction-icon-background color)})
(defn- transaction-type->icon [s]
(case s
@ -74,23 +74,24 @@
[list/item-icon {:icon :icons/forward}]])
;; TODO(yenda) hook with re-frame
(defn empty-text [s]
[rn/text {:style history-styles/empty-text} s])
[react/text {:style history.styles/empty-text} s])
(defview history-list []
(letsubs [transactions-history-list [:wallet/transactions-history-list]
transactions-loading? [:wallet/transactions-loading?]]
[rn/scroll-view
transactions-loading? [:wallet/transactions-loading?]
error-message [:wallet.transactions/error-message?]]
[react/scroll-view
(when error-message [wallet.views/error-message-view history.styles/error-container history.styles/error-message])
[list/section-list {:sections transactions-history-list
:render-fn render-transaction
:empty-component (empty-text (i18n/label :t/transactions-history-empty))
:on-refresh #(rf/dispatch [:refresh-transactions])
:on-refresh #(re-frame/dispatch [:update-transactions])
:refreshing transactions-loading?}]]))
(defview unsigned-list [transactions]
[]
[rn/scroll-view
[react/scroll-view
[list/flat-list {:data transactions
:render-fn render-transaction
:empty-component (empty-text (i18n/label :t/transactions-unsigned-empty))}]])
@ -117,19 +118,19 @@
(defview sign-all []
[]
[rn/keyboard-avoiding-view {:style history-styles/sign-all-view}
[rn/view {:style history-styles/sign-all-done}
[btn/primary-button {:style history-styles/sign-all-done-button
[react/keyboard-avoiding-view {:style history.styles/sign-all-view}
[react/view {:style history.styles/sign-all-done}
[button/primary-button {:style history.styles/sign-all-done-button
:text (i18n/label :t/done)
:on-press #(rf/dispatch [:navigate-back])}]]
[rn/view {:style history-styles/sign-all-popup}
[rn/text {:style history-styles/sign-all-popup-sign-phrase} "one two three"] ;; TODO hook
[rn/text {:style history-styles/sign-all-popup-text} (i18n/label :t/transactions-sign-all-text)]
[rn/view {:style history-styles/sign-all-actions}
[rn/text-input {:style history-styles/sign-all-input
:on-press #(re-frame/dispatch [:navigate-back])}]]
[react/view {:style history.styles/sign-all-popup}
[react/text {:style history.styles/sign-all-popup-sign-phrase} "one two three"] ;; TODO hook
[react/text {:style history.styles/sign-all-popup-text} (i18n/label :t/transactions-sign-all-text)]
[react/view {:style history.styles/sign-all-actions}
[react/text-input {:style history.styles/sign-all-input
:secure-text-entry true
:placeholder (i18n/label :t/transactions-sign-input-placeholder)}]
[btn/primary-button {:text (i18n/label :t/transactions-sign-all) :on-press #(on-sign-transaction %)}]]]])
[button/primary-button {:text (i18n/label :t/transactions-sign-all) :on-press #(on-sign-transaction %)}]]]])
;; Filter history
@ -137,13 +138,13 @@
[list/item
[list/item-icon (transaction-type->icon "pending")] ;; TODO add proper token data
[list/item-content label symbol]
[chk/checkbox {:checked? true #_checked?}]])
[checkbox/checkbox {:checked? true #_checked?}]])
(defn- item-type [{:keys [id label checked?]}]
[list/item
[list/item-icon (transaction-type->icon id)]
[list/item-content label]
[chk/checkbox checked?]])
[checkbox/checkbox checked?]])
(def filter-data
[{:title (i18n/label :t/transactions-filter-tokens)
@ -163,13 +164,13 @@
(defview filter-history []
[]
[rn/view
[react/view
[toolbar/toolbar2 {}
[toolbar/nav-clear-text (i18n/label :t/done)]
[toolbar/content-title (i18n/label :t/transactions-filter-title)]
[toolbar/text-action #(utils/show-popup "TODO" "Select All")
(i18n/label :t/transactions-filter-select-all)]]
[rn/scroll-view
[react/scroll-view
[list/section-list {:sections filter-data}]]])
;; TODO(jeluard) whole swipe logic
@ -177,10 +178,10 @@
(defn- main-section [view-id unsigned-transactions]
(let [tabs (tab-list unsigned-transactions)]
[rn/view {:style history-styles/main-section}
[react/view {:style history.styles/main-section}
[tabs/tabs {:selected-view-id @view-id
:tab-list tabs}]
[rn/swiper (merge tst/swiper
[react/swiper (merge tabs.styles/swiper
{:index (get-tab-index tabs @view-id)
:loop false})
;:ref #(reset! swiper %)
@ -193,8 +194,8 @@
;; TODO(yenda) must reflect selected wallet
(defview transactions []
[unsigned-transactions [:wallet/unsigned-transactions]]
(let [view-id (r/atom :wallet-transactions-history)]
[rn/view {:style history-styles/wallet-transactions-container}
(letsubs [unsigned-transactions [:wallet/unsigned-transactions]]
(let [view-id (reagent/atom :wallet-transactions-history)]
[react/view {:style history.styles/wallet-transactions-container}
[toolbar-view view-id]
[main-section view-id unsigned-transactions]]))
[main-section view-id unsigned-transactions]])))

View File

@ -1,39 +1,26 @@
(ns status-im.ui.screens.wallet.main.styles
(:require [status-im.components.styles :as st]
(:require [status-im.components.styles :as styles]
[status-im.utils.platform :as platform]))
(def wallet-container
{:flex 1
:background-color st/color-white})
:background-color styles/color-white})
(def wallet-error-container
(def error-container
{:align-self :center
:justify-content :center
:border-radius 20
:flex-direction :row
:background-color st/color-blue5})
:background-color styles/color-blue5})
(def wallet-exclamation-container
{:background-color st/color-red-2
:justify-content :center
:margin-top 5
:margin-left 10
:margin-right 7
:margin-bottom 5
:border-radius 100})
(def wallet-error-exclamation
{:width 16
:height 16})
(def wallet-error-message
{:color st/color-white
(def error-message
{:color styles/color-white
:padding-top 3
:padding-right 10
:font-size 13})
(def toolbar
{:background-color st/color-blue5
{:background-color styles/color-blue5
:elevation 0})
(def toolbar-title-container
@ -45,7 +32,7 @@
{:flex-direction :row})
(def toolbar-title-text
{:color st/color-white
{:color styles/color-white
:font-size 17
:margin-right 4})
@ -69,7 +56,7 @@
(def main-section
{:padding 16
:background-color st/color-blue4})
:background-color styles/color-blue4})
(def total-balance-container
{:margin-top 18
@ -81,12 +68,12 @@
(def total-balance-value
{:font-size 37
:color st/color-white})
:color styles/color-white})
(def total-balance-currency
{:font-size 37
:margin-left 9
:color st/color-white
:color styles/color-white
:opacity 0.4})
(def value-variation
@ -95,7 +82,7 @@
(def value-variation-title
{:font-size 14
:color st/color-white
:color styles/color-white
:opacity 0.6})
(def today-variation-container
@ -106,22 +93,22 @@
(def today-variation-container-positive
(merge today-variation-container
{:background-color st/color-green-1}))
{:background-color styles/color-green-1}))
(def today-variation-container-negative
(merge today-variation-container
{:background-color st/color-red-3}))
{:background-color styles/color-red-3}))
(def today-variation
{:font-size 12})
(def today-variation-positive
(merge today-variation
{:color st/color-green-2}))
{:color styles/color-green-2}))
(def today-variation-negative
(merge today-variation
{:color st/color-red-4}))
{:color styles/color-red-4}))
(def buttons
{:margin-top 34})
@ -131,13 +118,13 @@
;;;;;;;;;;;;;;;;;;;;
(def asset-section
{:background-color st/color-white
{:background-color styles/color-white
:padding-vertical 16})
(def asset-section-title
{:font-size 14
:margin-left 16
:color st/color-gray4})
:color styles/color-gray4})
(def asset-item-value-container
{:flex 1
@ -146,19 +133,19 @@
(def asset-item-value
{:font-size 16
:color st/color-black})
:color styles/color-black})
(def add-asset-icon
{:border-radius 32
:background-color st/color-blue4-transparent})
:background-color styles/color-blue4-transparent})
(def add-asset-text
{:font-size 16
:color st/color-blue4})
:color styles/color-blue4})
(def asset-item-currency
{:font-size 16
:color st/color-gray4
:color styles/color-gray4
:margin-left 6})
(defn asset-border [color]

View File

@ -17,6 +17,7 @@
[status-im.utils.platform :as platform]
[status-im.utils.utils :as utils]
[status-im.ui.screens.wallet.main.styles :as wallet-styles]
[status-im.ui.screens.wallet.views :as wallet.views]
[status-im.utils.money :as money]))
(defn- show-not-implemented! []
@ -47,16 +48,9 @@
[(assoc (act/opts [{:text (i18n/label :t/wallet-settings) :value show-not-implemented!}]) :icon-opts {:color :white})
transaction-history-action]]])
(defn error-message-view [error-message]
[rn/view {:style wallet-styles/wallet-error-container}
[rn/view {:style wallet-styles/wallet-exclamation-container}
[vi/icon :icons/exclamation_mark {:color :white
:container-style wallet-styles/wallet-error-exclamation}]]
[rn/text {:style wallet-styles/wallet-error-message} (i18n/label :t/wallet-error)]])
(defn main-section [usd-value change error-message]
[rn/view {:style wallet-styles/main-section}
(when error-message [error-message-view error-message])
(when error-message [wallet.views/error-message-view wallet-styles/error-container wallet-styles/error-message])
[rn/view {:style wallet-styles/total-balance-container}
[rn/view {:style wallet-styles/total-balance}
[rn/text {:style wallet-styles/total-balance-value} usd-value]
@ -125,7 +119,7 @@
:data [{}]
:renderItem (list/wrap-render-fn render-add-asset-fn)}]
:render-section-header-fn #()
:on-refresh #(rf/dispatch [:refresh-wallet])
:on-refresh #(rf/dispatch [:update-wallet])
:refreshing (or prices-loading? balance-loading?)}]]))
(defview wallet []
@ -134,7 +128,7 @@
portfolio-change [:portfolio-change]
prices-loading? [:prices-loading?]
balance-loading? [:wallet/balance-loading?]
error-message [:wallet/error-message]]
error-message [:wallet/error-message?]]
[rn/view {:style wallet-styles/wallet-container}
[toolbar-view]
[rn/scroll-view

View File

@ -0,0 +1,13 @@
(ns status-im.ui.screens.wallet.navigation
(:require [re-frame.core :as re-frame]
[status-im.ui.screens.navigation :as navigation]))
(defmethod navigation/preload-data! :wallet
[db _]
(re-frame/dispatch [:update-wallet])
db)
(defmethod navigation/preload-data! :wallet-transactions
[db _]
(re-frame/dispatch [:update-transactions])
db)

View File

@ -0,0 +1,15 @@
(ns status-im.ui.screens.wallet.styles
(:require [status-im.components.styles :as st]))
(def wallet-exclamation-container
{:background-color st/color-red-2
:justify-content :center
:margin-top 5
:margin-left 10
:margin-right 7
:margin-bottom 5
:border-radius 100})
(def wallet-error-exclamation
{:width 16
:height 16})

View File

@ -16,9 +16,14 @@
(fn [db]
(get-in db [:prices :last-day])))
(reg-sub :wallet/error-message
(reg-sub :wallet/error-message?
(fn [db]
(get-in db [:wallet :wallet/error])))
(or (get-in db [:wallet :errors :balance-update])
(get-in db [:wallet :errors :prices-update]))))
(reg-sub :wallet.transactions/error-message?
(fn [db]
(get-in db [:wallet :errors :transactions-update])))
(reg-sub :eth-balance
:<- [:balance]

View File

@ -0,0 +1,12 @@
(ns status-im.ui.screens.wallet.views
(:require [status-im.components.react :as react]
[status-im.ui.screens.wallet.styles :as styles]
[status-im.components.icons.vector-icons :as vector-icons]
[status-im.i18n :as i18n]))
(defn error-message-view [error-container-style error-message-style]
[react/view {:style error-container-style}
[react/view {:style styles/wallet-exclamation-container}
[vector-icons/icon :icons/exclamation_mark {:color :white
:container-style styles/wallet-error-exclamation}]]
[react/text {:style error-message-style} (i18n/label :t/wallet-error)]])

View File

@ -14,15 +14,20 @@
clear-error"
(run-test-sync
(re-frame/reg-fx ::events/init-store #())
(re-frame/reg-fx :get-prices #())
(re-frame/reg-fx :get-balance #())
(re-frame/dispatch [:initialize-db])
(let [error (re-frame/subscribe [:wallet/error-message])
message :error]
(let [error (re-frame/subscribe [:wallet/error-message?])
message "failed balance update"]
(re-frame/dispatch [:update-balance-fail message])
(is (= message @error)))
(let [error (re-frame/subscribe [:wallet/error-message])
message :error]
(let [error (re-frame/subscribe [:wallet/error-message?])]
(re-frame/dispatch [:update-wallet])
(is (nil? @error)))
(let [error (re-frame/subscribe [:wallet/error-message?])
message "failed price update"]
(re-frame/dispatch [:update-prices-fail message])
(is (= message @error)))
(let [error (re-frame/subscribe [:wallet/error-message])]
(re-frame/dispatch [:wallet/clear-error-message])
(is (nil? @error)))))
(is (= message @error))))
(let [error (re-frame/subscribe [:wallet/error-message?])]
(re-frame/dispatch [:update-wallet])
(is (nil? @error))))