Added extensions loading panel

Signed-off-by: Julien Eluard <julien.eluard@gmail.com>
This commit is contained in:
Julien Eluard 2018-07-13 17:50:35 +02:00
parent f82285b1f4
commit ae976e205f
No known key found for this signature in database
GPG Key ID: 6FD7DB5437FCBEF6
36 changed files with 548 additions and 87 deletions

1
.env
View File

@ -14,3 +14,4 @@ DEBUG_WEBVIEW=1
INSTABUG_SURVEYS=1
GROUP_CHATS_ENABLED=0
CACHED_WEBVIEWS_ENABLED=1
EXTENSIONS=1

View File

@ -9,4 +9,5 @@ POW_TIME=1
DEFAULT_NETWORK=testnet_rpc
INSTABUG_TOKEN=758630ed52864cbad9c5eeeac596c60c
DEBUG_WEBVIEW=1
GROUP_CHATS_ENABLED=1
GROUP_CHATS_ENABLED=1
EXTENSIONS=0

View File

@ -13,3 +13,4 @@ DEBUG_WEBVIEW=1
GROUP_CHATS_ENABLED=0
MAINNET_WARNING_ENABLED=1
CACHED_WEBVIEWS_ENABLED=1
EXTENSIONS=0

View File

@ -12,3 +12,4 @@ INSTABUG_TOKEN=758630ed52864cbad9c5eeeac596c60c
DEBUG_WEBVIEW=1
GROUP_CHATS_ENABLED=0
MAINNET_WARNING_ENABLED=1
EXTENSIONS=1

View File

@ -13,3 +13,4 @@ DEBUG_WEBVIEW=1
INSTABUG_SURVEYS=1
GROUP_CHATS_ENABLED=0
MAINNET_WARNING_ENABLED=1
EXTENSIONS=0

View File

@ -13,4 +13,5 @@ TESTFAIRY_TOKEN=969f6c921cb435cea1d41d1ea3f5b247d6026d55
INSTABUG_TOKEN=758630ed52864cbad9c5eeeac596c60c
DEBUG_WEBVIEW=0
GROUP_CHATS_ENABLED=0
MAINNET_WARNING_ENABLED=1
MAINNET_WARNING_ENABLED=1
EXTENSIONS=0

View File

