Implement wallet/transactions basic skeleton

This commit is contained in:
Julien Eluard 2017-08-11 12:07:10 +02:00 committed by Roman Volosovskyi
parent 69a84c8315
commit 9bacc65c8d
20 changed files with 268 additions and 165 deletions

1
.env
View File

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

View File

@ -6,9 +6,7 @@
animated-view
text
icon
touchable-highlight
list-view
list-item]]
touchable-highlight]]
[status-im.components.chat-icon.screen :refer [chat-icon-view-menu-item]]
[status-im.chat.styles.screen :as st]
[status-im.i18n :refer [label label-pluralize]]

View File

@ -11,42 +11,38 @@
[status-im.ui.screens.discover.views :refer [discover]]
[status-im.ui.screens.contacts.views :refer [contact-groups-list]]
[status-im.ui.screens.wallet.main-screen.views :refer [wallet]]
[status-im.components.tabs.tabs :refer [tabs]]
[status-im.utils.config :as config]
[status-im.components.tabs.views :refer [tabs]]
[status-im.components.tabs.styles :as st]
[status-im.components.styles :as common-st]
[status-im.i18n :refer [label]]
[cljs.core.async :as a]))
(def tab-list
[{:view-id :chat-list
:title (label :t/chats)
:screen chats-list
:icon-inactive :icon_chats
:icon-active :icon_chats_active
:index 0}
{:view-id :discover
:title (label :t/discover)
:screen discover
:icon-inactive :icon_discover
:icon-active :icon_discover_active
:index 1}
{:view-id :contact-list
:title (label :t/contacts)
:screen contact-groups-list
:icon-inactive :icon_contacts
:icon-active :icon_contacts_active
:index 2}
{:view-id :wallet
:title "Wallet"
:screen wallet
:icon-inactive :icon_contacts
:icon-active :icon_contacts_active
:index 3}])
(concat
[{:view-id :chat-list
:title (label :t/chats)
:screen chats-list
:icon-inactive :icon_chats
:icon-active :icon_chats_active}
{:view-id :discover
:title (label :t/discover)
:screen discover
:icon-inactive :icon_discover
:icon-active :icon_discover_active}
{:view-id :contact-list
:title (label :t/contacts)
:screen contact-groups-list
:icon-inactive :icon_contacts
:icon-active :icon_contacts_active}]
(when config/wallet-tab-enabled?
[{:view-id :wallet
:title "Wallet"
:screen wallet
:icon-inactive :icon_contacts
:icon-active :icon_contacts_active}])))
(def tab->index {:chat-list 0
:discover 1
:contact-list 2
:wallet 3})
(def tab->index (reduce #(assoc %1 (:view-id %2) (count %1)) {} tab-list))
(def index->tab (clojure.set/map-invert tab->index))
@ -116,12 +112,9 @@
:loop false
:ref #(reset! main-swiper %)
:on-momentum-scroll-end (on-scroll-end swiped? scroll-ended @view-id)})
[chats-list]
[discover (= @view-id :discover)]
[contact-groups-list (= @view-id :contact-list)]]
;; TODO(oskarth): While wallet is in WIP we hide the wallet component
;;[wallet (= @view-id :wallet)]
(doall
(map-indexed (fn [index {vid :view-id screen :screen}]
^{:key index} [screen (= @view-id vid)]) tab-list))]
[tabs {:selected-view-id @view-id
:prev-view-id @prev-view-id
:tab-list tab-list}]

View File

@ -40,6 +40,7 @@
(def list-view-class (get-class "ListView"))
(def scroll-view (get-class "ScrollView"))
(def flat-list-class (get-class "FlatList"))
(def web-view (get-class "WebView"))
(def keyboard-avoiding-view-class (get-class "KeyboardAvoidingView"))
@ -105,6 +106,20 @@
:resizeMode "contain"
:style style}]))
(defn- wrap-render-fn [f]
(fn [o]
(let [{:keys [item index separators]} (js->clj o :keywordize-keys true)]
(r/as-element (f item index separators)))))
(defn flat-list
"A function wrapping the creation of FlatList.
See https://facebook.github.io/react-native/docs/flatlist.html"
([data render-fn] (flat-list data render-fn {}))
([data render-fn props]
[flat-list-class (merge {:data (clj->js data) :renderItem (wrap-render-fn render-fn) :keyExtractor (fn [_ i] i)} props)]))
;; TODO Migrate to new FlatList and SectionList when appropriate. ListView will eventually get deprecated
;; see https://facebook.github.io/react-native/docs/using-a-listview.html
(defn list-view [props]
[list-view-class (merge {:enableEmptySections true} props)])

