Fix bot subscriptions

This commit is contained in:
janherich 2017-10-20 14:04:24 +02:00 committed by Roman Volosovskyi
parent abe7f0a42a
commit 0ccfefa6c3
8 changed files with 116 additions and 107 deletions

View File

@ -95,7 +95,7 @@ function amountParameterBox(params, context) {
sliderValue: sliderValue sliderValue: sliderValue
}); });
}catch (err){ } catch (err) {
status.setDefaultDb({ status.setDefaultDb({
transaction: txData, transaction: txData,

View File

@ -35,7 +35,7 @@ status.defineSubscription(
function(params) { function(params) {
return getFeeExplanation(params.value); return getFeeExplanation(params.value);
} }
) );
function amountParameterBox(params, context) { function amountParameterBox(params, context) {
if (!params["bot-db"]) { if (!params["bot-db"]) {
@ -62,13 +62,25 @@ function amountParameterBox(params, context) {
var sliderValue = params["bot-db"]["sliderValue"] || 0; var sliderValue = params["bot-db"]["sliderValue"] || 0;
status.setDefaultDb({ try {
transaction: txData,
calculatedFee: calculateFee(sliderValue, txData),
feeExplanation: getFeeExplanation(sliderValue),
sliderValue: sliderValue
});
status.setDefaultDb({
transaction: txData,
calculatedFee: calculateFee(sliderValue, txData),
feeExplanation: getFeeExplanation(sliderValue),
sliderValue: sliderValue
});
} catch (err) {
status.setDefaultDb({
transaction: txData,
calculatedFee: "0",
feeExplanation: "",
sliderValue: sliderValue
});
}
return { return {
title: I18n.t('send_title'), title: I18n.t('send_title'),
showBack: true, showBack: true,

View File

@ -1,54 +1,45 @@
(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.handlers :as handlers])) [status-im.utils.handlers :as handlers]
[status-im.chat.models.input :as input-model]
[taoensso.timbre :as log]))
;;;; Helper fns ;;;; Helper fns
(defn- chats-with-bot [chats bot] (defn- subscription-values [sub-params current-bot-db]
(reduce (fn [acc [_ {:keys [chat-id contacts]}]] (reduce (fn [sub-values [sub-key sub-path]]
(let [contacts (map :identity contacts)] (assoc sub-values sub-key (get-in current-bot-db sub-path)))
(if (some #{bot} contacts)
(conj acc chat-id)
acc)))
[]
chats))
(defn- subscription-values [subscriptions current-bot-db]
(reduce (fn [sub-values [sub-name sub-path]]
(assoc sub-values sub-name (get-in current-bot-db sub-path)))
{} {}
subscriptions)) sub-params))
;; TODO(janherich): optimze this, for sure we don't need to re-calculate all bot subscriptions every time something in bot db changes ;; 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
[db {:keys [bot path value]}] [{:keys [bot-db bot-subscriptions chats current-chat-id] :as app-db} path]
(let [{:keys [bot-db chats]} db (let [owner-id (some-> app-db
subscriptions (get-in db [:bot-subscriptions 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 [{:keys [bot subscriptions name]} subscriptions :owner-id)]
:let [subs-values (subscription-values subscriptions (get bot-db bot))]] (when-let [subscriptions (and owner-id (get-in bot-subscriptions (concat [owner-id] [path])))]
{:chat-id bot {:call-jail-function-n
:function :subscription (for [[sub-name sub-params] subscriptions]
:parameters {:name name {:chat-id owner-id
:subscriptions subs-values} :function :subscription
:callback-events-creator (fn [jail-response] :parameters {:name sub-name
(into [[::calculated-subscription :subscriptions (subscription-values sub-params
{:bot bot (get bot-db current-chat-id))}
:path [name] :callback-events-creator (fn [jail-response]
:result jail-response}]] [[::calculated-subscription
(map (fn [chat-id] {:bot current-chat-id
[::calculated-subscription :path [sub-name]
{:bot chat-id :result jail-response}]])})})))
:path [name]
:result jail-response}])
(chats-with-bot chats bot))))})}))
(defn set-in-bot-db (defn set-in-bot-db
[{:keys [current-chat-id] :as app-db} {:keys [bot path value] :as params}] [{:keys [current-chat-id] :as app-db} {:keys [bot path value] :as params}]
(let [bot (or bot current-chat-id) (let [bot (or bot current-chat-id)
new-db (assoc-in app-db (concat [:bot-db bot] path) value)] 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 params)))) (check-subscriptions-fx new-db path))))
(defn update-bot-db (defn update-bot-db
[{:keys [current-chat-id] :as app-db} {:keys [bot db]}] [{:keys [current-chat-id] :as app-db} {:keys [bot db]}]
@ -59,6 +50,28 @@
[{:keys [current-chat-id] :as app-db}] [{:keys [current-chat-id] :as app-db}]
(assoc-in app-db [:bot-db current-chat-id] nil)) (assoc-in app-db [:bot-db current-chat-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 add-active-bot-subscriptions
[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)))
;;;; Handlers ;;;; Handlers
(handlers/register-handler-fx (handlers/register-handler-fx
@ -73,21 +86,6 @@
(fn [db [params]] (fn [db [params]]
(update-bot-db db params))) (update-bot-db db params)))
(handlers/register-handler-db
:register-bot-subscription
[re-frame/trim-v]
(fn [db [{:keys [bot subscriptions] :as opts}]]
(reduce
(fn [db [sub-name sub-path]]
(let [keywordized-sub-path (mapv keyword
(if (coll? sub-path)
sub-path
[sub-path]))]
(update-in db [:bot-subscriptions keywordized-sub-path] conj
(assoc-in opts [:subscriptions sub-name] keywordized-sub-path))))
db
subscriptions)))
(handlers/register-handler-db (handlers/register-handler-db
::calculated-subscription ::calculated-subscription
[re-frame/trim-v] [re-frame/trim-v]

View File

@ -1,12 +1,6 @@
(ns status-im.bots.subs (ns status-im.bots.subs
(:require [re-frame.core :refer [reg-sub subscribe]])) (:require [re-frame.core :refer [reg-sub subscribe]]))
(reg-sub
:bot-subscription
(fn [db [_ path]]
(let [chat-id (subscribe [:get-current-chat-id])]
(get-in db (concat [:bot-db @chat-id] path)))))
(reg-sub (reg-sub
:current-bot-db :current-bot-db
(fn [db] (fn [db]

View File

@ -46,6 +46,10 @@
;;;; Helper functions ;;;; Helper functions
(defn- extract-command-request-owners [commands requests]
[commands requests]
(into #{} (keep :owner-id) (concat commands requests)))
(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 map with keys :db (new db with up-to-date suggestions) and (optionally)
@ -58,9 +62,14 @@
(= type :grant-permissions)))) (= type :grant-permissions))))
commands (commands-model/commands-for-chat db current-chat-id) commands (commands-model/commands-for-chat db current-chat-id)
{:keys [dapp?]} (get-in db [:contacts/contacts current-chat-id]) {:keys [dapp?]} (get-in db [:contacts/contacts current-chat-id])
new-db (cond-> db ;; TODO(janherich) surely there is a better place to merge in possible commands/request/subscriptions into current chat
true (assoc-in [:chats current-chat-id :possible-requests] requests) ;; then in `:update-suggestions` which is called whenever commands for chat are loaded, chat view is opened
true (assoc-in [:chats current-chat-id :possible-commands] commands) ;; or new message is received from network - it's unnecessary to call it as a response to last two events
new-db (cond-> (-> db
(update-in [:chats current-chat-id] merge {:possible-commands commands
:possible-requests requests})
(bots-events/add-active-bot-subscriptions (extract-command-request-owners
commands requests)))
(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))]
@ -90,12 +99,12 @@
(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]) ""))
{:keys [dapp?]} (get-in db [:contacts/contacts current-chat-id])] {:keys [dapp?]} (get-in db [:contacts/contacts current-chat-id])]
(cond-> {:db db} (cond-> {:db db}
(and dapp? (not (str/blank? chat-text))) (and dapp? (not (str/blank? chat-text)))
(assoc :call-jail-function {:chat-id current-chat-id (assoc :call-jail-function {:chat-id current-chat-id
:function :on-message-input-change :function :on-message-input-change
:parameters {:message chat-text} :parameters {:message chat-text}
:context {:data (get local-storage current-chat-id) :context {:data (get local-storage current-chat-id)
:from current-account-id}})))) :from current-account-id}}))))
(defn set-chat-input-metadata (defn set-chat-input-metadata
"Set input metadata for active chat. Takes db and metadata and returns updated db." "Set input metadata for active chat. Takes db and metadata and returns updated db."
@ -193,14 +202,14 @@
(load-chat-parameter-box new-db (:command command)))] (load-chat-parameter-box new-db (:command command)))]
(cond-> {:db new-db} (cond-> {:db new-db}
chat-parameter-box-fx chat-parameter-box-fx
(merge chat-parameter-box-fx) (merge chat-parameter-box-fx)
(and (= selection (+ (count const/command-char) (and (= selection (+ (count const/command-char)
(count (get-in command [:command :name])) (count (get-in command [:command :name]))
(count const/spacing-char))) (count const/spacing-char)))
(get-in command [:command :sequential-params])) (get-in command [:command :sequential-params]))
(merge (chat-input-focus new-db :seq-input-ref))))) (merge (chat-input-focus new-db :seq-input-ref)))))
(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"
@ -221,19 +230,19 @@
(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})
(not (and sequential-params (not (and sequential-params
prevent-auto-focus?)) prevent-auto-focus?))
(merge (chat-input-focus (:db fx) :input-ref)) (merge (chat-input-focus (:db fx) :input-ref))
sequential-params sequential-params
(as-> fx' (as-> fx'
(cond-> (update fx' :db (cond-> (update fx' :db
set-chat-seq-arg-input-text set-chat-seq-arg-input-text
(str/join const/spacing-char prefill)) (str/join const/spacing-char prefill))
(not prevent-auto-focus?) (not prevent-auto-focus?)
(merge fx' (chat-input-focus (:db fx') :seq-input-ref))))))) (merge fx' (chat-input-focus (:db fx') :seq-input-ref)))))))
(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"

View File

@ -3,7 +3,7 @@
[status-im.utils.handlers :as handlers] [status-im.utils.handlers :as handlers]
[status-im.utils.utils :refer [show-popup]] [status-im.utils.utils :refer [show-popup]]
[status-im.utils.types :refer [json->clj]] [status-im.utils.types :refer [json->clj]]
[status-im.commands.utils :refer [generate-hiccup reg-handler]] [status-im.commands.utils :refer [reg-handler]]
[clojure.string :as s] [clojure.string :as s]
[status-im.components.react :as r] [status-im.components.react :as r]
[status-im.constants :refer [console-chat-id]] [status-im.constants :refer [console-chat-id]]

View File

@ -182,13 +182,7 @@
(reg-handler ::add-commands (reg-handler ::add-commands
[(after (fn [_ [id]] [(after (fn [_ [id]]
(dispatch [:invoke-commands-loading-callbacks id]) (dispatch [:invoke-commands-loading-callbacks id])
(dispatch [:invoke-chat-loaded-callbacks id]))) (dispatch [:invoke-chat-loaded-callbacks id])))
(after (fn [{:contacts/keys [contacts]} [id]]
(let [subscriptions (get-in contacts [id :subscriptions])]
(doseq [[name opts] subscriptions]
(dispatch [:register-bot-subscription
(assoc opts :bot id
:name name)])))))
(after #(dispatch [:update-suggestions]))] (after #(dispatch [:update-suggestions]))]
add-commands) add-commands)

View File

@ -4,7 +4,7 @@
(:require [status-im.components.react :as r] (:require [status-im.components.react :as r]
[re-frame.core :refer [dispatch]] [re-frame.core :refer [dispatch]]
[taoensso.timbre :as log] [taoensso.timbre :as log]
[cljs.core.async :refer [<! timeout]] [cljs.core.async :as async :refer [<! timeout]]
[status-im.utils.js-resources :as js-res] [status-im.utils.js-resources :as js-res]
[status-im.utils.platform :as p] [status-im.utils.platform :as p]
[status-im.utils.scheduler :as scheduler] [status-im.utils.scheduler :as scheduler]
@ -126,9 +126,10 @@
(defn execute-call [{:keys [jail-id path params callback]}] (defn execute-call [{:keys [jail-id path params callback]}]
(when status (when status
(call-module (call-module
#(do #(do
(log/debug :call-jail :jail-id jail-id) (log/debug :call-jail :jail-id jail-id)
(log/debug :call-jail :path path) (log/debug :call-jail :path path)
(log/debug :call-jail :params params)
;; this debug message can contain sensitive info ;; this debug message can contain sensitive info
#_(log/debug :call-jail :params params) #_(log/debug :call-jail :params params)
(let [params' (update params :context assoc (let [params' (update params :context assoc
@ -154,7 +155,8 @@
(defn remove-duplicate-calls (defn remove-duplicate-calls
"Removes duplicates by [jail path] keys, remains the last one." "Removes duplicates by [jail path] keys, remains the last one."
[[all-keys calls] {:keys [jail-id path] :as call}] [[all-keys calls] {:keys [jail-id path] :as call}]
(if (contains? all-keys [jail-id path]) (if (and (contains? all-keys [jail-id path])
(not= (second path) :subscription))
[all-keys calls] [all-keys calls]
[(conj all-keys [jail-id path]) [(conj all-keys [jail-id path])
(conj calls call)])) (conj calls call)]))
@ -167,7 +169,7 @@
(let [[_ new-calls] (reduce remove-duplicate-calls [#{} '()] @raw-jail-calls)] (let [[_ new-calls] (reduce remove-duplicate-calls [#{} '()] @raw-jail-calls)]
(reset! raw-jail-calls '()) (reset! raw-jail-calls '())
(swap! jail-calls (fn [old-calls] (swap! jail-calls (fn [old-calls]
(concat new-calls old-calls)))) (concat new-calls old-calls))))
(recur (<! (timeout check-raw-calls-interval))))) (recur (<! (timeout check-raw-calls-interval)))))
(defn execute-calls-loop! (defn execute-calls-loop!
@ -175,8 +177,8 @@
which reduces chances of response shuffling" which reduces chances of response shuffling"
[] []
(go-loop [_ nil] (go-loop [_ nil]
(let [next-call (last @jail-calls)] (let [next-call (first @jail-calls)]
(swap! jail-calls butlast) (swap! jail-calls rest)
(when next-call (when next-call
(execute-call next-call))) (execute-call next-call)))
(recur (<! (timeout interval-between-calls))))) (recur (<! (timeout interval-between-calls)))))