[ISSUE #2881] Updated wallet / send transaction to latest design

Signed-off-by: Oskar Thoren <ot@oskarthoren.com>
This commit is contained in:
Julien Eluard 2018-01-18 10:27:11 +01:00 committed by Oskar Thoren
parent 1046560498
commit d958c96d67
No known key found for this signature in database
GPG Key ID: 5128AB0637CD85AF
72 changed files with 1126 additions and 1286 deletions

6
package-lock.json generated
View File

@ -7885,9 +7885,9 @@
"integrity": "sha1-v74cRNilsUpvOjpAXYrab1R6UW4="
},
"react-native-popup-menu": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/react-native-popup-menu/-/react-native-popup-menu-0.8.3.tgz",
"integrity": "sha1-HbsLT4iclBC2myKKidV7Vq6lzC4="
"version": "0.12.2",
"resolved": "https://registry.npmjs.org/react-native-popup-menu/-/react-native-popup-menu-0.12.2.tgz",
"integrity": "sha1-ZWi1LPZFZj8GClUPIyQfoVUr+g4="
},
"react-native-qrcode": {
"version": "0.2.6",

View File

@ -68,7 +68,7 @@
"react-native-linear-gradient": "2.4.0",
"react-native-orientation": "3.1.0",
"react-native-os": "1.1.0",
"react-native-popup-menu": "0.8.3",
"react-native-popup-menu": "0.12.2",
"react-native-qrcode": "0.2.6",
"react-native-randombytes": "3.0.0",
"react-native-sortable-listview": "0.2.6",

View File

@ -1,5 +1,4 @@
(ns status-im.android.platform
(:require [status-im.react-native.js-dependencies :as rn-dependencies]))
(ns status-im.android.platform)
(def fonts
{:light {:font-family "Roboto-Light"}
@ -9,21 +8,10 @@
:toolbar-title {:font-family "Roboto-Regular"}
:roboto-mono {:font-family "RobotoMono-Medium"}})
;; Dialogs
(defn show-dialog [{:keys [title options callback]}]
(let [dialog (new rn-dependencies/dialogs)]
(.set dialog (clj->js {:title title
:items (mapv :text options)
:itemsCallback callback}))
(.show dialog)))
;; Structure to be exported
(def platform-specific
{:fonts fonts
:list-selection-fn show-dialog
:tabs {:tab-shadows? true}
:chats {:action-button? true
:new-chat-in-toolbar? false

View File

@ -17,14 +17,13 @@
[status-im.ui.components.icons.vector-icons :as vector-icons]
[status-im.ui.components.status-bar.view :as status-bar]
[status-im.ui.components.chat-icon.screen :as chat-icon-screen]
[status-im.ui.components.animation :as anim]
[status-im.ui.components.animation :as anim]
[status-im.ui.components.sync-state.offline :as offline]
[status-im.ui.components.toolbar.view :as toolbar]))
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.ui.components.animation :as animation]))
(defview chat-icon []
(letsubs [{:keys [chat-id group-chat name color]} [:get-current-chat]]
[chat-icon-screen/chat-icon-view-action chat-id group-chat name color true]))
(letsubs [{:keys [chat-id group-chat name]} [:get-current-chat]]
[chat-icon-screen/chat-icon-view-action chat-id group-chat name true]))
(defn- toolbar-action [show-actions?]
[react/touchable-highlight
@ -51,7 +50,7 @@
creating? [:get :accounts/creating-account?]]
[react/view
[status-bar/status-bar]
[toolbar/toolbar {:show-sync-bar? true}
[toolbar/toolbar {}
(when-not (or show-actions? creating?)
(if (empty? accounts)
[toolbar/nav-clear-text (i18n/label :t/recover)
@ -76,15 +75,15 @@
(defview messages-view-animation [message-view]
;; smooths out appearance of message-view
(letsubs [opacity (anim/create-value 0)
(letsubs [opacity (animation/create-value 0)
duration (if platform/android? 100 200)
timeout (if platform/android? 50 0)]
{:component-did-mount (fn [component]
(anim/start
(anim/anim-sequence
[(anim/anim-delay timeout)
(anim/spring opacity {:toValue 1
:duration duration})])))}
(animation/start
(animation/anim-sequence
[(animation/anim-delay timeout)
(animation/spring opacity {:toValue 1
:duration duration})])))}
[react/with-activity-indicator
{:style style/message-view-preview
:preview [react/view style/message-view-preview]}

View File

@ -36,7 +36,7 @@
{:flex-direction :row
:align-items :flex-end})
(defnstyle input-root [content-height anim-margin]
(defn input-root [content-height anim-margin]
{:align-items :flex-start
:background-color color-input
:flex-direction :row
@ -45,8 +45,7 @@
:margin-top anim-margin
:padding-left 10
:padding-right 10
:android {:border-radius 4}
:ios {:border-radius 8}})
:border-radius 8})
(defnstyle input-touch-handler-view [container-width]
{:position :absolute

View File

@ -95,12 +95,11 @@
(merge style-message-text
{:marginTop (if incoming-group 4 0)}))
(defnstyle message-view
(defn message-view
[{:keys [content-type outgoing group-chat selected]}]
(merge {:padding 12
:backgroundColor styles/color-white
:android {:border-radius 4}
:ios {:border-radius 8}}
:border-radius 8}
(when (= content-type constants/content-type-command)
{:paddingTop 10
:paddingBottom 14})))

View File

@ -1,7 +1,4 @@
(ns status-im.ios.platform
(:require [status-im.i18n :refer [label]]
[status-im.utils.core :as utils]
[status-im.react-native.js-dependencies :as rn-dependencies]))
(ns status-im.ios.platform)
(def fonts
{:light {:font-family "SFUIText-Light"}
@ -12,26 +9,10 @@
:toolbar-title {:font-family "SFUIText-Semibold"}
:roboto-mono {:font-family "RobotoMono-Medium"}})
;; Dialogs
(defn action-sheet-options [options]
(let [destructive-opt-index (utils/first-index :destructive? options)
cancel-option {:text (label :t/cancel)}
options (conj options cancel-option)]
(clj->js (merge {:options (mapv :text options)
:cancelButtonIndex (dec (count options))}
(when destructive-opt-index {:destructiveButtonIndex destructive-opt-index})))))
(defn show-action-sheet [{:keys [options callback]}]
(.showActionSheetWithOptions (.-ActionSheetIOS rn-dependencies/react-native)
(action-sheet-options options)
callback))
;; Structure to be exported
(def platform-specific
{:fonts fonts
:list-selection-fn show-action-sheet
:tabs {:tab-shadows? false}
:chats {:action-button? false
:new-chat-in-toolbar? true

View File

@ -263,6 +263,10 @@
:unsigned-transaction-expired "Unsigned transaction expired"
:status "Status"
:recipient "Recipient"
:specify-recipient "Specify recipient..."
:recipient-code "Enter recipient address"
:enter-contact-code "Enter Contact Code"
:recent-recipients "Recent recipients"
:to "To"
:from "From"
:data "Data"
@ -312,9 +316,10 @@
:send-request "Send request"
:share "Share"
:eth "ETH"
:gwei "Gwei"
:currency "Currency"
:usd-currency "USD"
:amount-placeholder "Specify amount"
:amount-placeholder "Specify amount..."
:transactions "Transactions"
:transaction-details "Transaction details"
:transaction-failed "Transaction failed"
@ -342,6 +347,7 @@
:not-applicable "Not applicable for unsigned transactions"
:send-transaction "Send transaction"
:receive-transaction "Receive transaction"
:new-transaction "New Transaction"
:transaction-history "Transaction History"
;; Wallet Send
@ -357,6 +363,7 @@
:wallet-transaction-total-fee "Total Fee"
:validation-amount-invalid-number "Amount is not a valid number"
:validation-amount-is-too-precise "Amount is too precise. The smallest unit you can send is 1 Wei (1x10^-18 ETH)"
:scan-qr-code "Scan a QR code with a wallet address"

View File

@ -1,12 +1,6 @@
(ns status-im.ui.components.action-button.styles
(:require-macros [status-im.utils.styles :refer [defstyle defnstyle]])
(:require [status-im.utils.platform :as p]
[status-im.ui.components.styles :refer [color-white
color-light-blue-transparent
color-light-blue
color-light-gray
color-black
color-gray4]]))
(:require [status-im.ui.components.styles :as styles]))
(defstyle action-button
{:padding-left 16
@ -15,34 +9,34 @@
:ios {:height 63}
:android {:height 56}})
(defnstyle action-button-icon-container [cyrcle-color]
(defnstyle action-button-icon-container [circle-color]
{:border-radius 50
:width 40
:height 40
:align-items :center
:justify-content :center
:ios {:background-color (or cyrcle-color color-light-blue-transparent)}})
:ios {:background-color (or circle-color styles/color-light-blue-transparent)}})
(def action-button-label-container
{:padding-left 16})
(defstyle action-button-label
{:ios {:color color-light-blue
{:ios {:color styles/color-light-blue
:letter-spacing -0.2
:font-size 17
:line-height 20}
:android {:color color-black
:android {:color styles/color-black
:font-size 16}})
(defstyle actions-list
{:background-color color-white
{:background-color styles/color-white
:android {:padding-top 8
:padding-bottom 8}})
(def action-button-label-disabled
(merge action-button-label
{:color color-gray4}))
{:color styles/color-gray4}))
(defstyle action-button-icon-container-disabled
{:border-radius 50
@ -50,5 +44,5 @@
:height 40
:align-items :center
:justify-content :center
:ios {:background-color color-light-gray}})
:ios {:background-color styles/color-light-gray}})

View File

@ -6,19 +6,19 @@
(defn- callback [options]
(fn [index]
(when (< index (count options))
(when-let [handler (:actione (nth options index))]
(when-let [handler (:action (nth options index))]
(handler)))))
(defn- options [options]
(defn- prepare-options [title message options]
(let [destructive-opt-index (utils/first-index :destructive? options)] ;; TODO Can only be a single destructive?
(clj->js (merge {:options (conj (mapv :label options) (i18n/label :t/cancel))
:cancelButtonIndex (count options)}
(when destructive-opt-index
{:destructiveButtonIndex destructive-opt-index})))))
{:destructiveButtonIndex destructive-opt-index})
(when title {:title title})
(when message {:message message})))))
(defn show [{:keys [title message options callback]}]
(defn show [{:keys [title message options]}]
(.showActionSheetWithOptions (.-ActionSheetIOS rn-dependencies/react-native)
(merge (options options)
(when title {:title title})
(when message {:message message}))
callback))
(prepare-options title message options)
(callback options)))

View File

@ -0,0 +1,23 @@
(ns status-im.ui.components.bottom-buttons.styles
(:require [status-im.ui.components.colors :as colors]))
(def wrapper
{:position :absolute
:bottom 0
:left 0
:right 0})
(def border
{:margin-horizontal 16
:border-top-width 1
:border-color colors/white-light-transparent})
(def container
{:flex-direction :row
:align-items :center
:justify-content :space-between})
(def container-single
{:flex-direction :row
:align-items :center
:justify-content :center})

View File

@ -0,0 +1,21 @@
(ns status-im.ui.components.bottom-buttons.view
(:require [status-im.ui.components.bottom-buttons.styles :as styles]
[status-im.ui.components.react :as react]))
(defn bottom-button
([button] (bottom-button nil button))
([style button]
[react/view styles/wrapper
[react/view styles/border]
[react/view (merge styles/container-single style)
button]]))
(defn bottom-buttons
([left right] (bottom-buttons nil left right))
([style left right]
[react/view styles/wrapper
[react/view styles/border]
[react/view (merge styles/container style)
left
[react/view {:flex 1}]
right]]))

View File

@ -10,13 +10,12 @@
(def button-container styles/flex)
(defnstyle button [disabled?]
{:flex-direction :row
:justify-content :center
:align-items :center
:android {:background-color border-color}
:ios (when-not disabled?
{:background-color border-color})})
(def button
{:flex-direction :row
:justify-content :center
:align-items :center
:padding-horizontal 12})
(defn- border [position]
(let [radius (if platform/ios? 8 4)]
@ -39,27 +38,27 @@
(merge {:border-color border-color}
(border position)))
(defnstyle button-text [disabled?]
(defstyle button-text
{:font-weight :normal
:color styles/color-white
:padding-horizontal 16
:android (merge
{:font-size 14
:padding-vertical 10
:letter-spacing 0.5}
(when disabled? {:opacity 0.4}))
:ios (merge
{:font-size 15
:padding-vertical 9
:letter-spacing -0.2}
(when disabled? {:opacity 0.6}))})
:android {:font-size 14
:padding-vertical 10
:letter-spacing 0.5}
:ios {:font-size 15
:padding-vertical 9
:letter-spacing -0.2}})
(defstyle button-text-disabled
{:android {:opacity 0.4}
:ios {:opacity 0.6}})
(defstyle button-borders
{:android {:border-radius 4}
:ios {:border-radius 8
;; Border radius is ignored with transparent background unless overflow "hidden" is used
;; See https://github.com/facebook/react-native/issues/13760
:overflow :hidden}})
{:border-radius 8
:ios {;; Border radius is ignored with transparent background unless overflow "hidden" is used
;; See https://github.com/facebook/react-native/issues/13760
:overflow :hidden}})
(def primary-button
(merge
@ -73,4 +72,4 @@
button-borders
{:background-color styles/color-blue4-transparent}))
(def secondary-button-text {:color styles/color-blue4})
(def secondary-button-text {:color styles/color-blue4})

View File

@ -1,36 +1,33 @@
(ns status-im.ui.components.button.view
(:require [status-im.ui.components.button.styles :as button.styles]
(:require [status-im.ui.components.button.styles :as styles]
[status-im.ui.components.react :as react]
[status-im.utils.platform :as platform]))
(defn button-text [{:keys [disabled? text-style]} label]
[react/text {:style (merge (button.styles/button-text disabled?)
text-style)
:font (if platform/android? :medium :default)
:uppercase? (get-in platform/platform-specific [:uppercase?])}
label])
(defn button [{:keys [on-press style disabled? fit-to-text?] :as props} label]
[react/touchable-highlight (merge {:underlay-color button.styles/border-color-high}
(defn button [{:keys [on-press style disabled? fit-to-text? text-style] :or {fit-to-text? true}} label icon]
[react/touchable-highlight (merge {:underlay-color styles/border-color-high}
(when-not fit-to-text?
{:style button.styles/button-container})
{:style styles/button-container})
(when (and on-press (not disabled?))
{:on-press on-press}))
[react/view {:style (merge (button.styles/button disabled?)
[react/view {:style (merge styles/button
style)}
[button-text props
label]]])
[react/text {:style (merge styles/button-text
text-style
(when disabled?
{:opacity 0.4}))
:font (if platform/android? :medium :default)
:uppercase? (get-in platform/platform-specific [:uppercase?])}
label]
icon]])
(defn primary-button [{:keys [style text-style] :as m} label]
[button (assoc m
:fit-to-text? true
:style (merge button.styles/primary-button style)
:text-style (merge button.styles/primary-button-text text-style))
:style (merge styles/primary-button style)
:text-style (merge styles/primary-button-text text-style))
label])
(defn secondary-button [{:keys [style text-style] :as m} label]
[button (assoc m
:fit-to-text? true
:style (merge button.styles/secondary-button style)
:text-style (merge button.styles/secondary-button-text text-style))
:style (merge styles/secondary-button style)
:text-style (merge styles/secondary-button-text text-style))
label])

View File

@ -2,7 +2,6 @@
(:require [goog.object :as object]
[reagent.core :as r]
[clojure.walk :refer [keywordize-keys]]
[status-im.utils.platform :as platform]
[status-im.react-native.js-dependencies :as rn-dependecies]))
(def default-camera (.-default rn-dependecies/camera))
@ -19,15 +18,13 @@
(defn set-torch [state]
(set! (.-torchMode default-camera) (get torch-modes state)))
(defn request-access [callback]
(if platform/android?
(callback true)
(-> (.checkVideoAuthorizationStatus default-camera)
(.then #(callback %))
(.catch #(callback false)))))
(defn request-access-ios [then else]
(-> (.checkVideoAuthorizationStatus default-camera)
(.then then)
(.catch else)))
(defn camera [props]
(r/create-element default-camera (clj->js (merge {:inverted true} props))))
(defn get-qr-code-data [code]
(.-data code))
(.-data code))

View File

@ -1,12 +1,8 @@
(ns status-im.ui.components.carousel.carousel
(:require [reagent.impl.component :as rc]
[status-im.ui.components.react :refer [view
scroll-view
touchable-without-feedback
text]]
[status-im.ui.components.react :as react]
[status-im.ui.components.carousel.styles :as st]
[taoensso.timbre :as log]
[status-im.ui.components.react :as r]
[status-im.react-native.js-dependencies :as rn-dependencies]))
@ -160,15 +156,15 @@
gap (get-gap data)
count (get-count data)]
(doall (map-indexed (fn [index child]
(let [page-index index
touchable-data {:key index
:onPress #(go-to-page component page-index)}]
[touchable-without-feedback touchable-data
[view {:style [(st/page index count page-width gap)
page-style]
:onLayout #(log/debug "view onLayout" %)}
(let [page-index index
touchable-data {:key index
:onPress #(go-to-page component page-index)}]
[react/touchable-without-feedback touchable-data
[react/view {:style [(st/page index count page-width gap)
page-style]
:onLayout #(log/debug "view onLayout" %)}
child]])) children))))
child]])) children))))
(defn reagent-render [data children]
(let [starting-position (atom 0)
@ -176,18 +172,18 @@
state (reagent.core/state component)
gap (get-gap state)]
(log/debug "reagent-render: " data state)
[view {:style st/scroll-view-container}
[scroll-view {:contentContainerStyle (st/content-container gap)
:automaticallyAdjustContentInsets false
:bounces false
:decelerationRate 0.9
:horizontal true
:onLayout #(on-layout-change % component)
:scrollEnabled (not (get state :scrolling?))
:onScrollBeginDrag #(reset! starting-position (get-current-position %))
:onScrollEndDrag #(on-scroll-end % component @starting-position)
:showsHorizontalScrollIndicator false
:ref #(set! (.-scrollView component) %)}
[react/view {:style st/scroll-view-container}
[react/scroll-view {:contentContainerStyle (st/content-container gap)
:automaticallyAdjustContentInsets false
:bounces false
:decelerationRate 0.9
:horizontal true
:onLayout #(on-layout-change % component)
:scrollEnabled (not (get state :scrolling?))
:onScrollBeginDrag #(reset! starting-position (get-current-position %))
:onScrollEndDrag #(on-scroll-end % component @starting-position)
:showsHorizontalScrollIndicator false
:ref #(set! (.-scrollView component) %)}
(get-pages component state children)]]))
(defn carousel [_ _]

View File

@ -1,11 +1,11 @@
(ns status-im.ui.components.chat-icon.screen
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [status-im.ui.components.react :as react]
(:require [clojure.string :as string]
[status-im.i18n :as i18n]
[status-im.ui.components.react :as react]
[status-im.ui.components.chat-icon.styles :as styles]
[status-im.ui.components.styles :as components.styles]
[status-im.react-native.resources :as resources]
[clojure.string :as string]
[status-im.i18n :as i18n]))
[status-im.react-native.resources :as resources]))
(defn default-chat-icon [name styles]
[react/view (:default-chat-icon styles)
@ -64,7 +64,7 @@
:default-chat-icon (styles/default-chat-icon-chat-list color)
:default-chat-icon-text styles/default-chat-icon-text}])
(defn chat-icon-view-action [chat-id group-chat name _color online]
(defn chat-icon-view-action [chat-id group-chat name online]
^{:key chat-id}
[chat-icon-view chat-id group-chat name online
{:container styles/container-chat-list

View File

@ -3,9 +3,12 @@
(def white "#ffffff")
(def white-light-transparent "rgba(255, 255, 255, 0.1)") ;; Used as icon background color for a dark foreground
(def white-transparent "rgba(255, 255, 255, 0.2)") ;; Used as icon color on dark background
(def blue "#4360df") ;; Used as main wallet color
(def white-lighter-transparent "rgba(255, 255, 255, 0.6)") ;; Used for input placeholder color
(def black "#000000") ;; Used as the default text color
(def gray "#939ba1") ;; Used as a background for a light foreground and for text descriptions
(def gray "#939ba1") ;; Used as a background for a light foreground and as section header and secondary text color
(def gray-light "#d9dae1") ;; Used as divider color
(def gray-lighter "#eef2f5") ;; Used as a background or shadow
(def blue "#4360df") ;; Used as main wallet color
(def red "#ff2d55") ;; Used to highlight errors or "dangerous" actions
(def light-gray "#eef2f5") ;; Used as a background or shadow
(def text-light-gray "#212121") ;; Used for labels (home items)
(def text-light-gray "#212121") ;; Used for labels (home items)

View File

@ -1,16 +1,17 @@
(ns status-im.ui.components.context-menu
(:require [goog.object :as object]
(:require [status-im.react-native.js-dependencies :as rn-dependencies]
[status-im.ui.components.action-sheet :as action-sheet]
[status-im.ui.components.react :as react]
[goog.object :as object]
[reagent.core :as r]
[status-im.ui.components.styles :as st]
[status-im.utils.platform :refer [platform-specific ios?]]
[status-im.ui.components.react :as rn]
[status-im.react-native.js-dependencies :as rn-dependencies]))
[status-im.utils.platform :as platform]))
(defn- get-property [name]
(object/get rn-dependencies/popup-menu name))
(defn- get-class [name]
(rn/adapt-class (get-property name)))
(react/adapt-class (get-property name)))
(def menu (get-class "Menu"))
(def menu-context (get-class "MenuContext"))
@ -18,7 +19,7 @@
(def menu-options (get-class "MenuOptions"))
(def menu-option (get-class "MenuOption"))
(defn context-menu-options [custom-styles]
(defn- context-menu-options [custom-styles]
{:customStyles {:optionsContainer
(merge {:elevation 2
:margin-top 0
@ -33,39 +34,22 @@
:height 48}
(:optionWrapper custom-styles))}})
(defn context-menu-text [destructive?]
(defn- context-menu-text [destructive?]
{:font-size 15
:line-height 20
:color (if destructive? st/color-light-red st/text1-color)})
(def list-selection-fn (:list-selection-fn platform-specific))
(defn open-ios-menu [title options]
(list-selection-fn {:options options
:title title
:callback (fn [index]
(when (< index (count options))
(when-let [handler (:value (nth options index))]
(handler))))})
nil)
(defn context-menu [trigger options & custom-styles trigger-style]
(if ios?
[rn/touchable-highlight {:style trigger-style
:on-press #(open-ios-menu nil options)}
[rn/view
(if platform/ios?
[react/touchable-highlight {:style trigger-style
:on-press #(action-sheet/show options)}
[react/view
trigger]]
[menu {:onSelect #(when % (do (%) nil))}
[menu-trigger {:style trigger-style} trigger]
[menu-options (context-menu-options custom-styles)
(for [{:keys [style value destructive?] :as option} options]
(for [{:keys [style action destructive?] :as option} options]
^{:key option}
[menu-option {:value value}
[rn/text {:style (merge (context-menu-text destructive?) style)}
(:text option)]])]]))
(defn modal-menu [trigger style title options]
[rn/touchable-highlight {:style style
:on-press #(open-ios-menu title options)}
[rn/view
trigger]])
[menu-option {:value action}
[react/text {:style (merge (context-menu-text destructive?) style)}
(:label option)]])]]))

View File

@ -0,0 +1,15 @@
(ns status-im.ui.components.dialog
(:require [status-im.react-native.js-dependencies :as rn-dependencies]))
(defn- callback [options]
(fn [index]
(when (< index (count options))
(when-let [handler (:action (nth options index))]
(handler)))))
(defn- show [{:keys [title options]}]
(let [dialog (new rn-dependencies/dialogs)]
(.set dialog (clj->js {:title title
:items (mapv :label options)
:itemsCallback (callback options)}))
(.show dialog)))

View File

@ -1,7 +1,6 @@
(ns status-im.ui.components.list.styles
(:require-macros [status-im.utils.styles :refer [defstyle]])
(:require [status-im.ui.components.styles :as styles]
[status-im.utils.platform :as platform]))
(:require [status-im.ui.components.colors :as colors]))
(def item
{:flex-direction :row
@ -19,25 +18,27 @@
:justify-content :center})
(def primary-text-base
{:font-size 17
:color styles/color-black})
{:font-size 16
:color colors/black})
(def primary-text
(merge primary-text-base
{:padding-top (if platform/ios? 13 14)}))
{:padding-top 12}))
(def primary-text-only
(merge primary-text-base
{:padding-vertical 16}))
(def secondary-text
{:font-size 16
:color styles/color-gray4
{:font-size 14
:color colors/gray
:padding-top 4})
(def image-size 40)
(def item-image
{:width 40
:height 40})
{:width image-size
:height image-size})
(def icon-size 24)
(def icon-wrapper-size (+ icon-size (* 2 8)))
@ -61,9 +62,9 @@
:margin-vertical vertical-margin})
(def content-item-wrapper
{:flex 1
:justify-content :center
:margin-left horizontal-margin})
{:flex 1
:justify-content :center
:margin-horizontal horizontal-margin})
(def right-item-wrapper
{:justify-content :center
@ -71,16 +72,15 @@
(def base-separator
{:height 1
:background-color styles/color-gray5
:opacity 0.5})
:background-color colors/gray-light})
(def separator
(merge
base-separator
{:margin-left 70}))
{:margin-left 70}))
(defstyle list-header-footer-spacing
{:android {:background-color styles/color-white
{:android {:background-color colors/white
:height 8}})
(defstyle section-separator
@ -90,7 +90,7 @@
(defstyle section-header
{:font-size 14
:color styles/color-gray4
:color colors/gray
:margin-left 16
:android {:margin-top 11
:margin-bottom 3}

View File

@ -31,20 +31,21 @@
(defn item
([content] (item nil content))
([left-action content] (item left-action content nil))
([left-action content right-action]
([left content] (item left content nil))
([left content right]
[react/view {:style styles/item}
[react/view {:style styles/left-item-wrapper}
left-action]
left]
[react/view {:style styles/content-item-wrapper}
content]
(when right-action
(when right
[react/view {:style styles/right-item-wrapper}
right-action])]))
right])]))
(defn touchable-item [handler item]
[react/touchable-highlight {:on-press handler}
item])
[react/view
item]])
(defn item-icon
[{:keys [icon style icon-opts]}]
@ -84,7 +85,7 @@
(def item-icon-forward
[item-icon {:style styles/item-icon
:icon :icons/forward
:icon-opts {:color colors/white-transparent}}])
:icon-opts {:color colors/white-light-transparent}}])
(defn- wrap-render-fn [f]
(fn [data]

View File

@ -1,41 +1,36 @@
(ns status-im.ui.components.list-selection
(:require [re-frame.core :as re-frame]
[status-im.ui.components.react :refer [copy-to-clipboard
sharing
linking]]
[status-im.utils.platform :refer [platform-specific ios?]]
[status-im.i18n :refer [label]]))
[status-im.i18n :as i18n]
[status-im.ui.components.action-sheet :as action-sheet]
[status-im.ui.components.dialog :as dialog]
[status-im.ui.components.react :as react]
[status-im.utils.platform :as platform]))
(defn open-share [content]
(defn- open-share [content]
(when (or (:message content)
(:url content))
(.share sharing (clj->js content))))
(.share react/sharing (clj->js content))))
(defn share-options [text]
[{:text (label :t/sharing-copy-to-clipboard)
:value #(copy-to-clipboard text)}
{:text (label :t/sharing-share)
:value #(open-share {:message text})}])
[{:label (i18n/label :t/sharing-copy-to-clipboard)
:action #(react/copy-to-clipboard text)}
{:label (i18n/label :t/sharing-share)
:action #(open-share {:message text})}])
(defn show [options]
(if platform/ios?
(action-sheet/show options)
(dialog/show options)))
(defn share [text dialog-title]
(let [list-selection-fn (:list-selection-fn platform-specific)]
(list-selection-fn {:title dialog-title
:options (share-options text)
:callback (fn [index]
(case index
0 (copy-to-clipboard text)
1 (open-share {:message text})
:default))
:cancel-text (label :t/sharing-cancel)})))
(show {:title dialog-title
:options (share-options text)
:cancel-text (i18n/label :t/sharing-cancel)}))
(defn browse [link]
(let [list-selection-fn (:list-selection-fn platform-specific)]
(list-selection-fn {:title (label :t/browsing-title)
:options [{:text (label :t/browsing-open-in-browser)}
{:text (label :t/browsing-open-in-web-browser)}]
:callback (fn [index]
(case index
0 (re-frame/dispatch [:open-browser {:url link}])
1 (.openURL linking link)
:default))
:cancel-text (label :t/browsing-cancel)})))
(show {:title (i18n/label :t/browsing-title)
:options [{:text (i18n/label :t/browsing-open-in-browser)
:action (re-frame/dispatch [:open-browser {:url link}])}
{:text (i18n/label :t/browsing-open-in-web-browser)
:action (.openURL react/linking link)}]
:cancel-text (i18n/label :t/browsing-cancel)}))

View File

@ -1,6 +1,7 @@
(ns status-im.ui.components.permissions
(:require [status-im.utils.platform :as platform]
[taoensso.timbre :as log]
[status-im.ui.components.camera :as camera]
[status-im.react-native.js-dependencies :as rn-dependencies]))
(def permissions-class (.-PermissionsAndroid rn-dependencies/react-native))
@ -27,4 +28,6 @@
(else-fn)))
(.catch else-fn))))
(then)))
(if ((set permissions) :camera)
(camera/request-access-ios then else)
(then))))

