[#6644] Chat command recipients should be able to install an extension

Signed-off-by: Julien Eluard <julien.eluard@gmail.com>
This commit is contained in:
Andrey Shovkoplyas 2018-11-28 17:08:57 +01:00 committed by Julien Eluard
parent 37ef82b04d
commit 582c2960ec
No known key found for this signature in database
GPG Key ID: 6FD7DB5437FCBEF6
16 changed files with 124 additions and 61 deletions

View File

@ -11,7 +11,7 @@
com.taoensso/timbre {:mvn/version "4.10.0"}
hickory {:mvn/version "0.7.1"}
com.cognitect/transit-cljs {:mvn/version "0.8.248"}
status-im/pluto {:mvn/version "iteration-4-4"}
status-im/pluto {:mvn/version "iteration-4-5"}
mvxcvi/alphabase {:mvn/version "1.0.0"}
rasom/cljs-react-navigation {:mvn/version "0.1.4"}}

View File

@ -11,7 +11,7 @@
[com.taoensso/timbre "4.10.0"]
[hickory "0.7.1"]
[com.cognitect/transit-cljs "0.8.248"]
[status-im/pluto "iteration-4-4"]
[status-im/pluto "iteration-4-5"]
[mvxcvi/alphabase "1.0.0"]
[rasom/cljs-react-navigation "0.1.4"]]
:plugins [[lein-cljsbuild "1.1.7"]

View File

@ -127,7 +127,8 @@
:suggestions? :view}]}
:hook
(reify hooks/Hook
(hook-in [_ id {:keys [description scope parameters preview short-preview on-send on-receive on-send-sync]} cofx]
(hook-in [_ id {extension-id :id} {:keys [description scope parameters preview short-preview
on-send on-receive on-send-sync]} cofx]
(let [new-command (if on-send-sync
(reify protocol/Command
(id [_] (name id))
@ -140,7 +141,9 @@
(short-preview [_ props] (short-preview props))
(preview [_ props] (preview props))
protocol/Yielding
(yield-control [_ props _] {:dispatch (on-send-sync props)}))
(yield-control [_ props _] {:dispatch (on-send-sync props)})
protocol/Extension
(extension-id [_] extension-id))
(reify protocol/Command
(id [_] (name id))
(scope [_] scope)
@ -150,9 +153,11 @@
(on-send [_ command-message _] (when on-send {:dispatch (on-send command-message)}))
(on-receive [_ command-message _] (when on-receive {:dispatch (on-receive command-message)}))
(short-preview [_ props] (short-preview props))
(preview [_ props] (preview props))))]
(preview [_ props] (preview props))
protocol/Extension
(extension-id [_] extension-id)))]
(load-commands cofx [new-command])))
(unhook [_ id {:keys [scope]} {:keys [db] :as 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

View File

@ -68,3 +68,7 @@
"Function which takes original parameters + cofx map and returns new map of parameters")
(enhance-receive-parameters [this parameters cofx]
"Function which takes original parameters + cofx map and returns new map of parameters"))
(defprotocol Extension
"Protocol for defining extension"
(extension-id [this]))

View File

@ -12,12 +12,15 @@
[chat-id type parameter-map cofx]
(let [command-path (commands/command-id type)
new-parameter-map (and (satisfies? protocol/EnhancedParameters type)
(protocol/enhance-send-parameters type parameter-map cofx))]
(protocol/enhance-send-parameters type parameter-map cofx))
params (merge (or new-parameter-map parameter-map)
(when (satisfies? protocol/Extension type)
{:extension-id (protocol/extension-id type)}))]
{:chat-id chat-id
:content-type constants/content-type-command
:content {:chat-id chat-id
:command-path command-path
:params (or new-parameter-map parameter-map)}}))
:params params}}))
(fx/defn validate-and-send
"Validates and sends command in current chat"

View File

@ -512,13 +512,19 @@
(handlers/register-handler-fx
:extensions.ui/show-button-pressed
(fn [cofx [_ url]]
(extensions.registry/load cofx url)))
(extensions.registry/load cofx url false)))
(handlers/register-handler-fx
:extensions.ui/install-extension-button-pressed
(fn [{:keys [db] :as cofx} [_ url]]
(fx/merge cofx
{:db (assoc-in db [:extensions/manage :url :value] url)}
(extensions.registry/load url true))))
(handlers/register-handler-fx
:extensions.ui/install-button-pressed
[(re-frame/inject-cofx :random-id-generator)]
(fn [cofx [_ data]]
(extensions.registry/install cofx data)))
(fn [cofx [_ data modal?]]
(extensions.registry/install cofx data modal?)))
;; log-level module

