[#5260] Implement web3 provider Opt-in access

Signed-off-by: Andrey Shovkoplyas <motor4ik@gmail.com>
This commit is contained in:
Andrey Shovkoplyas 2018-08-14 15:04:22 +03:00
parent d354da6cb9
commit 42abd16e9a
No known key found for this signature in database
GPG Key ID: EAAB7C8622D860A4
14 changed files with 330 additions and 77 deletions

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill="" fill-rule="evenodd" clip-rule="evenodd" d="M10.7803 3C10.9365 3.00012 11.0809 3.07953 11.159 3.20831L13.9414 7.79169C14.0196 7.92059 14.0196 8.07941 13.9414 8.20831L11.159 12.7917C11.0809 12.9205 10.9365 12.9999 10.7803 13H5.21978C5.06353 12.9999 4.91922 12.9205 4.84106 12.7917L2.05865 8.20831C1.98045 8.07941 1.98045 7.92059 2.05865 7.79169L4.84106 3.20831C4.91922 3.07953 5.06353 3.00012 5.21978 3H10.7803ZM6 8C6 9.10455 6.89541 10 8 10C8.53042 10 9.03915 9.78931 9.41421 9.41418C9.78927 9.03912 10 8.53046 10 8C10 6.89545 9.10458 6 8 6C6.89541 6 6 6.89545 6 8Z"/>
</svg>

After

Width:  |  Height:  |  Size: 689 B

View File

@ -9,10 +9,17 @@ WebViewBridge.onMessage = function (message) {
window.location.href = "about:blank"; window.location.href = "about:blank";
else if (data.type === "status-api-success") else if (data.type === "status-api-success")
{
if (data.keys == 'WEB3')
{
window.dispatchEvent(new CustomEvent('ethereumprovider', { detail: { ethereum: new StatusHttpProvider("")} }));
}
else
{ {
window.STATUS_API = data.data; window.STATUS_API = data.data;
window.postMessage({ type: 'STATUS_API_SUCCESS', permissions: data.keys }, "*"); window.postMessage({ type: 'STATUS_API_SUCCESS', permissions: data.keys }, "*");
} }
}
else if (data.type === "web3-send-async-callback") else if (data.type === "web3-send-async-callback")
{ {

View File

@ -24,5 +24,12 @@
host: window.location.hostname host: window.location.hostname
}); });
} }
else if (event.data.type === 'ETHEREUM_PROVIDER_REQUEST') {
bridgeSend({
type: 'status-api-request',
permissions: ['WEB3'],
host: window.location.hostname
});
}
}); });
}()); }());

View File

