[#11469] Browser bookmarks

Signed-off-by: andrey <motor4ik@gmail.com>
This commit is contained in:
andrey 2020-12-09 16:08:21 +01:00
parent efc4f6c750
commit 3241d3273e
No known key found for this signature in database
GPG Key ID: 89B67245FD2F0272
27 changed files with 566 additions and 220 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 487 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 861 B

View File

@ -0,0 +1,13 @@
(ns quo.previews.icons
(:require [quo.design-system.colors :as colors]
[quo.react-native :as rn]
[status-im.ui.components.icons.icons :as icons]
[status-im.ui.components.icons.vector-icons :as vector-icons]))
(defn preview []
[rn/scroll-view {:background-color (:ui-background @colors/theme)
:flex 1}
(for [i (keys icons/icons)]
[rn/view {:flex-direction :row}
[vector-icons/icon (keyword i)]
[rn/text i]])])

View File

@ -12,7 +12,8 @@
[reagent.core :as reagent]
[quo.design-system.colors :as colors]
[quo.theme :as theme]
[status-im.ui.screens.routing.core :as navigation]))
[status-im.ui.screens.routing.core :as navigation]
[quo.previews.icons :as icons]))
(def screens [{:name :texts
:insets {:top false}
@ -37,7 +38,10 @@
:component bottom-sheet/preview}
{:name :controls
:insets {:top false}
:component controls/preview}])
:component controls/preview}
{:name :icons
:insets {:top false}
:component icons/preview}])
(defn theme-switcher []
[rn/view {:style {:flex-direction :row

View File

@ -24,7 +24,8 @@
[status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im.ui.components.bottom-sheet.core :as bottom-sheet]
[status-im.browser.webview-ref :as webview-ref]
["eth-phishing-detect" :as eth-phishing-detect]))
["eth-phishing-detect" :as eth-phishing-detect]
[status-im.utils.debounce :as debounce]))
(fx/defn update-browser-option
[{:keys [db]} option-key option-value]
@ -114,9 +115,9 @@
:cb resolve-ens-contenthash-callback}}))
(fx/defn update-browser
[{:keys [db now]}
[{:keys [db]}
{:keys [browser-id] :as browser}]
(let [updated-browser (-> (assoc browser :timestamp now)
(let [updated-browser (-> browser
(update-dapp-name)
(check-if-phishing-url))]
{:db (update-in db
@ -126,6 +127,35 @@
:params [(select-keys updated-browser [:browser-id :timestamp :name :dapp? :history :history-index])]
:on-success #()}]}))
(fx/defn store-bookmark
{:events [:browser/store-bookmark]}
[{:keys [db]}
{:keys [url] :as bookmark}]
{:db (assoc-in db [:bookmarks/bookmarks url] bookmark)
::json-rpc/call [{:method "browsers_storeBookmark"
:params [bookmark]
:on-success #()}]})
(fx/defn update-bookmark
{:events [:browser/update-bookmark]}
[{:keys [db]}
{:keys [url] :as bookmark}]
{:db (update-in db
[:bookmarks/bookmarks url]
merge bookmark)
::json-rpc/call [{:method "browsers_updateBookmark"
:params [url bookmark]
:on-success #()}]})
(fx/defn delete-bookmark
{:events [:browser/delete-bookmark]}
[{:keys [db]}
url]
{:db (update db :bookmarks/bookmarks dissoc url)
::json-rpc/call [{:method "browsers_deleteBookmark"
:params [url]
:on-success #()}]})
(defn can-go-back? [{:keys [history-index]}]
(pos? history-index))
@ -221,7 +251,7 @@
(let [browser (get-current-browser (:db cofx))
options (get-in cofx [:db :browser/options])
current-url (:url options)]
(when (and (not= "about:blank" url) (not= current-url url) (not= (str current-url "/") url))
(when (and (not (string/blank? url)) (not= "about:blank" url) (not= current-url url) (not= (str current-url "/") url))
(let [resolved-ens (first (filter (fn [v]
(not= (.indexOf ^js url (second v)) -1))
(:resolved-ens options)))
@ -417,21 +447,6 @@
(= type constants/api-request)
(browser.permissions/process-permission cofx dapp-name permission messageId params))))
(defn filter-letters-numbers-and-replace-dot-on-dash
[^js value]
(let [cc (.charCodeAt value 0)]
(cond (or (and (> cc 96) (< cc 123))
(and (> cc 64) (< cc 91))
(and (> cc 47) (< cc 58)))
value
(= cc 46)
"-")))
(fx/defn open-chat-from-browser
[cofx host]
(let [topic (string/lower-case (apply str (map filter-letters-numbers-and-replace-dot-on-dash host)))]
{:dispatch [:chat.ui/start-public-chat topic nil]}))
(re-frame/reg-fx
:browser/resolve-ens-content
(fn [{:keys [registry ens-name cb]}]
@ -495,3 +510,9 @@
#(when (= (:view-id db) :browser)
(merge (navigation/navigate-back %)
{:dispatch [:browser.ui/browser-item-selected (get-in db [:browser/options :browser-id])]}))))
(fx/defn open-empty-tab
{:events [:browser.ui/open-empty-tab]}
[cofx]
(debounce/clear :browser/navigation-state-changed)
(navigation/navigate-to-cofx cofx :empty-tab nil))

View File

@ -33,8 +33,7 @@
:history-index 0
:history ["https://cryptokitties.co"]
:dapp? false
:name "Browser"
:timestamp 1}))
:name "Browser"}))
"some properties of the browser are not correct")
(testing "then a second dapp"
@ -49,8 +48,7 @@
{:browser-id dapp2-id
:history-index 0
:history ["http://test2.com"]
:dapp? false
:timestamp 2}))
:dapp? false}))
"some properties of the browser are not correct")
(testing "then removes the second dapp"
@ -70,8 +68,7 @@
:history-index 0
:history ["https://cryptokitties.co"]
:dapp? false
:name "Browser"
:timestamp 2}))
:name "Browser"}))
"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")
@ -91,8 +88,7 @@
:history-index 1
:history ["https://cryptokitties.co" dapp1-url2]
:dapp? false
:name "Browser"
:timestamp 4}))
:name "Browser"}))
"some properties of the browser are not correct")
(testing "then navigates to previous page"
@ -104,8 +100,7 @@
:history-index 0
:history ["https://cryptokitties.co" dapp1-url2]
:dapp? false
:name "Browser"
:timestamp 5}))
:name "Browser"}))
"some properties of the browser are not correct")
(testing "then navigates to next page")
@ -117,6 +112,5 @@
:history-index 1
:history ["https://cryptokitties.co" dapp1-url2]
:dapp? false
:name "Browser"
:timestamp 6}))
:name "Browser"}))
"some properties of the browser are not correct"))))))))))))

