Refactor browser and permissions

- move db and models ns to browser module
- remove browser navigation/preload-data
- rewrite dapp-permission logic and move code to permissions ns
- rewrite all browser events according to guidelines and move them to
`status-im.events` ns
- rewrite tests for browser and permissions
This commit is contained in:
yenda 2018-09-21 15:41:40 +02:00
parent c1014fd772
commit 8459fef358
No known key found for this signature in database
GPG Key ID: 0095623C0069DCE6
33 changed files with 943 additions and 758 deletions

View File

@ -0,0 +1,315 @@
(ns status-im.browser.core
(:require [clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.browser.permissions :as browser.permissions]
[status-im.constants :as constants]
[status-im.data-store.browser :as browser-store]
[status-im.i18n :as i18n]
[status-im.js-dependencies :as dependencies]
[status-im.native-module.core :as status]
[status-im.ui.components.list-selection :as list-selection]
[status-im.ui.screens.browser.default-dapps :as default-dapps]
[status-im.ui.screens.navigation :as navigation]
[status-im.utils.ethereum.core :as ethereum]
[status-im.utils.ethereum.ens :as ens]
[status-im.utils.ethereum.resolver :as resolver]
[status-im.utils.handlers-macro :as handlers-macro]
[status-im.utils.http :as http]
[status-im.utils.multihash :as multihash]
[status-im.utils.platform :as platform]
[status-im.utils.random :as random]
[status-im.utils.types :as types]
[status-im.utils.universal-links.core :as utils.universal-links]
[taoensso.timbre :as log]))
(defn initialize-browsers
[{:keys [db all-stored-browsers]}]
(let [browsers (into {} (map #(vector (:browser-id %) %) all-stored-browsers))]
{:db (assoc db :browser/browsers browsers)}))
(defn initialize-dapp-permissions
[{:keys [db all-dapp-permissions]}]
(let [dapp-permissions (into {} (map #(vector (:dapp %) %) all-dapp-permissions))]
{:db (assoc db :dapps/permissions dapp-permissions)}))
(defn get-current-url [{:keys [history history-index]}]
(when (and history-index history)
(nth history history-index)))
(defn secure? [{:keys [error? dapp?] :as browser}]
(or dapp?
(and (not error?)
(string/starts-with? (get-current-url browser) "https://"))))
(defn remove-browser [browser-id {:keys [db]}]
{:db (update-in db [:browser/browsers] dissoc browser-id)
:data-store/tx [(browser-store/remove-browser-tx browser-id)]})
(defn check-if-dapp-in-list [{:keys [history history-index] :as browser}]
(let [history-host (http/url-host (try (nth history history-index) (catch js/Error _)))
dapp (first (filter #(= history-host (http/url-host (:dapp-url %))) (apply concat (mapv :data default-dapps/all))))]
(if dapp
;;TODO(yenda): the consequence of this is that if user goes to a different
;;url from a dapp browser, the name of the browser in the home screen will
;;change
(assoc browser :dapp? true :name (:name dapp))
(assoc browser :dapp? false :name (i18n/label :t/browser)))))
(defn check-if-phishing-url [{:keys [history history-index] :as browser}]
(let [history-host (http/url-host (try (nth history history-index) (catch js/Error _)))]
(assoc browser :unsafe? (dependencies/phishing-detect history-host))))
(defn update-browser
[{:keys [browser-id history history-index error? dapp?] :as browser}
{:keys [db now]}]
(let [updated-browser (-> (assoc browser :timestamp now)
(check-if-dapp-in-list)
(check-if-phishing-url))]
{:db (update-in db
[:browser/browsers browser-id]
merge updated-browser)
:data-store/tx [(browser-store/save-browser-tx updated-browser)]}))
(defn get-current-browser [db]
(get-in db [:browser/browsers (get-in db [:browser/options :browser-id])]))
(defn can-go-back? [{:keys [history-index]}]
(pos? history-index))
(defn navigate-to-previous-page
[cofx]
(let [{:keys [history-index] :as browser} (get-current-browser (:db cofx))]
(when (can-go-back? browser)
(update-browser (assoc browser :history-index (dec history-index)) cofx))))
(defn can-go-forward? [{:keys [history-index history]}]
(< history-index (dec (count history))))
(defn navigate-to-next-page
[cofx]
(let [{:keys [history-index] :as browser} (get-current-browser (:db cofx))]
(when (can-go-forward? browser)
(update-browser (assoc browser :history-index (inc history-index)) cofx))))
(defn update-browser-history
;; TODO: not clear how this works
[browser url loading? cofx]
(when-not loading?
(let [history-index (:history-index browser)
history (:history browser)
history-url (get-current-url browser)]
(when (not= history-url url)
(let [slash? (= url (str history-url "/"))
new-history (if slash?
(assoc history history-index url)
(conj (subvec history 0 (inc history-index)) url))
new-index (if slash?
history-index
(dec (count new-history)))]
(update-browser (assoc browser
:history new-history
:history-index new-index)
cofx))))))
(defn ens? [host]
(and (string? host)
(string/ends-with? host ".eth")))
(defn resolve-ens-multihash-callback [hex]
(let [hash (when hex (multihash/base58 (multihash/create :sha2-256 (subs hex 2))))]
(if (and hash (not= hash resolver/default-hash))
(re-frame/dispatch [:browser.callback/resolve-ens-multihash-success hash])
(re-frame/dispatch [:browser.callback/resolve-ens-multihash-error]))))
(defn resolve-ens-multihash-success
[hash {:keys [db] :as cofx}]
(let [options (:browser/options db)
browsers (:browser/browsers db)
browser (get browsers (:browser-id options))
history-index (:history-index browser)]
(handlers-macro/merge-fx
cofx
{:db (assoc-in db [:browser/options :resolving?] false)}
(update-browser (assoc-in browser [:history history-index]
(str "https://ipfs.infura.io/ipfs/" hash))))))
(defn resolve-ens-multihash
[host loading? error? {{:keys [web3 network] :as db} :db}]
(when (and (not loading?)
(not error?)
(ens? host))
(let [network (get-in db [:account/account :networks network])
chain (ethereum/network->chain-keyword network)]
{:db (assoc-in db [:browser/options :resolving?] true)
:browser/resolve-ens-multihash {:web3 web3
:registry (get ens/ens-registries
chain)
:ens-name host
:cb resolve-ens-multihash-callback}})))
(defn update-browser-option
[option-key option-value {:keys [db]}]
{:db (assoc-in db [:browser/options option-key] option-value)})
(defn handle-browser-error [cofx]
(handlers-macro/merge-fx cofx
(update-browser-option :error? true)
(update-browser-option :loading? false)))
(defn update-browser-loading-option
[loading? cofx]
;; TODO(yenda) why are we doing this ?
(when platform/ios?
(update-browser-option :loading? loading? cofx)))
(defn update-browser-on-nav-change
[browser url loading? error? cofx]
(when (not= "about:blank" url)
(let [host (http/url-host url)]
(handlers-macro/merge-fx cofx
(resolve-ens-multihash host loading? error?)
(update-browser-history browser url loading?)))))
(defn navigation-state-changed [event error? cofx]
(let [browser (get-current-browser (:db cofx))
{:strs [url loading]} (js->clj event)]
(handlers-macro/merge-fx cofx
(update-browser-loading-option loading)
(update-browser-on-nav-change browser url loading error?))))
(defn open-url-in-current-browser
"Opens a url in the current browser, which mean no new entry is added to the home page
and history of the current browser is updated so that the user can navigate back to the
origin url"
;; TODO(yenda) is that desirable ?
[url cofx]
(let [browser (get-current-browser (:db cofx))
normalized-url (http/normalize-and-decode-url url)
host (http/url-host normalized-url)]
(handlers-macro/merge-fx cofx
(update-browser-option :url-editing? false)
(resolve-ens-multihash host false false)
(update-browser-history browser normalized-url false))))
(defn navigate-to-browser
[{{:keys [view-id]} :db :as cofx}]
(if (= view-id :dapp-description)
(navigation/navigate-reset
{:index 1
:actions [{:routeName :home}
{:routeName :browser}]}
cofx)
(navigation/navigate-to-cofx :browser nil cofx)))
(defn open-url
"Opens a url in the browser. If a host can be extracted from the url and
there is already a browser for this host, this browser is reused
If the browser is reused, the history is flushed"
[url {:keys [db] :as cofx}]
(let [normalized-url (http/normalize-and-decode-url url)
host (http/url-host normalized-url)
browser {:browser-id (or host (random/id))
:history-index 0
:history [normalized-url]}]
(handlers-macro/merge-fx cofx
{:db (assoc db :browser/options
{:browser-id (:browser-id browser)})}
(navigate-to-browser)
(update-browser browser)
(resolve-ens-multihash host false false))))
(defn open-existing-browser
"Opens an existing browser with it's history"
[browser-id {:keys [db] :as cofx}]
(let [browser (get-in db [:browser/browsers browser-id])]
(handlers-macro/merge-fx cofx
{:db (assoc db :browser/options
{:browser-id browser-id})}
(update-browser browser)
(navigation/navigate-to-cofx :browser nil))))
(defn web3-send-async
[{:keys [method] :as payload} message-id {:keys [db]}]
(if (or (= method constants/web3-send-transaction)
(= method constants/web3-personal-sign))
{:db (update-in db [:wallet :transactions-queue] conj {:message-id message-id :payload payload})
;;TODO(yenda): refactor check-dapps-transactions-queue to remove this dispatch
:dispatch [:check-dapps-transactions-queue]}
{:browser/call-rpc [payload
#(re-frame/dispatch [:browser.callback/call-rpc
{:type constants/web3-send-async-callback
:messageId message-id
:error %1
:result %2}])]}))
(defn send-to-bridge [message cofx]
{:browser/send-to-bridge {:message message
:webview (get-in cofx [:db :webview-bridge])}})
(defn web3-send-async-read-only
[dapp-name {:keys [method] :as payload} message-id {:keys [db] :as cofx}]
(let [{:dapps/keys [permissions]} db]
(if (and (#{"eth_accounts" "eth_coinbase" "eth_sendTransaction" "eth_sign"
"eth_signTypedData" "personal_sign" "personal_ecRecover"} method)
(not (some #{"WEB3"} (get-in permissions [dapp-name :permissions]))))
(send-to-bridge {:type constants/web3-send-async-callback
:messageId message-id
:error "Denied"}
cofx)
(web3-send-async payload message-id cofx))))
(defn process-bridge-message
[message {:keys [db] :as cofx}]
(let [{:browser/keys [options browsers]} db
{:keys [browser-id]} options
browser (get browsers browser-id)
data (types/json->clj message)
{{:keys [url]} :navState :keys [type host permissions payload messageId]} data
{:keys [dapp? name]} browser
dapp-name (if dapp? name host)]
(cond
(and (= type constants/history-state-changed)
platform/ios?
(not= "about:blank" url))
(update-browser-history browser url false cofx)
(= type constants/web3-send-async)
(web3-send-async payload messageId cofx)
(= type constants/web3-send-async-read-only)
(web3-send-async-read-only dapp-name payload messageId cofx)
(= type constants/status-api-request)
(browser.permissions/process-permissions dapp-name permissions cofx))))
(defn handle-message-link [link cofx]
(if (utils.universal-links/universal-link? link)
(utils.universal-links/handle-url link cofx)
{:browser/show-browser-selection link}))
(re-frame/reg-fx
:browser/resolve-ens-multihash
(fn [{:keys [web3 registry ens-name cb]}]
(resolver/content web3 registry ens-name cb)))
(re-frame/reg-fx
:browser/send-to-bridge
(fn [{:keys [message webview]}]
(.sendToBridge webview (types/clj->json message))))
(re-frame/reg-fx
:browser/call-rpc
(fn [[payload callback]]
(status/call-rpc
(types/clj->json payload)
(fn [response]
(if (= "" response)
(do
(log/warn :web3-response-error)
(callback "web3-response-error" nil))
(callback nil (.parse js/JSON response)))))))
(re-frame/reg-fx
:browser/show-browser-selection
(fn [link]
(list-selection/browse link)))

View File

@ -1,6 +1,6 @@
(ns status-im.ui.screens.browser.db
(:require-macros [status-im.utils.db :refer [allowed-keys]])
(:require [cljs.spec.alpha :as spec]))
(ns status-im.browser.db
(:require [cljs.spec.alpha :as spec])
(:require-macros [status-im.utils.db :refer [allowed-keys]]))
(spec/def :browser/browser-id (spec/nilable string?))
(spec/def :browser/timestamp (spec/nilable int?))
@ -9,12 +9,16 @@
(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/unsafe? (spec/nilable boolean?))
(spec/def :browser/loading? (spec/nilable boolean?))
(spec/def :browser/resolving? (spec/nilable boolean?))
(spec/def :browser/url-editing? (spec/nilable boolean?))
(spec/def :browser/show-tooltip (spec/nilable keyword?))
(spec/def :browser/show-permission (spec/nilable map?))
(spec/def :browser/permissions-queue (spec/nilable any?))
(spec/def :browser/pending-permissions set?)
(spec/def :browser/allowed-permissions set?)
(spec/def :browser/requested-permissions set?)
(spec/def :browser/options
(spec/nilable
@ -25,7 +29,9 @@
:browser/url-editing?
:browser/show-tooltip
:browser/show-permission
:browser/permissions-queue
:browser/pending-permissions
:browser/allowed-permissions
:browser/requested-permissions
:browser/error?])))
(spec/def :browser/browser

View File

@ -0,0 +1,125 @@
(ns status-im.browser.permissions
(:require [clojure.set :as set]
[status-im.constants :as constants]
[status-im.data-store.dapp-permissions :as dapp-permissions]
[status-im.i18n :as i18n]
[status-im.utils.ethereum.core :as ethereum]
[status-im.utils.handlers-macro :as handlers-macro]))
(def supported-permissions
{constants/dapp-permission-contact-code {:title (i18n/label :t/wants-to-access-profile)
:description (i18n/label :t/your-contact-code)
:icon :icons/profile-active}
constants/dapp-permission-web3 {:title (i18n/label :t/dapp-would-like-to-connect-wallet)
:description (i18n/label :t/allowing-authorizes-this-dapp)
:icon :icons/wallet-active}})
(defn get-pending-permissions [db]
(get-in db [:browser/options :pending-permissions]))
(defn remove-pending-permission [db pending-permission]
(update-in db [:browser/options :pending-permissions] disj pending-permission))
(defn get-allowed-permissions [db]
(get-in db [:browser/options :allowed-permissions]))
(defn get-requested-permissions [db]
(get-in db [:browser/options :requested-permissions]))
(defn add-allowed-permission [db allowed-permission]
(update-in db [:browser/options :allowed-permissions] conj allowed-permission))
(defn get-permissions-data [allowed-permissions cofx]
(let [account (get-in cofx [:db :account/account])]
(select-keys {constants/dapp-permission-contact-code (:public-key account)
constants/dapp-permission-web3 (ethereum/normalized-address
(:address account))}
(vec allowed-permissions))))
(defn send-permissions-data-to-bridge
"If there is granted permissions, return the data to the bridge
If no permission were granted and dapp requested web3 permission,
return `web3-permission-request-denied` message type
Otherwise do nothing"
;;TODO(yenda): this was the behavior of the code prior to refactoring
;;if this is not the intended behavior please create an issue for that
[{:keys [db] :as cofx}]
(let [allowed-permissions (get-allowed-permissions db)
requested-permissions (get-requested-permissions db)
new-db (update db :browser/options dissoc
:pending-permissions
:allowed-permissions
:requested-permissions)]
(cond
(not-empty allowed-permissions)
{:db new-db
:browser/send-to-bridge {:message {:type constants/status-api-success
:data (get-permissions-data allowed-permissions cofx)
:keys (vec allowed-permissions)}
:webview (:webview-bridge db)}}
(and (empty? allowed-permissions)
(requested-permissions constants/dapp-permission-web3))
{:db new-db
:browser/send-to-bridge {:message {:type constants/web3-permission-request-denied}
:webview (:webview-bridge db)}}
:else
{:db new-db})))
(defn update-dapp-permissions
[dapp-name {:keys [db]}]
(let [allowed-permissions-set (get-allowed-permissions db)
allowed-permissions {:dapp dapp-name
:permissions (vec allowed-permissions-set)}]
(when (not-empty allowed-permissions-set)
{:db (assoc-in db [:dapps/permissions dapp-name] allowed-permissions)
:data-store/tx [(dapp-permissions/save-dapp-permissions allowed-permissions)]})))
(defn process-next-permission
"Process next permission by removing it from pending permissions
and prompting user
if there is no pending permissions left, save all granted permissions
and return the result to the bridge"
[dapp-name {:keys [db] :as cofx}]
(let [pending-permissions (get-pending-permissions db)
next-permission (first pending-permissions)]
(if next-permission
{:db (-> db
(remove-pending-permission next-permission)
(assoc-in [:browser/options :show-permission]
{:requested-permission next-permission
:dapp-name dapp-name}))}
(handlers-macro/merge-fx cofx
{:db (assoc-in db [:browser/options :show-permission] nil)}
(update-dapp-permissions dapp-name)
(send-permissions-data-to-bridge)))))
(defn allow-permission
"Add permission to set of allowed permission and process next permission"
[dapp-name permission {:keys [db] :as cofx}]
(handlers-macro/merge-fx cofx
{:db (add-allowed-permission db permission)}
(process-next-permission dapp-name)))
(defn process-permissions
"Process the permissions requested by a dapp
If all supported permissions are already granted, return the result immediatly
to the bridge
Otherwise process the first permission which will prompt user"
[dapp-name requested-permissions cofx]
(let [requested-permissions-set (set requested-permissions)
current-dapp-permissions (get-in cofx [:db :dapps/permissions dapp-name :permissions])
current-dapp-permissions-set (set current-dapp-permissions)
supported-permissions-set (set (keys supported-permissions))
allowed-permissions (set/intersection requested-permissions-set
current-dapp-permissions-set)
pending-permissions (-> requested-permissions-set
(set/intersection supported-permissions-set)
(set/difference current-dapp-permissions-set))
new-cofx (update-in cofx [:db :browser/options] merge
{:pending-permissions pending-permissions
:allowed-permissions allowed-permissions
:requested-permissions requested-permissions-set})]
(if (empty? pending-permissions)
(send-permissions-data-to-bridge new-cofx)
(process-next-permission dapp-name new-cofx))))

View File

@ -7,6 +7,8 @@
[status-im.accounts.recover.core :as accounts.recover]
[status-im.accounts.update.core :as accounts.update]
[status-im.bootnodes.core :as bootnodes]
[status-im.browser.core :as browser]
[status-im.browser.permissions :as browser.permissions]
[status-im.data-store.core :as data-store]
[status-im.fleet.core :as fleet]
[status-im.hardwallet.core :as hardwallet]
@ -522,3 +524,120 @@
:hardwallet.ui/go-to-settings-button-pressed
(fn [_ _]
{:hardwallet/open-nfc-settings nil}))
(handlers/register-handler-fx
:hardwallet.ui/connect-info-button-pressed
(fn [cofx _]
(browser/open-url "https://hardwallet.status.im" cofx)))
;; browser module
(handlers/register-handler-fx
:browser.ui/browser-item-selected
(fn [cofx [_ browser-id]]
(browser/open-existing-browser browser-id cofx)))
(handlers/register-handler-fx
:browser.ui/url-input-pressed
(fn [cofx _]
(browser/update-browser-option :url-editing? true cofx)))
(handlers/register-handler-fx
:browser.ui/url-input-blured
(fn [cofx _]
(browser/update-browser-option :url-editing? false cofx)))
(handlers/register-handler-fx
:browser.ui/url-submitted
(fn [cofx [_ url]]
(browser/open-url-in-current-browser url cofx)))
(handlers/register-handler-fx
:browser.ui/message-link-pressed
(fn [cofx [_ link]]
(browser/handle-message-link link cofx)))
(handlers/register-handler-fx
:browser.ui/remove-browser-pressed
(fn [cofx [_ browser-id]]
(browser/remove-browser browser-id cofx)))
(handlers/register-handler-fx
:browser.ui/lock-pressed
(fn [cofx [_ secure?]]
(browser/update-browser-option :show-tooltip (if secure? :secure :not-secure) cofx)))
(handlers/register-handler-fx
:browser.ui/close-tooltip-pressed
(fn [cofx _]
(browser/update-browser-option :show-tooltip nil cofx)))
(handlers/register-handler-fx
:browser.ui/previous-page-button-pressed
(fn [cofx _]
(browser/navigate-to-previous-page cofx)))
(handlers/register-handler-fx
:browser.ui/next-page-button-pressed
(fn [cofx _]
(browser/navigate-to-next-page cofx)))
(handlers/register-handler-fx
:browser/navigation-state-changed
(fn [cofx [_ event error?]]
(browser/navigation-state-changed event error? cofx)))
(handlers/register-handler-fx
:browser/bridge-message-received
(fn [cofx [_ message]]
(browser/process-bridge-message message cofx)))
(handlers/register-handler-fx
:browser/error-occured
(fn [cofx _]
(browser/handle-browser-error cofx)))
(handlers/register-handler-fx
:browser/loading-started
(fn [cofx _]
(browser/update-browser-option :error? false cofx)))
(handlers/register-handler-fx
:browser.callback/resolve-ens-multihash-success
(fn [cofx [_ hash]]
(browser/resolve-ens-multihash-success hash cofx)))
(handlers/register-handler-fx
:browser.callback/resolve-ens-multihash-error
(fn [cofx _]
(browser/update-browser-option :resolving? false cofx)))
(handlers/register-handler-fx
:browser.callback/call-rpc
(fn [cofx [_ message]]
(browser/send-to-bridge message cofx)))
(handlers/register-handler-fx
:browser.permissions.ui/dapp-permission-allowed
(fn [cofx [_ dapp-name permission]]
(browser.permissions/allow-permission dapp-name permission cofx)))
(handlers/register-handler-fx
:browser.permissions.ui/dapp-permission-denied
(fn [cofx [_ dapp-name]]
(browser.permissions/process-next-permission dapp-name cofx)))
(handlers/register-handler-fx
:browser.ui/open-in-status-option-selected
(fn [cofx [_ url]]
(browser/open-url url cofx)))
(handlers/register-handler-fx
:browser.ui/open-dapp-button-pressed
(fn [cofx [_ dapp-url]]
(browser/open-url dapp-url cofx)))
(handlers/register-handler-fx
:browser.ui/dapp-url-submitted
(fn [cofx [_ dapp-url]]
(browser/open-url dapp-url cofx)))

View File

@ -7,7 +7,7 @@
[status-im.data-store.core :as data-store]
[status-im.data-store.realm.core :as realm]
[status-im.i18n :as i18n]
[status-im.models.browser :as browser]
[status-im.browser.core :as browser]
[status-im.models.contacts :as models.contacts]
[status-im.models.dev-server :as models.dev-server]
[status-im.protocol.core :as protocol]

View File

@ -1,195 +0,0 @@
(ns status-im.models.browser
(:require [re-frame.core :as re-frame]
[clojure.string :as string]
[status-im.constants :as constants]
[status-im.data-store.browser :as browser-store]
[status-im.data-store.dapp-permissions :as dapp-permissions]
[status-im.i18n :as i18n]
[status-im.js-dependencies :as dependencies]
[status-im.ui.screens.browser.default-dapps :as default-dapps]
[status-im.utils.http :as http]
[status-im.utils.ethereum.resolver :as resolver]
[status-im.utils.ethereum.core :as ethereum]
[status-im.utils.ethereum.ens :as ens]
[status-im.utils.multihash :as multihash]
[status-im.utils.handlers-macro :as handlers-macro]
[status-im.ui.screens.navigation :as navigation]))
(defn get-current-url [{:keys [history history-index]}]
(when (and history-index history)
(nth history history-index)))
(defn can-go-back? [{:keys [history-index]}]
(pos? history-index))
(defn can-go-forward? [{:keys [history-index history]}]
(< history-index (dec (count history))))
(defn check-if-dapp-in-list [{:keys [history history-index] :as browser}]
(let [history-host (http/url-host (try (nth history history-index) (catch js/Error _)))
dapp (first (filter #(= history-host (http/url-host (:dapp-url %))) (apply concat (mapv :data default-dapps/all))))]
(if dapp
(assoc browser :dapp? true :name (:name dapp))
(assoc browser :dapp? false :name (i18n/label :t/browser)))))
(defn check-if-phishing-url [{:keys [history history-index] :as browser}]
(let [history-host (http/url-host (try (nth history history-index) (catch js/Error _)))]
(assoc browser :unsafe? (dependencies/phishing-detect history-host))))
(defn update-browser-fx [browser {:keys [db now]}]
(let [updated-browser (-> (assoc browser :timestamp now)
(check-if-dapp-in-list)
(check-if-phishing-url))]
{:db (update-in db [:browser/browsers (:browser-id updated-browser)]
merge updated-browser)
:data-store/tx [(browser-store/save-browser-tx updated-browser)]}))
(defn update-browser-history-fx [browser url loading? cofx]
(when-not loading?
(let [history-index (:history-index browser)
history (:history browser)
history-url (try (nth history history-index) (catch js/Error _))]
(when (not= history-url url)
(let [slash? (= url (str history-url "/"))
new-history (if slash?
(assoc history history-index url)
(conj (subvec history 0 (inc history-index)) url))
new-index (if slash?
history-index
(dec (count new-history)))]
(update-browser-fx (assoc browser :history new-history :history-index new-index)
cofx))))))
(defn ens? [host]
(and (string? host)
(string/ends-with? host ".eth")))
(defn ens-multihash-callback [hex]
(let [hash (when hex (multihash/base58 (multihash/create :sha2-256 (subs hex 2))))]
(if (and hash (not= hash resolver/default-hash))
(re-frame/dispatch [:ens-multihash-resolved hash])
(re-frame/dispatch [:update-browser-options {:resolving? false}]))))
(defn resolve-multihash-fx [host loading error? {{:keys [web3 network] :as db} :db}]
(let [network (get-in db [:account/account :networks network])
chain (ethereum/network->chain-keyword network)]
(if (and (not loading) (not error?) (ens? host))
{:db (assoc-in db [:browser/options :resolving?] true)
:resolve-ens-multihash {:web3 web3
:registry (get ens/ens-registries
chain)
:ens-name host
:cb ens-multihash-callback}}
{})))
(defn navigate-to-browser
[{{:keys [view-id]} :db :as cofx}]
(if (= view-id :dapp-description)
(navigation/navigate-reset
{:index 1
:actions [{:routeName :home}
{:routeName :browser}]}
cofx)
(navigation/navigate-to-cofx :browser nil cofx)))
(defn update-new-browser-and-navigate
[host browser {:keys [db] :as cofx}]
(handlers-macro/merge-fx
cofx
{:db (assoc db :browser/options
{:browser-id (:browser-id browser)
:resolving? (ens? host)})}
(navigate-to-browser)
(update-browser-fx browser)
(resolve-multihash-fx host false false)))
(defn update-browser-and-navigate [browser cofx]
(merge (update-browser-fx browser cofx)
{:dispatch [:navigate-to :browser {:browser-id (:browser-id browser)}]}))
(def permissions {constants/dapp-permission-contact-code {:title (i18n/label :t/wants-to-access-profile)
:description (i18n/label :t/your-contact-code)
:icon :icons/profile-active}
constants/dapp-permission-web3 {:title (i18n/label :t/dapp-would-like-to-connect-wallet)
:description (i18n/label :t/allowing-authorizes-this-dapp)
:icon :icons/wallet-active}})
(defn update-dapp-permissions-fx [{:keys [db]} permissions]
{:db (-> db
(assoc-in [:browser/options :show-permission] nil)
(assoc-in [:dapps/permissions (:dapp permissions)] permissions))
:data-store/tx [(dapp-permissions/save-dapp-permissions permissions)]})
(defn request-permission [{:keys [dapp-name index requested-permissions permissions-allowed user-permissions
permissions-data]
:as params}
{:keys [db] :as cofx}]
;; iterate all requested permissions
(if (< index (count requested-permissions))
(let [requested-permission (get requested-permissions index)]
;; if requested permission exists and valid continue if not decline permission
(if (and requested-permission (get permissions requested-permission))
;; if permission already allowed go to next, if not, show confirmation dialog
(if ((set user-permissions) requested-permission)
{:dispatch [:next-dapp-permission params requested-permission permissions-data]}
{:db (assoc-in db [:browser/options :show-permission] {:requested-permission requested-permission
:params params})})
{:dispatch [:next-dapp-permission params]}))
(cond-> (update-dapp-permissions-fx cofx {:dapp dapp-name
:permissions (vec (set (concat (keys permissions-allowed)
user-permissions)))})
(not (zero? (count permissions-allowed)))
(assoc :send-to-bridge-fx [{:type constants/status-api-success
:data permissions-allowed
:keys (keys permissions-allowed)}
(:webview-bridge db)])
(and (zero? (count permissions-allowed)) (= constants/dapp-permission-web3 (first requested-permissions)))
(assoc :send-to-bridge-fx [{:type constants/web3-permission-request-denied}
(:webview-bridge db)])
true
(assoc :dispatch [:check-permissions-queue]))))
(defn next-permission [{:keys [params permission permissions-data]} cofx]
(request-permission
(cond-> params
true
(update :index inc)
(and permission permissions-data)
(assoc-in [:permissions-allowed permission] (get permissions-data permission)))
cofx))
(defn web3-send-async [{:keys [method] :as payload} message-id {:keys [db]}]
(if (or (= method constants/web3-send-transaction)
(= method constants/web3-personal-sign))
{:db (update-in db [:wallet :transactions-queue] conj {:message-id message-id :payload payload})
:dispatch [:check-dapps-transactions-queue]}
{:call-rpc [payload
#(re-frame/dispatch [:send-to-bridge
{:type constants/web3-send-async-callback
:messageId message-id
:error %1
:result %2}])]}))
(defn web3-send-async-read-only [dapp-name {:keys [method] :as payload} message-id {:keys [db] :as cofx}]
(let [{:dapps/keys [permissions]} db]
(if (and (#{"eth_accounts" "eth_coinbase" "eth_sendTransaction" "eth_sign"
"eth_signTypedData" "personal_sign" "personal_ecRecover"} method)
(not (some #{"WEB3"} (get-in permissions [dapp-name :permissions]))))
{:dispatch [:send-to-bridge
{:type constants/web3-send-async-callback
:messageId message-id
:error "Denied"}]}
(web3-send-async payload message-id cofx))))
(defn initialize-browsers
[{:keys [db all-stored-browsers]}]
(let [browsers (into {} (map #(vector (:browser-id %) %) all-stored-browsers))]
{:db (assoc db :browser/browsers browsers)}))
(defn initialize-dapp-permissions
[{:keys [db all-dapp-permissions]}]
(let [dapp-permissions (into {} (map #(vector (:dapp %) %) all-dapp-permissions))]
{:db (assoc db :dapps/permissions dapp-permissions)}))

View File

@ -1,6 +1,8 @@
(ns status-im.models.dev-server
(:require [status-im.network.core :as network]
[clojure.string :as string]))
(:require [clojure.string :as string]
[status-im.browser.core :as browser]
[status-im.network.core :as network]
[status-im.utils.handlers-macro :as handlers-macro]))
(defn start-if-needed
[{{:account/keys [account]} :db}]
@ -17,9 +19,10 @@
{:dev-server/respond [200 {:message "Pong!"}]})
(defmethod process-request! [:POST "dapp" "open"]
[{{:keys [url]} :data}]
{:dispatch [:open-url-in-browser url]
:dev-server/respond [200 {:message "URL has been opened."}]})
[{{:keys [url]} :data cofx :cofx}]
(handlers-macro/merge-fx cofx
{:dev-server/respond [200 {:message "URL has been opened."}]}
(browser/open-url url)))
(defmethod process-request! [:POST "network" nil]
[{:keys [cofx data]}]

View File

@ -110,18 +110,18 @@
[second_param first_param]))))
(defn web3-error-callback [fx {:keys [webview-bridge]} {:keys [message-id]} message]
(assoc fx :send-to-bridge-fx [{:type constants/web3-send-async-callback
:messageId message-id
:error message}
webview-bridge]))
(assoc fx :browser/send-to-bridge {:message {:type constants/web3-send-async-callback
:messageId message-id
:error message}
:webview webview-bridge}))
(defn dapp-complete-transaction [id result method message-id webview]
(cond-> {:send-to-bridge-fx [{:type constants/web3-send-async-callback
:messageId message-id
:result {:jsonrpc "2.0"
:id (int id)
:result result}}
webview]
(cond-> {:browser/send-to-bridge {:message {:type constants/web3-send-async-callback
:messageId message-id
:result {:jsonrpc "2.0"
:id (int id)
:result result}}
:webview webview}
:dispatch [:navigate-back]}
(= method constants/web3-personal-sign)

View File

@ -31,7 +31,7 @@
(defn browse [link]
(show {:title (i18n/label :t/browsing-title)
:options [{:label (i18n/label :t/browsing-open-in-status)
:action #(re-frame/dispatch [:open-url-in-browser link])}
:action #(re-frame/dispatch [:browser.ui/open-in-status-option-selected link])}
{:label (i18n/label :t/browsing-open-in-web-browser)
:action #(.openURL react/linking (http/normalize-url link))}]
:cancel-text (i18n/label :t/browsing-cancel)}))
@ -39,5 +39,5 @@
(defn browse-dapp [link]
(show {:title (i18n/label :t/browsing-title)
:options [{:label (i18n/label :t/browsing-open-in-status)
:action #(re-frame/dispatch [:open-url-in-browser link])}]
:action #(re-frame/dispatch [:browser.ui/open-in-status-option-selected link])}]
:cancel-text (i18n/label :t/browsing-cancel)}))

View File

@ -29,9 +29,7 @@
[components/separator]
[react/view add-new.styles/input-container
[react/text-input {:on-change-text #(reset! url-text %)
:on-submit-editing #(do
(re-frame/dispatch [:navigate-to :home])
(re-frame/dispatch [:open-url-in-browser @url-text]))
:on-submit-editing #(re-frame/dispatch [:browser.ui/dapp-url-submitted @url-text])
:placeholder (i18n/label :t/enter-url)
:auto-capitalize :none
:auto-correct false
@ -62,7 +60,7 @@
:icon :icons/address
:icon-opts {:color colors/blue}
:accessibility-label :open-dapp-button
:on-press #(re-frame/dispatch [:open-url-in-browser dapp-url])}]
:on-press #(re-frame/dispatch [:browser.ui/open-dapp-button-pressed dapp-url])}]
[components/separator {:margin-left 72}]]
[react/view styles/description-container
[react/i18n-text {:style styles/gray-label :key :description}]

View File

@ -1,175 +0,0 @@
(ns status-im.ui.screens.browser.events
(:require [re-frame.core :as re-frame]
[status-im.constants :as constants]
[status-im.data-store.browser :as browser-store]
[status-im.models.browser :as model]
[status-im.native-module.core :as status]
[status-im.ui.components.list-selection :as list-selection]
status-im.ui.screens.browser.navigation
[status-im.utils.handlers :as handlers]
[status-im.utils.handlers-macro :as handlers-macro]
[status-im.utils.http :as http]
[status-im.utils.platform :as platform]
[status-im.utils.random :as random]
[status-im.utils.types :as types]
[status-im.utils.universal-links.core :as utils.universal-links]
[taoensso.timbre :as log]
[status-im.utils.ethereum.resolver :as resolver]
[status-im.utils.ethereum.core :as ethereum]))
(re-frame/reg-fx
:browse
(fn [link]
(if (utils.universal-links/universal-link? link)
(utils.universal-links/open! link)
(list-selection/browse link))))
(re-frame/reg-fx
:call-rpc
(fn [[payload callback]]
(status/call-rpc
(types/clj->json payload)
(fn [response]
(if (= "" response)
(do
(log/warn :web3-response-error)
(callback "web3-response-error" nil))
(callback nil (.parse js/JSON response)))))))
(re-frame/reg-fx
:send-to-bridge-fx
(fn [[message webview]]
(.sendToBridge webview (types/clj->json message))))
(re-frame/reg-fx
:resolve-ens-multihash
(fn [{:keys [web3 registry ens-name cb]}]
(resolver/content web3 registry ens-name cb)))
(handlers/register-handler-fx
:browse-link-from-message
(fn [_ [_ link]]
{:browse link}))
(handlers/register-handler-fx
:ens-multihash-resolved
(fn [{:keys [db] :as cofx} [_ hash]]
(let [options (:browser/options db)
browsers (:browser/browsers db)
browser (get browsers (:browser-id options))
history-index (:history-index browser)]
(handlers-macro/merge-fx
cofx
{:db (assoc-in db [:browser/options :resolving?] false)}
(model/update-browser-fx
(assoc-in browser [:history history-index] (str "https://ipfs.infura.io/ipfs/" hash)))))))
(handlers/register-handler-fx
:open-url-in-browser
(fn [cofx [_ url]]
(let [normalized-url (http/normalize-and-decode-url url)
host (http/url-host normalized-url)]
(model/update-new-browser-and-navigate
host
{:browser-id (or host (random/id))
:history-index 0
:history [normalized-url]}
cofx))))
(handlers/register-handler-fx
:send-to-bridge
(fn [cofx [_ message]]
{:send-to-bridge-fx [message (get-in cofx [:db :webview-bridge])]}))
(handlers/register-handler-fx
:open-browser
(fn [cofx [_ browser]]
(model/update-browser-and-navigate browser cofx)))
(handlers/register-handler-fx
:update-browser-on-nav-change
(fn [cofx [_ browser url loading error?]]
(let [host (http/url-host url)]
(handlers-macro/merge-fx
cofx
(model/resolve-multihash-fx host loading error?)
(model/update-browser-history-fx browser url loading)))))
(handlers/register-handler-fx
:update-browser-options
(fn [{:keys [db]} [_ options]]
{:db (update db :browser/options merge options)}))
(handlers/register-handler-fx
:remove-browser
(fn [{:keys [db]} [_ browser-id]]
{:db (update-in db [:browser/browsers] dissoc browser-id)
:data-store/tx [(browser-store/remove-browser-tx browser-id)]}))
(defn nav-update-browser [cofx browser history-index]
(model/update-browser-fx (assoc browser :history-index history-index) cofx))
(handlers/register-handler-fx
:browser-nav-back
(fn [cofx [_ {:keys [history-index] :as browser}]]
(when (pos? history-index)
(nav-update-browser cofx browser (dec history-index)))))
(handlers/register-handler-fx
:browser-nav-forward
(fn [cofx [_ {:keys [history-index] :as browser}]]
(when (< history-index (dec (count (:history browser))))
(nav-update-browser cofx browser (inc history-index)))))
(handlers/register-handler-fx
:on-bridge-message
(fn [{:keys [db] :as cofx} [_ message]]
(let [{:browser/keys [options browsers]} db
{:keys [browser-id]} options
browser (get browsers browser-id)
data (types/json->clj message)
{{:keys [url]} :navState :keys [type host permissions payload messageId]} data
{:keys [dapp? name]} browser
dapp-name (if dapp? name host)]
(cond
(and (= type constants/history-state-changed) platform/ios? (not= "about:blank" url))
(model/update-browser-history-fx browser url false cofx)
(= type constants/web3-send-async)
(model/web3-send-async payload messageId cofx)
(= type constants/web3-send-async-read-only)
(model/web3-send-async-read-only dapp-name payload messageId cofx)
(= type constants/status-api-request)
{:db (update-in db [:browser/options :permissions-queue] conj {:dapp-name dapp-name
:permissions permissions})
:dispatch [:check-permissions-queue]}))))
(handlers/register-handler-fx
:check-permissions-queue
(fn [{:keys [db] :as cofx} _]
(let [{:keys [show-permission permissions-queue]} (:browser/options db)]
(when (and (nil? show-permission) (last permissions-queue))
(let [{:keys [dapp-name permissions]} (last permissions-queue)
{:account/keys [account]} db]
(handlers-macro/merge-fx
cofx
{:db (update-in db [:browser/options :permissions-queue] drop-last)}
(model/request-permission
{:dapp-name dapp-name
:index 0
:user-permissions (get-in db [:dapps/permissions dapp-name :permissions])
:requested-permissions permissions
:permissions-data {constants/dapp-permission-contact-code (:public-key account)
constants/dapp-permission-web3 (ethereum/normalized-address
(:address account))}})))))))
(handlers/register-handler-fx
:next-dapp-permission
(fn [cofx [_ params permission permissions-data]]
(model/next-permission {:params params
:permission permission
:permissions-data permissions-data}
cofx)))

View File

@ -1,6 +0,0 @@
(ns status-im.ui.screens.browser.navigation
(:require [status-im.ui.screens.navigation :as navigation]))
(defmethod navigation/preload-data! :browser
[db [_ _ options]]
(assoc db :browser/options options))

View File

@ -1,20 +1,18 @@
(ns status-im.ui.screens.browser.permissions.views
(:require-macros [status-im.utils.views :as views])
(:require [status-im.ui.components.animation :as anim]
[status-im.ui.components.react :as react]
[status-im.ui.screens.browser.styles :as styles]
[re-frame.core :as re-frame]
(:require [re-frame.core :as re-frame]
[reagent.core :as reagent]
[status-im.browser.permissions :as browser.permissions]
[status-im.i18n :as i18n]
[status-im.ui.components.animation :as anim]
[status-im.ui.components.chat-icon.screen :as chat-icon.screen]
[status-im.ui.components.common.common :as components.common]
[status-im.ui.components.icons.vector-icons :as icons]
[status-im.ui.components.colors :as colors]
[reagent.core :as reagent]
[status-im.models.browser :as model]
[status-im.ui.components.chat-icon.screen :as chat-icon.screen]))
[status-im.ui.components.react :as react]
[status-im.ui.screens.browser.styles :as styles])
(:require-macros [status-im.utils.views :as views]))
(views/defview permissions-panel [{:keys [dapp? name] :as browser} {:keys [requested-permission params]}]
(views/letsubs [dapp [:get-dapp-by-name name]
bottom-anim-value (anim/create-value -354)
(views/defview permissions-panel [{:keys [dapp? name dapp] :as browser} {:keys [requested-permission dapp-name]}]
(views/letsubs [bottom-anim-value (anim/create-value -354)
alpha-value (anim/create-value 0)
hide-panel #(anim/start
(anim/parallel
@ -27,8 +25,7 @@
(anim/timing alpha-value {:toValue 0.6
:duration 500})]))}
(let [_ (when-not requested-permission (js/setTimeout hide-panel 10))
{:keys [dapp-name]} params
{:keys [title description icon]} (get model/permissions requested-permission)]
{:keys [title description icon]} (get browser.permissions/supported-permissions requested-permission)]
[react/view styles/permissions-panel-container
[react/animated-view {:style (styles/permissions-panel-background alpha-value)}]
[react/animated-view {:style (styles/permissions-panel bottom-anim-value)}
@ -55,11 +52,10 @@
[react/text {:style styles/permissions-panel-description-label}
description]
[react/view {:flex-direction :row :margin-top 14}
[components.common/button {:on-press #(re-frame/dispatch [:next-dapp-permission params])
[components.common/button {:on-press #(re-frame/dispatch [:browser.permissions.ui/dapp-permission-denied dapp-name])
:label (i18n/label :t/deny)}]
[react/view {:width 16}]
[components.common/button {:on-press #(re-frame/dispatch [:next-dapp-permission params requested-permission
(:permissions-data params)])
[components.common/button {:on-press #(re-frame/dispatch [:browser.permissions.ui/dapp-permission-allowed dapp-name requested-permission])
:label (i18n/label :t/allow)}]]
;; TODO (andrey) will be in next PR
#_[react/view {:flex-direction :row :margin-top 19}

View File

@ -1,14 +1,12 @@
(ns status-im.ui.screens.browser.site-blocked.views
(:require-macros [status-im.utils.views :as views])
(:require [reagent.core :as reagent]
[re-frame.core :as re-frame]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.react :as react]
[status-im.ui.components.button.view :as button]
[status-im.ui.screens.browser.site-blocked.styles :as styles]
(:require [re-frame.core :as re-frame]
[status-im.i18n :as i18n]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.common.common :as components.common]
[status-im.ui.components.icons.vector-icons :as vector-icons]))
[status-im.ui.components.icons.vector-icons :as vector-icons]
[status-im.ui.components.react :as react]
[status-im.ui.screens.browser.site-blocked.styles :as styles])
(:require-macros [status-im.utils.views :as views]))
(defn chat-link []
[react/text {:on-press #(.openURL react/linking "status-im://chat/public/status")
@ -30,7 +28,7 @@
[react/view styles/buttons-container
[components.common/button {:on-press (fn []
(let [handler (if can-go-back?
:browser-nav-back
:browser.ui/previous-page-button-pressed
:navigate-back)]
(re-frame/dispatch [handler])))
:label (i18n/label :t/browsing-site-blocked-go-back)}]]]])

View File

@ -1,6 +1,6 @@
(ns status-im.ui.screens.browser.styles
(:require-macros [status-im.utils.styles :refer [defstyle defnstyle]])
(:require [status-im.ui.components.colors :as colors]))
(:require [status-im.ui.components.colors :as colors])
(:require-macros [status-im.utils.styles :refer [defnstyle defstyle]]))
(def browser {:flex 1})

View File

@ -1,14 +1,28 @@
(ns status-im.ui.screens.browser.subs
(:require [re-frame.core :as re-frame]))
(:require [re-frame.core :as re-frame]
[status-im.browser.core :as browser]))
(re-frame/reg-sub
:browsers
(fn [db _]
(:browser/browsers db)))
(re-frame/reg-sub
:browser/browsers
:<- [:browsers]
:<- [:contacts/dapps-by-name]
(fn [[browsers dapps]]
(reduce (fn [acc [k {:keys [dapp? name] :as browser}]]
(cond-> (update acc k assoc
:url (browser/get-current-url browser)
:secure? (browser/secure? browser))
dapp? (assoc-in [k :dapp] (get dapps name))))
browsers
browsers)))
(re-frame/reg-sub
:get-current-browser
:<- [:get :browser/options]
:<- [:browsers]
:<- [:browser/browsers]
(fn [[options browsers]]
(get browsers (:browser-id options))))

View File

@ -1,54 +1,43 @@
(ns status-im.ui.screens.browser.views
(:require-macros [status-im.utils.slurp :refer [slurp]]
[status-im.utils.views :as views])
(:require [clojure.string :as string]
[cljs.reader :as reader]
[reagent.core :as reagent]
(:require [cljs.reader :as reader]
[re-frame.core :as re-frame]
[reagent.core :as reagent]
[status-im.browser.core :as browser]
[status-im.i18n :as i18n]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.react :as react]
[status-im.ui.components.react :as components]
[status-im.ui.components.status-bar.view :as status-bar]
[status-im.ui.components.toolbar.view :as toolbar.view]
[status-im.ui.components.webview-bridge :as components.webview-bridge]
[status-im.ui.components.icons.vector-icons :as icons]
[status-im.ui.components.toolbar.actions :as actions]
[status-im.ui.components.tooltip.views :as tooltip]
[status-im.ui.components.react :as react]
[status-im.ui.components.status-bar.view :as status-bar]
[status-im.ui.components.styles :as components.styles]
[status-im.ui.components.toolbar.actions :as actions]
[status-im.ui.components.toolbar.view :as toolbar.view]
[status-im.ui.components.tooltip.views :as tooltip]
[status-im.ui.components.webview-bridge :as components.webview-bridge]
[status-im.ui.screens.browser.permissions.views :as permissions.views]
[status-im.ui.screens.browser.site-blocked.views :as site-blocked.views]
[status-im.ui.screens.browser.styles :as styles]
[status-im.utils.js-resources :as js-res]
[status-im.utils.ethereum.core :as ethereum]
[status-im.models.browser :as model]
[status-im.utils.http :as http]
[status-im.utils.platform :as platform]))
[status-im.utils.js-resources :as js-res])
(:require-macros
[status-im.utils.slurp :refer [slurp]]
[status-im.utils.views :as views]))
(def browser-config
(reader/read-string (slurp "./src/status_im/utils/browser_config.edn")))
(defn toolbar-content [url {:keys [dapp? history history-index] :as browser} error? url-editing?]
(let [url-text (atom url)
history-url (try (nth history history-index) (catch js/Error _))
secure? (or dapp? (and (not error?) (string/starts-with? history-url "https://")))]
(defn toolbar-content [url {:keys [secure?] :as browser} url-editing?]
(let [url-text (atom url)]
[react/view
[react/view (styles/toolbar-content false)
[react/touchable-highlight {:on-press #(re-frame/dispatch [:update-browser-options
{:show-tooltip (if secure? :secure :not-secure)}])}
[react/touchable-highlight {:on-press #(re-frame/dispatch [:browser.ui/lock-pressed secure?])}
(if secure?
[icons/icon :icons/lock {:color colors/green}]
[icons/icon :icons/lock-opened])]
(if url-editing?
[react/text-input {:on-change-text #(reset! url-text %)
:on-blur #(re-frame/dispatch [:update-browser-options {:url-editing? false}])
:on-submit-editing #(do
(re-frame/dispatch [:update-browser-options {:url-editing? false}])
(re-frame/dispatch [:update-browser-on-nav-change
browser
(http/normalize-and-decode-url @url-text)
false
false]))
:on-blur #(re-frame/dispatch [:browser.ui/url-input-blured])
:on-submit-editing #(re-frame/dispatch [:browser.ui/url-submitted @url-text])
:placeholder (i18n/label :t/enter-url)
:auto-capitalize :none
:auto-correct false
@ -56,7 +45,7 @@
:default-value url
:ellipsize :end
:style styles/url-input}]
[react/touchable-highlight {:style {:flex 1} :on-press #(re-frame/dispatch [:update-browser-options {:url-editing? true}])}
[react/touchable-highlight {:style {:flex 1} :on-press #(re-frame/dispatch [:browser.ui/url-input-pressed])}
[react/text {:style styles/url-text} (http/url-host url)]])]]))
(defn toolbar [webview error? url browser browser-id url-editing?]
@ -67,8 +56,8 @@
(.sendToBridge @webview "navigate-to-blank"))
(re-frame/dispatch [:navigate-back])
(when error?
(re-frame/dispatch [:remove-browser browser-id]))))]
[toolbar-content url browser error? url-editing?]
(re-frame/dispatch [:browser.ui/remove-browser-pressed browser-id]))))]
[toolbar-content url browser url-editing?]
[toolbar.view/actions [{:icon :icons/wallet
:icon-opts {:color :black
:accessibility-label :wallet-modal-button}
@ -83,13 +72,6 @@
[react/text {:style styles/web-view-error-text}
(str desc)]]))
(defn on-navigation-change [event browser error?]
(let [{:strs [url loading]} (js->clj event)]
(when platform/ios?
(re-frame/dispatch [:update-browser-options {:loading? loading}]))
(when (not= "about:blank" url)
(re-frame/dispatch [:update-browser-on-nav-change browser url loading error?]))))
(defn get-inject-js [url]
(when url
(let [domain-name (nth (re-find #"^\w+://(www\.)?([^/:]+)" url) 2)]
@ -97,13 +79,13 @@
(defn navigation [webview browser can-go-back? can-go-forward?]
[react/view styles/toolbar
[react/touchable-highlight {:on-press #(re-frame/dispatch [:browser-nav-back browser])
[react/touchable-highlight {:on-press #(re-frame/dispatch [:browser.ui/previous-page-button-pressed])
:disabled (not can-go-back?)
:style (when-not can-go-back? styles/disabled-button)
:accessibility-label :previou-page-button}
:accessibility-label :previous-page-button}
[react/view
[icons/icon :icons/arrow-left]]]
[react/touchable-highlight {:on-press #(re-frame/dispatch [:browser-nav-forward browser])
[react/touchable-highlight {:on-press #(re-frame/dispatch [:browser.ui/next-page-button-pressed])
:disabled (not can-go-forward?)
:style (merge styles/forward-button
(when-not can-go-forward? styles/disabled-button))
@ -115,7 +97,7 @@
[icons/icon :icons/refresh]]])
;; should-component-update is called only when component's props are changed,
;; that's why it can't be used in `brwoser`, because `url` comes from subs
;; that's why it can't be used in `browser`, because `url` comes from subs
(views/defview browser-component
[{:keys [webview error? url browser browser-id unsafe? can-go-back?
can-go-forward? url-editing? resolving? network-id address
@ -141,11 +123,10 @@
:bounces false
:local-storage-enabled true
:render-error web-view-error
:on-navigation-state-change #(on-navigation-change % browser error?)
:on-bridge-message #(re-frame/dispatch [:on-bridge-message %])
:on-load #(re-frame/dispatch [:update-browser-options {:error? false}])
:on-error #(re-frame/dispatch [:update-browser-options {:error? true
:loading? false}])
:on-navigation-state-change #(re-frame/dispatch [:browser/navigation-state-changed % error?])
:on-bridge-message #(re-frame/dispatch [:browser/bridge-message-received %])
:on-load #(re-frame/dispatch [:browser/loading-started])
:on-error #(re-frame/dispatch [:browser/error-occured])
:injected-on-start-loading-java-script (str (not opt-in?) js-res/web3
(get-inject-js url)
(if opt-in?
@ -157,7 +138,7 @@
:injected-java-script js-res/webview-js}])
(when (or loading? resolving?)
[react/view styles/web-view-loading
[components/activity-indicator {:animating true}]])]
[react/activity-indicator {:animating true}]])]
[navigation webview browser can-go-back? can-go-forward?]
[permissions.views/permissions-anim-panel browser show-permission]
(when show-tooltip
@ -165,7 +146,7 @@
(if (= show-tooltip :secure)
(i18n/label :t/browser-secure)
(i18n/label :t/browser-not-secure))
#(re-frame/dispatch [:update-browser-options {:show-tooltip nil}])])])
#(re-frame/dispatch [:browser.ui/close-tooltip-pressed])])])
(views/defview browser []
(views/letsubs [webview (atom nil)
@ -174,9 +155,9 @@
{:keys [error? loading? url-editing? show-tooltip show-permission resolving?]} [:get :browser/options]
rpc-url [:get :rpc-url]
network-id [:get-network-id]]
(let [can-go-back? (model/can-go-back? browser)
can-go-forward? (model/can-go-forward? browser)
url (model/get-current-url browser)
(let [can-go-back? (browser/can-go-back? browser)
can-go-forward? (browser/can-go-forward? browser)
url (browser/get-current-url browser)
opt-in? (:web3-opt-in? settings)]
[browser-component {:webview webview
:dapp? dapp?

View File

@ -168,7 +168,7 @@
(defn text-message
[{:keys [content timestamp-str group-chat outgoing] :as message}]
[message-view message
(let [parsed-text (cached-parse-text content :browse-link-from-message)
(let [parsed-text (cached-parse-text content :browser.ui/message-link-pressed)
ref (reagent/atom nil)
collapsible? (should-collapse? content group-chat)
collapsed? (reagent/atom collapsible?)

View File

@ -1,14 +1,16 @@
(ns status-im.ui.screens.contacts.subs
(:require [re-frame.core :refer [reg-sub subscribe]]
[status-im.utils.contacts :as utils.contacts]
[status-im.utils.ethereum.core :as ethereum]
[status-im.utils.identicon :as identicon]
[status-im.utils.contacts :as utils.contacts]))
[status-im.utils.identicon :as identicon]))
(reg-sub :get-current-contact-identity :contacts/identity)
(reg-sub :get-contacts :contacts/contacts)
(reg-sub :get-dapps :contacts/dapps)
(reg-sub :get-dapps
(fn [db]
(:contacts/dapps db)))
(reg-sub :get-current-contact
:<- [:get-contacts]
@ -70,10 +72,17 @@
(get all-contacts identity')
(utils.contacts/whisper-id->new-contact identity')))))
(reg-sub :get-dapp-by-name
:<- [:get-dapps]
(fn [dapps [_ name]]
(first (filter #(= (:name %) name) (apply concat (map :data dapps))))))
(reg-sub :contacts/dapps-by-name
:<- [:all-dapps]
(fn [dapps]
(reduce (fn [dapps-by-name category]
(merge dapps-by-name
(reduce (fn [acc {:keys [name] :as dapp}]
(assoc acc name dapp))
{}
(:data category))))
{}
dapps)))
(reg-sub :get-contact-name-by-identity
:<- [:get-contacts]

View File

@ -15,7 +15,7 @@
status-im.ui.screens.profile.db
status-im.ui.screens.network-settings.db
status-im.ui.screens.offline-messaging-settings.db
status-im.ui.screens.browser.db
status-im.browser.db
status-im.ui.screens.add-new.db
status-im.ui.screens.add-new.new-public-chat.db))

View File

@ -2,13 +2,10 @@
(:require status-im.events
status-im.chat.events
status-im.dev-server.events
[status-im.models.contacts :as models.contacts]
status-im.ui.screens.add-new.events
status-im.ui.screens.add-new.new-chat.events
status-im.ui.screens.group.chat-settings.events
status-im.ui.screens.group.events
[status-im.ui.screens.navigation :as navigation]
[status-im.utils.dimensions :as dimensions]
status-im.utils.universal-links.events
status-im.web3.events
status-im.ui.screens.add-new.new-chat.navigation
@ -25,21 +22,17 @@
status-im.ui.screens.wallet.collectibles.cryptostrikers.events
status-im.ui.screens.wallet.collectibles.etheremon.events
status-im.ui.screens.wallet.collectibles.superrare.events
status-im.ui.screens.browser.events
status-im.utils.keychain.events
[re-frame.core :as re-frame]
[status-im.hardwallet.core :as hardwallet]
[status-im.native-module.core :as status]
[status-im.ui.components.permissions :as permissions]
[status-im.transport.core :as transport]
[status-im.transport.inbox :as inbox]
[status-im.ui.screens.db :refer [app-db]]
[status-im.utils.datetime :as time]
[status-im.utils.random :as random]
[status-im.ui.components.permissions :as permissions]
[status-im.utils.dimensions :as dimensions]
[status-im.utils.handlers :as handlers]
[status-im.utils.handlers-macro :as handlers-macro]
[status-im.utils.http :as http]
[status-im.utils.utils :as utils]
[status-im.hardwallet.core :as hardwallet]))
[status-im.utils.utils :as utils]))
(defn- http-get [{:keys [url response-validator success-event-creator failure-event-creator timeout-ms]}]
(let [on-success #(re-frame/dispatch (success-event-creator %))

View File

@ -22,7 +22,7 @@
[toolbar/actions [{:icon :icons/info
:icon-opts {:color :black
:accessibility-label :hardwallet-connect-info-button}
:handler #(re-frame/dispatch [:open-url-in-browser "https://hardwallet.status.im"])}]]]
:handler #(re-frame/dispatch [:hardwallet.ui/connect-info-button-pressed])}]]]
[react/view styles/hardwallet-connect
[react/view styles/hardwallet-card-image-container
[react/image {:source (:hardwallet-card resources/ui)

View File

@ -3,6 +3,6 @@
(re-frame/reg-sub :home-items
:<- [:get-active-chats]
:<- [:browsers]
:<- [:browser/browsers]
(fn [[chats browsers]]
(sort-by #(-> % second :timestamp) > (merge chats browsers))))

View File

@ -39,7 +39,9 @@
(views/defview home-list-item [[home-item-id home-item]]
(views/letsubs [swiped? [:delete-swipe-position home-item-id]]
(let [delete-action (if (:chat-id home-item) :remove-chat-and-navigate-home :remove-browser)
(let [delete-action (if (:chat-id home-item)
:remove-chat-and-navigate-home
:browser.ui/remove-browser-pressed)
inner-item-view (if (:chat-id home-item)
inner-item/home-list-chat-item-inner-view
inner-item/home-list-browser-item-inner-view)

View File

@ -18,7 +18,7 @@
[status-im.ui.components.icons.vector-icons :as vector-icons]
[status-im.ui.components.chat-icon.screen :as chat-icon.screen]
[status-im.ui.components.common.common :as components.common]
[status-im.models.browser :as model]))
[status-im.browser.core :as browser]))
(defview command-short-preview [message]
(letsubs [id->command [:get-id->command]]
@ -105,27 +105,25 @@
[message-content-text last-message]
[unviewed-indicator chat-id]]]]])))
(defview home-list-browser-item-inner-view [{:keys [name] :as browser}]
(letsubs [dapp [:get-dapp-by-name name]
url (model/get-current-url browser)]
[react/touchable-highlight {:on-press #(re-frame/dispatch [:open-browser browser])}
[react/view styles/chat-container
[react/view styles/chat-icon-container
(if dapp
[chat-icon.screen/dapp-icon-browser dapp 36]
[react/view styles/browser-icon-container
[vector-icons/icon :icons/discover {:color component.styles/color-light-gray6}]])]
[react/view styles/chat-info-container
[react/view styles/item-upper-container
[react/view styles/name-view
[react/view {:flex-shrink 1}
[react/text {:style styles/name-text
:accessibility-label :chat-name-text
:number-of-lines 1}
name]]]]
[react/view styles/item-lower-container
[react/view styles/last-message-container
[react/text {:style styles/last-message-text
:accessibility-label :chat-url-text
:number-of-lines 1}
(or url (i18n/label :t/dapp))]]]]]]))
(defn home-list-browser-item-inner-view [{:keys [dapp url name browser-id] :as browser}]
[react/touchable-highlight {:on-press #(re-frame/dispatch [:browser.ui/browser-item-selected browser-id])}
[react/view styles/chat-container
[react/view styles/chat-icon-container
(if dapp
[chat-icon.screen/dapp-icon-browser dapp 36]
[react/view styles/browser-icon-container
[vector-icons/icon :icons/discover {:color component.styles/color-light-gray6}]])]
[react/view styles/chat-info-container
[react/view styles/item-upper-container
[react/view styles/name-view
[react/view {:flex-shrink 1}
[react/text {:style styles/name-text
:accessibility-label :chat-name-text
:number-of-lines 1}
name]]]]
[react/view styles/item-lower-container
[react/view styles/last-message-container
[react/text {:style styles/last-message-text
:accessibility-label :chat-url-text
:number-of-lines 1}
(or url (i18n/label :t/dapp))]]]]]])

View File

@ -3,7 +3,8 @@
[status-im.utils.handlers :as handlers]
[status-im.utils.ethereum.erc721 :as erc721]
[status-im.utils.ethereum.tokens :as tokens]
[status-im.utils.money :as money]))
[status-im.utils.money :as money]
[status-im.browser.core :as browser]))
(defmulti load-collectible-fx (fn [symbol _] symbol))
@ -64,5 +65,5 @@
(handlers/register-handler-fx
:open-collectible-in-browser
(fn [_ [_ data]]
{:dispatch [:open-url-in-browser data]}))
(fn [cofx [_ url]]
(browser/open-url url cofx)))

View File

@ -39,7 +39,7 @@
(defn handle-browse [url cofx]
(log/info "universal-links: handling browse " url)
{:browse url})
{:browser/show-browser-selection url})
(defn handle-public-chat [public-chat cofx]
(log/info "universal-links: handling public chat " public-chat)

View File

@ -0,0 +1,127 @@
(ns status-im.test.browser.core
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.browser.core :as browser]
[status-im.utils.types :as types]
[status-im.utils.handlers-macro :as handlers-macro]))
(defn has-navigated-to-browser? [result]
(and (= (get result :status-im.ui.screens.navigation/navigate-to)
:browser)
(= (get-in result [:db :view-id])
:browser)))
(defn has-wrong-properties?
[result dapp-url expected-browser]
(let [browser (get-in result [:db :browser/browsers dapp-url])]
(reduce (fn [acc k]
(if (= (k browser)
(k expected-browser))
acc
(conj acc [k (str "was expecting " (k expected-browser) " got " (k browser))])))
nil
(keys expected-browser))))
(deftest browser-test
(let [dapp1-url "cryptokitties.co"
dapp2-url "http://test2.com"]
(testing "user opens a dapp"
(let [result-open (browser/open-url dapp1-url {:now 1})]
(is (= dapp1-url (get-in result-open [:db :browser/options :browser-id]))
"browser-id should be dapp1-url")
(is (has-navigated-to-browser? result-open)
"should navigate to :browser")
(is (not (has-wrong-properties? result-open
dapp1-url
{:browser-id "cryptokitties.co"
:history-index 0
:history ["http://cryptokitties.co"]
:dapp? true
:name "CryptoKitties"
:timestamp 1}))
"some properties of the browser are not correct")
(testing "then a second dapp"
(let [result-open-2 (browser/open-url dapp2-url {:db (:db result-open)
:now 2})
dapp2-host "test2.com"]
(is (= dapp2-host (get-in result-open-2 [:db :browser/options :browser-id]))
"browser-id should be dapp2 host")
(is (has-navigated-to-browser? result-open-2)
"should navigate to :browser")
(is (not (has-wrong-properties? result-open-2
dapp2-host
{:browser-id "test2.com"
:history-index 0
:history ["http://test2.com"]
:dapp? false
:timestamp 2}))
"some properties of the browser are not correct")
(testing "then removes the second dapp"
(let [result-remove-2 (browser/remove-browser dapp2-host {:db (:db result-open-2)})]
(is (= #{dapp1-url}
(set (keys (get-in result-remove-2 [:db :browser/browsers]))))
"the second dapp shouldn't be in the browser list anymore")))))
(testing "then opens the dapp again"
(let [result-open-existing (browser/open-existing-browser dapp1-url {:db (:db result-open)
:now 2})
dapp1-url2 (str "http://" dapp1-url "/nav2")
browser (get-in result-open-existing [:db :browser/browsers dapp1-url])]
(is (not (has-wrong-properties? result-open-existing
dapp1-url
{:browser-id "cryptokitties.co"
:history-index 0
:history ["http://cryptokitties.co"]
:dapp? true
:name "CryptoKitties"
:timestamp 2}))
"some properties of the browser are not correct")
(is (nil? (browser/navigate-to-next-page result-open-existing))
"nothing should happen if user tries to navigate to next page")
(is (nil? (browser/navigate-to-previous-page result-open-existing))
"nothing should happen if user tries to navigate to previous page")
(testing "then navigates to a new url in the dapp"
(let [result-navigate (browser/navigation-state-changed
(clj->js {"url" dapp1-url2
"loading" false})
false
{:db (:db result-open-existing)
:now 4})]
(is (not (has-wrong-properties? result-navigate
dapp1-url
{:browser-id "cryptokitties.co"
:history-index 1
:history ["http://cryptokitties.co" dapp1-url2]
:dapp? true
:name "CryptoKitties"
:timestamp 4}))
"some properties of the browser are not correct")
(testing "then navigates to previous page"
(let [result-previous (browser/navigate-to-previous-page {:db (:db result-navigate)
:now 5})]
(is (not (has-wrong-properties? result-previous
dapp1-url
{:browser-id "cryptokitties.co"
:history-index 0
:history ["http://cryptokitties.co" dapp1-url2]
:dapp? true
:name "CryptoKitties"
:timestamp 5}))
"some properties of the browser are not correct")
(testing "then navigates to next page")
(let [result-next (browser/navigate-to-next-page {:db (:db result-previous)
:now 6})]
(is (not (has-wrong-properties? result-next
dapp1-url
{:browser-id "cryptokitties.co"
:history-index 1
:history ["http://cryptokitties.co" dapp1-url2]
:dapp? true
:name "CryptoKitties"
:timestamp 6}))
"some properties of the browser are not correct"))))))))))))

View File

@ -1,213 +0,0 @@
(ns status-im.test.browser.events
(:require [cljs.test :refer-macros [deftest is testing]]
[day8.re-frame.test :refer-macros [run-test-sync]]
[status-im.init.core :as init]
status-im.ui.screens.db
status-im.ui.screens.subs
[re-frame.core :as re-frame]
[status-im.models.browser :as model]
[status-im.utils.types :as types]
[status-im.utils.handlers :as handlers]
[status-im.utils.handlers-macro :as handlers-macro]
[status-im.models.browser :as browser]))
(defn test-fixtures []
(re-frame/reg-fx :init/init-store #())
(re-frame/reg-fx :browse #())
(re-frame/reg-fx :data-store/tx #())
(re-frame/reg-cofx
:data-store/all-browsers
(fn [coeffects _]
(assoc coeffects :all-stored-browsers [])))
(re-frame/reg-cofx
:data-store/all-dapp-permissions
(fn [coeffects _]
(assoc coeffects :all-dapp-permissions [])))
(re-frame/reg-fx :send-to-bridge-fx #())
(re-frame/reg-fx
:show-dapp-permission-confirmation-fx
(fn [[permission {:keys [dapp-name permissions-data index] :as params}]]
(if (and (= dapp-name "test.com") (#{0 1} index))
(re-frame/dispatch [:next-dapp-permission params permission permissions-data])
(re-frame/dispatch [:next-dapp-permission params]))))
(handlers/register-handler-fx
[(re-frame/inject-cofx :data-store/all-browsers)
(re-frame/inject-cofx :data-store/all-dapp-permissions)]
:initialize-test
(fn [cofx [_]]
(handlers-macro/merge-fx cofx
(init/initialize-app-db)
(browser/initialize-browsers)
(browser/initialize-dapp-permissions)))))
(deftest browser-events
(run-test-sync
(test-fixtures)
(re-frame/dispatch [:initialize-test])
(let [browsers (re-frame/subscribe [:browsers])
dapp1-url "cryptokitties.co"
dapp2-url "http://test2.com"]
(testing "open and remove dapps"
(is (zero? (count @browsers)))
(re-frame/dispatch [:open-url-in-browser dapp1-url])
(is (= 1 (count @browsers)))
(re-frame/dispatch [:open-url-in-browser dapp2-url])
(is (= 2 (count @browsers)))
(let [browser1 (first (vals @browsers))
browser2 (second (vals @browsers))]
(is (and (:dapp? browser1)
(not (:dapp? browser2))))
(is (and (zero? (:history-index browser1))
(zero? (:history-index browser2))))
(is (and (= [(str "http://" dapp1-url) (:history browser1)])
(= [dapp2-url] (:history browser2)))))
(re-frame/dispatch [:remove-browser dapp1-url])
(is (= 1 (count @browsers))))
(testing "navigate dapp"
(re-frame/dispatch [:open-browser (first (vals @browsers))])
(let [browser (re-frame/subscribe [:get-current-browser])
dapp2-url2 (str dapp2-url "/nav2")
dapp2-url3 (str dapp2-url "/nav3")]
(is (zero? (:history-index @browser)))
(is (= [dapp2-url] (:history @browser)))
(is (and (not (model/can-go-back? @browser))
(not (model/can-go-forward? @browser))))
(re-frame/dispatch [:browser-nav-back])
(re-frame/dispatch [:browser-nav-forward])
(re-frame/dispatch [:update-browser-on-nav-change @browser dapp2-url2 false])
(is (= 1 (:history-index @browser)))
(is (= [dapp2-url dapp2-url2] (:history @browser)))
(is (and (model/can-go-back? @browser)
(not (model/can-go-forward? @browser))))
(re-frame/dispatch [:browser-nav-back @browser])
(is (zero? (:history-index @browser)))
(is (= [dapp2-url dapp2-url2] (:history @browser)))
(is (and (not (model/can-go-back? @browser))
(model/can-go-forward? @browser)))
(re-frame/dispatch [:update-browser-on-nav-change @browser dapp2-url3 false])
(is (= 1 (:history-index @browser)))
(is (= [dapp2-url dapp2-url3] (:history @browser)))
(re-frame/dispatch [:browser-nav-back @browser])
(is (zero? (:history-index @browser)))
(is (= [dapp2-url dapp2-url3] (:history @browser)))
(re-frame/dispatch [:browser-nav-forward @browser])
(is (= 1 (:history-index @browser)))
(is (= [dapp2-url dapp2-url3] (:history @browser))))))
(let [dapps-permissions (re-frame/subscribe [:get :dapps/permissions])
dapp-name "test.com"
dapp-name2 "test2.org"]
(testing "dapps permissions"
(is (zero? (count @dapps-permissions)))
(re-frame/dispatch [:on-bridge-message (types/clj->json {:type "status-api-request"
:host dapp-name
:permissions ["FAKE_PERMISSION"]})
nil nil])
(re-frame/dispatch [:next-dapp-permission
{:dapp-name dapp-name
:index 0
:requested-permissions ["FAKE_PERMISSION"]
:permissions-data "Data"}])
(is (= {:dapp dapp-name
:permissions []}
(get @dapps-permissions dapp-name)))
(re-frame/dispatch [:on-bridge-message (types/clj->json {:type "status-api-request"
:host dapp-name
:permissions ["CONTACT_CODE"]})
nil nil])
(re-frame/dispatch [:next-dapp-permission
{:dapp-name dapp-name
:index 0
:requested-permissions ["CONTACT_CODE"]
:permissions-data {"CONTACT_CODE" "Data"}}
"CONTACT_CODE"
{"CONTACT_CODE" "Data"}])
(is (= 1 (count @dapps-permissions)))
(is (= {:dapp dapp-name
:permissions ["CONTACT_CODE"]}
(get @dapps-permissions dapp-name)))
(re-frame/dispatch [:on-bridge-message (types/clj->json {:type "status-api-request"
:host dapp-name
:permissions ["CONTACT_CODE" "FAKE_PERMISSION"]})
nil nil])
(is (= 1 (count @dapps-permissions)))
(is (= {:dapp dapp-name
:permissions ["CONTACT_CODE"]}
(get @dapps-permissions dapp-name)))
(re-frame/dispatch [:on-bridge-message (types/clj->json {:type "status-api-request"
:host dapp-name
:permissions ["FAKE_PERMISSION"]})
nil nil])
(is (= 1 (count @dapps-permissions)))
(is (= {:dapp dapp-name
:permissions ["CONTACT_CODE"]}
(get @dapps-permissions dapp-name)))
(re-frame/dispatch [:on-bridge-message (types/clj->json {:type "status-api-request"
:host dapp-name2
:permissions ["CONTACT_CODE"]})
nil nil])
(re-frame/dispatch [:next-dapp-permission
{:dapp-name dapp-name2
:index 0
:requested-permissions ["CONTACT_CODE" "FAKE_PERMISSION"]
:permissions-data "Data"}])
(is (= 2 (count @dapps-permissions)))
(is (= {:dapp dapp-name2
:permissions []}
(get @dapps-permissions dapp-name2)))))))

View File

@ -0,0 +1,87 @@
(ns status-im.test.browser.permissions
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.browser.permissions :as permissions]
[status-im.utils.types :as types]
[status-im.utils.handlers-macro :as handlers-macro]
[status-im.browser.core :as browser]))
(deftest permissions-test
(let [dapp-name "test.com"
dapp-name2 "test2.org"
cofx {:db (assoc-in (:db (browser/open-url dapp-name {}))
[:account/account :public-key] "public-key")}]
(testing "dapps permissions are initialized"
(is (zero? (count (get-in cofx [:db :dapps/permissions]))))
(is (= dapp-name (get-in cofx [:db :browser/options :browser-id]))))
(testing "receiving an unsupported permission"
(is (nil? (:browser/send-to-bridge (browser/process-bridge-message (types/clj->json {:type "status-api-request"
:host dapp-name
:permissions ["FAKE_PERMISSION"]})
cofx)))
"nothing should happen"))
(testing "receiving a supported permission and an unsupported one"
(let [result-ask (browser/process-bridge-message (types/clj->json {:type "status-api-request"
:host dapp-name
:permissions ["CONTACT_CODE" "FAKE_PERMISSION"]})
cofx)]
(is (= (get-in result-ask [:db :browser/options :show-permission])
{:requested-permission "CONTACT_CODE", :dapp-name "test.com"}))
(is (zero? (count (get-in result-ask [:db :dapps/permissions]))))
(testing "then user accepts the supported permission"
(let [accept-result (permissions/allow-permission dapp-name "CONTACT_CODE" {:db (:db result-ask)})]
(is (= (get-in accept-result [:browser/send-to-bridge :message])
{:type "status-api-success"
:data {"CONTACT_CODE" "public-key"}
:keys ["CONTACT_CODE"]})
"the data should have been sent to the bridge")
(is (= (get-in accept-result [:db :dapps/permissions])
{"test.com" {:dapp "test.com", :permissions ["CONTACT_CODE"]}})
"the dapp should now have CONTACT_CODE permission")
(testing "then dapps asks for permission again"
(let [result-ask-again (browser/process-bridge-message (types/clj->json {:type "status-api-request"
:host dapp-name
:permissions ["CONTACT_CODE"]})
{:db (:db accept-result)})]
(is (= (get-in result-ask-again
[:browser/send-to-bridge :message])
{:type "status-api-success"
:data {"CONTACT_CODE" "public-key"}
:keys ["CONTACT_CODE"]})
"the response should be immediatly sent to the bridge")))
(testing "then user switch to another dapp that asks for permissions"
(let [new-dapp (browser/open-url dapp-name2 {:db (:db accept-result)})
result-ask2 (browser/process-bridge-message (types/clj->json {:type "status-api-request"
:host dapp-name2
:permissions ["CONTACT_CODE" "FAKE_PERMISSION"]})
{:db (:db new-dapp)})]
(is (= (get-in result-ask2 [:db :dapps/permissions])
{"test.com" {:dapp "test.com", :permissions ["CONTACT_CODE"]}})
"there should only be permissions for dapp-name at that point")
(is (nil? (get-in result-ask2
[:browser/send-to-bridge :message]))
"no message should be sent to the bridge")
(testing "then user accepts permission for dapp-name2"
(let [accept-result2 (permissions/allow-permission dapp-name2 "CONTACT_CODE" {:db (:db result-ask2)})]
(is (= (get-in accept-result2 [:db :dapps/permissions])
{"test.com" {:dapp "test.com" :permissions ["CONTACT_CODE"]}
"test2.org" {:dapp "test2.org" :permissions ["CONTACT_CODE"]}})
"there should be permissions for both dapps now")
(is (= (get-in accept-result2
[:browser/send-to-bridge :message])
{:type "status-api-success"
:data {"CONTACT_CODE" "public-key"}
:keys ["CONTACT_CODE"]})
"the response should be sent to the bridge")))))
(testing "then user refuses the permission"
(let [result-refuse (permissions/process-next-permission dapp-name {:db (:db result-ask)})]
(is (zero? (count (get-in result-refuse [:db :dapps/permissions])))
"no permissions should be granted")
(is (nil? (get-in result-refuse [:browser/send-to-bridge :message]))
"no message should be sent to bridge")))))))))

View File

@ -4,7 +4,8 @@
[status-im.test.contacts.events]
[status-im.test.contacts.subs]
[status-im.test.data-store.realm.core]
[status-im.test.browser.events]
[status-im.test.browser.core]
[status-im.test.browser.permissions]
[status-im.test.wallet.subs]
[status-im.test.wallet.transactions.subs]
[status-im.test.wallet.transactions.views]
@ -114,4 +115,5 @@
'status-im.test.accounts.recover.core
'status-im.test.ui.screens.currency-settings.models
'status-im.test.ui.screens.wallet.db
'status-im.test.browser.events)
'status-im.test.browser.core
'status-im.test.browser.permissions)

View File

@ -12,7 +12,7 @@
(is (= :my-profile
(get-in (models/handle-qr-code "0x04e1433c1a8ad71280e6d4b1814aa3958ba6eb451da47ea1d4a4bfc4a04969c445548f3bd9d40fa7e4356aa62075b4d7615179ef1332f1d6a7c59b96c4ab8e04c1" cofx) [:db :view-id]))))
(testing "handle universal link"
(is (= (:browse (models/handle-qr-code "status-im://browse/www.cryptokitties.co" cofx))
(is (= (:browser/show-browser-selection (models/handle-qr-code "status-im://browse/www.cryptokitties.co" cofx))
"status-im://browse/www.cryptokitties.co")))
(testing "handle invalid qr code"
(is (:utils/show-popup (models/handle-qr-code "a random string" cofx)))))

View File

@ -25,8 +25,8 @@
(testing "it open the dapps"
(is
(= "status-im://browse/www.cryptokitties.co"
(:browse (links/handle-url "status-im://browse/www.cryptokitties.co"
{:db db}))))))
(:browser/show-browser-selection (links/handle-url "status-im://browse/www.cryptokitties.co"
{:db db}))))))
(testing "a user profile link"
(testing "it loads the profile"
(let [actual (links/handle-url "status-im://user/0x04fbce10971e1cd7253b98c7b7e54de3729ca57ce41a2bfb0d1c4e0a26f72c4b6913c3487fa1b4bb86125770f1743fb4459da05c1cbe31d938814cfaf36e252073"