Add animation for keycard interaction
Signed-off-by: Gheorghe Pinzaru <feross95@gmail.com>
After Width: | Height: | Size: 598 B |
After Width: | Height: | Size: 518 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.6 KiB |
|
@ -19,17 +19,16 @@
|
|||
:keycard-empty (js/require "./resources/images/ui/keycard-empty.png")
|
||||
:keycard-phone (js/require "./resources/images/ui/keycard-phone.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")
|
||||
:not-keycard (js/require "./resources/images/ui/not-keycard.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")
|
||||
:phone-nfc-on (js/require "./resources/images/ui/phone-nfc-on.png")
|
||||
:phone-nfc-off (js/require "./resources/images/ui/phone-nfc-off.png")
|
||||
:dapp-store (js/require "./resources/images/ui/dapp-store.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 {}))
|
||||
|
||||
|
|
|
@ -551,6 +551,7 @@ var TopLevel = {
|
|||
"Uri" : function () {},
|
||||
"url" : function () {},
|
||||
"Value" : function () {},
|
||||
"ValueXY": function() {},
|
||||
"value" : function () {},
|
||||
"verify" : function () {},
|
||||
"verifyPin" : function () {},
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
:keycard-empty "images/ui/keycard-empty.png"
|
||||
:keycard-phone "images/ui/keycard-phone.png"
|
||||
:keycard-connection "images/ui/keycard-connection.png"
|
||||
:keycard-nfc-on "images/ui/keycard-nfc-on.png"
|
||||
:keycard-wrong "images/ui/keycard-wrong.png"
|
||||
:not-keycard "images/ui/not-keycard.png"
|
||||
:status-logo "images/ui/status-logo.png"
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
BIN
ios/StatusIm/Images.xcassets/keycard-logo-big.imageset/keycard_logo_big.png
vendored
Normal file
After Width: | Height: | Size: 598 B |
BIN
ios/StatusIm/Images.xcassets/keycard-logo-big.imageset/keycard_logo_big@2x.png
vendored
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
ios/StatusIm/Images.xcassets/keycard-logo-big.imageset/keycard_logo_big@3x.png
vendored
Normal file
After Width: | Height: | Size: 1.6 KiB |
|
@ -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"
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 518 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 420 KiB |
Before Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 41 KiB |
|
@ -37,23 +37,20 @@
|
|||
(doseq [event ["keyCardOnConnected" "keyCardOnDisconnected"]]
|
||||
(.removeAllListeners event-emitter event)))
|
||||
|
||||
(defn register-card-events []
|
||||
(log/debug "[keycard] register-card-events")
|
||||
(defn on-card-connected [callback]
|
||||
(when (and config/hardwallet-enabled?
|
||||
platform/android?)
|
||||
(.addListener event-emitter "keyCardOnConnected" callback)))
|
||||
|
||||
(remove-event-listeners)
|
||||
(defn on-card-disconnected [callback]
|
||||
(when (and config/hardwallet-enabled?
|
||||
platform/android?)
|
||||
(.addListener event-emitter "keyCardOnDisconnected" callback)))
|
||||
|
||||
(re-frame/dispatch [:hardwallet.callback/on-register-card-events
|
||||
{: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 register-card-events []
|
||||
(remove-event-listeners)
|
||||
(on-card-connected #(re-frame/dispatch [:hardwallet.callback/on-card-connected %]))
|
||||
(on-card-disconnected #(re-frame/dispatch [:hardwallet.callback/on-card-disconnected %])))
|
||||
|
||||
(defn get-application-info [{:keys [pairing on-success]}]
|
||||
(log/debug "[keycard] get-application-info")
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
(ns status-im.hardwallet.common
|
||||
(:require [clojure.string :as string]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.ui.screens.keycard.keycard-interaction :as keycard-sheet]
|
||||
[status-im.ethereum.core :as ethereum]
|
||||
[status-im.i18n :as i18n]
|
||||
[status-im.ui.screens.navigation :as navigation]
|
||||
|
@ -9,6 +10,7 @@
|
|||
[status-im.utils.platform :as platform]
|
||||
[status-im.utils.types :as types]
|
||||
[taoensso.timbre :as log]
|
||||
[status-im.ui.components.bottom-sheet.events :as bottom-sheet]
|
||||
[status-im.utils.keychain.core :as keychain]
|
||||
[status-im.hardwallet.nfc :as nfc]))
|
||||
|
||||
|
@ -156,6 +158,69 @@
|
|||
(assoc-in [:hardwallet :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
|
||||
"Adds listener to hardware back button on Android.
|
||||
During keycard setup we show user a warning that setup will be cancelled
|
||||
|
|
|
@ -82,11 +82,6 @@
|
|||
(onboarding/proceed-with-generating-key)))
|
||||
(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
|
||||
{:events [:profile.ui/keycard-settings-button-pressed]}
|
||||
[{:keys [db] :as cofx}]
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
(navigation/navigate-to-cofx :keycard-recovery-enter-mnemonic nil)))
|
||||
|
||||
(fx/defn start-import-flow
|
||||
{:events [:hardwallet/recover-with-keycard-pressed]}
|
||||
{:events [::recover-with-keycard-pressed]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
{:db (assoc-in db [:hardwallet :flow] :import)
|
||||
|
@ -149,7 +149,7 @@
|
|||
(if (nil? name)
|
||||
{:hardwallet/generate-name-and-photo
|
||||
{:public-key whisper-public-key
|
||||
:on-success :hardwallet/on-name-and-photo-generated}}
|
||||
:on-success ::on-name-and-photo-generated}}
|
||||
(fx/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:hardwallet :setup-step] nil)
|
||||
|
@ -247,7 +247,7 @@
|
|||
(navigation/navigate-to-cofx :keycard-recovery-pin nil)))
|
||||
|
||||
(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)
|
||||
(re-frame/inject-cofx ::multiaccounts.create/get-signing-phrase)]}
|
||||
[{:keys [db] :as cofx} whisper-name photo-path]
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
[status-im.wallet.db :as wallet.db]
|
||||
[status-im.signing.gas :as signing.gas]
|
||||
[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.pin.subs
|
||||
status-im.ui.screens.hardwallet.setup.subs
|
||||
|
|
|
@ -60,6 +60,9 @@
|
|||
(defn create-value [value]
|
||||
(js/ReactNative.Animated.Value. value))
|
||||
|
||||
(defn create-value-xy [value]
|
||||
(js/ReactNative.Animated.ValueXY. value))
|
||||
|
||||
(defn add [anim-x anim-y]
|
||||
(js/ReactNative.Animated.add. anim-x anim-y))
|
||||
|
||||
|
@ -80,3 +83,4 @@
|
|||
(defn easing-out [] (.-out (easing)))
|
||||
|
||||
(defn cubic [] (.-cubic (easing)))
|
||||
(def bezier (.-bezier (easing)))
|
||||
|
|
|
@ -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]])))
|
|
@ -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}]])})))
|
|
@ -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})
|
|
@ -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])}]]])
|
|
@ -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])]))
|
|
@ -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]))
|
||||
|
||||
(re-frame/reg-sub
|
|
@ -3,7 +3,7 @@
|
|||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.ui.components.react :as react]
|
||||
[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.i18n :as i18n]
|
||||
[status-im.utils.config :as config]
|
||||
|
|
|
@ -19,8 +19,11 @@
|
|||
:Animated #js {:View #js {}
|
||||
:FlatList #js {}
|
||||
:Text #js {}}
|
||||
:Easing #js {:bezier (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 webview-bridge #js {:default #js {}})
|
||||
|
|
|
@ -1067,6 +1067,19 @@
|
|||
"tribute-to-talk-you-require-snt": "You require SNT for new people to start a chat.",
|
||||
"try-again": "Try again",
|
||||
"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...",
|
||||
"ulc-enabled": "ULC enabled",
|
||||
"unable-to-read-this-code": "Unable to read this code",
|
||||
|
|