@ -10,7 +10,8 @@
com.andrewmcveigh/cljs-time {:mvn/version "0.5.2"}
com.taoensso/timbre {:mvn/version "4.10.0"}
hickory {:mvn/version "0.7.1"}
com.cognitect/transit-cljs {:mvn/version "0.8.248"}}
com.cognitect/transit-cljs {:mvn/version "0.8.248"}
status-im/pluto {:mvn/version "iteration-2-SNAPSHOT"}}
:aliases
{:dev {:extra-deps

View File

@ -10,7 +10,8 @@
[com.andrewmcveigh/cljs-time "0.5.2"]
[com.taoensso/timbre "4.10.0"]
[hickory "0.7.1"]
[com.cognitect/transit-cljs "0.8.248"]]
[com.cognitect/transit-cljs "0.8.248"]
[status-im/pluto "iteration-2-SNAPSHOT"]]
:plugins [[lein-cljsbuild "1.1.7"]
[lein-re-frisk "0.5.8"]
[lein-cljfmt "0.5.7"]

View File

@ -1,6 +1,8 @@
(ns status-im.chat.commands.core
(:require [clojure.set :as set]
(:require [re-frame.core :as re-frame]
[clojure.set :as set]
[clojure.string :as string]
[pluto.host :as host]
[status-im.constants :as constants]
[status-im.chat.constants :as chat-constants]
[status-im.chat.commands.protocol :as protocol]
@ -8,6 +10,7 @@
[status-im.chat.models :as chat-model]
[status-im.chat.models.input :as input-model]
[status-im.chat.models.message :as message-model]
[status-im.utils.handlers :as handlers]
[status-im.utils.handlers-macro :as handlers-macro]))
(def register
@ -80,7 +83,7 @@
(defn index-commands
"Takes collecton of things implementing the command protocol, and
correctly indexes them by their composite ids and access scopes."
[commands {:keys [db]}]
[commands]
(let [id->command (reduce (fn [acc command]
(assoc acc (command-id command)
{:type command
@ -100,9 +103,66 @@
access-scopes)))
{}
id->command)]
{:db (assoc db
:id->command id->command
:access-scope->command-id access-scope->command-id)}))
{:id->command id->command
:access-scope->command-id access-scope->command-id}))
(defn load-commands
"Takes collection of things implementing the command protocol and db,
correctly indexes them and adds them to db in a way that preserves existing commands"
[commands {:keys [db]}]
(let [{:keys [id->command access-scope->command-id]} (index-commands commands)]
{:db (-> db
(update :id->command merge id->command)
(update :access-scope->command-id #(merge-with (fnil into #{}) % access-scope->command-id)))}))
(defn remove-command
"Remove command form db, correctly updating all indexes"
[command {:keys [db]}]
(let [id (command-id command)]
{:db (-> db
(update :id->command dissoc id)
(update :access-scope->command-id (fn [access-scope->command-id]
(reduce (fn [acc [scope command-ids-set]]
(if (command-ids-set id)
(if (= 1 (count command-ids-set))
acc
(assoc acc scope (disj command-ids-set id)))
(assoc acc scope command-ids-set)))
{}
access-scope->command-id))))}))
(def command-hook
"Hook for extensions"
(reify host/AppHook
(id [_] :commands)
(properties [_] {:scope #{:personal-chats :public-chats}
:description :string
:short-preview :view
:preview :view
:parameters [{:id :keyword
:type {:one-of #{:text :phone :password :number}}
:placeholder :string
:suggestions? :component}]})
(hook-in [_ id {:keys [description scope parameters preview short-preview]} cofx]
(let [new-command (reify protocol/Command
(id [_] (name id))
(scope [_] scope)
(description [_] description)
(parameters [_] parameters)
(validate [_ _ _])
(on-send [_ _ _])
(on-receive [_ _ _])
(short-preview [_ props] (short-preview props))
(preview [_ props] (preview props)))]
(load-commands [new-command] cofx)))
(unhook [_ id {:keys [scope]} {:keys [db] :as cofx}]
(remove-command (get-in db [:id->command [(name id) scope] :type]) cofx))))
(handlers/register-handler-fx
:load-commands
[re-frame/trim-v]
(fn [cofx [commands]]
(load-commands commands cofx)))
(defn chat-commands
"Takes `id->command`, `access-scope->command-id` and `chat` parameters and returns

View File

@ -12,6 +12,7 @@
[status-im.ui.components.chat-preview :as chat-preview]
[status-im.ui.components.list.views :as list]
[status-im.ui.components.animation :as animation]
[status-im.ui.components.svgimage :as svgimage]
[status-im.i18n :as i18n]
[status-im.constants :as constants]
[status-im.utils.ethereum.core :as ethereum]
@ -40,21 +41,40 @@
#_[react/text {:style transactions-styles/asset-balance}
(str (money/internal->formatted amount symbol decimals))]]]))
(defn- render-nft-asset [selected-event-creator]
(fn [{:keys [name symbol amount] :as asset}]
[react/touchable-highlight
{:on-press #(re-frame/dispatch (selected-event-creator (clojure.core/name symbol)))}
[react/view transactions-styles/asset-container
[react/view transactions-styles/asset-main
[react/image {:source (-> asset :icon :source)
:style transactions-styles/asset-icon}]
[react/text {:style transactions-styles/asset-symbol} name]]
[react/text {:style transactions-styles/nft-asset-amount} (money/to-fixed amount)]]]))
(def assets-separator [react/view transactions-styles/asset-separator])
(defview choose-asset [selected-event-creator]
(defview choose-asset [nft? selected-event-creator]
(letsubs [assets [:wallet/visible-assets-with-amount]]
[react/view
[list/flat-list {:data (filter #(not (:nft? %)) assets)
[list/flat-list {:data (filter #(if nft?
(:nft? %)
(not (:nft? %)))
assets)
:key-fn (comp name :symbol)
:render-fn (render-asset selected-event-creator)
:render-fn (if nft?
(render-nft-asset selected-event-creator)
(render-asset selected-event-creator))
:enableEmptySections true
:separator assets-separator
:keyboardShouldPersistTaps :always
:bounces false}]]))
(defn choose-asset-suggestion [selected-event-creator]
[choose-asset selected-event-creator])
[choose-asset false selected-event-creator])
(defn choose-nft-asset-suggestion [selected-event-creator]
[choose-asset true selected-event-creator])
(defn personal-send-request-short-preview
[label-key {:keys [content]}]
@ -77,6 +97,32 @@
:type :number
:placeholder "Amount"}])
(defview choose-nft-token [selected-event-creator]
(letsubs [{:keys [input-params]} [:selected-chat-command]
collectibles [:collectibles]]
(let [collectible-tokens (get collectibles (keyword (:symbol input-params)))]
[react/view {:flex-direction :row
:align-items :center
:padding-vertical 11}
(map
(fn [[id {:keys [name image_url]}]]
[react/touchable-highlight
{:key id
:on-press #(re-frame/dispatch (selected-event-creator (str id)))}
[react/view {:flex-direction :column
:align-items :center
:margin-left 10
:border-radius 2
:border-width 1
:border-color colors/gray}
[svgimage/svgimage {:style transactions-styles/nft-token-icon
:source {:uri image_url}}]
[react/text {} name]]])
collectible-tokens)])))
(defn choose-nft-token-suggestion [selected-event-creator]
[choose-nft-token selected-event-creator])
;;TODO(goranjovic): currently we only allow tokens which are enabled in Manage assets here
;; because balances are only fetched for them. Revisit this decision with regard to battery/network consequences
;; if we were to update all balances.

View File

@ -23,6 +23,17 @@
(def asset-symbol
{:color colors/black})
(def nft-asset-amount
{:font-size 16
:color colors/gray
:padding-right 14})
(def nft-token-icon
{:width 100
:height 100
:margin-left 20
:margin-right 20})
(def asset-name
{:color colors/gray
:padding-left 4})

View File

@ -25,7 +25,8 @@
:prefill [(get parameter-map :asset)
(get parameter-map :amount)]}
:content-type constants/content-type-command-request}}
path))
path
{:content-type constants/content-type-command}))
(defn- create-command-message
"Create message map from chat-id, command & input parameters"

View File

@ -157,56 +157,10 @@
{:padding-top 12
:padding-bottom 10})))
(def author
{:color styles/color-gray4
:margin-bottom 4
:font-size 12})
(def audio-container
{:flex-direction :row
:align-items :center})
(def play-view
{:width 33
:height 33
:border-radius 16
:elevation 1})
(def play-image
{:width 33
:height 33})
(def track-container
{:margin-top 10
:margin-left 10
:width 120
:height 26
:elevation 1})
(def track
{:position :absolute
:top 4
:width 120
:height 2
:background-color :#EC7262})
(def track-mark
{:position :absolute
:left 0
:top 0
:width 2
:height 10
:background-color :#4A5258})
(def track-duration-text
{:position :absolute
:left 1
:top 11
:font-size 11
:color :#4A5258
:letter-spacing 1
:line-height 15})
(def status-container
{:flex 1
:align-self :center

View File

@ -280,10 +280,12 @@
:<- [:chat-parameter-box]
:<- [:show-suggestions?]
:<- [:validation-messages]
(fn [[chat-parameter-box show-suggestions? validation-messages]]
:<- [:selected-chat-command]
(fn [[chat-parameter-box show-suggestions? validation-messages {:keys [command-completion]}]]
(and chat-parameter-box
(not validation-messages)
(not show-suggestions?))))
(not show-suggestions?)
(not (= :complete command-completion)))))
(reg-sub
:show-suggestions-view?

View File

@ -42,22 +42,12 @@
:font :default}
status])])))
(defn message-content-audio [_]
[react/view style/audio-container
[react/view style/play-view
[react/image {:style style/play-image}]]
[react/view style/track-container
[react/view style/track]
[react/view style/track-mark]
[react/text {:style style/track-duration-text
:font :default}
"03:39"]]])
(defview message-content-command
[command-message]
(letsubs [id->command [:get-id->command]]
(when-let [command (commands-receiving/lookup-command-by-ref command-message id->command)]
(commands/generate-preview command command-message))))
(if-let [command (commands-receiving/lookup-command-by-ref command-message id->command)]
(commands/generate-preview command command-message)
[react/text (str "Unhandled command: " (-> command-message :content :command-path first))])))
(def rtl-characters-regex #"[^\u0591-\u06EF\u06FA-\u07FF\u200F\u202B\u202E\uFB1D-\uFDFD\uFE70-\uFEFC]*?[\u0591-\u06EF\u06FA-\u07FF\u200F\u202B\u202E\uFB1D-\uFDFD\uFE70-\uFEFC]")
@ -235,11 +225,10 @@
[wrapper message [emoji-message message]])
(defmethod message-content :default
[wrapper {:keys [content-type content] :as message}]
[wrapper {:keys [content-type] :as message}]
[wrapper message
[message-view message
[message-content-audio {:content content
:content-type content-type}]]])
[react/text {} (str "Unhandled content-type " content-type)]]])
(defn- text-status [status]
[react/view style/delivery-view

View File

@ -0,0 +1,7 @@
(ns status-im.extensions.core
(:require [clojure.string :as string]))
(defn url->storage-details [s]
(when s
(let [[_ type id] (string/split s #".*[:/]([a-z]*)@(.*)")]
[(keyword type) id])))

View File

@ -0,0 +1,52 @@
(ns status-im.extensions.registry
(:require [pluto.reader :as reader]
[pluto.registry :as registry]
[pluto.host :as host]
[pluto.storage :as storage]
[pluto.storage.gist :as gist]
[status-im.extensions.core :as extension]
[status-im.chat.commands.core :as commands]
[status-im.chat.commands.impl.transactions :as transactions]
[status-im.ui.components.react :as react]))
(def components
{'view react/view
'text react/text
'asset-selector transactions/choose-nft-asset-suggestion
'token-selector transactions/choose-nft-token-suggestion})
(def app-hooks #{commands/command-hook})
(def capacities
(reduce (fn [capacities hook]
(assoc-in capacities [:hooks (host/id hook)] hook))
{:components components
:queries #{:get-in}
:events #{:set-in}
:permissions {:read {:include-paths #{[:network]
[:current-chat-id]
[:chats #".*"]}}
:write {:include-paths #{}}}}
app-hooks))
(defn parse [{:keys [data]}]
(try
(let [{:keys [errors] :as extension-data} (reader/parse {:capacities capacities} data)]
(when errors
(println "Failed to parse status extensions" errors))
extension-data)
(catch :default e (println "EXC" e))))
(def storages
{:gist (gist/GistStorage.)})
(defn read-extension [o]
(-> o :value first :content reader/read))
(defn load-from [url f]
(let [[type id] (extension/url->storage-details url)
storage (get storages type)]
(when (and storage id)
(storage/fetch storage
{:value id}
#(f %)))))

View File

@ -72,7 +72,7 @@
:contacts/dapps default-dapps)}
(group-chat-messages)
(add-default-contacts)
(commands/index-commands commands/register))))
(commands/load-commands commands/register))))
(defn process-pending-messages
"Change status of own messages which are still in `sending` status to `not-sent`

View File

@ -12,6 +12,9 @@
:mailserver-reconnect "Could not connect to mailserver. Tap to reconnect"
:fetching-messages "Fetching messages..."
:search-for "Search for..."
:find "Find"
:install "Install"
:success "Success"
:cancel "Cancel"
:next "Next"
:open "Open"
@ -710,6 +713,18 @@
:network-id "Network ID"
:specify-network-id "Specify network id"
:extension "Extension"
:extensions "Extensions"
:extension-installed "You installed an extension"
:extension-find "Find extension"
:extension-address "Extension address"
:extension-url "Enter an extension URL"
:no-extension "No extension installed"
:identifier "Identifier"
:errors "Errors"
:hooks "Hooks"
:permissions "Permissions"
;; invalid-key
:invalid-key-title "We detected a problem with the encryption key"

View File

@ -4,6 +4,7 @@
[status-im.constants :as constants]
[status-im.utils.platform :as platform]
[status-im.utils.dimensions :as dimensions]
pluto.registry
status-im.transport.db
status-im.ui.screens.accounts.db
status-im.ui.screens.contacts.db
@ -55,7 +56,8 @@
:tooltips {}
:desktop/desktop {:tab-view-id :home}
:dimensions/window (dimensions/window)
:push-notifications/stored {}})
:push-notifications/stored {}
:registry {}})
;;;;GLOBAL
@ -120,6 +122,8 @@
(spec/def :navigation.screen-params/collectibles-list map?)
(spec/def :navigation.screen-params/show-extension map?)
(spec/def :navigation/screen-params (spec/nilable (allowed-keys :opt-un [:navigation.screen-params/network-details
:navigation.screen-params/browser
:navigation.screen-params/profile-qr-viewer
@ -127,7 +131,8 @@
:navigation.screen-params/group-contacts
:navigation.screen-params/edit-contact-group
:navigation.screen-params/dapp-description
:navigation.screen-params/collectibles-list])))
:navigation.screen-params/collectibles-list
:navigation.screen-params/show-extension])))
(spec/def :desktop/desktop (spec/nilable any?))
(spec/def ::tooltips (spec/nilable any?))
@ -144,6 +149,9 @@
(spec/def ::collectible (spec/nilable map?))
(spec/def ::collectibles (spec/nilable map?))
(spec/def ::extension-url (spec/nilable string?))
(spec/def ::staged-extension (spec/nilable any?))
;;;;NODE
(spec/def ::message-envelopes (spec/nilable map?))
@ -284,4 +292,7 @@
:notifications/notifications
::device-UUID
::collectible
::collectibles]))
::collectibles
::extension-url
::staged-extension
:registry/registry]))

