Refactor adding subscriptions + basic tests

This commit is contained in:
janherich 2017-10-25 17:33:46 +02:00 committed by Roman Volosovskyi
parent df83cbb987
commit 55164b8eac
15 changed files with 276 additions and 190 deletions

View File

@ -1,5 +1,6 @@
(ns status-im.bots.events (ns status-im.bots.events
(:require [re-frame.core :as re-frame] (:require [re-frame.core :as re-frame]
[status-im.utils.utils :as utils]
[status-im.utils.handlers :as handlers] [status-im.utils.handlers :as handlers]
[status-im.chat.models.input :as input-model] [status-im.chat.models.input :as input-model]
[taoensso.timbre :as log])) [taoensso.timbre :as log]))
@ -12,65 +13,85 @@
{} {}
sub-params)) sub-params))
;; TODO(janherich): do this properly instead of the ugly hack with hardcoded bot-db key
;; und uneffective lookup of the owner of selected-chat-command
(defn- check-subscriptions-fx (defn- check-subscriptions-fx
[{:keys [bot-db bot-subscriptions chats current-chat-id] :as app-db} path] [{:keys [bot-db bot-subscriptions] :as app-db} {:keys [bot path]}]
(let [owner-id (some-> app-db (when-let [subscriptions (and bot (get-in bot-subscriptions (concat [bot] [path])))]
(input-model/selected-chat-command current-chat-id (get-in app-db [:chats current-chat-id :input-text])) {:call-jail-function-n
:command (for [[sub-name sub-params] subscriptions]
:owner-id)] {:chat-id bot
(when-let [subscriptions (and owner-id (get-in bot-subscriptions (concat [owner-id] [path])))] :function :subscription
{:call-jail-function-n :parameters {:name sub-name
(for [[sub-name sub-params] subscriptions] :subscriptions (subscription-values sub-params (get bot-db bot))}
{:chat-id owner-id :callback-events-creator (fn [jail-response]
:function :subscription [[::calculated-subscription
:parameters {:name sub-name {:bot bot
:subscriptions (subscription-values sub-params :path [sub-name]
(get bot-db current-chat-id))} :result jail-response}]])})}))
:callback-events-creator (fn [jail-response]
[[::calculated-subscription
{:bot current-chat-id
:path [sub-name]
:result jail-response}]])})})))
(defn set-in-bot-db (defn set-in-bot-db
[{:keys [current-chat-id] :as app-db} {:keys [bot path value] :as params}] "Associates value at specified path in bot-db and checks if there are any subscriptions
(let [bot (or bot current-chat-id) dependent on that path, if yes, adds effects for calling jail-functions to recalculate
new-db (assoc-in app-db (concat [:bot-db bot] path) value)] relevant subscriptions."
[app-db {:keys [bot path value] :as params}]
(let [new-db (assoc-in app-db (concat [:bot-db bot] path) value)]
(merge {:db new-db} (merge {:db new-db}
(check-subscriptions-fx new-db path)))) (check-subscriptions-fx new-db params))))
(defn update-bot-db (defn update-bot-db
[{:keys [current-chat-id] :as app-db} {:keys [bot db]}] [app-db {:keys [bot db]}]
(let [bot (or bot current-chat-id)] (update-in app-db [:bot-db bot] merge db))
(update-in app-db [:bot-db bot] merge db)))
(defn clear-bot-db (defn clear-bot-db
[{:keys [current-chat-id] :as app-db}] [app-db bot-id]
(assoc-in app-db [:bot-db current-chat-id] nil)) (assoc-in app-db [:bot-db bot-id] nil))
(def ^:private keywordize-vector (partial mapv keyword)) (def ^:private keywordize-vector (partial mapv keyword))
;; TODO(janherich): do something with this horrible triple nested reduce so it's more readable (defn- transform-bot-subscriptions
"Transforms bot subscriptions as returned from jail in the following format:
`{:calculatedFee {:subscriptions {:value [\"sliderValue\"]
:tx [\"transaction\"]}}
:feeExplanation {:subscriptions {:value [\"sliderValue\"]}}}`
into data-structure better suited for subscription lookups based on changes
in `:bot-db`:
`{[:sliderValue] {:calculatedFee {:value [:sliderValue]
:tx [:transaction]}
:feeExplanation {:value [:sliderValue]}}
[:transaction] {:calculatedFee {:value [:sliderValue]
:tx [:transaction]}}}`
In the resulting data-structure, the top level keys are the (keywordized) paths
in the `:bot-db` data structure, so it's quick and easy to look-up all the
subscriptions which must be recalculated when something in their path changes."
[bot-subscriptions]
(reduce-kv (fn [acc sub-key {:keys [subscriptions]}]
(reduce-kv (fn [acc sub-param-key sub-param-path]
(update acc
(keywordize-vector sub-param-path)
assoc sub-key
(utils/map-values subscriptions keywordize-vector)))
acc
subscriptions))
{}
bot-subscriptions))
(defn add-active-bot-subscriptions (defn add-active-bot-subscriptions
"Add subscriptions for selected bot identities into app-db"
[app-db bot-identities] [app-db bot-identities]
(let [relevant-bots (select-keys (:contacts/contacts app-db) bot-identities) (assoc app-db :bot-subscriptions (-> app-db
active-subscriptions (reduce (fn [acc [bot-id {:keys [subscriptions]}]] :contacts/contacts
(reduce (fn [acc [sub-key {:keys [subscriptions]}]] (select-keys bot-identities)
(reduce (fn [acc [sub-param-key sub-param-path]] (utils/map-values (comp transform-bot-subscriptions :subscriptions)))))
(update-in acc [bot-id (keywordize-vector sub-param-path)]
assoc sub-key (into {} (defn calculated-subscription
(map (fn [[k v]] [db {:keys [bot path]
[k (keywordize-vector v)])) {:keys [error result]} :result}]
subscriptions))) (if error
acc db
subscriptions)) (assoc-in db (concat [:bot-db bot] path) (:returned result))))
acc
subscriptions))
{}
relevant-bots)]
(assoc app-db :bot-subscriptions active-subscriptions)))
;;;; Handlers ;;;; Handlers
@ -89,8 +110,5 @@
(handlers/register-handler-db (handlers/register-handler-db
::calculated-subscription ::calculated-subscription
[re-frame/trim-v] [re-frame/trim-v]
(fn [db [{:keys [bot path] (fn [db [params]]
{:keys [error result]} :result}]] (calculated-subscription db params)))
(if error
db
(assoc-in db (concat [:bot-db bot] path) (:returned result)))))