View File

@ -6,35 +6,36 @@
[status-im.accounts.update.core :as accounts.update]
[status-im.i18n :as i18n]
[status-im.utils.fx :as fx]
[clojure.set :as set]))
[clojure.set :as set]
[status-im.ui.screens.navigation :as navigation]))
(fx/defn update-hooks
[{:keys [db] :as cofx} hook-fn extension-key]
[{:keys [db] :as cofx} hook-fn extension-id]
(let [account (get db :account/account)
hooks (get-in account [:extensions extension-key :hooks])]
hooks (get-in account [:extensions extension-id :hooks])]
(apply fx/merge cofx
(mapcat (fn [[_ extension-hooks]]
(map (fn [[hook-id {:keys [hook-ref parsed]}]]
(partial hook-fn (:hook hook-ref) hook-id parsed))
(partial hook-fn (:hook hook-ref) hook-id {:id extension-id} parsed))
extension-hooks))
hooks))))
(fx/defn add-to-registry
[{:keys [db] :as cofx} extension-key extension-data active?]
[{:keys [db] :as cofx} extension-id extension-data active?]
(let [{:keys [hooks]} extension-data
data {:hooks hooks
:active? active?}]
(fx/merge cofx
{:db (update-in db [:account/account :extensions extension-key] merge data)}
(update-hooks hooks/hook-in extension-key))))
{:db (update-in db [:account/account :extensions extension-id] merge data)}
(update-hooks hooks/hook-in extension-id))))
(fx/defn remove-from-registry
[cofx extension-key]
[cofx extension-id]
(let [extensions (get-in cofx [:db :account/account :extensions])]
(fx/merge cofx
(when (get-in extensions [extension-key :active?])
(update-hooks hooks/unhook extension-key))
{:db (update-in cofx [:db :account/account :extensions] dissoc extension-key)})))
(when (get-in extensions [extension-id :active?])
(update-hooks hooks/unhook extension-id))
{:db (update-in cofx [:db :account/account :extensions] dissoc extension-id)})))
(fx/defn change-state
[cofx extension-key active?]
@ -48,21 +49,23 @@
(update-hooks hook-fn extension-key))))
(fx/defn install
[{:keys [db] :as cofx} {:keys [hooks] :as extension-data}]
[{:keys [db] :as cofx} {:keys [hooks] :as extension-data} modal?]
(let [{:extensions/keys [manage]
:account/keys [account]} db
{:keys [url]} manage
extension {:id (:value url)
url (get-in manage [:url :value])
extension {:id url
:name (get-in extension-data ['meta :name])
:url (:value url)
:url url
:active? true}
new-extensions (assoc (:extensions account) (:url extension) extension)]
new-extensions (assoc (:extensions account) url extension)]
(fx/merge cofx
{:utils/show-popup {:title (i18n/label :t/success)
:content (i18n/label :t/extension-installed)
:on-dismiss #(re-frame/dispatch [:navigate-to-clean :my-profile])}}
:on-dismiss #(re-frame/dispatch (if modal?
[:navigate-back]
[:navigate-to-clean :my-profile]))}}
(when hooks (accounts.update/account-update {:extensions new-extensions} {}))
(when hooks (add-to-registry (:value url) extension-data true)))))
(when hooks (add-to-registry url extension-data true)))))
(fx/defn uninstall
[{:keys [db] :as cofx} extension-key]
@ -75,13 +78,13 @@
(accounts.update/account-update {:extensions new-extensions} {}))))
(fx/defn load
[cofx url]
[cofx url modal?]
(if (get-in cofx [:db :account/account :extensions url])
{:utils/show-popup {:title (i18n/label :t/error)
:content (i18n/label :t/extension-is-already-added)}}
{:extensions/load {:extensions [{:url (string/trim url)
:active? true}]
:follow-up :extensions/stage}}))
:follow-up (if modal? :extensions/stage-modal :extensions/stage)}}))
(fx/defn initialize
[{{:account/keys [account]} :db}]
@ -108,3 +111,15 @@
(keys)
(map #(existing-hooks-for % cofx extension-data))
(apply set/union)))
(fx/defn stage-extension [{:keys [db] :as cofx} extension-data modal?]
(let [hooks (existing-hooks cofx extension-data)]
(if (empty? hooks)
(fx/merge cofx
{:db (assoc db :staged-extension extension-data)}
(navigation/navigate-to-cofx (if modal? :show-extension-modal :show-extension) nil))
{:utils/show-popup {:title (i18n/label :t/error)
:content (i18n/label :t/extension-hooks-cannot-be-added
{:hooks (->> hooks
(map name)
(clojure.string/join ", "))})}})))

