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
(:require [re-frame.core :as re-frame]
[status-im.utils.utils :as utils]
[status-im.utils.handlers :as handlers]
[status-im.chat.models.input :as input-model]
[taoensso.timbre :as log]))
@ -12,65 +13,85 @@
{}
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
[{:keys [bot-db bot-subscriptions chats current-chat-id] :as app-db} path]
(let [owner-id (some-> app-db
(input-model/selected-chat-command current-chat-id (get-in app-db [:chats current-chat-id :input-text]))
:command
:owner-id)]
(when-let [subscriptions (and owner-id (get-in bot-subscriptions (concat [owner-id] [path])))]
{:call-jail-function-n
(for [[sub-name sub-params] subscriptions]
{:chat-id owner-id
:function :subscription
:parameters {:name sub-name
:subscriptions (subscription-values sub-params
(get bot-db current-chat-id))}
:callback-events-creator (fn [jail-response]
[[::calculated-subscription
{:bot current-chat-id
:path [sub-name]
:result jail-response}]])})})))
[{:keys [bot-db bot-subscriptions] :as app-db} {:keys [bot path]}]
(when-let [subscriptions (and bot (get-in bot-subscriptions (concat [bot] [path])))]
{:call-jail-function-n
(for [[sub-name sub-params] subscriptions]
{:chat-id bot
:function :subscription
:parameters {:name sub-name
:subscriptions (subscription-values sub-params (get bot-db bot))}
:callback-events-creator (fn [jail-response]
[[::calculated-subscription
{:bot bot
:path [sub-name]
:result jail-response}]])})}))
(defn set-in-bot-db
[{:keys [current-chat-id] :as app-db} {:keys [bot path value] :as params}]
(let [bot (or bot current-chat-id)
new-db (assoc-in app-db (concat [:bot-db bot] path) value)]
"Associates value at specified path in bot-db and checks if there are any subscriptions
dependent on that path, if yes, adds effects for calling jail-functions to recalculate
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}
(check-subscriptions-fx new-db path))))
(check-subscriptions-fx new-db params))))
(defn update-bot-db
[{:keys [current-chat-id] :as app-db} {:keys [bot db]}]
(let [bot (or bot current-chat-id)]
(update-in app-db [:bot-db bot] merge db)))
[app-db {:keys [bot db]}]
(update-in app-db [:bot-db bot] merge db))
(defn clear-bot-db
[{:keys [current-chat-id] :as app-db}]
(assoc-in app-db [:bot-db current-chat-id] nil))
[app-db bot-id]
(assoc-in app-db [:bot-db bot-id] nil))
(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
"Add subscriptions for selected bot identities into app-db"
[app-db bot-identities]
(let [relevant-bots (select-keys (:contacts/contacts app-db) bot-identities)
active-subscriptions (reduce (fn [acc [bot-id {:keys [subscriptions]}]]
(reduce (fn [acc [sub-key {:keys [subscriptions]}]]
(reduce (fn [acc [sub-param-key sub-param-path]]
(update-in acc [bot-id (keywordize-vector sub-param-path)]
assoc sub-key (into {}
(map (fn [[k v]]
[k (keywordize-vector v)]))
subscriptions)))
acc
subscriptions))
acc
subscriptions))
{}
relevant-bots)]
(assoc app-db :bot-subscriptions active-subscriptions)))
(assoc app-db :bot-subscriptions (-> app-db
:contacts/contacts
(select-keys bot-identities)
(utils/map-values (comp transform-bot-subscriptions :subscriptions)))))
(defn calculated-subscription
[db {:keys [bot path]
{:keys [error result]} :result}]
(if error
db
(assoc-in db (concat [:bot-db bot] path) (:returned result))))
;;;; Handlers
@ -89,8 +110,5 @@
(handlers/register-handler-db
::calculated-subscription
[re-frame/trim-v]
(fn [db [{:keys [bot path]
{:keys [error result]} :result}]]
(if error
db
(assoc-in db (concat [:bot-db bot] path) (:returned result)))))
(fn [db [params]]
(calculated-subscription db params)))

View File

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

View File

