* Fix #1886

Completly rework the way how async handlers work

Delay further processing of message till async handler returns result

* Fix indent

* Fix #1903
This commit is contained in:
Jan Herich 2017-09-21 13:08:51 +02:00 committed by Roman Volosovskyi
parent bce18bf32c
commit 805d874846
8 changed files with 121 additions and 183 deletions

View File

@ -352,26 +352,27 @@ function handleSend(params, context) {
gasPrice: calculateGasPrice(params["bot-db"]["sliderValue"]) gasPrice: calculateGasPrice(params["bot-db"]["sliderValue"])
}; };
web3.eth.sendTransaction(data, function(error, hash) { web3.eth.sendTransaction(data, function(error, hash) {
if (error) { if (error) {
status.sendSignal("handler-data", { status.sendSignal("handler-result", {
status: "failed", status: "failed",
messageId: context["message-id"], error: {
error: error markup: status.components.validationMessage(
I18n.t('validation_tx_title'),
I18n.t('validation_tx_failed')
)
},
origParams: context["orig-params"]
}); });
} else { } else {
status.sendSignal("handler-data", { status.sendSignal("handler-result", {
status: "sent", status: "success",
messageId: context["message-id"], hash: hash,
hash: hash origParams: context["orig-params"]
}); });
} }
}); });
// async handler, so we don't return anything immediately
return {
status: 'not-confirmed'
};
} }
function previewSend(params, context) { function previewSend(params, context) {
@ -449,36 +450,6 @@ function previewSend(params, context) {
} else { } else {
markup = [firstRow]; markup = [firstRow];
} }
if (!(context["handler-data"]
&& context["handler-data"]["status"] === "sent")) {
var pendingRow = status.components.text(
{
style: {
color: "#9199a0",
fontSize: 12,
lineHeight: 18
}
},
I18n.t('send_transaction_pending')
);
markup.push(pendingRow);
}
if (context["handler-data"]
&& context["handler-data"]["status"] === "failed") {
var errorRow = status.components.text(
{
style: {
color: "red",
fontSize: 12,
lineHeight: 18
}
},
I18n.t('send_transaction_failed')
);
markup.push(errorRow);
}
return { return {
markup: status.components.view( markup: status.components.view(
@ -512,6 +483,7 @@ var send = {
params: paramsSend, params: paramsSend,
validator: validateSend, validator: validateSend,
handler: handleSend, handler: handleSend,
asyncHandler: true,
preview: previewSend, preview: previewSend,
shortPreview: shortPreviewSend shortPreview: shortPreviewSend
}; };

View File

@ -14,8 +14,6 @@ I18n.translations = {
send_explanation_3: 'probably within 30 seconds.', send_explanation_3: 'probably within 30 seconds.',
send_explanation_4: 'probably within a few seconds.', send_explanation_4: 'probably within a few seconds.',
send_sending_to: 'to ', send_sending_to: 'to ',
send_transaction_pending: 'transaction pending',
send_transaction_failed: 'transaction failed',
eth: 'ETH', eth: 'ETH',
@ -25,6 +23,8 @@ I18n.translations = {
request_requesting_from: 'from ', request_requesting_from: 'from ',
validation_title: 'Amount', validation_title: 'Amount',
validation_tx_title: 'Transaction',
validation_tx_failed: 'Transaction failed',
validation_amount_specified: 'Amount must be specified', validation_amount_specified: 'Amount must be specified',
validation_invalid_number: 'Amount is not valid number', validation_invalid_number: 'Amount is not valid number',
validation_amount_is_too_small: 'Amount is too precise. The smallest unit you can send is 1 Wei (1x10^-18 ETH)', validation_amount_is_too_small: 'Amount is too precise. The smallest unit you can send is 1 Wei (1x10^-18 ETH)',

View File

@ -27,6 +27,7 @@ Command.prototype.create = function (com) {
this.description = com.description; this.description = com.description;
this.handler = com.handler; this.handler = com.handler;
this["has-handler"] = com.handler != null; this["has-handler"] = com.handler != null;
this["async-handler"] = (com.handler != null) && com.asyncHandler
this["registered-only"] = com.registeredOnly; this["registered-only"] = com.registeredOnly;
this.validator = com.validator; this.validator = com.validator;
this.color = com.color; this.color = com.color;
@ -41,6 +42,7 @@ Command.prototype.create = function (com) {
this["execute-immediately?"] = com.executeImmediately; this["execute-immediately?"] = com.executeImmediately;
this["sequential-params"] = com.sequentialParams; this["sequential-params"] = com.sequentialParams;
this["hide-send-button"] = com.hideSendButton; this["hide-send-button"] = com.hideSendButton;
this.addToCatalog(); this.addToCatalog();
return this; return this;

View File

@ -13,111 +13,110 @@
(defn generate-context (defn generate-context
"Generates context for jail call" "Generates context for jail call"
[{:keys [chats handler-data] :accounts/keys [current-account-id]} message-id chat-id to group-id] [{:keys [chats] :accounts/keys [current-account-id]} chat-id to group-id]
(merge {:platform platform/platform (merge {:platform platform/platform
:from current-account-id :from current-account-id
:to to :to to
:chat {:chat-id chat-id :chat {:chat-id chat-id
:group-chat (or (get-in chats [chat-id :group-chat]) :group-chat (or (get-in chats [chat-id :group-chat])
(not (nil? group-id)))} (not (nil? group-id)))}}
:handler-data (get handler-data message-id)}
i18n/delimeters)) i18n/delimeters))
;;;; Coeffects ;;;; Coeffects
(reg-cofx (reg-cofx
::get-persisted-message ::get-persisted-message
(fn [coeffects _] (fn [coeffects _]
(assoc coeffects :get-persisted-message msg-store/get-by-id))) (assoc coeffects :get-persisted-message msg-store/get-by-id)))
;;;; Effects ;;;; Effects
(reg-fx (reg-fx
::update-persisted-message ::update-persisted-message
(fn [message] (fn [message]
(msg-store/update message))) (msg-store/update message)))
(reg-fx (reg-fx
:chat-fx/call-jail :chat-fx/call-jail
(fn [{:keys [callback-events-creator] :as opts}] (fn [{:keys [callback-events-creator] :as opts}]
(status/call-jail (status/call-jail
(-> opts (-> opts
(dissoc :callback-events-creator) (dissoc :callback-events-creator)
(assoc :callback (assoc :callback
(fn [jail-response] (fn [jail-response]
(doseq [event (callback-events-creator jail-response)] (doseq [event (callback-events-creator jail-response)]
(dispatch event)))))))) (dispatch event))))))))
;;;; Handlers ;;;; Handlers
(register-handler-fx (register-handler-fx
::jail-command-data-response ::jail-command-data-response
[trim-v] [trim-v]
(fn [{:keys [db]} [{{:keys [returned]} :result} {:keys [message-id on-requested]} data-type]] (fn [{:keys [db]} [{{:keys [returned]} :result} {:keys [message-id on-requested]} data-type]]
(cond-> {} (cond-> {}
returned returned
(assoc :db (assoc-in db [:message-data data-type message-id] returned)) (assoc :db (assoc-in db [:message-data data-type message-id] returned))
(and returned (and returned
(= :preview data-type)) (= :preview data-type))
(assoc ::update-persisted-message {:message-id message-id (assoc ::update-persisted-message {:message-id message-id
:preview (prn-str returned)}) :preview (prn-str returned)})
on-requested on-requested
(assoc :dispatch (on-requested returned))))) (assoc :dispatch (on-requested returned)))))
(register-handler-fx (register-handler-fx
:request-command-data :request-command-data
[trim-v] [trim-v]
(fn [{:keys [db]} (fn [{:keys [db]}
[{{:keys [command content-command params type]} :content [{{:keys [command content-command params type]} :content
:keys [chat-id jail-id group-id message-id] :as message} :keys [chat-id jail-id group-id message-id handler-data] :as message}
data-type]] data-type]]
(let [{:keys [chats] (let [{:keys [chats]
:accounts/keys [current-account-id] :accounts/keys [current-account-id]
:contacts/keys [contacts]} db :contacts/keys [contacts]} db
jail-id (or jail-id chat-id) jail-id (or jail-id chat-id)
jail-id (if (get-in chats [jail-id :group-chat]) jail-id (if (get-in chats [jail-id :group-chat])
(get-in chats [jail-id :command-suggestions (keyword command) :owner-id]) (get-in chats [jail-id :command-suggestions (keyword command) :owner-id])
jail-id)] jail-id)]
(if (get-in contacts [jail-id :commands-loaded?]) (if (get-in contacts [jail-id :commands-loaded?])
(let [path [(if (= :response (keyword type)) :responses :commands) (let [path [(if (= :response (keyword type)) :responses :commands)
(or content-command command) (or content-command command)
data-type] data-type]
to (get-in contacts [chat-id :address]) to (get-in contacts [chat-id :address])
jail-params {:parameters params jail-params {:parameters params
:context (generate-context db message-id chat-id to group-id)}] :context (generate-context db chat-id to group-id)}]
{:chat-fx/call-jail {:jail-id jail-id {:chat-fx/call-jail {:jail-id jail-id
:path path :path path
:params jail-params :params jail-params
:callback-events-creator (fn [jail-response] :callback-events-creator (fn [jail-response]
[[::jail-command-data-response [[::jail-command-data-response
jail-response message data-type]])}}) jail-response message data-type]])}})
{:dispatch-n [[:add-commands-loading-callback jail-id {:dispatch-n [[:add-commands-loading-callback jail-id
#(dispatch [:request-command-data message data-type])] #(dispatch [:request-command-data message data-type])]
[:load-commands! jail-id]]})))) [:load-commands! jail-id]]}))))
(register-handler-fx (register-handler-fx
:execute-command-immediately :execute-command-immediately
[trim-v] [trim-v]
(fn [_ [{command-name :name :as command}]] (fn [_ [{command-name :name :as command}]]
(case (keyword command-name) (case (keyword command-name)
:grant-permissions :grant-permissions
{:dispatch [:request-permissions {:dispatch [:request-permissions
[:read-external-storage] [:read-external-storage]
#(dispatch [:initialize-geth])]} #(dispatch [:initialize-geth])]}
(log/debug "ignoring command: " command)))) (log/debug "ignoring command: " command))))
(register-handler-fx (register-handler-fx
:request-command-preview :request-command-preview
[trim-v (inject-cofx ::get-persisted-message)] [trim-v (inject-cofx ::get-persisted-message)]
(fn [{:keys [db get-persisted-message]} [{:keys [message-id] :as message}]] (fn [{:keys [db get-persisted-message]} [{:keys [message-id] :as message}]]
(let [previews (get-in db [:message-data :preview])] (let [previews (get-in db [:message-data :preview])]
(when-not (contains? previews message-id) (when-not (contains? previews message-id)
(let [{serialized-preview :preview} (get-persisted-message message-id)] (let [{serialized-preview :preview} (get-persisted-message message-id)]
;; if preview is already cached in db, do not request it from jail ;; if preview is already cached in db, do not request it from jail
;; and write it directly to message-data path ;; and write it directly to message-data path
(if serialized-preview (if serialized-preview
{:db (assoc-in db {:db (assoc-in db
[:message-data :preview message-id] [:message-data :preview message-id]
(reader/read-string serialized-preview))} (reader/read-string serialized-preview))}
{:dispatch [:request-command-data message :preview]})))))) {:dispatch [:request-command-data message :preview]}))))))

View File

@ -107,20 +107,9 @@
params' (assoc params :command content')] params' (assoc params :command content')]
(dispatch [:prepare-command! wallet-chat-id params']))))))) (dispatch [:prepare-command! wallet-chat-id params'])))))))
(register-handler ::check-preview-refetch
(fn [db [_ chat-id {:keys [message-id] :as message}]]
(let [handler-data (get-in db [:handler-data message-id])]
(if (:fetch-preview handler-data)
(do (dispatch [:request-command-data (assoc message :jail-id chat-id) :preview])
(handler-data/save-data {:message-id message-id
:data (dissoc handler-data :fetch-preview)})
(update-in db [:handler-data message-id] dissoc :fetch-preview))
db))))
(register-handler ::send-command! (register-handler ::send-command!
(u/side-effect! (u/side-effect!
(fn [_ [_ add-to-chat-id params hidden-params]] (fn [_ [_ add-to-chat-id params hidden-params]]
(dispatch [::check-preview-refetch add-to-chat-id (:command params)])
(dispatch [::add-command add-to-chat-id params]) (dispatch [::add-command add-to-chat-id params])
(dispatch [::save-command! add-to-chat-id params hidden-params]) (dispatch [::save-command! add-to-chat-id params hidden-params])
(when (not= add-to-chat-id wallet-chat-id) (when (not= add-to-chat-id wallet-chat-id)
@ -163,11 +152,15 @@
to (get-in contacts [chat-id :address]) to (get-in contacts [chat-id :address])
identity (or owner-id bot chat-id) identity (or owner-id bot chat-id)
bot-db (get bot-db (or 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 jail-params {:parameters params
:context {:from address :context (cond-> {:from address
:to to :to to
:current-account (get accounts current-account-id) :current-account (get accounts current-account-id)
:message-id id}}] :message-id id}
(:async-handler command)
(assoc :orig-params orig-params))}]
(dispatch (dispatch
[:check-and-load-commands! [:check-and-load-commands!
identity identity
@ -175,8 +168,11 @@
{:jail-id identity {:jail-id identity
:path [handler-type name :handler] :path [handler-type name :handler]
:params jail-params :params jail-params
:callback (fn [res] :callback (if (:async-handler command) ; async handler, we ignore return value
(dispatch [:command-handler! chat-id orig-params res]))})]))))) (fn [_]
(log/debug "Async command handler called"))
(fn [res]
(dispatch [:command-handler! chat-id orig-params res])))})])))))
(register-handler :prepare-message (register-handler :prepare-message
(u/side-effect! (u/side-effect!

View File

@ -1,31 +0,0 @@
(ns status-im.commands.events.jail
(:require [re-frame.core :as re-frame]
[status-im.utils.handlers :as handlers]
[status-im.data-store.handler-data :as handler-data]
[taoensso.timbre :as log]))
(re-frame/reg-fx
:save-handler-data
(fn [data]
(handler-data/save-data data)))
(handlers/register-handler-fx
:set-handler-data
[re-frame/trim-v]
(fn [{:keys [db]} [chat-id {:keys [messageId] :as data}]]
(let [;; this is very bad, we should refactor our db ASAP
message (->> (get-in db [:chats chat-id :messages])
(filter #(= (:message-id %) messageId))
first)
handler-data (cond-> (dissoc data :messageId)
;; message not there yet, indicate we want to re-fetch preview once it lands there
(nil? message)
(assoc :fetch-preview true))
old-handler-data (get-in db [:handler-data messageId] {})
new-handler-data (merge old-handler-data handler-data)]
(cond-> {:db (assoc-in db [:handler-data messageId] new-handler-data)
:save-handler-data {:message-id messageId
:data new-handler-data}}
;; message was already added to db, we can re-fetch preview
(not (nil? message))
(assoc :dispatch [:request-command-data (assoc message :jail-id chat-id) :preview])))))

View File

@ -10,14 +10,13 @@
[status-im.constants :refer [console-chat-id]] [status-im.constants :refer [console-chat-id]]
[status-im.i18n :refer [get-contact-translated]] [status-im.i18n :refer [get-contact-translated]]
[taoensso.timbre :as log] [taoensso.timbre :as log]
[status-im.data-store.local-storage :as local-storage] [status-im.data-store.local-storage :as local-storage]))
status-im.commands.events.jail))
(defn command-handler! (defn command-handler!
[_ [chat-id [_ [chat-id
{:keys [command] :as params} {:keys [command] :as params}
{:keys [result error]}]] {:keys [result error]}]]
(let [{:keys [context returned]} result (let [{:keys [returned]} result
{handler-error :error} returned] {handler-error :error} returned]
(cond (cond
handler-error handler-error
@ -26,10 +25,8 @@
result result
(let [command' (assoc command :handler-data returned) (let [command' (assoc command :handler-data returned)
params' (assoc params :command command')] params' (assoc params :command command')]
(if (:eth_sendTransaction context) (dispatch [:prepare-command! chat-id params']))
(dispatch [:wait-for-transaction (:id command) params'])
(dispatch [:prepare-command! chat-id params'])))
(not (or error handler-error)) (not (or error handler-error))
(dispatch [:prepare-command! chat-id params]) (dispatch [:prepare-command! chat-id params])

View File

@ -219,7 +219,10 @@
:markup data}]) :markup data}])
"send-message" (dispatch [:send-message-from-jail {:chat-id chat_id "send-message" (dispatch [:send-message-from-jail {:chat-id chat_id
:message data}]) :message data}])
"handler-data" (dispatch [:set-handler-data chat_id data]) "handler-result" (let [orig-params (:origParams data)]
;; TODO(janherich): figure out and fix chat_id from event
(dispatch [:command-handler! (:chat-id orig-params) orig-params
{:result {:returned (dissoc data :origParams)}}]))
(log/debug "Unknown jail signal " type)))) (log/debug "Unknown jail signal " type))))
(register-handler-fx (register-handler-fx