Add animation for keycard interaction

Signed-off-by: Gheorghe Pinzaru <feross95@gmail.com>
This commit is contained in:
Gheorghe Pinzaru 2020-02-25 12:22:33 +03:00
parent bca946aa78
commit c9d1adbc27
No known key found for this signature in database
GPG Key ID: C9A094959935A952
35 changed files with 643 additions and 30 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 598 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -19,17 +19,16 @@
:keycard-empty (js/require "./resources/images/ui/keycard-empty.png") :keycard-empty (js/require "./resources/images/ui/keycard-empty.png")
:keycard-phone (js/require "./resources/images/ui/keycard-phone.png") :keycard-phone (js/require "./resources/images/ui/keycard-phone.png")
:keycard-connection (js/require "./resources/images/ui/keycard-connection.png") :keycard-connection (js/require "./resources/images/ui/keycard-connection.png")
:keycard-nfc-on (js/require "./resources/images/ui/keycard-nfc-on.png")
:keycard-wrong (js/require "./resources/images/ui/keycard-wrong.png") :keycard-wrong (js/require "./resources/images/ui/keycard-wrong.png")
:not-keycard (js/require "./resources/images/ui/not-keycard.png") :not-keycard (js/require "./resources/images/ui/not-keycard.png")
:status-logo (js/require "./resources/images/ui/status-logo.png") :status-logo (js/require "./resources/images/ui/status-logo.png")
:hold-card-animation (js/require "./resources/images/ui/hold-card-animation.gif")
:warning-sign (js/require "./resources/images/ui/warning-sign.png") :warning-sign (js/require "./resources/images/ui/warning-sign.png")
:phone-nfc-on (js/require "./resources/images/ui/phone-nfc-on.png") :phone-nfc-on (js/require "./resources/images/ui/phone-nfc-on.png")
:phone-nfc-off (js/require "./resources/images/ui/phone-nfc-off.png") :phone-nfc-off (js/require "./resources/images/ui/phone-nfc-off.png")
:dapp-store (js/require "./resources/images/ui/dapp-store.png") :dapp-store (js/require "./resources/images/ui/dapp-store.png")
:ens-header (js/require "./resources/images/ui/ens-header.png") :ens-header (js/require "./resources/images/ui/ens-header.png")
:new-chat-header (js/require "./resources/images/ui/new-chat-header.png")}) :new-chat-header (js/require "./resources/images/ui/new-chat-header.png")
:onboarding-phone (js/require "./resources/images/ui/onboarding-phone.png")})
(def loaded-images (atom {})) (def loaded-images (atom {}))

View File