@ -246,6 +246,7 @@
(def regx-emoji #"^((?:[\u261D\u26F9\u270A-\u270D]|\uD83C[\uDF85\uDFC2-\uDFC4\uDFC7\uDFCA-\uDFCC]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66-\uDC69\uDC6E\uDC70-\uDC78\uDC7C\uDC81-\uDC83\uDC85-\uDC87\uDCAA\uDD74\uDD75\uDD7A\uDD90\uDD95\uDD96\uDE45-\uDE47\uDE4B-\uDE4F\uDEA3\uDEB4-\uDEB6\uDEC0\uDECC]|\uD83E[\uDD18-\uDD1C\uDD1E\uDD1F\uDD26\uDD30-\uDD39\uDD3D\uDD3E\uDDD1-\uDDDD])(?:\uD83C[\uDFFB-\uDFFF])?|(?:[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u270A\u270B\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF93\uDFA0-\uDFCA\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF4\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC3E\uDC40\uDC42-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDD7A\uDD95\uDD96\uDDA4\uDDFB-\uDE4F\uDE80-\uDEC5\uDECC\uDED0-\uDED2\uDEEB\uDEEC\uDEF4-\uDEF8]|\uD83E[\uDD10-\uDD3A\uDD3C-\uDD3E\uDD40-\uDD45\uDD47-\uDD4C\uDD50-\uDD6B\uDD80-\uDD97\uDDC0\uDDD0-\uDDE6])|(?:[#\*0-9\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u2660\u2663\u2665\u2666\u2668\u267B\u267F\u2692-\u2697\u2699\u269B\u269C\u26A0\u26A1\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299]|\uD83C[\uDC04\uDCCF\uDD70\uDD71\uDD7E\uDD7F\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE02\uDE1A\uDE2F\uDE32-\uDE3A\uDE50\uDE51\uDF00-\uDF21\uDF24-\uDF93\uDF96\uDF97\uDF99-\uDF9B\uDF9E-\uDFF0\uDFF3-\uDFF5\uDFF7-\uDFFF]|\uD83D[\uDC00-\uDCFD\uDCFF-\uDD3D\uDD49-\uDD4E\uDD50-\uDD67\uDD6F\uDD70\uDD73-\uDD7A\uDD87\uDD8A-\uDD8D\uDD90\uDD95\uDD96\uDDA4\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA-\uDE4F\uDE80-\uDEC5\uDECB-\uDED2\uDEE0-\uDEE5\uDEE9\uDEEB\uDEEC\uDEF0\uDEF3-\uDEF8]|\uD83E[\uDD10-\uDD3A\uDD3C-\uDD3E\uDD40-\uDD45\uDD47-\uDD4C\uDD50-\uDD6B\uDD80-\uDD97\uDDC0\uDDD0-\uDDE6])\uFE0F|[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF])+$") (def regx-emoji #"^((?:[\u261D\u26F9\u270A-\u270D]|\uD83C[\uDF85\uDFC2-\uDFC4\uDFC7\uDFCA-\uDFCC]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66-\uDC69\uDC6E\uDC70-\uDC78\uDC7C\uDC81-\uDC83\uDC85-\uDC87\uDCAA\uDD74\uDD75\uDD7A\uDD90\uDD95\uDD96\uDE45-\uDE47\uDE4B-\uDE4F\uDEA3\uDEB4-\uDEB6\uDEC0\uDECC]|\uD83E[\uDD18-\uDD1C\uDD1E\uDD1F\uDD26\uDD30-\uDD39\uDD3D\uDD3E\uDDD1-\uDDDD])(?:\uD83C[\uDFFB-\uDFFF])?|(?:[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u270A\u270B\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF93\uDFA0-\uDFCA\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF4\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC3E\uDC40\uDC42-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDD7A\uDD95\uDD96\uDDA4\uDDFB-\uDE4F\uDE80-\uDEC5\uDECC\uDED0-\uDED2\uDEEB\uDEEC\uDEF4-\uDEF8]|\uD83E[\uDD10-\uDD3A\uDD3C-\uDD3E\uDD40-\uDD45\uDD47-\uDD4C\uDD50-\uDD6B\uDD80-\uDD97\uDDC0\uDDD0-\uDDE6])|(?:[#\*0-9\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u2660\u2663\u2665\u2666\u2668\u267B\u267F\u2692-\u2697\u2699\u269B\u269C\u26A0\u26A1\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299]|\uD83C[\uDC04\uDCCF\uDD70\uDD71\uDD7E\uDD7F\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE02\uDE1A\uDE2F\uDE32-\uDE3A\uDE50\uDE51\uDF00-\uDF21\uDF24-\uDF93\uDF96\uDF97\uDF99-\uDF9B\uDF9E-\uDFF0\uDFF3-\uDFF5\uDFF7-\uDFFF]|\uD83D[\uDC00-\uDCFD\uDCFF-\uDD3D\uDD49-\uDD4E\uDD50-\uDD67\uDD6F\uDD70\uDD73-\uDD7A\uDD87\uDD8A-\uDD8D\uDD90\uDD95\uDD96\uDDA4\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA-\uDE4F\uDE80-\uDEC5\uDECB-\uDED2\uDEE0-\uDEE5\uDEE9\uDEEB\uDEEC\uDEF0\uDEF3-\uDEF8]|\uD83E[\uDD10-\uDD3A\uDD3C-\uDD3E\uDD40-\uDD45\uDD47-\uDD4C\uDD50-\uDD6B\uDD80-\uDD97\uDDC0\uDDD0-\uDDE6])\uFE0F|[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF])+$")
(def ^:const dapp-permission-contact-code "CONTACT_CODE") (def ^:const dapp-permission-contact-code "CONTACT_CODE")
(def ^:const dapp-permission-web3 "WEB3")
(def ^:const status-api-success "status-api-success") (def ^:const status-api-success "status-api-success")
(def ^:const status-api-request "status-api-request") (def ^:const status-api-request "status-api-request")
(def ^:const history-state-changed "history-state-changed") (def ^:const history-state-changed "history-state-changed")

View File

@ -50,16 +50,23 @@
(merge (update-browser-fx cofx browser) (merge (update-browser-fx cofx browser)
{:dispatch [:navigate-to :browser (:browser-id browser)]})) {:dispatch [:navigate-to :browser (:browser-id browser)]}))
(def permissions {constants/dapp-permission-contact-code {:label (i18n/label :t/your-contact-code)}}) (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] (defn update-dapp-permissions-fx [{:keys [db]} permissions]
{:db (assoc-in db [:dapps/permissions (:dapp permissions)] 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)]}) :data-store/tx [(dapp-permissions/save-dapp-permissions permissions)]})
(defn request-permission [cofx (defn request-permission [{:keys [dapp-name index requested-permissions permissions-allowed user-permissions
{:keys [dapp-name index requested-permissions permissions-allowed user-permissions permissions-data]
permissions-data webview] :as params}
:as params}] {:keys [db] :as cofx}]
;; iterate all requested permissions ;; iterate all requested permissions
(if (< index (count requested-permissions)) (if (< index (count requested-permissions))
(let [requested-permission (get requested-permissions index)] (let [requested-permission (get requested-permissions index)]
@ -68,7 +75,8 @@
;; if permission already allowed go to next, if not, show confirmation dialog ;; if permission already allowed go to next, if not, show confirmation dialog
(if ((set user-permissions) requested-permission) (if ((set user-permissions) requested-permission)
{:dispatch [:next-dapp-permission params requested-permission permissions-data]} {:dispatch [:next-dapp-permission params requested-permission permissions-data]}
{:show-dapp-permission-confirmation-fx [requested-permission params]}) {:db (assoc-in db [:browser/options :show-permission] {:requested-permission requested-permission
:params params})})
{:dispatch [:next-dapp-permission params]})) {:dispatch [:next-dapp-permission params]}))
(assoc (update-dapp-permissions-fx cofx {:dapp dapp-name (assoc (update-dapp-permissions-fx cofx {:dapp dapp-name
:permissions (vec (set (concat (keys permissions-allowed) :permissions (vec (set (concat (keys permissions-allowed)
@ -76,19 +84,20 @@
:send-to-bridge-fx [{:type constants/status-api-success :send-to-bridge-fx [{:type constants/status-api-success
:data permissions-allowed :data permissions-allowed
:keys (keys permissions-allowed)} :keys (keys permissions-allowed)}
webview]))) (:webview-bridge db)]
:dispatch [:check-permissions-queue])))
(defn next-permission [cofx params & [permission permissions-data]] (defn next-permission [{:keys [params permission permissions-data]} cofx]
(request-permission (request-permission
cofx
(cond-> params (cond-> params
true true
(update :index inc) (update :index inc)
(and permission permissions-data) (and permission permissions-data)
(assoc-in [:permissions-allowed permission] (get permissions-data permission))))) (assoc-in [:permissions-allowed permission] (get permissions-data permission)))
cofx))
(defn web3-send-async [{:keys [db]} {:keys [method] :as payload} message-id] (defn web3-send-async [{:keys [method] :as payload} message-id {:keys [db]}]
(if (or (= method constants/web3-send-transaction) (if (or (= method constants/web3-send-transaction)
(= method constants/web3-personal-sign)) (= method constants/web3-personal-sign))
{:db (update-in db [:wallet :transactions-queue] conj {:message-id message-id :payload payload}) {:db (update-in db [:wallet :transactions-queue] conj {:message-id message-id :payload payload})

View File

@ -720,5 +720,10 @@
:browser-secure "Connection is secure. Make sure you really trust this site before signing transactions or entering personal data." :browser-secure "Connection is secure. Make sure you really trust this site before signing transactions or entering personal data."
:browser-not-secure "Connection is not secure! Do not sign transactions or send personal data on this site." :browser-not-secure "Connection is not secure! Do not sign transactions or send personal data on this site."
:make-sure-you-trust-dapp "Make sure that you trust this DApp" :make-sure-you-trust-dapp "Make sure that you trust this DApp"
:would-like-to-access "Would like to Access" :wants-to-access-profile "wants to access to your profile"
:your-contact-code "Your Contact Code"}) :your-contact-code "Granting access authorizes this DApp to retrieve your contact code"
:dapp-would-like-to-connect-wallet "would like\n to connect to your wallet"
:allowing-authorizes-this-dapp "Allowing authorizes this DApp to retrieve your wallet address and enable Web3"
:manage-permissions "Manage permissions"
:deny "Deny"
:allow "Allow"})

View File

@ -149,6 +149,18 @@
:default-chat-icon (styles/default-chat-icon-chat-list components.styles/default-chat-color) :default-chat-icon (styles/default-chat-icon-chat-list components.styles/default-chat-color)
:default-chat-icon-text styles/default-chat-icon-text}]) :default-chat-icon-text styles/default-chat-icon-text}])
(defn dapp-icon-permission [contact size]
[contact-icon-view contact
{:container {:width size :height size}
:online-view-wrapper styles/online-view-wrapper
:online-view styles/online-view
:online-dot-left styles/online-dot-left
:online-dot-right styles/online-dot-right
:size size
:chat-icon (styles/custom-size-icon size)
:default-chat-icon (styles/default-chat-icon-profile components.styles/default-chat-color size)
:default-chat-icon-text styles/default-chat-icon-text}])
(defn profile-icon-view [photo-path name color edit? size] (defn profile-icon-view [photo-path name color edit? size]
(let [styles {:container {:width size :height size} (let [styles {:container {:width size :height size}
:online-view styles/online-view-profile :online-view styles/online-view-profile

View File

@ -151,7 +151,8 @@
:icons/camera (components.svg/slurp-svg "./resources/icons/camera.svg") :icons/camera (components.svg/slurp-svg "./resources/icons/camera.svg")
:icons/check (components.svg/slurp-svg "./resources/icons/check.svg") :icons/check (components.svg/slurp-svg "./resources/icons/check.svg")
:icons/dots (components.svg/slurp-svg "./resources/icons/dots.svg") :icons/dots (components.svg/slurp-svg "./resources/icons/dots.svg")
:icons/warning (components.svg/slurp-svg "./resources/icons/warning.svg")})) :icons/warning (components.svg/slurp-svg "./resources/icons/warning.svg")
:icons/settings (components.svg/slurp-svg "./resources/icons/settings.svg")}))
(defn normalize-property-name [n] (defn normalize-property-name [n]
(if (= n :icons/options) (if (= n :icons/options)

View File

@ -12,6 +12,8 @@
(spec/def :browser/loading? (spec/nilable boolean?)) (spec/def :browser/loading? (spec/nilable boolean?))
(spec/def :browser/url-editing? (spec/nilable boolean?)) (spec/def :browser/url-editing? (spec/nilable boolean?))
(spec/def :browser/show-tooltip (spec/nilable keyword?)) (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/options (spec/def :browser/options
(allowed-keys (allowed-keys
@ -19,6 +21,8 @@
:browser/loading? :browser/loading?
:browser/url-editing? :browser/url-editing?
:browser/show-tooltip :browser/show-tooltip
:browser/show-permission
:browser/permissions-queue
:browser/error?])) :browser/error?]))
(spec/def :browser/browser (spec/def :browser/browser

View File

@ -3,18 +3,19 @@
[status-im.utils.handlers :as handlers] [status-im.utils.handlers :as handlers]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.utils.random :as random] [status-im.utils.random :as random]
[status-im.i18n :as i18n]
[status-im.ui.components.list-selection :as list-selection] [status-im.ui.components.list-selection :as list-selection]
[status-im.utils.universal-links.core :as utils.universal-links] [status-im.utils.universal-links.core :as utils.universal-links]
[status-im.data-store.browser :as browser-store] [status-im.data-store.browser :as browser-store]
[status-im.utils.http :as http] [status-im.utils.http :as http]
[status-im.models.browser :as model] [status-im.models.browser :as model]
[status-im.utils.platform :as platform] [status-im.utils.platform :as platform]
[status-im.utils.utils :as utils]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.native-module.core :as status] [status-im.native-module.core :as status]
[taoensso.timbre :as log] [taoensso.timbre :as log]
[status-im.utils.types :as types])) [status-im.utils.handlers-macro :as handlers-macro]
[status-im.utils.types :as types]
[status-im.utils.handlers-macro :as handlers-macro]
[status-im.constants :as constants]))
(re-frame/reg-fx (re-frame/reg-fx
:browse :browse
@ -40,18 +41,6 @@
(fn [[message webview]] (fn [[message webview]]
(.sendToBridge webview (types/clj->json message)))) (.sendToBridge webview (types/clj->json message))))
(re-frame/reg-fx
:show-dapp-permission-confirmation-fx
(fn [[permission {:keys [dapp-name permissions-data] :as params}]]
(utils/show-confirmation
{:ios-confirm-style "default"}
(str "\"" dapp-name "\" " (i18n/label :t/would-like-to-access) " " (:label (get model/permissions permission)))
(i18n/label :t/make-sure-you-trust-dapp)
nil
#(re-frame/dispatch [:next-dapp-permission params permission permissions-data])
#(re-frame/dispatch [:next-dapp-permission params])
(i18n/label :t/dont-allow))))
(handlers/register-handler-fx (handlers/register-handler-fx
:initialize-browsers :initialize-browsers
[(re-frame/inject-cofx :data-store/all-browsers)] [(re-frame/inject-cofx :data-store/all-browsers)]
@ -132,7 +121,7 @@
:on-bridge-message :on-bridge-message
[re-frame/trim-v] [re-frame/trim-v]
(fn [{:keys [db] :as cofx} [message]] (fn [{:keys [db] :as cofx} [message]]
(let [{:browser/keys [options browsers] :keys [webview-bridge]} db (let [{:browser/keys [options browsers]} db
{:keys [browser-id]} options {:keys [browser-id]} options
browser (get browsers browser-id) browser (get browsers browser-id)
data (types/json->clj message) data (types/json->clj message)
@ -143,23 +132,38 @@
(model/update-browser-history-fx cofx browser url false) (model/update-browser-history-fx cofx browser url false)
(= type constants/web3-send-async) (= type constants/web3-send-async)
(model/web3-send-async cofx payload messageId) (model/web3-send-async payload messageId cofx)
(= type constants/status-api-request) (= type constants/status-api-request)
(let [{:account/keys [account]} db (let [{:keys [dapp? name]} browser
{:keys [dapp? name]} browser
dapp-name (if dapp? name host)] dapp-name (if dapp? name host)]
(model/request-permission {: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
[re-frame/trim-v]
(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 cofx
{:db (update-in db [:browser/options :permissions-queue] drop-last)}
(model/request-permission
{:dapp-name dapp-name {:dapp-name dapp-name
:webview webview-bridge
:index 0 :index 0
:user-permissions (get-in db [:dapps/permissions dapp-name :permissions]) :user-permissions (get-in db [:dapps/permissions dapp-name :permissions])
:requested-permissions permissions :requested-permissions permissions
:permissions-data {constants/dapp-permission-contact-code (:public-key account)}})))))) :permissions-data {constants/dapp-permission-contact-code (:public-key account)}})))))))
(handlers/register-handler-fx (handlers/register-handler-fx
:next-dapp-permission :next-dapp-permission
[re-frame/trim-v] [re-frame/trim-v]
(fn [cofx [params permission permissions-data]] (fn [cofx [params permission permissions-data]]
(model/next-permission cofx params permission permissions-data))) (model/next-permission {:params params
:permission permission
:permissions-data permissions-data}
cofx)))

View File

@ -0,0 +1,83 @@
(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]
[status-im.i18n :as i18n]
[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]))
(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)
alpha-value (anim/create-value 0)
hide-panel #(anim/start
(anim/parallel
[(anim/spring bottom-anim-value {:toValue -354})
(anim/timing alpha-value {:toValue 0
:duration 500})]))]
{:component-did-mount #(anim/start
(anim/parallel
[(anim/spring bottom-anim-value {:toValue -20})
(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)]
[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)}
[react/view styles/permissions-panel-icons-container
(if dapp?
[chat-icon.screen/dapp-icon-permission dapp 48]
[react/view styles/permissions-panel-dapp-icon-container
[react/text {:style styles/permissions-panel-d-label} "Ð"]])
[react/view {:margin-left 3 :margin-right 3}
[react/view styles/dot]]
[react/view {:margin-right 3}
[react/view styles/dot]]
[react/view styles/permissions-panel-ok-icon-container
[icons/icon :icons/ok styles/permissions-panel-ok-ico]]
[react/view {:margin-left 3 :margin-right 3}
[react/view styles/dot]]
[react/view {:margin-right 3}
[react/view styles/dot]]
[react/view styles/permissions-panel-wallet-icon-container
(when icon
[icons/icon icon {:color :white}])]]
[react/text {:style styles/permissions-panel-title-label}
(str "\"" dapp-name "\" " title)]
[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])
: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)])
:label (i18n/label :t/allow)}]]
;; TODO (andrey) will be in next PR
#_[react/view {:flex-direction :row :margin-top 19}
[icons/icon :icons/settings {:color colors/blue}]
[react/text {:style styles/permissions-panel-permissions-label}
(i18n/label :t/manage-permissions)]]]])))
;; NOTE (andrey) we need this complex function, to show animation before component will be unmounted
(defn permissions-anim-panel [browser show-permission]
(let [timeout (atom nil)
render? (reagent/atom false)]
(fn [browser show-permission]
(if show-permission
(do
(when @timeout
(js/clearTimeout @timeout)
(reset! timeout nil))
(when-not @render? (reset! render? true)))
(reset! timeout (js/setTimeout #(reset! render? false) 600)))
(when @render?
[permissions-panel browser show-permission]))))

View File

@ -82,3 +82,92 @@
:letter-spacing -0.2 :letter-spacing -0.2
:margin-horizontal 5}) :margin-horizontal 5})
(def dot
{:height 4
:width 4
:background-color "#E4E6EB"
:border-radius 2})
(def permissions-panel-container
{:position :absolute
:top 0
:bottom 0
:right 0
:left 0})
(defn permissions-panel-background [alpha-value]
{:flex 1
:background-color colors/black
:opacity alpha-value})
(defn permissions-panel [bottom-anim-value]
{:height 354
:position :absolute
:bottom bottom-anim-value
:right 0
:left 0
:align-items :center
:background-color :white
:border-top-left-radius 8
:border-top-right-radius 8})
(def permissions-panel-icons-container
{:margin-top 26
:align-items :center
:justify-content :center
:flex-direction :row})
(def permissions-panel-dapp-icon-container
{:height 48
:width 48
:background-color "#E4E6EB"
:border-radius 24
:align-items :center
:justify-content :center})
(def permissions-panel-d-label
{:font-size 22
:color colors/gray
:font-weight :bold})
(def permissions-panel-ok-icon-container
{:height 24
:width 24
:background-color "#48EA77"
:border-radius 12
:align-items :center
:justify-content :center})
(def permissions-panel-ok-ico
{:color :white
:width 12
:height 12})
(def permissions-panel-wallet-icon-container
{:height 48
:width 48
:background-color colors/blue
:border-radius 24
:align-items :center
:justify-content :center})
(def permissions-panel-title-label
{:margin-horizontal 20
:font-size 22
:line-height 28
:text-align :center
:margin-top 19
:font-weight :bold})
(def permissions-panel-description-label
{:margin-horizontal 20
:color colors/gray
:font-size 15
:line-height 22
:text-align :center
:margin-top 9})
(def permissions-panel-permissions-label
{:color colors/blue
:font-size 14
:margin-left 10})

