send transaction layer 1

This commit is contained in:
Andrey Shovkoplyas 2017-09-27 13:40:50 +03:00 committed by Roman Volosovskyi
parent 62affda881
commit 9246d7df21
35 changed files with 1056 additions and 402 deletions

View File

@ -0,0 +1,3 @@
<svg width="16px" height="8px" viewBox="0 0 16 8">
<path fill="none" d="M0,0 L15.969209,0.0223819541 L9.39011025,6.60148066 C8.60906167,7.38252924 7.34273171,7.38252924 6.56168312,6.60148066 L6.55741249,6.59719708 L0,0 Z"/>
</svg>

After

Width:  |  Height:  |  Size: 234 B

View File

@ -55,3 +55,5 @@
(defn create-value-xy [x y]
(js/ReactNative.Animated.ValueXY. (clj->js {:x x, :y y})))
(def easing js/ReactNative.Easing)

View File

@ -67,6 +67,7 @@
:icons/dropdown (slurp-svg "./resources/icons/dropdown.svg")
:icons/grab (slurp-svg "./resources/icons/grab.svg")
:icons/share (slurp-svg "./resources/icons/share.svg")
:icons/tooltip-triangle (slurp-svg "./resources/icons/tooltip-triangle.svg")
:icons/network (slurp-svg "./resources/icons/network.svg")})
(defn normalize-property-name [n]

View File

@ -106,3 +106,5 @@
(def toolbar-text-action-disabled {:color styles/color-gray7})
(def item-text-white-background {:color styles/color-blue4})
(def ios-content-item {:position :absolute :right 90 :left 90})

View File

@ -7,7 +7,8 @@
[status-im.components.context-menu :as context-menu]
[status-im.components.toolbar-new.actions :as act]
[status-im.components.toolbar-new.styles :as tst]
[status-im.components.icons.vector-icons :as vi]))
[status-im.components.icons.vector-icons :as vi]
[status-im.utils.platform :as platform]))
;; Navigation item
@ -103,10 +104,15 @@
action-items]
[rn/view {:style (merge (tst/toolbar-wrapper background-color flat?) style)}
[rn/view {:style tst/toolbar}
(when platform/ios?
[rn/view tst/ios-content-item
content-item])
(when nav-item
[rn/view {:style (tst/toolbar-nav-actions-container 0)}
nav-item])
content-item
(if platform/ios?
[rn/view st/flex]
content-item)
action-items]
(when-not no-sync-bar? [sync-state-gradient-view/sync-state-gradient-view])]))

View File