View File

@ -1,23 +0,0 @@
(ns status-im.components.tabs.tab
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
[status-im.components.react :refer [view
text
image
touchable-highlight]]
[status-im.utils.platform :as p]
[status-im.components.tabs.styles :as st]))
(defn tab [{:keys [view-id title icon-active icon-inactive selected-view-id prev-view-id]}]
(let [active? (= view-id selected-view-id)
previous? (= view-id prev-view-id)]
[touchable-highlight {:style st/tab
:disabled active?
:onPress #(dispatch [:navigate-to-tab view-id])}
[view {:style st/tab-container}
[view
[image {:source {:uri (if active? icon-active icon-inactive)}
:style st/tab-icon}]]
[view
[text {:style (st/tab-title active?)
:font (if (and p/ios? active?) :medium :regular)}
title]]]]))

View File

@ -1,4 +1,4 @@
(ns status-im.components.tabs.tabs
(ns status-im.components.tabs.views
(:require-macros [status-im.utils.views :refer [defview]])
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
[status-im.components.react :refer [view
@ -9,20 +9,34 @@
touchable-highlight]]
[reagent.core :as r]
[status-im.components.tabs.styles :as st]
[status-im.components.tabs.tab :refer [tab]]
[status-im.components.animation :as anim]
[status-im.utils.platform :refer [platform-specific]]))
[status-im.utils.platform :as p]))
(defn create-tab [index data selected-view-id prev-view-id]
(defn tab [{:keys [view-id title icon-active icon-inactive selected-view-id prev-view-id]}]
(let [active? (= view-id selected-view-id)
previous? (= view-id prev-view-id)]
[touchable-highlight {:style st/tab
:disabled active?
:onPress #(dispatch [:navigate-to-tab view-id])}
[view {:style st/tab-container}
[view
[image {:source {:uri (if active? icon-active icon-inactive)}
:style st/tab-icon}]]
[view
[text {:style (st/tab-title active?)
:font (if (and p/ios? active?) :medium :regular)}
title]]]]))
(defn- create-tab [index data selected-view-id prev-view-id]
(let [data (merge data {:key index
:index index
:selected-view-id selected-view-id
:prev-view-id prev-view-id})]
[tab data]))
(defn tabs-container [& children]
(defn- tabs-container [& children]
(let [tabs-hidden? (subscribe [:tabs-hidden?])
shadows? (get-in platform-specific [:tabs :tab-shadows?])]
shadows? (get-in p/platform-specific [:tabs :tab-shadows?])]
(into [animated-view {:style (merge (st/tabs-container @tabs-hidden?)
(if-not shadows? st/tabs-container-line))
:pointerEvents (if @tabs-hidden? :none :auto)}]
@ -32,12 +46,4 @@
[tabs-container
(into
[view st/tabs-inner-container]
(let [tabs (into [] tab-list)]
[[create-tab 0 (nth tabs 0) selected-view-id prev-view-id]
[create-tab 1 (nth tabs 1) selected-view-id prev-view-id]
[create-tab 2 (nth tabs 2) selected-view-id prev-view-id]
;; WALLET TAB
;;[create-tab 3 (nth tabs 3) selected-view-id prev-view-id]
])
;; todo: figure why it doesn't work on iOS release build
#_(map-indexed #(create-tab %1 %2 selected-view-id prev-view-id) tab-list))])
(map-indexed #(create-tab %1 %2 selected-view-id prev-view-id) tab-list))])

View File

@ -14,6 +14,8 @@
[status-im.accessibility-ids :as id]
[status-im.utils.platform :refer [platform-specific]]))
;; TODO This is our old toolbar component. Please do not use it and consider moving legacy usage to the new toolbar_new component.
(defn toolbar [{title :title
nav-action :nav-action
hide-nav? :hide-nav?

View File

@ -1,5 +1,7 @@
(ns status-im.components.toolbar-new.actions
(:require [status-im.components.toolbar-new.styles :as st]))
(:require [re-frame.core :refer [dispatch]]
[status-im.accessibility-ids :as id]
[status-im.components.toolbar-new.styles :as st]))
(def nothing
{:image {:source nil
@ -25,7 +27,7 @@
:style st/action-default}
:handler handler})
(defn search-icon []
(def search-icon
{:image {:source {:uri :icon_search_dark}
:style (merge st/action-default
{:opacity 0.4})}})
@ -33,7 +35,11 @@
(defn back [handler]
{:image {:source {:uri :icon_back_dark}
:style st/action-default}
:handler handler})
:handler handler
:accessibility-label id/toolbar-back-button})
(def default-back
(back #(dispatch [:navigate-back])))
(defn back-white [handler]
{:image {:source {:uri :icon_back_white}

View File

@ -1,81 +1,70 @@
(ns status-im.components.toolbar-new.view
(:require [re-frame.core :refer [subscribe dispatch]]
[status-im.components.react :refer [view
icon
text
text-input
image
touchable-highlight]]
[status-im.components.react :as rn]
[status-im.components.sync-state.gradient :refer [sync-state-gradient-view]]
[status-im.components.styles :refer [icon-default
icon-search
color-gray4]]
[status-im.components.styles :as st]
[status-im.components.context-menu :refer [context-menu]]
[status-im.components.toolbar-new.actions :as act]
[status-im.components.toolbar-new.styles :as st]
[status-im.accessibility-ids :as id]
[status-im.utils.platform :refer [platform-specific]]
[status-im.components.toolbar-new.styles :as tst]
[reagent.core :as r]))
(defn toolbar [{title :title
nav-action :nav-action
hide-nav? :hide-nav?
actions :actions
custom-action :custom-action
background-color :background-color
custom-content :custom-content
hide-border? :hide-border?
border-style :border-style
title-style :title-style
style :style}]
(let [style (merge (st/toolbar-wrapper background-color) style)]
[view {:style style}
[view st/toolbar
[view (st/toolbar-nav-actions-container actions)
(when-not hide-nav?
(if nav-action
[touchable-highlight {:style st/toolbar-button
:on-press (:handler nav-action)}
[view
[image (:image nav-action)]]]
[touchable-highlight {:style st/toolbar-button
:on-press #(dispatch [:navigate-back])
:accessibility-label id/toolbar-back-button}
[view
[image {:source {:uri :icon_back_dark}
:style icon-default}]]]))]
(or custom-content
[view {:style st/toolbar-title-container}
[text {:style (merge st/toolbar-title-text title-style)
:font :toolbar-title}
title]])
[view (st/toolbar-actions-container (count actions) custom-action)
(if actions
(for [{action-image :image
action-options :options
action-handler :handler} actions]
(with-meta
(cond (= action-image :blank)
[view st/toolbar-action]
(defn nav-button
[{:keys [handler accessibility-label image]}]
[rn/touchable-highlight
(merge {:style tst/toolbar-button
:on-press handler}
(when accessibility-label
{:accessibility-label accessibility-label}))
[rn/view
[rn/image image]]])
action-options
(defn toolbar [{:keys [title
nav-action
hide-nav?
actions
custom-action
background-color
custom-content
hide-border?
border-style
title-style
style]}]
(let [style (merge (tst/toolbar-wrapper background-color) style)]
[rn/view {:style style}
[rn/view tst/toolbar
(when-not hide-nav?
[rn/view (tst/toolbar-nav-actions-container actions)
[nav-button (or nav-action act/default-back)]])
(or custom-content
[rn/view {:style tst/toolbar-title-container}
[rn/text {:style (merge tst/toolbar-title-text title-style)
:font :toolbar-title}
title]])
[rn/view (tst/toolbar-actions-container (count actions) custom-action)
(if actions
(for [{:keys [image options handler]} actions]
(with-meta
(cond (= image :blank)
[rn/view tst/toolbar-action]
options
[context-menu
[view st/toolbar-action [image action-image]]
action-options
[rn/view tst/toolbar-action [rn/image image]]
options
nil
st/toolbar-button]
tst/toolbar-button]
:else
[touchable-highlight {:style st/toolbar-button
:on-press action-handler}
[view st/toolbar-action
[image action-image]]])
{:key (str "action-" action-image)}))
[rn/touchable-highlight {:style tst/toolbar-button
:on-press handler}
[rn/view tst/toolbar-action
[rn/image image]]])
{:key (str "action-" image)}))
custom-action)]]
[sync-state-gradient-view]
(when-not hide-border?
[view (merge st/toolbar-border-container border-style)
[view st/toolbar-border]])]))
[rn/view (merge tst/toolbar-border-container border-style)
[rn/view tst/toolbar-border]])]))
(def search-text-input (r/atom nil))
@ -89,21 +78,21 @@
title
custom-title
on-search-submit]}]
[view st/toolbar-with-search-content
[rn/view tst/toolbar-with-search-content
(if show-search?
[text-input
{:style st/toolbar-search-input
[rn/text-input
{:style tst/toolbar-search-input
:ref #(reset! search-text-input %)
:auto-focus true
:placeholder search-placeholder
:placeholder-text-color color-gray4
:placeholder-text-color st/color-gray4
:on-change-text #(dispatch [:set-in [:toolbar-search :text] %])
:on-submit-editing (when on-search-submit
#(toolbar-search-submit on-search-submit))}]
(or custom-title
[view
[text {:style st/toolbar-title-text
:font :toolbar-title}
[rn/view
[rn/text {:style tst/toolbar-title-text
:font :toolbar-title}
title]]))])
(defn toolbar-with-search [{:keys [show-search?
@ -122,7 +111,7 @@
[(act/close #(do
(.clear @search-text-input)
(dispatch [:set-in [:toolbar-search :text] ""])))]
[(act/search-icon)])
[act/search-icon])
(into [(act/search #(toggle-search-fn search-key))] actions))]
[toolbar {:style style
:nav-action (if show-search?

View File

@ -326,4 +326,8 @@
;;testfairy warning
:testfairy-title "Warning!"
:testfairy-message "You are using app installed from a nightly build. For testing purposes this build includes session recording if wifi connection is used, so all your interaction with app is saved (as video and log) and might be used by development team to investigate possible issues. Saved video/log do not include your passwords. Recording is done only if app is installed from a nightly build. Nothing is recorded if app is installed from PlayStore or TestFlight."})
:testfairy-message "You are using app installed from a nightly build. For testing purposes this build includes session recording if wifi connection is used, so all your interaction with app is saved (as video and log) and might be used by development team to investigate possible issues. Saved video/log do not include your passwords. Recording is done only if app is installed from a nightly build. Nothing is recorded if app is installed from PlayStore or TestFlight."
;; wallet
:transactions "Transactions"
:transactions-sign-all "Sign all"})

View File

@ -41,8 +41,8 @@
[status-im.ui.screens.profile.edit.views :refer [edit-my-profile]]
[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.receive.views :refer [receive-transaction]]
[status-im.ui.screens.wallet.send.views :refer [send-transaction]]
[status-im.ui.screens.wallet.history.views :refer [wallet-transactions]]))
(defn validate-current-view
[current-view signed-up?]
@ -60,7 +60,6 @@
(let [component (case current-view
:wallet main-tabs
:wallet-send-transaction send-transaction
;;:wallet-receive-transaction receive-transaction
:discover main-tabs
:discover-search-results discover-search-results
:chat-list main-tabs
@ -102,5 +101,6 @@
:unsigned-transactions unsigned-transactions
:transaction-details transaction-details
:confirmation-success confirmation-success
:contact-list-modal contact-list-modal)]
:contact-list-modal contact-list-modal
:wallet-transactions wallet-transactions)]
[component])]])]])))))

View File

@ -0,0 +1,46 @@
(ns status-im.ui.screens.wallet.history.styles
(:require-macros [status-im.utils.styles :refer [defnstyle]])
(:require [status-im.components.styles :as common]))
(def wallet-transactions-container
{:flex 1
:background-color common/color-white})
(def toolbar-buttons-container
{:flex-direction :row
:flex-shrink 1
:justify-content :space-between
:width 68
:margin-right 12})
(def item
{:flex-direction :row
:flex 1})
(def item-text-view
{:flex 1
:flex-direction :column})
(def primary-text
{:flex 1
:font-size 16
:color common/color-black})
(def secondary-text
{:font-size 16
:color common/color-gray4})
(def item-icon
{:width 40
:height 40})
(def secondary-action
(merge item-icon {:align-self "flex-end"}))
;;;;;;;;;;;;;;;;;;
;; Main section ;;
;;;;;;;;;;;;;;;;;;
(def main-section
{:padding 16
:background-color common/color-white})

View File

@ -0,0 +1,49 @@
(ns status-im.ui.screens.wallet.history.views
(:require-macros [status-im.utils.views :refer [defview]])
(:require [status-im.components.react :as rn]
[status-im.components.toolbar-new.view :as toolbar]
[status-im.ui.screens.wallet.history.styles :as st]
[status-im.i18n :as i18n]))
(defn unsigned-action []
[rn/view {:style st/toolbar-buttons-container}
[rn/text (i18n/label :t/transactions-sign-all)]])
(defn toolbar-view []
[toolbar/toolbar
{:title (i18n/label :t/transactions)
:title-style {:text-align "center"}
:custom-action [unsigned-action]}])
(defn- icon-status [k]
(case k
:pending :dropdown_white
:dropdown_white))
(defn render-transaction
[item]
[rn/view {:style st/item}
[rn/image {:source {:uri :console}
:style st/item-icon}]
#_
[rn/icon :dropdown-white #_(icon-status (:status item)) st/item-icon]
[rn/view {:style st/item-text-view}
(let [m (:content item)]
[rn/text {:style st/primary-text} (str (:value m) " " (:symbol m))])
[rn/text {:style st/secondary-text} (:to item)]]
[rn/icon :forward_gray st/secondary-action]])
(defn main-section []
[rn/view {:style st/main-section}
(rn/flat-list [{:to "0xAAAAA" :content {:value "5" :symbol "ETH"} :status :pending}
{:from "0xAAAAA" :content {:value "5" :symbol "ETH"} :status :sent}
{:to "0xAAAAA" :content {:value "5" :symbol "ETH"} :status :pending}] render-transaction)])
(defview wallet-transactions []
[]
[rn/view {:style st/wallet-transactions-container}
[toolbar-view]
[rn/scroll-view
[main-section]]])

View File

@ -26,7 +26,7 @@
(defn toolbar-view []
[toolbar/toolbar {:style st/toolbar
:nav-action (act/list-white #(rf/dispatch [:navigate-to-modal :unsigned-transactions]))
:nav-action (act/list-white #(rf/dispatch [:navigate-to-modal :wallet-transactions]))
:custom-content [toolbar-title]
:custom-action [toolbar-buttons]}])

View File

@ -3,7 +3,11 @@
(def config (js->clj (.-default rn-dependencies/config) :keywordize-keys true))
(defn get-config [k]
(get config k))
(defn get-config
([k] (get config k))
([k not-found] (get config k not-found)))
(def testfairy-enabled? (= "1" (get-config :TESTFAIRY_ENABLED)))
(defn enabled? [v] (= "1" v))
(def testfairy-enabled? (enabled? (get-config :TESTFAIRY_ENABLED)))
(def wallet-tab-enabled? (enabled? (get-config :WALLET_TAB_ENABLED 0)))

View File

@ -4,13 +4,13 @@
(.cloneWithRows ds (reduce (fn [ac el] (.push ac el) ac)
(clj->js []) rows)))
(defn data-source [config]
(defn- data-source [config]
(js/ReactNative.ListView.DataSource. (clj->js config)))
(defn to-datasource [items]
(clone-with-rows (data-source {:rowHasChanged not=}) items))
(defn clone-with-rows-inverted [ds rows]
(defn- clone-with-rows-inverted [ds rows]
(let [rows (reduce (fn [ac el] (.push ac el) ac)
(clj->js []) (reverse rows))
row-ids (.reverse (.map rows (fn [_ index] index)))]

View File

@ -1,6 +1,6 @@
(ns status-im.react-native.js-dependencies)
(def action-button #js {})
(def action-button #js {:default #js {:Item #js {}}})
(def android-sms-listener #js {})
(def autolink #js {:default #js {}})
(def config #js {:default #js {}})
@ -29,12 +29,13 @@
#js {:NativeModules #js {}
:Animated #js {:View #js {}
:Text #js {}}
:DeviceEventEmitter #js {:addListener (fn [])}})
:DeviceEventEmitter #js {:addListener (fn [])}
:Dimensions #js {:get (fn [])}})
(def realm #js {:schemaVersion (fn [])
:close (fn [])})
(def sortable-listview #js {})
(def swiper #js {})
(def vector-icons #js {})
(def vector-icons #js {:default #js {}})
(def webview-bridge #js {:default #js {}})

View File

@ -0,0 +1,8 @@
(ns status-im.test.components.main-tabs
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.components.main-tabs :as tabs]))
(deftest tab->index
(is (contains? tabs/tab->index :chat-list))
(is (= 1 (:discover tabs/tab->index))))

View File

@ -2,6 +2,7 @@
(:require [doo.runner :refer-macros [doo-tests]]
[status-im.test.contacts.handlers]
[status-im.test.chat.models.input]
[status-im.test.components.main-tabs]
[status-im.test.handlers]
[status-im.test.utils.utils]
[status-im.test.utils.money]
@ -17,6 +18,7 @@
(doo-tests 'status-im.test.contacts.handlers
'status-im.test.chat.models.input
'status-im.test.components.main-tabs
'status-im.test.handlers
'status-im.test.utils.utils
'status-im.test.utils.money

View File

@ -10,6 +10,8 @@
(def b (atom {:identity "b"}))
(def c (atom {:identity "c"}))
(declare recv!)
;; The network is unreliable.
(defn random-broadcast! [chat-id message]
(when (> (rand-int 10) 5) (recv! a chat-id message))