View File

@ -70,13 +70,19 @@
:params [allowed-permissions]
:on-success #()}]}))
(fx/defn revoke-dapp-permissions
(fx/defn revoke-permissions
{:events [:browser/revoke-dapp-permissions]}
[{:keys [db] :as cofx} dapp]
(fx/merge cofx
{:db (update-in db [:dapps/permissions] dissoc dapp)
{:db (update-in db [:dapps/permissions] dissoc dapp)
::json-rpc/call [{:method "permissions_deleteDappPermissions"
:params [dapp]
:on-success #()}]}
:on-success #()}]}))
(fx/defn revoke-dapp-permissions
[cofx dapp]
(fx/merge cofx
(revoke-permissions dapp)
(navigation/navigate-back)))
(fx/defn clear-dapps-permissions

View File

@ -127,6 +127,10 @@
"browsers_getBrowsers" {}
"browsers_addBrowser" {}
"browsers_deleteBrowser" {}
"browsers_getBookmarks" {}
"browsers_storeBookmark" {}
"browsers_updateBookmark" {}
"browsers_deleteBookmark" {}
"mailservers_getMailserverRequestGaps" {}
"mailservers_addMailserverRequestGaps" {}
"mailservers_deleteMailserverRequestGaps" {}

View File

@ -724,11 +724,6 @@
(fn [cofx [_ url]]
(browser/open-url cofx url)))
(handlers/register-handler-fx
:browser.ui/open-modal-chat-button-pressed
(fn [cofx [_ host]]
(browser/open-chat-from-browser cofx host)))
(handlers/register-handler-fx
:dapps/revoke-access
(fn [cofx [_ dapp]]

View File

@ -115,6 +115,15 @@
all-stored-browsers)]
{:db (assoc db :browser/browsers browsers)}))
(fx/defn initialize-bookmarks
{:events [::initialize-bookmarks]}
[{:keys [db]} stored-bookmarks]
(let [bookmarks (reduce (fn [acc {:keys [url] :as bookmark}]
(assoc acc url bookmark))
{}
stored-bookmarks)]
{:db (assoc db :bookmarks/bookmarks bookmarks)}))
(fx/defn initialize-invitations
{:events [::initialize-invitations]}
[{:keys [db]} invitations]
@ -245,6 +254,8 @@
:on-success #(re-frame/dispatch [::protocol/initialize-protocol {:mailserver-ranges (or % {})}])}
{:method "browsers_getBrowsers"
:on-success #(re-frame/dispatch [::initialize-browsers %])}
{:method "browsers_getBookmarks"
:on-success #(re-frame/dispatch [::initialize-bookmarks %])}
{:method "permissions_getDappPermissions"
:on-success #(re-frame/dispatch [::initialize-dapp-permissions %])}
{:method "mailservers_getMailservers"

View File

@ -130,6 +130,7 @@
(reg-root-key-sub :browsers :browser/browsers)
(reg-root-key-sub :browser/options :browser/options)
(reg-root-key-sub :dapps/permissions :dapps/permissions)
(reg-root-key-sub :bookmarks :bookmarks/bookmarks)
;;stickers
(reg-root-key-sub :stickers/selected-pack :stickers/selected-pack)
@ -1033,7 +1034,7 @@
:browser/browsers-vals
:<- [:browser/browsers]
(fn [browsers]
(sort-by :timestamp > (vals browsers))))
(vals browsers)))
(re-frame/reg-sub
:get-current-browser

View File

@ -64,7 +64,8 @@
:top -3
:z-index 3
:height 3
:background-color colors/white}}
:background-color colors/white}
:accessibility-label :loading-indicator}
[react/animated-view {:style (animated-bar-style blue-bar-left-margin
parent-width
colors/blue)}]

View File

@ -18,7 +18,7 @@
(defn main-tab? [view-id]
(contains?
#{:chat-stack :browser-stack :wallet-stack :profile-stack :status-stack
:status :home :wallet :open-dapp :my-profile :wallet-onboarding-setup}
:status :home :wallet :empty-tab :my-profile :wallet-onboarding-setup}
view-id))
(def tabs-list-data

View File

@ -0,0 +1,49 @@
(ns status-im.ui.screens.browser.bookmarks.views
(:require [status-im.ui.components.keyboard-avoid-presentation :as kb-presentation]
[status-im.ui.components.react :as react]
[status-im.ui.components.topbar :as topbar]
[status-im.i18n :as i18n]
[status-im.ui.components.toolbar :as toolbar]
[quo.core :as quo]
[status-im.ui.components.colors :as colors]
[reagent.core :as reagent]
[clojure.string :as string]
[re-frame.core :as re-frame]))
(defn screen [{:keys [url name new]}]
(let [input-name (reagent/atom name)]
(fn []
(let [edit? (not new)]
[kb-presentation/keyboard-avoiding-view {:style {:flex 1}}
[react/view {:flex 1}
[topbar/topbar
{:modal? true
:border-bottom true
:title (if edit? (i18n/label :t/edit-favourite) (i18n/label :t/new-favourite))}]
[react/view {:style {:flex 1}}
[quo/text-input
{:container-style {:margin 16 :margin-top 10}
:accessibility-label :bookmark-input
:max-length 100
:auto-focus true
:show-cancel false
:label (i18n/label :t/name)
:default-value name
:on-change-text #(reset! input-name %)}]
[react/text {:style {:margin 16 :color colors/gray}}
url]]
[toolbar/toolbar
{:show-border? true
:center
[quo/button
{:accessibility-label :save-bookmark
:type :secondary
:disabled (string/blank? @input-name)
:on-press #(do (if edit?
(re-frame/dispatch [:browser/update-bookmark {:url url :name @input-name}])
(re-frame/dispatch [:browser/store-bookmark {:url url :name @input-name}]))
(re-frame/dispatch [:navigate-back]))}
(if edit? (i18n/label :t/save) (i18n/label :t/add-favourite))]}]]]))))
(defn new-bookmark []
[screen @(re-frame/subscribe [:get-screen-params])])

View File

@ -1,17 +1,13 @@
(ns status-im.ui.screens.browser.open-dapp.styles
(:require [status-im.ui.components.colors :as colors]
[status-im.ui.components.styles :as components.styles]
[status-im.utils.styles :as styles]))
(ns status-im.ui.screens.browser.empty-tab.styles
(:require [status-im.ui.components.colors :as colors]))
(styles/defn input []
{:border-radius components.styles/border-radius
:background-color colors/gray-lighter
:margin-horizontal 16
:margin-bottom 9
:margin-top 24
:height 36
:padding-horizontal 14
:android {:padding 0}})
(def input
{:height 36
:padding 0})
(def input-container-style
{:margin-horizontal 16
:margin-vertical 10})
(defn browser-icon-container []
{:width 40
@ -24,7 +20,7 @@
(defn dapp-store-container []
{:margin 16
:border-color colors/gray-lighter
:margin-top 8
:margin-top 18
:border-width 1
:border-radius 12
:padding-vertical 16

View File

@ -0,0 +1,136 @@
(ns status-im.ui.screens.browser.empty-tab.views
(:require [re-frame.core :as re-frame]
[status-im.i18n :as i18n]
[status-im.react-native.resources :as resources]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.common.common :as components.common]
[status-im.ui.components.icons.vector-icons :as icons]
[quo.core :as quo]
[status-im.ui.components.list.views :as list]
[status-im.ui.components.react :as react]
[status-im.ui.screens.browser.accounts :as accounts]
[status-im.ui.screens.browser.empty-tab.styles :as styles]
[status-im.ui.screens.wallet.components.views :as components]
[status-im.ui.screens.browser.views :as browser]
[status-im.utils.http :as http]
[reagent.core :as reagent])
(:require-macros [status-im.utils.views :as views]))
(defn hide-sheet-and-dispatch [event]
(re-frame/dispatch [:bottom-sheet/hide])
(js/setTimeout #(re-frame/dispatch event) 200))
(defn list-item [_]
(let [loaded (reagent/atom nil)]
(fn [{:keys [name url] :as bookmark}]
[quo/list-item
{:accessibility-label (keyword (str "fav-item" name))
:on-press #(re-frame/dispatch [:browser.ui/open-url url])
:on-long-press (fn []
(re-frame/dispatch
[:bottom-sheet/show-sheet
{:content (fn []
[react/view {:flex 1}
[quo/list-item
{:theme :accent
:title (i18n/label :t/open-in-new-tab)
:accessibility-label :remove-dapp-from-list
:icon :main-icons/tabs
:on-press #(hide-sheet-and-dispatch [:browser.ui/open-url url])}]
[quo/list-item
{:theme :accent
:title (i18n/label :t/edit)
:accessibility-label :remove-dapp-from-list
:icon :main-icons/edit
:on-press #(hide-sheet-and-dispatch [:navigate-to :new-bookmark bookmark])}]
[quo/list-item
{:theme :negative
:title (i18n/label :t/delete)
:accessibility-label :clear-all-dapps
:icon :main-icons/delete
:on-press #(hide-sheet-and-dispatch [:browser/delete-bookmark url])}]])}]))
:title name
:subtitle (or url (i18n/label :t/dapp))
:icon [react/view {:width 40
:height 40
:align-items :center
:justify-content :center}
(when (or (nil? @loaded) @loaded)
[react/image {:onLoad #(reset! loaded true)
:style {:width 32 :height 32 :position :absolute :top 4 :left 4}
:source {:uri (str "https://" (http/url-host url) "/favicon.ico")}}])
(when-not @loaded
[react/view {:width 40
:height 40
:border-radius 20
:background-color colors/gray-lighter
:align-items :center
:justify-content :center}
[icons/icon :main-icons/browser {:color colors/gray}]])]}])))
(def dapp-image-data {:image (resources/get-image :dapp-store) :width 768 :height 333})
(defn dapp-image [] [components.common/image-contain nil dapp-image-data])
(defn list-header [empty?]
[react/view
[react/touchable-highlight {:on-press #(re-frame/dispatch [:browser.ui/open-url "https://dap.ps"])}
[react/view (styles/dapp-store-container)
[dapp-image nil dapp-image-data]
[react/text {:style styles/open-dapp-store} (i18n/label :t/open-dapp-store)]
[react/text {:style {:color colors/blue :font-size 13 :line-height 22}} "https://dap.ps ->"]]]
(when-not empty?
[react/view {:margin-top 14 :margin-left 16 :margin-bottom 4}
[react/text {:style {:line-height 22 :font-size 15 :color colors/gray}}
(i18n/label :t/favourites)]])])
(views/defview select-account []
(views/letsubs [accounts [:accounts-without-watch-only]
{:keys [name color] :as dapps-account} [:dapps-account]]
[react/view {:position :absolute
:z-index 2
:align-items :center
:bottom 16
:left 0
:right 0
:padding-horizontal 32}
[quo/button
{:accessibility-label :select-account
:type :scale
:on-press #(re-frame/dispatch [:bottom-sheet/show-sheet
{:content (accounts/accounts-list accounts dapps-account)}])}
[react/view (styles/dapps-account color)
[icons/icon :main-icons/account {:color colors/white-persist}]
[react/view {:flex-shrink 1}
[react/text {:numberOfLines 1
:style {:margin-horizontal 6 :color colors/white-persist
:typography :main-medium}}
name]]
[icons/icon :main-icons/dropdown {:color colors/white-transparent-persist}]]]]))
(views/defview empty-tab []
(views/letsubs [bookmarks [:bookmarks]
dapps-account [:dapps-account]
url-text (atom nil)]
(let [bookmarks (vals bookmarks)]
[react/keyboard-avoiding-view {:style {:flex 1}}
[quo/text-input {:on-change-text #(reset! url-text %)
:on-submit-editing #(re-frame/dispatch [:browser.ui/open-url @url-text])
:placeholder (i18n/label :t/enter-url)
:auto-capitalize :none
:auto-correct false
:style styles/input
:container-style styles/input-container-style
:accessibility-label :dapp-url-input
:return-key-type :go}]
[components/separator-dark]
[list/flat-list {:header [list-header (empty? bookmarks)]
:data bookmarks
:key-fn :browser-id
:empty-component [react/view {:align-items :center :margin-top 20}
[icons/icon :main-icons/favourite {:color colors/gray}]
[react/text {:style {:color colors/gray :margin-top 4}}
(i18n/label :t/favourite-description)]]
:render-fn list-item}]
[browser/navigation
{:dapps-account dapps-account
:empty-tab true}]])))

View File

@ -1,111 +0,0 @@
(ns status-im.ui.screens.browser.open-dapp.views
(:require [re-frame.core :as re-frame]
[status-im.i18n :as i18n]
[status-im.react-native.resources :as resources]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.common.common :as components.common]
[status-im.ui.components.icons.vector-icons :as icons]
[quo.core :as quo]
[status-im.ui.components.list.views :as list]
[status-im.ui.components.react :as react]
[status-im.ui.screens.browser.accounts :as accounts]
[status-im.ui.screens.browser.open-dapp.styles :as styles]
[status-im.ui.screens.wallet.components.views :as components])
(:require-macros [status-im.utils.views :as views]))
(defn hide-sheet-and-dispatch [event]
(re-frame/dispatch [:bottom-sheet/hide])
(re-frame/dispatch event))
(defn list-item [{:keys [browser-id name url]}]
[quo/list-item
{:on-press #(re-frame/dispatch [:browser.ui/browser-item-selected browser-id])
:on-long-press (fn []
(re-frame/dispatch
[:bottom-sheet/show-sheet
{:content (fn []
[react/view {:flex 1}
[quo/list-item
{:theme :accent
:title (i18n/label :t/remove)
:accessibility-label :remove-dapp-from-list
:icon :main-icons/delete
:on-press #(hide-sheet-and-dispatch [:browser.ui/remove-browser-pressed browser-id])}]
[quo/list-item
{:theme :negative
:title (i18n/label :t/clear-all)
:accessibility-label :clear-all-dapps
:icon :main-icons/delete
:on-press #(hide-sheet-and-dispatch [:browser.ui/clear-all-browsers-pressed])}]])
:content-height 128}]))
:title name
:subtitle (or url (i18n/label :t/dapp))
:icon [react/view (styles/browser-icon-container)
[icons/icon :main-icons/browser {:color colors/gray}]]}])
(def dapp-image-data {:image (resources/get-image :dapp-store) :width 768 :height 333})
(defn dapp-image [] [components.common/image-contain nil dapp-image-data])
(defn list-header [empty?]
[react/view (when empty? {:flex 1})
[react/touchable-highlight {:on-press #(re-frame/dispatch [:browser.ui/open-url "https://dap.ps"])}
[react/view (styles/dapp-store-container)
[dapp-image nil dapp-image-data]
[react/text {:style styles/open-dapp-store} (i18n/label :t/open-dapp-store)]
[react/text {:style {:color colors/blue :font-size 13 :line-height 22}} "https://dap.ps ->"]]]
(if empty?
[react/view {:flex 1 :align-items :center :justify-content :center}
[react/text {:style {:color colors/gray :font-size 15}} (i18n/label :t/browsed-websites)]]
[react/view {:margin-top 14 :margin-left 16 :margin-bottom 4}
[react/text {:style {:line-height 22 :font-size 15 :color colors/gray}}
(i18n/label :t/recent)]])])
(views/defview select-account []
(views/letsubs [accounts [:accounts-without-watch-only]
{:keys [name color] :as dapps-account} [:dapps-account]]
[react/view {:position :absolute
:z-index 2
:align-items :center
:bottom 16
:left 0
:right 0
:padding-horizontal 32}
[quo/button
{:accessibility-label :select-account
:type :scale
:on-press #(re-frame/dispatch [:bottom-sheet/show-sheet
{:content (accounts/accounts-list accounts dapps-account)}])}
[react/view (styles/dapps-account color)
[icons/icon :main-icons/account {:color colors/white-persist}]
[react/view {:flex-shrink 1}
[react/text {:numberOfLines 1
:style {:margin-horizontal 6 :color colors/white-persist
:typography :main-medium}}
name]]
[icons/icon :main-icons/dropdown {:color colors/white-transparent-persist}]]]]))
(views/defview open-dapp []
(views/letsubs [browsers [:browser/browsers-vals]
url-text (atom nil)]
[react/keyboard-avoiding-view {:style {:flex 1}}
[react/text-input {:on-change-text #(reset! url-text %)
:on-submit-editing #(re-frame/dispatch [:browser.ui/open-url @url-text])
:placeholder (i18n/label :t/enter-url)
:auto-capitalize :none
:auto-correct false
:style (styles/input)
:accessibility-label :dapp-url-input
:return-key-type :go}]
[components/separator]
(if (empty? browsers)
[list-header true]
[react/scroll-view
[list-header false]
[list/flat-list {:data browsers
:footer [react/view
{:style {:height 64
:align-self :stretch}}]
:key-fn :browser-id
:end-fill-color colors/white
:render-fn list-item}]])
[select-account]]))

View File

@ -0,0 +1,97 @@
(ns status-im.ui.screens.browser.options.views
(:require [status-im.ui.components.react :as react]
[quo.core :as quo]
[status-im.i18n :as i18n]
[re-frame.core :as re-frame]
[status-im.ui.components.chat-icon.screen :as chat-icon]
[status-im.ui.components.colors :as colors]
[status-im.ui.screens.wallet.components.views :as components]
[status-im.browser.core :as browser]
[status-im.utils.http :as http]
[status-im.utils.utils :as utils]
[status-im.ui.components.icons.vector-icons :as icons]
[status-im.constants :as constants]))
(defn hide-sheet-and-dispatch [event]
(re-frame/dispatch [:bottom-sheet/hide])
(re-frame/dispatch event))
(defn wallet-connection [host account]
(fn []
[react/view {:flex 1}
[react/text {:style {:align-self :center :margin-horizontal 16 :margin-vertical 8}}
(str "“" host "” " (i18n/label :t/has-permissions))]
[quo/list-item
{:icon [chat-icon/custom-icon-view-list (:name account) (:color account)]
:title (:name account)
:subtitle (utils/get-shortened-checksum-address (:address account))
:accessory [icons/icon :main-icons/check {:color colors/gray}]}]
[react/view {:padding-vertical 8}
[components/separator]]
[quo/list-item
{:theme :negative
:title (i18n/label :t/revoke-access)
:accessibility-label :revoke-access
:icon :main-icons/cancel
:on-press #(hide-sheet-and-dispatch [:browser/revoke-dapp-permissions host])}]]))
(defn browser-options [url account empty-tab name]
(fn []
(let [topic (http/topic-from-url url)
bookmarks @(re-frame/subscribe [:bookmarks])
permissions @(re-frame/subscribe [:dapps/permissions])
fav? (get bookmarks url)
connected? (some #{constants/dapp-permission-web3} (get-in permissions [(http/url-host url) :permissions]))]
[react/view {:flex 1}
(when-not empty-tab
[:<>
[quo/list-item
{:theme :accent
:title (i18n/label :t/new-tab)
:accessibility-label :new-tab
:icon :main-icons/add
:on-press #(hide-sheet-and-dispatch [:browser.ui/open-empty-tab])}]
[quo/list-item
{:theme :accent
:title (if fav? (i18n/label :t/remove-favourite) (i18n/label :t/add-favourite))
:accessibility-label :add-remove-fav
:icon (if fav? :main-icons/delete :main-icons/favourite)
:on-press #(hide-sheet-and-dispatch (if fav?
[:browser/delete-bookmark url]
[:navigate-to :new-bookmark {:url url :name name :new true}]))}]
[quo/list-item
{:theme :accent
:title (i18n/label :t/share)
:accessibility-label :share
:icon :main-icons/share
:on-press (fn []
(re-frame/dispatch-sync [:bottom-sheet/hide])
(js/setTimeout
#(browser/share-link url)
200))}]
[quo/list-item
{:icon [chat-icon/custom-icon-view-list
topic
(rand-nth colors/chat-colors)]
:accessibility-label :open-chat
:title (str "#" topic)
:subtitle (i18n/label :t/open-chat)
:on-press #(hide-sheet-and-dispatch [:chat.ui/start-public-chat topic nil])
:chevron true}]
[components/separator]])
(if connected?
[quo/list-item
{:icon [chat-icon/custom-icon-view-list (:name account) (:color account)]
:title (:name account)
:subtitle (i18n/label :t/connected)
:accessibility-label :connected-account
:chevron true
:on-press #(hide-sheet-and-dispatch [:bottom-sheet/show-sheet
{:content (wallet-connection (http/url-host url) account)}])}]
[quo/list-item
{:theme :accent
:title (i18n/label :t/connect-wallet)
:accessibility-label :connect-account
:icon :main-icons/wallet
:on-press #(hide-sheet-and-dispatch [:browser/bridge-message-received
"{\"type\":\"api-request\",\"permission\":\"web3\"}"])}])])))

View File

@ -11,7 +11,7 @@
:justify-content :space-between
:border-top-color colors/gray-lighter
:border-top-width 1
:padding-horizontal 32})
:padding-horizontal 24})
(def disabled-button
{:opacity 0.4})
@ -41,7 +41,8 @@
:padding-horizontal 10
:align-items :center
:align-self :center
:margin-top 10})
:margin-horizontal 16
:margin-vertical 10})
(def url-input
{:flex 1

View File

@ -0,0 +1,75 @@
(ns status-im.ui.screens.browser.tabs.views
(:require-macros [status-im.utils.views :as views])
(:require [status-im.ui.components.react :as react]
[status-im.ui.components.topbar :as topbar]
[status-im.i18n :as i18n]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.plus-button :as components.plus-button]
[status-im.ui.components.list.views :as list]
[quo.core :as quo]
[status-im.ui.components.icons.vector-icons :as icons]
[status-im.ui.screens.wallet.components.views :as components]
[re-frame.core :as re-frame]
[status-im.utils.http :as http]
[reagent.core :as reagent]))
(defn list-item [_]
(let [loaded (reagent/atom nil)]
(fn [{:keys [browser-id name url empty-tab]}]
[react/view {:flex-direction :row :flex 1}
[react/view {:flex 1}
[quo/list-item
{:on-press #(if empty-tab
(re-frame/dispatch [:browser.ui/open-empty-tab])
(re-frame/dispatch [:browser.ui/browser-item-selected browser-id]))
:title name
:subtitle (when-not empty-tab (or url (i18n/label :t/dapp)))
:accessibility-label (keyword (str "tab-item" name))
:icon [react/view {:width 40
:height 40
:align-items :center
:justify-content :center}
(when (or (nil? @loaded) @loaded)
[react/image {:onLoad #(reset! loaded true)
:style {:width 32 :height 32 :position :absolute :top 4 :left 4}
:source {:uri (str "https://" (http/url-host url) "/favicon.ico")}}])
(when-not @loaded
[react/view {:width 40
:height 40
:border-radius 20
:background-color colors/gray-lighter
:align-items :center
:justify-content :center}
[icons/icon :main-icons/browser {:color colors/gray}]])]}]]
(when-not empty-tab
[react/touchable-highlight
{:style {:width 60 :justify-content :center :align-items :center}
:accessibility-label :empty-tab
:on-press #(re-frame/dispatch [:browser.ui/remove-browser-pressed browser-id])}
[icons/icon :main-icons/close-circle {:color colors/gray}]])])))
(views/defview tabs []
(views/letsubs [browsers [:browser/browsers-vals]]
[react/view {:flex 1}
[topbar/topbar
{:modal? true
:border-bottom false
:navigation :none
:right-accessories
[{:label (i18n/label :t/close-all)
:accessibility-label :close-all
:on-press #(do (re-frame/dispatch [:browser.ui/clear-all-browsers-pressed])
(re-frame/dispatch [:browser.ui/open-empty-tab]))}]
:title (i18n/label :t/tabs)}]
[components/separator-dark]
[list/flat-list {:data (conj browsers {:empty-tab true
:name (i18n/label :t/empty-tab)
:url ""})
:footer [react/view
{:style {:height 64
:align-self :stretch}}]
:key-fn :browser-id
:render-fn list-item}]
[components.plus-button/plus-button
{:on-press #(re-frame/dispatch [:browser.ui/open-empty-tab])}]]))

View File

@ -10,7 +10,6 @@
[status-im.ui.components.icons.vector-icons :as icons]
[status-im.ui.components.react :as react]
[status-im.ui.components.styles :as components.styles]
[status-im.ui.components.topbar :as topbar]
[status-im.ui.components.tooltip.views :as tooltip]
[status-im.ui.components.webview :as components.webview]
[status-im.ui.screens.browser.accounts :as accounts]
@ -22,13 +21,16 @@
[status-im.utils.js-resources :as js-res]
[status-im.utils.contenthash :as contenthash]
[status-im.ui.components.permissions :as components.permissions]
[quo.core :as quo])
[quo.core :as quo]
[status-im.ui.screens.wallet.components.views :as components]
[status-im.ui.screens.browser.options.views :as options])
(:require-macros [status-im.utils.views :as views]))
(defn toolbar-content [url url-original {:keys [secure?]} url-editing? unsafe?]
(defn toolbar-content [url url-original secure? url-editing? unsafe?]
(let [url-text (atom url)]
[react/view (styles/toolbar-content)
[react/touchable-highlight {:on-press #(re-frame/dispatch [:browser.ui/lock-pressed secure?])}
[react/touchable-highlight {:on-press #(re-frame/dispatch [:browser.ui/lock-pressed secure?])
:accessibility-label :security-icon}
(if secure?
[icons/tiny-icon :tiny-icons/tiny-lock {:color colors/green}]
[icons/tiny-icon :tiny-icons/tiny-lock-broken {:color colors/dark}])]
@ -42,7 +44,8 @@
:auto-focus true
:default-value url
:ellipsize :end
:style styles/url-input}]
:style styles/url-input
:accessibility-label :browser-input}]
[react/touchable-highlight {:style styles/url-text-container
:on-press #(re-frame/dispatch [:browser.ui/url-input-pressed])}
[react/text {:number-of-lines 1} (http/url-host url-original)]])
@ -51,19 +54,6 @@
:accessibility-label :refresh-page-button}
[icons/icon :main-icons/refresh]])]))
(defn toolbar [error? url url-original browser browser-id url-editing? unsafe?]
[topbar/topbar
{:navigation {:icon :main-icons/close
:on-press (fn []
(debounce/clear :browser/navigation-state-changed)
(re-frame/dispatch [:navigate-back])
(when error?
(re-frame/dispatch [:browser.ui/remove-browser-pressed browser-id])))}
:title-align :left
:title-component
[react/view {:flex 1}
[toolbar-content url url-original browser url-editing? unsafe?]]}])
(defn- web-view-error [_ _ desc]
(reagent/as-element
[react/view styles/web-view-error
@ -74,13 +64,16 @@
[react/text {:style styles/web-view-error-text}
(str desc)]]))
(views/defview navigation [url can-go-back? can-go-forward? dapps-account]
(views/letsubs [height [:dimensions/window-height]
accounts [:accounts-without-watch-only]]
(views/defview navigation [{:keys [url can-go-back? can-go-forward? dapps-account empty-tab browser-id name]}]
(views/letsubs [accounts [:accounts-without-watch-only]]
[react/view (styles/navbar)
[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)
[react/touchable-highlight {:on-press #(if can-go-back?
(re-frame/dispatch [:browser.ui/previous-page-button-pressed])
(do
(re-frame/dispatch [:browser.ui/remove-browser-pressed browser-id])
(re-frame/dispatch [:browser.ui/open-empty-tab])))
:disabled empty-tab
:style (when empty-tab styles/disabled-button)
:accessibility-label :previous-page-button}
[react/view
[icons/icon :main-icons/arrow-left]]]
@ -90,20 +83,32 @@
:accessibility-label :next-page-button}
[react/view
[icons/icon :main-icons/arrow-right]]]
[react/touchable-highlight
{:on-press #(browser/share-link url)
:accessibility-label :modal-share-link-button}
[icons/icon :main-icons/share]]
[react/touchable-highlight
{:accessibility-label :select-account
:on-press #(re-frame/dispatch [:bottom-sheet/show-sheet
{:content (accounts/accounts-list accounts dapps-account)
:content-height (/ height 2)}])}
{:content (accounts/accounts-list accounts dapps-account)}])}
[chat-icon/custom-icon-view-list (:name dapps-account) (:color dapps-account) 32]]
[react/touchable-highlight
{:on-press #(re-frame/dispatch [:browser.ui/open-modal-chat-button-pressed (http/url-host url)])
:accessibility-label :modal-chat-button}
[icons/icon :main-icons/message]]]))
{:on-press #(do
(when empty-tab
(re-frame/dispatch [:navigate-to :browser]))
(re-frame/dispatch [:navigate-to :browser-tabs]))
:accessibility-label :browser-open-tabs}
[icons/icon :main-icons/tabs]]
[react/touchable-highlight
{:on-press #(when-not empty-tab
(re-frame/dispatch
[:bottom-sheet/show-sheet
{:content (options/browser-options
url
dapps-account
empty-tab
name)}]))
:style (when empty-tab styles/disabled-button)
:accessibility-label :browser-options}
[icons/icon :main-icons/more]]]))
(def resources-to-permissions-map {"android.webkit.resource.VIDEO_CAPTURE" :camera
"android.webkit.resource.AUDIO_CAPTURE" :record-audio})
@ -161,9 +166,9 @@
;; should-component-update is called only when component's props are changed,
;; that's why it can't be used in `browser`, because `url` comes from subs
(views/defview browser-component
[{:keys [error? url browser browser-id unsafe? can-go-back? ignore-unsafe
can-go-forward? resolving? network-id url-original
show-permission show-tooltip dapp? name dapps-account resources-permission?]}]
[{:keys [error? url browser-id unsafe? can-go-back? ignore-unsafe
can-go-forward? resolving? network-id url-original dapp? dapp
show-permission show-tooltip name dapps-account resources-permission?]}]
{:should-component-update (fn [_ _ args]
(let [[_ props] args]
(not (nil? (:url props)))))}
@ -197,8 +202,13 @@
:on-load #(re-frame/dispatch [:browser/loading-started])
:on-error #(re-frame/dispatch [:browser/error-occured])
:injected-java-script-before-content-loaded (js-res/ethereum-provider (str network-id))}])]
[navigation url-original can-go-back? can-go-forward? dapps-account]
[permissions.views/permissions-panel [(:dapp? browser) (:dapp browser) dapps-account] show-permission]
[navigation {:url url-original
:name name
:can-go-back? can-go-back?
:can-go-forward? can-go-forward?
:dapps-account dapps-account
:browser-id browser-id}]
[permissions.views/permissions-panel [dapp? dapp dapps-account] show-permission]
(when show-tooltip
[tooltip/bottom-tooltip-info
(if (= show-tooltip :secure)
@ -208,7 +218,7 @@
(views/defview browser []
(views/letsubs [window-width [:dimensions/window-width]
{:keys [browser-id dapp? name unsafe? ignore-unsafe] :as browser} [:get-current-browser]
{:keys [browser-id dapp? dapp name unsafe? ignore-unsafe secure?] :as browser} [:get-current-browser]
{:keys [url error? loading? url-editing? show-tooltip show-permission resolving?]} [:browser/options]
dapps-account [:dapps-account]
network-id [:chain-id]
@ -217,15 +227,16 @@
can-go-forward? (browser/can-go-forward? browser)
url-original (browser/get-current-url browser)]
[react/view {:style styles/browser}
[toolbar error? url url-original browser browser-id url-editing? unsafe?]
[toolbar-content url url-original secure? url-editing? unsafe?]
[components/separator-dark]
[react/view
(when loading?
[connectivity/loading-indicator window-width])]
[browser-component {:dapp? dapp?
:dapp dapp
:error? error?
:url url
:url-original url-original
:browser browser
:browser-id browser-id
:unsafe? unsafe?
:ignore-unsafe ignore-unsafe

View File

@ -1,19 +1,23 @@
(ns status-im.ui.screens.routing.browser-stack
(:require [status-im.ui.screens.routing.core :as navigation]
[status-im.ui.screens.browser.open-dapp.views :as open-dapp]
[status-im.ui.screens.browser.empty-tab.views :as empty-tab]
[status-im.ui.screens.browser.views :as browser]
[status-im.ui.screens.browser.tabs.views :as browser.tabs]
[status-im.ui.components.tabbar.styles :as tabbar.styles]))
(defonce stack (navigation/create-stack))
(defn browser-stack []
[stack {:initial-route-name :open-dapp
[stack {:initial-route-name :empty-tab
:header-mode :none}
[{:name :open-dapp
[{:name :empty-tab
:insets {:top true}
:style {:padding-bottom tabbar.styles/tabs-diff}
:component open-dapp/open-dapp}
:component empty-tab/empty-tab}
{:name :browser
:back-handler :noop
:options {:animationEnabled false}
:component browser/browser}]])
:component browser/browser}
{:name :browser-tabs
:insets {:top true}
:component browser.tabs/tabs}]])

View File

@ -26,6 +26,7 @@
[status-im.ui.screens.wallet.send.views :as wallet]
[status-im.ui.screens.link-previews-settings.views :as link-previews]
[status-im.ui.screens.status.new.views :as status.new]
[status-im.ui.screens.browser.bookmarks.views :as bookmarks]
[status-im.ui.screens.routing.status-stack :as status-stack]))
(defonce main-stack (navigation/create-stack))
@ -145,6 +146,10 @@
:transition :presentation-ios
:insets {:bottom true}
:component status.new/my-status}
{:name :new-bookmark
:transition :presentation-ios
:insets {:bottom true}
:component bookmarks/new-bookmark}
{:name :profile
:transition :presentation-ios
:insets {:bottom true}

View File

@ -4,3 +4,7 @@
(defn separator []
{:height 1
:background-color colors/gray-lighter})
(defn separator-dark []
{:height 1
:background-color colors/black-transparent})

View File

@ -4,3 +4,6 @@
(defn separator []
[react/view (styles/separator)])
(defn separator-dark []
[react/view (styles/separator-dark)])

View File

@ -156,3 +156,16 @@
(mapcat split-param)
(map url-decode)
(apply hash-map))))
(defn filter-letters-numbers-and-replace-dot-on-dash
[^js value]
(let [cc (.charCodeAt value 0)]
(cond (or (and (> cc 96) (< cc 123))
(and (> cc 64) (< cc 91))
(and (> cc 47) (< cc 58)))
value
(= cc 46)
"-")))
(defn topic-from-url [url]
(string/lower-case (apply str (map filter-letters-numbers-and-replace-dot-on-dash (url-host url)))))

View File

@ -1292,8 +1292,12 @@
"membership-description": "Group membership requires you to be accepted by the group admin",
"group-membership-request": "Group membership request",
"members-limit-reached": "Members limit reached",
"favourite": "Favourite",
"favourites": "Favourites",
"new-favourite": "New favourite",
"edit-favourite": "Edit favourite",
"remove-favourite": "Remove favourite",
"add-favourite": "Add favourite",
"add-to-favourites": "Add to favourites",
"favourites-empty": "Addresses added to favourites will appear here",
"contacts-empty": "Contacts with ENS names will appear here",
@ -1331,5 +1335,14 @@
"default-assets": "Default ERC20 and ERC721",
"increase-gas": "Increase Gas",
"cancelling": "Cancelling",
"refresh": "Refresh"
"refresh": "Refresh",
"close-all": "Close all",
"tabs": "Tabs",
"new-tab": "New tab",
"empty-tab": "Empty tab",
"open-in-new-tab": "Open in new tab",
"has-permissions": "has permission to access",
"connect-wallet": "Connect wallet",
"open-chat": "Open chat",
"favourite-description": "Your favourite websites will appear here"
}