@ -127,8 +127,7 @@
(status/discard-transaction id)))))
(register-handler ::transaction-queued
(after #(dispatch [:navigate-to-modal :unsigned-transactions]))
(fn [db [_ {:keys [id message_id args] :as transaction}]]
(fn [{:wallet/keys [send-transaction] :as db} [_ {:keys [id message_id args] :as transaction}]]
(let [{:keys [from to value data gas gasPrice]} args]
(if (transaction-valid? transaction)
(let [transaction {:id id
@ -140,6 +139,9 @@
:gas-price (.toDecimal js/Web3.prototype gasPrice)
:timestamp (time/now-ms)
:message-id message_id}]
(if (:waiting-signal? send-transaction)
(dispatch [:wallet/transaction-queued id])
(dispatch [:navigate-to-modal :unsigned-transactions]))
(assoc-in db [:transactions-queue id] transaction))
db))))
@ -147,7 +149,7 @@
(u/side-effect!
(fn [{:keys [transactions modal]} [_ {:keys [id response]}]]
(let [{:keys [hash error]} response
{:keys [message-id]} (transactions id)]
{:keys [message-id]} (get transactions id)]
(log/debug :parsed-response response)
(when-not (and error (string? error) (not (s/blank? error)))
(if (and message-id (not (s/blank? message-id)))

View File

@ -3,50 +3,52 @@
(def translations
{
;;common
:members-title "Members"
:not-implemented "!not implemented"
:chat-name "Chat name"
:notifications-title "Notifications and sounds"
:offline "Offline"
:search-for "Search for..."
:cancel "Cancel"
:next "Next"
:type-a-message "Type a message..."
:type-a-command "Start typing a command..."
:error "Error"
:unknown-status-go-error "Unknown status-go error"
:node-unavailable "No ethereum node running"
:members-title "Members"
:not-implemented "!not implemented"
:chat-name "Chat name"
:notifications-title "Notifications and sounds"
:offline "Offline"
:search-for "Search for..."
:cancel "Cancel"
:next "Next"
:type-a-message "Type a message..."
:type-a-command "Start typing a command..."
:error "Error"
:unknown-status-go-error "Unknown status-go error"
:node-unavailable "No ethereum node running"
:yes "Yes"
:no "No"
:camera-access-error "To grant the required camera permission, please, go to your system settings and make sure that Status > Camera is selected."
:photos-access-error "To grant the required photos permission, please, go to your system settings and make sure that Status > Photos is selected."
:camera-access-error "To grant the required camera permission, please, go to your system settings and make sure that Status > Camera is selected."
:photos-access-error "To grant the required photos permission, please, go to your system settings and make sure that Status > Photos is selected."
;;drawer
:invite-friends "Invite friends"
:faq "FAQ"
:switch-users "Switch users"
:feedback "Got feedback?\nShake your phone!"
:view-all "View all"
:current-network "Current network"
:invite-friends "Invite friends"
:faq "FAQ"
:switch-users "Switch users"
:feedback "Got feedback?\nShake your phone!"
:view-all "View all"
:current-network "Current network"
;;chat
:is-typing "is typing"
:and-you "and you"
:search-chat "Search chat"
:members {:one "1 member"
:other "{{count}} members"
:zero "no members"}
:members-active {:one "1 member"
:other "{{count}} members"
:zero "no members"}
:public-group-status "Public"
:active-online "Online"
:active-unknown "Unknown"
:available "Available"
:no-messages "No messages"
:suggestions-requests "Requests"
:suggestions-commands "Commands"
:faucet-success "Faucet request has been received"
:faucet-error "Faucet request error"
:is-typing "is typing"
:and-you "and you"
:search-chat "Search chat"
:members {:one "1 member"
:other "{{count}} members"
:zero "no members"}
:members-active {:one "1 member"
:other "{{count}} members"
:zero "no members"}
:public-group-status "Public"
:active-online "Online"
:active-unknown "Unknown"
:available "Available"
:no-messages "No messages"
:suggestions-requests "Requests"
:suggestions-commands "Commands"
:faucet-success "Faucet request has been received"
:faucet-error "Faucet request error"
;;sync
:sync-in-progress "Syncing..."
@ -275,6 +277,7 @@
:sign-in-to-status "Sign in to Status"
:sign-in "Sign in"
:wrong-password "Wrong password"
:enter-password "Enter password"
;;recover
:recover-from-passphrase "Recover from passphrase"
@ -356,6 +359,7 @@
:wallet-add-asset "Add asset"
:wallet-total-value "Total value"
:wallet-settings "Wallet Settings"
:signing-phrase-description "Sign the transaction by entering your password. Make sure that the words above match your secret signing phrase"
:request-transaction "Request Transaction"
:send-request "Send request"
:share "Share"
@ -364,6 +368,8 @@
:transaction-details "Transaction details"
:transactions-sign "Sign"
:transactions-sign-all "Sign all"
:transactions-sign-transaction "Sign Transaction"
:transactions-sign-later "Sign Later"
: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 password"
:transactions-history "History"
@ -374,15 +380,26 @@
:transactions-filter-tokens "Tokens"
:transactions-filter-type "Type"
:transactions-filter-select-all "Select all"
:view-transaction-details "View Transaction Details"
:transaction-description "If you want to be sure you transaction will not be compromised wait until it gets at least 10 block confirmations"
:transaction-sent "Transaction Sent"
:transaction-moved-text "The transaction has ben successfully moved to “Unsigned”"
:transaction-moved-title "Transaction Moved"
:sign-later-title "Sign Transaction Later?"
:sign-later-text "You will be able to sign in in the transaction history"
:not-applicable "Not applicable for unsigned transactions"
;; Wallet Send
:wallet-send-transaction "Send a Transaction"
:wallet-send-step-one "Step 1 of 3"
:wallet-send-transaction "Send Transaction"
:wallet-send-step "Step {{step}} of {{number}}"
:wallet-choose-recipient "Choose Recipient"
:wallet-choose-from-contacts "Choose From Contacts"
:wallet-address-from-clipboard "Use Address From Clipboard"
:wallet-browse-photos "Browse Photos"
:validation-amount-invalid-number "Amount is not valid number"
:validation-amount-is-too-precise "Amount is too precise. The smallest unit you can send is 1 Wei (1x10^-18 ETH)"
;network settings
:new-network "New network"

View File

@ -112,6 +112,7 @@
:my-profile/profile
:my-profile/default-name
:wallet/request-transaction
:wallet/send-transaction
:networks/selected-network
:networks/networks
:node/after-start

View File

@ -11,6 +11,7 @@
status-im.ui.screens.wallet.subs
status-im.ui.screens.wallet.transactions.subs
status-im.ui.screens.wallet.send.subs
status-im.ui.screens.wallet.request.subs
status-im.ui.screens.network-settings.subs
status-im.transactions.subs
status-im.bots.subs))

View File

@ -43,9 +43,12 @@
[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.choose-recipient.views :refer [choose-recipient]]
[status-im.ui.screens.wallet.request.views :refer [request-transaction]]
[status-im.ui.screens.wallet.wallet-list.views :refer [wallet-list-screen]]
[status-im.ui.screens.wallet.transactions.views :as wallet-transactions]
[status-im.ui.screens.wallet.send.transaction-sent.views :refer [transaction-sent]]
[status-im.components.status-bar :as status-bar]
[status-im.ui.screens.network-settings.views :refer [network-settings]]
[status-im.ui.screens.network-settings.add-rpc.views :refer [add-rpc-url]]
@ -69,6 +72,8 @@
(:wallet :chat-list :discover :contact-list) main-tabs
:wallet-list wallet-list-screen
:wallet-send-transaction send-transaction
:wallet-transaction-sent transaction-sent
:choose-recipient choose-recipient
:wallet-request-transaction request-transaction
:wallet-transactions wallet-transactions/transactions
:wallet-transaction-details wallet-transactions/transaction-details

View File

@ -0,0 +1,124 @@
(ns status-im.ui.screens.wallet.choose-recipient.styles
(:require-macros [status-im.utils.styles :refer [defnstyle defstyle]])
(:require [status-im.components.styles :as styles]))
(def wallet-container
{:flex 1
:background-color styles/color-blue4})
(def toolbar-buttons-container
{:flex-direction :row
:flex-shrink 1
:justify-content :space-between
:width 68
:margin-right 12})
(def choose-recipient-container
{:flex-direction :row
:padding-top 20
:padding-bottom 20
:justify-content :center})
(def choose-recipient-label
{:color :white})
(defstyle recipient-buttons
{:flex-direction :column
:margin-horizontal 28
:margin-vertical 20
:border-radius 8
:ios {:background-color styles/color-blue6}})
(def recipient-icon {:margin-right 20})
(def recipient-icon-disabled {:margin-right 20
:opacity 0.3})
(def recipient-button
{:flex-direction :row
:justify-content :space-between
:margin-vertical 10
:margin-left 20})
(def recipient-button-text
{:color :white
:align-self :center
:font-size 14})
(def recipient-button-text-disabled
(merge recipient-button-text {:color "rgba(255, 255, 255, 0.3)"}))
(defnstyle recipient-touchable [divider?]
(cond-> {:border-color styles/color-gray-transparent-light}
divider? (assoc :ios {:border-bottom-width 1})))
(def recipient-touchable-disabled
{:background-color styles/color-blue4
:border-bottom-left-radius 8
:border-bottom-right-radius 8
:border-left-width 1
:border-bottom-width 1
:border-right-width 1
:border-color "rgba(255, 255, 255, 0.3)"})
(def qr-container
{:flex 1})
(def preview
{:flex 1
:justify-content :flex-end
:align-items :center})
(def corner-dimensions
{:position :absolute
:width 40
:height 40})
(defn corner-left-bottom [dimension]
(let [viewport-offset 0.1666]
(merge corner-dimensions {:bottom (* viewport-offset dimension)
:left (* viewport-offset dimension)})))
(defn corner-right-bottom [dimension]
(let [viewport-offset 0.1666]
(merge corner-dimensions {:right (* viewport-offset dimension)
:bottom (* viewport-offset dimension)})))
(defn corner-left-top [dimension]
(let [viewport-offset 0.1666]
(merge corner-dimensions {:top (* viewport-offset dimension)
:left (* viewport-offset dimension)})))
(defn corner-right-top [dimension]
(let [viewport-offset 0.1666]
(merge corner-dimensions {:top (* viewport-offset dimension)
:right (* viewport-offset dimension)})))
(def viewfinder-port {:position :absolute
:left 0
:top 0
:bottom 0
:right 0
:flex 1})
(defn viewfinder-translucent [height width side]
(let [viewport-offset 0.1666
height-offset (* viewport-offset height)
width-offset (* viewport-offset width)]
(cond-> {:position :absolute
:background-color :black
:opacity 0.7}
(= :top side) (assoc :height height-offset
:width width)
(= :right side) (assoc :height (- height height-offset)
:width width-offset
:bottom 0
:right 0)
(= :bottom side) (assoc :height height-offset
:width (- width width-offset)
:bottom 0
:left 0)
(= :left side) (assoc :height (- height (* 2 height-offset))
:width width-offset
:top height-offset
:left 0))))

View File

@ -0,0 +1,95 @@
(ns status-im.ui.screens.wallet.choose-recipient.views
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [re-frame.core :as re-frame]
[status-im.utils.utils :as utils]
[status-im.components.toolbar-new.view :as toolbar]
[status-im.components.toolbar-new.actions :as act]
[status-im.i18n :as i18n]
[status-im.ui.screens.wallet.styles :as wallet.styles]
[status-im.components.react :as react]
[status-im.components.icons.vector-icons :as vector-icons]
[status-im.ui.screens.wallet.choose-recipient.styles :as styles]
[status-im.components.status-bar :as status-bar]
[status-im.components.camera :as camera]
[clojure.string :as string]))
(defn- show-not-implemented! []
(utils/show-popup "TODO" "Not implemented yet!"))
(defn choose-from-contacts []
(re-frame/dispatch [:navigate-to-modal
:contact-list-modal
{:handler #(re-frame/dispatch [:wallet-open-send-transaction (:address %1) (:name %1)])
:action :send
:params {:hide-actions? true}}]))
(defn toolbar-view []
[toolbar/toolbar2 {:style wallet.styles/toolbar
:no-sync-bar? true}
[toolbar/nav-button (act/back-white act/default-handler)]
[toolbar/content-title {:color :white} (i18n/label :t/wallet-choose-recipient)]
[toolbar/actions [{:icon :icons/flash-active
:icon-opts {:color :white}
:handler show-not-implemented!}]]])
(defn recipient-buttons []
[react/view {:style styles/recipient-buttons}
[react/touchable-highlight {:style (styles/recipient-touchable true)
:on-press choose-from-contacts}
[react/view {:style styles/recipient-button}
[react/text {:style styles/recipient-button-text}
(i18n/label :t/wallet-choose-from-contacts)]
[vector-icons/icon :icons/qr {:color :white
:container-style styles/recipient-icon}]]]
[react/touchable-highlight {:style (styles/recipient-touchable true)
:on-press #(react/get-from-clipboard
(fn [clipboard]
(re-frame/dispatch [:choose-recipient clipboard])))}
[react/view {:style styles/recipient-button}
[react/text {:style styles/recipient-button-text}
(i18n/label :t/wallet-address-from-clipboard)]
[vector-icons/icon :icons/copy-from {:color :white
:container-style styles/recipient-icon}]]]
[react/touchable-highlight {:style styles/recipient-touchable-disabled}
[react/view {:style styles/recipient-button}
[react/text {:style styles/recipient-button-text-disabled}
(i18n/label :t/wallet-browse-photos)]
[vector-icons/icon :icons/browse {:color :white
:container-style styles/recipient-icon-disabled}]]]])
(defn viewfinder [{:keys [height width]}]
(let [min-dimension (min height width)]
[react/view {:style styles/viewfinder-port}
[react/view {:style (styles/viewfinder-translucent height width :top)}]
[react/view {:style (styles/viewfinder-translucent height width :right)}]
[react/view {:style (styles/viewfinder-translucent height width :bottom)}]
[react/view {:style (styles/viewfinder-translucent height width :left)}]
[react/image {:source {:uri :corner_left_top}
:style (styles/corner-left-top min-dimension)}]
[react/image {:source {:uri :corner_right_top}
:style (styles/corner-right-top min-dimension)}]
[react/image {:source {:uri :corner_left_bottom}
:style (styles/corner-left-bottom min-dimension)}]
[react/image {:source {:uri :corner_right_bottom}
:style (styles/corner-right-bottom min-dimension)}]]))
(defview choose-recipient []
(letsubs [camera-dimensions [:camera-dimensions]]
[react/view {:style styles/wallet-container}
[status-bar/status-bar {:type :wallet}]
[toolbar-view]
[react/view {:style styles/qr-container
:on-layout #(let [layout (.. % -nativeEvent -layout)]
(re-frame/dispatch [:set-in [:wallet :camera-dimensions]
{:width (.-width layout)
:height (.-height layout)}]))}
[camera/camera {:style styles/preview
:aspect :fill
:captureAudio false
:onBarCodeRead (fn [code]
(let [data (-> code
.-data
(string/replace #"ethereum:" ""))]
(re-frame/dispatch [:choose-recipient data])))}]
[viewfinder camera-dimensions]]
[recipient-buttons]]))

View File

@ -0,0 +1,12 @@
(ns status-im.ui.screens.wallet.components.animations
(:require [status-im.components.animation :as animation]))
(defn animate-tooltip [bottom-value opacity-value]
(fn []
(animation/start
(animation/parallel
[(animation/timing opacity-value {:toValue 1
:duration 500})
(animation/timing bottom-value {:toValue 8.5
:easing (.bezier animation/easing 0.685, 0.000, 0.025, 1.185)
:duration 500})]))))

View File

@ -12,15 +12,23 @@
:height 52
:letter-spacing -0.2})
(def label
{:color :white
:font-size 14
:line-height 16
:letter-spacing -0.2})
(defstyle label
{:color :white
:ios {:font-size 14
:line-height 16
:letter-spacing -0.2}
:android {:font-size 12
:line-height 12}})
(def amount-text-input-container
{:margin-top 8})
(def label-transparent
(merge label
{:color styles/color-white-transparent}))
(defnstyle amount-container [active?]
{:margin-top 8
:height 52
{:height 52
:background-color (if active?
styles/color-white-transparent-4
styles/color-white-transparent-3)
@ -42,31 +50,100 @@
:justify-content :center})
(defstyle currency-container
{:margin-top 8
{:margin-top 8
:height 52
;;TODO disabled
:border-width 1
:border-color styles/color-white-transparent-4
;:background-color styles/color-white-transparent-3
:justify-content :center
:padding 14
:ios {:border-radius 8}
:android {:border-radius 4}})
(defstyle recipient-container
{:flex-direction :row
:flex 1
:margin-top 8
:height 52
:align-items :center
:background-color styles/color-white-transparent-3
:justify-content :center
:padding 14
:padding-vertical 14
:padding-left 14
:padding-right 8
:ios {:border-radius 8}
:android {:border-radius 4}})
(defstyle wallet-container
{:flex-direction :row
:margin-top 8
:height 52
:background-color styles/color-white-transparent-3
:align-items :center
:padding 14
:ios {:border-radius 8}
:android {:border-radius 4}})
{:flex-direction :row
:margin-top 8
:height 52
;;TODO disabled
:border-width 1
:border-color styles/color-white-transparent-4
;:background-color styles/color-white-transparent-3
:align-items :center
:padding 14
:ios {:border-radius 8}
:android {:border-radius 4}})
(def wallet-name
{:color :white
:font-size 15
:letter-spacing -0.2})
(defn participant [address?]
{:color (if address? :white styles/color-white-transparent)
:flex-shrink 1
:font-size 15
:letter-spacing -0.2})
(def wallet-value
{:padding-left 6
:color styles/color-white-transparent-5
:font-size 15
:letter-spacing -0.2})
(def separator
{:height 1
:margin-horizontal 15
:background-color styles/color-white-transparent-1
:margin-top 16})
(def button-text
{:color :white
:font-size 15
:letter-spacing -0.2})
(def tooltip-container
{:position :absolute
:align-items :center
:left 0
:right 0
:top 0})
(defn tooltip-animated [bottom-value opacity-value]
{:position :absolute
:align-items :center
:left 0
:right 0
:bottom bottom-value
:opacity opacity-value})
(def tooltip-text-container
{:padding-horizontal 16
:padding-vertical 9
:background-color :white
:border-radius 8})
(def tooltip-text
{:color styles/color-red-2
:font-size 15})
(def tooltip-triangle
{:width 16
:height 8
:viewBox "0 0 16 8"})
(def recipient-name-container
{:padding-right 6})

View File

@ -1,29 +1,48 @@
(ns status-im.ui.screens.wallet.components.views
(:require-macros [status-im.utils.views :as views])
(:require [status-im.components.react :as react]
[status-im.components.styles :as components.styles]
[status-im.ui.screens.wallet.components.styles :as styles]
[status-im.i18n :as i18n]
[reagent.core :as reagent]))
[reagent.core :as reagent]
[status-im.components.icons.vector-icons :as vector-icons]
[status-im.components.animation :as animation]
[status-im.utils.platform :as platform]
[status-im.ui.screens.wallet.components.animations :as animations]))
(views/defview tooltip [label & [style]]
(views/letsubs [bottom-value (animation/create-value 16)
opacity-value (animation/create-value 0)]
{:component-did-mount (animations/animate-tooltip bottom-value opacity-value)}
[react/view styles/tooltip-container
[react/animated-view {:style (merge (styles/tooltip-animated bottom-value opacity-value) style)}
[react/view styles/tooltip-text-container
[react/text {:style styles/tooltip-text} label]]
[vector-icons/icon :icons/tooltip-triangle {:color :white :style styles/tooltip-triangle}]]]))
(defn amount-input []
(let [active? (reagent/atom false)]
(fn [& [{:keys [input-options style error]}]]
(let [{:keys [on-focus on-blur]} input-options]
[react/view components.styles/flex
[react/text {:style styles/label} (i18n/label :t/amount)]
[react/view styles/amount-text-input-container
[react/view (merge (styles/amount-container @active?) style)
[react/text-input
(merge
{:keyboard-type :numeric
:placeholder "0.000"
:placeholder-text-color "#ffffff66"
:selection-color :white
:style styles/text-input
:on-focus #(do (reset! active? true)
(when on-focus (on-focus)))
:on-blur #(do (reset! active? false)
(when on-blur (on-blur)))}
(dissoc input-options :on-focus :on-blur))]]
(when-not (nil? error)
[tooltip error])]]))))
(defn amount-input [& [{:keys [input-options style]}]]
(let [active? (reagent/atom false)
{:keys [on-focus on-blur]} input-options]
(fn []
[react/view {:flex 1}
[react/text {:style styles/label} (i18n/label :t/amount)]
[react/view (merge (styles/amount-container @active?) style)
[react/text-input
(merge
{:keyboard-type :numeric
:placeholder "0.000"
:placeholder-text-color "#ffffff66"
:selection-color :white
:style styles/text-input
:on-focus #(do (reset! active? true)
(when on-focus (on-focus)))
:on-blur #(do (reset! active? false)
(when on-blur (on-blur)))}
(dissoc input-options :on-focus :on-blur))]]])))
;;TODO (andrey) this should be choose component with the list of currencies
(defn choose-currency [& [style]]
@ -33,6 +52,25 @@
style)
[react/text {:style styles/wallet-name} "ETH"]]])
(defn choose-recipient [{:keys [address name on-press style]}]
(let [address? (and (not (nil? address)) (not= address ""))]
[react/touchable-highlight {:on-press on-press}
[react/view
[react/text {:style styles/label} (i18n/label :t/recipient)]
[react/view (merge styles/recipient-container
style)
(when name
[react/view styles/recipient-name-container
[react/text {:style (styles/participant true)
:number-of-lines 1}
name]])
[react/view components.styles/flex
[react/text {:style (styles/participant (and (not name) address?))
:number-of-lines 1
:ellipsizeMode :middle}
(if address? address "Choose recipient...")]]
[vector-icons/icon :icons/forward {:color :white}]]]]))
;;TODO (andrey) this should be choose component with the list of wallets
(views/defview choose-wallet [& [style]]
(views/letsubs [eth-balance [:eth-balance]]
@ -41,10 +79,21 @@
[react/view (merge styles/wallet-container
style)
[react/text {:style styles/wallet-name} "Main wallet"]
[react/text {:style styles/wallet-value} (str eth-balance " ETH")]]]))
[react/text {:style styles/wallet-value
:number-of-lines 1
:ellipsizeMode :middle}
(str eth-balance " ETH")]]]))
(defn network-label
([n] (network-label [{} n]))
([style n] [react/view (merge styles/network-container
style)
[react/text {:style styles/network} n]]))
[react/text {:style styles/network} n]]))
(defn separator []
[react/view styles/separator])
(defn button-text [label]
[react/text {:style styles/button-text
:font (if platform/android? :medium :default)
:uppercase? (get-in platform/platform-specific [:uppercase?])} label])