View File

@ -66,13 +66,13 @@
:justify-content :center})
(def qr-code
{:background-color colors/light-gray
{:background-color colors/gray-lighter
:flex-grow 1
:align-items :center
:justify-content :center})
(def footer
{:background-color colors/light-gray
{:background-color colors/gray-lighter
:flex-direction :row
:justify-content :center
:padding-bottom 50})

View File

@ -1,8 +1,11 @@
(ns status-im.ui.components.status-bar.styles
(:require [status-im.ui.components.styles :as styles]
(:require [status-im.ui.components.colors :as colors]
[status-im.ui.components.styles :as styles]
[status-im.utils.platform :as platform])
(:require-macros [status-im.utils.styles :refer [defstyle]]))
(def elevation 2)
(defn- create-status-bar-style [{:keys [background-color bar-style translucent?]
:or {bar-style "light-content"}}]
{:background-color (if translucent? "transparent" background-color)
@ -17,13 +20,13 @@
;; :main
(defstyle status-bar-main
{:ios (create-status-bar-style {:background-color styles/color-white
{:ios (create-status-bar-style {:background-color colors/white
:bar-style "default"})
:android (create-status-bar-style {:translucent? true
:bar-style "dark-content"})})
(def view-main
(create-view-style {:background-color styles/color-white}))
(create-view-style {:background-color colors/white}))
;; :transparent
(defstyle status-bar-transparent
@ -45,25 +48,25 @@
;; :modal-white
(defstyle status-bar-modal-white
{:ios (create-status-bar-style {:background-color styles/color-white
{:ios (create-status-bar-style {:background-color colors/white
:bar-style "default"})
:android (create-status-bar-style {:background-color styles/color-black
:bar-style "light-content"})})
(defstyle view-modal-white
{:ios (create-view-style {:background-color styles/color-white})
{:ios (create-view-style {:background-color colors/white})
:android (create-view-style {:background-color styles/color-black
:height 0})})
;; :modal-wallet
(defstyle status-bar-modal-wallet
{:ios (create-status-bar-style {:background-color styles/color-blue4})
:android (create-status-bar-style {:background-color styles/color-black})})
(def status-bar-modal-wallet
(create-status-bar-style {:background-color colors/blue}))
(defstyle view-model-wallet
{:ios (create-view-style {:background-color styles/color-blue4})
:android (create-view-style {:background-color styles/color-black
:height 0})})
(defstyle view-modal-wallet
{:ios (create-view-style {:background-color colors/blue})
:android (create-view-style {:background-color colors/blue
:height 0
:elevation elevation})})
;; :transaction
(defstyle status-bar-transaction
@ -75,21 +78,32 @@
:android (create-view-style {:background-color styles/color-dark-blue-2
:height 0})})
;; :wallet
(defstyle status-bar-wallet
{:ios (create-status-bar-style {:background-color styles/color-blue4})
;; TODO(jeluard) Fix status-bar mess by removing useless view and introducing 2dn level tab-bar
;; :wallet HOME
(defstyle status-bar-wallet-tab
{:ios (create-status-bar-style {:background-color colors/blue})
:android (create-status-bar-style {:translucent? true})})
(def view-wallet
(def view-wallet-tab
(create-view-style {:background-color styles/color-blue4}))
;; :wallet
(defstyle status-bar-wallet
{:ios (create-status-bar-style {:background-color colors/blue})
:android (create-status-bar-style {:translucent? true})})
(def view-wallet
(create-view-style {:background-color styles/color-blue4
:elevation elevation}))
;; :default
(defstyle status-bar-default
{:ios (create-status-bar-style {:background-color styles/color-white
{:ios (create-status-bar-style {:background-color colors/white
:bar-style "default"})
:android (create-status-bar-style {:translucent? true
:bar-style "dark-content"})})
(defstyle view-default
(create-view-style {:background-color styles/color-white
:elevation 2}))
(create-view-style {:background-color colors/white
:elevation elevation}))

View File

@ -9,9 +9,10 @@
:transparent [styles/status-bar-transparent styles/view-transparent]
:modal [styles/status-bar-modal styles/view-modal]
:modal-white [styles/status-bar-modal-white styles/view-modal-white]
:modal-wallet [styles/status-bar-modal-wallet styles/view-model-wallet]
:modal-wallet [styles/status-bar-modal-wallet styles/view-modal-wallet]
:transaction [styles/status-bar-transaction styles/view-transaction]
:wallet [styles/status-bar-wallet styles/view-wallet]
:wallet-tab [styles/status-bar-wallet-tab styles/view-wallet-tab]
[styles/status-bar-default styles/view-default])]
[react/view
[react/status-bar status-bar-style]

View File

@ -160,6 +160,8 @@
{:background-color color-white
:flex 1})
(def border-radius 8)
;; TODO(goranjovic): replace all platform conditional uppercase styling with a reference to this var
(def uppercase?
(condp = platform/platform

View File

@ -1,120 +0,0 @@
(ns status-im.ui.components.sync-state.gradient
(:require [re-frame.core :refer [subscribe dispatch]]
[reagent.core :as r]
[status-im.ui.components.react :refer [view
text
animated-view
linear-gradient
get-dimensions]]
[status-im.ui.components.sync-state.styles :as st]
[status-im.ui.components.animation :as anim]
[taoensso.timbre :as log]))
(def gradient-animation-duration 700)
(def synced-disappear-delay 2500)
(def gradient-width 250)
(def in-progress-animation-delay 1500)
(def window-width (:width (get-dimensions "window")))
(declare start-gradient-reverse-animation)
(defn- start-gradient-animation [{:keys [gradient-position sync-state] :as context}]
(when (= @sync-state :in-progress)
(anim/start
(anim/timing gradient-position
{:toValue (- window-width (/ gradient-width 3))
:duration gradient-animation-duration})
(fn [_]
(start-gradient-reverse-animation context)))))
(defn- start-gradient-reverse-animation [{:keys [gradient-position sync-state] :as context}]
(when (= @sync-state :in-progress)
(anim/start
(anim/timing gradient-position
{:toValue (- 0 (* 2 (/ gradient-width 3)))
:duration gradient-animation-duration})
(fn [_]
(start-gradient-animation context)))))
(defn- start-synced-animation [{:keys [sync-state-opacity in-progress-opacity synced-opacity]}]
(anim/start
(anim/timing in-progress-opacity {:toValue 0.0
:duration 250}))
(anim/start
(anim/timing synced-opacity {:toValue 1.0
:duration 250})
(fn [_]
(anim/start
(anim/timing sync-state-opacity {:toValue 0.0
:duration 250
:delay synced-disappear-delay})
(fn [_]
(dispatch [:set :sync-state :done]))))))
(defn start-in-progress-animation [component]
(r/set-state component
{:pending? true
:animation (js/setTimeout
(fn []
(dispatch [:set :sync-state :in-progress])
(r/set-state component {:pending? false}))
in-progress-animation-delay)}))
(defn start-offline-animation [{:keys [sync-state-opacity]}]
(anim/start
(anim/timing sync-state-opacity {:toValue 0.0
:duration 250})))
(defn clear-pending-animation [component]
(let [{:keys [pending? animation]} (r/state component)]
(when pending?
(r/set-state component {:pending? false})
(js/clearTimeout animation))))
(defn sync-state-gradient-view []
(let [sync-state (subscribe [:sync-state])
gradient-position (anim/create-value 0)
sync-state-opacity (anim/create-value 0.0)
in-progress-opacity (anim/create-value 0.0)
synced-opacity (anim/create-value 0.0)
context {:sync-state sync-state
:gradient-position gradient-position
:sync-state-opacity sync-state-opacity
:in-progress-opacity in-progress-opacity
:synced-opacity synced-opacity}
on-update (fn [component _]
(case @sync-state
:pending (start-in-progress-animation component)
:in-progress (do
(anim/set-value gradient-position 0)
(anim/set-value sync-state-opacity 1)
(anim/set-value in-progress-opacity 1)
(anim/set-value synced-opacity 0)
(start-gradient-animation context))
:synced (start-synced-animation context)
:done (clear-pending-animation component)
:offline (do (clear-pending-animation component)
(start-offline-animation context))
(log/debug "Sync state:" @sync-state)))]
(r/create-class
{:component-did-mount
on-update
:component-did-update
on-update
:display-name "sync-state-gradient-view"
:reagent-render
(fn []
[view st/sync-style-gradient
[animated-view {:style (st/loading-wrapper sync-state-opacity)}
[animated-view {:style (st/gradient-wrapper in-progress-opacity gradient-position)}
[linear-gradient {:colors ["#89b1fe" "#8b5fe4" "#8b5fe4" "#89b1fe"]
:start {:x 0 :y 1}
:end {:x 1 :y 1}
:locations [0 0.3 0.7 1]
:style (st/gradient gradient-width)}]]
(when (not= @sync-state :in-progress)
[animated-view {:style (st/synced-wrapper synced-opacity window-width)}])]])})))

View File

@ -1,31 +1,5 @@
(ns status-im.ui.components.sync-state.styles
(:require-macros [status-im.utils.styles :refer [defstyle defnstyle]]))
(def sync-style-gradient
{:position :relative
:height 0
:top -2})
(defn loading-wrapper [opacity]
{:background-color "#89b1fe"
:opacity opacity
:height 2})
(defn gradient-wrapper [in-progress-opacity position]
{:position :absolute
:left position
:opacity in-progress-opacity})
(defn gradient [width]
{:width width
:height 2})
(defn synced-wrapper [opacity window-width]
{:opacity opacity
:position :absolute
:width window-width
:background-color "#5fc48d"
:height 2})
(:require-macros [status-im.utils.styles :refer [defnstyle]]))
(defnstyle offline-wrapper [top opacity window-width pending?]
{:ios {:z-index 0}

View File

@ -1,49 +1,51 @@
(ns status-im.ui.components.toolbar.view
(:require [reagent.core :as r]
[re-frame.core :as rf]
[status-im.ui.components.react :as rn]
[status-im.ui.components.sync-state.gradient :as sync-state-gradient-view]
[status-im.ui.components.styles :as st]
[status-im.ui.components.context-menu :as context-menu]
[status-im.ui.components.toolbar.actions :as act]
[status-im.ui.components.toolbar.styles :as tst]
[status-im.ui.components.icons.vector-icons :as vi]
[status-im.utils.platform :as platform]))
(:require [reagent.core :as reagent]
[re-frame.core :as re-frame]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.icons.vector-icons :as vector-icons]
[status-im.ui.components.list-selection :as list-selection]
[status-im.ui.components.react :as react]
[status-im.ui.components.styles :as components.styles]
[status-im.ui.components.toolbar.actions :as actions]
[status-im.ui.components.toolbar.styles :as styles]
[status-im.utils.platform :as platform]
[status-im.utils.utils :as utils]))
;; Navigation item
(defn nav-item
[{:keys [handler accessibility-label style] :or {handler #(rf/dispatch [:navigate-back])}} item]
[rn/touchable-highlight
[{:keys [handler accessibility-label style] :or {handler #(re-frame/dispatch [:navigate-back])}} item]
[react/touchable-highlight
(merge {:on-press handler}
(when accessibility-label
{:accessibility-label accessibility-label}))
[rn/view {:style style}
[react/view {:style style}
item]])
(defn nav-button
[{:keys [icon icon-opts] :as props}]
[nav-item (merge {:style tst/nav-item-button} props)
[vi/icon icon icon-opts]])
[nav-item (merge {:style styles/nav-item-button} props)
[vector-icons/icon icon icon-opts]])
(defn nav-text
([text] (nav-text text nil))
([text handler] (nav-text nil text handler))
([props text] (nav-text props text nil))
([props text handler]
[rn/text (merge {:style (merge tst/item tst/item-text) :on-press (or handler #(rf/dispatch [:navigate-back]))})
[react/text (utils/deep-merge {:style (merge styles/item styles/item-text) :on-press (or handler #(re-frame/dispatch [:navigate-back]))}
props)
text]))
(defn nav-clear-text
([text] (nav-clear-text text nil))
([text handler]
(nav-text tst/item-text-white-background text handler)))
(nav-text styles/item-text-white-background text handler)))
(def default-nav-back [nav-button act/default-back])
(def default-nav-back [nav-button actions/default-back])
;; Content
(defn content-wrapper [content]
[rn/view {:style tst/toolbar-container}
[react/view {:style styles/toolbar-container}
content])
(defn content-title
@ -51,39 +53,37 @@
([title-style title]
(content-title title-style title nil nil))
([title-style title subtitle-style subtitle]
[rn/view {:style tst/toolbar-title-container}
[rn/text {:style (merge tst/toolbar-title-text title-style)
:font :toolbar-title}
[react/view {:style styles/toolbar-title-container}
[react/text {:style (merge styles/toolbar-title-text title-style)
:font :toolbar-title}
title]
(when subtitle [rn/text {:style subtitle-style} subtitle])]))
(when subtitle
[react/text {:style subtitle-style}
subtitle])]))
;; Actions
(defn text-action [{:keys [style handler disabled?]} title]
[rn/text {:style (merge tst/item tst/item-text style
(when disabled? tst/toolbar-text-action-disabled))
:on-press (when-not disabled? handler)}
[react/text {:style (merge styles/item styles/item-text style
(when disabled? styles/toolbar-text-action-disabled))
:on-press (when-not disabled? handler)}
title])
(def blank-action [rn/view {:style (merge tst/item tst/toolbar-action)}])
(defn- option-actions [icon icon-opts options]
[context-menu/context-menu
[rn/view {:style tst/toolbar-action}
[vi/icon icon icon-opts]]
options
nil
tst/item])
(def blank-action [react/view {:style (merge styles/item styles/toolbar-action)}])
(defn- icon-action [icon {:keys [overlay-style] :as icon-opts} handler]
[rn/touchable-highlight {:on-press handler}
[rn/view {:style (merge tst/item tst/toolbar-action)}
[react/touchable-highlight {:on-press handler}
[react/view {:style (merge styles/item styles/toolbar-action)}
(when overlay-style
[rn/view overlay-style])
[vi/icon icon icon-opts]]])
[react/view overlay-style])
[vector-icons/icon icon icon-opts]]])
(defn- option-actions [icon icon-opts options]
[icon-action icon icon-opts
#(list-selection/show {:options options})])
(defn actions [v]
[rn/view {:style tst/toolbar-actions}
[react/view {:style styles/toolbar-actions}
(for [{:keys [image icon icon-opts options handler]} v]
(with-meta
(cond (= image :blank)
@ -98,72 +98,72 @@
(defn toolbar
([props nav-item content-item] (toolbar props nav-item content-item [actions [{:image :blank}]]))
([{:keys [background-color style flat? show-sync-bar?]}
([{:keys [background-color style flat?]}
nav-item
content-item
action-items]
;; TODO remove extra view wen we remove sync-state-gradient
[rn/view
[rn/view {:style (merge (tst/toolbar background-color flat?) style)}
;; On iOS title must be centered. Current solution is a workaround and eventually this will be sorted out using flex
(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])
(if platform/ios?
[rn/view st/flex]
content-item)
action-items]
(when show-sync-bar? [sync-state-gradient-view/sync-state-gradient-view])]))
[react/view {:style (merge (styles/toolbar background-color flat?) style)}
;; On iOS title must be centered. Current solution is a workaround and eventually this will be sorted out using flex
(when platform/ios?
[react/view styles/ios-content-item
content-item])
(when nav-item
[react/view {:style (styles/toolbar-nav-actions-container 0)}
nav-item])
(if platform/ios?
[react/view components.styles/flex]
content-item)
action-items]))
(defn simple-toolbar
"A simple toolbar whose content is a single line text"
"A simple toolbar composed of a nav-back item and a single line title."
([] (simple-toolbar nil))
([title] (toolbar nil default-nav-back [content-title title])))
([title] (simple-toolbar nil title))
([m title] (simple-toolbar m default-nav-back title))
([m nav-back title]
(toolbar m nav-back [content-title title])))
(def search-text-input (r/atom nil))
(def search-text-input (reagent/atom nil))
(defn- toolbar-search-submit [on-search-submit]
(let [text @(rf/subscribe [:get-in [:toolbar-search :text]])]
(let [text @(re-frame/subscribe [:get-in [:toolbar-search :text]])]
(on-search-submit text)
(rf/dispatch [:set-in [:toolbar-search :text] nil])))
(re-frame/dispatch [:set-in [:toolbar-search :text] nil])))
(defn- toolbar-with-search-content [{:keys [show-search?
search-placeholder
title
custom-title
on-search-submit]}]
[rn/view tst/toolbar-with-search-content
[react/view styles/toolbar-with-search-content
(if show-search?
[rn/text-input
{:style tst/toolbar-search-input
[react/text-input
{:style styles/toolbar-search-input
:ref #(reset! search-text-input %)
:auto-focus true
:placeholder search-placeholder
:placeholder-text-color st/color-gray4
:on-change-text #(rf/dispatch [:set-in [:toolbar-search :text] %])
:placeholder-text-color colors/gray
:on-change-text #(re-frame/dispatch [:set-in [:toolbar-search :text] %])
:on-submit-editing (when on-search-submit
#(toolbar-search-submit on-search-submit))}]
(or custom-title
[rn/view
[rn/text {:style tst/toolbar-title-text
:font :toolbar-title}
[react/view
[react/text {:style styles/toolbar-title-text
:font :toolbar-title}
title]]))])
(defn- toggle-search-fn [text]
(rf/dispatch [:set-in [:toolbar-search :show] text])
(rf/dispatch [:set-in [:toolbar-search :text] ""]))
(re-frame/dispatch [:set-in [:toolbar-search :show] text])
(re-frame/dispatch [:set-in [:toolbar-search :text] ""]))
(defn- search-actions [show-search? search-text search-key actions]
(if show-search?
(if (pos? (count search-text))
[(act/close #(do
(.clear @search-text-input)
(rf/dispatch [:set-in [:toolbar-search :text] ""])))]
[act/search-icon])
(into [(act/search #(toggle-search-fn search-key))] actions)))
[(actions/close #(do
(.clear @search-text-input)
(re-frame/dispatch [:set-in [:toolbar-search :text] ""])))]
[actions/search-icon])
(into [(actions/search #(toggle-search-fn search-key))] actions)))
(defn toolbar-with-search [{:keys [show-search?
@ -178,7 +178,7 @@
:style style}
[nav-button
(if show-search?
(act/back #(toggle-search-fn nil))
(or nav-action (if modal? act/default-close act/default-back)))]
(actions/back #(toggle-search-fn nil))
(or nav-action (if modal? actions/default-close actions/default-back)))]
[toolbar-with-search-content opts]
[actions (search-actions show-search? search-text search-key (:actions opts))]])
[actions (search-actions show-search? search-text search-key (:actions opts))]])

View File

@ -12,8 +12,7 @@
(defstyle login-badge-container
{:background-color :white
:ios {:border-radius 8
:padding-top 16
:ios {:padding-top 16
:height 150}
:android {:border-radius 4
:padding-top 12
@ -29,13 +28,12 @@
(merge sign-it-text
{:color st/color-gray2}))
(defstyle sign-in-button
(def sign-in-button
{:background-color st/color-blue3
:align-items :center
:justify-content :center
:height 52
:ios {:border-radius 8}
:android {:border-radius 4}})
:border-radius 8})
(def processing-view
{:position :absolute

View File

@ -14,9 +14,7 @@
[status-im.ui.components.react :as components]))
(defn login-toolbar []
[toolbar/toolbar
{:background-color :transparent
:hide-border? true}
[toolbar/toolbar {:background-color :transparent}
[toolbar/nav-button (act/back-white #(dispatch [:navigate-back]))]
[toolbar/content-title {:color :white} (i18n/label :t/sign-in-to-status)]])

View File

@ -49,8 +49,7 @@
{:background-color :white
:justify-content :center
:height 64
:ios {:border-radius 8}
:android {:border-radius 4}})
:border-radius 8})
(def account-badge
{:flex-direction :row

View File

@ -1,7 +1,8 @@
(ns status-im.ui.screens.contacts.subs
(:require [re-frame.core :refer [reg-sub subscribe]]
[status-im.utils.identicon :refer [identicon]]
[clojure.string :as str]))
(:require [clojure.string :as string]
[re-frame.core :refer [reg-sub subscribe]]
[status-im.utils.ethereum.core :as ethereum]
[status-im.utils.identicon :as identicon]))
(reg-sub :current-contact
(fn [db [_ k]]
@ -94,9 +95,9 @@
(defn search-filter [text item]
(let [name (-> (or (:name item) "")
(str/lower-case))
text (str/lower-case text)]
(not= (str/index-of name text) nil)))
(string/lower-case))
text (string/lower-case text)]
(not= (string/index-of name text) nil)))
(defn search-filter-reaction [contacts text]
(if text
@ -177,7 +178,7 @@
contacts))
(reg-sub :contacts-by-chat
(fn [[_ fn chat-id] _]
(fn [[_ _ chat-id] _]
[(subscribe [:get-chat chat-id])
(subscribe [:get-contacts])])
chat-contacts)
@ -196,7 +197,21 @@
(:photo-path (first contacts))
:else
(identicon chat-id)))))
(identicon/identicon chat-id)))))
(defn- address= [{:keys [address] :as contact} s]
(when (and address (= (ethereum/normalized-address s)
(ethereum/normalized-address address)))
contact))
(defn- contact-by-address [[_ contact] s]
(when (address= contact s)
contact))
(reg-sub :contact/by-address
:<- [:get-contacts]
(fn [contacts [_ address]]
(some #(contact-by-address % address) contacts)))
(reg-sub :contacts/by-address
:<- [:get-contacts]

View File

@ -41,19 +41,19 @@
[toolbar/content-title (label :t/edit-contacts)]])
(defn contact-options [{:keys [unremovable?] :as contact} group]
(let [delete-contact-opt {:value #(u/show-confirmation
(let [delete-contact-opt {:action #(u/show-confirmation
(str (label :t/delete-contact) "?") (label :t/delete-contact-confirmation)
(label :t/delete)
(fn [] (dispatch [:hide-contact contact])))
:text (label :t/delete-contact)
:label (label :t/delete-contact)
:destructive? true}
options (if unremovable? [] [delete-contact-opt])]
(if group
(conj options
{:value #(dispatch [:remove-contact-from-group
(:whisper-identity contact)
(:group-id group)])
:text (label :t/remove-from-group)})
{:action #(dispatch [:remove-contact-from-group
(:whisper-identity contact)
(:group-id group)])
:label (label :t/remove-from-group)})
options)))
(defn contact-group-form [{:keys [contacts contacts-count group edit? click-handler]}]
@ -63,8 +63,8 @@
[common/form-title subtitle
{:count-value contacts-count
:extended? edit?
:options [{:value #(dispatch [:navigate-to :edit-contact-group group :contact-group])
:text (label :t/edit-group)}]}])
:options [{:action #(dispatch [:navigate-to :edit-contact-group group :contact-group])
:label (label :t/edit-group)}]}])
[view st/contacts-list
[common/list-footer]
(doall

View File

@ -143,12 +143,11 @@
:margin-bottom 4
:margin-right 2}})
(defstyle chat-button-container
(def chat-button-container
{:justify-content :center
:align-items :center
:background-color styles/color-blue4-transparent
:ios {:border-radius 8}
:android {:border-radius 4}})
:border-radius 8})
(defstyle chat-button-inner
{:flex-direction :row
@ -257,7 +256,7 @@
:justify-content :center
:align-items :center
:flex-direction :column
:ios {:border-radius 8
:ios {
:border-color styles/color-light-blue6}
:android {:border-radius 4}})

View File

@ -28,8 +28,8 @@
[view
[contact-view
{:contact row
:extend-options [{:value #(dispatch [:remove-group-chat-participants #{(:whisper-identity row)}])
:text (label :t/remove)}]
:extend-options [{:action #(dispatch [:remove-group-chat-participants #{(:whisper-identity row)}])
:label (label :t/remove)}]
:extended? admin?}]
(when-not (= row (last limited-contacts))
[common/list-separator])])

View File

@ -33,16 +33,16 @@
:header list/default-header}]])
(defn chat-extended-options [item]
[{:value #(re-frame/dispatch [:remove-group-chat-participants #{(:whisper-identity item)}])
:text (i18n/label :t/remove)}])
[{:action #(re-frame/dispatch [:remove-group-chat-participants #{(:whisper-identity item)}])
:label (i18n/label :t/remove)}])
(defn contact-extended-options [group-id]
(fn [item]
[{:value #(re-frame/dispatch [:remove-contact-from-group
(:whisper-identity item)
group-id])
[{:action #(re-frame/dispatch [:remove-contact-from-group
(:whisper-identity item)
group-id])
:accessibility-label :remove-button
:text (i18n/label :t/remove-from-group)}]))
:label (i18n/label :t/remove-from-group)}]))
(defview edit-chat-group-contact-list []
(letsubs [chat-name [:chat :name]

View File

@ -106,10 +106,10 @@
[view
[contact-view
{:contact row
:extend-options [{:value #(dispatch [:remove-contact-from-group
(:whisper-identity row)
(:group-id group)])
:text (label :t/remove-from-group)}]
:extend-options [{:action #(dispatch [:remove-contact-from-group
(:whisper-identity row)
(:group-id group)])
:label (label :t/remove-from-group)}]
:extended? true}]
(when-not (= row (last contacts))
[common/list-separator])])

View File

@ -15,7 +15,7 @@
[status-im.utils.platform :as platform]))
(defn toolbar-view []
[toolbar/toolbar {:show-sync-bar? true}
[toolbar/toolbar {}
nil
[toolbar/content-title (i18n/label :t/status)]
[toolbar/actions

View File

@ -52,7 +52,7 @@
(views/defview main-tabs []
(views/letsubs [view-id [:get :view-id]]
[react/view common.styles/flex
[status-bar.view/status-bar {:type (if (= view-id :wallet) :wallet :main)}]
[status-bar.view/status-bar {:type (if (= view-id :wallet) :wallet-tab :main)}]
[react/view common.styles/main-container
[react/with-activity-indicator

View File

@ -33,9 +33,8 @@
:align-items :center
:justify-content :center
:background-color common/color-light-blue
:ios {:border-radius 8
:opacity 0.9}
:android {:border-radius 4}})
:border-radius 8
:ios {:opacity 0.9}})
(defstyle connect-button-label
{:color common/color-white
@ -84,10 +83,10 @@
:align-items :center
:justify-content :center
:background-color common/color-light-blue-transparent
:ios {:width 343
:border-radius 8}
:android {:width 328
:border-radius 4}})
:border-radius 8
:ios {:width 343}
:android {:width 328}})
(defstyle edit-button-label
{:color common/color-light-blue

View File

@ -33,7 +33,8 @@
[react/view styles/wnode-item-inner
[react/text {:style styles/wnode-item-name-text}
name]
#_(when connected?
#_
(when connected?
[react/text {:style styles/wnode-item-connected-text}
(i18n/label :t/connected)])]]]])))

View File

@ -35,7 +35,7 @@
(def profile-status-container
{:background-color colors/gray
:margin-top 16
:border-radius 4
:border-radius 8
:padding 16
:max-height 114})
@ -201,7 +201,7 @@
(defstyle edit-profile-status
{:background-color styles/color-light-gray
:border-radius 4
:border-radius 8
:height 90
:padding-horizontal 16
:padding-bottom 16

View File

@ -42,8 +42,8 @@
[toolbar/toolbar {}
nil
[toolbar/content-title ""]
[common/icon-or-label {:on-press #(re-frame/dispatch [:my-profile/save-profile])}
:t/done {} :icons/ok {:color colors/blue}]])
[common/icon-or-label {:on-press #(re-frame/dispatch [:my-profile/save-profile])}
:t/done {} :icons/ok {:color colors/blue}]])
(defn profile-toolbar [contact]
[toolbar/toolbar {}
@ -52,8 +52,8 @@
[toolbar/actions
(when (and (not (:pending? contact))
(not (:unremovable? contact)))
[(actions/opts [{:value #(re-frame/dispatch [:hide-contact contact])
:text (i18n/label :t/remove-from-contacts)}])])]])
[(actions/opts [{:action #(re-frame/dispatch [:hide-contact contact])
:label (i18n/label :t/remove-from-contacts)}])])]])
(defn online-text [last-online]
(let [last-online-date (time/to-date last-online)
@ -88,27 +88,24 @@
:on-change-text #(re-frame/dispatch [:my-profile/update-name %])}]])
(def profile-icon-options
[{:text (i18n/label :t/image-source-gallery)
:value #(re-frame/dispatch [:my-profile/update-picture])}
{:text (i18n/label :t/image-source-make-photo)
:value (fn []
(re-frame/dispatch [:request-permissions
[:camera :write-external-storage]
(fn []
(camera/request-access
#(if %
(re-frame/dispatch [:navigate-to :profile-photo-capture])
(utils/show-popup (i18n/label :t/error)
(i18n/label :t/camera-access-error)))))]))}])
[{:label (i18n/label :t/image-source-gallery)
:action #(re-frame/dispatch [:my-profile/update-picture])}
{:label (i18n/label :t/image-source-make-photo)
:action (fn []
(re-frame/dispatch [:request-permissions
[:camera :write-external-storage]
#(re-frame/dispatch [:navigate-to :profile-photo-capture])
#(utils/show-popup (i18n/label :t/error)
(i18n/label :t/camera-access-error))]))}])
(defn profile-badge-edit [{:keys [name last-online] :as account}]
[react/view styles/profile-badge-edit
[context-menu/modal-menu
[chat-icon.screen/my-profile-icon {:account account
:edit? true}]
styles/modal-menu
(i18n/label :t/image-source-title)
profile-icon-options]
[react/touchable-highlight {:on-press #(list-selection/show {:title (i18n/label :t/image-source-title)
:options profile-icon-options})}
[react/view styles/modal-menu
[chat-icon.screen/my-profile-icon {:account account
:edit? true}]]]
[react/view styles/profile-badge-name-container
[profile-name-input name]
(when-not (nil? last-online)
@ -164,15 +161,15 @@
(defn profile-options [contact k text]
(into []
(concat [{:value (show-qr contact k text)
:text (i18n/label :t/show-qr)}]
(concat [{:action (show-qr contact k text)
:label (i18n/label :t/show-qr)}]
(when text
(list-selection/share-options text)))))
(defn profile-info-address-item [{:keys [address] :as contact}]
[profile-info-item
{:label (i18n/label :t/address)
:value address
:action address
:options (profile-options contact :address address)
:text-mode :middle
:accessibility-label :profile-address}])
@ -180,7 +177,7 @@
(defn profile-info-public-key-item [public-key contact]
[profile-info-item
{:label (i18n/label :t/public-key)
:value public-key
:action public-key
:options (profile-options contact :public-key public-key)
:text-mode :middle
:accessibility-label :profile-public-key}])
@ -207,7 +204,7 @@
(i18n/label :t/not-specified)
phone)]
[profile-info-item {:label (i18n/label :t/phone-number)
:value phone-text
:action phone-text
:options options
:empty-value? phone-empty?
:accessibility-label :profile-phone-number}]))

View File

@ -17,11 +17,9 @@
[_ [_ identifier]]
(re-frame/dispatch [:request-permissions
[:camera]
(fn []
(camera/request-access
#(if % (re-frame/dispatch [:navigate-to :qr-scanner identifier])
(utils/show-popup (i18n/label :t/error)
(i18n/label :t/camera-access-error)))))]))
#(re-frame/dispatch [:navigate-to :qr-scanner identifier])
#(utils/show-popup (i18n/label :t/error)
(i18n/label :t/camera-access-error))]))
(register-handler :scan-qr-code
(re-frame/after navigate-to-scanner)

View File

@ -41,6 +41,7 @@
[status-im.ui.screens.wallet.settings.views :as wallet-settings]
[status-im.ui.screens.wallet.transactions.views :as wallet-transactions]
[status-im.ui.screens.wallet.send.transaction-sent.views :refer [transaction-sent transaction-sent-modal]]
[status-im.ui.screens.wallet.components.views :refer [contact-code recent-recipients recipient-qr-code]]
[status-im.ui.components.status-bar.view :as status-bar]
[status-im.ui.screens.discover.search-results.views :as discover-search]
[status-im.ui.screens.discover.recent-statuses.views :as discover-recent]
@ -96,6 +97,7 @@
{:view :wallet-send-transaction
:parent :wallet
:hide? (not android?)
:component send-transaction}
{:view :wallet-request-transaction
@ -108,6 +110,7 @@
{:view :choose-recipient
:parent :wallet-send-transaction
:hide? true
:component choose-recipient}
{:view :wallet-transaction-sent
@ -134,7 +137,6 @@
:browser browser
:wallet-send-transaction send-transaction
:wallet-transaction-sent transaction-sent
:choose-recipient choose-recipient
:wallet-request-transaction request-transaction
(:transactions-history :unsigned-transactions) wallet-transactions/transactions
:wallet-transaction-details wallet-transactions/transaction-details
@ -169,11 +171,15 @@
:paste-json-text paste-json-text
:add-rpc-url add-rpc-url
:network-details network-details
:recent-recipients recent-recipients
:recipient-qr-code recipient-qr-code
:contact-code contact-code
:qr-viewer qr-code-viewer/qr-viewer
(throw (str "Unknown view: " current-view)))]
[(if android? menu-context view) common-styles/flex
[view common-styles/flex
(if (and signed-up?
(if (and android?
signed-up?
(#{:home :wallet :my-profile :chat :wallet-send-transaction
:choose-recipient :wallet-transaction-sent :transactions-history
:unsigned-transactions :wallet-request-transaction :edit-my-profile
@ -184,7 +190,7 @@
(when modal-view
[view common-styles/modal
[modal {:animation-type :slide
:transparent false
:transparent true
:on-request-close #(dispatch [:navigate-back])}
(let [component (case modal-view
:qr-scanner qr-scanner

View File

@ -13,16 +13,15 @@
toggled-state (if (= :on flashlight-state) :off :on)]
(assoc-in db [:wallet :send-transaction :camera-flashlight] toggled-state))))
(defn- fill-request-details [db {:keys [address name value symbol gas gasPrice] :as m}]
(defn- fill-request-details [db {:keys [address name value symbol gas gasPrice]}]
{:pre [(not (nil? address))]}
(update-in
db [:wallet :send-transaction]
#(cond-> (assoc % :to address)
value (assoc :amount value)
name (assoc :to-name name)
symbol (assoc :symbol symbol)
gas (assoc :gas (money/bignumber gas))
gasPrice (assoc :gas-price (money/bignumber gasPrice))
#(cond-> (assoc % :to address :to-name name)
value (assoc :amount value)
symbol (assoc :symbol symbol)
gas (assoc :gas (money/bignumber gas))
gasPrice (assoc :gas-price (money/bignumber gasPrice))
(and symbol (not gasPrice))
(assoc :gas-price (ethereum/estimate-gas symbol)))))
@ -37,12 +36,13 @@
(handlers/register-handler-fx
:wallet/fill-request-from-url
(fn [{{:keys [web3 network] :as db} :db} [_ data name]]
(fn [{{:keys [network] :as db} :db} [_ data]]
(let [{:keys [view-id]} db
current-chain-id (get-in constants/default-networks [network :raw-config :NetworkId])
{:keys [address chain-id] :as details} (extract-details data current-chain-id)
valid-network? (boolean (= current-chain-id chain-id))]
(cond-> {:db db}
(cond-> {:db db
:dispatch [:navigate-back]}
(and address (= :choose-recipient view-id)) (assoc :dispatch [:navigate-back])
(and address valid-network?) (update :db #(fill-request-details % details))
(not address) (assoc :show-error (i18n/label :t/wallet-invalid-address {:data data}))
@ -52,5 +52,4 @@
:wallet/fill-request-from-contact
(fn [{db :db} [_ {:keys [address name]}]]
{:db (fill-request-details db {:address address :name name})
:dispatch-n [[:navigate-back]
[:navigate-back]]}))
:dispatch [:navigate-back]}))

View File

@ -1,38 +1,12 @@
(ns status-im.ui.screens.wallet.choose-recipient.styles
(:require-macros [status-im.utils.styles :refer [defnstyle defstyle]])
(:require [status-im.ui.components.styles :as styles]))
(:require [status-im.ui.components.colors :as colors]))
(def toolbar
{:background-color :transparent})
(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})
:background-color colors/blue})
(def recipient-button
{:flex-direction :row
@ -45,24 +19,12 @@
: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})
{:position :absolute
:top 0
:left 0
:right 0
:bottom 0})
(def preview
{:flex 1
@ -74,25 +36,21 @@
: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-left-bottom [height width size]
(merge corner-dimensions {:bottom (int (/ (- height size) 2))
:left (int (/ (- width size) 2))}))
(defn corner-right-bottom [dimension]
(let [viewport-offset 0.1666]
(merge corner-dimensions {:right (* viewport-offset dimension)
:bottom (* viewport-offset dimension)})))
(defn corner-right-bottom [height width size]
(merge corner-dimensions {:right (int (/ (- width size) 2))
:bottom (int (/ (- height size) 2))}))
(defn corner-left-top [dimension]
(let [viewport-offset 0.1666]
(merge corner-dimensions {:top (* viewport-offset dimension)
:left (* viewport-offset dimension)})))
(defn corner-left-top [height width size]
(merge corner-dimensions {:top (int (/ (- height size) 2))
:left (int (/ (- width size) 2))}))
(defn corner-right-top [dimension]
(let [viewport-offset 0.1666]
(merge corner-dimensions {:top (* viewport-offset dimension)
:right (* viewport-offset dimension)})))
(defn corner-right-top [height width size]
(merge corner-dimensions {:top (int (/ (- height size) 2))
:right (int (/ (- width size) 2))}))
(def viewfinder-port {:position :absolute
:left 0
@ -101,24 +59,37 @@
:right 0
:flex 1})
(defn viewfinder-translucent [height width side]
(let [viewport-offset 0.1666
min-dimension (min height width)
min-offset (* viewport-offset min-dimension)]
(defn viewfinder-translucent [height width size side]
(let [top-bottom-width width
top-bottom-height (int (/ (- height size) 2))
left-right-width (int (/ (- width size) 2))
left-right-height (- height (* 2 top-bottom-height))]
(cond-> {:position :absolute
:background-color :black
:opacity 0.7}
(= :top side) (assoc :height min-offset
:width width)
(= :right side) (assoc :height (- height min-offset)
:width min-offset
:bottom 0
:right 0)
(= :bottom side) (assoc :height min-offset
:width (- width min-offset)
(= :top side) (assoc :height top-bottom-height
:width top-bottom-width)
(= :right side) (assoc :height left-right-height
:width left-right-width
:top top-bottom-height
:right 0)
(= :bottom side) (assoc :height top-bottom-height
:width top-bottom-width
:bottom 0
:left 0)
(= :left side) (assoc :height (- height (* 2 min-offset))
:width min-offset
:top min-offset
:left 0))))
:left 0)
(= :left side) (assoc :height left-right-height
:width left-right-width
:top top-bottom-height
:left 0))))
(def qr-code
{:flex 1
:background-color colors/white-lighter-transparent
:align-items :center})
(defn qr-code-text [dimensions]
{:zIndex 1
:padding-top 20
:color :white
:text-align :center
:width (int (/ (:width dimensions) 2))})

View File

@ -1,100 +1,65 @@
(ns status-im.ui.screens.wallet.choose-recipient.views
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [clojure.string :as string]
[re-frame.core :as re-frame]
(:require [re-frame.core :as re-frame]
[status-im.i18n :as i18n]
[status-im.ui.components.bottom-buttons.view :as bottom-buttons]
[status-im.ui.components.button.view :as button]
[status-im.ui.components.camera :as camera]
[status-im.ui.components.icons.vector-icons :as vector-icons]
[status-im.ui.components.react :as react]
[status-im.ui.components.status-bar.view :as status-bar]
[status-im.ui.components.toolbar.actions :as actions]
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.ui.components.toolbar.actions :as act]
[status-im.i18n :as i18n]
[status-im.ui.screens.wallet.choose-recipient.styles :as styles]
[status-im.utils.platform :as platform]
[status-im.ui.screens.wallet.styles :as wallet.styles]))
[status-im.utils.platform :as platform]))
(defn choose-from-contacts []
(re-frame/dispatch [:navigate-to-modal
:contact-list-modal
{:handler #(re-frame/dispatch [:wallet/fill-request-from-contact %])
:action :send
:params {:hide-actions? true}}]))
(defn toolbar-view [camera-flashlight]
[toolbar/toolbar {:style wallet.styles/toolbar}
[toolbar/nav-button (act/back-white act/default-handler)]
[toolbar/content-title {:color :white} (i18n/label :t/wallet-choose-recipient)]
[toolbar/actions [{:icon (if (= :on camera-flashlight) :icons/flash-active
:icons/flash-inactive)
(defn- toolbar-view [camera-flashlight]
[toolbar/toolbar styles/toolbar
[toolbar/nav-button (actions/back-white actions/default-handler)]
[toolbar/content-title {:color :white}
(i18n/label :t/wallet-choose-recipient)]
[toolbar/actions [{:icon (if (= :on camera-flashlight)
:icons/flash-active
:icons/flash-inactive)
:icon-opts {:color :white}
:handler #(re-frame/dispatch [:wallet/toggle-flashlight])}]]])
:handler #(re-frame/dispatch [:wallet/toggle-flashlight])}]]])
(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 [:wallet/fill-request-from-url (string/trim-newline clipboard) nil])))}
[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]} size]
[react/view {:style styles/viewfinder-port}
[react/view {:style (styles/viewfinder-translucent height width size :top)}]
[react/view {:style (styles/viewfinder-translucent height width size :right)}]
[react/view {:style (styles/viewfinder-translucent height width size :bottom)}]
[react/view {:style (styles/viewfinder-translucent height width size :left)}]
[react/image {:source {:uri :corner_left_top}
:style (styles/corner-left-top height width size)}]
[react/image {:source {:uri :corner_right_top}
:style (styles/corner-right-top height width size)}]
[react/image {:source {:uri :corner_left_bottom}
:style (styles/corner-left-bottom height width size)}]
[react/image {:source {:uri :corner_right_bottom}
:style (styles/corner-right-bottom height width size)}]])
(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)}]]))
(defn- size [{:keys [height width]}]
(int (* 2 (/ (min height width) 3))))
(defview choose-recipient []
(letsubs [camera-dimensions [:wallet.send/camera-dimensions]
(letsubs [dimensions (react/get-dimensions "window")
camera-flashlight [:wallet.send/camera-flashlight]
camera-permitted? [:wallet.send/camera-permitted?]
view [:get :view-id]]
[react/view {:style styles/wallet-container}
[status-bar/status-bar {:type :wallet}]
[react/view {:style styles/qr-code}
[status-bar/status-bar {:type :transparent}]
[toolbar-view camera-flashlight]
[react/view {:style styles/qr-container
:pointerEvents :none
:on-layout #(let [layout (.. % -nativeEvent -layout)]
(re-frame/dispatch [:wallet.send/set-camera-dimensions
{:width (.-width layout)
:height (.-height layout)}]))}
(when (and
(= view :choose-recipient)
(or platform/android?
camera-permitted?))
[react/with-activity-indicator
{}
[camera/camera {:style styles/preview
:aspect :fill
:captureAudio false
:torchMode (camera/set-torch camera-flashlight)
:onBarCodeRead #(re-frame/dispatch [:wallet/fill-request-from-url (camera/get-qr-code-data %) nil])}]])
[viewfinder camera-dimensions]]
[recipient-buttons]]))
[react/text {:style (styles/qr-code-text dimensions)}
(i18n/label :t/scan-qr-code)]
[react/view {:style styles/qr-container
:pointer-events :none}
[react/with-activity-indicator
{}
[camera/camera {:style styles/preview
:aspect :fill
:captureAudio false
:torchMode (camera/set-torch camera-flashlight)
:onBarCodeRead #(re-frame/dispatch [:wallet/fill-request-from-url (camera/get-qr-code-data %) nil])}]]
[viewfinder dimensions (size dimensions)]]
[bottom-buttons/bottom-button
[button/button {:disabled? false :on-press #(re-frame/dispatch [:navigate-back])}
(i18n/label :t/cancel)]]]))

View File

@ -0,0 +1,75 @@
(ns status-im.ui.screens.wallet.components
"
Higher-level components used in the wallet screens.
"
(:require [status-im.utils.utils :as utils]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.icons.vector-icons :as vector-icons]
[status-im.ui.components.react :as react]
[status-im.ui.components.status-bar.view :as status-bar]
[status-im.ui.components.toolbar.actions :as actions]
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.ui.screens.wallet.styles :as styles]))
;; Wallet tab has a different coloring scheme (dark) that forces color changes (background, text)
;; It might be replaced by some theme mechanism
(defn text-input [props text]
[react/text-input (utils/deep-merge {:placeholder-text-color colors/white-lighter-transparent
:selection-color colors/white
:style {:color colors/white
:font-size 15
:height 52
:letter-spacing -0.2}}
props)
text])
(defn- toolbar [action title]
[toolbar/toolbar {:style styles/toolbar}
[toolbar/nav-button action]
[toolbar/content-title {:color :white}
title]])
(defn simple-screen [title content]
[react/view {:flex 1 :background-color colors/blue}
[status-bar/status-bar {:type :wallet}]
[toolbar (actions/back-white actions/default-handler)
title]
content])
(defn- cartouche-content [{:keys [disabled?]} content]
[react/view {:style (styles/cartouche-content-wrapper disabled?)}
[react/view {:flex 1}
content]])
(defn cartouche [{:keys [disabled? on-press icon] :or {icon :icons/forward} :as m} header content]
[react/view {:style styles/cartouche-container}
[react/text {:style styles/cartouche-header}
header]
(if (or disabled? (nil? on-press))
[cartouche-content m content]
[react/touchable-highlight {:on-press on-press}
[react/view
[cartouche-content m
(if-not (true? disabled?)
[react/view styles/cartouche-icon-wrapper
[react/view {:flex 1} ;; Let content shrink if needed
content]
[vector-icons/icon icon {:color :white}]]
content)]]])])
(defn- cartouche-primary-text [s]
[react/text {:style styles/cartouche-primary-text}
s])
(defn- cartouche-secondary-text [s]
[react/text {:style styles/cartouche-secondary-text}
s])
(defn cartouche-text-content [primary secondary]
[react/view {:flex-direction :row
:justify-content :space-between
:padding-horizontal 15
:padding-vertical 15}
[cartouche-primary-text primary]
[cartouche-secondary-text secondary]])

View File

@ -7,6 +7,6 @@
(animation/parallel
[(animation/timing opacity-value {:toValue 1
:duration 500})
(animation/timing bottom-value {:toValue 8.5
(animation/timing bottom-value {:toValue -40
:easing (.bezier (animation/easing) 0.685, 0.000, 0.025, 1.185)
:duration 500})]))))
:duration 500})]))))

View File

@ -1,24 +1,23 @@
(ns status-im.ui.screens.wallet.components.styles
(:require-macros [status-im.utils.styles :refer [defnstyle defstyle]])
(:require [status-im.ui.components.styles :as styles]))
(:require [status-im.ui.components.colors :as colors]
[status-im.ui.components.styles :as styles]))
(def text-content
{:color :white})
{:color colors/white})
(def text-secondary-content
{:color styles/color-white-transparent})
{:color colors/white-lighter-transparent})
(def text
{:margin-right 10})
(def text-list-primary-content
(merge text {:color styles/color-black}))
(merge text {:color colors/black}))
(def text-input
(merge text-content
{:padding-left 14
:padding-right 14
:font-size 15
{:font-size 15
:padding-bottom 0
:padding-top 0
:height 52
@ -36,15 +35,14 @@
(def label-transparent
(merge label
{:color styles/color-white-transparent}))
{:color colors/white-lighter-transparent}))
(defnstyle amount-container [active?]
(defn amount-container [active?]
{:height 52
:background-color (if active?
styles/color-white-transparent-4
colors/white-light-transparent
styles/color-white-transparent-3)
:ios {:border-radius 8}
:android {:border-radius 4}})
:border-radius 8})
(def network
{:color :white
@ -56,11 +54,11 @@
:height 27
:border-radius 100
:border-width 1
:border-color styles/color-white-transparent-4
:border-color colors/white-light-transparent
:align-items :center
:justify-content :center})
(defstyle asset-container
(def asset-container
{:margin-top 8
:height 52
:background-color styles/color-white-transparent-3
@ -68,12 +66,23 @@
:padding-left 14
:padding-vertical 14
:padding-right 8
:ios {:border-radius 8}
:android {:border-radius 4}})
:border-radius 8})
(def asset-container-read-only
{:margin-top 8
:height 52
:border-color styles/color-white-transparent-3
:border-width 1
:justify-content :center
:padding-left 14
:padding-vertical 14
:padding-right 8
:border-radius 8})
(def asset-content-container
{:flex-direction :row
:align-items :center})
{:flex-direction :row
:align-items :center
:margin-vertical 11})
(def asset-icon
{:background-color styles/color-gray9
@ -97,37 +106,19 @@
(defstyle container-disabled
{:border-width 1
:border-color styles/color-white-transparent-4
:border-color colors/white-light-transparent
:background-color nil
:ios {:border-radius 8}
:android {:border-radius 4}})
:border-radius 8})
(defstyle recipient-container
{:flex-direction :row
:flex 1
:margin-top 8
:height 52
:align-items :center
:background-color styles/color-white-transparent-3
:padding-vertical 14
:padding-left 14
:padding-right 8
:ios {:border-radius 8}
:android {:border-radius 4}})
(defstyle wallet-container
(def wallet-container
{: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
:border-color colors/white-light-transparent
:align-items :center
:padding 14
:ios {:border-radius 8}
:android {:border-radius 4}})
:border-radius 8})
(def wallet-name
{:color :white
@ -135,11 +126,34 @@
:letter-spacing -0.2})
(defn participant [address?]
{:color (if address? :white styles/color-white-transparent)
{:color (if address? :white colors/white-lighter-transparent)
:flex-shrink 1
:font-size 15
:letter-spacing -0.2})
(def recipient-container
{:flex-direction :row})
(def recipient-icon
{:margin-top 11})
(def recipient-name
{:flex 1
:flex-direction :column
:margin-horizontal 12
:margin-vertical 16})
(def recipient-address
{:margin-vertical 17
:color colors/white})
(def recipient-no-address
{:color colors/white-lighter-transparent})
(def recent-recipients
{:flex 1
:background-color colors/white})
(def wallet-value-container
{:flex 1
:flex-direction :row})
@ -191,31 +205,3 @@
(def tooltip-triangle
{:width 16
:height 8})
(def recipient-name-container
{:padding-right 6})
(def today-variation-container
{:border-radius 100
:margin-left 8
:padding-horizontal 8
:padding-vertical 4})
(def today-variation-container-positive
(merge today-variation-container
{:background-color styles/color-green-1}))
(def today-variation-container-negative
(merge today-variation-container
{:background-color styles/color-red-3}))
(def today-variation
{:font-size 12})
(def today-variation-positive
(merge today-variation
{:color styles/color-green-2}))
(def today-variation-negative
(merge today-variation
{:color styles/color-red-4}))

View File

@ -1,64 +1,44 @@
(ns status-im.ui.screens.wallet.components.views
(:require-macros [status-im.utils.views :as views])
(:require [reagent.core :as reagent]
(:require [clojure.string :as string]
[reagent.core :as reagent]
[re-frame.core :as re-frame]
[status-im.ui.components.react :as react]
[status-im.ui.components.status-bar.view :as status-bar]
[status-im.ui.components.styles :as components.styles]
[status-im.ui.screens.wallet.components.styles :as styles]
[status-im.i18n :as i18n]
[status-im.ui.components.animation :as animation]
[status-im.ui.components.bottom-buttons.view :as bottom-buttons]
[status-im.ui.components.button.view :as button]
[status-im.ui.components.chat-icon.screen :as chat-icon]
[status-im.ui.components.icons.vector-icons :as vector-icons]
[status-im.ui.components.list.views :as list]
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.ui.components.list.styles :as list.styles]
[status-im.ui.components.list-selection :as list-selection]
[status-im.ui.components.react :as react]
[status-im.ui.components.styles :as components.styles]
[status-im.ui.screens.wallet.components :as components]
[status-im.ui.screens.wallet.components.animations :as animations]
[status-im.ui.screens.wallet.components.styles :as styles]
[status-im.ui.screens.wallet.choose-recipient.views :as choose-recipient]
[status-im.ui.screens.wallet.main.views :as main]
[status-im.ui.screens.wallet.utils :as wallet.utils]
[status-im.utils.ethereum.core :as ethereum]
[status-im.utils.ethereum.tokens :as tokens]
[status-im.utils.platform :as platform]
[status-im.ui.screens.wallet.components.animations :as animations]
[status-im.ui.screens.wallet.main.views :as main]
[status-im.ui.screens.wallet.utils :as wallet.utils]))
[status-im.utils.platform :as platform]))
(views/defview tooltip [label & [style]]
(views/letsubs [bottom-value (animation/create-value 16)
(views/defview tooltip [label]
(views/letsubs [bottom-value (animation/create-value -30)
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/animated-view {:style (styles/tooltip-animated bottom-value opacity-value)}
[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 disabled?]}]]
(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?) (if disabled? styles/container-disabled style))
[react/text-input
(merge
{:style styles/text-input}
(if disabled?
{:editable false}
{:keyboard-type :numeric
:placeholder (i18n/label :t/amount-placeholder)
:placeholder-text-color components.styles/color-white-transparent
:selection-color components.styles/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])]]))))
(views/defview view-asset [symbol]
(defn view-asset [symbol]
[react/view
[react/text {:style styles/label}
(i18n/label :t/wallet-asset)]
[react/view styles/asset-container
[react/view styles/asset-container-read-only
[react/text {:style styles/asset-text}
(name symbol)]]])
@ -76,11 +56,9 @@
[list/item-image icon]
[list/item-content
[react/view {:flex-direction :row}
[react/text {:style (merge styles/text-list-primary-content)
:number-of-lines 1}
[react/text {:style styles/text-list-primary-content}
name]
[react/text {:uppercase? true
:number-of-lines 1}
[react/text {:uppercase? true}
(clojure.core/name symbol)]]
[list/item-secondary (wallet.utils/format-amount (symbol balance) decimals)]]]]])
@ -88,14 +66,11 @@
(views/letsubs [network [:network]
visible-tokens [:wallet.settings/visible-tokens]
balance [:balance]]
[react/view components.styles/flex
[status-bar/status-bar {}]
[toolbar/toolbar {}
[toolbar/nav-clear-text (i18n/label :t/done)]
[toolbar/content-title (i18n/label :t/wallet-assets)]]
[react/view {:style components.styles/flex}
[list/flat-list {:data (concat [tokens/ethereum] (main/current-tokens visible-tokens network))
:render-fn #(render-token % balance type)}]]]))
[components/simple-screen (i18n/label :t/wallet-assets)
[react/view {:style (assoc components.styles/flex :background-color :white)}
[list/flat-list {:default-separator? true
:data (concat [tokens/ethereum] (main/current-tokens visible-tokens network))
:render-fn #(render-token % balance type)}]]]))
(defn send-assets []
[assets :send])
@ -109,54 +84,115 @@
:request :wallet-request-assets
(throw (str "Unknown type: " k))))
(views/defview choose-asset [{:keys [type symbol]}]
(views/defview asset-selector [{:keys [disabled? type symbol]}]
(views/letsubs [balance [:balance]
network [:network]]
(let [{:keys [name icon decimals]} (tokens/asset-for (ethereum/network->chain-keyword network) symbol)]
[react/view
[react/text {:style styles/label}
(i18n/label :t/wallet-asset)]
[react/touchable-highlight {:style styles/asset-container
:on-press #(re-frame/dispatch [:navigate-to (type->view type)])}
[react/view styles/asset-content-container
[list/item-image (assoc icon :style styles/asset-icon :image-style {:width 32 :height 32})]
[react/view styles/asset-text-content
[react/view styles/asset-label-content
[react/text {:style (merge styles/text-content styles/asset-label)}
name]
[react/text {:style styles/text-secondary-content}
(clojure.core/name symbol)]]
[react/text {:style (merge styles/text-secondary-content styles/asset-label)}
(str (wallet.utils/format-amount (symbol balance) decimals))]]
[vector-icons/icon :icons/forward {:color :white}]]]])))
[components/cartouche {:disabled? disabled? :on-press #(re-frame/dispatch [:navigate-to (type->view type)])}
(i18n/label :t/wallet-asset)
[react/view styles/asset-content-container
[list/item-image (assoc icon :style styles/asset-icon :image-style {:width 32 :height 32})]
[react/view styles/asset-text-content
[react/view styles/asset-label-content
[react/text {:style (merge styles/text-content styles/asset-label)}
name]
[react/text {:style styles/text-secondary-content}
(clojure.core/name symbol)]]
[react/text {:style (merge styles/text-secondary-content styles/asset-label)}
(str (wallet.utils/format-amount (symbol balance) decimals))]]]])))
(defn choose-recipient-content [{:keys [address name on-press style]}]
(let [address? (and (not (nil? address)) (not= address ""))]
[react/view
[react/text {:style styles/label} (i18n/label :t/recipient)]
[react/view (merge styles/recipient-container
(when-not on-press styles/container-disabled)
style)
(when name
[react/view styles/recipient-name-container
[react/text {:style (styles/participant true)
:number-of-lines 1}
name]])
(defn- recipient-address [address]
[react/text {:style (merge styles/recipient-address (when-not address styles/recipient-no-address))}
(or (ethereum/normalized-address address) (i18n/label :t/specify-recipient))])
(views/defview recipient-contact [address name]
(views/letsubs [contact [:contact/by-address address]]
(let [address? (and (not (nil? address)) (not= address ""))]
[react/view styles/recipient-container
[react/view styles/recipient-icon
[chat-icon/chat-icon (:photo-path contact) {:size list.styles/image-size}]]
[react/view styles/recipient-name
[react/text {:style (styles/participant true)
:number-of-lines 1}
name]
[react/text {:style (styles/participant (and (not name) address?))}
(ethereum/normalized-address address)]]])))
(defn render-contact [contact]
[list/touchable-item #(re-frame/dispatch [:wallet/fill-request-from-contact contact])
[list/item
[chat-icon/chat-icon (:photo-path contact) {:size list.styles/image-size}]
[list/item-content
[list/item-primary (:name contact)]
[react/text {:style list.styles/secondary-text}
(ethereum/normalized-address (:address contact))]]]])
(views/defview recent-recipients []
(views/letsubs [contacts [:contacts-filtered :all-added-people-contacts]]
[components/simple-screen
(i18n/label :t/recipient)
[react/view styles/recent-recipients
[list/flat-list {:data contacts
:render-fn render-contact}]]]))
(defn contact-code []
(let [content (reagent/atom nil)]
(fn []
[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...")]]
(when on-press
[vector-icons/icon :icons/forward {:color :white}])]]))
[components/simple-screen
(i18n/label :t/recipient)
[react/view components.styles/flex
[components/cartouche {}
(i18n/label :t/recipient)
[components/text-input {:multiline true
:placeholder (i18n/label :t/recipient-code)
:on-change-text #(reset! content %)}]]
[bottom-buttons/bottom-button
[button/button {:disabled? (string/blank? @content)
:on-press #(re-frame/dispatch [:wallet/fill-request-from-url @content])}
(i18n/label :t/done)]]]]])))
(defn choose-recipient [{:keys [on-press] :as m}]
(if on-press
[react/touchable-highlight {:on-press on-press}
[react/view ;; TODO(jeluard) remove extra view when migrating to latest RN
[choose-recipient-content m]]]
[react/view
[choose-recipient-content m]]))
(defn recipient-qr-code []
[choose-recipient/choose-recipient])
(defn- request-camera-permissions []
(re-frame/dispatch [:request-permissions [:camera]
#(re-frame/dispatch [:navigate-to :recipient-qr-code])]))
(defn- on-choose-recipient []
(list-selection/show {:title (i18n/label :t/wallet-choose-recipient)
:options [{:label (i18n/label :t/recent-recipients)
:action #(re-frame/dispatch [:navigate-to :recent-recipients])}
{:label (i18n/label :t/scan-qr)
:action request-camera-permissions}
{:label (i18n/label :t/enter-contact-code)
:action #(re-frame/dispatch [:navigate-to :contact-code])}]}))
(defn recipient-selector [{:keys [name address disabled?]}]
[components/cartouche {:on-press on-choose-recipient :disabled? disabled? :icon :icons/dots-horizontal}
(i18n/label :t/wallet-choose-recipient)
(if name
[recipient-contact address name]
[recipient-address address])])
(defn- amount-input [{:keys [input-options disabled?]}]
[react/view components.styles/flex
[components/text-input
(merge
(if disabled?
{:editable false}
{:keyboard-type :numeric
:placeholder (i18n/label :t/amount-placeholder)
:style components.styles/flex})
input-options)]])
(defn amount-selector [{:keys [error disabled?] :as m}]
[react/view
[components/cartouche {:disabled? disabled?}
(i18n/label :t/amount)
[amount-input m]]
(when error
[tooltip error])])
(defn separator []
[react/view styles/separator])
@ -165,15 +201,3 @@
[react/text {:style styles/button-text
:font (if platform/android? :medium :default)
:uppercase? (get-in platform/platform-specific [:uppercase?])} label])
(defn change-display [change]
(let [pos-change? (or (pos? change) (zero? change))]
[react/view {:style (if pos-change?
styles/today-variation-container-positive
styles/today-variation-container-negative)}
[react/text {:style (if pos-change?
styles/today-variation-positive
styles/today-variation-negative)}
(if change
(str (when pos-change? "+") change "%")
"-%")]]))

View File

@ -38,4 +38,4 @@
{:error (i18n/label :t/validation-amount-is-too-precise) :value value}
:else
{:value value}))))
{:value value}))))

View File

@ -66,7 +66,7 @@
{:background-color colors/blue})
(def action
{:background-color colors/white-light-transparent
{:background-color colors/white-transparent
:border-radius 50})
(def action-label
@ -74,7 +74,7 @@
(def action-separator
{:height 1
:background-color colors/white-transparent
:background-color colors/white-light-transparent
:margin-left 70})
;; Assets section
@ -116,6 +116,3 @@
{:font-size 16
:color styles/color-gray4
:margin-left 6})
(defn asset-border [color]
{:border-color color :border-width 1 :border-radius 32})

View File

@ -2,7 +2,6 @@
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [re-frame.core :as re-frame]
[status-im.i18n :as i18n]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.styles :as components.styles]
[status-im.ui.components.list.views :as list]
[status-im.ui.components.react :as react]
@ -18,12 +17,12 @@
[status-im.utils.platform :as platform]))
(defn toolbar-view []
[toolbar/toolbar {:style wallet.styles/toolbar}
[toolbar/toolbar {:style wallet.styles/toolbar :flat? true}
nil
[toolbar/content-wrapper]
[toolbar/actions
[(assoc (act/opts [{:text (i18n/label :t/wallet-manage-assets)
:value #(re-frame/dispatch [:navigate-to-modal :wallet-settings-assets])}])
[(assoc (act/opts [{:label (i18n/label :t/wallet-manage-assets)
:action #(re-frame/dispatch [:navigate-to-modal :wallet-settings-assets])}])
:icon-opts {:color :white})]]])
(defn- total-section [usd-value syncing? error-message]
@ -38,23 +37,23 @@
[react/text {:style styles/total-balance-currency} (i18n/label :t/usd-currency)]]
[react/text {:style styles/total-value} (i18n/label :t/wallet-total-value)]]])
(defn- render-action [{:keys [name icon action]}]
(defn- render-action [{:keys [label icon action]}]
[react/touchable-highlight {:on-press action}
[react/view
[list/item
[list/item-icon {:icon icon :style styles/action :icon-opts {:color :white}}]
[list/item-primary-only {:style styles/action-label}
name]
label]
list/item-icon-forward]]])
(def actions
[{:name (i18n/label :t/send-transaction)
[{:label (i18n/label :t/send-transaction)
:icon :icons/arrow-right
:action #(re-frame/dispatch [:navigate-to :wallet-send-transaction])}
{:name (i18n/label :t/receive-transaction)
{:label (i18n/label :t/receive-transaction)
:icon :icons/arrow-left
:action #(re-frame/dispatch [:navigate-to :wallet-request-transaction])}
{:name (i18n/label :t/transaction-history)
{:label (i18n/label :t/transaction-history)
:icon :icons/transaction-history
:action #(re-frame/dispatch [:navigate-to :transactions-history])}])

View File

@ -6,12 +6,11 @@
{:flex 1
:align-items :center})
(defstyle qr-container
(def qr-container
{:margin-top 8
:padding 16
:background-color styles/color-white
:ios {:border-radius 8}
:android {:border-radius 4}})
:border-radius 8})
(def share-icon-container
{:margin-right 8})

View File

@ -1,24 +1,24 @@
(ns status-im.ui.screens.wallet.request.views
(:require-macros [status-im.utils.views :as views])
(:require [re-frame.core :as re-frame]
[status-im.i18n :as i18n]
[status-im.ui.components.common.common :as common]
[status-im.ui.components.icons.vector-icons :as vector-icons]
[status-im.ui.components.react :as react]
[status-im.ui.components.qr-code-viewer.views :as components.qr-code-viewer]
[status-im.ui.components.status-bar.view :as status-bar]
[status-im.ui.components.styles :as components.styles]
[status-im.ui.components.qr-code-viewer.views :as qr-code-viewer]
[status-im.ui.components.toolbar.actions :as actions]
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.ui.components.status-bar.view :as status-bar]
[status-im.ui.screens.wallet.styles :as wallet.styles]
[status-im.ui.components.common.common :as common]
[status-im.ui.components.icons.vector-icons :as vi]
[status-im.ui.screens.wallet.components.views :as components]
[status-im.ui.screens.wallet.request.styles :as styles]
[status-im.ui.components.styles :as components.styles]
[status-im.i18n :as i18n]
[status-im.ui.screens.wallet.styles :as wallet.styles]
[status-im.utils.ethereum.core :as ethereum]
[status-im.utils.ethereum.eip681 :as eip681]
[status-im.utils.ethereum.tokens :as tokens]))
(defn toolbar-view []
[toolbar/toolbar {:style wallet.styles/toolbar :hide-border? true}
[toolbar/toolbar {:style wallet.styles/toolbar}
[toolbar/nav-button (actions/back-white actions/default-handler)]
[toolbar/content-title {:color :white} (i18n/label :t/request-transaction)]])
@ -37,7 +37,7 @@
(views/defview qr-code [amount symbol]
(views/letsubs [account [:get-current-account]
chain-id [:get-network-id]]
[components.qr-code-viewer/qr-code
[qr-code-viewer/qr-code
(let [address (ethereum/normalized-address (:address account))
params {:chain-id chain-id :value amount :symbol (or symbol :ETH)}]
{:value (generate-value address params)
@ -61,22 +61,20 @@
[react/with-activity-indicator
{:style wallet.styles/qr-code-preview}
[qr-code amount symbol]]]]
[react/view wallet.styles/amount-container
[components/amount-input
{:error amount-error
:input-options {:on-focus (fn [] (when @scroll (js/setTimeout #(.scrollToEnd @scroll) 100)))
:on-change-text #(re-frame/dispatch [:wallet.request/set-and-validate-amount %])}}]]
[react/view wallet.styles/choose-asset-container
[components/choose-asset {:type :request
:symbol symbol}]]]]
[components/amount-selector
{:error amount-error
:input-options {:on-focus (fn [] (when @scroll (js/setTimeout #(.scrollToEnd @scroll) 100)))
:on-change-text #(re-frame/dispatch [:wallet.request/set-and-validate-amount %])}}]
[components/asset-selector {:type :request
:symbol symbol}]]]
[components/separator]
[react/view wallet.styles/buttons-container
[react/touchable-highlight {:style wallet.styles/button :disabled true}
[react/view (wallet.styles/button-container false)
[vi/icon :icons/share {:color :white :container-style styles/share-icon-container}]
[vector-icons/icon :icons/share {:color :white :container-style styles/share-icon-container}]
[components/button-text (i18n/label :t/share)]]]
[react/view components.styles/flex]
[react/touchable-highlight {:style wallet.styles/button :disabled (not request-enabled?) :on-press 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}]]]]]))
[vector-icons/icon :icons/forward {:color :white :container-style wallet.styles/forward-icon-container}]]]]]))

View File

@ -15,9 +15,7 @@
(spec/def ::later? (spec/nilable boolean?))
(spec/def ::height double?)
(spec/def ::width double?)
(spec/def ::camera-dimensions (spec/keys :req-un [::height ::width]))
(spec/def ::camera-flashlight #{:on :off})
(spec/def ::camera-permitted? boolean?)
(spec/def ::in-progress? boolean?)
(spec/def ::from-chat? (spec/nilable boolean?))
(spec/def ::symbol (spec/nilable keyword?))
@ -28,6 +26,6 @@
(spec/def :wallet/send-transaction (allowed-keys
:opt-un [::amount ::to ::to-name ::amount-error ::password
::waiting-signal? ::signing? ::id ::later?
::camera-dimensions ::camera-flashlight ::in-progress?
::wrong-password? ::camera-permitted? ::from-chat? ::symbol ::advanced?
::camera-flashlight ::in-progress?
::wrong-password? ::from-chat? ::symbol ::advanced?
::gas ::gas-price]))

View File

@ -255,11 +255,6 @@
:signing? false
:wrong-password? false)}))
(handlers/register-handler-fx
:wallet.send/set-camera-dimensions
(fn [{:keys [db]} [_ camera-dimensions]]
{:db (assoc-in db [:wallet :send-transaction :camera-dimensions] camera-dimensions)}))
(handlers/register-handler-fx
:wallet.send/set-password
(fn [{:keys [db]} [_ password]]

View File

@ -1,29 +1,11 @@
(ns status-im.ui.screens.wallet.send.styles
(:require-macros [status-im.utils.styles :refer [defnstyle defstyle]])
(:require [status-im.ui.components.styles :as styles]
(:require [status-im.ui.components.colors :as colors]
[status-im.ui.components.styles :as styles]
[status-im.ui.screens.wallet.components.styles :as wallet.components.styles]))
(def toolbar
{:background-color styles/color-blue5
:elevation 0
:padding-bottom 10})
(def toolbar-title-container
(def send-transaction-form
{:flex 1
:flex-direction :row
:margin-left 6})
(def toolbar-title-text
{:color styles/color-white
:font-size 17
:margin-right 4})
(def toolbar-icon
{:width 24
:height 24})
(def toolbar-title-icon
(merge toolbar-icon {:opacity 0.4}))
:padding-bottom 60})
(defn animated-sign-panel [bottom-value]
{:position :absolute
@ -34,7 +16,7 @@
(defn sign-panel [opacity-value]
{:opacity opacity-value
:border-radius 8
:background-color :white
:background-color colors/white
:padding-top 12
:padding-horizontal 12})
@ -43,12 +25,12 @@
:height 36
:align-items :center
:justify-content :center
:background-color styles/color-light-gray})
:background-color colors/gray-lighter})
(def signing-phrase
{:font-size 15
:letter-spacing -0.2
:color :black})
:color colors/black})
(def signing-phrase-description
{:padding-top 8})
@ -71,13 +53,16 @@
:left 0
:align-items :center
:justify-content :center
:background-color (str styles/color-black "1A")})
:background-color (str colors/black "1A")})
(def empty-text
{:text-align :center
:margin-top 22
:margin-horizontal 92})
(def advanced-cartouche
{:padding-bottom 50})
(def advanced-button
{:flex-direction :row
:background-color styles/color-blue6
@ -89,63 +74,43 @@
{:align-items :center})
(def advanced-wrapper
{:margin-horizontal 15})
{:margin-top 24
:margin-bottom 16})
(def advanced-options-wrapper
{:height 52
:background-color styles/color-white-transparent-3
:border-radius 4
:margin-top 16
:margin-bottom 16
:align-items :center
:flex-direction :row
:padding-vertical 14
:padding-right 8
:ios {:border-radius 8}
:android {:border-radius 4}})
{:align-items :center
:flex-direction :row})
(def advanced-options-text-wrapper
{:flex 1
:flex-direction :row
:justify-content :space-between
:margin-horizontal 15})
{:flex 1
:flex-direction :row
:justify-content :space-between
:margin-vertical 15})
(def advanced-label
{:text-align-vertical :center
:margin-left 4})
(def advanced-fees-text
{:color styles/color-white})
{:color colors/white})
(def advanced-fees-details-text
{:color styles/color-white-transparent})
{:color colors/white-lighter-transparent})
(def transaction-fee-block-wrapper
{:flex-direction :row
:margin-top 15})
(def transaction-fee-column-wrapper
{:flex 0.5
:margin-horizontal 15})
(def transaction-fee-bubble
(merge advanced-options-wrapper
{:flex-direction :row
:justify-content :space-between
:padding-horizontal 15}))
(def transaction-fee-bubble-read-only
(merge transaction-fee-bubble
{:background-color styles/color-blue6}))
{:flex-direction :row})
(def transaction-fee-info
{:margin 15})
(def transaction-fee-input
{:flex 1
:keyboard-type :numeric
{:keyboard-type :numeric
:auto-capitalize "none"
:placeholder "0.000"
:placeholder-text-color styles/color-white-transparent
:selection-color :white
:style wallet.components.styles/text-input})
:placeholder-text-color colors/white-lighter-transparent
:selection-color colors/white
:style wallet.components.styles/text-input})
(def sign-buttons
{:background-color colors/blue
:padding-vertical 8})

View File

@ -31,21 +31,11 @@
(fn [send-transaction]
(:advanced? send-transaction)))
(re-frame/reg-sub :wallet.send/camera-dimensions
:<- [::send-transaction]
(fn [send-transaction]
(:camera-dimensions send-transaction)))
(re-frame/reg-sub :wallet.send/camera-flashlight
:<- [::send-transaction]
(fn [send-transaction]
(:camera-flashlight send-transaction)))
(re-frame/reg-sub :wallet.send/camera-permitted?
:<- [::send-transaction]
(fn [send-transaction]
(:camera-permitted? send-transaction)))
(re-frame/reg-sub :wallet.send/wrong-password?
:<- [::send-transaction]
(fn [send-transaction]
@ -79,7 +69,7 @@
(re-frame/reg-sub :wallet.send/transaction
:<- [::send-transaction]
:<- [:balance]
(fn [[{:keys [amount to symbol] :as transaction} balance]]
(fn [[{:keys [amount symbol] :as transaction} balance]]
(assoc transaction :sufficient-funds? (or (nil? amount)
(money/sufficient-funds? amount (get balance symbol))))))

View File

@ -2,9 +2,9 @@
(:require [re-frame.core :as re-frame]
[status-im.i18n :as i18n]
[status-im.ui.components.animation :as animation]
[status-im.ui.components.camera :as camera]
[status-im.ui.components.bottom-buttons.view :as bottom-buttons]
[status-im.ui.components.button.view :as button]
[status-im.ui.components.common.common :as common]
[status-im.ui.components.styles :as styles]
[status-im.ui.components.icons.vector-icons :as vector-icons]
[status-im.ui.components.react :as react]
[status-im.ui.components.status-bar.view :as status-bar]
@ -13,11 +13,11 @@
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.ui.screens.wallet.components.styles :as wallet.components.styles]
[status-im.ui.screens.wallet.components.views :as components]
[status-im.ui.screens.wallet.components :as wallet.components]
[status-im.ui.screens.wallet.send.animations :as send.animations]
[status-im.ui.screens.wallet.send.styles :as send.styles]
[status-im.ui.screens.wallet.styles :as wallet.styles]
[status-im.utils.money :as money]
[status-im.utils.platform :as platform]
[status-im.utils.utils :as utils])
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
@ -56,16 +56,15 @@
;; "Cancel" and "Sign Transaction >" buttons, signing with password
(defview signing-buttons [cancel-handler sign-handler in-progress?]
(letsubs [sign-enabled? [:wallet.send/sign-password-enabled?]]
[react/view wallet.styles/buttons-container
[react/touchable-highlight {:style wallet.styles/button :on-press cancel-handler}
[react/view (wallet.styles/button-container true)
[components/button-text (i18n/label :t/cancel)]]]
[react/view components.styles/flex]
[react/touchable-highlight {:style wallet.styles/button :on-press sign-handler}
[react/view (wallet.styles/button-container sign-enabled?)
(when in-progress? [react/activity-indicator {:animating? true}])
[components/button-text (i18n/label :t/transactions-sign-transaction)]
[vector-icons/icon :icons/forward {:color :white :container-style wallet.styles/forward-icon-container}]]]]))
[bottom-buttons/bottom-buttons
send.styles/sign-buttons
[button/button {:style components.styles/flex
:on-press cancel-handler}
(i18n/label :t/cancel)]
[button/button {:style (wallet.styles/button-container sign-enabled?)
:on-press sign-handler}
(i18n/label :t/transactions-sign-transaction)
[vector-icons/icon :icons/forward {:color :white :container-style wallet.styles/forward-icon-container}]]]))
(defn- sign-enabled? [amount-error to amount]
(and
@ -75,46 +74,30 @@
;; "Sign Later" and "Sign Transaction >" buttons
(defn- sign-buttons [amount-error to amount sufficient-funds? sign-later-handler]
(let [sign-enabled? (sign-enabled? amount-error to amount)
(let [sign-enabled? (sign-enabled? amount-error to amount)
immediate-sign-enabled? (and sign-enabled? sufficient-funds?)]
[react/view wallet.styles/buttons-container
[bottom-buttons/bottom-buttons
send.styles/sign-buttons
(when sign-enabled?
[react/touchable-highlight {:style wallet.styles/button :on-press sign-later-handler}
[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 {:style wallet.styles/button
:on-press (when immediate-sign-enabled? #(re-frame/dispatch [:wallet.send/set-signing? true]))}
[react/view (wallet.styles/button-container immediate-sign-enabled?)
[components/button-text (i18n/label :t/transactions-sign-transaction)]
[vector-icons/icon :icons/forward {:color :white :container-style wallet.styles/forward-icon-container}]]]]))
[button/button {:style components.styles/flex
:on-press sign-later-handler}
(i18n/label :t/transactions-sign-later)])
[button/button {:style components.styles/flex
:disabled? (not immediate-sign-enabled?)
:on-press #(re-frame/dispatch [:wallet.send/set-signing? true])
:text-style {:color :white}}
(i18n/label :t/transactions-sign-transaction)
[vector-icons/icon :icons/forward {:color (if immediate-sign-enabled? :white :gray) :container-style wallet.styles/forward-icon-container}]]]))
(defn- request-camera-permissions []
(when platform/android?
(re-frame/dispatch [:request-permissions [:camera]]))
(camera/request-access
(fn [permitted?]
(re-frame/dispatch [:set-in [:wallet :send-transaction :camera-permitted?] permitted?])
(re-frame/dispatch [:navigate-to :choose-recipient]))))
(defn handler [discard?]
(if discard?
#(re-frame/dispatch [:wallet/discard-transaction-navigate-back])
act/default-handler))
(defn- toolbar-modal [from-chat?]
(defn- toolbar [discard? action title]
[toolbar/toolbar {:style wallet.styles/toolbar}
[toolbar/nav-button (act/close-white (if from-chat?
#(re-frame/dispatch [:wallet/discard-transaction-navigate-back])
act/default-handler))]
[toolbar/content-title {:color :white} (i18n/label :t/send-transaction)]])
(defn- toolbar-view [signing?]
[toolbar/toolbar {:style wallet.styles/toolbar}
[toolbar/nav-button (act/back-white (if signing?
#(re-frame/dispatch [:wallet/discard-transaction-navigate-back])
act/default-handler))]
[toolbar/content-title {:color :white} (i18n/label :t/send-transaction)]])
(defn- transaction-fee-toolbar []
[toolbar/toolbar {:style wallet.styles/toolbar}
[toolbar/nav-button (act/close-white #(re-frame/dispatch [:wallet/discard-transaction-navigate-back]))]
[toolbar/content-title {:color :white} (i18n/label :t/wallet-transaction-fee)]])
[toolbar/nav-button (action (handler discard?))]
[toolbar/content-title {:color :white} title]])
(defn- max-fee [gas gas-price]
(when (and gas gas-price)
@ -125,109 +108,83 @@
[react/keyboard-avoiding-view wallet.styles/wallet-modal-container
[react/view components.styles/flex
[status-bar/status-bar {:type :modal-wallet}]
[transaction-fee-toolbar]
[react/view send.styles/transaction-fee-block-wrapper
[react/view send.styles/transaction-fee-column-wrapper
[react/text {:style send.styles/advanced-fees-text}
(i18n/label :t/gas-limit)]
[toolbar true act/close-white
(i18n/label :t/wallet-transaction-fee)]
[react/view {:flex-direction :row}
[wallet.components/cartouche {}
(i18n/label :t/gas-limit)
[react/text-input (merge send.styles/transaction-fee-input
{:on-change-text #(re-frame/dispatch [:wallet.send/set-gas %])
:default-value (str (money/to-fixed gas))})]]
[wallet.components/cartouche {}
(i18n/label :t/gas-price)
[react/view send.styles/advanced-options-wrapper
[react/text-input (merge send.styles/transaction-fee-input
{:on-change-text #(re-frame/dispatch [:wallet.send/set-gas %])
:default-value (str (money/to-fixed gas))})]]]
[react/view send.styles/transaction-fee-column-wrapper
[react/text {:style send.styles/advanced-fees-text}
(i18n/label :t/gas-price)]
[react/view send.styles/transaction-fee-bubble
[react/text-input (merge send.styles/transaction-fee-input
{:on-change-text #(re-frame/dispatch [:wallet.send/set-gas-price (money/->wei :gwei %)])
:default-value (str (money/to-fixed (money/wei-> :gwei gas-price)))})]
[react/text {:style send.styles/advanced-fees-details-text}
"Gwei"]]]]
[wallet.components/cartouche-secondary-text
(i18n/label :t/gwei)]]]]
[react/view send.styles/transaction-fee-info
[react/text {:style send.styles/advanced-fees-text}
(i18n/label :t/wallet-transaction-fee-details)]]
[components/separator]
[react/view send.styles/transaction-fee-block-wrapper
[react/view send.styles/transaction-fee-column-wrapper
[react/text {:style send.styles/advanced-fees-text}
(i18n/label :t/amount)]
[react/view send.styles/transaction-fee-bubble-read-only
[react/text {:style send.styles/advanced-fees-text}
(str (money/to-fixed (money/wei->ether amount)))]
[react/text {:style send.styles/advanced-fees-details-text}
(name symbol)]]]
[react/view send.styles/transaction-fee-column-wrapper
[react/text {:style send.styles/advanced-fees-text}
(i18n/label :t/wallet-transaction-total-fee)]
[react/view send.styles/transaction-fee-bubble-read-only
[react/text {:style send.styles/advanced-fees-text}
(str (money/to-fixed (max-fee gas gas-price)))]
[react/text {:style send.styles/advanced-fees-details-text}
"ETH"]]]]]]))
[wallet.components/cartouche {:disabled? true}
(i18n/label :t/amount)
[wallet.components/cartouche-text-content
(str (money/to-fixed (money/wei->ether amount)))
(name symbol)]]
[wallet.components/cartouche {:disabled? true}
(i18n/label :t/wallet-transaction-total-fee)
[wallet.components/cartouche-text-content
(str (money/to-fixed (max-fee gas gas-price)))
(i18n/label :t/eth)]]]]]))
(defn- advanced-options-wrapper [on-press content]
(if on-press
[react/touchable-highlight {:on-press on-press}
[react/view
content]]
[react/view
content]))
(defn- advanced-cartouche [{:keys [gas gas-price]} modal?]
[react/view send.styles/advanced-cartouche
[wallet.components/cartouche {:disabled? modal? :on-press #(re-frame/dispatch [:navigate-to-modal :wallet-transaction-fee])}
(i18n/label :t/wallet-transaction-fee)
[react/view send.styles/advanced-options-text-wrapper
[react/text {:style send.styles/advanced-fees-text}
(str (money/to-fixed (max-fee gas gas-price)) " " (i18n/label :t/eth))]
[react/text {:style send.styles/advanced-fees-details-text}
(str (money/to-fixed gas) " * " (money/to-fixed (money/wei-> :gwei gas-price)) (i18n/label :t/gwei))]]]])
(defn- advanced-options [{:keys [gas gas-price]} on-press]
[react/touchable-highlight {:on-press on-press}
[react/view
[react/text {:style wallet.components.styles/label}
(i18n/label :t/wallet-transaction-fee)]
[advanced-options-wrapper on-press
[react/view send.styles/advanced-options-wrapper
[react/view send.styles/advanced-options-text-wrapper
[react/text {:style send.styles/advanced-fees-text}
(str (money/to-fixed (max-fee gas gas-price)) " ETH")]
[react/text {:style send.styles/advanced-fees-details-text}
(str (money/to-fixed gas) " * " (money/to-fixed (money/wei-> :gwei gas-price)) "GWEI")]]
(when on-press
[vector-icons/icon :icons/forward {:color :white}])]]]])
(defn- advanced-options [advanced? transaction modal?]
[react/view {:style send.styles/advanced-wrapper}
[react/touchable-highlight {:on-press #(re-frame/dispatch [:wallet.send/toggle-advanced (not advanced?)])}
[react/view {:style send.styles/advanced-button-wrapper}
[react/view {:style send.styles/advanced-button}
[react/text {:style (merge wallet.components.styles/label send.styles/advanced-label)}
(i18n/label :t/wallet-advanced)]
[vector-icons/icon (if advanced? :icons/up :icons/down) {:color :white}]]]]
(when advanced?
[advanced-cartouche transaction modal?])])
(defn- send-transaction-panel [{:keys [modal? transaction scroll advanced? symbol]}]
(let [{:keys [amount amount-error signing? to to-name sufficient-funds? in-progress? from-chat?]} transaction]
[react/keyboard-avoiding-view wallet.styles/wallet-modal-container
[react/view components.styles/flex
[status-bar/status-bar {:type (if modal? :modal-wallet :wallet)}]
(if modal? [toolbar-modal from-chat?] [toolbar-view signing?])
[toolbar from-chat? (if modal? act/close-white act/back-white)
(i18n/label :t/send-transaction)]
[common/network-info {:text-color :white}]
[react/scroll-view (merge {:keyboardShouldPersistTaps :always} (when-not modal? {:ref #(reset! scroll %)}))
[react/view components.styles/flex
[react/view wallet.styles/choose-participant-container
[components/choose-recipient (merge {:address to
:name to-name}
(when-not modal?
{:on-press request-camera-permissions}))]]
[react/view wallet.styles/choose-asset-container
(if modal?
[components/view-asset symbol]
[components/choose-asset {:type :send
:symbol symbol}])]
[react/view wallet.styles/amount-container
[components/amount-input
(merge
{:error (or amount-error
(when-not sufficient-funds? (i18n/label :t/wallet-insufficient-funds)))
:input-options {:default-value (str (money/wei->ether amount))
:on-focus (fn [] (when @scroll (js/setTimeout #(.scrollToEnd @scroll) 100)))
:on-change-text #(re-frame/dispatch [:wallet.send/set-and-validate-amount %])}}
(when modal?
{:disabled? true}))]]
[react/view {:style send.styles/advanced-wrapper}
[react/touchable-highlight {:on-press #(re-frame/dispatch [:wallet.send/toggle-advanced (not advanced?)])}
[react/view {:style send.styles/advanced-button-wrapper}
[react/view {:style send.styles/advanced-button}
[react/text {:style (merge wallet.components.styles/label send.styles/advanced-label)}
(i18n/label :t/wallet-advanced)]
[vector-icons/icon (if advanced? :icons/up :icons/down) {:color :white}]]]]
(when advanced?
[advanced-options transaction (when-not modal? #(re-frame/dispatch [:navigate-to-modal :wallet-transaction-fee]))])]]]
[components/separator]
[react/view send.styles/send-transaction-form
[components/recipient-selector {:disabled? modal?
:address to
:name to-name}]
[components/asset-selector {:disabled? modal?
:type :send
:symbol symbol}]
[components/amount-selector {:disabled? modal?
:error (or amount-error
(when-not sufficient-funds? (i18n/label :t/wallet-insufficient-funds)))
:input-options {:default-value (str (money/to-fixed (money/wei->ether amount)))
:max-length 21
:on-focus (fn [] (when @scroll (js/setTimeout #(.scrollToEnd @scroll) 100)))
:on-change-text #(re-frame/dispatch [:wallet.send/set-and-validate-amount %])}}]
[advanced-options advanced? transaction modal?]]]
(if signing?
[signing-buttons
#(re-frame/dispatch (if modal? [:wallet/cancel-signing-modal] [:wallet/discard-transaction]))
@ -257,5 +214,6 @@
[react/view wallet.styles/wallet-modal-container
[react/view components.styles/flex
[status-bar/status-bar {:type :modal-wallet}]
[toolbar-modal false]
[toolbar false act/close-white
(i18n/label :t/send-transaction)]
[react/text {:style send.styles/empty-text} (i18n/label :t/unsigned-transaction-expired)]]])))

View File

@ -1,13 +1,15 @@
(ns status-im.ui.screens.wallet.settings.views
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [re-frame.core :as re-frame]
[status-im.i18n :as i18n]
[status-im.ui.components.list.views :as list]
[status-im.ui.components.react :as react]
[status-im.ui.components.styles :as components.styles]
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.i18n :as i18n]
[status-im.ui.screens.wallet.styles :as wallet.styles]
[status-im.utils.ethereum.core :as ethereum]
[status-im.utils.ethereum.tokens :as tokens]
[status-im.ui.components.styles :as components.styles]))
[status-im.utils.ethereum.tokens :as tokens]))
(defn- render-token [{:keys [symbol name icon]} visible-tokens]
[list/item
@ -21,10 +23,12 @@
(defview manage-assets []
(letsubs [network [:network]
visible-tokens [:wallet.settings/visible-tokens]]
[react/view components.styles/flex
[toolbar/toolbar {}
[toolbar/nav-clear-text (i18n/label :t/done)]
[toolbar/content-title (i18n/label :t/wallet-assets)]]
[react/view (merge components.styles/flex {:background-color :white})
[toolbar/toolbar #_{} {:style wallet.styles/toolbar}
[toolbar/nav-text {:style {:color :white}}
(i18n/label :t/done)]
[toolbar/content-title {:color :white}
(i18n/label :t/wallet-assets)]]
[react/view {:style components.styles/flex}
[list/flat-list {:data (tokens/tokens-for (ethereum/network->chain-keyword network))
:render-fn #(render-token % visible-tokens)}]]]))

View File

@ -10,7 +10,7 @@
:justify-content :center
:ios {:border-radius 20
:margin-top 6}
:android {:border-radius 4
:android {
:margin-top 18}
:background-color colors/blue})
@ -44,9 +44,12 @@
(def wallet-container
{:flex 1})
(defstyle toolbar
(def toolbar
{:background-color colors/blue})
(defstyle toolbar-modal
{:background-color colors/blue
:android {:elevation 0}})
:android {:elevation 2}})
(def buttons-container
{:flex-direction :row
@ -69,19 +72,43 @@
{:flex 1
:background-color colors/blue})
(def choose-participant-container
{:margin-top 16
:margin-horizontal 15})
(def amount-container
{:margin-top 16
:margin-bottom 16
:margin-horizontal 15
:flex-direction :row})
(def choose-asset-container
{:margin-top 16
:margin-horizontal 15})
(def cartouche-container
{:flex 1
:margin-top 16
:margin-horizontal 16})
(def cartouche-header
{:color colors/white})
(defn cartouche-content-wrapper [disabled?]
(merge
{:flex-direction :row
:margin-top 8
:border-radius styles/border-radius
:padding-left 14
:padding-right 8}
(if disabled?
{:border-color colors/white-light-transparent
:border-width 1}
{:background-color colors/white-transparent})))
(def cartouche-icon-wrapper
{:flex 1
:flex-direction :row
:justify-content :space-between
:align-items :center})
(def cartouche-primary-text
{:color styles/color-white})
(def cartouche-secondary-text
{:color styles/color-white-transparent})
(def qr-code-preview
{:width 256

View File

@ -1,13 +1,13 @@
(ns status-im.ui.screens.wallet.transactions.styles
(:require-macros [status-im.utils.styles :refer [defnstyle defstyle]])
(:require [status-im.ui.components.styles :as styles]
[status-im.ui.screens.main-tabs.styles :as tabs.styles]
[status-im.utils.platform :as platform]))
(:require [status-im.ui.components.colors :as colors]
[status-im.ui.components.styles :as styles]
[status-im.ui.screens.main-tabs.styles :as tabs.styles]))
(def error-container
{:align-self :center
:justify-content :center
:border-radius 4
:border-radius 8
:padding-vertical 4
:flex-direction :row
:background-color styles/color-gray9})
@ -234,4 +234,8 @@
:width 4
:height 4
:border-radius 2
:background-color styles/color-cyan})
:background-color styles/color-cyan})
(def filter-container
{:flex 1})
;:background-color colors/white})

View File

@ -1,19 +1,17 @@
(ns status-im.ui.screens.wallet.transactions.views
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [re-frame.core :as re-frame]
[status-im.i18n :as i18n]
[status-im.ui.components.button.view :as button]
[status-im.ui.components.checkbox.view :as checkbox]
[status-im.ui.components.list.views :as list]
[status-im.ui.components.react :as react]
[status-im.ui.components.status-bar.view :as status-bar]
[status-im.ui.components.styles :as styles]
[status-im.ui.components.styles :as components.styles]
[status-im.ui.components.toolbar.actions :as actions]
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.i18n :as i18n]
[status-im.ui.screens.wallet.transactions.styles :as transactions.styles]
[status-im.ui.screens.wallet.transactions.styles :as styles]
[status-im.ui.screens.wallet.views :as wallet.views]
[status-im.utils.money :as money]
[status-im.ui.components.styles :as common.styles])
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
[status-im.utils.money :as money]))
(defn on-delete-transaction
[{:keys [id]}]
@ -23,7 +21,7 @@
(merge
{:icon :icons/filter
:handler #(re-frame/dispatch [:navigate-to-modal :wallet-transactions-filter])}
(when filter? {:icon-opts {:overlay-style transactions.styles/corner-dot}})))
(when filter? {:icon-opts {:overlay-style styles/corner-dot}})))
(defn- all-checked? [filter-data]
(and (every? :checked? (:type filter-data))
@ -40,7 +38,7 @@
(defn action-buttons [{:keys [id] :as transaction}]
[react/view {:style transactions.styles/action-buttons}
[react/view {:style styles/action-buttons}
[button/primary-button {:style {:margin-right 12}
:on-press #(re-frame/dispatch [:wallet/show-sign-transaction id])}
(i18n/label :t/transactions-sign)]
@ -53,17 +51,17 @@
(defn- transaction-icon [k background-color color]
{:icon k
:icon-opts {:color color}
:style (transactions.styles/transaction-icon-background background-color)})
:style (styles/transaction-icon-background background-color)})
(defn- transaction-type->icon [k]
(case k
:unsigned (transaction-icon :icons/dots-horizontal styles/color-gray4-transparent styles/color-gray7)
:inbound (transaction-icon :icons/arrow-left styles/color-green-3-light styles/color-green-3)
:outbound (transaction-icon :icons/arrow-right styles/color-blue4-transparent styles/color-blue4)
(:postponed :pending) (transaction-icon :icons/arrow-right styles/color-gray4-transparent styles/color-gray7)
:unsigned (transaction-icon :icons/dots-horizontal components.styles/color-gray4-transparent components.styles/color-gray7)
:inbound (transaction-icon :icons/arrow-left components.styles/color-green-3-light components.styles/color-green-3)
:outbound (transaction-icon :icons/arrow-right components.styles/color-blue4-transparent components.styles/color-blue4)
(:postponed :pending) (transaction-icon :icons/arrow-right components.styles/color-gray4-transparent components.styles/color-gray7)
(throw (str "Unknown transaction type: " k))))
(defn render-transaction [{:keys [hash from-contact to-contact to from type value symbol time-formatted] :as transaction}]
(defn render-transaction [{:keys [hash from-contact to-contact to from type value time-formatted] :as transaction}]
(let [[label contact address] (if (inbound? type)
[(i18n/label :t/from) from-contact from]
[(i18n/label :t/to) to-contact to])]
@ -72,20 +70,20 @@
[list/item
[list/item-icon (transaction-type->icon (keyword type))]
[list/item-content
[react/view {:style transactions.styles/amount-time}
[react/text {:style transactions.styles/tx-amount
[react/view {:style styles/amount-time}
[react/text {:style styles/tx-amount
:ellipsize-mode "tail"
:number-of-lines 1}
(money/wei->str :eth value)]
[react/text {:style transactions.styles/tx-time}
[react/text {:style styles/tx-time}
time-formatted]]
[react/view {:style transactions.styles/address-row}
[react/text {:style transactions.styles/address-label}
[react/view {:style styles/address-row}
[react/text {:style styles/address-label}
label]
(when contact
[react/text {:style transactions.styles/address-contact}
[react/text {:style styles/address-contact}
contact])
[react/text {:style transactions.styles/address-hash
[react/text {:style styles/address-hash
:ellipsize-mode "middle"
:number-of-lines 1}
address]]
@ -93,10 +91,7 @@
[action-buttons transaction])]
[list/item-icon {:icon :icons/forward
:style {:margin-top 10}
:icon-opts transactions.styles/forward}]]]]))
;; TODO(yenda) hook with re-frame
(defn- empty-text [s] [react/text {:style transactions.styles/empty-text} s])
:icon-opts styles/forward}]]]]))
(defn filtered-transaction? [transaction filter-data]
(:checked? (some #(when (= (:type transaction) (:id %)) %) (:type filter-data))))
@ -109,21 +104,23 @@
transactions-loading? [:wallet.transactions/transactions-loading?]
error-message [:wallet.transactions/error-message?]
filter-data [:wallet.transactions/filters]]
[react/view styles/flex
[react/view components.styles/flex
(when error-message
[wallet.views/error-message-view transactions.styles/error-container transactions.styles/error-message])
[wallet.views/error-message-view styles/error-container styles/error-message])
[list/section-list {:sections (map #(update-transactions % filter-data) transactions-history-list)
:render-fn render-transaction
:empty-component (empty-text (i18n/label :t/transactions-history-empty))
:empty-component [react/text {:style styles/empty-text}
(i18n/label :t/transactions-history-empty)]
:on-refresh #(re-frame/dispatch [:update-transactions])
:refreshing (boolean transactions-loading?)}]]))
(defview unsigned-list []
(letsubs [transactions [:wallet.transactions/unsigned-transactions-list]]
[react/view {:style styles/flex}
[react/view {:style components.styles/flex}
[list/flat-list {:data transactions
:render-fn render-transaction
:empty-component (empty-text (i18n/label :t/transactions-unsigned-empty))}]]))
:empty-component [react/text {:style styles/empty-text}
(i18n/label :t/transactions-unsigned-empty)]}]]))
;; Filter history
@ -146,29 +143,29 @@
(defview filter-history []
(letsubs [filter-data [:wallet.transactions/filters]]
[react/view styles/flex
[react/view styles/filter-container
[toolbar/toolbar {}
[toolbar/nav-clear-text (i18n/label :t/done)]
[toolbar/content-title (i18n/label :t/transactions-filter-title)]
[toolbar/text-action {:handler #(re-frame/dispatch [:wallet.transactions/filter-all])
:disabled? (all-checked? filter-data)}
(i18n/label :t/transactions-filter-select-all)]]
[react/view {:style styles/flex}
[react/view {:style (merge {:background-color :white} components.styles/flex)}
[list/section-list {:sections (wrap-filter-data filter-data)}]]]))
(defn history-tab [active?]
[react/text {:uppercase? true
:style (transactions.styles/tab-title active?)}
:style (styles/tab-title active?)}
(i18n/label :t/transactions-history)])
(defview unsigned-tab [active?]
(letsubs [unsigned-transactions-count [:wallet.transactions/unsigned-transactions-count]]
[react/view {:flex-direction :row}
[react/text {:style (transactions.styles/tab-title active?)
[react/text {:style (styles/tab-title active?)
:uppercase? true}
(i18n/label :t/transactions-unsigned)]
(when (pos? unsigned-transactions-count)
[react/text {:style transactions.styles/tab-unsigned-transactions-count}
[react/text {:style styles/tab-unsigned-transactions-count}
(str " " unsigned-transactions-count)])]))
(def tabs-list
@ -178,21 +175,21 @@
:content unsigned-tab}])
(defn tab [view-id content active?]
[react/touchable-highlight {:style common.styles/flex
[react/touchable-highlight {:style components.styles/flex
:disabled active?
:on-press #(re-frame/dispatch [:navigation-replace view-id])}
[react/view {:style (transactions.styles/tab active?)}
[react/view {:style (styles/tab active?)}
[content active?]]])
(defn tabs [current-view-id]
[react/view {:style transactions.styles/tabs-container}
[react/view {:style styles/tabs-container}
(for [{:keys [content view-id]} tabs-list]
^{:key view-id} [tab view-id content (= view-id current-view-id)])])
(defview transactions []
(letsubs [current-tab [:get :view-id]
filter-data [:wallet.transactions/filters]]
[react/view {:style styles/flex}
[react/view {:style components.styles/flex}
[status-bar/status-bar]
[toolbar-view current-tab filter-data]
[tabs current-tab]
@ -209,41 +206,41 @@
(defn details-header [{:keys [value date type symbol]}]
[react/view {:style transactions.styles/details-header}
[react/view {:style transactions.styles/details-header-icon}
[react/view {:style styles/details-header}
[react/view {:style styles/details-header-icon}
[list/item-icon (transaction-type->icon type)]]
[react/view {:style transactions.styles/details-header-infos}
[react/text {:style transactions.styles/details-header-value} (pretty-print-asset symbol value)]
[react/text {:style transactions.styles/details-header-date} date]]])
[react/view {:style styles/details-header-infos}
[react/text {:style styles/details-header-value} (pretty-print-asset symbol value)]
[react/text {:style styles/details-header-date} date]]])
(defn progress-bar [progress]
[react/view {:style transactions.styles/progress-bar}
[react/view {:style (transactions.styles/progress-bar-done progress)}]
[react/view {:style (transactions.styles/progress-bar-todo (- 100 progress))}]])
[react/view {:style styles/progress-bar}
[react/view {:style (styles/progress-bar-done progress)}]
[react/view {:style (styles/progress-bar-todo (- 100 progress))}]])
(defn details-confirmations [confirmations confirmations-progress]
[react/view {:style transactions.styles/details-block}
[react/view {:style styles/details-block}
[progress-bar confirmations-progress]
[react/text {:style transactions.styles/details-confirmations-count}
[react/text {:style styles/details-confirmations-count}
(str confirmations " " (i18n/label :t/confirmations))]
[react/text {:style transactions.styles/details-confirmations-helper-text}
[react/text {:style styles/details-confirmations-helper-text}
(i18n/label :t/confirmations-helper-text)]])
(defn details-list-row
([label value]
(details-list-row label value nil))
([label value extra-value]
[react/view {:style transactions.styles/details-row}
[react/text {:style transactions.styles/details-item-label} (i18n/label label)]
[react/view {:style transactions.styles/details-item-value-wrapper}
[react/text {:style transactions.styles/details-item-value} (str value)]
[react/text {:style transactions.styles/details-item-extra-value} (str extra-value)]]]))
[react/view {:style styles/details-row}
[react/text {:style styles/details-item-label} (i18n/label label)]
[react/view {:style styles/details-item-value-wrapper}
[react/text {:style styles/details-item-value} (str value)]
[react/text {:style styles/details-item-extra-value} (str extra-value)]]]))
(defn details-list [{:keys [block hash
from from-wallet from-contact
to to-wallet to-contact
gas-limit gas-price-gwei gas-price-eth gas-used cost nonce data]}]
[react/view {:style transactions.styles/details-block}
[react/view {:style styles/details-block}
[details-list-row :t/block block]
[details-list-row :t/hash hash]
[details-list-row :t/from
@ -260,23 +257,24 @@
[details-list-row :t/data data]])
(defn details-action [hash url]
[(actions/opts [{:text (i18n/label :t/copy-transaction-hash) :value #(react/copy-to-clipboard hash)}
{:text (i18n/label :t/open-on-etherscan) :value #(.openURL react/linking url)}])])
[(actions/opts [{:label (i18n/label :t/copy-transaction-hash) :action #(react/copy-to-clipboard hash)}
{:label (i18n/label :t/open-on-etherscan) :action #(.openURL react/linking url)}])])
(defview transaction-details []
(letsubs [{:keys [hash url type] :as transaction} [:wallet.transactions/transaction-details]
confirmations [:wallet.transactions.details/confirmations]
confirmations-progress [:wallet.transactions.details/confirmations-progress]]
[react/view {:style styles/flex}
[react/view {:style components.styles/flex}
[status-bar/status-bar]
[toolbar/toolbar {}
toolbar/default-nav-back
[toolbar/content-title (i18n/label :t/transaction-details)]
(when transaction [toolbar/actions (details-action hash url)])]
(if transaction
[react/scroll-view {:style styles/main-container}
[react/scroll-view {:style components.styles/main-container}
[details-header transaction]
[details-confirmations confirmations confirmations-progress]
[react/view {:style transactions.styles/details-separator}]
[react/view {:style styles/details-separator}]
[details-list transaction]]
[empty-text (i18n/label :t/unsigned-transaction-expired)])]))
[react/text {:style styles/empty-text}
(i18n/label :t/unsigned-transaction-expired)])]))

View File

@ -6,6 +6,5 @@
(defn identicon
([hash] (identicon hash default-size))
([hash options]
(str "data:image/png;base64,"
(str (new dependencies/identicon-js hash options)))))
(str "data:image/png;base64,"
(str (new dependencies/identicon-js hash options)))))

View File

@ -80,3 +80,10 @@
(.catch (or on-error
(fn [error]
(show-popup "Error" (str error))))))))
(defn deep-merge
"Recursively merge maps"
[& maps]
(if (every? map? maps)
(apply merge-with deep-merge maps)
(last maps)))