Refactor adding subscriptions + basic tests
This commit is contained in:
parent
df83cbb987
commit
55164b8eac
|
@ -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)))))
|
|
||||||
|
|
|
@ -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])])))
|
||||||
|
|
|
@ -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})
|
||||||
|
|
|
@ -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!
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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?]
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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)))
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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]))
|
||||||
|
|
|
@ -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)})))
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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]))))))
|
|
@ -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
|
||||||
|
|
|
@ -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))))
|
||||||
|
|
Loading…
Reference in New Issue