[BUG #2228] Implemented wallet filtering screen

This commit is contained in:
Julien Eluard 2017-10-26 15:13:24 +02:00 committed by Julien Eluard
parent 0403a7cdc1
commit 0730420017
19 changed files with 161 additions and 91 deletions

View File

@ -1,5 +1,6 @@
(ns status-im.constants
(:require [status-im.utils.types :as types]
(:require [status-im.i18n :as i18n]
[status-im.utils.types :as types]
[status-im.utils.config :as config]))
(def ethereum-rpc-url "http://localhost:8545")
@ -25,6 +26,15 @@
(def default-network "testnet_rpc")
(def default-wallet-transactions
{:filters
{: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}
;; TODO(jeluard) Restore once we support postponing transaction
#_
{:id :postponed :label (i18n/label :t/postponed) :checked? true}]}})
(defn- transform-config [networks]
(->> networks
(map (fn [[network-name {:keys [config] :as data}]]

View File

@ -276,6 +276,10 @@
:confirmations-helper-text "Please wait for at least 12 confirmations to make sure your transaction is processed securely"
:copy-transaction-hash "Copy transaction hash"
:open-on-etherscan "Open on Etherscan.io"
:incoming "Incoming"
:outgoing "Outgoing"
:pending "Pending"
:postponed "Postponed"
;;webview
:web-view-error "oops, error"

View File

@ -2,11 +2,13 @@
(:require-macros [status-im.utils.styles :refer [defnstyle]])
(:require [status-im.ui.components.styles :as st]))
(def wrapper
{:padding 16})
(defnstyle icon-check-container [checked?]
{:background-color (if checked? st/color-light-blue st/color-gray5)
:alignItems :center
:justifyContent :center
:margin-right 16
:android {:border-radius 2
:width 17
:height 17}

View File

@ -1,11 +1,13 @@
(ns status-im.ui.components.checkbox.view
(:require [cljs.spec.alpha :as s]
[status-im.ui.components.checkbox.styles :as cst]
[status-im.ui.components.react :as rn]
[status-im.utils.platform :as p]))
(:require [reagent.core :as reagent]
[status-im.ui.components.checkbox.styles :as styles]
[status-im.ui.components.react :as react]))
(defn checkbox [{:keys [on-press checked?]}]
[rn/touchable-highlight {:on-press on-press}
[rn/view (cst/icon-check-container checked?)
;; TODO(jeluard) Migrate to native checkbox provided by RN 0.49
;; https://facebook.github.io/react-native/docs/checkbox.html
(defn checkbox [{:keys [on-value-change checked?]}]
[react/touchable-highlight {:style styles/wrapper :on-press #(do (when on-value-change (on-value-change (not checked?))))}
[react/view (styles/icon-check-container checked?)
(when checked?
[rn/icon :check_on cst/check-icon])]])
[react/icon :check_on styles/check-icon])]])

View File

@ -1,6 +0,0 @@
(ns status-im.ui.components.item-checkbox
(:require [reagent.core :as r]
[status-im.react-native.js-dependencies :as rn-dependencies]))
(def item-checkbox rn-dependencies/camera)

View File

@ -10,6 +10,12 @@
{:flex 1
:flex-direction :column})
(def item-checkbox
{:flex 1
:flex-direction :column
:align-items :center
:justify-content :center})
(def primary-text-base
{:font-size 17
:color styles/color-black})

View File

@ -20,6 +20,7 @@
"
(:require [reagent.core :as r]
[status-im.ui.components.list.styles :as lst]
[status-im.ui.components.checkbox.view :as checkbox]
[status-im.ui.components.react :as rn]
[status-im.ui.components.icons.vector-icons :as vi]
[status-im.utils.platform :as p]))
@ -72,6 +73,11 @@
[& children]
(into [rn/view {:style lst/item-text-view}] (keep identity children)))
(defn item-checkbox
[{:keys [style] :as props}]
[rn/view {:style (merge style lst/item-checkbox)}
[checkbox/checkbox props]])
(defn- wrap-render-fn [f]
(fn [data]
;; For details on passed data
@ -114,9 +120,10 @@
(let [{:keys [section]} (js->clj data :keywordize-keys true)]
(r/as-element (f section)))))
(defn- default-render-section-header [{:keys [title]}]
(defn- default-render-section-header [{:keys [title data]}]
(when (seq data)
[rn/text {:style lst/section-header}
title])
title]))
(defn- wrap-per-section-render-fn [props]
;; TODO(jeluard) Somehow wrapping `:render-fn` does not work

View File

@ -75,9 +75,11 @@
nil
tst/item])
(defn- icon-action [icon icon-opts handler]
(defn- icon-action [icon {:keys [overlay-style] :as icon-opts} handler]
[rn/touchable-highlight {:on-press handler}
[rn/view {:style (merge tst/item tst/toolbar-action)}
(when overlay-style
[rn/view overlay-style])
[vi/icon icon icon-opts]]])
(defn actions [v]

View File

@ -34,6 +34,7 @@
:tags []
:sync-state :done
:wallet {}
:wallet.transactions constants/default-wallet-transactions
:prices {}
:notifications {}
:network constants/default-network
@ -177,6 +178,7 @@
:discoveries/request-discoveries-timer
:discoveries/new-discover
:wallet/wallet
:wallet/wallet.transactions
:prices/prices
:prices/prices-loading?
:notifications/notifications]))

