[fix #1604]: Commands send, request, location are sent several times if quickly tap on send button

Signed-off-by: Dmitry Novotochinov <trybeee@gmail.com>
This commit is contained in:
Alexander Pantyukhov 2018-01-10 09:52:54 +01:00 committed by Dmitry Novotochinov
parent 6dd585f3e0
commit 74f9ea32a3
No known key found for this signature in database
GPG Key ID: 267674DCC86628D9
16 changed files with 466 additions and 505 deletions

View File

@ -21,6 +21,7 @@
status-im.chat.events.commands
status-im.chat.events.requests
status-im.chat.events.animation
status-im.chat.events.send-message
status-im.chat.events.queue-message
status-im.chat.events.receive-message
status-im.chat.events.sign-up
@ -55,6 +56,16 @@
(fn [cofx _]
(assoc cofx :get-stored-chat chats-store/get-by-id)))
(re-frame/reg-cofx
:message-exists?
(fn [cofx]
(assoc cofx :message-exists? messages-store/exists?)))
(re-frame/reg-cofx
:get-last-clock-value
(fn [cofx]
(assoc cofx :get-last-clock-value messages-store/get-last-clock-value)))
;;;; Effects
(def ^:private update-message-queue (async/chan 100))
@ -208,7 +219,7 @@
:update-message {:message-id message-id
:user-statuses statuses}}
;; for public chats and 1-1 bot/dapp chats, it makes no sense to signalise `:seen` msg
(not (or public? (get-in contacts [chat-id] :dapp?)))
(not (or public? (get-in contacts [chat-id :dapp?])))
(assoc :protocol-send-seen {:web3 web3
:message (cond-> {:from me
:to from
@ -224,6 +235,7 @@
(mapv #(vector :chat-received-message/add %)))]
{:dispatch-n messages-events})))
;; TODO(alwx): can be simplified
(handlers/register-handler-fx
:account-generation-message
[(re-frame/inject-cofx :get-stored-message)]

View File

@ -91,24 +91,3 @@
(def commands-with-delivery-status
(disj commands-names "password" "faucet" "debug"))
;;;; Handlers
;; TODO(janherich) remove this once send-message events are refactored
(handlers/register-handler-fx
:invoke-console-command-handler!
[re-frame/trim-v (re-frame/inject-cofx :random-id)]
(fn [cofx [{:keys [chat-id command] :as command-params}]]
(let [fx-fn (get console-commands->fx (-> command :command :name))]
(-> cofx
(fx-fn command)
(update :dispatch-n (fnil conj []) [:prepare-command! chat-id command-params])))))
;; TODO(janherich) remove this once send-message events are refactored
(handlers/register-handler-fx
:console-respond-command
[(re-frame/inject-cofx :random-id-seq) re-frame/trim-v]
(fn [{:keys [random-id-seq]} [command]]
(when-let [messages (console-respond-command-messages command random-id-seq)]
(let [events (mapv #(vector :chat-received-message/add %) messages)]
{:dispatch-n events}))))

View File

@ -6,9 +6,10 @@
[status-im.chat.utils :as chat-utils]
[status-im.chat.models :as model]
[status-im.chat.models.input :as input-model]
[status-im.chat.models.commands :as commands-model]
[status-im.chat.models.message :as message-model]
[status-im.chat.events.commands :as commands-events]
[status-im.chat.events.animation :as animation-events]
[status-im.chat.events.send-message :as send-message-events]
[status-im.bots.events :as bots-events]
[status-im.ui.components.react :as react-comp]
[status-im.utils.datetime :as time]
@ -149,7 +150,7 @@
:path path
:params params
:callback-events-creator (fn [jail-response]
[[:received-bot-response
[[:chat-received-message/bot-response
{:chat-id current-chat-id
:command command
:parameter-index parameter-index}
@ -406,20 +407,20 @@
(handlers/register-handler-fx
::send-command
[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 (-> db
clear-seq-arguments
(set-chat-input-metadata nil)
(set-chat-input-text nil)
(model/set-chat-ui-props {:sending-in-progress? false}))
;; 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])
message-model/send-interceptors
(fn [cofx [{:keys [command] :as command-message}]]
(let [{{:keys [current-public-key current-chat-id]
:accounts/keys [current-account-id] :as db} :db} cofx
fx (message-model/process-command cofx
{: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}]}))
:address current-account-id})]
(update fx :db #(-> %
(clear-seq-arguments)
(set-chat-input-metadata nil)
(set-chat-input-text nil))))))
(handlers/register-handler-fx
::check-command-type
@ -464,7 +465,7 @@
"no command detected, when not empty, proceed by sending text message without command processing"
[db cofx input-text current-chat-id current-public-key]
(when-not (string/blank? input-text)
(send-message-events/prepare-message (assoc cofx :db (-> db
(message-model/send-message (assoc cofx :db (-> db
(set-chat-input-metadata nil)
(set-chat-input-text nil)))
{:message-text input-text
@ -472,6 +473,7 @@
:identity current-public-key
:address (:accounts/current-account-id db)})))
(handlers/register-handler-fx
:send-current-message
[(re-frame/inject-cofx :random-id)

View File

@ -1,11 +1,13 @@
(ns status-im.chat.events.receive-message
(:require [re-frame.core :as re-frame]
[taoensso.timbre :as log]
[status-im.utils.handlers :as handlers]
[status-im.data-store.chats :as chat-store]
[status-im.data-store.messages :as messages-store]
[status-im.chat.events.commands :as commands-events]
[status-im.chat.models.message :as message-model]))
[status-im.chat.models.message :as message-model]
[status-im.constants :as constants]
[status-im.utils.handlers :as handlers]
[status-im.utils.random :as random]))
;;;; Coeffects
@ -16,16 +18,6 @@
(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? messages-store/exists?)))
(re-frame/reg-cofx
:get-last-clock-value
(fn [cofx]
(assoc cofx :get-last-clock-value messages-store/get-last-clock-value)))
;;;; FX
(handlers/register-handler-fx
@ -67,3 +59,41 @@
(get-in db [:contacts/contacts chat-id :jail-loaded?]))
(message-model/receive cofx message)
{:dispatch-later [{:ms 400 :dispatch [:chat-received-message/add-when-commands-loaded message]}]})))
;; TODO(alwx): refactor this when status-im.commands.handlers.jail is refactored
(handlers/register-handler-fx
:chat-received-message/bot-response
(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
(re-frame/dispatch [:update-bot-db {:bot bot-id
:db update-db}]))
(re-frame/dispatch [:suggestions-handler (assoc params
:bot-id bot-id
:result data
:default-db default-db)])
(doseq [message log-messages]
(let [{:keys [message type]} message]
(when (or (not= type "debug")
js/goog.DEBUG
(get-in contacts [chat-id :debug?]))
(re-frame/dispatch [:chat-received-message/add
{:message-id (random/id)
:content (str type ": " message)
:content-type constants/content-type-log-message
:outgoing false
:chat-id chat-id
:from chat-id
:to "me"}]))))
(when content
(re-frame/dispatch [:chat-received-message/add
{:message-id (random/id)
:content (str content)
:content-type constants/text-content-type
:outgoing false
:chat-id chat-id
:from chat-id
:to "me"}])))))