View File

@ -53,6 +53,7 @@
(nav-text (merge props styles/item-text-white-background) text)))
(def default-nav-back [nav-button actions/default-back])
(def default-nav-close [nav-button actions/default-close])
(defn nav-back-count
([]
@ -160,4 +161,5 @@
(defn simple-toolbar
"A simple toolbar composed of a nav-back item and a single line title."
([] (simple-toolbar nil))
([title] (toolbar nil default-nav-back [content-title title])))
([title] (simple-toolbar title false))
([title modal?] (toolbar nil (if modal? default-nav-close default-nav-back) [content-title title])))

View File

@ -1,9 +1,7 @@
(ns status-im.ui.screens.chat.message.message
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [re-frame.core :as re-frame]
[reagent.core :as reagent]
[status-im.ui.components.react :as react]
[status-im.ui.components.animation :as animation]
[status-im.ui.components.list-selection :as list-selection]
[status-im.ui.components.icons.vector-icons :as vector-icons]
[status-im.ui.components.action-sheet :as action-sheet]
@ -12,22 +10,31 @@
[status-im.ui.screens.chat.styles.message.message :as style]
[status-im.ui.screens.chat.photos :as photos]
[status-im.constants :as constants]
[status-im.ui.components.chat-icon.screen :as chat-icon.screen]
[status-im.utils.core :as utils]
[status-im.ui.screens.chat.utils :as chat.utils]
[status-im.utils.identicon :as identicon]
[status-im.utils.gfycat.core :as gfycat]
[status-im.utils.platform :as platform]
[status-im.i18n :as i18n]
[status-im.ui.components.colors :as colors]
[clojure.string :as string]))
[status-im.ui.components.icons.vector-icons :as icons]))
(defn install-extension-message [extension-id outgoing]
[react/touchable-highlight {:on-press #(re-frame/dispatch
[:extensions.ui/install-extension-button-pressed extension-id])}
[react/view style/extension-container
[icons/icon :icons/info {:color (if outgoing colors/white colors/gray)}]
[react/text {:style (style/extension-text outgoing)}
(i18n/label :to-see-this-message)]
[react/text {:style (style/extension-install outgoing)}
(i18n/label :install-the-extension)]]])
(defview message-content-command
[command-message]
(letsubs [id->command [:chats/id->command]]
(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))])))
(if-let [extension-id (get-in command-message [:content :params :extension-id])]
[install-extension-message extension-id (:outgoing command-message)]
[react/text (str "Unhandled command: " (-> command-message :content :command-path first))]))))
(defview message-timestamp [t justify-timestamp? outgoing command? content]
(when-not command?

View File

@ -198,3 +198,17 @@
:color (if outgoing
colors/wild-blue-yonder
colors/gray)})
(def extension-container
{:align-items :center
:margin 10})
(defn extension-text [outgoing]
{:font-size 12
:margin-top 10
:color (if outgoing colors/white-transparent colors/gray)})
(defn extension-install [outgoing]
{:font-size 12
:color (if outgoing colors/white colors/blue)})

View File

