[iOS] Perform preflight check for local network permission (#16150)
Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
This commit is contained in:
parent
9b52bba95d
commit
2df1b46975
|
@ -304,6 +304,14 @@ RCT_EXPORT_METHOD(hashMessage:(NSString *)message
|
||||||
callback(@[result]);
|
callback(@[result]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RCT_EXPORT_METHOD(localPairingPreflightOutboundCheck:(RCTResponseSenderBlock)callback) {
|
||||||
|
#if DEBUG
|
||||||
|
NSLog(@"LocalPairingPreflightOutboundCheck() method called");
|
||||||
|
#endif
|
||||||
|
NSString *result = StatusgoLocalPairingPreflightOutboundCheck();
|
||||||
|
callback(@[result]);
|
||||||
|
}
|
||||||
|
|
||||||
RCT_EXPORT_METHOD(getConnectionStringForBootstrappingAnotherDevice:(NSString *)configJSON
|
RCT_EXPORT_METHOD(getConnectionStringForBootstrappingAnotherDevice:(NSString *)configJSON
|
||||||
callback:(RCTResponseSenderBlock)callback) {
|
callback:(RCTResponseSenderBlock)callback) {
|
||||||
|
|
||||||
|
|
|
@ -248,6 +248,13 @@
|
||||||
(log/debug "[native-module] hash-message")
|
(log/debug "[native-module] hash-message")
|
||||||
(.hashMessage ^js (status) message callback))
|
(.hashMessage ^js (status) message callback))
|
||||||
|
|
||||||
|
(defn local-pairing-preflight-outbound-check
|
||||||
|
"Checks whether the device has allows connecting to the local server"
|
||||||
|
[callback]
|
||||||
|
(log/info "[native-module] Performing local pairing preflight check")
|
||||||
|
(when platform/ios?
|
||||||
|
(.localPairingPreflightOutboundCheck ^js (status) callback)))
|
||||||
|
|
||||||
(defn get-connection-string-for-bootstrapping-another-device
|
(defn get-connection-string-for-bootstrapping-another-device
|
||||||
"Generates connection string form status-go for the purpose of local pairing on the sender end"
|
"Generates connection string form status-go for the purpose of local pairing on the sender end"
|
||||||
[config-json callback]
|
[config-json callback]
|
||||||
|
|
|
@ -10,7 +10,8 @@
|
||||||
[utils.re-frame :as rf]
|
[utils.re-frame :as rf]
|
||||||
[status-im2.contexts.chat.messages.link-preview.events :as link-preview]
|
[status-im2.contexts.chat.messages.link-preview.events :as link-preview]
|
||||||
[taoensso.timbre :as log]
|
[taoensso.timbre :as log]
|
||||||
[status-im2.constants :as constants]))
|
[status-im2.constants :as constants]
|
||||||
|
[quo2.foundations.colors :as colors]))
|
||||||
|
|
||||||
(rf/defn status-node-started
|
(rf/defn status-node-started
|
||||||
[{db :db :as cofx} {:keys [error]}]
|
[{db :db :as cofx} {:keys [error]}]
|
||||||
|
@ -55,7 +56,7 @@
|
||||||
:peers-count (count (:peers peer-stats)))}))
|
:peers-count (count (:peers peer-stats)))}))
|
||||||
|
|
||||||
(rf/defn handle-local-pairing-signals
|
(rf/defn handle-local-pairing-signals
|
||||||
[{:keys [db] :as cofx} {:keys [type action data] :as event}]
|
[{:keys [db] :as cofx} {:keys [type action data error] :as event}]
|
||||||
(log/info "local pairing signal received"
|
(log/info "local pairing signal received"
|
||||||
{:event event})
|
{:event event})
|
||||||
(let [{:keys [account password]} data
|
(let [{:keys [account password]} data
|
||||||
|
@ -78,7 +79,7 @@
|
||||||
(and (some? account) (some? password)))
|
(and (some? account) (some? password)))
|
||||||
multiaccount-data (when received-account?
|
multiaccount-data (when received-account?
|
||||||
(merge account {:password password}))
|
(merge account {:password password}))
|
||||||
navigate-to-syncing-devices? (and connection-success? receiver?)
|
navigate-to-syncing-devices? (and (or connection-success? error-on-pairing?) receiver?)
|
||||||
user-in-syncing-devices-screen? (= (:view-id db) :syncing-progress)]
|
user-in-syncing-devices-screen? (= (:view-id db) :syncing-progress)]
|
||||||
(merge {:db (cond-> db
|
(merge {:db (cond-> db
|
||||||
connection-success?
|
connection-success?
|
||||||
|
@ -92,12 +93,21 @@
|
||||||
|
|
||||||
completed-pairing?
|
completed-pairing?
|
||||||
(assoc-in [:syncing :pairing-status] :completed))}
|
(assoc-in [:syncing :pairing-status] :completed))}
|
||||||
(when (and navigate-to-syncing-devices? (not user-in-syncing-devices-screen?))
|
(cond
|
||||||
{:dispatch [:navigate-to :syncing-progress]})
|
(and navigate-to-syncing-devices? (not user-in-syncing-devices-screen?))
|
||||||
(when (and completed-pairing? sender?)
|
{:dispatch [:navigate-to :syncing-progress]}
|
||||||
{:dispatch [:syncing/clear-states]})
|
|
||||||
(when (and completed-pairing? receiver?)
|
(and completed-pairing? sender?)
|
||||||
{:dispatch [:multiaccounts.login/local-paired-user]}))))
|
{:dispatch [:syncing/clear-states]}
|
||||||
|
|
||||||
|
(and completed-pairing? receiver?)
|
||||||
|
{:dispatch [:multiaccounts.login/local-paired-user]}
|
||||||
|
|
||||||
|
(and error-on-pairing? (some? error))
|
||||||
|
{:dispatch [:toasts/upsert
|
||||||
|
{:icon :i/alert
|
||||||
|
:icon-color colors/danger-50
|
||||||
|
:text error}]}))))
|
||||||
|
|
||||||
(rf/defn process
|
(rf/defn process
|
||||||
{:events [:signals/signal-received]}
|
{:events [:signals/signal-received]}
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
(ns status-im2.contexts.syncing.events
|
(ns status-im2.contexts.syncing.events
|
||||||
(:require [native-module.core :as native-module]
|
(:require [native-module.core :as native-module]
|
||||||
|
[re-frame.core :as re-frame]
|
||||||
|
[status-im.data-store.settings :as data-store.settings]
|
||||||
|
[status-im.node.core :as node]
|
||||||
|
[status-im.utils.platform :as utils.platform]
|
||||||
|
[status-im2.config :as config]
|
||||||
|
[status-im2.constants :as constants]
|
||||||
[taoensso.timbre :as log]
|
[taoensso.timbre :as log]
|
||||||
[utils.re-frame :as rf]
|
[utils.re-frame :as rf]
|
||||||
[utils.security.core :as security]
|
[utils.security.core :as security]
|
||||||
[status-im2.config :as config]
|
[utils.transforms :as transforms]))
|
||||||
[status-im.node.core :as node]
|
|
||||||
[re-frame.core :as re-frame]
|
|
||||||
[status-im.data-store.settings :as data-store.settings]
|
|
||||||
[status-im.utils.platform :as utils.platform]
|
|
||||||
[status-im2.constants :as constants]))
|
|
||||||
|
|
||||||
(rf/defn local-pairing-update-role
|
(rf/defn local-pairing-update-role
|
||||||
{:events [:syncing/update-role]}
|
{:events [:syncing/update-role]}
|
||||||
|
@ -31,6 +32,19 @@
|
||||||
:custom-bootnodes-enabled? false}}]
|
:custom-bootnodes-enabled? false}}]
|
||||||
(node/get-multiaccount-node-config db)))
|
(node/get-multiaccount-node-config db)))
|
||||||
|
|
||||||
|
(rf/defn preflight-outbound-check-for-local-pairing
|
||||||
|
{:events [:syncing/preflight-outbound-check]}
|
||||||
|
[_ set-checks-passed]
|
||||||
|
(let [callback
|
||||||
|
(fn [raw-response]
|
||||||
|
(log/info "Local pairing preflight check"
|
||||||
|
{:response raw-response
|
||||||
|
:event :syncing/preflight-outbound-check})
|
||||||
|
(let [^js response-js (transforms/json->js raw-response)
|
||||||
|
error (transforms/js->clj (.-error response-js))]
|
||||||
|
(set-checks-passed (empty? error))))]
|
||||||
|
(native-module/local-pairing-preflight-outbound-check callback)))
|
||||||
|
|
||||||
(rf/defn initiate-local-pairing-with-connection-string
|
(rf/defn initiate-local-pairing-with-connection-string
|
||||||
{:events [:syncing/input-connection-string-for-bootstrapping]
|
{:events [:syncing/input-connection-string-for-bootstrapping]
|
||||||
:interceptors [(re-frame/inject-cofx :random-guid-generator)]}
|
:interceptors [(re-frame/inject-cofx :random-guid-generator)]}
|
||||||
|
|
|
@ -13,10 +13,35 @@
|
||||||
[status-im2.contexts.syncing.scan-sync-code.style :as style]
|
[status-im2.contexts.syncing.scan-sync-code.style :as style]
|
||||||
[utils.i18n :as i18n]
|
[utils.i18n :as i18n]
|
||||||
[utils.re-frame :as rf]
|
[utils.re-frame :as rf]
|
||||||
[status-im2.contexts.syncing.utils :as sync-utils]))
|
[status-im2.contexts.syncing.utils :as sync-utils]
|
||||||
|
[status-im.utils.platform :as platform]))
|
||||||
|
|
||||||
|
;; Android allow local network access by default. So, we need this check on iOS only.
|
||||||
|
(defonce preflight-check-passed? (reagent/atom (if platform/ios? false true)))
|
||||||
|
|
||||||
(defonce camera-permission-granted? (reagent/atom false))
|
(defonce camera-permission-granted? (reagent/atom false))
|
||||||
|
|
||||||
|
(defn request-camera-permission
|
||||||
|
[]
|
||||||
|
(rf/dispatch
|
||||||
|
[:request-permissions
|
||||||
|
{:permissions [:camera]
|
||||||
|
:on-allowed #(reset! camera-permission-granted? true)
|
||||||
|
:on-denied #(rf/dispatch
|
||||||
|
[:toasts/upsert
|
||||||
|
{:icon :i/info
|
||||||
|
:icon-color colors/danger-50
|
||||||
|
:override-theme :light
|
||||||
|
:text (i18n/label :t/camera-permission-denied)}])}]))
|
||||||
|
|
||||||
|
(defn perform-preflight-check
|
||||||
|
"Performing the check for the first time
|
||||||
|
will trigger local network access permission in iOS.
|
||||||
|
This permission is required for local pairing
|
||||||
|
https://github.com/status-im/status-mobile/issues/16135"
|
||||||
|
[]
|
||||||
|
(rf/dispatch [:syncing/preflight-outbound-check #(reset! preflight-check-passed? %)]))
|
||||||
|
|
||||||
(defn- header
|
(defn- header
|
||||||
[active-tab read-qr-once? title]
|
[active-tab read-qr-once? title]
|
||||||
[:<>
|
[:<>
|
||||||
|
@ -59,27 +84,49 @@
|
||||||
(reset! active-tab id)
|
(reset! active-tab id)
|
||||||
(reset! read-qr-once? false))}]]])
|
(reset! read-qr-once? false))}]]])
|
||||||
|
|
||||||
(defn- camera-permission-view
|
(defn get-labels-and-on-press-method
|
||||||
[request-camera-permission]
|
[]
|
||||||
[rn/view {:style style/camera-permission-container}
|
(if @camera-permission-granted?
|
||||||
[quo/text
|
{:title-label-key :t/enable-access-to-local-network
|
||||||
{:size :paragraph-1
|
:description-label-key :t/to-pair-with-your-other-device-in-the-network
|
||||||
:weight :medium
|
:button-icon :i/world
|
||||||
:style style/enable-camera-access-header}
|
:button-label :t/enable-network-access
|
||||||
(i18n/label :t/enable-access-to-camera)]
|
:accessibility-label :perform-preflight-check
|
||||||
[quo/text
|
:on-press perform-preflight-check}
|
||||||
{:size :paragraph-2
|
{:title-label-key :t/enable-access-to-camera
|
||||||
:weight :regular
|
:description-label-key :t/to-scan-a-qr-enable-your-camera
|
||||||
:style style/enable-camera-access-sub-text}
|
:button-icon :i/camera
|
||||||
(i18n/label :t/to-scan-a-qr-enable-your-camera)]
|
:button-label :t/enable-camera
|
||||||
[quo/button
|
:accessibility-label :request-camera-permission
|
||||||
{:before :i/camera
|
:on-press request-camera-permission}))
|
||||||
:type :primary
|
|
||||||
:size 32
|
(defn- camera-and-local-network-access-permission-view
|
||||||
:accessibility-label :request-camera-permission
|
[]
|
||||||
:override-theme :dark
|
(let [{:keys [title-label-key
|
||||||
:on-press request-camera-permission}
|
description-label-key
|
||||||
(i18n/label :t/enable-camera)]])
|
button-icon
|
||||||
|
button-label
|
||||||
|
accessibility-label
|
||||||
|
on-press]} (get-labels-and-on-press-method)]
|
||||||
|
[rn/view {:style style/camera-permission-container}
|
||||||
|
[quo/text
|
||||||
|
{:size :paragraph-1
|
||||||
|
:weight :medium
|
||||||
|
:style style/enable-camera-access-header}
|
||||||
|
(i18n/label title-label-key)]
|
||||||
|
[quo/text
|
||||||
|
{:size :paragraph-2
|
||||||
|
:weight :regular
|
||||||
|
:style style/enable-camera-access-sub-text}
|
||||||
|
(i18n/label description-label-key)]
|
||||||
|
[quo/button
|
||||||
|
{:before button-icon
|
||||||
|
:type :primary
|
||||||
|
:size 32
|
||||||
|
:accessibility-label accessibility-label
|
||||||
|
:override-theme :dark
|
||||||
|
:on-press on-press}
|
||||||
|
(i18n/label button-label)]]))
|
||||||
|
|
||||||
(defn- qr-scan-hole-area
|
(defn- qr-scan-hole-area
|
||||||
[qr-view-finder]
|
[qr-view-finder]
|
||||||
|
@ -117,14 +164,16 @@
|
||||||
(i18n/label :t/ensure-qr-code-is-in-focus-to-scan)]]]))
|
(i18n/label :t/ensure-qr-code-is-in-focus-to-scan)]]]))
|
||||||
|
|
||||||
(defn- scan-qr-code-tab
|
(defn- scan-qr-code-tab
|
||||||
[qr-view-finder request-camera-permission]
|
[qr-view-finder]
|
||||||
[:<>
|
[:<>
|
||||||
[rn/view {:style style/scan-qr-code-container}]
|
[rn/view {:style style/scan-qr-code-container}]
|
||||||
(when (empty? @qr-view-finder)
|
(when (empty? @qr-view-finder)
|
||||||
[qr-scan-hole-area qr-view-finder])
|
[qr-scan-hole-area qr-view-finder])
|
||||||
(if (and @camera-permission-granted? (boolean (not-empty @qr-view-finder)))
|
(if (and @preflight-check-passed?
|
||||||
|
@camera-permission-granted?
|
||||||
|
(boolean (not-empty @qr-view-finder)))
|
||||||
[viewfinder @qr-view-finder]
|
[viewfinder @qr-view-finder]
|
||||||
[camera-permission-view request-camera-permission])])
|
[camera-and-local-network-access-permission-view])])
|
||||||
|
|
||||||
(defn- enter-sync-code-tab
|
(defn- enter-sync-code-tab
|
||||||
[]
|
[]
|
||||||
|
@ -185,21 +234,9 @@
|
||||||
|
|
||||||
(defn f-view
|
(defn f-view
|
||||||
[{:keys [title show-bottom-view? background]}]
|
[{:keys [title show-bottom-view? background]}]
|
||||||
(let [insets (safe-area/get-insets)
|
(let [insets (safe-area/get-insets)
|
||||||
active-tab (reagent/atom 1)
|
active-tab (reagent/atom 1)
|
||||||
qr-view-finder (reagent/atom {})
|
qr-view-finder (reagent/atom {})]
|
||||||
request-camera-permission (fn []
|
|
||||||
(rf/dispatch
|
|
||||||
[:request-permissions
|
|
||||||
{:permissions [:camera]
|
|
||||||
:on-allowed #(reset! camera-permission-granted? true)
|
|
||||||
:on-denied #(rf/dispatch
|
|
||||||
[:toasts/upsert
|
|
||||||
{:icon :i/info
|
|
||||||
:icon-color colors/danger-50
|
|
||||||
:override-theme :light
|
|
||||||
:text (i18n/label
|
|
||||||
:t/camera-permission-denied)}])}]))]
|
|
||||||
(fn []
|
(fn []
|
||||||
(let [camera-ref (atom nil)
|
(let [camera-ref (atom nil)
|
||||||
read-qr-once? (atom false)
|
read-qr-once? (atom false)
|
||||||
|
@ -215,7 +252,9 @@
|
||||||
3000)
|
3000)
|
||||||
(check-qr-code-data data)))
|
(check-qr-code-data data)))
|
||||||
scan-qr-code-tab? (= @active-tab 1)
|
scan-qr-code-tab? (= @active-tab 1)
|
||||||
show-camera? (and scan-qr-code-tab? @camera-permission-granted?)
|
show-camera? (and scan-qr-code-tab?
|
||||||
|
@camera-permission-granted?
|
||||||
|
@preflight-check-passed?)
|
||||||
show-holes? (and show-camera?
|
show-holes? (and show-camera?
|
||||||
(boolean (not-empty @qr-view-finder)))]
|
(boolean (not-empty @qr-view-finder)))]
|
||||||
(rn/use-effect
|
(rn/use-effect
|
||||||
|
@ -230,7 +269,7 @@
|
||||||
[rn/view {:style (style/root-container (:top insets))}
|
[rn/view {:style (style/root-container (:top insets))}
|
||||||
[header active-tab read-qr-once? title]
|
[header active-tab read-qr-once? title]
|
||||||
(case @active-tab
|
(case @active-tab
|
||||||
1 [scan-qr-code-tab qr-view-finder request-camera-permission]
|
1 [scan-qr-code-tab qr-view-finder]
|
||||||
2 [enter-sync-code-tab]
|
2 [enter-sync-code-tab]
|
||||||
nil)
|
nil)
|
||||||
[rn/view {:style style/flex-spacer}]
|
[rn/view {:style style/flex-spacer}]
|
||||||
|
|
|
@ -2092,6 +2092,9 @@
|
||||||
"synchronise-your-data-across-your-devices": "Synchronise your data across your devices",
|
"synchronise-your-data-across-your-devices": "Synchronise your data across your devices",
|
||||||
"scan-sync-qr-code": "Scan QR code",
|
"scan-sync-qr-code": "Scan QR code",
|
||||||
"enter-sync-code": "Enter sync code",
|
"enter-sync-code": "Enter sync code",
|
||||||
|
"enable-access-to-local-network": "Enable access to local network",
|
||||||
|
"to-pair-with-your-other-device-in-the-network": "To pair with your other device in the network",
|
||||||
|
"enable-network-access": "Enable network access",
|
||||||
"enable-access-to-camera": "Enable access to camera",
|
"enable-access-to-camera": "Enable access to camera",
|
||||||
"link-preview-loading-message": "Generating preview",
|
"link-preview-loading-message": "Generating preview",
|
||||||
"to-scan-a-qr-enable-your-camera": "To scan a QR, enable your camera",
|
"to-scan-a-qr-enable-your-camera": "To scan a QR, enable your camera",
|
||||||
|
|
Loading…
Reference in New Issue