From 27e777d1bda517c25fbd4bbabdc5da94e7dc7c91 Mon Sep 17 00:00:00 2001 From: Foo Pang Date: Tue, 9 Jan 2018 10:36:48 +0800 Subject: [PATCH] [Fix #1785] Handle timeout for network failure in utils.utils/http-get and http-post Signed-off-by: Julien Eluard --- .re-natal | 3 +- package-lock.json | 5 +++ package.json | 1 + .../react_native/js_dependencies.cljs | 1 + src/status_im/ui/screens/events.cljs | 25 +++++++-------- src/status_im/ui/screens/wallet/events.cljs | 7 +++- src/status_im/ui/screens/wallet/subs.cljs | 6 ++++ .../ui/screens/wallet/transactions/subs.cljs | 5 +++ .../ui/screens/wallet/transactions/views.cljs | 3 ++ src/status_im/ui/screens/wallet/views.cljs | 13 +++++--- src/status_im/utils/utils.cljs | 32 ++++++++++++------- 11 files changed, 70 insertions(+), 31 deletions(-) diff --git a/.re-natal b/.re-natal index 4fa97832b4..8df24503f8 100644 --- a/.re-natal +++ b/.re-natal @@ -48,7 +48,8 @@ "rn-snoopy/stream/filter", "rn-snoopy/stream/buffer", "react-native/Libraries/vendor/emitter/EventEmitter", - "react-native-background-timer" + "react-native-background-timer", + "react-native-fetch-polyfill" ], "imageDirs": [ "resources/images" diff --git a/package-lock.json b/package-lock.json index 790bff94a1..408db67205 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8088,6 +8088,11 @@ "resolved": "https://registry.npmjs.org/react-native-fcm/-/react-native-fcm-10.0.3.tgz", "integrity": "sha1-HcU47YifkLelvB0FMMxRbmmwnow=" }, + "react-native-fetch-polyfill": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/react-native-fetch-polyfill/-/react-native-fetch-polyfill-1.1.2.tgz", + "integrity": "sha1-JWtaCr14zEmS96fPglQ9ovISSnM=" + }, "react-native-fs": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/react-native-fs/-/react-native-fs-2.8.1.tgz", diff --git a/package.json b/package.json index eac389472d..36d497a8f1 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "react-native-crypto": "2.1.1", "react-native-dialogs": "0.0.20", "react-native-fcm": "10.0.3", + "react-native-fetch-polyfill": "1.1.2", "react-native-fs": "2.8.1", "react-native-http": "github:tradle/react-native-http#834492d", "react-native-http-bridge": "github:status-im/react-native-http-bridge", diff --git a/react-native/src/status_im/react_native/js_dependencies.cljs b/react-native/src/status_im/react_native/js_dependencies.cljs index 45803f229f..c8e75e2ba6 100644 --- a/react-native/src/status_im/react_native/js_dependencies.cljs +++ b/react-native/src/status_im/react_native/js_dependencies.cljs @@ -30,3 +30,4 @@ (def snoopy-buffer (js/require "rn-snoopy/stream/buffer")) (def EventEmmiter (js/require "react-native/Libraries/vendor/emitter/EventEmitter")) (def background-timer (.-default (js/require "react-native-background-timer"))) +(def fetch (.-default (js/require "react-native-fetch-polyfill"))) diff --git a/src/status_im/ui/screens/events.cljs b/src/status_im/ui/screens/events.cljs index b0b81a2d1c..0e839b9448 100644 --- a/src/status_im/ui/screens/events.cljs +++ b/src/status_im/ui/screens/events.cljs @@ -109,21 +109,18 @@ (re-frame/reg-fx :http-post - (fn [{:keys [action data success-event-creator failure-event-creator]}] - (utils/http-post action - data - #(re-frame/dispatch (success-event-creator %)) - #(re-frame/dispatch (failure-event-creator %))))) + (fn [{:keys [action data success-event-creator failure-event-creator timeout-ms]}] + (let [on-success #(re-frame/dispatch (success-event-creator %)) + on-error #(re-frame/dispatch (failure-event-creator %)) + opts {:timeout-ms timeout-ms}] + (utils/http-post action data on-success on-error opts)))) -(defn- http-get [{:keys [url response-validator success-event-creator failure-event-creator]}] - (if response-validator - (utils/http-get url - response-validator - #(re-frame/dispatch (success-event-creator %)) - #(re-frame/dispatch (failure-event-creator %))) - (utils/http-get url - #(re-frame/dispatch (success-event-creator %)) - #(re-frame/dispatch (failure-event-creator %))))) +(defn- http-get [{:keys [url response-validator success-event-creator failure-event-creator timeout-ms]}] + (let [on-success #(re-frame/dispatch (success-event-creator %)) + on-error #(re-frame/dispatch (failure-event-creator %)) + opts {:valid-response? response-validator + :timeout-ms timeout-ms}] + (utils/http-get url on-success on-error opts))) (re-frame/reg-fx :http-get diff --git a/src/status_im/ui/screens/wallet/events.cljs b/src/status_im/ui/screens/wallet/events.cljs index 92174e1fd9..c50aaffc54 100644 --- a/src/status_im/ui/screens/wallet/events.cljs +++ b/src/status_im/ui/screens/wallet/events.cljs @@ -119,7 +119,7 @@ :success-event :update-transactions-success :error-event :update-transactions-fail} :db (-> db - (clear-error-message :transaction-update) + (clear-error-message :transactions-update) (assoc-in [:wallet :transactions-loading?] true))}))) (handlers/register-handler-db @@ -219,3 +219,8 @@ :wallet/update-gas-price-success (fn [db [_ price edit?]] (assoc-in db [:wallet (if edit? :edit :send-transaction) :gas-price] price))) + +(handlers/register-handler-fx + :wallet/show-error + (fn [] + {:show-error (i18n/label :t/wallet-error)})) diff --git a/src/status_im/ui/screens/wallet/subs.cljs b/src/status_im/ui/screens/wallet/subs.cljs index 63674291a5..20722cee92 100644 --- a/src/status_im/ui/screens/wallet/subs.cljs +++ b/src/status_im/ui/screens/wallet/subs.cljs @@ -38,3 +38,9 @@ :<- [:wallet] (fn [wallet] (:balance-loading? wallet))) + +(reg-sub :wallet/error-message? + :<- [:wallet] + (fn [wallet] + (or (get-in wallet [:errors :balance-update]) + (get-in wallet [:errors :prices-update])))) diff --git a/src/status_im/ui/screens/wallet/transactions/subs.cljs b/src/status_im/ui/screens/wallet/transactions/subs.cljs index 9ccd28abe7..cd02e4f310 100644 --- a/src/status_im/ui/screens/wallet/transactions/subs.cljs +++ b/src/status_im/ui/screens/wallet/transactions/subs.cljs @@ -159,3 +159,8 @@ (reg-sub :wallet.transactions/filters (fn [db] (get-in db [:wallet.transactions :filters]))) + +(reg-sub :wallet.transactions/error-message? + :<- [:wallet] + (fn [wallet] + (get-in wallet [:errors :transactions-update]))) diff --git a/src/status_im/ui/screens/wallet/transactions/views.cljs b/src/status_im/ui/screens/wallet/transactions/views.cljs index 636bb6d264..8cd6de0849 100644 --- a/src/status_im/ui/screens/wallet/transactions/views.cljs +++ b/src/status_im/ui/screens/wallet/transactions/views.cljs @@ -102,8 +102,11 @@ (defview history-list [] (letsubs [transactions-history-list [:wallet.transactions/transactions-history-list] transactions-loading? [:wallet.transactions/transactions-loading?] + error-message? [:wallet.transactions/error-message?] filter-data [:wallet.transactions/filters]] [react/view components.styles/flex + (when error-message? + (re-frame/dispatch [:wallet/show-error])) [list/section-list {:sections (map #(update-transactions % filter-data) transactions-history-list) :render-fn render-transaction :empty-component [react/text {:style styles/empty-text} diff --git a/src/status_im/ui/screens/wallet/views.cljs b/src/status_im/ui/screens/wallet/views.cljs index f798b3628d..98e6304d6d 100644 --- a/src/status_im/ui/screens/wallet/views.cljs +++ b/src/status_im/ui/screens/wallet/views.cljs @@ -58,7 +58,7 @@ (defn current-tokens [visible-tokens network] (filter #(contains? visible-tokens (:symbol %)) (tokens/tokens-for (ethereum/network->chain-keyword network)))) -(defn- asset-section [network balance visible-tokens prices-loading? balance-loading?] +(defn- asset-section [network balance visible-tokens refreshing?] (let [tokens (current-tokens visible-tokens network) assets (map #(assoc % :amount (get balance (:symbol %))) (concat [tokens/ethereum] tokens))] [react/view styles/asset-section @@ -68,7 +68,7 @@ :data assets :render-fn render-asset :on-refresh #(re-frame/dispatch [:update-wallet (map :symbol tokens)]) - :refreshing (boolean (or prices-loading? balance-loading?))}]])) + :refreshing refreshing?}]])) (defview wallet [] (letsubs [network [:network] @@ -76,11 +76,16 @@ visible-tokens [:wallet.settings/visible-tokens] portfolio-value [:portfolio-value] prices-loading? [:prices-loading?] - balance-loading? [:wallet/balance-loading?]] + balance-loading? [:wallet/balance-loading?] + error-message? [:wallet/error-message?]] [react/view {:style components.styles/flex} + (when error-message? + (re-frame/dispatch [:wallet/show-error])) [toolbar-view] [react/view components.styles/flex [total-section portfolio-value] [list/action-list actions {:container-style styles/action-section}] - [asset-section network balance visible-tokens prices-loading? balance-loading?]]])) + [asset-section network balance visible-tokens + (and (or prices-loading? balance-loading?) + (not error-message?))]]])) diff --git a/src/status_im/utils/utils.cljs b/src/status_im/utils/utils.cljs index 27ab2ea268..1b4ce3d482 100644 --- a/src/status_im/utils/utils.cljs +++ b/src/status_im/utils/utils.cljs @@ -1,8 +1,12 @@ (ns status-im.utils.utils (:require [status-im.constants :as const] [status-im.i18n :as i18n] + [clojure.string :as str] [status-im.react-native.js-dependencies :as rn-dependencies])) +;; Default HTTP request timeout ms +(def http-request-default-timeout-ms 3000) + (defn show-popup [title content] (.alert (.-Alert rn-dependencies/react-native) title @@ -38,31 +42,37 @@ {:text (i18n/label :t/yes) :onPress on-accept}))))) (defn http-post + "Performs an HTTP POST request" ([action data on-success] (http-post action data on-success nil)) ([action data on-success on-error] - (-> (.fetch js/window - (str const/server-address action) - (clj->js {:method "POST" - :headers {:accept "application/json" - :content-type "application/json"} - :body (.stringify js/JSON (clj->js data))})) + (http-post action data on-success on-error nil)) + ([action data on-success on-error {:keys [timeout-ms] :as opts}] + (-> (rn-dependencies/fetch (str const/server-address action) + (clj->js {:method "POST" + :headers {:accept "application/json" + :content-type "application/json"} + :body (.stringify js/JSON (clj->js data)) + :timeout (or timeout-ms http-request-default-timeout-ms)})) (.then (fn [response] (.text response))) (.then (fn [text] (let [json (.parse js/JSON text) - obj (js->clj json :keywordize-keys true)] + obj (js->clj json :keywordize-keys true)] (on-success obj)))) (.catch (or on-error (fn [error] (show-popup "Error" (str error)))))))) (defn http-get + "Performs an HTTP GET request" ([url on-success on-error] - (http-get url nil on-success on-error)) - ([url valid-response? on-success on-error] - (-> (.fetch js/window url (clj->js {:method "GET" - :headers {"Cache-Control" "no-cache"}})) + (http-get url on-success on-error nil)) + ([url on-success on-error {:keys [valid-response? timeout-ms] :as opts}] + (-> (rn-dependencies/fetch url + (clj->js {:method "GET" + :headers {"Cache-Control" "no-cache"} + :timeout (or timeout-ms http-request-default-timeout-ms)})) (.then (fn [response] (let [ok? (.-ok response) ok?' (if valid-response?