View File

@ -1,6 +1,10 @@
(ns status-im.ui.screens.wallet.db
(:require [cljs.spec.alpha :as spec]
status-im.ui.screens.wallet.request.db))
status-im.ui.screens.wallet.request.db
status-im.ui.screens.wallet.send.db
[status-im.i18n :as i18n]
[clojure.string :as string]))
;; (angusiguess) If we add more error types we can treat them as 'one-of' the following
(spec/def :wallet/error #{:error})
@ -15,3 +19,20 @@
;; TODO(oskarth): spec for balance as BigNumber
;; TODO(oskarth): Spec for prices as as: {:from ETH, :to USD, :price 290.11, :last-day 304.17}
(defn get-amount-validation-error [amount web3]
(let [amount' (string/replace amount #"," ".")
amount-splited (string/split amount' #"[.]")]
(cond
(or (nil? amount) (= amount "") (= amount "0") (re-matches #"0[,.]0*$" amount))
nil
(or (js/isNaN (js/parseFloat amount'))
(try (when (<= (.toWei web3 amount' "ether") 0) true)
(catch :default err true)))
(i18n/label :t/validation-amount-invalid-number)
(and (= (count amount-splited) 2) (> (count (last amount-splited)) 18))
(i18n/label :t/validation-amount-is-too-precise)
:else nil)))

View File

@ -3,9 +3,6 @@
(:require [status-im.components.styles :as styles]
[status-im.utils.platform :as platform]))
(def wallet-container
{:flex 1})
(def error-container
{:align-self :center
:justify-content :center
@ -19,12 +16,9 @@
:padding-right 10
:font-size 13})
(def toolbar
{:background-color (if platform/ios? styles/color-blue4 styles/color-blue5)
:elevation 0})
(def toolbar-title-container
{:flex-direction :row})
{:flex-direction :row
:padding-left 24})
(def toolbar-title-text
{:color styles/color-white
@ -59,21 +53,21 @@
:color styles/color-white})
(defstyle total-balance-currency
{:font-size 37
:margin-left 9
:color styles/color-white-transparent-5
:android {:letter-spacing 1.5}
:ios {:letter-spacing 1.16}})
{:font-size 37
:margin-left 9
:color styles/color-white-transparent-5
:android {:letter-spacing 1.5}
:ios {:letter-spacing 1.16}})
(def value-variation
{:flex-direction :row
:align-items :center})
(defstyle value-variation-title
{:font-size 14
:color styles/color-white-transparent-6
:android {:letter-spacing -0.18}
:ios {:letter-spacing -0.2}})
{:font-size 14
:color styles/color-white-transparent-6
:android {:letter-spacing -0.18}
:ios {:letter-spacing -0.2}})
(def today-variation-container
{:border-radius 100
@ -101,9 +95,9 @@
{:color styles/color-red-4}))
(defstyle buttons
{:margin-top 34
:android {:margin-horizontal 21}
:ios {:margin-horizontal 29}})
{:margin-top 34
:android {:margin-horizontal 21}
:ios {:margin-horizontal 29}})
(defstyle main-button-text
{:padding-vertical 13

View File

@ -6,7 +6,6 @@
[status-im.components.drawer.view :as drawer]
[status-im.components.list.views :as list]
[status-im.components.react :as react]
[status-im.components.styles :as styles]
[status-im.components.icons.vector-icons :as vi]
[status-im.components.toolbar-new.view :as toolbar]
[status-im.components.toolbar-new.actions :as act]
@ -15,7 +14,9 @@
[status-im.utils.config :as config]
[status-im.utils.utils :as utils]
[status-im.utils.platform :as platform]
[status-im.ui.screens.wallet.main.styles :as wallet.styles]
[status-im.ui.screens.wallet.main.styles :as styles]
[status-im.ui.screens.wallet.styles :as wallet.styles]
[status-im.components.styles :as components.styles]
[status-im.ui.screens.wallet.views :as wallet.views]))
(defn- show-not-implemented! []
@ -23,18 +24,18 @@
(defn toolbar-title []
[react/touchable-highlight {:on-press #(rf/dispatch [:navigate-to :wallet-list])}
[react/view {:style wallet.styles/toolbar-title-container}
[react/text {:style wallet.styles/toolbar-title-text
[react/view {:style styles/toolbar-title-container}
[react/text {:style styles/toolbar-title-text
:font :toolbar-title}
(i18n/label :t/main-wallet)]
[vi/icon
:icons/dropdown
{:container-style wallet.styles/toolbar-title-icon
{:container-style styles/toolbar-title-icon
:color :white}]]])
(def transaction-history-action
{:icon :icons/transaction-history
:icon-opts (merge {:color :white :style {:viewBox "-108 65.9 24 24"}} wallet.styles/toolbar-icon)
:icon-opts (merge {:color :white :style {:viewBox "-108 65.9 24 24"}} styles/toolbar-icon)
:handler #(rf/dispatch [:navigate-to :wallet-transactions])})
(defn toolbar-view []
@ -49,27 +50,27 @@
(defn- change-display [change]
(let [pos-change? (or (pos? change) (zero? change))]
[react/view {:style (if pos-change?
wallet.styles/today-variation-container-positive
wallet.styles/today-variation-container-negative)}
styles/today-variation-container-positive
styles/today-variation-container-negative)}
[react/text {:style (if pos-change?
wallet.styles/today-variation-positive
wallet.styles/today-variation-negative)}
styles/today-variation-positive
styles/today-variation-negative)}
(if change
(str (when pos-change? "+") change "%")
"-%")]]))
(defn main-section [usd-value change error-message]
[react/view {:style wallet.styles/main-section}
(when error-message [wallet.views/error-message-view wallet.styles/error-container wallet.styles/error-message])
[react/view {:style wallet.styles/total-balance-container}
[react/view {:style wallet.styles/total-balance}
[react/text {:style wallet.styles/total-balance-value} usd-value]
[react/text {:style wallet.styles/total-balance-currency} "USD"]]
[react/view {:style wallet.styles/value-variation}
[react/text {:style wallet.styles/value-variation-title}
[react/view {:style styles/main-section}
(when error-message [wallet.views/error-message-view styles/error-container styles/error-message])
[react/view {:style styles/total-balance-container}
[react/view {:style styles/total-balance}
[react/text {:style styles/total-balance-value} usd-value]
[react/text {:style styles/total-balance-currency} "USD"]]
[react/view {:style styles/value-variation}
[react/text {:style styles/value-variation-title}
(i18n/label :t/wallet-total-value)]
[change-display change]]
[btn/buttons {:style wallet.styles/buttons :button-text-style wallet.styles/main-button-text}
[btn/buttons {:style styles/buttons :button-text-style styles/main-button-text}
[{:text (i18n/label :t/wallet-send)
:on-press #(do (rf/dispatch [:navigate-to :wallet-send-transaction])
(when platform/android?
@ -83,15 +84,15 @@
(defn- token->image [id]
(case id
"eth" {:source (:ethereum resources/assets) :style (wallet.styles/asset-border styles/color-gray-transparent-light)}))
"eth" {:source (:ethereum resources/assets) :style (styles/asset-border components.styles/color-gray-transparent-light)}))
(defn add-asset []
[list/touchable-item show-not-implemented!
[react/view
[list/item
[list/item-icon {:icon :icons/add :style wallet.styles/add-asset-icon :icon-opts {:color :blue}}]
[react/view {:style wallet.styles/asset-item-value-container}
[react/text {:style wallet.styles/add-asset-text}
[list/item-icon {:icon :icons/add :style styles/add-asset-icon :icon-opts {:color :blue}}]
[react/view {:style styles/asset-item-value-container}
[react/text {:style styles/add-asset-text}
(i18n/label :t/wallet-add-asset)]]]]])
(defn render-asset [{:keys [id currency amount]}]
@ -101,9 +102,9 @@
[react/view
[list/item
[list/item-image {:uri :launch_logo}]
[react/view {:style wallet.styles/asset-item-value-container}
[react/text {:style wallet.styles/asset-item-value} (str amount)]
[react/text {:style wallet.styles/asset-item-currency
[react/view {:style styles/asset-item-value-container}
[react/text {:style styles/asset-item-value} (str amount)]
[react/text {:style styles/asset-item-currency
:uppercase? true}
id]]
[list/item-icon {:icon :icons/forward}]]]]
@ -112,17 +113,17 @@
[list/item
(let [{:keys [source style]} (token->image id)]
[list/item-image source style])
[react/view {:style wallet.styles/asset-item-value-container}
[react/text {:style wallet.styles/asset-item-value} (str amount)]
[react/text {:style wallet.styles/asset-item-currency
[react/view {:style styles/asset-item-value-container}
[react/text {:style styles/asset-item-value} (str amount)]
[react/text {:style styles/asset-item-currency
:uppercase? true}
id]]]]
[add-asset]))
(defn asset-section [eth prices-loading? balance-loading?]
(let [assets [{:id "eth" :currency :eth :amount eth}]]
[react/view {:style wallet.styles/asset-section}
[react/text {:style wallet.styles/asset-section-title} (i18n/label :t/wallet-assets)]
[react/view {:style styles/asset-section}
[react/text {:style styles/asset-section-title} (i18n/label :t/wallet-assets)]
[list/flat-list
{:data (conj assets {}) ;; Extra map triggers rendering for add-asset
:render-fn render-asset
@ -138,6 +139,6 @@
error-message [:wallet/error-message?]]
[react/view {:style wallet.styles/wallet-container}
[toolbar-view]
[react/view {:style styles/flex}
[react/view components.styles/flex
[main-section portfolio-value portfolio-change error-message]
[asset-section eth-balance prices-loading? balance-loading?]]]))

View File

@ -15,3 +15,9 @@
(defmethod navigation/preload-data! :wallet-request-transaction
[db _]
(dissoc db :wallet/request-transaction))
(defmethod navigation/preload-data! :wallet-send-transaction
[db [event]]
(if (= event :navigate-back)
db
(dissoc db :wallet/send-transaction)))

View File

@ -3,6 +3,7 @@
(:require [cljs.spec.alpha :as spec]))
(spec/def ::amount (spec/nilable string?))
(spec/def ::amount-error (spec/nilable string?))
(spec/def :wallet/request-transaction (allowed-keys
:opt-un [::amount]))
:opt-un [::amount ::amount-error]))

View File

@ -1,7 +1,7 @@
(ns status-im.ui.screens.wallet.request.events
(:require
[re-frame.core :as re-frame :refer [dispatch reg-fx]]
[status-im.utils.handlers :as handlers]))
[status-im.utils.handlers :as handlers]
[status-im.ui.screens.wallet.db :as wallet.db]))
(handlers/register-handler-fx
:wallet-send-request
@ -10,4 +10,11 @@
[:navigate-to-clean :chat-list]
[:chat-with-command whisper-identity :request
{:contact contact
:amount (:amount request-transaction)}]]}))
:amount (:amount request-transaction)}]]}))
(handlers/register-handler-fx
:wallet-validate-request-amount
(fn [{{:keys [web3] :wallet/keys [request-transaction] :as db} :db} _]
(let [amount (:amount request-transaction)
error (wallet.db/get-amount-validation-error amount web3)]
{:db (assoc-in db [:wallet/request-transaction :amount-error] error)})))

View File

@ -1,9 +1,6 @@
(ns status-im.ui.screens.wallet.request.styles
(:require [status-im.components.styles :as styles]))
(def main-container
{:flex 1})
(def network-label
{:margin-top 27})
@ -14,44 +11,5 @@
(def qr-container
{:margin-top 16})
(def choose-wallet-container
{:margin-top 27
:margin-horizontal 15})
(def amount-container
{:margin-top 16
:margin-horizontal 15
:flex-direction :row})
(def choose-currency-container
{:margin-left 8})
(def choose-currency
{:width 116})
(def separator
{:height 1
:margin-horizontal 15
:background-color styles/color-white-transparent-1
:margin-top 16})
(def buttons-container
{:margin-vertical 15
:padding-horizontal 12
:flex-direction :row
:align-items :center})
(def share-icon-container
{:margin-right 8})
(def forward-icon-container
{:margin-left 8})
(def button-text
{:color :white
:font-size 15
:letter-spacing -0.2})
(def button-container
{:flex-direction :row
:align-items :center})

View File

@ -0,0 +1,11 @@
(ns status-im.ui.screens.wallet.request.subs
(:require [re-frame.core :as re-frame]))
(re-frame/reg-sub
:wallet.request/request-enabled?
:<- [:get-in [:wallet/request-transaction :amount]]
:<- [:get-in [:wallet/request-transaction :amount-error]]
(fn [[amount amount-error]]
(and
(nil? amount-error)
(not (nil? amount)) (not= amount ""))))

View File

@ -7,7 +7,7 @@
[status-im.components.toolbar-new.actions :as actions]
[status-im.components.toolbar-new.view :as toolbar]
[status-im.components.status-bar :as status-bar]
[status-im.ui.screens.wallet.send.styles :as wallet-styles]
[status-im.ui.screens.wallet.styles :as wallet.styles]
[status-im.components.icons.vector-icons :as vi]
[status-im.ui.screens.wallet.components.views :as components]
[status-im.ui.screens.wallet.request.styles :as styles]
@ -16,7 +16,7 @@
[status-im.utils.platform :as platform]))
(defn toolbar-view []
[toolbar/toolbar2 {:style wallet-styles/toolbar :hide-border? true}
[toolbar/toolbar2 {:style wallet.styles/toolbar :hide-border? true}
[toolbar/nav-button (actions/back-white actions/default-handler)]
[toolbar/content-title {:color :white} (i18n/label :t/request-transaction)]])
@ -36,41 +36,40 @@
:fgColor "#4360df"
:size 256}]))
(defn button-text [label]
[react/text {:style styles/button-text
:font (if platform/android? :medium :default)
:uppercase? (get-in platform/platform-specific [:uppercase?])} label])
(views/defview request-transaction []
;;Because input field is in the end of view we will scroll to the end on input focus event
(views/letsubs [scroll (atom nil)]
[react/keyboard-avoiding-view wallet-styles/wallet-modal-container
(views/letsubs [amount-error [:get-in [:wallet/request-transaction :amount-error]]
request-enabled? [:wallet.request/request-enabled?]
scroll (atom nil)]
[react/keyboard-avoiding-view wallet.styles/wallet-modal-container
[status-bar/status-bar {:type :wallet}]
[toolbar-view]
[react/scroll-view {:ref #(reset! scroll %)}
[react/view styles/main-container
[react/view components.styles/flex
[react/view styles/network-container
;;TODO (andrey) name of active network should be used
[components/network-label styles/network-label "Testnet"]
[react/view styles/qr-container
[qr-code]]]
[react/view styles/choose-wallet-container
[react/view wallet.styles/choose-wallet-container
[components/choose-wallet]]
[react/view styles/amount-container
[react/view wallet.styles/amount-container
[components/amount-input
{:input-options {:on-focus (fn [] (when @scroll (js/setTimeout #(.scrollToEnd @scroll) 100)))
{:error amount-error
:input-options {:on-focus (fn [] (when @scroll (js/setTimeout #(.scrollToEnd @scroll) 100)))
:on-change-text
#(re-frame/dispatch [:set-in [:wallet/request-transaction :amount] %])}}]
[react/view styles/choose-currency-container
[components/choose-currency styles/choose-currency]]]]]
[react/view styles/separator]
[react/view styles/buttons-container
#(do (re-frame/dispatch [:set-in [:wallet/request-transaction :amount] %])
(re-frame/dispatch [:wallet-validate-request-amount]))}}]
[react/view wallet.styles/choose-currency-container
[components/choose-currency wallet.styles/choose-currency]]]]]
[components/separator]
[react/view wallet.styles/buttons-container
[react/touchable-highlight {:on-press #()}
[react/view styles/button-container
[react/view (wallet.styles/button-container false)
[vi/icon :icons/share {:color :white :container-style styles/share-icon-container}]
[button-text (i18n/label :t/share)]]]
[components/button-text (i18n/label :t/share)]]]
[react/view components.styles/flex]
[react/touchable-highlight {:on-press send-request}
[react/view styles/button-container
[button-text (i18n/label :t/send-request)]
[vi/icon :icons/forward {:color :white :container-style styles/forward-icon-container}]]]]]))
[react/touchable-highlight {:on-press (when request-enabled? send-request)}
[react/view (wallet.styles/button-container request-enabled?)
[components/button-text (i18n/label :t/send-request)]
[vi/icon :icons/forward {:color :white :container-style wallet.styles/forward-icon-container}]]]]]))

View File

@ -0,0 +1,11 @@
(ns status-im.ui.screens.wallet.send.animations
(:require [status-im.components.animation :as animation]))
(defn animate-sign-panel [opacity-value bottom-value]
(animation/start
(animation/parallel
[(animation/timing opacity-value {:toValue 1
:duration 500})
(animation/timing bottom-value {:toValue 53
:easing (.bezier animation/easing 0.685, 0.000, 0.025, 1.185)
:duration 500})])))

View File

@ -0,0 +1,17 @@
(ns status-im.ui.screens.wallet.send.db
(:require-macros [status-im.utils.db :refer [allowed-keys]])
(:require [cljs.spec.alpha :as spec]))
(spec/def ::amount (spec/nilable string?))
(spec/def ::to-address (spec/nilable string?))
(spec/def ::to-name (spec/nilable string?))
(spec/def ::amount-error (spec/nilable string?))
(spec/def ::password (spec/nilable string?))
(spec/def ::transaction-id (spec/nilable string?))
(spec/def ::waiting-signal? (spec/nilable boolean?))
(spec/def ::signing? (spec/nilable boolean?))
(spec/def ::later? (spec/nilable boolean?))
(spec/def :wallet/send-transaction (allowed-keys
:opt-un [::amount ::to-address ::to-name ::amount-error ::password
::waiting-signal? ::signing? ::transaction-id ::later?]))

View File

@ -1,8 +1,123 @@
(ns status-im.ui.screens.wallet.send.events
(:require [re-frame.core :as re-frame]
[status-im.ui.screens.wallet.db :as wallet.db]))
[status-im.utils.handlers :as handlers]
[status-im.ui.screens.wallet.db :as wallet.db]
[status-im.native-module.core :as status]
[status-im.utils.types :as types]
[clojure.string :as string]
[status-im.utils.money :as money]
[status-im.utils.utils :as utils]
[status-im.i18n :as i18n]))
(re-frame/reg-event-db
;;;; FX
(re-frame/reg-fx
::accept-transaction
(fn [{:keys [password id on-completed]}]
(status/complete-transactions (list id) password on-completed)))
(re-frame/reg-fx
::send-transaction
(fn [{:keys [web3] :as params}]
(when web3
(.sendTransaction
(.-eth web3)
(clj->js (select-keys params [:from :to :value]))
#()))))
(re-frame/reg-fx
::show-transaction-moved
(fn []
(utils/show-popup (i18n/label :t/transaction-moved-title) (i18n/label :t/transaction-moved-text))))
(re-frame/reg-fx
::discard-transaction
(fn [id]
(status/discard-transaction id)))
;;;; Handlers
(handlers/register-handler-db
:choose-recipient
(fn [db [_ recipient]]
(assoc-in db [:wallet :send :recipient] recipient)))
(handlers/register-handler-fx
:wallet-open-send-transaction
(fn [{db :db} [_ address name]]
{:db (update db :wallet/send-transaction
#(assoc % :to-address address
:to-name name))
:dispatch-n [[:navigate-back]
[:navigate-back]]}))
(handlers/register-handler-fx
:wallet-validate-amount
(fn [{{:keys [web3] :wallet/keys [send-transaction] :as db} :db} _]
(let [amount (:amount send-transaction)
error (wallet.db/get-amount-validation-error amount web3)]
{:db (assoc-in db [:wallet/send-transaction :amount-error] error)})))
(handlers/register-handler-fx
::transaction-completed
(fn [{db :db} [_ {:keys [id response]}]]
(let [{:keys [hash error]} response]
(when-not (and error (string? error) (not (string/blank? error)))
{:db (-> db
(assoc-in [:wallet/send-transaction :transaction-id] nil)
(assoc :wrong-password? false))
:dispatch [:navigate-to :wallet-transaction-sent]}))))
(defn on-transactions-completed [raw-results]
(let [results (:results (types/json->clj raw-results))]
(doseq [result results]
;;TODO (andrey) legacy, should be removed with old transactions screens
(re-frame/dispatch [:transaction-completed {:id (name (key result)) :response (second result)}])
(re-frame/dispatch [::transaction-completed {:id (name (key result)) :response (second result)}]))))
(handlers/register-handler-fx
:wallet/transaction-queued
(fn [{{:wallet/keys [send-transaction] :as db} :db} [_ transaction-id]]
(let [{:keys [later? password]} send-transaction]
(if later?
{:db (-> db
(assoc-in [:wallet/send-transaction :waiting-signal?] false)
(assoc :transactions (:transactions-queue db)))
:dispatch [:navigate-back]
::show-transaction-moved nil}
{:db (assoc-in db [:wallet/send-transaction :transaction-id] transaction-id)
::accept-transaction {:id transaction-id
:password password
:on-completed on-transactions-completed}}))))
(handlers/register-handler-fx
:wallet/sign-transaction
(fn [{{:keys [web3]
:wallet/keys [send-transaction]
:accounts/keys [accounts current-account-id] :as db} :db} [_ later?]]
(let [{:keys [amount transaction-id password]} send-transaction
amount' (money/to-wei (string/replace amount #"," "."))]
(if transaction-id
{::accept-transaction {:id transaction-id
:password password
:on-completed on-transactions-completed}}
{:db (update-in db [:wallet/send-transaction]
#(assoc % :waiting-signal? true
:later? later?))
::send-transaction {:web3 web3
:from (get-in accounts [current-account-id :address])
:to (:to-address send-transaction)
:value amount'}}))))
(handlers/register-handler-fx
:wallet/discard-transaction
(fn [{{:wallet/keys [send-transaction] :as db} :db} _]
(let [{:keys [transaction-id]} send-transaction]
(merge {:db (-> db
(update-in [:wallet/send-transaction]
#(assoc % :signing? false :transaction-id nil))
(assoc :wrong-password? false))}
(when transaction-id
;;TODO (andrey) use ::discard-transaction fx instead
{:dispatch-n [[:deny-transaction transaction-id]
[:status-im.transactions.handlers/remove-transaction transaction-id]]})))))

View File

@ -1,18 +1,9 @@
(ns status-im.ui.screens.wallet.send.styles
(:require-macros [status-im.utils.styles :refer [defnstyle defstyle]])
(:require [status-im.components.styles :as styles]
[status-im.utils.platform :as platform]))
(def wallet-container
{:flex 1
:background-color styles/color-blue4})
(def wallet-modal-container
{:flex 1
:background-color styles/color-blue3})
(:require [status-im.components.styles :as styles]))
(def toolbar
{:background-color styles/color-blue4
{:background-color styles/color-blue5
:elevation 0
:padding-bottom 10})
@ -33,119 +24,40 @@
(def toolbar-title-icon
(merge toolbar-icon {:opacity 0.4}))
(def toolbar-buttons-container
{:flex-direction :row
:flex-shrink 1
:justify-content :space-between
:width 68
:margin-right 12})
(defn animated-sign-panel [bottom-value]
{:position :absolute
:left 12
:right 12
:bottom bottom-value})
(def choose-recipient-container
{:flex-direction :row
:padding-top 20
:padding-bottom 20
:justify-content :center})
(defn sign-panel [opacity-value]
{:opacity opacity-value
:border-radius 8
:background-color :white
:padding-top 12
:padding-horizontal 12})
(def choose-recipient-label
{:color :white})
(def signing-phrase-container
{:border-radius 8
:height 36
:align-items :center
:justify-content :center
:background-color styles/color-light-gray})
(defstyle recipient-buttons
{:flex-direction :column
:margin-horizontal 28
:margin-vertical 20
:border-radius 8
:ios {:background-color styles/color-blue6}})
(def signing-phrase
{:font-size 15
:letter-spacing -0.2
:color :black})
(def recipient-icon {:margin-right 20})
(def signing-phrase-description
{:padding-top 8})
(def recipient-icon-disabled {:margin-right 20
:opacity 0.3})
(def password-container
{:flex 1
:padding-vertical 20})
(def recipient-button
{:flex-direction :row
:justify-content :space-between
:margin-vertical 10
:margin-left 20})
(def recipient-button-text
{:color :white
:align-self :center
:font-size 14})
(def recipient-button-text-disabled
(merge recipient-button-text {:color "rgba(255, 255, 255, 0.3)"}))
(defnstyle recipient-touchable [divider?]
(cond-> {:border-color styles/color-gray-transparent-light}
divider? (assoc :ios {:border-bottom-width 1})))
(def recipient-touchable-disabled
{:background-color styles/color-blue4
:border-bottom-left-radius 8
:border-bottom-right-radius 8
:border-left-width 1
:border-bottom-width 1
:border-right-width 1
:border-color "rgba(255, 255, 255, 0.3)"})
(def qr-container
{:flex 1})
(def preview
{:flex 1
:justify-content :flex-end
:align-items :center})
(def corner-dimensions
{:position :absolute
:width 40
:height 40})
(defn corner-left-bottom [dimension]
(let [viewport-offset 0.1666]
(merge corner-dimensions {:bottom (* viewport-offset dimension)
:left (* viewport-offset dimension)})))
(defn corner-right-bottom [dimension]
(let [viewport-offset 0.1666]
(merge corner-dimensions {:right (* viewport-offset dimension)
:bottom (* viewport-offset dimension)})))
(defn corner-left-top [dimension]
(let [viewport-offset 0.1666]
(merge corner-dimensions {:top (* viewport-offset dimension)
:left (* viewport-offset dimension)})))
(defn corner-right-top [dimension]
(let [viewport-offset 0.1666]
(merge corner-dimensions {:top (* viewport-offset dimension)
:right (* viewport-offset dimension)})))
(def viewfinder-port {:position :absolute
:left 0
:top 0
:bottom 0
:right 0
:flex 1})
(defn viewfinder-translucent [height width side]
(let [viewport-offset 0.1666
height-offset (* viewport-offset height)
width-offset (* viewport-offset width)]
(cond-> {:position :absolute
:background-color :black
:opacity 0.7}
(= :top side) (assoc :height height-offset
:width width)
(= :right side) (assoc :height (- height height-offset)
:width width-offset
:bottom 0
:right 0)
(= :bottom side) (assoc :height height-offset
:width (- width width-offset)
:bottom 0
:left 0)
(= :left side) (assoc :height (- height (* 2 height-offset))
:width width-offset
:top height-offset
:left 0))))
(def password
{:padding 0
:font-size 15
:letter-spacing -0.2
:height 20})

View File

@ -4,3 +4,20 @@
(re-frame/reg-sub :camera-dimensions
(fn [db]
(get-in db [:wallet :camera-dimensions])))
(re-frame/reg-sub
:wallet.send/sign-enabled?
:<- [:get-in [:wallet/send-transaction :amount]]
:<- [:get-in [:wallet/send-transaction :to-address]]
:<- [:get-in [:wallet/send-transaction :amount-error]]
(fn [[amount to-address amount-error]]
(and
(nil? amount-error)
(not (nil? to-address)) (not= to-address "")
(not (nil? amount)) (not= amount ""))))
(re-frame/reg-sub
:wallet.send/sign-password-enabled?
:<- [:get-in [:wallet/send-transaction :password]]
(fn [password]
(and (not (nil? password)) (not= password ""))))

View File

@ -0,0 +1,50 @@
(ns status-im.ui.screens.wallet.send.transaction-sent.styles
(:require-macros [status-im.utils.styles :refer [defnstyle defstyle]]))
(def transaction-sent-container
{:align-items :center})
(def ok-icon-container
{:width 56
:height 56
:border-radius 28
:background-color :white
:align-items :center
:justify-content :center
:margin-top 57
:margin-bottom 16})
(def transaction-sent
{:color :white
:font-size 17})
(def gap
{:height 8})
(def transaction-sent-description
{:color :white
:opacity 0.6
:font-size 14
:text-align :center
:padding-horizontal 16})
(def transaction-details-container
{:height 42
:background-color "#00000033"
:margin-horizontal 16
:opacity 0.2
:align-items :center
:justify-content :center
:border-radius 8})
(def transaction-details
{:color :white
:font-size 15})
(def got-it-container
{:align-items :center
:padding-vertical 18})
(def got-it
{:color :white
:font-size 15})

View File

@ -0,0 +1,38 @@
(ns status-im.ui.screens.wallet.send.transaction-sent.views
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [status-im.components.react :as react]
[status-im.components.status-bar :as status-bar]
[status-im.components.icons.vector-icons :as vi]
[status-im.ui.screens.wallet.styles :as wallet.styles]
[status-im.ui.screens.wallet.send.transaction-sent.styles :as styles]
[status-im.components.styles :as components.styles]
[re-frame.core :as re-frame]
[status-im.ui.screens.wallet.components.views :as components]
[status-im.i18n :as i18n]
[status-im.utils.platform :as platform]))
(defview transaction-sent []
[react/view wallet.styles/wallet-modal-container
[status-bar/status-bar {:type :transparent}]
[react/view styles/transaction-sent-container
[react/view styles/ok-icon-container
[vi/icon :icons/ok {:color components.styles/color-blue4}]]
[react/text {:style styles/transaction-sent
:font (if platform/android? :medium :default)}
(i18n/label :t/transaction-sent)]
[react/view styles/gap]
[react/text {:style styles/transaction-sent-description} (i18n/label :t/transaction-description)]]
[react/view components.styles/flex]
[react/touchable-highlight {:on-press #()}; TODO (andrey) #(re-frame/dispatch [:navigate-to-clean :wallet-transaction-details])}
[react/view styles/transaction-details-container
[react/text {:style styles/transaction-details
:font (if platform/android? :medium :default)
:uppercase? (get-in platform/platform-specific [:uppercase?])}
(i18n/label :t/view-transaction-details)]]]
[components/separator]
[react/touchable-highlight {:on-press #(re-frame/dispatch [:navigate-to-clean :wallet])}
[react/view styles/got-it-container
[react/text {:style styles/got-it
:font (if platform/android? :medium :default)
:uppercase? (get-in platform/platform-specific [:uppercase?])}
(i18n/label :t/got-it)]]]])

View File

@ -4,7 +4,6 @@
[status-im.components.react :as react]
[re-frame.core :as re-frame]
[status-im.components.button.view :as button]
[status-im.components.styles :as styles]
[status-im.components.status-bar :as status-bar]
[status-im.components.toolbar-new.actions :as act]
[status-im.components.toolbar-new.view :as toolbar]
@ -13,77 +12,113 @@
[status-im.i18n :as i18n]
[status-im.ui.screens.wallet.send.styles :as send.styles]
[status-im.components.icons.vector-icons :as vector-icons]
[reagent.core :as r]))
[reagent.core :as r]
[status-im.utils.platform :as platform]
[status-im.ui.screens.wallet.request.styles :as request.styles]
[status-im.ui.screens.wallet.send.styles :as styles]
[status-im.ui.screens.wallet.components.views :as components]
[status-im.components.styles :as components.styles]
[status-im.components.icons.vector-icons :as vi]
[status-im.components.animation :as animation]
[status-im.ui.screens.wallet.styles :as wallet.styles]
[status-im.ui.screens.wallet.send.animations :as send.animations]))
(defn- show-not-implemented! []
(utils/show-popup "TODO" "Not implemented yet!"))
(defn toolbar-view [signing?]
[toolbar/toolbar2 {:style wallet.styles/toolbar}
[toolbar/nav-button (act/back-white (if signing?
#(do (re-frame/dispatch [:wallet/discard-transaction])
(act/default-handler))
act/default-handler))]
[toolbar/content-title {:color :white} (i18n/label :t/send-transaction)]])
(defn toolbar-view []
[toolbar/toolbar2 {:style send.styles/toolbar
:no-sync-bar? true}
[toolbar/nav-button (act/close-white act/default-handler)]
[toolbar/content-title {:color :white} (i18n/label :t/wallet-choose-recipient)]
[toolbar/actions [{:icon :icons/flash-active
:icon-opts {:color :white}
:handler show-not-implemented!}]]])
(defn sign-later []
(utils/show-question
(i18n/label :t/sign-later-title)
(i18n/label :t/sign-later-text)
#(re-frame/dispatch [:wallet/sign-transaction true])))
(defn recipient-buttons []
[react/view {:style send.styles/recipient-buttons}
[react/touchable-highlight {:style (send.styles/recipient-touchable true)}
[react/view {:style send.styles/recipient-button}
[react/text {:style send.styles/recipient-button-text}
(i18n/label :t/wallet-choose-from-contacts)]
[vector-icons/icon :icons/qr {:color :white
:container-style send.styles/recipient-icon}]]]
[react/touchable-highlight {:style (send.styles/recipient-touchable true)
:on-press #(react/get-from-clipboard
(fn [clipboard]
(re-frame/dispatch [:choose-recipient clipboard])))}
[react/view {:style send.styles/recipient-button}
[react/text {:style send.styles/recipient-button-text}
(i18n/label :t/wallet-address-from-clipboard)]
[vector-icons/icon :icons/copy-from {:color :white
:container-style send.styles/recipient-icon}]]]
[react/touchable-highlight {:style send.styles/recipient-touchable-disabled}
[react/view {:style send.styles/recipient-button}
[react/text {:style send.styles/recipient-button-text-disabled}
(i18n/label :t/wallet-browse-photos)]
[vector-icons/icon :icons/browse {:color :white
:container-style send.styles/recipient-icon-disabled}]]]])
(defview sign-panel []
(letsubs [account [:get-current-account]
;;TODO (andrey) use send-transaction map after we remove old transactions ui
wrong-password? [:get :wrong-password?];[:get-in [:wallet/send-transaction :wrong-password?]]
signing-phrase (:signing-phrase @account)
bottom-value (animation/create-value -250)
opacity-value (animation/create-value 0)]
{:component-did-mount #(send.animations/animate-sign-panel opacity-value bottom-value)}
[react/animated-view {:style (styles/animated-sign-panel bottom-value)}
[react/animated-view {:style (styles/sign-panel opacity-value)}
[react/view styles/signing-phrase-container
[react/text {:style styles/signing-phrase} signing-phrase]]
[react/text {:style styles/signing-phrase-description} (i18n/label :t/signing-phrase-description)]
[react/view styles/password-container
[react/text-input
{:auto-focus true
:secure-text-entry true
:placeholder (i18n/label :t/enter-password)
:placeholder-text-color "#939ba1"
:on-change-text #(do
(re-frame/dispatch [:set-in [:wallet/send-transaction :password] %]))
:style styles/password}]]]
(when wrong-password?
[components/tooltip (i18n/label :t/wrong-password)])]))
(defn viewfinder [{:keys [height width]}]
(let [min-dimension (min height width)]
[react/view {:style send.styles/viewfinder-port}
[react/view {:style (send.styles/viewfinder-translucent height width :top)}]
[react/view {:style (send.styles/viewfinder-translucent height width :right)}]
[react/view {:style (send.styles/viewfinder-translucent height width :bottom)}]
[react/view {:style (send.styles/viewfinder-translucent height width :left)}]
[react/image {:source {:uri :corner_left_top}
:style (send.styles/corner-left-top min-dimension)}]
[react/image {:source {:uri :corner_right_top}
:style (send.styles/corner-right-top min-dimension)}]
[react/image {:source {:uri :corner_left_bottom}
:style (send.styles/corner-left-bottom min-dimension)}]
[react/image {:source {:uri :corner_right_bottom}
:style (send.styles/corner-right-bottom min-dimension)}]]))
(defview signing-buttons []
(letsubs [sign-enabled? [:wallet.send/sign-password-enabled?]]
[react/view wallet.styles/buttons-container
[react/touchable-highlight {:on-press #(re-frame/dispatch [:wallet/discard-transaction])}
[react/view (wallet.styles/button-container true)
[components/button-text (i18n/label :t/cancel)]]]
[react/view components.styles/flex]
[react/touchable-highlight {:on-press #(re-frame/dispatch [:wallet/sign-transaction])}
[react/view (wallet.styles/button-container sign-enabled?)
[components/button-text (i18n/label :t/transactions-sign-transaction)]
[vi/icon :icons/forward {:color :white :container-style wallet.styles/forward-icon-container}]]]]))
(defview sign-buttons []
(letsubs [sign-enabled? [:wallet.send/sign-enabled?]]
[react/view wallet.styles/buttons-container
(when sign-enabled?
[react/touchable-highlight {:on-press sign-later}
[react/view (wallet.styles/button-container sign-enabled?)
[components/button-text (i18n/label :t/transactions-sign-later)]]])
[react/view components.styles/flex]
[react/touchable-highlight {:on-press (when sign-enabled? #(re-frame/dispatch [:set-in [:wallet/send-transaction :signing?] true]))}
[react/view (wallet.styles/button-container sign-enabled?)
[components/button-text (i18n/label :t/transactions-sign-transaction)]
[vi/icon :icons/forward {:color :white :container-style wallet.styles/forward-icon-container}]]]]))
(defview send-transaction []
(letsubs [camera-dimensions [:camera-dimensions]]
[react/view {:style send.styles/wallet-container}
[status-bar/status-bar {:type :wallet}]
[toolbar-view]
[react/view {:style send.styles/qr-container
:on-layout #(let [layout (.. % -nativeEvent -layout)]
(re-frame/dispatch [:set-in [:wallet :camera-dimensions]
{:width (.-width layout)
:height (.-height layout)}]))}
[camera/camera {:style send.styles/preview
:aspect :fill
:captureAudio false
:onBarCodeRead (fn [code]
(let [data (-> code
.-data
(str/replace #"ethereum:" ""))]
(re-frame/dispatch [:choose-recipient data])))}]
[viewfinder camera-dimensions]]
[recipient-buttons]]))
(letsubs [amount [:get-in [:wallet/send-transaction :amount]]
amount-error [:get-in [:wallet/send-transaction :amount-error]]
signing? [:get-in [:wallet/send-transaction :signing?]]
to-address [:get-in [:wallet/send-transaction :to-address]]
to-name [:get-in [:wallet/send-transaction :to-name]]]
[react/keyboard-avoiding-view wallet.styles/wallet-modal-container
[react/view components.styles/flex
[status-bar/status-bar {:type :wallet}]
[toolbar-view signing?]
[react/scroll-view {:keyboardShouldPersistTaps :always}
[react/view components.styles/flex
[react/view wallet.styles/choose-participant-container
[components/choose-recipient {:address to-address
:name to-name
:on-press #(re-frame/dispatch [:navigate-to :choose-recipient])}]]
[react/view wallet.styles/choose-wallet-container
[components/choose-wallet]]
[react/view wallet.styles/amount-container
[components/amount-input
{:error amount-error
:input-options {:auto-focus true
:default-value amount
:on-change-text #(do
(re-frame/dispatch [:set-in [:wallet/send-transaction :amount] %])
(re-frame/dispatch [:wallet-validate-amount]))}}]
[react/view wallet.styles/choose-currency-container
[components/choose-currency wallet.styles/choose-currency]]]]]
[components/separator]
(if signing?
[signing-buttons]
[sign-buttons])
(when signing?
[sign-panel])]]))

View File

@ -1,5 +1,15 @@
(ns status-im.ui.screens.wallet.styles
(:require [status-im.components.styles :as st]))
(:require-macros [status-im.utils.styles :refer [defstyle]])
(:require [status-im.components.styles :as st]
[status-im.components.styles :as styles]))
(def wallet-container
{:flex 1})
(defstyle toolbar
{:ios {:background-color styles/color-blue4}
:android {:background-color styles/color-blue5
:elevation 0}})
(def wallet-exclamation-container
{:background-color st/color-red-2
@ -13,3 +23,42 @@
(def wallet-error-exclamation
{:width 16
:height 16})
(def buttons-container
{:margin-vertical 15
:padding-horizontal 12
:flex-direction :row
:align-items :center})
(def forward-icon-container
{:margin-left 8})
(defn button-container [enabled?]
(merge
{:flex-direction :row
:align-items :center}
(when-not enabled?
{:opacity 0.4})))
(def wallet-modal-container
{:flex 1
:background-color styles/color-blue4})
(def choose-participant-container
{:margin-top 16
:margin-horizontal 15})
(def choose-wallet-container
{:margin-top 16
:margin-horizontal 15})
(def amount-container
{:margin-top 16
:margin-horizontal 15
:flex-direction :row})
(def choose-currency-container
{:margin-left 8})
(def choose-currency
{:width 116})

View File

@ -23,6 +23,9 @@
(defn bignumber [n]
(dependencies/Web3.prototype.toBigNumber (str n)))
(defn to-wei [str]
(dependencies/Web3.prototype.toWei str "ether"))
(def eth-units
{:wei (bignumber "1")
:kwei (bignumber "1000")

View File

@ -24,6 +24,18 @@
(when on-cancel {:onPress on-cancel}))
{:text (or s "OK") :onPress on-accept :style "destructive"})))))
(defn show-question
([title content on-accept]
(show-question title content on-accept nil))
([title content on-accept on-cancel]
(.alert (.-Alert rn-dependencies/react-native)
title
content
(clj->js
(vector (merge {:text (i18n/label :t/no)}
(when on-cancel {:onPress on-cancel}))
{:text (i18n/label :t/yes) :onPress on-accept})))))
(defn http-post
([action data on-success]
(http-post action data on-success nil))