Fixes #3639 Persist browser history

Signed-off-by: Andrey Shovkoplyas <motor4ik@gmail.com>
This commit is contained in:
pacamara 2018-06-21 22:05:12 +01:00 committed by Andrey Shovkoplyas
parent ff4071b7b5
commit 5e3f65eac8
No known key found for this signature in database
GPG Key ID: EAAB7C8622D860A4
9 changed files with 230 additions and 20 deletions

View File

@ -0,0 +1,17 @@
(ns status-im.data-store.realm.schemas.account.v8.browser)
(def schema {:name :browser
:primaryKey :browser-id
:properties {:browser-id :string
:name :string
:timestamp :int
:dapp? {:type :bool
:default false}
:url {:type :string
:optional true}
:contact {:type :string
:optional true}
:history-index {:type :int
:optional true}
:history {:type :vector
:optional true}}})

View File

@ -0,0 +1,56 @@
(ns status-im.models.browser-history
(:require [re-frame.core :as re-frame]))
(defn dont-store-history-on-nav-change? [db]
(get-in db [:browser/options :dont-store-history-on-nav-change?]))
(defn dont-store-history-on-nav-change! []
(re-frame/dispatch [:update-browser-options {:dont-store-history-on-nav-change? true}]))
(defn clear-dont-store-history-on-nav-change! []
(re-frame/dispatch [:update-browser-options {:dont-store-history-on-nav-change? false}]))
(defn dont-store-history-on-nav-change-if-history-exists [db browser-id]
(let [browsers (get-in db [:browser/browsers])
browser (get browsers browser-id)]
(hash-map :dont-store-history-on-nav-change? (some? (:history browser)))))
(defn back [browser]
(let [back-index (dec (:history-index browser))
back-url (nth (:history browser) back-index)]
(dont-store-history-on-nav-change!)
(re-frame/dispatch [:update-browser (-> browser (assoc :url back-url :history-index back-index))])))
(defn forward [browser]
(let [forward-index (inc (:history-index browser))
forward-url (nth (:history browser) forward-index)]
(dont-store-history-on-nav-change!)
(re-frame/dispatch [:update-browser (-> browser (assoc :url forward-url :history-index forward-index))])))
(defn can-go-back? [browser]
(let [hi (:history-index browser)]
(and (some? hi) (not= hi 0))))
(defn can-go-forward? [browser]
(let [hi (:history-index browser)]
(and (some? hi)
(< hi (dec (count (:history browser)))))))
(defn record-history-in-browser-if-needed [db raw-browser url loading]
(let [browser (assoc raw-browser :url url)]
(cond
loading
browser
(dont-store-history-on-nav-change? db)
(do (clear-dont-store-history-on-nav-change!)
browser)
:else
(let [history-index (:history-index browser)
history (or (:history browser) [])
history-url (if history-index (nth history history-index) nil)
history-to-index (if history-index (subvec history 0 (inc history-index)) [])
new-history (if (not= history-url url) (conj history-to-index url) history)
new-index (dec (count new-history))]
(assoc browser :history new-history :history-index new-index)))))

View File

@ -10,17 +10,17 @@
(spec/def :browser/name (spec/nilable string?))
(spec/def :browser/dapp? (spec/nilable boolean?))
(spec/def :browser/fullscreen? (spec/nilable boolean?))
(spec/def :browser/can-go-back? (spec/nilable boolean?))
(spec/def :browser/can-go-forward? (spec/nilable boolean?))
(spec/def :browser/error? (spec/nilable boolean?))
(spec/def :browser/history (spec/nilable vector?))
(spec/def :browser/history-index (spec/nilable int?))
(spec/def :browser/dont-store-history-on-nav-change? (spec/nilable boolean?))
(spec/def :browser/options
(allowed-keys
:opt-un [:browser/browser-id
:browser/can-go-back?
:browser/can-go-forward?
:browser/fullscreen?
:browser/error?]))
:browser/error?
:browser/dont-store-history-on-nav-change?]))
(spec/def :browser/browser
(allowed-keys
@ -29,6 +29,8 @@
:opt-un [:browser/name
:browser/dapp?
:browser/url
:browser/contact]))
:browser/contact
:browser/history
:browser/history-index]))
(spec/def :browser/browsers (spec/nilable (spec/map-of :global/not-empty-string :browser/browser)))
(spec/def :browser/browsers (spec/nilable (spec/map-of :global/not-empty-string :browser/browser)))

View File

