From 148d43fdabd5e23fc16f04c3542fb23750d09aec Mon Sep 17 00:00:00 2001 From: Roman Volosovskyi Date: Wed, 1 May 2019 20:24:36 +0300 Subject: [PATCH] [#7871] Handle multiple opened screens with qr code readers --- src/status_im/browser/permissions.cljs | 2 +- .../extensions/capacities/camera/events.cljs | 3 +- src/status_im/qr_scanner/core.cljs | 15 +++-- src/status_im/ui/screens/navigation.cljs | 8 +-- .../ui/screens/qr_scanner/views.cljs | 27 ++++---- src/status_im/ui/screens/routing/core.cljs | 64 +++++++++++-------- src/status_im/ui/screens/views.cljs | 2 +- src/status_im/utils/navigation.cljs | 5 +- test/cljs/status_im/test/browser/core.cljs | 2 +- test/cljs/status_im/test/mailserver/core.cljs | 6 +- test/cljs/status_im/test/sign_in/flow.cljs | 2 +- 11 files changed, 75 insertions(+), 61 deletions(-) diff --git a/src/status_im/browser/permissions.cljs b/src/status_im/browser/permissions.cljs index 3542f3ce81..2d091db09b 100644 --- a/src/status_im/browser/permissions.cljs +++ b/src/status_im/browser/permissions.cljs @@ -34,7 +34,7 @@ (= permission constants/dapp-permission-qr-code) (fx/merge (assoc-in cofx [:db :browser/options :yielding-control?] true) - (qr-scanner/scan-qr-code {:modal? false} + (qr-scanner/scan-qr-code {} {:handler :browser.bridge.callback/qr-code-scanned :cancel-handler :browser.bridge.callback/qr-code-canceled :data {:dapp-name dapp-name diff --git a/src/status_im/extensions/capacities/camera/events.cljs b/src/status_im/extensions/capacities/camera/events.cljs index 96bfcb45a6..aa65db3d5b 100644 --- a/src/status_im/extensions/capacities/camera/events.cljs +++ b/src/status_im/extensions/capacities/camera/events.cljs @@ -55,8 +55,7 @@ (handlers/register-handler-fx :extensions/camera-qr-code (fn [{:keys [db] :as cofx} [_ _ {:keys [on-success on-failure]}]] - (qr-scanner/scan-qr-code cofx {:modal? false - :deny-handler :extensions/camera-denied} + (qr-scanner/scan-qr-code cofx {:deny-handler :extensions/camera-denied} {:handler :extensions/camera-qr-code-scanned :cancel-handler :extensions/camera-cancel :data {:on-success on-success diff --git a/src/status_im/qr_scanner/core.cljs b/src/status_im/qr_scanner/core.cljs index 28e5edee7a..734fc546f7 100644 --- a/src/status_im/qr_scanner/core.cljs +++ b/src/status_im/qr_scanner/core.cljs @@ -5,11 +5,12 @@ [status-im.utils.fx :as fx])) (fx/defn scan-qr-code - [{:keys [db]} {:keys [modal? deny-handler] :as identifier} qr-codes] - {:db (assoc db :qr-codes qr-codes) + [{:keys [db]} {:keys [deny-handler] :as identifier} qr-codes] + {:db (assoc-in db [:qr-codes identifier] qr-codes) :request-permissions-fx {:permissions [:camera] - :on-allowed #(re-frame/dispatch [(if modal? :navigate-to-modal :navigate-to) - :qr-scanner {:current-qr-context identifier}]) + :on-allowed #(re-frame/dispatch + [:navigate-to :qr-scanner + {:current-qr-context identifier}]) :on-denied (if (nil? deny-handler) (fn [] (utils/set-timeout @@ -23,7 +24,7 @@ (merge {:db (-> db (update :qr-codes dissoc context) (dissoc :current-qr-context))} - (when-let [qr-codes (:qr-codes db)] + (when-let [qr-codes (get-in db [:qr-codes context])] {:dispatch [(:handler qr-codes) context data (dissoc qr-codes :handler)]}))) (fx/defn set-qr-code-cancel @@ -31,6 +32,6 @@ (merge {:db (-> db (update :qr-codes dissoc context) (dissoc :current-qr-context))} - (when-let [qr-codes (:qr-codes db)] + (when-let [qr-codes (get-in db [:qr-codes context])] (when-let [handler (:cancel-handler qr-codes)] - {:dispatch [handler context qr-codes]})))) \ No newline at end of file + {:dispatch [handler context qr-codes]})))) diff --git a/src/status_im/ui/screens/navigation.cljs b/src/status_im/ui/screens/navigation.cljs index 82e8692204..7f81154812 100644 --- a/src/status_im/ui/screens/navigation.cljs +++ b/src/status_im/ui/screens/navigation.cljs @@ -53,7 +53,7 @@ {:db (if (= view-id go-to-view-id) db (push-view db go-to-view-id)) - ::navigate-to go-to-view-id})) + ::navigate-to [go-to-view-id screen-params]})) (fx/defn navigate-reset [{:keys [db]} {:keys [index actions] :as config}] @@ -76,9 +76,9 @@ (re-frame/reg-fx ::navigate-to - (fn [view-id] - (log/debug :navigate-to view-id) - (navigation/navigate-to (name view-id)))) + (fn [[view-id params]] + (log/debug :navigate-to view-id params) + (navigation/navigate-to (name view-id) params))) (re-frame/reg-fx ::navigate-back diff --git a/src/status_im/ui/screens/qr_scanner/views.cljs b/src/status_im/ui/screens/qr_scanner/views.cljs index 8e9a329695..b355ea9e3f 100644 --- a/src/status_im/ui/screens/qr_scanner/views.cljs +++ b/src/status_im/ui/screens/qr_scanner/views.cljs @@ -23,20 +23,25 @@ (defn on-barcode-read [identifier data] (re-frame/dispatch [:qr-scanner.callback/scan-qr-code-success identifier (camera/get-qr-code-data data)])) -(defview qr-scanner [] - (letsubs [{identifier :current-qr-context} [:get-screen-params] - camera-initialized? (reagent/atom false) +;; identifier is passed via navigation params instead of subs in order to ensure +;; that two separate instances of `qr-scanner` screen can work simultaneously +(defview qr-scanner [{identifier :current-qr-context} screen-focused?] + (letsubs [camera-initialized? (reagent/atom false) barcode-read? (reagent/atom false)] [react/view styles/barcode-scanner-container [qr-scanner-toolbar (or (:toolbar-title identifier) (i18n/label :t/scan-qr)) identifier] - [camera/camera {:onBarCodeRead #(if (:multiple? identifier) - (on-barcode-read identifier %) - (when-not @barcode-read? - (do (reset! barcode-read? true) - (on-barcode-read identifier %)))) - :ref #(reset! camera-initialized? true) - :captureAudio false - :style styles/barcode-scanner}] + ;; camera component should be hidden if screen is not shown + ;; otherwise another screen with camera from a different stack + ;; will not work properly + (when @screen-focused? + [camera/camera {:onBarCodeRead #(if (:multiple? identifier) + (on-barcode-read identifier %) + (when-not @barcode-read? + (do (reset! barcode-read? true) + (on-barcode-read identifier %)))) + :ref #(reset! camera-initialized? true) + :captureAudio false + :style styles/barcode-scanner}]) [react/view styles/rectangle-container [react/view styles/rectangle [react/image {:source {:uri :corner_left_top} diff --git a/src/status_im/ui/screens/routing/core.cljs b/src/status_im/ui/screens/routing/core.cljs index 2bf1932887..8cb24adef7 100644 --- a/src/status_im/ui/screens/routing/core.cljs +++ b/src/status_im/ui/screens/routing/core.cljs @@ -21,10 +21,11 @@ (defonce view-id (reagent.core/atom nil)) -(defn navigation-events [current-view-id modal?] +(defn navigation-events [current-view-id modal? screen-focused?] [:> navigation/navigation-events {:on-will-focus (fn [] + (reset! screen-focused? true) (when (not= @view-id current-view-id) (reset! view-id current-view-id)) (log/debug :on-will-focus current-view-id) @@ -34,13 +35,18 @@ :on-did-focus (fn [] (when-not modal? - (status-bar/set-status-bar current-view-id)))}]) + (status-bar/set-status-bar current-view-id))) + :on-will-blur + (fn [] (reset! screen-focused? false))}]) (defn wrap "Wraps screen with main view and adds navigation-events component" [view-id component] - (fn [] - (let [main-view (react/create-main-screen-view view-id)] + (fn [args] + (let [main-view (react/create-main-screen-view view-id) + ;; params passed to :navigate-to + params (get-in args [:navigation :state :params]) + screen-focused? (reagent.core/atom true)] (if platform/ios? [main-view (assoc common-styles/flex :margin-bottom @@ -64,38 +70,40 @@ :else tabs.styles/minimized-tabs-height)) - [component] - [navigation-events view-id false]] + [component params screen-focused?] + [navigation-events view-id false screen-focused?]] [main-view common-styles/flex - [component] - [navigation-events view-id false]])))) + [component params screen-focused?] + [navigation-events view-id false screen-focused?]])))) (defn wrap-modal [modal-view component] "Wraps modal screen with necessary styling and adds :on-request-close handler on Android" - (fn [] - (if platform/android? - [react/view common-styles/modal - [react/modal - {:transparent true - :animation-type :slide - :on-request-close (fn [] - (cond - (#{:wallet-send-transaction-modal - :wallet-sign-message-modal} - modal-view) - (re-frame/dispatch - [:wallet/discard-transaction-navigate-back]) + (fn [args] + (let [params (get-in args [:navigation :state :params]) + active? (reagent.core/atom true)] + (if platform/android? + [react/view common-styles/modal + [react/modal + {:transparent true + :animation-type :slide + :on-request-close (fn [] + (cond + (#{:wallet-send-transaction-modal + :wallet-sign-message-modal} + modal-view) + (re-frame/dispatch + [:wallet/discard-transaction-navigate-back]) - :else - (re-frame/dispatch [:navigate-back])))} + :else + (re-frame/dispatch [:navigate-back])))} + [react/main-screen-modal-view modal-view + [component params active?]] + [navigation-events modal-view true active?]]] [react/main-screen-modal-view modal-view - [component]] - [navigation-events modal-view true]]] - [react/main-screen-modal-view modal-view - [component] - [navigation-events modal-view true]]))) + [component params active?] + [navigation-events modal-view true active?]])))) (defn prepare-config [config] (-> config diff --git a/src/status_im/ui/screens/views.cljs b/src/status_im/ui/screens/views.cljs index a31cab1813..fb29a7f261 100644 --- a/src/status_im/ui/screens/views.cljs +++ b/src/status_im/ui/screens/views.cljs @@ -82,7 +82,7 @@ platform/android? (not js/goog.DEBUG) (not (contains? #{:intro :login :progress} @view-id))) - (navigation/navigate-to @view-id))) + (navigation/navigate-to @view-id nil))) ;; see https://reactnavigation.org/docs/en/state-persistence.html#development-mode :persistenceKey (when js/goog.DEBUG rand-label)}] [bottom-sheet]]))}))) diff --git a/src/status_im/utils/navigation.cljs b/src/status_im/utils/navigation.cljs index 3b7a09ec08..0a6285c647 100644 --- a/src/status_im/utils/navigation.cljs +++ b/src/status_im/utils/navigation.cljs @@ -19,13 +19,14 @@ (defn can-be-called? [] @navigator-ref) -(defn navigate-to [route] +(defn navigate-to [route params] (when (can-be-called?) (.dispatch @navigator-ref (.navigate navigation-actions - #js {:routeName (name route)})))) + #js {:routeName (name route) + :params (clj->js params)})))) (defn- navigate [params] (when (can-be-called?) diff --git a/test/cljs/status_im/test/browser/core.cljs b/test/cljs/status_im/test/browser/core.cljs index 8b46035c2b..42c2f0cd5c 100644 --- a/test/cljs/status_im/test/browser/core.cljs +++ b/test/cljs/status_im/test/browser/core.cljs @@ -5,7 +5,7 @@ (defn has-navigated-to-browser? [result] (and (= (get result :status-im.ui.screens.navigation/navigate-to) - :browser) + [:browser nil]) (= (get-in result [:db :view-id]) :browser))) diff --git a/test/cljs/status_im/test/mailserver/core.cljs b/test/cljs/status_im/test/mailserver/core.cljs index cf00d218cd..9e54c9386d 100644 --- a/test/cljs/status_im/test/mailserver/core.cljs +++ b/test/cljs/status_im/test/mailserver/core.cljs @@ -145,7 +145,7 @@ :error true}} (-> actual :db :mailserver.edit/mailserver)))) (testing "it navigates to edit-mailserver view" - (is (= :edit-mailserver + (is (= [:edit-mailserver nil] (:status-im.ui.screens.navigation/navigate-to actual)))))) (testing "when an id is given" (testing "when the mailserver is in the list" @@ -159,7 +159,7 @@ :error false}} (-> actual :db :mailserver.edit/mailserver)))) (testing "it navigates to edit-mailserver view" - (is (= :edit-mailserver + (is (= [:edit-mailserver nil] (:status-im.ui.screens.navigation/navigate-to actual)))))) (testing "when the mailserver is not in the list" (let [actual (mailserver/edit cofx "not-existing")] @@ -172,7 +172,7 @@ :error true}} (-> actual :db :mailserver.edit/mailserver)))) (testing "it navigates to edit-mailserver view" - (is (= :edit-mailserver + (is (= [:edit-mailserver nil] (:status-im.ui.screens.navigation/navigate-to actual))))))))) (deftest connected-mailserver diff --git a/test/cljs/status_im/test/sign_in/flow.cljs b/test/cljs/status_im/test/sign_in/flow.cljs index 88c2a4b9e4..065511c945 100644 --- a/test/cljs/status_im/test/sign_in/flow.cljs +++ b/test/cljs/status_im/test/sign_in/flow.cljs @@ -50,7 +50,7 @@ (testing "Request notifications permissions." (is (contains? efx :notifications/request-notifications-permissions))) (testing "Navigate to :home." - (is (= :home (efx :status-im.ui.screens.navigation/navigate-to)))) + (is (= [:home nil] (efx :status-im.ui.screens.navigation/navigate-to)))) (testing "Account selected." (is (contains? new-db :account/account))) (testing "Chats initialized."