Merge pull request #1686 from status-im/feature/wallet-transactions

Implemented all wallet/transactions screens
This commit is contained in:
Oskar Thorén 2017-08-28 09:32:42 +02:00 committed by GitHub
commit f07eff4ab1
13 changed files with 263 additions and 102 deletions

View File

@ -1,2 +1,2 @@
TESTFAIRY_ENABLED=1
WALLET_WIP_ENABLED=0
WALLET_WIP_ENABLED=1

View File

@ -16,9 +16,9 @@
{:flex-direction :row}))
(def action-button
{:flex-basis 0
:flex 1
:align-items :center})
{:flex-direction :row
:justify-content :center
:align-items :center})
(def action-button-center
(merge action-button
@ -27,7 +27,7 @@
:border-right-width 1}))
(def action-button-text
{:font-size 18
{:font-size 15
:font-weight "normal"
:color st/color-white
:padding-horizontal 16

View File

@ -10,15 +10,22 @@
{:flex 1
:flex-direction :column})
(def primary-text-base
{:font-size 17
:color st/color-black})
(def primary-text
{:font-size 20
:color st/color-black
:padding-top 13})
(merge primary-text-base
{:padding-top 12}))
(def primary-text-only
(merge primary-text-base
{:padding-vertical 22}))
(def secondary-text
{:font-size 16
:color st/color-gray4
:padding-top 6})
:padding-top 4})
(def item-icon
{:width 40
@ -29,11 +36,6 @@
(def secondary-action item-icon)
(def action-buttons
{:flex 1
:flex-direction :row
:padding-vertical 12})
(def base-separator
{:height 1
:background-color st/color-gray5
@ -49,7 +51,7 @@
(def section-separator base-separator)
(def section-header
{:margin-vertical 2
:margin-bottom 12
:margin-top 16
:margin-left 16})
{:font-size 14
:margin-vertical 2
:margin-top 16
:margin-left 16})

View File

@ -1,4 +1,21 @@
(ns status-im.components.list.views
"
Wrapper for react-native list components.
(defn render [{:keys [title subtitle]}]
[item
[item-icon :dots_vertical_white]
[item-content title subtitle]
[item-icon :arrow_right_gray]])
[flat-list {:data [{:title \"\" :subtitle \"\"}] :render-fn render}]
[section-list {:sections [{:title :key :unik :data {:title \"\" :subtitle \"\"}}] :render-fn render}]
or with a per-section `render-fn`
[section-list {:sections [{:title \"\" :key :unik :render-fn render :data {:title \"\" :subtitle \"\"}}]}]
"
(:require [reagent.core :as r]
[status-im.components.list.styles :as lst]
[status-im.components.react :as rn]
@ -7,12 +24,34 @@
(def flat-list-class (rn/get-class "FlatList"))
(def section-list-class (rn/get-class "SectionList"))
(defn item
([content] (item nil content))
([left-action content] (item left-action content nil))
([left-action content right-action]
[rn/view {:style lst/item}
left-action
content
right-action]))
(defn item-icon [k]
[rn/icon k lst/item-icon])
(defn item-content
([primary] (item-content primary nil))
([primary secondary] (item-content primary secondary nil))
([primary secondary extra]
[rn/view {:style lst/item-text-view}
[rn/text {:style (if secondary lst/primary-text lst/primary-text-only)} primary]
(when secondary
[rn/text {:style lst/secondary-text :ellipsize-mode "middle" :number-of-lines 1} secondary])
extra]))
(defn- wrap-render-fn [f]
(fn [data]
;; For details on passed data
;; https://facebook.github.io/react-native/docs/sectionlist.html#rendersectionheader
;; https://facebook.github.io/react-native/docs/sectionlist.html#renderitem
(let [{:keys [item index separators]} (js->clj data :keywordize-keys true)]
(r/as-element (f item index separators)))))
(r/as-element (f (js->clj item) index separators)))))
(defn- separator []
[rn/view lst/separator])
@ -21,8 +60,8 @@
[rn/view lst/section-separator])
(defn base-list-props [render-fn empty-component]
(merge {:renderItem (wrap-render-fn render-fn)
:keyExtractor (fn [_ i] i)}
(merge {:keyExtractor (fn [_ i] i)}
(when render-fn {:renderItem (wrap-render-fn render-fn)})
(when p/ios? {:ItemSeparatorComponent (fn [] (r/as-element [separator]))})
; TODO(jeluard) Does not work with our current ReactNative version
(when empty-component {:ListEmptyComponent (r/as-element [empty-component])})))
@ -30,33 +69,34 @@
(defn flat-list
"A wrapper for FlatList.
See https://facebook.github.io/react-native/docs/flatlist.html"
([data render-fn] (flat-list data render-fn {}))
([data render-fn {:keys [empty-component] :as props}]
(if (and (empty? data) empty-component)
;; TODO(jeluard) remove when native :ListEmptyComponent is supported
empty-component
[flat-list-class
(merge (base-list-props render-fn empty-component)
{:data (clj->js data)}
props)])))
[{:keys [data render-fn empty-component] :as props}]
(if (and (empty? data) empty-component)
;; TODO(jeluard) remove when native :ListEmptyComponent is supported
empty-component
[flat-list-class
(merge (base-list-props render-fn empty-component)
{:data (clj->js data)}
props)]))
(defn- wrap-render-section-header-fn [f]
(fn [data]
;; For details on passed data
;; https://facebook.github.io/react-native/docs/sectionlist.html#rendersectionheader
(let [{:keys [isection]} (js->clj data :keywordize-keys true)]
(let [{:keys [section]} (js->clj data :keywordize-keys true)]
(r/as-element (f section)))))
(defn- default-render-section-header [m]
[rn/text {:style lst/section-header} (:title m)])
(defn section-list
"A wrapper for SectionList.
See https://facebook.github.io/react-native/docs/sectionlist.html"
([data render-fn render-section-header-fn] (section-list data render-fn render-section-header-fn {}))
([data render-fn render-section-header-fn {:keys [empty-component] :as props}]
(if (and (empty? data) empty-component)
empty-component
[section-list-class
(merge (base-list-props render-fn empty-component)
{:sections (clj->js data)
:renderSectionHeader (wrap-render-section-header-fn render-section-header-fn)}
(when p/ios? {:SectionSeparatorComponent (fn [] (r/as-element [section-separator]))})
props)])))
[{:keys [sections render-fn empty-component render-section-header-fn] :or {render-section-header-fn default-render-section-header} :as props}]
(if (and (empty? sections) empty-component)
empty-component
[section-list-class
(merge (base-list-props render-fn empty-component)
{:sections (clj->js (map #(if-let [f (:render-fn %)] (assoc % :renderItem (wrap-render-fn f)) %) sections))
:renderSectionHeader (wrap-render-section-header-fn render-section-header-fn)}
(when p/ios? {:SectionSeparatorComponent (fn [] (r/as-element [section-separator]))})
props)]))

View File

@ -77,8 +77,7 @@
(defn text
([t]
(r/as-element [text-class t]))
([{:keys [uppercase?] :as opts
:or {font :default}} t & ts]
([{:keys [uppercase?] :as opts} t & ts]
(r/as-element
(let [ts (cond->> (conj ts t)
uppercase? (map clojure.string/upper-case))]

View File

@ -10,6 +10,7 @@
(def color-blue-transparent "#7099e632")
(def color-black "#000000")
(def color-purple "#a187d5")
(def color-gray-transparent "rgba(0, 0, 0, 0.4)")
(def color-gray "#838c93de")
(def color-gray2 "#8f838c93")
(def color-gray3 "#00000040")

View File

@ -1,15 +1,9 @@
(ns status-im.components.toolbar-new.styles
(:require-macros [status-im.utils.styles :refer [defstyle defnstyle]])
(:require [status-im.components.styles :refer [text1-color
color-white
color-light-gray
color-gray5
color-gray4
color-blue
color-black]]
(:require [status-im.components.styles :as st]
[status-im.utils.platform :as p]))
(def toolbar-background1 color-white)
(def toolbar-background1 st/color-white)
(def toolbar-icon-width 24)
(def toolbar-icon-height 24)
@ -42,7 +36,7 @@
:ios {:align-items :center}})
(defstyle toolbar-title-text
{:color text1-color
{:color st/text1-color
:letter-spacing -0.2
:font-size 17
:ios {:text-align "center"}})
@ -81,7 +75,7 @@
:padding-left 0
:padding-bottom 0
:text-align-vertical :center
:color color-black
:color st/color-black
:ios {:padding-left 8
:padding-top 2
:letter-spacing -0.2}})
@ -93,3 +87,9 @@
(def toolbar-button
{:paddingVertical 16
:paddingHorizontal 12})
(def toolbar-right-action
{:color st/color-blue4
:font-size 17
:margin-right 12})

View File

@ -18,6 +18,10 @@
[rn/view
[rn/image image]]])
(defn text-action [handler title]
[rn/text {:style tst/toolbar-right-action :onPress handler}
title])
(defn toolbar [{:keys [title
nav-action
hide-nav?

View File

@ -50,7 +50,7 @@
{:component-did-update #(when-not (seq transactions) (rf/dispatch [:navigate-back]))
:component-will-unmount #(rf/dispatch [:set-in [:transactions-list-ui-props :confirmed?] false])}
(let [offline? (or (= network-status :offline) (= sync-state :offline))]
[(if platform/ios? rn/keyboard-avoiding-view rn/view) (merge {:behavior :padding} st/transactions-screen)
[rn/keyboard-avoiding-view st/transactions-screen
[status-bar/status-bar {:type :transaction}]
[toolbar-view transactions]
[rn/view {:style st/transactions-screen-content-container}

View File

@ -334,8 +334,15 @@
:transactions-to "To"
:transactions-sign "Sign"
:transactions-sign-all "Sign all"
:transactions-sign-all-text "Sign the transaction by entering your password.\nMake sure that the words above match your secret signing phrase"
:transactions-sign-input-placeholder "Enter your passphrase"
:transactions-sign-all-done "Done"
:transactions-delete "Delete"
:transactions-history "History"
:transactions-unsigned "Unsigned"
:transactions-history-empty "You don't have a history transactions"
:transactions-unsigned-empty "You don't have unsigned transactions"})
:transactions-unsigned-empty "You don't have unsigned transactions"
:transactions-filter-title "Filter History"
:transactions-filter-tokens "Tokens"
:transactions-filter-type "Type"
:transactions-filter-select-all "Select all"})

View File

@ -42,7 +42,7 @@
[status-im.ui.screens.profile.photo-capture.views :refer [profile-photo-capture]]
[status-im.ui.screens.profile.qr-code.views :refer [qr-code-view]]
[status-im.ui.screens.wallet.send.views :refer [send-transaction]]
[status-im.ui.screens.wallet.history.views :refer [wallet-transactions]]))
[status-im.ui.screens.wallet.history.views :as wallet-history]))
(defn validate-current-view
[current-view signed-up?]
@ -58,8 +58,6 @@
(when view-id
(let [current-view (validate-current-view view-id signed-up?)]
(let [component (case current-view
:wallet-transactions-unsigned wallet-transactions
:wallet-transactions-history wallet-transactions
:wallet main-tabs
:wallet-send-transaction send-transaction
:discover main-tabs
@ -105,6 +103,8 @@
:transaction-details transaction-details
:confirmation-success confirmation-success
:contact-list-modal contact-list-modal
:wallet-transactions wallet-transactions
:wallet-transactions wallet-history/transactions
:wallet-transactions-filter wallet-history/filter-history
:wallet-transactions-sign-all wallet-history/sign-all
(throw (str "Unknown modal view: " modal-view)))]
[component])]])]])))))

View File

@ -5,11 +5,6 @@
{:flex 1
:background-color st/color-white})
(def toolbar-right-action
{:color st/color-blue4
:font-size 17
:margin-right 12})
(def main-section
{:flex 1
:position :relative
@ -18,4 +13,51 @@
(def empty-text
{:text-align :center
:margin-top 22
:margin-horizontal 92})
:margin-horizontal 92})
(def action-buttons
{:flex 1
:flex-direction :row
:padding-vertical 12})
(def sign-all-view
{:flex 1
:flex-direction :column
:justify-content :center
:background-color st/color-gray-transparent})
(def sign-all-popup
{:align-self :flex-start
:background-color st/color-white
:margin-horizontal 12
:border-radius 8})
(def sign-all-popup-sign-phrase
{:border-radius 8
:margin-top 12
:margin-horizontal 12
:text-align :center
:padding-vertical 9
:background-color st/color-light-gray})
(def sign-all-popup-text
{:margin-top 8
:margin-horizontal 12})
(def sign-all-actions
{:flex-direction :row
:justify-content :space-between
:margin-horizontal 12
:margin-vertical 20})
(def sign-all-input
{:width 150
:height 38})
(def sign-all-done
{:position :absolute
:right 0
:top 0})
(def sign-all-done-button
{:background-color :transparent})

View File

@ -1,12 +1,13 @@
(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.react :as rn]
[status-im.components.list.styles :as list-st]
[status-im.components.list.views :as list]
[status-im.components.tabs.styles :as tst]
[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 st]
[status-im.utils.utils :as utils]
@ -17,24 +18,19 @@
;; TODO(yenda) implement
(utils/show-popup "TODO" "Sign Transaction"))
(defn on-sign-all-transactions
[m]
;; TODO(yenda) implement
(utils/show-popup "TODO" "Sign All Transactions"))
(defn on-delete-transaction
[m]
;; TODO(yenda) implement
(utils/show-popup "TODO" "Delete Transaction"))
(defn unsigned-action []
[rn/text {:style st/toolbar-right-action :onPress on-sign-all-transactions}
[toolbar/text-action #(rf/dispatch [:navigate-to-modal :wallet-transactions-sign-all])
(i18n/label :t/transactions-sign-all)])
(defn history-action []
;; TODO(jeluard)
[rn/text {:style st/toolbar-right-action}
"AAAAA"])
;; TODO(jeluard) replace with proper icon
[toolbar/text-action #(rf/dispatch [:navigate-to-modal :wallet-transactions-filter])
"History"])
(defn toolbar-view [view-id]
[toolbar/toolbar
@ -48,28 +44,20 @@
:dropdown_white))
(defn action-buttons [m ]
[rn/view {:style list-st/action-buttons}
[rn/view {:style st/action-buttons}
[btn/primary-button {:text (i18n/label :t/transactions-sign) :on-press #(on-sign-transaction m)}]
[btn/secondary-button {:text (i18n/label :t/transactions-delete) :on-press #(on-delete-transaction m)}]])
(defn- unsigned? [state] (= "unsigned" state))
(defn transaction-details [{:keys [to state] {:keys [value symbol]} :content :as m}]
[rn/view {:style list-st/item-text-view}
[rn/text {:style list-st/primary-text} (str value " " symbol)]
[rn/text {:style list-st/secondary-text :ellipsize-mode "middle" :number-of-lines 1} (str (i18n/label :t/transactions-to) " " to)]
(if (unsigned? state)
[action-buttons m])])
(defn render-transaction [m]
[rn/view {:style list-st/item}
[rn/image {:source {:uri :console}
:style list-st/item-icon}]
[transaction-details m]
[rn/icon :forward_gray list-st/secondary-action]])
(defn render-section-header [m]
[rn/text {:style list-st/section-header} (:title m)])
(defn render-transaction [{:keys [to state] {:keys [value symbol]} :content :as m}]
[list/item
[list/item-icon :ok_blue]
[list/item-content
(str value " " symbol) (str (i18n/label :t/transactions-to) " " to)
(if (unsigned? state)
[action-buttons m])]
[list/item-icon :forward_gray]])
(def dummy-transaction-data
[{:to "0x829bd824b016326a401d083b33d092293333a830" :content {:value "0,4909" :symbol "ETH"} :state :unsigned}
@ -94,26 +82,104 @@
[rn/text {:style st/empty-text} s])
(defview history-list []
[list/section-list dummy-transaction-data-sorted render-transaction render-section-header
{:empty-component (empty-text (i18n/label :t/transactions-history-empty))}])
[list/section-list {:sections dummy-transaction-data-sorted
:render-fn render-transaction
:empty-component (empty-text (i18n/label :t/transactions-history-empty))}])
(defview unsigned-list []
[list/flat-list dummy-transaction-data render-transaction
{:empty-component (empty-text (i18n/label :t/transactions-unsigned-empty))}])
[list/flat-list {:data dummy-transaction-data
:render-fn render-transaction
:empty-component (empty-text (i18n/label :t/transactions-unsigned-empty))}])
(def tab-list
[{:view-id :wallet-transactions-unsigned
:title (i18n/label :t/transactions-unsigned)
:screen unsigned-list}
:title (i18n/label :t/transactions-unsigned)
:screen unsigned-list}
{:view-id :wallet-transactions-history
:title (i18n/label :t/transactions-history)
:screen history-list}])
:title (i18n/label :t/transactions-history)
:screen history-list}])
(def tab->index (reduce #(assoc %1 (:view-id %2) (count %1)) {} tab-list))
(defn get-tab-index [view-id]
(get tab->index view-id 0))
;; Sign all
(defview sign-all []
[]
[rn/keyboard-avoiding-view {:style st/sign-all-view}
[rn/view {:style st/sign-all-done}
[btn/primary-button {:style st/sign-all-done-button
:text (i18n/label :t/transactions-sign-all-done)
:on-press #(rf/dispatch [:navigate-back])}]]
[rn/view {:style st/sign-all-popup}
[rn/text {:style st/sign-all-popup-sign-phrase} "one two three"] ;; TODO hook
[rn/text {:style st/sign-all-popup-text} (i18n/label :t/transactions-sign-all-text)]
[rn/view {:style st/sign-all-actions}
[rn/text-input {:style st/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 %)}]]]])
;; Filter history
(defn- token->icon [s]
(case s
"GNO" :dollar_green
:ok_blue))
(defn item-tokens [{:keys [symbol label]}]
[list/item
[list/item-icon (token->icon symbol)]
[list/item-content label symbol]
;; TODO checkbox
])
(defn- type->icon [k]
(case k
"incoming" :dollar_green
"outgoing" :ok_blue
"pending" :dollar_green
"postponed" :ok_blue
:ok_blue))
(defn item-type [{:keys [id label]}]
[list/item
[list/item-icon (type->icon id)]
[list/item-content label]
;; TODO checkbox
])
(def filter-data
[{:title (i18n/label :t/transactions-filter-tokens)
:key :tokens
: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)
:key :type
:render-fn item-type
:data [{:id :incoming :label "Incoming"}
{:id :outgoing :label "Outgoing"}
{:id :pending :label "Pending"}
{:id :postponed :label "Postponed"}]}])
(defview filter-history []
[]
[rn/view
[toolbar/toolbar
;; TODO(jeluard) replace with icon when available and toolbar has been refactored
{:title (i18n/label :t/transactions-filter-title)
:nav-action (act/back #(rf/dispatch [:navigate-back]));; TODO close modal
:custom-action
[toolbar/text-action #(rf/dispatch [:navigate-to-modal :wallet-transactions-filter])
(i18n/label :t/transactions-filter-select-all)]}]
[rn/scroll-view
[list/section-list {:sections filter-data}]]])
;; TODO(jeluard) whole swipe logic
;; extract navigate-tab action (on tap)
@ -135,7 +201,7 @@
(def initial-tab (-> tab-list first :view-id))
(defview wallet-transactions []
(defview transactions []
[]
(let [view-id (r/atom initial-tab)]
[rn/view {:style st/wallet-transactions-container}