@ -1,5 +1,6 @@
(ns status-im.ui.screens.browser.events
(:require status-im.ui.screens.browser.navigation
[status-im.models.browser-history :as browser-history]
[status-im.utils.handlers :as handlers]
[re-frame.core :as re-frame]
[status-im.utils.random :as random]
@ -73,6 +74,15 @@
(-> (add-browser-fx cofx new-browser)
(update-in [:db :browser/options] #(assoc % :browser-id (:browser-id new-browser)))))))
(handlers/register-handler-fx
:update-browser-on-nav-change
[re-frame/trim-v]
(fn [{:keys [db now] :as cofx} [browser url loading]]
(let [new-browser (get-new-browser browser now)
new-browser-with-history-updated (browser-history/record-history-in-browser-if-needed db new-browser url loading)]
(-> (add-browser-fx cofx new-browser-with-history-updated)
(update-in [:db :browser/options] assoc :browser-id (:browser-id new-browser-with-history-updated))))))
(handlers/register-handler-fx
:update-browser-options
[re-frame/trim-v]

View File

@ -1,6 +1,8 @@
(ns status-im.ui.screens.browser.navigation
(:require [status-im.ui.screens.navigation :as navigation]))
(:require [status-im.ui.screens.navigation :as navigation]
[status-im.models.browser-history :as browser-history]))
(defmethod navigation/preload-data! :browser
[db [_ _ {:keys [browser/browser-id]}]]
(assoc db :browser/options {:browser-id browser-id}))
(let [dont-store (browser-history/dont-store-history-on-nav-change-if-history-exists db browser-id)]
(assoc db :browser/options (assoc dont-store :browser-id browser-id))))

View File

@ -27,6 +27,9 @@
:align-items :center
:padding-horizontal 32})
(def disabled-button
{:opacity 0.4})
(def forward-button
{:margin-left 72})

View File

@ -3,6 +3,7 @@
[status-im.utils.views :as views])
(:require [cljs.reader :as reader]
[re-frame.core :as re-frame]
[status-im.models.browser-history :as browser-history]
[status-im.ui.components.react :as react]
[status-im.ui.screens.browser.styles :as styles]
[status-im.ui.components.status-bar.view :as status-bar]
@ -64,10 +65,9 @@
[components/activity-indicator {:animating true}]]))
(defn on-navigation-change [event browser]
(let [{:strs [url canGoBack canGoForward]} (js->clj event)]
(when (and (not (:dapp? browser)) (not= "about:blank" url))
(re-frame/dispatch [:update-browser (assoc browser :url url)]))
(re-frame/dispatch [:update-browser-options {:can-go-back? canGoBack :can-go-forward? canGoForward}])))
(let [{:strs [url loading]} (js->clj event)]
(when (not= "about:blank" url)
(re-frame/dispatch [:update-browser-on-nav-change browser url loading]))))
(defn get-inject-js [url]
(let [domain-name (nth (re-find #"^\w+://(www\.)?([^/:]+)" url) 2)]
@ -115,16 +115,17 @@
[react/view styles/background
[react/text (i18n/label :t/enter-dapp-url)]])
[react/view styles/toolbar
[react/touchable-highlight {:on-press #(.goBack @webview)
:disabled (not can-go-back?)
[react/touchable-highlight {:on-press #(browser-history/back browser)
:disabled (not (browser-history/can-go-back? browser))
:style (if (not (browser-history/can-go-back? browser)) styles/disabled-button)
:accessibility-label :previou-page-button}
[react/view (when (not can-go-back?) {:opacity 0.4})
[react/view
[vector-icons/icon :icons/arrow-left]]]
[react/touchable-highlight {:on-press #(.goForward @webview)
:disabled (not can-go-forward?)
:style styles/forward-button
[react/touchable-highlight {:on-press #(browser-history/forward browser)
:disabled (not (browser-history/can-go-forward? browser))
:style (merge styles/forward-button (if (not (browser-history/can-go-forward? browser)) styles/disabled-button))
:accessibility-label :next-page-button}
[react/view (when (not can-go-forward?) {:opacity 0.4})
[react/view
[vector-icons/icon :icons/arrow-right]]]]
(when-not dapp?
[tooltip/bottom-tooltip-info

View File

@ -0,0 +1,117 @@
(ns status-im.test.models.browser-history
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.models.browser-history :as model]
[re-frame.core :as re-frame]))
(def test-history ["http://oldest-site-visited.com", "http://most-recent-site-visited.com"])
(def test-browser-id "1234567890")
(deftest dont-store-history-on-nav-change?-test
(testing "dont-store-history-on-nav-change?"
(let [db {:browser/options {:dont-store-history-on-nav-change? true}}]
(is (model/dont-store-history-on-nav-change? db)))))
(defn fake-dispatch-dont-store-history-on-nav-change! [event]
(is (= :update-browser-options (get event 0)))
(let [eventMap (get event 1)]
(is (= (:dont-store-history-on-nav-change? eventMap) true))))
(deftest dont-store-history-on-nav-change!-test
(testing "dont-store-history-on-nav-change!"
(with-redefs [re-frame/dispatch fake-dispatch-dont-store-history-on-nav-change!]
(model/dont-store-history-on-nav-change!))))
(defn fake-dispatch-clear-dont-store-history-on-nav-change! [event]
(is (= :update-browser-options (get event 0)))
(let [eventMap (get event 1)]
(is (= (:dont-store-history-on-nav-change? eventMap) false))))
(deftest clear-dont-store-history-on-nav-change-test
(testing "clear-dont-store-history-on-nav-change!"
(with-redefs [re-frame/dispatch fake-dispatch-clear-dont-store-history-on-nav-change!]
(model/clear-dont-store-history-on-nav-change!))))
(deftest dont-store-history-on-nav-change-if-history-exists-test
(testing "dont-store-history-on-nav-change-if-history-exists"
(let [browser {:browser-id test-browser-id :history test-history}
db {:browser/browsers {test-browser-id browser}}
browser-no-history {:browser-id test-browser-id}
db-no-history {:browser/browsers {test-browser-id browser-no-history}}
result (model/dont-store-history-on-nav-change-if-history-exists db test-browser-id)
result-no-history (model/dont-store-history-on-nav-change-if-history-exists db-no-history test-browser-id)]
(is (get result :dont-store-history-on-nav-change?))
(is (not (get result-no-history :dont-store-history-on-nav-change?))))))
(defn dispatch-on-back-forwards [event expected-index]
(let [eventType (get event 0)
eventMap (get event 1)]
(if (= :update-browser eventType)
(do (is (= (:history-index eventMap) expected-index))
(is (= (:url eventMap) (get test-history expected-index))))
(do (is (= eventType :update-browser-options))
(is (= (:dont-store-history-on-nav-change? eventMap) true))))))
(defn dispatch-on-back [event]
(dispatch-on-back-forwards event 0))
(deftest back-test
(testing "back"
(let [browser {:browser-id test-browser-id :history-index 1 :history test-history}]
(with-redefs [re-frame/dispatch dispatch-on-back]
(model/back browser)))))
(defn dispatch-on-forward [event]
(dispatch-on-back-forwards event 1))
(deftest forward-test
(testing "forward"
(let [browser {:browser-id test-browser-id :history-index 0 :history test-history}]
(with-redefs [re-frame/dispatch dispatch-on-forward]
(model/forward browser)))))
(deftest can-go-back?-test
(testing "can-go-back?"
(let [browser {:history-index 0 :history test-history}]
(is (= (model/can-go-back? browser) false)))
(let [browser {:history-index 1 :history test-history}]
(is (= (model/can-go-back? browser) true)))))
(deftest can-go-forward?-test
(testing "can-go-forward?"
(let [browser {:history-index 0 :history test-history}]
(is (= (model/can-go-forward? browser) true)))
(let [browser {:history-index 1 :history test-history}]
(is (= (model/can-go-forward? browser) false)))))
(deftest record-history-in-browser-if-needed-test-1
(testing "record-history-in-browser-if-needed: dont record when still loading"
(let [raw-browser {:history-index 1 :history test-history}
url "http://third-site.com"
db {:browser/browsers {test-browser-id raw-browser}}]
(let [browser (model/record-history-in-browser-if-needed db raw-browser url true)]
(is (= (:history-index browser) 1))
(is (= (count (:history browser)) 2))))))
(defn record-history-in-browser-if-needed-test-2-dispatch [event]
(is (= :update-browser-options (get event 0)))
(let [eventMap (get event 1)]
(is (= (:dont-store-history-on-nav-change? eventMap) false))))
(deftest record-history-in-browser-if-needed-test-2
(testing "record-history-in-browser-if-needed: dont record if :dont-store-history-on-nav-change? true"
(let [raw-browser {:history-index 1 :history test-history}
url "http://third-site.com"
db {:browser/browsers {test-browser-id raw-browser} :browser/options {:dont-store-history-on-nav-change? true}}]
(with-redefs [re-frame/dispatch record-history-in-browser-if-needed-test-2-dispatch]
(let [browser (model/record-history-in-browser-if-needed db raw-browser url false)]
(is (= (:history-index browser) 1))
(is (= (count (:history browser)) 2)))))))
(deftest record-history-in-browser-if-needed-test-3
(testing "record-history-in-browser-if-needed: record if not loading and allowed"
(let [raw-browser {:history-index 1 :history test-history}
url "http://third-site.com"
db {:browser/browsers {test-browser-id raw-browser} :browser/options {:dont-store-history-on-nav-change? false}}]
(let [browser (model/record-history-in-browser-if-needed db raw-browser url false)]
(is (= (:history-index browser) 2))
(is (= (count (:history browser)) 3))))))

View File

@ -12,6 +12,7 @@
[status-im.test.bots.events]
[status-im.test.models.mailserver]
[status-im.test.models.bootnode]
[status-im.test.models.browser-history]
[status-im.test.models.account]
[status-im.test.models.contact]
[status-im.test.models.network]
@ -68,6 +69,7 @@
'status-im.test.models.mailserver
'status-im.test.models.bootnode
'status-im.test.models.account
'status-im.test.models.browser-history
'status-im.test.models.contact
'status-im.test.models.network
'status-im.test.models.wallet