View File

@ -12,7 +12,7 @@
;; Effects
(re-frame/reg-fx
::request-answered
:chat-requests/mark-as-answered
(fn [{:keys [chat-id message-id]}]
(requests-store/mark-as-answered chat-id message-id)))
@ -28,7 +28,7 @@
[re-frame/trim-v]
(fn [{:keys [db]} [chat-id message-id]]
{:db (update-in db [:chats chat-id :requests] dissoc message-id)
::request-answered {:chat-id chat-id
:chat-requests/mark-as-answered {:chat-id chat-id
:message-id message-id}}))
(defn add-request

View File

@ -1,103 +1,58 @@
(ns status-im.chat.events.send-message
(:require [re-frame.core :as re-frame]
[status-im.utils.handlers :as handlers]
[status-im.chat.utils :as chat-utils]
[status-im.chat.models.message :as message-model]
[status-im.constants :as constants]
[status-im.utils.clocks :as clocks]
[status-im.utils.config :as config]
[status-im.chat.models :as chat.models]
[status-im.chat.utils :as chat.utils]
[status-im.protocol.core :as protocol]
[status-im.data-store.chats :as chats-store]
[status-im.native-module.core :as status]
[status-im.protocol.core :as protocol]
[status-im.utils.config :as config]
[status-im.utils.handlers :as handlers]
[status-im.utils.random :as random]
[status-im.utils.types :as types]
[status-im.utils.datetime :as datetime]
[taoensso.timbre :as log]))
(re-frame/reg-fx
::send-notification
:send-notification
(fn [fcm-token]
(log/debug "send-notification fcm-token: " fcm-token)
(status/notify fcm-token #(log/debug "send-notification cb result: " %))))
(re-frame/reg-fx
::send-message
(fn [message]
(protocol/send-message! message)))
:send-group-message
(fn [value]
(protocol/send-group-message! value)))
(re-frame/reg-fx
::send-group-message
(fn [message]
(protocol/send-group-message! message)))
:send-public-group-message
(fn [value]
(protocol/send-public-group-message! value)))
(re-frame/reg-fx
::send-public-group-message
(fn [message]
(protocol/send-public-group-message! message)))
:send-message
(fn [value]
(protocol/send-message! value)))
(defn- send-message
[{:keys [web3 network-status local-storage chats]
:contacts/keys [contacts]
:accounts/keys [accounts current-account-id]
:as db}
{:keys [message-type content from chat-id to] :as message}]
(let [{:keys [dapp? fcm-token]} (get contacts chat-id)
{:keys [public-key private-key]} (get chats chat-id)
sender-name (get-in accounts [current-account-id :name])]
;; whenever we are sending message to DApp, we are assuming it's a status bot,
;; so we are just calling jail `on-message-send` function
(when message
(if dapp?
{:call-jail-function {:chat-id chat-id
:function :on-message-send
:parameters {:message content}
:content {:data (get local-storage chat-id)
:from from}}}
(let [payload (select-keys message [:timestamp :content :content-type
:clock-value :show?])
message-to-send {:web3 web3
:message (-> (select-keys message [:message-id :from])
(assoc :payload (if (= :offline network-status)
(assoc payload :show? false)
payload)))}]
(case message-type
:group-user-message {::send-group-message
(assoc message-to-send
:group-id chat-id
:keypair {:public public-key
:private private-key})}
:public-group-user-message {::send-public-group-message
(assoc message-to-send
:group-id chat-id
:username sender-name)}
:user-message (cond-> {::send-message
(assoc-in message-to-send
[:message :to] to)}
fcm-token (assoc ::send-notification fcm-token))))))))
(re-frame/reg-fx
:update-message-overhead!
(fn [[chat-id network-status]]
(if (= network-status :offline)
(chats-store/inc-message-overhead chat-id)
(chats-store/reset-message-overhead chat-id))))
(defn prepare-message
[{:keys [db now random-id get-last-clock-value] :as cofx}
{:keys [chat-id identity message-text] :as params}]
(let [{:keys [group-chat public?]} (get-in db [:chats chat-id])
message (cond-> {:message-id random-id
:chat-id chat-id
:content message-text
:from identity
:content-type constants/text-content-type
:outgoing true
:timestamp now
:clock-value (clocks/send (get-last-clock-value chat-id))
:show? true}
(not group-chat)
(assoc :message-type :user-message
:to chat-id)
group-chat
(assoc :group-id chat-id)
(and group-chat public?)
(assoc :message-type :public-group-user-message)
(and group-chat (not public?))
(assoc :message-type :group-user-message))]
(as-> (chat.models/upsert-chat cofx {:chat-id chat-id})
fx (merge fx
{:db (chat.utils/add-message-to-db (:db fx) chat-id chat-id message)
:save-message message
;; TODO janherich - get rid of this, there is absolutely no reason why it can't be just
;; plain app-db inc + chat update
:dispatch [:update-message-overhead! chat-id (:network-status db)]}
(send-message (:db fx) message)))))
;;;; Handlers
(handlers/register-handler-fx
:chat-send-message/send-command
message-model/send-interceptors
(fn [cofx [add-to-chat-id params]]
(message-model/send-command cofx nil add-to-chat-id params)))
(handlers/register-handler-fx
:chat-send-message/from-jail
[re-frame/trim-v]
(fn [cofx [{:keys [chat-id message]}]]
(let [parsed-message (types/json->clj message)]
(message-model/handle-message-from-bot cofx {:message parsed-message
:chat-id chat-id}))))

View File

@ -13,8 +13,7 @@
console-chat-id]]
[status-im.utils.random :as random]
[status-im.utils.handlers :refer [register-handler register-handler-fx] :as u]
status-im.chat.events
status-im.chat.handlers.send-message))
status-im.chat.events))
(defn remove-chat
[db [_ chat-id]]
@ -94,14 +93,6 @@
:keypair keypair
:callback #(dispatch [:incoming-message %1 %2])}))))))))
(register-handler
:update-message-overhead!
(u/side-effect!
(fn [_ [_ chat-id network-status]]
(if (= network-status :offline)
(chats/inc-message-overhead chat-id)
(chats/reset-message-overhead chat-id)))))
(reg-fx
::save-public-chat
(fn [chat]

View File

@ -1,298 +0,0 @@
(ns status-im.chat.handlers.send-message
(:require [clojure.string :as s]
[re-frame.core :refer [after dispatch path]]
[status-im.chat.models.commands :as commands-model]
[status-im.chat.events.console :as console]
[status-im.chat.utils :as cu]
[status-im.constants :refer [console-chat-id
text-content-type
content-type-log-message
content-type-command
content-type-command-request] :as c]
[status-im.data-store.messages :as messages]
[status-im.native-module.core :as status]
[status-im.protocol.core :as protocol]
[status-im.utils.config :as config]
[status-im.utils.clocks :as clocks]
[status-im.utils.datetime :as datetime]
[status-im.utils.handlers :refer [register-handler] :as u]
[status-im.utils.random :as random]
[status-im.utils.types :as types]
[taoensso.timbre :as log]))
(defn prepare-command
[identity chat-id clock-value
{request-params :params
request-command :command
:keys [prefill prefillBotDb]
:as request}
{:keys [id params command to-message handler-data content-type]}]
(let [content (if request
{:command request-command
:params (assoc request-params :bot-db (:bot-db params))
:prefill prefill
:prefill-bot-db prefillBotDb}
{:command (:name command)
:scope (:scope command)
:params params})
content' (assoc content :handler-data handler-data
:type (name (:type command))
:content-command (:name command)
:content-command-scope-bitmask (:scope-bitmask command)
:content-command-ref (:ref command)
:preview (:preview command)
:short-preview (:short-preview command)
:bot (or (:bot command)
(:owner-id command)))]
{:message-id id
:from identity
:to chat-id
:timestamp (datetime/now-ms)
:content content'
:content-type (or content-type
(if request
content-type-command-request
content-type-command))
:outgoing true
:to-message to-message
:type (:type command)
:has-handler (:has-handler command)
:clock-value (clocks/send clock-value)
:show? true}))
(defn console-command? [chat-id command-name]
(and (= console-chat-id chat-id)
(console/commands-names command-name)))
(register-handler :check-commands-handlers!
(u/side-effect!
(fn [_ [_ {:keys [command message chat-id] :as params}]]
(let [{:keys [command] :as content} command]
(cond
(console-command? chat-id (:name command))
(dispatch [:invoke-console-command-handler! params])
(:has-handler command)
(dispatch [::invoke-command-handlers! params])
:else
(dispatch [:prepare-command! chat-id params])))
(dispatch [:set-chat-ui-props {:sending-in-progress? false}]))))
(defn- message-type [{:keys [group-chat public?]}]
(cond
(and group-chat public?) :public-group-user-message
(and group-chat (not public?)) :group-user-message
:else :user-message))
(register-handler :prepare-command!
(u/side-effect!
(fn [{:keys [current-public-key network-status chats] :as db}
[_ add-to-chat-id {{:keys [handler-data
command]
:as content} :command
chat-id :chat-id
:as params}]]
(let [clock-value (messages/get-last-clock-value chat-id)
request (:request handler-data)
hidden-params (->> (:params command)
(filter :hidden)
(map :name))
command' (-> (prepare-command current-public-key chat-id clock-value request content)
(assoc :message-type (message-type (get chats chat-id))))]
(dispatch [:update-message-overhead! chat-id network-status])
(dispatch [:set-chat-ui-props {:sending-in-progress? false}])
(dispatch [::send-command! add-to-chat-id (assoc params :command command') hidden-params])
(when (= console-chat-id chat-id)
(dispatch [:console-respond-command params]))))))
(register-handler ::send-command!
(u/side-effect!
(fn [_ [_ add-to-chat-id params hidden-params]]
(dispatch [::add-command add-to-chat-id params])
(dispatch [::save-command! add-to-chat-id params hidden-params])
(dispatch [::dispatch-responded-requests! params])
(dispatch [::send-command-protocol! (update-in params [:command :content]
dissoc :preview :short-preview)]))))
(register-handler ::add-command
(after (fn [_ [_ _ {:keys [handler]}]]
(when handler (handler))))
(fn [db [_ add-to-chat-id {:keys [chat-id command]}]]
(cu/add-message-to-db db add-to-chat-id chat-id command)))
(register-handler
::save-command!
(u/side-effect!
(fn [db [_ chat-id {:keys [command]} hidden-params]]
(let [
command (cond-> (-> command
(assoc :chat-id chat-id)
(update-in [:content :params] #(apply dissoc % hidden-params))
(dissoc :to-message :has-handler :raw-input)))]
(dispatch [:upsert-chat! {:chat-id chat-id}])
(messages/save command)))))
(register-handler ::dispatch-responded-requests!
(u/side-effect!
(fn [_ [_ {{:keys [to-message]} :command :keys [chat-id]}]]
(when to-message
(dispatch [:request-answered chat-id to-message])))))
(register-handler ::invoke-command-handlers!
(u/side-effect!
(fn [{:keys [bot-db]
:accounts/keys [accounts current-account-id]
:contacts/keys [contacts] :as db}
[_ {{:keys [command
params
id]} :command
:keys [chat-id address]
:as orig-params}]]
(let [{:keys [type name scope-bitmask bot owner-id]} command
handler-type (if (= :command type) :commands :responses)
to (get-in contacts [chat-id :address])
identity (or owner-id bot chat-id)
bot-db (get bot-db (or bot chat-id))
;; TODO what's actually semantic difference between `:parameters` and `:context`
;; and do we have some clear API for both ? seems very messy and unorganized now
jail-params {:parameters params
:context (cond-> {:from address
:to to
:current-account (get accounts current-account-id)
:message-id id}
(:async-handler command)
(assoc :orig-params orig-params))}]
(status/call-jail
{:jail-id identity
:path [handler-type [name scope-bitmask] :handler]
:params jail-params
:callback (if (:async-handler command) ; async handler, we ignore return value
(fn [_]
(log/debug "Async command handler called"))
(fn [res]
(dispatch [:command-handler! chat-id orig-params res])))})))))
(register-handler
:received-bot-response
(u/side-effect!
(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 bot-id
:db update-db}]))
(dispatch [:suggestions-handler (assoc params
:bot-id bot-id
:result data
:default-db default-db)])
(doseq [message log-messages]
(let [{:keys [message type]} message]
(when (or (not= type "debug")
js/goog.DEBUG
(get-in contacts [chat-id :debug?]))
(dispatch [:chat-received-message/add
{:message-id (random/id)
:content (str type ": " message)
:content-type content-type-log-message
:outgoing false
:chat-id chat-id
:from chat-id
:to "me"}]))))
(when content
(dispatch [:chat-received-message/add
{:message-id (random/id)
:content (str content)
:content-type text-content-type
:outgoing false
:chat-id chat-id
:from chat-id
:to "me"}]))))))
(defn handle-message-from-bot [{:keys [message chat-id]}]
(cond
(string? message)
(dispatch [:chat-received-message/add {:message-id (random/id)
:content (str message)
:content-type text-content-type
:outgoing false
:chat-id chat-id
:from chat-id
:to "me"}])
(= "request" (:type message))
(dispatch [:add-request-message!
{:content (:content message)
:chat-id chat-id}])))
(register-handler :send-message-from-jail
(u/side-effect!
(fn [_ [_ {:keys [chat-id message]}]]
(let [parsed-message (types/json->clj message)]
(handle-message-from-bot {:message parsed-message
:chat-id chat-id})))))
(register-handler :show-suggestions-from-jail
(u/side-effect!
(fn [_ [_ {:keys [chat-id markup]}]]
(let [markup' (types/json->clj markup)
result (assoc-in {} [:result :returned :markup] markup')]
(dispatch [:suggestions-handler
{:result result
:chat-id chat-id}])))))
(register-handler ::send-command-protocol!
(u/side-effect!
(fn [{:keys [web3 current-public-key chats network-status]
:accounts/keys [accounts current-account-id]
:contacts/keys [contacts] :as db}
[_ {:keys [chat-id command]}]]
(if (get-in contacts [chat-id :dapp?])
(when-let [text-message (get-in command [:content :handler-data :text-message])]
(handle-message-from-bot {:message text-message
:chat-id chat-id}))
(let [{:keys [public-key private-key]} (chats chat-id)
{:keys [group-chat public?]} (get-in db [:chats chat-id])
payload (-> command
(select-keys [:content :content-type
:clock-value :show?])
(assoc :timestamp (datetime/now-ms)))
payload (if (= network-status :offline)
(assoc payload :show? false)
payload)
options {:web3 web3
:message {:from current-public-key
:message-id (:message-id command)
:payload payload}}]
(cond
(and group-chat (not public?))
(protocol/send-group-message! (assoc options
:group-id chat-id
:keypair {:public public-key
:private private-key}))
(and group-chat public?)
(protocol/send-public-group-message!
(let [username (get-in accounts [current-account-id :name])]
(assoc options :group-id chat-id
:username username)))
:else
(protocol/send-message! (assoc-in options
[:message :to] chat-id))))))))
(register-handler
:add-request-message!
(u/side-effect!
(fn [_ [_ {:keys [content chat-id]}]]
(dispatch [:chat-received-message/add
{:message-id (random/id)
:content (assoc content :bot chat-id)
:content-type content-type-command-request
:outgoing false
:chat-id chat-id
:from chat-id
:to "me"}]))))

View File

@ -1,12 +1,15 @@
(ns status-im.chat.models.message
(:require [re-frame.core :as re-frame]
[status-im.utils.clocks :as clocks]
[status-im.constants :as constants]
[status-im.chat.utils :as chat-utils]
[status-im.chat.events.console :as console-events]
[status-im.chat.events.requests :as requests-events]
[status-im.chat.models :as chat-model]
[status-im.chat.models.commands :as commands-model]
[status-im.chat.events.requests :as requests-events]
[taoensso.timbre :as log]))
[status-im.chat.utils :as chat-utils]
[status-im.data-store.messages :as messages-store]
[status-im.utils.datetime :as datetime-utils]
[status-im.utils.clocks :as clocks-utils]
[status-im.utils.random :as random]))
(defn- get-current-account
[{:accounts/keys [accounts current-account-id]}]
@ -64,7 +67,7 @@
:chat-id chat-identifier
:timestamp (or timestamp now)
:show? true
:clock-value (clocks/receive
:clock-value (clocks-utils/receive
clock-value
(get-last-clock-value chat-identifier)))
public-key
@ -81,3 +84,273 @@
(assoc :save-message (dissoc enriched-message :new?)))
command-request?
(requests-events/add-request chat-identifier enriched-message))))))
(defn- handle-message-from-bot [cofx {:keys [message chat-id]}]
(when-let [message (cond
(string? message)
{:message-id (random/id)
:content (str message)
:content-type constants/text-content-type
:outgoing false
:chat-id chat-id
:from chat-id
:to "me"}
(= "request" (:type message))
{:message-id (random/id)
:content (assoc (:content message) :bot chat-id)
:content-type constants/content-type-command-request
:outgoing false
:chat-id chat-id
:from chat-id
:to "me"})]
(receive cofx message)))
(defn- send-dapp-message!
[{{:accounts/keys [current-account-id] :as db} :db :as cofx}
{{:keys [message-type]
:as message} :message
:keys [chat-id command] :as args}]
(if command
(when-let [text-message (get-in command [:content :handler-data :text-message])]
(handle-message-from-bot cofx {:message text-message
:chat-id chat-id}))
(let [data (get-in db [:local-storage chat-id])]
{:call-jail-function {:chat-id chat-id
:function :on-message-send
:parameters {:message (:content message)}
:context {:data data
:from current-account-id}}})))
(defn- generate-message
[{:keys [web3 current-public-key chats network-status]}
{:keys [chat-id command message] :as args}]
(if command
(let [payload (-> command
(select-keys [:content :content-type
:clock-value :show?])
(assoc :timestamp (datetime-utils/now-ms)))
payload (if (= network-status :offline)
(assoc payload :show? false)
payload)]
{:from current-public-key
:message-id (:message-id command)
:payload payload})
(let [message' (select-keys message [:from :message-id])
payload (select-keys message [:timestamp :content :content-type
:clock-value :show?])
payload (if (= network-status :offline)
(assoc payload :show? false)
payload)]
(assoc message' :payload payload))))
(defn send
[{{:keys [web3 chats]
:accounts/keys [accounts current-account-id]
:contacts/keys [contacts] :as db} :db :as cofx}
{:keys [chat-id command] :as args}]
(let [{:keys [dapp? fcm-token]} (get contacts chat-id)]
(if dapp?
(send-dapp-message! cofx args)
(let [{:keys [group-chat public?]} (get-in db [:chats chat-id])
options {:web3 web3
:message (generate-message db args)}]
(cond
(and group-chat (not public?))
(let [{:keys [public-key private-key]} (get chats chat-id)]
{:send-group-message (assoc options
:group-id chat-id
:keypair {:public public-key
:private private-key})})
(and group-chat public?)
{:send-public-group-message (assoc options :group-id chat-id
:username (get-in accounts [current-account-id :name]))}
:else
(merge {:send-message (assoc-in options [:message :to] chat-id)}
(when-not command) {:send-notification fcm-token}))))))
;;;; Send message
(def send-interceptors
[(re-frame/inject-cofx :random-id)
(re-frame/inject-cofx :random-id-seq)
(re-frame/inject-cofx :get-stored-chat)
(re-frame/inject-cofx :now)
(re-frame/inject-cofx :get-last-clock-value)
re-frame/trim-v])
(defn- prepare-message [clock-value params chat]
(let [{:keys [chat-id identity message-text]} params
{:keys [group-chat public?]} chat
message {:message-id (random/id)
:chat-id chat-id
:content message-text
:from identity
:content-type constants/text-content-type
:outgoing true
:timestamp (datetime-utils/now-ms)
:clock-value (clocks-utils/send clock-value)
:show? true}]
(cond-> message
(not group-chat)
(assoc :message-type :user-message
:to chat-id)
group-chat
(assoc :group-id chat-id)
(and group-chat public?)
(assoc :message-type :public-group-user-message)
(and group-chat (not public?))
(assoc :message-type :group-user-message)
(not group-chat)
(assoc :to chat-id :message-type :user-message))))
(defn send-message [{{:keys [network-status] :as db} :db
:keys [now get-stored-chat get-last-clock-value]}
{:keys [chat-id] :as params}]
(let [chat (get-in db [:chats chat-id])
message (prepare-message (get-last-clock-value chat-id) params chat)
params' (assoc params :message message)
fx {:db (chat-utils/add-message-to-db db chat-id chat-id message)
:update-message-overhead! [chat-id network-status]
:save-message message}]
(-> (merge fx (chat-model/upsert-chat (assoc fx :get-stored-chat get-stored-chat :now now)
{:chat-id chat-id}))
(as-> fx'
(merge fx' (send fx' params'))))))
(defn- prepare-command
[identity chat-id clock-value
{request-params :params
request-command :command
:keys [prefill prefillBotDb]
:as request}
{:keys [id params command to-message handler-data content-type]}]
(let [content (if request
{:command request-command
:params (assoc request-params :bot-db (:bot-db params))
:prefill prefill
:prefill-bot-db prefillBotDb}
{:command (:name command)
:scope (:scope command)
:params params})
content' (assoc content :handler-data handler-data
:type (name (:type command))
:content-command (:name command)
:content-command-scope-bitmask (:scope-bitmask command)
:content-command-ref (:ref command)
:preview (:preview command)
:short-preview (:short-preview command)
:bot (or (:bot command)
(:owner-id command)))]
{:message-id id
:from identity
:to chat-id
:timestamp (datetime-utils/now-ms)
:content content'
:content-type (or content-type
(if request
constants/content-type-command-request
constants/content-type-command))
:outgoing true
:to-message to-message
:type (:type command)
:has-handler (:has-handler command)
:clock-value (clocks-utils/send clock-value)
:show? true}))
(defn send-command
[{{:keys [current-public-key network-status] :as db} :db
:keys [now get-stored-chat random-id-seq get-last-clock-value]} result add-to-chat-id params]
(let [{{:keys [handler-data
command]
:as content} :command
chat-id :chat-id} params
request (:request handler-data)
hidden-params (->> (:params command)
(filter :hidden)
(map :name))
command' (prepare-command current-public-key chat-id (get-last-clock-value chat-id) request content)
params' (assoc params :command command')
fx {:db (-> (merge db (:db result))
(chat-utils/add-message-to-db add-to-chat-id chat-id command'))
:update-message-overhead! [chat-id network-status]
:save-message (-> command'
(assoc :chat-id chat-id)
(update-in [:content :params]
#(apply dissoc % hidden-params))
(dissoc :to-message :has-handler :raw-input))}]
(cond-> (merge fx
(chat-model/upsert-chat (assoc fx :get-stored-chat get-stored-chat :now now)
{:chat-id chat-id})
(dissoc result :db))
true
(as-> fx'
(merge fx' (send fx' params')))
(:to-message command')
(assoc :chat-requests/mark-as-answered {:chat-id chat-id
:message-id (:to-message command')})
(= constants/console-chat-id chat-id)
(as-> fx'
(let [messages (console-events/console-respond-command-messages params' random-id-seq)
events (mapv #(vector :chat-received-message/add %) messages)]
(update fx' :dispatch-n into events))))))
(defn invoke-console-command-handler
[{:keys [db] :as cofx} {:keys [chat-id command] :as command-params}]
(let [fx-fn (get console-events/console-commands->fx (-> command :command :name))
result (fx-fn cofx command)]
(send-command cofx result chat-id command-params)))
(defn invoke-command-handlers
[{{:keys [bot-db]
:accounts/keys [accounts current-account-id]
:contacts/keys [contacts] :as db} :db}
{{:keys [command params id]} :command
:keys [chat-id address]
:as orig-params}]
(let [{:keys [type name scope-bitmask bot owner-id]} command
handler-type (if (= :command type) :commands :responses)
to (get-in contacts [chat-id :address])
identity (or owner-id bot chat-id)
bot-db (get bot-db (or bot chat-id))
;; TODO what's actually semantic difference between `:parameters` and `:context`
;; and do we have some clear API for both ? seems very messy and unorganized now
jail-params {:parameters params
:context (cond-> {:from address
:to to
:current-account (get accounts current-account-id)
:message-id id}
(:async-handler command)
(assoc :orig-params orig-params))}]
{:call-jail {:jail-id identity
:path [handler-type [name scope-bitmask] :handler]
:params jail-params
:callback-events-creator (fn [jail-response]
(when-not (:async-handler command)
[[:command-handler! chat-id orig-params jail-response]]))}}))
(defn process-command
[{:keys [db] :as cofx} {:keys [command message chat-id] :as params}]
(let [{:keys [command] :as content} command]
(-> {:db (chat-model/set-chat-ui-props db {:sending-in-progress? false})}
(as-> fx'
(cond
(and (= constants/console-chat-id chat-id)
(console-events/commands-names (:name command)))
(invoke-console-command-handler (merge cofx fx') params)
(:has-handler command)
(merge fx' (invoke-command-handlers fx' params))
:else
(merge fx' (send-command cofx fx' chat-id params)))))))

View File

@ -96,7 +96,8 @@
current-public-key [:get-current-public-key]]
[list/flat-list {:data messages
:render-fn (fn [{:keys [message-id] :as message}]
^{:key message-id} [message-row {:group-chat group-chat
^{:key message-id}
[message-row {:group-chat group-chat
:current-public-key current-public-key
:row message}])
:inverted true

View File

@ -1,5 +1,6 @@
(ns status-im.chat.utils
(:require [status-im.chat.constants :as chat.constants]))
(:require [status-im.chat.constants :as chat.constants]
[taoensso.timbre :as log]))
(defn add-message-to-db
([db add-to-chat-id chat-id message] (add-message-to-db db add-to-chat-id chat-id message true))

View File

@ -20,7 +20,8 @@
[status-im.utils.platform :as platform]
[status-im.i18n :as i18n]
[clojure.string :as string]
[status-im.chat.events.console :as console]))
[status-im.chat.events.console :as console]
[taoensso.timbre :as log]))
(def window-width (:width (react/get-dimensions "window")))
@ -237,7 +238,7 @@
(photo from photo-path)))
(defn message-body
[{:keys [last-outgoing? message-type same-author? from outgoing] :as message} content]
[{:keys [last-outgoing? message-type same-author? from outgoing group-chat] :as message} content]
[react/view style/group-message-wrapper
[react/view (style/message-body message)
[react/view style/message-author
@ -248,7 +249,8 @@
[react/view (style/group-message-view message)
content
(when last-outgoing?
(if (= (keyword message-type) :group-user-message)
(if (or (= (keyword message-type) :group-user-message)
group-chat)
[group-message-delivery-status message]
[message-delivery-status message]))]]])
@ -289,15 +291,17 @@
(defn chat-message [{:keys [outgoing message-id chat-id from current-public-key] :as message}]
(reagent/create-class
{:display-name "chat-message"
{:display-name
"chat-message"
:component-did-mount
;; send `:seen` signal when we have signed-in user, message not from us and we didn't sent it already
#(when (and current-public-key message-id chat-id (not outgoing)
(fn []
(when (and current-public-key message-id chat-id (not outgoing)
(not (chat.utils/message-seen-by? message current-public-key)))
(events-buffer/dispatch [:send-seen! {:chat-id chat-id
:from from
:me current-public-key
:message-id message-id}]))
:message-id message-id}])))
:reagent-render
(fn [{:keys [outgoing group-chat content-type content] :as message}]
[message-container message

View File

@ -1,16 +1,15 @@
(ns status-im.commands.handlers.jail
(:require [re-frame.core :refer [after dispatch subscribe trim-v debug]]
(:require [clojure.string :as string]
[re-frame.core :refer [after dispatch subscribe trim-v debug]]
[status-im.utils.handlers :as handlers]
[status-im.utils.utils :refer [show-popup]]
[status-im.utils.types :refer [json->clj]]
[status-im.utils.types :as types]
[status-im.commands.utils :refer [reg-handler]]
[clojure.string :as s]
[status-im.ui.components.react :as r]
[status-im.constants :refer [console-chat-id]]
[status-im.i18n :refer [get-contact-translated]]
[taoensso.timbre :as log]
[status-im.data-store.local-storage :as local-storage]
[clojure.string :as str]))
[status-im.data-store.local-storage :as local-storage]))
(defn command-handler!
[_ [chat-id
@ -26,10 +25,10 @@
result
(let [command' (assoc command :handler-data returned)
params' (assoc params :command command')]
(dispatch [:prepare-command! chat-id params']))
(dispatch [:chat-send-message/send-command chat-id params']))
(not (or error handler-error))
(dispatch [:prepare-command! chat-id params])
(dispatch [:chat-send-message/send-command chat-id params])
:else nil)))
@ -41,7 +40,7 @@
current-input (get-in chats [chat-id :input-text])
path (if command
[:chats chat-id :parameter-boxes (:name command) parameter-index]
(when-not (str/blank? current-input)
(when-not (string/blank? current-input)
[:chats chat-id :parameter-boxes :message]))]
(dispatch [:choose-predefined-expandable-height :parameter-box (or (keyword height) :default)])
(when (and contains-markup? path (not= (get-in db path) markup))
@ -80,9 +79,10 @@
(defn print-error-message! [message]
(fn [_ params]
(when (:error (last params))
(show-popup "Error" (s/join "\n" [message params]))
(show-popup "Error" (string/join "\n" [message params]))
(log/debug message params))))
;; TODO(alwx): rewrite
(reg-handler :command-handler!
(after (print-error-message! "Error on command handling"))
(handlers/side-effect! command-handler!))
@ -96,6 +96,16 @@
:suggestions-event!
(handlers/side-effect! suggestions-events-handler!))
(reg-handler
:show-suggestions-from-jail
(handlers/side-effect!
(fn [_ [_ {:keys [chat-id markup]}]]
(let [markup' (types/json->clj markup)
result (assoc-in {} [:result :returned :markup] markup')]
(dispatch [:suggestions-handler
{:result result
:chat-id chat-id}])))))
(reg-handler :set-local-storage
(handlers/side-effect!
(fn [_ [{:keys [data chat-id]}]]

View File

@ -214,7 +214,7 @@
{:jail-id chat-id
:path path
:params params
:callback (or callback #(dispatch [:received-bot-response {:chat-id chat-id} %]))})))
:callback (or callback #(dispatch [:chat-received-message/bot-response {:chat-id chat-id} %]))})))
(defn set-soft-input-mode [mode]
(when status

View File

@ -17,7 +17,7 @@
[status-im.protocol.web3.keys :as web3.keys]
[status-im.utils.datetime :as datetime]
[status-im.utils.events-buffer :as events-buffer]
[taoensso.timbre :as log :refer-macros [debug]]
[taoensso.timbre :as log]
[status-im.native-module.core :as status]
[clojure.string :as string]
[status-im.utils.web3-provider :as web3-provider]
@ -477,7 +477,7 @@
:update-keys {:dispatch [:update-keys-received message]}
:online {:dispatch [:contact-online-received message]}
nil)]
(when (nil? route-fx) (debug "Unknown message type" type))
(when (nil? route-fx) (log/debug "Unknown message type" type))
(cache/add! processed-message)
(merge
{::save-processed-messages processed-message}

View File

@ -57,7 +57,7 @@
:callback (fn [jail-response]
(doseq [event (if callback-events-creator
(callback-events-creator jail-response)
[[:received-bot-response
[[:chat-received-message/bot-response
{:chat-id chat-id}
jail-response]])
:when event]
@ -92,8 +92,9 @@
(dissoc :callback-events-creator)
(assoc :callback
(fn [jail-response]
(when callback-events-creator
(doseq [event (callback-events-creator jail-response)]
(re-frame/dispatch event))))))))
(re-frame/dispatch event)))))))))
(re-frame/reg-fx
:call-jail-function
@ -349,7 +350,7 @@
:data data}])
"show-suggestions" (re-frame/dispatch [:show-suggestions-from-jail {:chat-id chat_id
:markup data}])
"send-message" (re-frame/dispatch [:send-message-from-jail {:chat-id chat_id
"send-message" (re-frame/dispatch [:chat-send-message/from-jail {:chat-id chat_id
:message data}])
"handler-result" (let [orig-params (:origParams data)]
;; TODO(janherich): figure out and fix chat_id from event