@ -551,6 +551,7 @@ var TopLevel = {
"Uri" : function () {}, "Uri" : function () {},
"url" : function () {}, "url" : function () {},
"Value" : function () {}, "Value" : function () {},
"ValueXY": function() {},
"value" : function () {}, "value" : function () {},
"verify" : function () {}, "verify" : function () {},
"verifyPin" : function () {}, "verifyPin" : function () {},

View File

@ -14,7 +14,6 @@
:keycard-empty "images/ui/keycard-empty.png" :keycard-empty "images/ui/keycard-empty.png"
:keycard-phone "images/ui/keycard-phone.png" :keycard-phone "images/ui/keycard-phone.png"
:keycard-connection "images/ui/keycard-connection.png" :keycard-connection "images/ui/keycard-connection.png"
:keycard-nfc-on "images/ui/keycard-nfc-on.png"
:keycard-wrong "images/ui/keycard-wrong.png" :keycard-wrong "images/ui/keycard-wrong.png"
:not-keycard "images/ui/not-keycard.png" :not-keycard "images/ui/not-keycard.png"
:status-logo "images/ui/status-logo.png" :status-logo "images/ui/status-logo.png"

View File

@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "keycard_logo_big.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "keycard_logo_big@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "keycard_logo_big@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 598 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "union-nfc.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "union-nfc@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "union-nfc@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 420 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

View File

@ -37,23 +37,20 @@
(doseq [event ["keyCardOnConnected" "keyCardOnDisconnected"]] (doseq [event ["keyCardOnConnected" "keyCardOnDisconnected"]]
(.removeAllListeners event-emitter event))) (.removeAllListeners event-emitter event)))
(defn register-card-events [] (defn on-card-connected [callback]
(log/debug "[keycard] register-card-events")
(when (and config/hardwallet-enabled? (when (and config/hardwallet-enabled?
platform/android?) platform/android?)
(.addListener event-emitter "keyCardOnConnected" callback)))
(defn on-card-disconnected [callback]
(when (and config/hardwallet-enabled?
platform/android?)
(.addListener event-emitter "keyCardOnDisconnected" callback)))
(defn register-card-events []
(remove-event-listeners) (remove-event-listeners)
(on-card-connected #(re-frame/dispatch [:hardwallet.callback/on-card-connected %]))
(re-frame/dispatch [:hardwallet.callback/on-register-card-events (on-card-disconnected #(re-frame/dispatch [:hardwallet.callback/on-card-disconnected %])))
{:on-card-connected
(.addListener event-emitter
"keyCardOnConnected"
#(re-frame/dispatch [:hardwallet.callback/on-card-connected %]))
:on-card-disconnected
(.addListener event-emitter
"keyCardOnDisconnected"
#(re-frame/dispatch [:hardwallet.callback/on-card-disconnected %]))}])))
(defn get-application-info [{:keys [pairing on-success]}] (defn get-application-info [{:keys [pairing on-success]}]
(log/debug "[keycard] get-application-info") (log/debug "[keycard] get-application-info")

View File

@ -1,6 +1,7 @@
(ns status-im.hardwallet.common (ns status-im.hardwallet.common
(:require [clojure.string :as string] (:require [clojure.string :as string]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.ui.screens.keycard.keycard-interaction :as keycard-sheet]
[status-im.ethereum.core :as ethereum] [status-im.ethereum.core :as ethereum]
[status-im.i18n :as i18n] [status-im.i18n :as i18n]
[status-im.ui.screens.navigation :as navigation] [status-im.ui.screens.navigation :as navigation]
@ -9,6 +10,7 @@
[status-im.utils.platform :as platform] [status-im.utils.platform :as platform]
[status-im.utils.types :as types] [status-im.utils.types :as types]
[taoensso.timbre :as log] [taoensso.timbre :as log]
[status-im.ui.components.bottom-sheet.events :as bottom-sheet]
[status-im.utils.keychain.core :as keychain] [status-im.utils.keychain.core :as keychain]
[status-im.hardwallet.nfc :as nfc])) [status-im.hardwallet.nfc :as nfc]))
@ -156,6 +158,69 @@
(assoc-in [:hardwallet :on-card-read] nil) (assoc-in [:hardwallet :on-card-read] nil)
(assoc-in [:hardwallet :last-on-card-read] nil))}) (assoc-in [:hardwallet :last-on-card-read] nil))})
(defn keycard-sheet-content [on-cancel]
(fn []
(keycard-sheet/connect-keycard
{:on-cancel #(re-frame/dispatch on-cancel)
:on-connect :hardwallet.callback/on-card-connected
:on-disconnect :hardwallet.callback/on-card-disconnected})))
(fx/defn show-pair-sheet
[cofx {:keys [on-cancel]
:or {on-cancel [::cancel-sheet-confirm]}}]
(log/debug "[hardwallet] show-pair-sheet")
(fx/merge cofx
{:dismiss-keyboard true}
(bottom-sheet/show-bottom-sheet
{:view {:show-handle? false
:backdrop-dismiss? false
:disable-drag? true
:content (keycard-sheet-content on-cancel)}})))
(fx/defn hide-pair-sheet
[{:keys [db] :as cofx}]
(fx/merge cofx
{:db (-> db
(assoc-in [:hardwallet :card-connected?] false)
(assoc-in [:hardwallet :card-read-in-progress?] false))}
(restore-on-card-connected)
(restore-on-card-read)
(bottom-sheet/hide-bottom-sheet)))
(fx/defn clear-pin
[{:keys [db] :as cofx}]
(fx/merge cofx
{:db (update-in db
[:hardwallet :pin]
merge
{:status nil
:login (get-in db [:hardwallet :pin :original])
:export-key []
:sign []
:puk []
:current []
:original []
:confirmation []
:error-label nil})}))
(fx/defn cancel-sheet-confirm
{:events [::cancel-sheet-confirm
:hardwallet/back-button-pressed]}
[cofx]
(fx/merge cofx
(hide-pair-sheet)
(clear-pin)))
(fx/defn cancel-sheet
{:events [::cancel-sheet]}
[_]
{:ui/show-confirmation {:title (i18n/label :t/keycard-cancel-setup-title)
:content (i18n/label :t/keycard-cancel-setup-text)
:confirm-button-text (i18n/label :t/yes)
:cancel-button-text (i18n/label :t/no)
:on-accept #(re-frame/dispatch [::cancel-sheet-confirm])
:on-cancel #()}})
(fx/defn on-add-listener-to-hardware-back-button (fx/defn on-add-listener-to-hardware-back-button
"Adds listener to hardware back button on Android. "Adds listener to hardware back button on Android.
During keycard setup we show user a warning that setup will be cancelled During keycard setup we show user a warning that setup will be cancelled

View File

@ -82,11 +82,6 @@
(onboarding/proceed-with-generating-key))) (onboarding/proceed-with-generating-key)))
(recovery/load-pair-screen cofx))))) (recovery/load-pair-screen cofx)))))
(fx/defn on-register-card-events
{:events [:hardwallet.callback/on-register-card-events]}
[{:keys [db]} listeners]
{:db (update-in db [:hardwallet :listeners] merge listeners)})
(fx/defn navigate-to-keycard-settings (fx/defn navigate-to-keycard-settings
{:events [:profile.ui/keycard-settings-button-pressed]} {:events [:profile.ui/keycard-settings-button-pressed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]

View File

@ -61,7 +61,7 @@
(navigation/navigate-to-cofx :keycard-recovery-enter-mnemonic nil))) (navigation/navigate-to-cofx :keycard-recovery-enter-mnemonic nil)))
(fx/defn start-import-flow (fx/defn start-import-flow
{:events [:hardwallet/recover-with-keycard-pressed]} {:events [::recover-with-keycard-pressed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge cofx (fx/merge cofx
{:db (assoc-in db [:hardwallet :flow] :import) {:db (assoc-in db [:hardwallet :flow] :import)
@ -149,7 +149,7 @@
(if (nil? name) (if (nil? name)
{:hardwallet/generate-name-and-photo {:hardwallet/generate-name-and-photo
{:public-key whisper-public-key {:public-key whisper-public-key
:on-success :hardwallet/on-name-and-photo-generated}} :on-success ::on-name-and-photo-generated}}
(fx/merge cofx (fx/merge cofx
{:db (-> db {:db (-> db
(assoc-in [:hardwallet :setup-step] nil) (assoc-in [:hardwallet :setup-step] nil)
@ -247,7 +247,7 @@
(navigation/navigate-to-cofx :keycard-recovery-pin nil))) (navigation/navigate-to-cofx :keycard-recovery-pin nil)))
(fx/defn on-name-and-photo-generated (fx/defn on-name-and-photo-generated
{:events [:hardwallet/on-name-and-photo-generated] {:events [::on-name-and-photo-generated]
:interceptors [(re-frame/inject-cofx :random-guid-generator) :interceptors [(re-frame/inject-cofx :random-guid-generator)
(re-frame/inject-cofx ::multiaccounts.create/get-signing-phrase)]} (re-frame/inject-cofx ::multiaccounts.create/get-signing-phrase)]}
[{:keys [db] :as cofx} whisper-name photo-path] [{:keys [db] :as cofx} whisper-name photo-path]

View File

@ -47,7 +47,7 @@
[status-im.wallet.db :as wallet.db] [status-im.wallet.db :as wallet.db]
[status-im.signing.gas :as signing.gas] [status-im.signing.gas :as signing.gas]
[status-im.utils.gfycat.core :as gfycat] [status-im.utils.gfycat.core :as gfycat]
status-im.ui.screens.hardwallet.connect.subs status-im.ui.screens.keycard.subs
status-im.ui.screens.hardwallet.settings.subs status-im.ui.screens.hardwallet.settings.subs
status-im.ui.screens.hardwallet.pin.subs status-im.ui.screens.hardwallet.pin.subs
status-im.ui.screens.hardwallet.setup.subs status-im.ui.screens.hardwallet.setup.subs

View File

@ -60,6 +60,9 @@
(defn create-value [value] (defn create-value [value]
(js/ReactNative.Animated.Value. value)) (js/ReactNative.Animated.Value. value))
(defn create-value-xy [value]
(js/ReactNative.Animated.ValueXY. value))
(defn add [anim-x anim-y] (defn add [anim-x anim-y]
(js/ReactNative.Animated.add. anim-x anim-y)) (js/ReactNative.Animated.add. anim-x anim-y))
@ -80,3 +83,4 @@
(defn easing-out [] (.-out (easing))) (defn easing-out [] (.-out (easing)))
(defn cubic [] (.-cubic (easing))) (defn cubic [] (.-cubic (easing)))
(def bezier (.-bezier (easing)))

View File

@ -0,0 +1,46 @@
(ns status-im.ui.screens.keycard.components.description
(:require [reagent.core :as reagent]
[status-im.ui.components.animation :as animation]
[status-im.ui.components.react :as react]
[status-im.ui.screens.keycard.components.style :as styles]))
(defn text-block-style [animated]
{:height 66
:margin-bottom 8
:opacity (animation/interpolate animated
{:inputRange [0 1]
:outputRange [1 0]})
:transform [{:translateY (animation/interpolate animated
{:inputRange [0 1]
:outputRange [0 10]})}]})
(def easing (animation/bezier 0.77 0 0.175 1))
(defonce animating (atom nil))
(defn animate-description [animated]
(when-not @animating
(reset! animating true)
;; TODO; Animate exit
(animation/set-value animated 1)
(animation/start
(animation/timing animated
{:toValue 0
:timing 300
:easing easing})
#(reset! animating false))))
(defn animated-description []
(let [current-text (reagent/atom nil)
animated-value (animation/create-value 0)]
(fn [{:keys [title description]}]
(when-not (= @current-text [title description])
(reset! current-text [title description])
(animate-description animated-value))
[react/animated-view {:style (text-block-style animated-value)}
[react/text {:style styles/title-style
:number-of-lines 1}
title]
[react/text {:style styles/helper-text-style
:number-of-lines 2}
description]])))

View File

@ -0,0 +1,339 @@
(ns status-im.ui.screens.keycard.components.keycard-animation
(:require [status-im.ui.components.react :as react]
[reagent.core :as reagent]
[status-im.ui.components.icons.vector-icons :as vector-icons]
[status-im.ui.components.animation :as animation]
[status-im.hardwallet.card :as keycard-nfc]
[status-im.react-native.resources :as resources]
[status-im.ui.components.colors :as colors]))
(defn circle [{:keys [animation-value color size]}]
[react/animated-view
{:style {:width size
:height size
:position "absolute"
:background-color color
:border-radius (/ size 2)
:opacity (animation/interpolate
animation-value
{:inputRange [0 1 2]
:outputRange [0.7 1 0]})
:transform [{:scale (animation/interpolate
animation-value
{:inputRange [0 1]
:outputRange [0.9 1]})}]}}])
(defn indicator-container [anim children]
[react/animated-view
{:style {:position "absolute"
:justify-content :center
:align-items :center
:border-radius 21
:width 42
:height 42
:top 16.5
:right -24
:shadow-offset {:width 0 :height 2}
:shadow-radius 16
:elevation 8
:shadow-opacity 0.1
:shadow-color "gba(0, 9, 26, 0.12)"
:background-color :white
:opacity anim
:transform [{:scale (animation/interpolate
anim
{:inputRange [0 1]
:outputRange [0 1]})}]}}
children])
(defn indicator [{:keys [state animation-value]}]
[indicator-container animation-value
(case @state
:error
[vector-icons/icon :main-icons/close {:color colors/red
:height 28
:width 28}]
:success
[vector-icons/icon :main-icons/check {:color colors/green
:height 28
:width 28}]
:connected
[vector-icons/icon :main-icons/check {:color colors/blue
:height 28
:width 28}]
:processing
[react/activity-indicator {:color colors/blue}]
nil)])
(defn animate-card-position [card-scale animation-value]
{:transform [{:scale card-scale}
{:translateX (animation/x animation-value)}
{:translateY (animation/y animation-value)}]})
(defn card-colors [state]
(case state
(:init :awaiting)
{:card-color "#2D2D2D"
:key-color "#27D8B9"
:chip-color "#F0CC73"}
(:connected :processing)
{:card-color colors/blue
:key-color :white
:chip-color :white}
:success
{:card-color colors/green
:key-color :white
:chip-color :white}
:error
{:card-color colors/red
:key-color :white
:chip-color :white}
nil))
(defn card [{:keys [card-scale state indicator-value animation-value]}]
(let [{:keys [card-color
chip-color
key-color]} (card-colors @state)]
[react/animated-view
{:style (merge
(animate-card-position card-scale animation-value)
{:height 80
:width 120
:border-radius 12
:position :absolute
:shadow-offset {:width 0 :height 2}
:shadow-radius 16
:elevation 8
:shadow-opacity 0.1
:shadow-color "gba(0, 9, 26, 0.12)"
:background-color card-color})}
[react/animated-view
{:style {:width 12
:height 9
:border-radius 3
:left 19.5
:top 30
:background-color chip-color}}]
[react/view
{:style {:position :absolute
:justify-content :center
:top 18
:right 19.5
:height 42
:width 25}}
[vector-icons/icon :main-icons/keycard-logo-big
{:color key-color
:width 25
:height 42}]]
[indicator {:state state
:animation-value indicator-value}]]))
(defn phone [{:keys [animation-value]}]
[react/animated-view {:style {:position :absolute
:bottom 0
:elevation 9
:opacity (animation/interpolate
animation-value
{:inputRange [0 1]
:outputRange [0 0.9]})
:transform [{:translateY (animation/interpolate
animation-value
{:inputRange [0 1]
:outputRange [125 10]})}]}}
[react/image
{:source (resources/get-image :onboarding-phone)
:style {:height 125
:width 86}}]])
(def circle-easing (animation/bezier 0.455 0.03 0.515 0.955))
(def card-easing (animation/bezier 0.77 0 0.175 1))
(defn- circle-animation [animation-value to delay]
(animation/timing animation-value
{:toValue to
:delay delay
:duration 1000
:easing circle-easing}))
(defn start-animation
[{:keys [small medium big card-scale phone
indicator card state]}]
(animation/set-value indicator 0)
(animation/set-value phone 0)
(let [phone-enter-at 7000
resets (animation/timing card-scale
{:toValue 0.66
:duration 1000
:easing card-easing})
card-loop (animation/anim-loop
(animation/anim-sequence
[(animation/timing card
{:toValue #js {:x -30
:y 30}
:duration 1000
:easing card-easing})
(animation/timing card
{:toValue {:x 45
:y 65}
:duration 1000
:delay 2000
:easing card-easing})
(animation/timing card
{:toValue #js {:x -30
:y 105}
:duration 1000
:delay 2000
:easing card-easing})
(animation/anim-delay 2000)]))
phone-entrance (animation/anim-sequence
[(animation/anim-delay phone-enter-at)
(animation/parallel
[(animation/timing card-scale
{:toValue 0.528
:duration 1000
:easing card-easing})
(animation/timing phone
{:toValue 1
:duration 1000
:easing card-easing})
card-loop])])
circles (animation/anim-loop
(animation/parallel
[(animation/anim-sequence
[(circle-animation small 1 0)
(circle-animation small 0 0)])
(animation/anim-sequence
[(circle-animation medium 1 200)
(circle-animation medium 0 0)])
(animation/anim-sequence
[(circle-animation big 1 400)
(circle-animation big 0 0)])]))
animation (animation/parallel
[resets
phone-entrance
circles])]
(reset! state :init)
;; TODO(Ferossgp): Think how to improve that, this creates desync of state with animation
(js/setTimeout #(compare-and-set! state :init :awaiting)
phone-enter-at)
(animation/start animation)))
(defn on-error [{:keys [state restart]}]
(reset! state :error)
(js/setTimeout #(when (= @state :error)
(restart)) 3000))
(defn on-success [{:keys [state]}]
(reset! state :success))
(defn on-connect
[{:keys [state card small indicator
medium big card-scale phone]}]
(let [connect-animation (animation/parallel
[(animation/timing card-scale
{:toValue 1
:timing 1000
:easing card-easing})
(animation/timing indicator
{:toValue 1
:timing 1000
:easing card-easing})
(animation/timing small
{:toValue 2
:timing 1000
:easing circle-easing})
(animation/timing medium
{:toValue 2
:timing 1000
:easing circle-easing})
(animation/timing big
{:toValue 2
:timing 1000
:easing circle-easing})
(animation/timing phone
{:toValue 0
:timing 1000
:easing card-easing})
(animation/timing card
{:toValue #js {:x 0
:y 0}
:timing 3000
:easing card-easing})])]
(reset! state :connected)
(js/setTimeout #(compare-and-set! state :connected :processing)
2000)
(animation/start connect-animation)))
(defn animated-circles [{:keys [state on-card-connected on-card-disconnected]}]
(let [animation-small (animation/create-value 0)
animation-medium (animation/create-value 0)
animation-big (animation/create-value 0)
animation-card (animation/create-value-xy #js {:x 0
:y 0})
card-scale (animation/create-value 0.66)
animation-phone (animation/create-value 0)
animation-indicator (animation/create-value 0)
on-start-animation #(start-animation
{:state state
:small animation-small
:medium animation-medium
:big animation-big
:phone animation-phone
:card animation-card
:card-scale card-scale
:indicator animation-indicator})
on-card-connected #(do
(on-card-connected)
(on-connect
{:state state
:indicator animation-indicator
:card animation-card
:card-scale card-scale
:phone animation-phone
:small animation-small
:medium animation-medium
:big animation-big}))
on-success #(on-success
{:state state})
on-error #(do
(on-card-disconnected)
(on-error
{:state state
:restart on-start-animation}))]
(reagent/create-class
{:component-did-mount
(fn []
(keycard-nfc/remove-event-listeners)
(keycard-nfc/on-card-connected on-card-connected)
(keycard-nfc/on-card-disconnected on-error)
(on-start-animation))
:component-will-unmount
(fn []
(keycard-nfc/remove-event-listeners))
:render
(fn []
[react/view {:style {:position :absolute
:top 0
:bottom 0
:left 0
:right 0
:justify-content :center
:align-items :center}}
[circle {:animation-value animation-big
:size 200
:color "#F1F4FF"}]
[circle {:animation-value animation-medium
:size 140
:color "#E3E8FA"}]
[circle {:animation-value animation-small
:size 80
:color "#D2D9F0"}]
[card {:animation-value animation-card
:state state
:indicator-value animation-indicator
:card-scale card-scale}]
[phone {:animation-value animation-phone}]])})))

View File

@ -0,0 +1,17 @@
(ns status-im.ui.screens.keycard.components.style
(:require [status-im.ui.components.colors :as colors]))
(def wrapper-style {:flex 1
:align-items :center
:justify-content :center})
(def container-style {:flex-direction :column
:align-items :center
:padding-horizontal 40})
(def helper-text-style {:text-align :center
:color colors/gray
:line-height 22})
(def title-style {:text-align :center
:line-height 22})

View File

@ -0,0 +1,26 @@
(ns status-im.ui.screens.keycard.components.turn-nfc
(:require [re-frame.core :as re-frame]
[status-im.i18n :as i18n]
[status-im.ui.components.icons.vector-icons :as vector-icons]
[status-im.ui.components.react :as react]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.button :as button]
[status-im.ui.screens.keycard.components.style :as styles]))
(defn turn-nfc-on []
[react/view {:style styles/wrapper-style}
[react/view {:style styles/container-style}
[vector-icons/icon :main-icons/union-nfc {:color colors/blue
:height 36
:width 36}]
[react/view {:margin-top 16}
[react/text {:style {:typography :title-bold}}
(i18n/label :t/turn-nfc-on)]]
[react/view {:margin-top 8}
[react/text {:number-of-lines 2
:style styles/helper-text-style}
(i18n/label :t/turn-nfc-description)]]
[button/button {:label :t/open-nfc-settings
:style {:margin-top 16}
:on-press #(re-frame/dispatch [:keycard.onboarding.nfc-on/open-nfc-settings-pressed])}]]])

View File

@ -0,0 +1,63 @@
(ns status-im.ui.screens.keycard.keycard-interaction
(:require [reagent.core :as reagent]
[re-frame.core :as re-frame]
[status-im.i18n :as i18n]
[status-im.ui.components.react :as react]
[status-im.ui.screens.keycard.components.keycard-animation
:refer [animated-circles]]
[status-im.ui.components.colors :as colors]
[status-im.ui.screens.keycard.components.description :as description]
[status-im.ui.screens.keycard.components.turn-nfc :as turn-nfc]
[status-im.ui.screens.keycard.components.style :as styles]))
(def state->translations
{:init {:title :t/keycard-init-title
:description :t/keycard-init-description}
:awaiting {:title :t/keycard-awaiting-title
:description :t/keycard-awaiting-description}
:processing {:title :t/keycard-processing-title
:description :t/keycard-processing-description}
:connected {:title :t/keycard-connected-title
:description :t/keycard-connected-description}
:error {:title :t/keycard-error-title
:description :t/keycard-error-description}
:success {:title :t/keycard-success-title
:description :t/keycard-success-description}})
(defn card-sync-flow []
(let [state (reagent/atom nil)]
(fn [{:keys [on-card-connected on-card-disconnected]}]
(let [translation (get state->translations @state)]
[react/view {:style styles/container-style}
[react/view {:height 200
:margin-bottom 20}
[animated-circles {:state state
:on-card-disconnected on-card-disconnected
:on-card-connected on-card-connected}]]
(when translation
[description/animated-description
{:title (i18n/label (:title translation))
:description (i18n/label (:description translation))}])]))))
(defn connect-keycard [{:keys [on-connect on-cancel on-disconnect]}]
(fn []
[react/view {:style {:flex 1
:align-items :center
:justify-content :center}}
(when on-cancel
[react/touchable-highlight
{:on-press on-cancel
:style {:position :absolute
:top 0
:right 0}}
[react/text {:style {:line-height 22
:padding-horizontal 16
:color colors/blue
:text-align :center}}
(i18n/label :t/cancel)]])
(if @(re-frame/subscribe [:hardwallet/nfc-enabled?])
[card-sync-flow {:on-card-disconnected
#(re-frame/dispatch [on-disconnect])
:on-card-connected
#(re-frame/dispatch [on-connect])}]
[turn-nfc/turn-nfc-on])]))

View File

@ -1,4 +1,4 @@
(ns status-im.ui.screens.hardwallet.connect.subs (ns status-im.ui.screens.keycard.subs
(:require [re-frame.core :as re-frame])) (:require [re-frame.core :as re-frame]))
(re-frame/reg-sub (re-frame/reg-sub

View File

@ -3,7 +3,7 @@
(:require [re-frame.core :as re-frame] (:require [re-frame.core :as re-frame]
[status-im.ui.components.react :as react] [status-im.ui.components.react :as react]
[status-im.multiaccounts.recover.core :as multiaccounts.recover] [status-im.multiaccounts.recover.core :as multiaccounts.recover]
[status-im.hardwallet.core :as hardwallet] [status-im.hardwallet.recovery :as hardwallet]
[status-im.hardwallet.nfc :as nfc] [status-im.hardwallet.nfc :as nfc]
[status-im.i18n :as i18n] [status-im.i18n :as i18n]
[status-im.utils.config :as config] [status-im.utils.config :as config]

View File

@ -19,9 +19,12 @@
:Animated #js {:View #js {} :Animated #js {:View #js {}
:FlatList #js {} :FlatList #js {}
:Text #js {}} :Text #js {}}
:Easing #js {:bezier (fn [])}
:DeviceEventEmitter #js {:addListener (fn [])} :DeviceEventEmitter #js {:addListener (fn [])}
:Dimensions #js {:get (fn [])}}) :Dimensions #js {:get (fn [])}})
(set! js/ReactNative react-native)
(def vector-icons #js {:default #js {}}) (def vector-icons #js {:default #js {}})
(def webview-bridge #js {:default #js {}}) (def webview-bridge #js {:default #js {}})
(def webview #js {:WebView #js {}}) (def webview #js {:WebView #js {}})

View File

@ -1067,6 +1067,19 @@
"tribute-to-talk-you-require-snt": "You require SNT for new people to start a chat.", "tribute-to-talk-you-require-snt": "You require SNT for new people to start a chat.",
"try-again": "Try again", "try-again": "Try again",
"turn-nfc-on": "Turn NFC on to continue", "turn-nfc-on": "Turn NFC on to continue",
"turn-nfc-description": "NFC is disabled on yor device. You can enable it in settings",
"keycard-init-title": "Looking for cards...",
"keycard-init-description": "Put the card to the back of your phone to continue",
"keycard-awaiting-title": "Still looking...",
"keycard-awaiting-description": "Try moving the card around to find the NFC reader on your device",
"keycard-processing-title": "Processing...",
"keycard-processing-description": "Try keeping the card still",
"keycard-connected-title": "Connected",
"keycard-connected-description": "Try keeping the card still",
"keycard-error-title": "Connection lost",
"keycard-error-description": "Connect the card again to continue",
"keycard-success-title": "Success",
"keycard-success-description": "You may remove the card now",
"type-a-message": "Type a message...", "type-a-message": "Type a message...",
"ulc-enabled": "ULC enabled", "ulc-enabled": "ULC enabled",
"unable-to-read-this-code": "Unable to read this code", "unable-to-read-this-code": "Unable to read this code",