View File

@ -1,8 +1,13 @@
(ns status-im.bots.subs (ns status-im.bots.subs
(:require [re-frame.core :refer [reg-sub subscribe]])) (:require [re-frame.core :as re-frame]
[status-im.chat.models.input :as input-model]))
(reg-sub (re-frame/reg-sub
:current-bot-db :current-bot-db
(fn [db] (fn [db]
(let [chat-id (subscribe [:get-current-chat-id])] (let [current-chat-id (re-frame/subscribe [:get-current-chat-id])
(get-in db [:bot-db @chat-id])))) command-owner (-> db
(input-model/selected-chat-command @current-chat-id)
:command
:owner-id)]
[command-owner (get-in db [:bot-db command-owner])])))

View File

@ -52,9 +52,7 @@
(defn update-suggestions (defn update-suggestions
"Update suggestions for current chat input, takes db as the only argument "Update suggestions for current chat input, takes db as the only argument
and returns map with keys :db (new db with up-to-date suggestions) and (optionally) and returns new db with up-to date suggestions"
:call-jail-function with jail function call params, if request to jail needs
to be made as a result of suggestions update."
[{:keys [chats current-chat-id] :as db}] [{:keys [chats current-chat-id] :as db}]
(let [chat-text (str/trim (or (get-in chats [current-chat-id :input-text]) "")) (let [chat-text (str/trim (or (get-in chats [current-chat-id :input-text]) ""))
requests (->> (commands-model/get-possible-requests db) requests (->> (commands-model/get-possible-requests db)
@ -73,7 +71,7 @@
(and dapp? (and dapp?
(str/blank? chat-text)) (str/blank? chat-text))
(assoc-in [:chats current-chat-id :parameter-boxes :message] nil))] (assoc-in [:chats current-chat-id :parameter-boxes :message] nil))]
{:db new-db})) new-db))
(defn set-chat-input-text (defn set-chat-input-text
"Set input text for current-chat and updates suggestions relevant to current input. "Set input text for current-chat and updates suggestions relevant to current input.
@ -131,7 +129,7 @@
seq-params? (-> (input-model/selected-chat-command db current-chat-id) seq-params? (-> (input-model/selected-chat-command db current-chat-id)
(get-in [:command :sequential-params]))] (get-in [:command :sequential-params]))]
(if seq-params? (if seq-params?
{:db (set-chat-seq-arg-input-text db arg)} (set-chat-seq-arg-input-text db arg)
(let [arg (str/replace arg (re-pattern const/arg-wrapping-char) "") (let [arg (str/replace arg (re-pattern const/arg-wrapping-char) "")
command-name (first command) command-name (first command)
command-args (into [] (rest command)) command-args (into [] (rest command))
@ -155,7 +153,7 @@
(let [parameter-index (input-model/argument-position db)] (let [parameter-index (input-model/argument-position db)]
(when (and command (> parameter-index -1)) (when (and command (> parameter-index -1))
(let [data (get-in db [:local-storage current-chat-id]) (let [data (get-in db [:local-storage current-chat-id])
bot-db (get bot-db (or bot current-chat-id)) bot-db (get bot-db owner-id)
path [(if (= :command type) :commands :responses) path [(if (= :command type) :commands :responses)
[name [name
(if (= :command type) (if (= :command type)
@ -176,7 +174,7 @@
:from current-account-id :from current-account-id
:to to} :to to}
(input-model/command-dependent-context-params current-chat-id command))}] (input-model/command-dependent-context-params current-chat-id command))}]
{:call-jail {:jail-id (or bot owner-id current-chat-id) {:call-jail {:jail-id owner-id
:path path :path path
:params params :params params
:callback-events-creator (fn [jail-response] :callback-events-creator (fn [jail-response]
@ -214,9 +212,9 @@
(defn select-chat-input-command (defn select-chat-input-command
"Selects command + (optional) arguments as input for active chat" "Selects command + (optional) arguments as input for active chat"
[{:keys [current-chat-id chat-ui-props] :as db} [{:keys [current-chat-id chat-ui-props] :as db}
{:keys [prefill prefill-bot-db sequential-params name] :as command} metadata prevent-auto-focus?] {:keys [prefill prefill-bot-db sequential-params name owner-id] :as command} metadata prevent-auto-focus?]
(let [db' (-> db (let [db' (-> db
bots-events/clear-bot-db (bots-events/clear-bot-db owner-id)
clear-seq-arguments clear-seq-arguments
(model/set-chat-ui-props {:show-suggestions? false (model/set-chat-ui-props {:show-suggestions? false
:show-emoji? false :show-emoji? false
@ -230,7 +228,8 @@
(input-model/join-command-args prefill))))) (input-model/join-command-args prefill)))))
fx (assoc (load-chat-parameter-box db' command) :db db')] fx (assoc (load-chat-parameter-box db' command) :db db')]
(cond-> fx (cond-> fx
prefill-bot-db (update :db bots-events/update-bot-db {:db prefill-bot-db}) prefill-bot-db (update :db bots-events/update-bot-db {:db prefill-bot-db
:bot owner-id})
(not (and sequential-params (not (and sequential-params
prevent-auto-focus?)) prevent-auto-focus?))
@ -246,15 +245,18 @@
(defn set-contact-as-command-argument (defn set-contact-as-command-argument
"Sets contact as command argument for active chat" "Sets contact as command argument for active chat"
[db {:keys [bot-db-key contact arg-index]}] [{:keys [current-chat-id] :as db} {:keys [bot-db-key contact arg-index]}]
(let [name (str/replace (:name contact) (re-pattern const/arg-wrapping-char) "") (let [name (str/replace (:name contact) (re-pattern const/arg-wrapping-char) "")
contact (select-keys contact [:address :whisper-identity :name :photo-path :dapp?])] contact (select-keys contact [:address :whisper-identity :name :photo-path :dapp?])
(-> (set-command-argument db arg-index name true) command-owner (-> db
(as-> fx (input-model/selected-chat-command current-chat-id)
(merge fx (bots-events/set-in-bot-db :command
(:db fx) :owner-id)]
{:path [:public (keyword bot-db-key)] (-> db
:value contact}))) (set-command-argument arg-index name true)
(bots-events/set-in-bot-db {:bot command-owner
:path [:public (keyword bot-db-key)]
:value contact})
(as-> fx (as-> fx
(let [{:keys [current-chat-id] (let [{:keys [current-chat-id]
:as new-db} (:db fx) :as new-db} (:db fx)
@ -283,7 +285,7 @@
{:url (i18n/get-contact-translated chat-id :dapp-url dapp-url) {:url (i18n/get-contact-translated chat-id :dapp-url dapp-url)
:name (i18n/get-contact-translated chat-id :name name)})) :name (i18n/get-contact-translated chat-id :name name)}))
owner-id (:owner-id command) owner-id (:owner-id command)
bot-db (get bot-db chat-id) bot-db (get bot-db owner-id)
params (merge (input-model/args->params content) params (merge (input-model/args->params content)
{:bot-db bot-db {:bot-db bot-db
:metadata metadata}) :metadata metadata})
@ -374,10 +376,10 @@
(fn [db [data]] (fn [db [data]]
(set-chat-input-metadata db data))) (set-chat-input-metadata db data)))
(handlers/register-handler-fx (handlers/register-handler-db
:set-command-argument :set-command-argument
[re-frame/trim-v] [re-frame/trim-v]
(fn [{:keys [db]} [[index arg move-to-next?]]] (fn [db [[index arg move-to-next?]]]
(set-command-argument db index arg move-to-next?))) (set-command-argument db index arg move-to-next?)))
(handlers/register-handler-fx (handlers/register-handler-fx
@ -393,9 +395,9 @@
(when-let [cmp-ref (get-in chat-ui-props [current-chat-id ref])] (when-let [cmp-ref (get-in chat-ui-props [current-chat-id ref])]
{::blur-rn-component cmp-ref}))) {::blur-rn-component cmp-ref})))
(handlers/register-handler-fx (handlers/register-handler-db
:update-suggestions :update-suggestions
(fn [{:keys [db]} _] (fn [db _]
(update-suggestions db))) (update-suggestions db)))
(handlers/register-handler-fx (handlers/register-handler-fx
@ -443,18 +445,18 @@
[re-frame/trim-v] [re-frame/trim-v]
(fn [{{:keys [current-public-key current-chat-id] (fn [{{:keys [current-public-key current-chat-id]
:accounts/keys [current-account-id] :as db} :db} [{:keys [command] :as command-message}]] :accounts/keys [current-account-id] :as db} :db} [{:keys [command] :as command-message}]]
(-> db {:db (-> db
clear-seq-arguments clear-seq-arguments
(set-chat-input-metadata nil) (set-chat-input-metadata nil)
(set-chat-input-text nil) (set-chat-input-text nil)
(model/set-chat-ui-props {:sending-in-progress? false}) (model/set-chat-ui-props {:sending-in-progress? false})
update-suggestions update-suggestions)
;; TODO: refactor send-message.cljs to use atomic pure handlers and get rid of this dispatch ;; TODO: refactor send-message.cljs to use atomic pure handlers and get rid of this dispatch
(assoc :dispatch [:check-commands-handlers! {:message (get-in db [:chats current-chat-id :input-text]) :dispatch [:check-commands-handlers! {:message (get-in db [:chats current-chat-id :input-text])
:command command-message :command command-message
:chat-id current-chat-id :chat-id current-chat-id
:identity current-public-key :identity current-public-key
:address current-account-id}])))) :address current-account-id}]}))
(handlers/register-handler-fx (handlers/register-handler-fx
::check-command-type ::check-command-type
@ -500,15 +502,15 @@
;; no command detected, when not empty, proceed by sending text message without command processing ;; no command detected, when not empty, proceed by sending text message without command processing
(if (str/blank? input-text) (if (str/blank? input-text)
{:db db} {:db db}
(-> db {:db (-> db
(set-chat-input-metadata nil) (set-chat-input-metadata nil)
(set-chat-input-text nil) (set-chat-input-text nil)
update-suggestions update-suggestions)
;; TODO: refactor send-message.cljs to use atomic pure handlers and get rid of this dispatch ;; TODO: refactor send-message.cljs to use atomic pure handlers and get rid of this dispatch
(assoc :dispatch [:prepare-message {:message input-text :dispatch [:prepare-message {:message input-text
:chat-id current-chat-id :chat-id current-chat-id
:identity current-public-key :identity current-public-key
:address (:accounts/current-account-id db)}]))))))) :address (:accounts/current-account-id db)}]})))))
;; TODO: remove this handler and leave only helper fn once all invocations are refactored ;; TODO: remove this handler and leave only helper fn once all invocations are refactored
(handlers/register-handler-db (handlers/register-handler-db
@ -561,10 +563,10 @@
(let [input-text (get-in db [:chats current-chat-id :input-text]) (let [input-text (get-in db [:chats current-chat-id :input-text])
command (input-model/selected-chat-command db current-chat-id input-text)] command (input-model/selected-chat-command db current-chat-id input-text)]
(if (get-in command [:command :sequential-params]) (if (get-in command [:command :sequential-params])
(-> (set-command-argument db 0 "" false) (-> db
(update :db set-chat-seq-arg-input-text "") (set-command-argument 0 "" false)
(as-> fx (set-chat-seq-arg-input-text "")
(merge fx (load-chat-parameter-box (:db fx) (:command command))))) (load-chat-parameter-box (:command command)))
(let [arg-pos (input-model/argument-position db)] (let [arg-pos (input-model/argument-position db)]
(when (pos? arg-pos) (when (pos? arg-pos)
(let [input-text (get-in db [:chats current-chat-id :input-text]) (let [input-text (get-in db [:chats current-chat-id :input-text])
@ -585,9 +587,9 @@
(fn [{:keys [db]} [params]] (fn [{:keys [db]} [params]]
(set-contact-as-command-argument db params))) (set-contact-as-command-argument db params)))
(handlers/register-handler-fx (handlers/register-handler-db
:show-suggestions :show-suggestions
(fn [{:keys [db]} _] (fn [db _]
(-> db (-> db
(model/toggle-chat-ui-prop :show-suggestions?) (model/toggle-chat-ui-prop :show-suggestions?)
(model/set-chat-ui-props {:validation-messages nil}) (model/set-chat-ui-props {:validation-messages nil})

View File

@ -11,27 +11,21 @@
[status-im.data-store.messages :as msg-store])) [status-im.data-store.messages :as msg-store]))
(re-frame/reg-cofx (re-frame/reg-cofx
:pop-up-chat? :pop-up-chat?
(fn [cofx] (fn [cofx]
(assoc cofx :pop-up-chat? (fn [chat-id] (assoc cofx :pop-up-chat? (fn [chat-id]
(or (not (chat-store/exists? chat-id)) (or (not (chat-store/exists? chat-id))
(chat-store/is-active? chat-id)))))) (chat-store/is-active? chat-id))))))
(re-frame/reg-cofx (re-frame/reg-cofx
:message-exists? :message-exists?
(fn [cofx] (fn [cofx]
(assoc cofx :message-exists? msg-store/exists?))) (assoc cofx :message-exists? msg-store/exists?)))
(re-frame/reg-cofx (re-frame/reg-cofx
:get-last-clock-value :get-last-clock-value
(fn [cofx] (fn [cofx]
(assoc cofx :get-last-clock-value msg-store/get-last-clock-value))) (assoc cofx :get-last-clock-value msg-store/get-last-clock-value)))
(re-frame/reg-cofx
:current-timestamp
(fn [cofx]
;; TODO (janherich) why is actual timestmap generation in random namespace ?
(assoc cofx :current-timestamp (random/timestamp))))
(defn- get-current-identity (defn- get-current-identity
[{:accounts/keys [accounts current-account-id]}] [{:accounts/keys [accounts current-account-id]}]
@ -39,7 +33,7 @@
(defn add-message (defn add-message
[{:keys [db message-exists? get-last-stored-message pop-up-chat? [{:keys [db message-exists? get-last-stored-message pop-up-chat?
get-last-clock-value current-timestamp random-id]} get-last-clock-value now random-id]}
{:keys [from group-id chat-id content-type {:keys [from group-id chat-id content-type
message-id timestamp clock-value] message-id timestamp clock-value]
:as message :as message
@ -57,7 +51,7 @@
(get-last-stored-message chat-identifier) (get-last-stored-message chat-identifier)
message) message)
:chat-id chat-identifier :chat-id chat-identifier
:timestamp (or timestamp current-timestamp) :timestamp (or timestamp now)
:clock-value (clocks/receive :clock-value (clocks/receive
clock-value clock-value
(get-last-clock-value chat-identifier)))] (get-last-clock-value chat-identifier)))]
@ -68,8 +62,7 @@
(assoc-in [:chats chat-identifier :last-message] message)) (assoc-in [:chats chat-identifier :last-message] message))
:dispatch-n [[:upsert-chat! {:chat-id chat-identifier :dispatch-n [[:upsert-chat! {:chat-id chat-identifier
:group-chat group-chat?}] :group-chat group-chat?}]
[:request-command-message-data enriched-message :short-preview] [:request-command-message-data enriched-message :short-preview]]
[:update-suggestions]]
:save-message (dissoc enriched-message :new?)} :save-message (dissoc enriched-message :new?)}
(get-in enriched-message [:content :command]) (get-in enriched-message [:content :command])
@ -77,7 +70,7 @@
(= (:content-type enriched-message) const/content-type-command-request) (= (:content-type enriched-message) const/content-type-command-request)
(update :dispatch-n conj [:add-request chat-identifier enriched-message]) (update :dispatch-n conj [:add-request chat-identifier enriched-message])
;; TODO(janherich) this shouldn't be dispatch, but plain function call, refactor after adding requests is refactored
true true
(update :dispatch-n conj [:update-suggestions]))) (update :dispatch-n conj [:update-suggestions])))
{:db db}))) {:db db})))
@ -85,8 +78,7 @@
(def ^:private receive-interceptors (def ^:private receive-interceptors
[(re-frame/inject-cofx :message-exists?) (re-frame/inject-cofx :get-last-stored-message) [(re-frame/inject-cofx :message-exists?) (re-frame/inject-cofx :get-last-stored-message)
(re-frame/inject-cofx :pop-up-chat?) (re-frame/inject-cofx :get-last-clock-value) (re-frame/inject-cofx :pop-up-chat?) (re-frame/inject-cofx :get-last-clock-value)
(re-frame/inject-cofx :current-timestamp) (re-frame/inject-cofx :random-id) (re-frame/inject-cofx :random-id) re-frame/trim-v])
re-frame/trim-v])
(handlers/register-handler-fx (handlers/register-handler-fx
:received-protocol-message! :received-protocol-message!

View File

@ -218,15 +218,16 @@
(register-handler :received-bot-response (register-handler :received-bot-response
(u/side-effect! (u/side-effect!
(fn [{:contacts/keys [contacts]} [_ {:keys [chat-id] :as params} {:keys [result] :as data}]] (fn [{:contacts/keys [contacts]} [_ {:keys [chat-id] :as params} {:keys [result bot-id] :as data}]]
(let [{:keys [returned context]} result (let [{:keys [returned context]} result
{:keys [markup text-message err]} returned {:keys [markup text-message err]} returned
{:keys [log-messages update-db default-db]} context {:keys [log-messages update-db default-db]} context
content (or err text-message)] content (or err text-message)]
(when update-db (when update-db
(dispatch [:update-bot-db {:bot chat-id (dispatch [:update-bot-db {:bot bot-id
:db update-db}])) :db update-db}]))
(dispatch [:suggestions-handler (assoc params (dispatch [:suggestions-handler (assoc params
:bot-id bot-id
:result data :result data
:default-db default-db)]) :default-db default-db)])
(doseq [message log-messages] (doseq [message log-messages]

View File

@ -8,9 +8,9 @@
(defview parameter-box-container [] (defview parameter-box-container []
[{:keys [markup]} [:chat-parameter-box] [{:keys [markup]} [:chat-parameter-box]
bot-db [:current-bot-db]] bot-id-bot-db [:current-bot-db]]
(when markup (when markup
(command-utils/generate-hiccup markup bot-db))) (command-utils/generate-hiccup markup (first bot-id-bot-db) (second bot-id-bot-db))))
(defview parameter-box-view [] (defview parameter-box-view []
[show-parameter-box? [:show-parameter-box?] [show-parameter-box? [:show-parameter-box?]

View File

@ -35,7 +35,7 @@
(defn suggestions-handler! (defn suggestions-handler!
[{:keys [chats] :as db} [{:keys [chats] :as db}
[{:keys [chat-id default-db command parameter-index result]}]] [{:keys [chat-id bot-id default-db command parameter-index result]}]]
(let [{:keys [markup height] :as returned} (get-in result [:result :returned]) (let [{:keys [markup height] :as returned} (get-in result [:result :returned])
contains-markup? (contains? returned :markup) contains-markup? (contains? returned :markup)
current-input (get-in chats [chat-id :input-text]) current-input (get-in chats [chat-id :input-text])
@ -47,36 +47,35 @@
(when (and contains-markup? path (not= (get-in db path) markup)) (when (and contains-markup? path (not= (get-in db path) markup))
(dispatch [:set-in path returned]) (dispatch [:set-in path returned])
(when default-db (when default-db
(dispatch [:update-bot-db {:bot chat-id (dispatch [:update-bot-db {:bot bot-id
:db default-db}]))))) :db default-db}])))))
(defn suggestions-events-handler! (defn suggestions-events-handler!
[{:keys [current-chat-id bot-db] :as db} [[n & data :as ev] val]] [{:keys [bot-db] :as db} [bot-id [n & data :as ev] val]]
(log/debug "Suggestion event: " n (first data) val) (log/debug "Suggestion event: " n (first data) val)
(let [{:keys [dapp?]} (get-in db [:contacts/contacts current-chat-id])] (case (keyword n)
(case (keyword n) :set-command-argument
:set-command-argument (let [[index value move-to-next?] (first data)]
(let [[index value move-to-next?] (first data)] (dispatch [:set-command-argument [index value move-to-next?]]))
(dispatch [:set-command-argument [index value move-to-next?]])) :set-value
:set-value (dispatch [:set-chat-input-text (first data)])
(dispatch [:set-chat-input-text (first data)]) :set
:set (let [opts {:bot bot-id
(let [opts {:bot current-chat-id :path (mapv keyword data)
:path (mapv keyword data) :value val}]
:value val}] (dispatch [:set-in-bot-db opts]))
(dispatch [:set-in-bot-db opts])) :set-command-argument-from-db
:set-command-argument-from-db (let [[index arg move-to-next?] (first data)
(let [[index arg move-to-next?] (first data) path (keyword arg)
path (keyword arg) value (str (get-in bot-db [bot-id path]))]
value (str (get-in bot-db [current-chat-id path]))] (dispatch [:set-command-argument [index value move-to-next?]]))
(dispatch [:set-command-argument [index value move-to-next?]])) :set-value-from-db
:set-value-from-db (let [path (keyword (first data))
(let [path (keyword (first data)) value (str (get-in bot-db [bot-id path]))]
value (str (get-in bot-db [current-chat-id path]))] (dispatch [:set-chat-input-text value]))
(dispatch [:set-chat-input-text value])) :focus-input
:focus-input (dispatch [:chat-input-focus :input-ref])
(dispatch [:chat-input-focus :input-ref]) nil))
nil)))
(defn print-error-message! [message] (defn print-error-message! [message]
(fn [_ params] (fn [_ params]

View File

@ -45,31 +45,30 @@
(def events #{:onPress :onValueChange :onSlidingComplete}) (def events #{:onPress :onValueChange :onSlidingComplete})
(defn wrap-event [[_ event]] (defn wrap-event [[_ event] bot-id]
(let [data (gensym)] #(dispatch [:suggestions-event! bot-id (update event 0 keyword) %]))
#(dispatch [:suggestions-event! (update event 0 keyword) %])))
(defn check-events [m] (defn check-events [m bot-id]
(let [ks (set (keys m)) (let [ks (set (keys m))
evs (set/intersection ks events)] evs (set/intersection ks events)]
(reduce #(update %1 %2 wrap-event) m evs))) (reduce #(update %1 %2 wrap-event bot-id) m evs)))
(defn generate-hiccup (defn generate-hiccup
([markup] ([markup]
(generate-hiccup markup {})) (generate-hiccup markup nil {}))
([markup data] ([markup bot-id bot-db]
(w/prewalk (w/prewalk
(fn [el] (fn [el]
(cond (cond
(and (vector? el) (= "subscribe" (first el))) (and (vector? el) (= "subscribe" (first el)))
(let [path (mapv keyword (second el))] (let [path (mapv keyword (second el))]
(get-in data path)) (get-in bot-db path))
(and (vector? el) (string? (first el))) (and (vector? el) (string? (first el)))
(-> el (-> el
(update 0 get-element) (update 0 get-element)
(update 1 check-events)) (update 1 check-events bot-id))
:else el)) :else el))
markup))) markup)))

View File

@ -120,9 +120,9 @@
(when status (when status
(call-module #(.discardTransaction status id)))) (call-module #(.discardTransaction status id))))
(defn parse-jail [chat-id file callback] (defn parse-jail [bot-id file callback]
(when status (when status
(call-module #(.parseJail status chat-id file callback)))) (call-module #(.parseJail status bot-id file callback))))
(defn execute-call [{:keys [jail-id path params callback]}] (defn execute-call [{:keys [jail-id path params callback]}]
(when status (when status

View File

@ -30,10 +30,11 @@
(handlers/register-handler-fx (handlers/register-handler-fx
:profile/send-transaction :profile/send-transaction
[trim-v] [trim-v]
(fn [_ [chat-id]] (fn [{:keys [db]} [chat-id]]
{:dispatch [:navigate-to :chat chat-id] (let [send-command (first (get-in db [:contacts/contacts "transactor-personal" :commands :send]))]
;;TODO get rid of timeout {:dispatch [:navigate-to :chat chat-id]
:dispatch-later [{:ms 100 :dispatch [:select-chat-input-command {:name "send"}]}]})) ;;TODO get rid of timeout
:dispatch-later [{:ms 100 :dispatch [:select-chat-input-command send-command]}]})))
(handlers/register-handler-fx (handlers/register-handler-fx
:profile/send-message :profile/send-message
@ -45,9 +46,10 @@
:my-profile/update-phone-number :my-profile/update-phone-number
;; Switch user to the console issuing the !phone command automatically to let him change his phone number. ;; Switch user to the console issuing the !phone command automatically to let him change his phone number.
;; We allow to change phone number only from console because this requires entering SMS verification code. ;; We allow to change phone number only from console because this requires entering SMS verification code.
(fn [_ _] (fn [{:keys [db]} _]
{:dispatch-n [[:navigate-to :chat console-chat-id] (let [phone-command (first (get-in db [:contacts/contacts "console" :responses :phone]))]
[:select-chat-input-command {:name "phone"}]]})) {:dispatch-n [[:navigate-to :chat console-chat-id]
[:select-chat-input-command phone-command]]})))
(defn get-current-account [{:keys [:accounts/current-account-id] :as db}] (defn get-current-account [{:keys [:accounts/current-account-id] :as db}]
(get-in db [:accounts/accounts current-account-id])) (get-in db [:accounts/accounts current-account-id]))

View File

@ -4,23 +4,26 @@
[status-im.ui.screens.wallet.db :as wallet.db] [status-im.ui.screens.wallet.db :as wallet.db]
[re-frame.core :as re-frame])) [re-frame.core :as re-frame]))
(defn chat-loaded-callback [amount] (defn chat-loaded-callback [request-command]
(fn [] (fn []
(re-frame/dispatch [:select-chat-input-command {:name "request" :prefill [amount]}]) (re-frame/dispatch [:select-chat-input-command request-command])
;;TODO get rid of timeout ;;TODO get rid of timeout
(js/setTimeout #(re-frame/dispatch [:send-current-message]) 100))) (js/setTimeout #(re-frame/dispatch [:send-current-message]) 100)))
(handlers/register-handler-fx (handlers/register-handler-fx
:wallet-send-request :wallet-send-request
(fn [{{:wallet/keys [request-transaction]} :db} [_ {:keys [whisper-identity]}]] (fn [{{:wallet/keys [request-transaction] :as db} :db} [_ {:keys [whisper-identity]}]]
{:dispatch-n [[:navigate-back] (let [request-command (first (get-in db [:contacts/contacts "transactor-personal" :commands :request]))]
[:navigate-to-clean :chat-list] {:dispatch-n [[:navigate-back]
[:add-chat-loaded-callback whisper-identity (chat-loaded-callback (:amount request-transaction))] [:navigate-to-clean :chat-list]
[:start-chat whisper-identity]]})) [:add-chat-loaded-callback whisper-identity (chat-loaded-callback
(assoc request-command
:prefill [(:amount request-transaction)]))]
[:start-chat whisper-identity]]})))
(handlers/register-handler-fx (handlers/register-handler-fx
:wallet-validate-request-amount :wallet-validate-request-amount
(fn [{{:wallet/keys [request-transaction] :as db} :db} _] (fn [{{:wallet/keys [request-transaction] :as db} :db} _]
(let [amount (:amount request-transaction) (let [amount (:amount request-transaction)
error (wallet.db/get-amount-validation-error amount)] error (wallet.db/get-amount-validation-error amount)]
{:db (assoc-in db [:wallet/request-transaction :amount-error] error)}))) {:db (assoc-in db [:wallet/request-transaction :amount-error] error)})))

View File

@ -34,9 +34,9 @@
title title
content content
(clj->js (clj->js
(vector (merge {:text (i18n/label :t/no)} (vector (merge {:text (i18n/label :t/no)}
(when on-cancel {:onPress on-cancel})) (when on-cancel {:onPress on-cancel}))
{:text (i18n/label :t/yes) :onPress on-accept}))))) {:text (i18n/label :t/yes) :onPress on-accept})))))
(defn http-post (defn http-post
([action data on-success] ([action data on-success]
@ -125,3 +125,11 @@
(if (contains? m k) (if (contains? m k)
(apply update m k f args) (apply update m k f args)
m)) m))
(defn map-values
"Efficiently apply function to all map values"
[m f]
(into {}
(map (fn [[k v]]
[k (f v)]))
m))

View File

@ -0,0 +1,50 @@
(ns status-im.test.bots.events
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.bots.events :as bots-events]))
(def ^:private initial-db
{:bot-db {}
:bot-subscriptions {}
:contacts/contacts
{"bot1" {:subscriptions
{:feeExplanation
{:subscriptions {:fee ["sliderValue"]
:tx ["transaction"]}}}}
"bot2" {:subscriptions
{:roundedValue
{:subscriptions {:amount ["input"]}}}}
"bot3" {:subscriptions
{:warning
{:subscriptions {:amount ["input"]}}}}}})
(deftest add-active-bot-subscriptions-test
(testing "That active bot subscriptions are correctly transformed and added to db"
(let [db (bots-events/add-active-bot-subscriptions initial-db #{"bot1" "bot3"})]
(is (= #{"bot1" "bot3"} (-> db :bot-subscriptions keys set)))
(is (= {[:sliderValue] {:feeExplanation {:fee [:sliderValue]
:tx [:transaction]}}
[:transaction] {:feeExplanation {:fee [:sliderValue]
:tx [:transaction]}}}
(-> db :bot-subscriptions (get "bot1")))))))
(defn- fake-subscription-call
[db {:keys [chat-id parameters]}]
(let [{:keys [name subscriptions]} parameters
simulated-jail-response {:result {:returned {:sub-call-arg-map subscriptions}}}]
(bots-events/calculated-subscription db {:bot chat-id
:path [name]
:result simulated-jail-response})))
(deftest set-in-bot-db-test
(let [{:keys [db call-jail-function-n]} (-> initial-db
(bots-events/add-active-bot-subscriptions #{"bot1" "bot2"})
(bots-events/set-in-bot-db {:bot "bot1"
:path [:sliderValue]
:value 2}))
new-db (reduce fake-subscription-call db call-jail-function-n)]
(testing "That setting in value in bot-db correctly updates bot-db"
(is (= 2 (get-in new-db [:bot-db "bot1" :sliderValue]))))
(testing "That subscriptions are fired-off"
(is (= {:sub-call-arg-map {:fee 2
:tx nil}}
(get-in new-db [:bot-db "bot1" :feeExplanation]))))))

View File

@ -6,6 +6,7 @@
[status-im.test.wallet.events] [status-im.test.wallet.events]
[status-im.test.wallet.transactions.subs] [status-im.test.wallet.transactions.subs]
[status-im.test.profile.events] [status-im.test.profile.events]
[status-im.test.bots.events]
[status-im.test.chat.models.input] [status-im.test.chat.models.input]
[status-im.test.components.main-tabs] [status-im.test.components.main-tabs]
[status-im.test.i18n] [status-im.test.i18n]
@ -35,6 +36,7 @@
;;'status-im.test.contacts.events ;;'status-im.test.contacts.events
'status-im.test.profile.events 'status-im.test.profile.events
'status-im.test.wallet.events 'status-im.test.wallet.events
'status-im.test.bots.events
'status-im.test.wallet.transactions.subs 'status-im.test.wallet.transactions.subs
'status-im.test.chat.models.input 'status-im.test.chat.models.input
'status-im.test.components.main-tabs 'status-im.test.components.main-tabs

View File

@ -36,3 +36,8 @@
(is (= {:a 1} (u/update-if-present {:a 0} :a inc))) (is (= {:a 1} (u/update-if-present {:a 0} :a inc)))
(is (= {:a 2} (u/update-if-present {:a 0} :a + 2))) (is (= {:a 2} (u/update-if-present {:a 0} :a + 2)))
(is (= {:a 0} (u/update-if-present {:a 0} :b inc)))) (is (= {:a 0} (u/update-if-present {:a 0} :b inc))))
(deftest map-values-test
(is (= {} (u/map-values {} inc)))
(is (= {:a 1} (u/map-values {:a 0} inc)))
(is (= {:a 1 :b 2} (u/map-values {:a 0 :b 1} inc))))