@ -15,17 +15,13 @@
(handlers/register-handler-fx
:extensions/stage
(fn [{:keys [db] :as cofx} [_ _ extension-data]]
(let [hooks (extensions.registry/existing-hooks cofx extension-data)]
(if (empty? hooks)
(fx/merge cofx
{:db (assoc db :staged-extension extension-data)}
(navigation/navigate-to-cofx :show-extension nil))
{:utils/show-popup {:title (i18n/label :t/error)
:content (i18n/label :t/extension-hooks-cannot-be-added
{:hooks (->> hooks
(map name)
(clojure.string/join ", "))})}}))))
(fn [cofx [_ _ extension-data]]
(extensions.registry/stage-extension cofx extension-data false)))
(handlers/register-handler-fx
:extensions/stage-modal
(fn [cofx [_ _ extension-data]]
(extensions.registry/stage-extension cofx extension-data true)))
(handlers/register-handler-fx
:extensions/add-to-registry

View File

@ -30,13 +30,13 @@
values))
hooks))
(views/defview show-extension []
(views/defview show-extension-base [modal?]
(views/letsubs [{:keys [data errors]} [:get-staged-extension]]
(if data
[react/view styles/screen
[status-bar/status-bar]
[react/keyboard-avoiding-view components.styles/flex
[toolbar/simple-toolbar (i18n/label :t/extension)]
[toolbar/simple-toolbar (i18n/label :t/extension) modal?]
[react/scroll-view {:keyboard-should-persist-taps :handled}
[react/view styles/wrapper
[react/view {:style {:border-radius 8 :margin 10 :padding 8 :background-color colors/red}}
@ -71,7 +71,7 @@
{:forward? true
:label (i18n/label :t/install)
:disabled? (not (empty? errors))
:on-press #(re-frame/dispatch [:extensions.ui/install-button-pressed data])}]]]]
:on-press #(re-frame/dispatch [:extensions.ui/install-button-pressed data modal?])}]]]]
[react/view styles/screen
[status-bar/status-bar]
[react/view {:flex 1}
@ -80,6 +80,12 @@
[react/text (i18n/label :t/invalid-extension)]
[react/text (str errors)]]]])))
(views/defview show-extension []
[show-extension-base false])
(views/defview show-extension-modal []
[show-extension-base true])
(def qr-code
[react/touchable-highlight {:on-press #(re-frame/dispatch [:qr-scanner.ui/scan-qr-code-pressed
{:toolbar-title (i18n/label :t/scan-qr)}

View File

@ -47,7 +47,7 @@
[status-im.ui.screens.fleet-settings.views :refer [fleet-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 [edit-extension show-extension]]
[status-im.ui.screens.extensions.add.views :refer [edit-extension show-extension show-extension-modal]]
[status-im.ui.screens.bootnodes-settings.views :refer [bootnodes-settings]]
[status-im.ui.screens.pairing.views :refer [installations]]
[status-im.ui.screens.bootnodes-settings.edit-bootnode.views :refer [edit-bootnode]]
@ -177,6 +177,9 @@
:chat-modal
(wrap-modal :chat-modal chat-modal)
:show-extension-modal
(wrap-modal :show-extension-modal show-extension-modal)
:wallet-send-modal-stack
{:screens
{:wallet-send-transaction-modal

View File

@ -21,9 +21,9 @@
:on-click? :event}
:hook
(reify hooks/Hook
(hook-in [_ id {:keys [label view on-click]} {:keys [db]}]
(hook-in [_ id env {:keys [label view on-click]} {:keys [db]}]
{:db (assoc-in db [:wallet :settings id] {:label label :view view :on-click on-click})})
(unhook [_ id _ {:keys [db]}]
(unhook [_ id env _ {:keys [db]}]
{:db (update-in db [:wallet :settings] dissoc id)}))})
(defn- render-token [{:keys [symbol name icon]} visible-tokens]

View File

@ -72,7 +72,7 @@
(fx/defn handle-extension [cofx url]
(log/info "universal-links: handling url profile" url)
(extensions.registry/load cofx url))
(extensions.registry/load cofx url false))
(defn handle-not-found [full-url]
(log/info "universal-links: no handler for " full-url))

View File

@ -780,9 +780,11 @@
"share-dapp-text": "Check out this DApp I'm using on Status: {{link}}",
"network-invalid-url": "Network URL is invalid",
"network-invalid-status-code": "Invalid status code: {{code}}",
"extension-is-already-added": "The extension is already added",
"extension-is-already-added": "The extension is already installed",
"extension-uninstalled": "The extension was uninstalled",
"extension-hooks-cannot-be-added": "The following hooks from this extension cannot be added: {{hooks}}",
"to-see-this-message": "To see this message,",
"install-the-extension": "install the extension",
"migrations-failed-title": "Migration failed",
"migrations-failed-content": "{{message}}\n\nPlease let us know about this problem at #status public chat. If you press \"Cancel\" button, nothing will happen. If you press \"{{erase-accounts-data-button-text}}\" button, account's db will be removed and you will be able to unlock account. All account's data will be lost.",
"migrations-erase-accounts-data-button": "Erase account's db"