View File

@ -20,7 +20,8 @@
[status-im.utils.http :as http] [status-im.utils.http :as http]
[status-im.ui.components.styles :as components.styles] [status-im.ui.components.styles :as components.styles]
[status-im.ui.components.colors :as colors] [status-im.ui.components.colors :as colors]
[clojure.string :as string])) [clojure.string :as string]
[status-im.ui.screens.browser.permissions.views :as permissions.views]))
(def browser-config (def browser-config
(reader/read-string (slurp "./src/status_im/utils/browser_config.edn"))) (reader/read-string (slurp "./src/status_im/utils/browser_config.edn")))
@ -55,6 +56,21 @@
[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 [:update-browser-options {:url-editing? true}])}
[react/text {:style styles/url-text} (http/url-host url)]])]])) [react/text {:style styles/url-text} (http/url-host url)]])]]))
(defn toolbar [webview error? url browser browser-id url-editing?]
[toolbar.view/toolbar {}
[toolbar.view/nav-button-with-count
(actions/close (fn []
(when @webview
(.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?]
[toolbar.view/actions [{:icon :icons/wallet
:icon-opts {:color :black
:accessibility-label :wallet-modal-button}
:handler #(re-frame/dispatch [:navigate-to-modal :wallet-modal])}]]])
(defn- web-view-error [_ code desc] (defn- web-view-error [_ code desc]
(reagent/as-element (reagent/as-element
[react/view styles/web-view-error [react/view styles/web-view-error
@ -74,11 +90,30 @@
(let [domain-name (nth (re-find #"^\w+://(www\.)?([^/:]+)" url) 2)] (let [domain-name (nth (re-find #"^\w+://(www\.)?([^/:]+)" url) 2)]
(get (:inject-js browser-config) domain-name))) (get (:inject-js browser-config) domain-name)))
(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])
:disabled (not can-go-back?)
:style (when-not can-go-back? styles/disabled-button)
:accessibility-label :previou-page-button}
[react/view
[icons/icon :icons/arrow-left]]]
[react/touchable-highlight {:on-press #(re-frame/dispatch [:browser-nav-forward browser])
:disabled (not can-go-forward?)
:style (merge styles/forward-button
(when-not can-go-forward? styles/disabled-button))
:accessibility-label :next-page-button}
[react/view
[icons/icon :icons/arrow-right]]]
[react/view {:flex 1}]
[react/touchable-highlight {:on-press #(.reload @webview)}
[icons/icon :icons/refresh]]])
(views/defview browser [] (views/defview browser []
(views/letsubs [webview (atom nil) (views/letsubs [webview (atom nil)
{:keys [address]} [:get-current-account] {:keys [address]} [:get-current-account]
{:keys [browser-id dapp? name] :as browser} [:get-current-browser] {:keys [browser-id dapp? name] :as browser} [:get-current-browser]
{:keys [error? loading? url-editing? show-tooltip]} [:get :browser/options] {:keys [error? loading? url-editing? show-tooltip show-permission]} [:get :browser/options]
rpc-url [:get :rpc-url] rpc-url [:get :rpc-url]
network-id [:get-network-id]] network-id [:get-network-id]]
(let [can-go-back? (model/can-go-back? browser) (let [can-go-back? (model/can-go-back? browser)
@ -86,19 +121,7 @@
url (model/get-current-url browser)] url (model/get-current-url browser)]
[react/view styles/browser [react/view styles/browser
[status-bar/status-bar] [status-bar/status-bar]
[toolbar.view/toolbar {} [toolbar webview error? url browser browser-id url-editing?]
[toolbar.view/nav-button-with-count
(actions/close (fn []
(when @webview
(.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?]
[toolbar.view/actions [{:icon :icons/wallet
:icon-opts {:color :black
:accessibility-label :wallet-modal-button}
:handler #(re-frame/dispatch [:navigate-to-modal :wallet-modal])}]]]
[react/view components.styles/flex [react/view components.styles/flex
[components.webview-bridge/webview-bridge [components.webview-bridge/webview-bridge
{:dapp? dapp? {:dapp? dapp?
@ -126,23 +149,8 @@
(when loading? (when loading?
[react/view styles/web-view-loading [react/view styles/web-view-loading
[components/activity-indicator {:animating true}]])] [components/activity-indicator {:animating true}]])]
[react/view styles/toolbar [navigation webview browser can-go-back? can-go-forward?]
[react/touchable-highlight {:on-press #(re-frame/dispatch [:browser-nav-back browser]) [permissions.views/permissions-anim-panel browser show-permission]
:disabled (not can-go-back?)
:style (when-not can-go-back? styles/disabled-button)
:accessibility-label :previou-page-button}
[react/view
[icons/icon :icons/arrow-left]]]
[react/touchable-highlight {:on-press #(re-frame/dispatch [:browser-nav-forward browser])
:disabled (not can-go-forward?)
:style (merge styles/forward-button
(when-not can-go-forward? styles/disabled-button))
:accessibility-label :next-page-button}
[react/view
[icons/icon :icons/arrow-right]]]
[react/view {:flex 1}]
[react/touchable-highlight {:on-press #(.reload @webview)}
[icons/icon :icons/refresh]]]
(when show-tooltip (when show-tooltip
[tooltip/bottom-tooltip-info [tooltip/bottom-tooltip-info
(if (= show-tooltip :secure) (if (= show-tooltip :secure)

View File

@ -135,6 +135,12 @@
:permissions ["FAKE_PERMISSION"]}) :permissions ["FAKE_PERMISSION"]})
nil nil]) 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 (is (= {:dapp dapp-name
:permissions []} :permissions []}
(get @dapps-permissions dapp-name))) (get @dapps-permissions dapp-name)))
@ -144,6 +150,14 @@
:permissions ["CONTACT_CODE"]}) :permissions ["CONTACT_CODE"]})
nil nil]) 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 (= 1 (count @dapps-permissions)))
(is (= {:dapp dapp-name (is (= {:dapp dapp-name
@ -177,6 +191,12 @@
:permissions ["CONTACT_CODE"]}) :permissions ["CONTACT_CODE"]})
nil nil]) 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 (= 2 (count @dapps-permissions)))
(is (= {:dapp dapp-name2 (is (= {:dapp dapp-name2