* 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"])
};
web3.eth.sendTransaction(data, function(error, hash) {
if (error) {
status.sendSignal("handler-data", {
status: "failed",
messageId: context["message-id"],
error: error
status.sendSignal("handler-result", {
status: "failed",
error: {
markup: status.components.validationMessage(
I18n.t('validation_tx_title'),
I18n.t('validation_tx_failed')
)
},
origParams: context["orig-params"]
});
} else {
status.sendSignal("handler-data", {
status: "sent",
messageId: context["message-id"],
hash: hash
status.sendSignal("handler-result", {
status: "success",
hash: hash,
origParams: context["orig-params"]
});
}
});
return {
status: 'not-confirmed'
};
// async handler, so we don't return anything immediately
}
function previewSend(params, context) {
@ -449,36 +450,6 @@ function previewSend(params, context) {
} else {
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 {
markup: status.components.view(
@ -512,6 +483,7 @@ var send = {
params: paramsSend,
validator: validateSend,
handler: handleSend,
asyncHandler: true,
preview: previewSend,
shortPreview: shortPreviewSend
};

View File

@ -14,8 +14,6 @@ I18n.translations = {
send_explanation_3: 'probably within 30 seconds.',
send_explanation_4: 'probably within a few seconds.',
send_sending_to: 'to ',
send_transaction_pending: 'transaction pending',
send_transaction_failed: 'transaction failed',
eth: 'ETH',
@ -25,6 +23,8 @@ I18n.translations = {
request_requesting_from: 'from ',
validation_title: 'Amount',
validation_tx_title: 'Transaction',
validation_tx_failed: 'Transaction failed',
validation_amount_specified: 'Amount must be specified',
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)',

View File

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

View File

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

View File

@ -107,20 +107,9 @@
params' (assoc params :command content')]
(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!
(u/side-effect!
(fn [_ [_ add-to-chat-id params hidden-params]]
(dispatch [::check-preview-refetch add-to-chat-id (:command params)])
(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])
(when (not= add-to-chat-id wallet-chat-id)
@ -163,11 +152,15 @@
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 {:from address
:to to
:current-account (get accounts current-account-id)
:message-id id}}]
:context (cond-> {:from address
:to to
:current-account (get accounts current-account-id)
:message-id id}
(:async-handler command)
(assoc :orig-params orig-params))}]
(dispatch
[:check-and-load-commands!
identity
@ -175,8 +168,11 @@
{:jail-id identity
:path [handler-type name :handler]
:params jail-params
:callback (fn [res]
(dispatch [:command-handler! chat-id orig-params res]))})])))))
: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 :prepare-message
(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.i18n :refer [get-contact-translated]]
[taoensso.timbre :as log]
[status-im.data-store.local-storage :as local-storage]
status-im.commands.events.jail))
[status-im.data-store.local-storage :as local-storage]))
(defn command-handler!
[_ [chat-id
{:keys [command] :as params}
{:keys [result error]}]]
(let [{:keys [context returned]} result
(let [{:keys [returned]} result
{handler-error :error} returned]
(cond
handler-error
@ -26,10 +25,8 @@
result
(let [command' (assoc command :handler-data returned)
params' (assoc params :command command')]
(if (:eth_sendTransaction context)
(dispatch [:wait-for-transaction (:id command) params'])
(dispatch [:prepare-command! chat-id params'])))
params' (assoc params :command command')]
(dispatch [:prepare-command! chat-id params']))
(not (or error handler-error))
(dispatch [:prepare-command! chat-id params])

View File

@ -219,7 +219,10 @@
:markup data}])
"send-message" (dispatch [:send-message-from-jail {:chat-id chat_id
: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))))
(register-handler-fx