View File

@ -26,6 +26,7 @@
status-im.ui.screens.network-settings.events
status-im.ui.screens.profile.events
status-im.ui.screens.qr-scanner.events
status-im.ui.screens.extensions.events
status-im.ui.screens.wallet.events
[status-im.models.wallet :as models.wallet]
status-im.ui.screens.wallet.collectibles.events

View File

@ -0,0 +1,55 @@
(ns status-im.ui.screens.extensions.add.events
(:require [re-frame.core :as re-frame]
[pluto.registry :as registry]
[status-im.extensions.registry :as extensions]
[status-im.ui.screens.navigation :as navigation]
[status-im.i18n :as i18n]
[status-im.utils.handlers :as handlers]
[status-im.utils.handlers-macro :as handlers-macro]))
(re-frame/reg-fx
:extension/load
(fn [[url follow-up-event]]
(extensions/load-from url #(re-frame/dispatch [follow-up-event (-> % extensions/read-extension extensions/parse)]))))
(handlers/register-handler-fx
:extension/install
[re-frame/trim-v]
(fn [cofx [extension-data]]
(let [extension-key (get-in extension-data ['meta :name])]
(handlers-macro/merge-fx cofx
{:show-confirmation {:title (i18n/label :t/success)
:content (i18n/label :t/extension-installed)
:on-accept #(re-frame/dispatch [:navigate-to-clean :home])
:on-cancel nil}}
(registry/add extension-data)
(registry/activate extension-key)))))
(handlers/register-handler-db
:extension/edit-address
[re-frame/trim-v]
(fn [db [address]]
(assoc db :extension-url address)))
(handlers/register-handler-db
:extension/stage
[re-frame/trim-v]
(fn [db [extension-data]]
(-> db
(assoc :staged-extension extension-data)
(navigation/navigate-to :show-extension))))
(handlers/register-handler-fx
:extension/show
[re-frame/trim-v]
(fn [cofx [uri]]
{:extension/load [uri :extension/stage]}))
(handlers/register-handler-fx
:extension/toggle-activation
[re-frame/trim-v]
(fn [cofx [id state]]
(when-let [toggle-fn (get {true registry/activate
false registry/deactivate}
state)]
(toggle-fn id cofx))))

View File

@ -0,0 +1,51 @@
(ns status-im.ui.screens.extensions.add.styles
(:require-macros [status-im.utils.styles :refer [defstyle]])
(:require [status-im.ui.components.styles :as styles]
[status-im.ui.components.colors :as colors]))
(def wrapper
{:flex 1
:margin 16})
(def input-container
{:flex-direction :row
:align-items :center
:justify-content :space-between
:border-radius styles/border-radius
:height 52
:margin-top 15})
(defstyle input
{:flex 1
:font-size 15
:letter-spacing -0.2
:android {:padding 0}})
(def bottom-container
{:flex-direction :row
:margin-horizontal 12
:margin-vertical 15})
(def hooks
{:margin-top 20
:margin-left 10})
(def text
{:color colors/black})
(def cartouche-container
{:flex 1
:margin-top 16
:margin-horizontal 16})
(def cartouche-header
{:color colors/gray})
(def cartouche-content-wrapper
{:flex-direction :row
:margin-top 8
:border-color colors/gray-lighter
:border-width 1
:border-radius styles/border-radius
:padding 16
:background-color colors/white-transparent})

View File

@ -0,0 +1,12 @@
(ns status-im.ui.screens.extensions.add.subs
(:require [re-frame.core :as re-frame]))
(re-frame/reg-sub
:get-extension-url
(fn [db]
(:extension-url db)))
(re-frame/reg-sub
:get-staged-extension
(fn [db]
(:staged-extension db)))

View File

@ -0,0 +1,78 @@
(ns status-im.ui.screens.extensions.add.views
(:require-macros [status-im.utils.views :as views])
(:require [re-frame.core :as re-frame]
[clojure.string :as string]
[status-im.ui.components.react :as react]
[status-im.i18n :as i18n]
[status-im.ui.components.styles :as components.styles]
[status-im.ui.components.common.common :as components.common]
[status-im.ui.components.status-bar.view :as status-bar]
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.ui.components.text-input.view :as text-input]
[status-im.ui.screens.extensions.add.styles :as styles]))
(defn cartouche [{:keys [header]} content]
[react/view {:style styles/cartouche-container}
[react/text {:style styles/cartouche-header}
header]
[react/view {:style styles/cartouche-content-wrapper}
[react/view {:flex 1}
[react/text {:style styles/text}
content]]]])
(defn hooks [{:keys [hooks]}]
(mapcat (fn [[hook-id values]]
(map (fn [[id]]
(symbol "hook" (str (name hook-id) "." (name id))))
values))
hooks))
(views/defview show-extension []
(views/letsubs [{:keys [data errors]} [:get-staged-extension]]
[react/view components.styles/flex
[status-bar/status-bar]
[react/keyboard-avoiding-view components.styles/flex
[toolbar/simple-toolbar (i18n/label :t/extension)]
[react/scroll-view {:keyboard-should-persist-taps :handled}
[react/view styles/wrapper
[cartouche {:header (i18n/label :t/identifier)}
(str (get-in data ['meta :name]))]
[cartouche {:header (i18n/label :t/name)}
(str (get-in data ['meta :name]))]
[cartouche {:header (i18n/label :t/description)}
(str (get-in data ['meta :description]))]
[cartouche {:header (i18n/label :t/hooks)}
(string/join " " (hooks data))]
[cartouche {:header (i18n/label :t/permissions)}
(i18n/label :t/none)]
[cartouche {:header (i18n/label :t/errors)}
(i18n/label :t/none)]]]
[react/view styles/bottom-container
[react/view components.styles/flex]
[components.common/bottom-button
{:forward? true
:label (i18n/label :t/install)
:disabled? (seq errors)
:on-press #(re-frame/dispatch [:extension/install data])}]]]]))
(views/defview add-extension []
(views/letsubs [extension-url [:get-extension-url]]
[react/view components.styles/flex
[status-bar/status-bar]
[react/keyboard-avoiding-view components.styles/flex
[toolbar/simple-toolbar (i18n/label :t/extension-find)]
[react/scroll-view {:keyboard-should-persist-taps :handled}
[react/view styles/wrapper
[text-input/text-input-with-label
{:label (i18n/label :t/extension-address)
:style styles/input
:container styles/input-container
:placeholder (i18n/label :t/extension-url)
:on-change-text #(re-frame/dispatch [:extension/edit-address %])}]]]
[react/view styles/bottom-container
[react/view components.styles/flex]
[components.common/bottom-button
{:forward? true
:label (i18n/label :t/find)
:disabled? (string/blank? extension-url)
:on-press #(re-frame/dispatch [:extension/show extension-url])}]]]]))

View File

@ -0,0 +1,12 @@
(ns status-im.ui.screens.extensions.events
(:require [re-frame.core :as re-frame]
[pluto.registry :as registry]
[status-im.utils.handlers :as handlers]
status-im.ui.screens.extensions.add.events))
(handlers/register-handler-db
:extensions/toggle-activation
[re-frame/trim-v]
(fn [db [id m]]
nil))

View File

@ -0,0 +1,21 @@
(ns status-im.ui.screens.extensions.styles
(:require [status-im.ui.components.colors :as colors]))
(def wrapper
{:flex 1
:background-color colors/white})
(defn wnode-icon [connected?]
{:width 40
:height 40
:border-radius 20
:background-color (if connected?
colors/blue
colors/gray-light)
:align-items :center
:justify-content :center})
(def empty-list
{:color colors/black
:text-align :center})

View File

@ -0,0 +1,8 @@
(ns status-im.ui.screens.extensions.subs
(:require [re-frame.core :as re-frame]
status-im.ui.screens.extensions.add.subs))
(re-frame/reg-sub
:get-extensions
(fn [db]
(seq (:registry db))))

View File

@ -0,0 +1,47 @@
(ns status-im.ui.screens.extensions.views
(:require-macros [status-im.utils.views :as views])
(:require [re-frame.core :as re-frame]
[status-im.i18n :as i18n]
[status-im.extensions.registry :as registry]
[status-im.ui.components.icons.vector-icons :as vector-icons]
[status-im.ui.components.list.views :as list]
[status-im.ui.components.react :as react]
[status-im.ui.components.status-bar.view :as status-bar]
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.ui.components.toolbar.actions :as toolbar.actions]
[status-im.ui.screens.extensions.styles :as styles]))
(def wnode-icon
[react/view (styles/wnode-icon true)
[vector-icons/icon :icons/wnode {:color :white}]])
(defn navigate-to-add-extension [wnode-id]
(re-frame/dispatch [:navigate-to :add-extension wnode-id]))
(defn- render-extension [[id {:keys [state]}]]
[list/list-item-with-checkbox
{:checked? (= :active state)
:on-value-change #(re-frame/dispatch [:extension/toggle-activation id %])}
[list/item
wnode-icon
[list/item-content
[list/item-primary id]
[list/item-secondary id]]]])
(views/defview extensions-settings []
(views/letsubs [extensions [:get-extensions]]
[react/view {:flex 1}
[status-bar/status-bar]
[toolbar/toolbar {}
toolbar/default-nav-back
[toolbar/content-title (i18n/label :t/extensions)]
[toolbar/actions
[(toolbar.actions/add false (partial navigate-to-add-extension nil))]]]
[react/view styles/wrapper
[list/flat-list {:data extensions
:default-separator? false
:key-fn first
:render-fn render-extension
:content-container-style (merge (when (zero? (count extensions)) {:flex-grow 1}) {:justify-content :center})
:empty-component [react/text {:style styles/empty-list}
(i18n/label :t/no-extension)]}]]]))

View File

@ -145,6 +145,11 @@
(letsubs [{:keys [sharing-usage-data?]} [:get-current-account]]
{:component-did-mount on-show}
[react/view
(when (and config/extensions-enabled? dev-mode?)
[profile.components/settings-item
{:label-kw :t/extensions
:action-fn #(re-frame/dispatch [:navigate-to :extensions-settings])
:accessibility-label :extensions-button}])
(when dev-mode?
[profile.components/settings-item
{:label-kw :t/network

View File

@ -3,6 +3,7 @@
[status-im.utils.ethereum.core :as ethereum]
status-im.chat.subs
status-im.ui.screens.accounts.subs
status-im.ui.screens.extensions.subs
status-im.ui.screens.home.subs
status-im.ui.screens.contacts.subs
status-im.ui.screens.group.subs

View File

@ -40,8 +40,10 @@
[status-im.ui.screens.network-settings.views :refer [network-settings]]
[status-im.ui.screens.network-settings.network-details.views :refer [network-details]]
[status-im.ui.screens.network-settings.edit-network.views :refer [edit-network]]
[status-im.ui.screens.extensions.views :refer [extensions-settings]]
[status-im.ui.screens.offline-messaging-settings.views :refer [offline-messaging-settings]]
[status-im.ui.screens.offline-messaging-settings.edit-mailserver.views :refer [edit-mailserver]]
[status-im.ui.screens.extensions.add.views :refer [add-extension show-extension]]
[status-im.ui.screens.bootnodes-settings.views :refer [bootnodes-settings]]
[status-im.ui.screens.bootnodes-settings.edit-bootnode.views :refer [edit-bootnode]]
[status-im.ui.screens.currency-settings.views :refer [currency-settings]]
@ -87,10 +89,13 @@
:login login
:recover recover
:network-settings network-settings
:extensions-settings extensions-settings
:network-details network-details
:edit-network edit-network
:offline-messaging-settings offline-messaging-settings
:edit-mailserver edit-mailserver
:add-extension add-extension
:show-extension show-extension
:bootnodes-settings bootnodes-settings
:edit-bootnode edit-bootnode
:currency-settings currency-settings

View File

@ -26,7 +26,7 @@
(defn load-token [web3 i items-number contract address symbol]
(when (< i items-number)
(erc721/token-of-owner-by-index web3 contract address i
(fn [v1 v2]
(fn [_ v2]
(load-token web3 (inc i) items-number contract address symbol)
(re-frame/dispatch [:load-collectible symbol (.toNumber v2)])))))

View File

@ -26,6 +26,7 @@
(def in-app-notifications-enabled? (enabled? (get-config :IN_APP_NOTIFICATIONS_ENABLED 0)))
(def cached-webviews-enabled? (enabled? (get-config :CACHED_WEBVIEWS_ENABLED 0)))
(def rn-bridge-threshold-warnings-enabled? (enabled? (get-config :RN_BRIDGE_THRESHOLD_WARNINGS 0)))
(def extensions-enabled? (enabled? (get-config :EXTENSIONS 0)))
;; CONFIG VALUES
(def log-level

View File

@ -18,6 +18,7 @@
(def public-chat-regex #".*/chat/public/(.*)$")
(def profile-regex #".*/user/(.*)$")
(def browse-regex #".*/browse/(.*)$")
(def extension-regex #".*/extension/(.*)$")
(defn match-url [url regex]
(some->> url
@ -50,6 +51,10 @@
(navigation/navigate-to-cofx :my-profile nil cofx)
(chat.events/show-profile profile-id true cofx)))
(defn handle-extension [url cofx]
(log/info "universal-links: handling url profile" url)
{:extension/load [url :extensions/stage]})
(defn handle-not-found [full-url]
(log/info "universal-links: no handler for " full-url))
@ -73,6 +78,9 @@
(match-url url browse-regex)
(handle-browse url cofx)
(match-url url extension-regex)
(handle-extension url cofx)
:else (handle-not-found url)))
(defn store-url-for-later

View File

@ -64,8 +64,8 @@
(def TestCommandInstance (TestCommand.))
(def AnotherTestCommandInstance (AnotherTestCommand.))
(deftest index-commands-test
(let [fx (core/index-commands #{TestCommandInstance AnotherTestCommandInstance} {:db {}})]
(deftest load-commands-test
(let [fx (core/load-commands #{TestCommandInstance AnotherTestCommandInstance} {:db {}})]
(testing "Primary composite key index for command is correctly created"
(is (= TestCommandInstance
(get-in fx [:db :id->command
@ -94,7 +94,7 @@
(core/command-id AnotherTestCommandInstance))))))
(deftest chat-commands-test
(let [fx (core/index-commands #{TestCommandInstance AnotherTestCommandInstance} {:db {}})]
(let [fx (core/load-commands #{TestCommandInstance AnotherTestCommandInstance} {:db {}})]
(testing "That relevant commands are looked up for chat"
(is (= #{TestCommandInstance AnotherTestCommandInstance}
(into #{}
@ -119,7 +119,7 @@
{:chat-id "contact"})))))))
(deftest selected-chat-command-test
(let [fx (core/index-commands #{TestCommandInstance AnotherTestCommandInstance} {:db {}})
(let [fx (core/load-commands #{TestCommandInstance AnotherTestCommandInstance} {:db {}})
commands (core/chat-commands (get-in fx [:db :id->command])
(get-in fx [:db :access-scope->command-id])
{:chat-id "contact"})]