View File

@ -19,6 +19,7 @@
status-im.ui.screens.qr-scanner.events
status-im.ui.screens.wallet.events
status-im.ui.screens.wallet.send.events
status-im.ui.screens.wallet.transactions.events
status-im.ui.screens.wallet.choose-recipient.events
[re-frame.core :refer [dispatch reg-fx reg-cofx] :as re-frame]
[status-im.native-module.core :as status]

View File

@ -111,12 +111,3 @@
(defn asset-border [color]
{:border-color color :border-width 1 :border-radius 32})
(def corner-dot
{:position :absolute
:top 12
:right 6
:width 4
:height 4
:border-radius 2
:background-color styles/color-cyan})

View File

@ -0,0 +1,21 @@
(ns status-im.ui.screens.wallet.transactions.events
(:require [status-im.utils.handlers :as handlers]))
(defn- mark-all-checked [filters]
(update filters :type #(map (fn [m] (assoc m :checked? true)) %)))
(defn- mark-checked [filters {:keys [type] :as m} checked?]
(update filters :type #(map (fn [{:keys [id] :as m}] (if (= type id) (assoc m :checked? checked?) m)) %)))
(defn- update-filters [db f]
(update-in db [:wallet.transactions :filters] f))
(handlers/register-handler-db
:wallet.transactions/filter
(fn [db [_ path checked?]]
(update-filters db #(mark-checked % path checked?))))
(handlers/register-handler-db
:wallet.transactions/filter-all
(fn [db]
(update-filters db mark-all-checked)))

View File

@ -225,3 +225,12 @@
{:background-color styles/color-light-gray3
:height 1
:margin-vertical 10})
(def corner-dot
{:position :absolute
:top 0
:right 0
:width 4
:height 4
:border-radius 2
:background-color styles/color-cyan})

View File

@ -161,3 +161,7 @@
(if (>= confirmations max-confirmations)
100
(* 100 (/ confirmations max-confirmations))))))
(reg-sub :wallet.transactions/filters
(fn [db]
(get-in db [:wallet.transactions :filters])))

View File

@ -12,18 +12,9 @@
[status-im.i18n :as i18n]
[status-im.ui.screens.wallet.transactions.styles :as transactions.styles]
[status-im.ui.screens.wallet.views :as wallet.views]
[status-im.utils.money :as money]
[status-im.utils.utils :as utils])
[status-im.utils.money :as money])
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
(defn- show-not-implemented! []
(utils/show-popup "TODO" "Not implemented yet!"))
(defn on-sign-transaction
[password]
;; TODO(yenda) implement
(re-frame/dispatch [:accept-transactions password]))
(defn on-delete-transaction
[{:keys [id]}]
(re-frame/dispatch [:wallet/discard-unsigned-transaction-with-confirmation id]))
@ -34,16 +25,22 @@
:handler #(re-frame/dispatch [:navigate-to-modal :wallet-transactions-sign-all])}
(i18n/label :t/transactions-sign-all)])
(def history-action
(defn history-action [filter?]
(merge
{:icon :icons/filter
:handler #(utils/show-popup "TODO" "Not implemented") #_(re-frame/dispatch [:navigate-to-modal :wallet-transactions-sign-all])})
:handler #(re-frame/dispatch [:navigate-to-modal :wallet-transactions-filter])}
(when filter? {:icon-opts {:overlay-style transactions.styles/corner-dot}})))
(defn toolbar-view [current-tab unsigned-transactions-count]
(defn- all-checked? [filter-data]
(and (every? :checked? (:type filter-data))
(every? :checked? (:tokens filter-data))))
(defn- toolbar-view [current-tab unsigned-transactions-count filter-data]
[toolbar/toolbar {:flat? true}
toolbar/default-nav-back
[toolbar/content-title (i18n/label :t/transactions)]
(case current-tab
:transactions-history [toolbar/actions [history-action]]
:transactions-history [toolbar/actions [(history-action (not (all-checked? filter-data)))]]
:unsigned-transactions nil)]) ;; TODO (andrey) implement [unsigned-action unsigned-transactions-count]
@ -106,14 +103,22 @@
;; TODO(yenda) hook with re-frame
(defn- empty-text [s] [react/text {:style transactions.styles/empty-text} s])
(defn filtered-transaction? [transaction filter-data]
;; TODO(jeluard) extend to token when available
(:checked? (some #(when (= (:type transaction) (:id %)) %) (:type filter-data))))
(defn update-transactions [m filter-data]
(update m :data (fn [v] (filter #(filtered-transaction? % filter-data) v))))
(defview history-list []
(letsubs [transactions-history-list [:wallet.transactions/transactions-history-list]
transactions-loading? [:wallet.transactions/transactions-loading?]
error-message [:wallet.transactions/error-message?]]
[react/view {:style styles/flex}
error-message [:wallet.transactions/error-message?]
filter-data [:wallet.transactions/filters]]
[react/view styles/flex
(when error-message
[wallet.views/error-message-view transactions.styles/error-container transactions.styles/error-message])
[list/section-list {:sections transactions-history-list
[list/section-list {:sections (map #(update-transactions % filter-data) transactions-history-list)
:render-fn render-transaction
:empty-component (empty-text (i18n/label :t/transactions-history-empty))
:on-refresh #(re-frame/dispatch [:update-transactions])
@ -128,47 +133,43 @@
;; Filter history
(defn- item-tokens [{:keys [symbol label checked?]}]
(defn- item-filter [{:keys [icon checked? path]} content]
[list/item
[list/item-icon (transaction-type->icon :pending)] ;; TODO(jeluard) add proper token data
[list/item-icon icon]
content
[list/item-checkbox {:checked? checked? :on-value-change #(re-frame/dispatch [:wallet.transactions/filter path %])}]])
#_ ;; TODO(jeluard) Will be used for ERC20 tokens
(defn- item-filter-tokens [{:keys [symbol label checked?]}]
[item-filter {:icon (transaction-type->icon :pending) :checked? checked?} ;; TODO(jeluard) add proper token icon
[list/item-content
[list/item-primary label]
[list/item-secondary symbol]]
[checkbox/checkbox {:checked? true #_checked?}]])
[list/item-secondary symbol]]])
(defn- item-type [{:keys [id label checked?]}]
[list/item
[list/item-icon (transaction-type->icon (keyword id))]
(defn- item-filter-type [{:keys [id label checked?]}]
(let [kid (keyword id)]
[item-filter {:icon (transaction-type->icon kid) :checked? checked? :path {:type kid}}
[list/item-content
[list/item-primary-only label]]
[checkbox/checkbox checked?]])
[list/item-primary-only label]]]))
(def filter-data
[{:title (i18n/label :t/transactions-filter-tokens)
:key :tokens
:renderItem (list/wrap-render-fn item-tokens)
:data [{:symbol "GNO" :label "Gnosis"}
{:symbol "SNT" :label "Status Network Token"}
{:symbol "SGT" :label "Status Genesis Token"}
{:symbol "GOL" :label "Golem"}]}
{:title (i18n/label :t/transactions-filter-type)
(defn- wrap-filter-data [m]
;; TODO(jeluard) Restore tokens filtering once token support is added
[{:title (i18n/label :t/transactions-filter-type)
:key :type
:renderItem (list/wrap-render-fn item-type)
:data [{:id :incoming :label "Incoming"}
{:id :outgoing :label "Outgoing"}
{:id :pending :label "Pending"}
{:id :postponed :label "Postponed"}]}])
:renderItem (list/wrap-render-fn item-filter-type)
:data (:type m)}])
(defview filter-history []
[]
[react/view
(letsubs [filter-data [:wallet.transactions/filters]]
[react/view styles/flex
[toolbar/toolbar {}
[toolbar/nav-clear-text (i18n/label :t/done)]
[toolbar/content-title (i18n/label :t/transactions-filter-title)]
[toolbar/text-action {:handler #(utils/show-popup "TODO" "Select All")}
[toolbar/text-action {:handler #(re-frame/dispatch [:wallet.transactions/filter-all])
:disabled? (all-checked? filter-data)}
(i18n/label :t/transactions-filter-select-all)]]
[react/view {:style styles/flex}
[list/section-list {:sections filter-data}]]])
[list/section-list {:sections (wrap-filter-data filter-data)}]]]))
(defn history-tab [active?]
[react/text {:uppercase? true
@ -195,10 +196,11 @@
(defview transactions []
(letsubs [unsigned-transactions-count [:wallet.transactions/unsigned-transactions-count]
current-tab [:get :view-id]]
current-tab [:get :view-id]
filter-data [:wallet.transactions/filters]]
[react/view {:style styles/flex}
[status-bar/status-bar]
[toolbar-view current-tab unsigned-transactions-count]
[toolbar-view current-tab unsigned-transactions-count filter-data]
[tabs/swipable-tabs tabs-list current-tab true
{:navigation-event :navigation-replace
:tab-style transactions.styles/tab

View File

@ -5,7 +5,6 @@
(def autolink #js {:default #js {}})
(def config #js {:default #js {}})
(def camera #js {:constants #js {}})
(def circle-checkbox #js {})
(def contacts #js {})
(def dialogs #js {})
(def dismiss-keyboard #js {})

View File

@ -5,6 +5,7 @@
[status-im.test.accounts.events]
[status-im.test.wallet.events]
[status-im.test.wallet.transactions.subs]
[status-im.test.wallet.transactions.views]
[status-im.test.profile.events]
[status-im.test.bots.events]
[status-im.test.chat.models.input]
@ -37,6 +38,7 @@
'status-im.test.wallet.events
'status-im.test.bots.events
'status-im.test.wallet.transactions.subs
'status-im.test.wallet.transactions.views
'status-im.test.chat.models.input
'status-im.test.i18n
'status-im.test.utils.utils

View File

@ -0,0 +1,12 @@
(ns status-im.test.wallet.transactions.views
(:require [cljs.test :refer [deftest is testing]]
[status-im.ui.screens.wallet.transactions.views :as views]))
(deftest filtered-transaction?
(is (not (true? (views/filtered-transaction? {:type :inbound} {:type [{:id :outbound :checked? true}]}))))
(is (not (true? (views/filtered-transaction? {:type :inbound} {:type [{:id :inbound :checked? false}]}))))
(is (true? (views/filtered-transaction? {:type :inbound} {:type [{:id :inbound :checked? true}]})))
(is (true? (views/filtered-transaction? {:type :inbound} {:type [{:id :outbound :checked? true} {:id :inbound :checked? true}]}))))
(deftest update-transactions
(is (= {:data '()} (views/update-transactions {:data {:type :inbound}} {:type [{:id :outbound :checked? true}]}))))