@ -11,27 +11,21 @@
[status-im.data-store.messages :as msg-store]))
(re-frame/reg-cofx
:pop-up-chat?
(fn [cofx]
(assoc cofx :pop-up-chat? (fn [chat-id]
(or (not (chat-store/exists? chat-id))
(chat-store/is-active? chat-id))))))
:pop-up-chat?
(fn [cofx]
(assoc cofx :pop-up-chat? (fn [chat-id]
(or (not (chat-store/exists? chat-id))
(chat-store/is-active? chat-id))))))
(re-frame/reg-cofx
:message-exists?
(fn [cofx]
(assoc cofx :message-exists? msg-store/exists?)))
:message-exists?
(fn [cofx]
(assoc cofx :message-exists? msg-store/exists?)))
(re-frame/reg-cofx
:get-last-clock-value
(fn [cofx]
(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))))
:get-last-clock-value
(fn [cofx]
(assoc cofx :get-last-clock-value msg-store/get-last-clock-value)))
(defn- get-current-identity
[{:accounts/keys [accounts current-account-id]}]
@ -39,7 +33,7 @@
(defn add-message
[{: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
message-id timestamp clock-value]
:as message
@ -57,7 +51,7 @@
(get-last-stored-message chat-identifier)
message)
:chat-id chat-identifier
:timestamp (or timestamp current-timestamp)
:timestamp (or timestamp now)
:clock-value (clocks/receive
clock-value
(get-last-clock-value chat-identifier)))]
@ -68,8 +62,7 @@
(assoc-in [:chats chat-identifier :last-message] message))
:dispatch-n [[:upsert-chat! {:chat-id chat-identifier
:group-chat group-chat?}]
[:request-command-message-data enriched-message :short-preview]
[:update-suggestions]]
[:request-command-message-data enriched-message :short-preview]]
:save-message (dissoc enriched-message :new?)}
(get-in enriched-message [:content :command])
@ -77,7 +70,7 @@
(= (:content-type enriched-message) const/content-type-command-request)
(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
(update :dispatch-n conj [:update-suggestions])))
{:db db})))
@ -85,8 +78,7 @@
(def ^:private receive-interceptors
[(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 :current-timestamp) (re-frame/inject-cofx :random-id)
re-frame/trim-v])
(re-frame/inject-cofx :random-id) re-frame/trim-v])
(handlers/register-handler-fx
:received-protocol-message!

View File

@ -218,15 +218,16 @@
(register-handler :received-bot-response
(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
{:keys [markup text-message err]} returned
{:keys [log-messages update-db default-db]} context
content (or err text-message)]
(when update-db
(dispatch [:update-bot-db {:bot chat-id
(dispatch [:update-bot-db {:bot bot-id
:db update-db}]))
(dispatch [:suggestions-handler (assoc params
:bot-id bot-id
:result data
:default-db default-db)])
(doseq [message log-messages]

View File

@ -8,9 +8,9 @@
(defview parameter-box-container []
[{:keys [markup]} [:chat-parameter-box]
bot-db [:current-bot-db]]
bot-id-bot-db [:current-bot-db]]
(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 []
[show-parameter-box? [:show-parameter-box?]

View File

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

View File

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

View File

@ -120,9 +120,9 @@
(when status
(call-module #(.discardTransaction status id))))
(defn parse-jail [chat-id file callback]
(defn parse-jail [bot-id file callback]
(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]}]
(when status

View File

@ -30,10 +30,11 @@
(handlers/register-handler-fx
:profile/send-transaction
[trim-v]
(fn [_ [chat-id]]
{:dispatch [:navigate-to :chat chat-id]
;;TODO get rid of timeout
:dispatch-later [{:ms 100 :dispatch [:select-chat-input-command {:name "send"}]}]}))
(fn [{:keys [db]} [chat-id]]
(let [send-command (first (get-in db [:contacts/contacts "transactor-personal" :commands :send]))]
{:dispatch [:navigate-to :chat chat-id]
;;TODO get rid of timeout
:dispatch-later [{:ms 100 :dispatch [:select-chat-input-command send-command]}]})))
(handlers/register-handler-fx
:profile/send-message
@ -45,9 +46,10 @@
:my-profile/update-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.
(fn [_ _]
{:dispatch-n [[:navigate-to :chat console-chat-id]
[:select-chat-input-command {:name "phone"}]]}))
(fn [{:keys [db]} _]
(let [phone-command (first (get-in db [:contacts/contacts "console" :responses :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}]
(get-in db [:accounts/accounts current-account-id]))

View File

@ -4,23 +4,26 @@
[status-im.ui.screens.wallet.db :as wallet.db]
[re-frame.core :as re-frame]))
(defn chat-loaded-callback [amount]
(defn chat-loaded-callback [request-command]
(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
(js/setTimeout #(re-frame/dispatch [:send-current-message]) 100)))
(handlers/register-handler-fx
:wallet-send-request
(fn [{{:wallet/keys [request-transaction]} :db} [_ {:keys [whisper-identity]}]]
{:dispatch-n [[:navigate-back]
[:navigate-to-clean :chat-list]
[:add-chat-loaded-callback whisper-identity (chat-loaded-callback (:amount request-transaction))]
[:start-chat whisper-identity]]}))
(fn [{{:wallet/keys [request-transaction] :as db} :db} [_ {:keys [whisper-identity]}]]
(let [request-command (first (get-in db [:contacts/contacts "transactor-personal" :commands :request]))]
{:dispatch-n [[:navigate-back]
[:navigate-to-clean :chat-list]
[:add-chat-loaded-callback whisper-identity (chat-loaded-callback
(assoc request-command
:prefill [(:amount request-transaction)]))]
[:start-chat whisper-identity]]})))
(handlers/register-handler-fx
:wallet-validate-request-amount
(fn [{{:wallet/keys [request-transaction] :as db} :db} _]
(let [amount (:amount request-transaction)
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
content
(clj->js
(vector (merge {:text (i18n/label :t/no)}
(when on-cancel {:onPress on-cancel}))
{:text (i18n/label :t/yes) :onPress on-accept})))))
(vector (merge {:text (i18n/label :t/no)}
(when on-cancel {:onPress on-cancel}))
{:text (i18n/label :t/yes) :onPress on-accept})))))
(defn http-post
([action data on-success]
@ -125,3 +125,11 @@
(if (contains? m k)
(apply update m k f args)
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.transactions.subs]
[status-im.test.profile.events]
[status-im.test.bots.events]
[status-im.test.chat.models.input]
[status-im.test.components.main-tabs]
[status-im.test.i18n]
@ -35,6 +36,7 @@
;;'status-im.test.contacts.events
'status-im.test.profile.events
'status-im.test.wallet.events
'status-im.test.bots.events
'status-im.test.wallet.transactions.subs
'status-im.test.chat.models.input
'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 2} (u/update-if-present {:a 0} :a + 2)))
(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))))