mirror of
https://github.com/status-im/status-react.git
synced 2025-01-30 20:55:03 +00:00
316 lines
13 KiB
Clojure
316 lines
13 KiB
Clojure
(ns status-im.chat.handlers.commands
|
|
(:require [re-frame.core :refer [enrich after dispatch]]
|
|
[status-im.utils.handlers :refer [register-handler] :as u]
|
|
[status-im.components.react :as react-comp]
|
|
[status-im.components.status :as status]
|
|
[status-im.models.commands :as commands]
|
|
[status-im.chat.utils :refer [console? not-console?]]
|
|
[clojure.string :as str]
|
|
[status-im.commands.utils :as cu]
|
|
[status-im.utils.phone-number :as pn]
|
|
[status-im.i18n :as i18n]
|
|
[status-im.utils.datetime :as time]
|
|
[status-im.utils.random :as random]
|
|
[status-im.utils.platform :as platform]
|
|
[taoensso.timbre :as log]))
|
|
|
|
(defn content-by-command
|
|
[{:keys [type]} content]
|
|
(if (and (= :command type) content)
|
|
(subs content (count cu/command-prefix))
|
|
content))
|
|
|
|
(defn command-dependent-context-params
|
|
[{:keys [name] :as command}]
|
|
(case name
|
|
"phone" {:suggestions (pn/get-examples)}
|
|
{}))
|
|
|
|
(defn invoke-suggestions-handler!
|
|
[{:keys [current-chat-id canceled-command] :as db} _]
|
|
(when-not canceled-command
|
|
(let [{:keys [command content params]} (get-in db [:chats current-chat-id :command-input])
|
|
data (get-in db [:local-storage current-chat-id])
|
|
{:keys [name type]} command
|
|
path [(if (= :command type) :commands :responses)
|
|
name
|
|
:params
|
|
0
|
|
:suggestions]
|
|
params {:parameters (or params {})
|
|
:context (merge {:data data}
|
|
(command-dependent-context-params command))}]
|
|
(status/call-jail current-chat-id
|
|
path
|
|
params
|
|
#(dispatch [:suggestions-handler {:command command
|
|
:content content
|
|
:chat-id current-chat-id} %])))))
|
|
|
|
(defn cancel-command!
|
|
[{:keys [canceled-command]}]
|
|
(when canceled-command
|
|
(dispatch [:start-cancel-command])))
|
|
|
|
(defn current-command
|
|
[{:keys [current-chat-id] :as db} k]
|
|
(get-in db [:chats current-chat-id :command-input :command k]))
|
|
|
|
(register-handler :set-chat-command-content
|
|
[(after (fn [db]
|
|
(let [trigger (keyword (current-command db :suggestions-trigger))]
|
|
(when (= :on-change trigger)
|
|
(invoke-suggestions-handler! db nil)))))
|
|
(after cancel-command!)
|
|
(after #(dispatch [:clear-validation-errors]))]
|
|
(fn [{:keys [current-chat-id] :as db} [_ content]]
|
|
(let [starts-as-command? (str/starts-with? content cu/command-prefix)
|
|
command? (= :command (current-command db :type))
|
|
{:keys [parameter-idx command]} (commands/get-command-input db)
|
|
parameter-name (-> command :params (get parameter-idx) :name)]
|
|
(as-> db db
|
|
(commands/set-chat-command-content db content)
|
|
(commands/set-command-parameter db parameter-name content)
|
|
(assoc-in db [:chats current-chat-id :input-text] nil)
|
|
(assoc db :canceled-command (and command? (not starts-as-command?)))))))
|
|
|
|
(register-handler :fill-chat-command-content
|
|
(u/side-effect!
|
|
(fn [db [_ content]]
|
|
(let [command? (= :command (current-command db :type))]
|
|
(dispatch
|
|
[:set-chat-command-content
|
|
(if command?
|
|
(str cu/command-prefix content)
|
|
content)])))))
|
|
|
|
(defn command-input
|
|
([{:keys [current-chat-id] :as db}]
|
|
(command-input db current-chat-id))
|
|
([db chat-id]
|
|
(get-in db [:chats chat-id :command-input])))
|
|
|
|
(register-handler ::validate!
|
|
(u/side-effect!
|
|
(fn [_ [_ command-input {:keys [chat-id handler]} {:keys [error result]}]]
|
|
;; todo handle error
|
|
(when-not error
|
|
(let [{:keys [errors validationHandler parameters]} (:returned result)]
|
|
(cond errors
|
|
(do
|
|
(dispatch [:set-chat-ui-props :sending-disabled? false])
|
|
(dispatch [::add-validation-errors chat-id errors]))
|
|
|
|
validationHandler
|
|
(do
|
|
(dispatch [:set-chat-ui-props :sending-disabled? false])
|
|
(dispatch [::validation-handler!
|
|
command-input
|
|
chat-id
|
|
validationHandler
|
|
parameters]))
|
|
|
|
:else (if handler
|
|
(handler)
|
|
(dispatch [::finish-command-staging command-input chat-id]))))))))
|
|
|
|
(register-handler :validate-command
|
|
(u/side-effect!
|
|
(fn [{:keys [current-chat-id current-account-id] :as db} [_ command-input command]]
|
|
(let [command-input (or command-input (commands/get-command-input db))
|
|
command (or command (commands/get-chat-command db))]
|
|
(dispatch [::start-command-validation! {:command-input command-input
|
|
:command command
|
|
:chat-id current-chat-id
|
|
:address current-account-id}])))))
|
|
|
|
(register-handler ::finish-command-staging
|
|
[(after #(dispatch [:start-cancel-command]))]
|
|
(u/side-effect!
|
|
(fn [db [_ command-input chat-id :as parameters]]
|
|
(let [db (assoc-in db [:chats chat-id :input-text] nil)
|
|
{:keys [command to-message-id params]} (or command-input (command-input db))
|
|
message-id (random/id)
|
|
command-info {:command command
|
|
:params params
|
|
:to-message to-message-id
|
|
:created-at (time/now-ms)
|
|
:id message-id
|
|
:chat-id chat-id}
|
|
request-data {:message-id message-id
|
|
:chat-id chat-id
|
|
:content {:command (:name command)
|
|
:params params
|
|
:type (:type command)}
|
|
:on-requested #(dispatch [:send-chat-message command-info])}]
|
|
(dispatch [:set-in [:command->chat (:id command-info)] chat-id])
|
|
(dispatch [:request-command-preview request-data])))))
|
|
|
|
(defn set-chat-command
|
|
[{:keys [current-chat-id] :as db} [_ command-key type]]
|
|
(-> db
|
|
(commands/set-command-input (or type :commands) command-key)
|
|
(assoc-in [:chats current-chat-id :command-input :content] cu/command-prefix)
|
|
(assoc :disable-input true)
|
|
(assoc :just-set-command? true)))
|
|
|
|
(register-handler :set-chat-command
|
|
[(after invoke-suggestions-handler!)
|
|
(after #(dispatch [:set-soft-input-mode :resize]))
|
|
(after #(dispatch [:command-edit-mode]))]
|
|
set-chat-command)
|
|
|
|
(defn set-response-command [db [_ to-message-id command-key params]]
|
|
(-> db
|
|
(commands/set-command-input :responses to-message-id command-key params)
|
|
(assoc :canceled-command false)))
|
|
|
|
(register-handler ::set-response-chat-command
|
|
[(after invoke-suggestions-handler!)
|
|
(after #(dispatch [:command-edit-mode]))
|
|
(after #(dispatch [:set-chat-input-text ""]))]
|
|
set-response-command)
|
|
|
|
(register-handler :set-response-chat-command
|
|
(u/side-effect!
|
|
(fn [{:keys [current-chat-id] :as db}
|
|
[_ to-message-id command-key params]]
|
|
(when (get-in db [:chats current-chat-id :responses command-key])
|
|
(dispatch [::set-response-chat-command to-message-id command-key params])))))
|
|
|
|
(register-handler ::add-validation-errors
|
|
(after #(dispatch [:fix-response-height]))
|
|
(fn [db [_ chat-id errors]]
|
|
(assoc-in db [:custom-validation-errors chat-id]
|
|
(map cu/generate-hiccup errors))))
|
|
|
|
(register-handler :clear-validation-errors
|
|
(fn [db]
|
|
(dissoc db :validation-errors :custom-validation-errors)))
|
|
|
|
(defn dispatch-error!
|
|
[chat-id title description]
|
|
(letfn [(wrap-params [p] (if (seqable? p) p [p]))]
|
|
(dispatch [::set-validation-error
|
|
chat-id
|
|
{:title (apply i18n/label (wrap-params title))
|
|
:description (apply i18n/label (wrap-params description))}])))
|
|
|
|
(def validation-handlers
|
|
{:phone (fn [command-input chat-id [number]]
|
|
(if (pn/valid-mobile-number? number)
|
|
(dispatch [::finish-command-staging command-input chat-id])
|
|
(dispatch-error! chat-id :t/phone-number :t/invalid-phone)))})
|
|
|
|
(defn validator [name]
|
|
(validation-handlers (keyword name)))
|
|
|
|
(register-handler ::validation-handler!
|
|
(u/side-effect!
|
|
(fn [_ [_ command-input chat-id name params]]
|
|
(when-let [handler (validator name)]
|
|
(handler command-input chat-id params)))))
|
|
|
|
(register-handler ::set-validation-error
|
|
(after #(dispatch [:fix-response-height]))
|
|
(fn [db [_ chat-id error]]
|
|
(assoc-in db [:validation-errors chat-id] [error])))
|
|
|
|
(register-handler :invoke-commands-suggestions!
|
|
(u/side-effect!
|
|
invoke-suggestions-handler!))
|
|
|
|
(register-handler :send-command!
|
|
(u/side-effect!
|
|
(fn [{:keys [current-chat-id current-account-id] :as db}]
|
|
(let [{:keys [params] :as command} (commands/get-chat-command db)
|
|
{:keys [parameter-idx]} (commands/get-command-input db)
|
|
|
|
last-parameter? (= (inc parameter-idx) (count params))
|
|
|
|
parameters {:command command :input command-input}
|
|
|
|
{:keys [command content]} (command-input db)
|
|
content' (content-by-command command content)]
|
|
(dispatch [:set-command-parameter
|
|
{:value content'
|
|
:parameter (params parameter-idx)}])
|
|
(if last-parameter?
|
|
(dispatch [:check-suggestions-trigger! parameters])
|
|
(dispatch [::start-command-validation!
|
|
{:chat-id current-chat-id
|
|
:address current-account-id
|
|
:handler #(dispatch [:next-command-parameter])}]))))))
|
|
|
|
(register-handler ::start-command-validation!
|
|
(u/side-effect!
|
|
(fn [db [_ {:keys [command-input chat-id address] :as data}]]
|
|
(let [command-input' (or command-input (commands/get-command-input db))
|
|
{:keys [parameter-idx params command]} command-input'
|
|
{:keys [name type]} command
|
|
current-parameter (-> command
|
|
:params
|
|
(get parameter-idx)
|
|
:name)
|
|
to (get-in db [:contacts chat-id :address])
|
|
context {:current-parameter current-parameter
|
|
:from address
|
|
:to to}
|
|
path [(if (= :command type) :commands :responses)
|
|
name
|
|
:validator]
|
|
parameters {:context context
|
|
:parameters params}]
|
|
(status/call-jail chat-id
|
|
path
|
|
parameters
|
|
#(dispatch [::validate! command-input data %]))))))
|
|
|
|
(register-handler :request-command-preview
|
|
(u/side-effect!
|
|
(fn [{:keys [chats]} [_ {{:keys [command params content-command type]} :content
|
|
:keys [message-id chat-id on-requested] :as message} data-type]]
|
|
(if-not (get-in chats [chat-id :commands-loaded])
|
|
(do (dispatch [:add-commands-loading-callback
|
|
chat-id
|
|
#(dispatch [:request-command-preview message data-type])])
|
|
(dispatch [:load-commands! chat-id]))
|
|
(let [data-type (or data-type :preview)
|
|
path [(if (= :response (keyword type)) :responses :commands)
|
|
(if content-command content-command command)
|
|
data-type]
|
|
params {:parameters params
|
|
:context (merge {:platform platform/platform} i18n/delimeters)}
|
|
callback #(do (when-let [result (get-in % [:result :returned])]
|
|
(dispatch [:set-in [:message-data data-type message-id]
|
|
(if (string? result)
|
|
result
|
|
(cu/generate-hiccup result))]))
|
|
(when on-requested (on-requested %)))]
|
|
(status/call-jail chat-id path params callback))))))
|
|
|
|
(register-handler :set-command-parameter
|
|
(fn [db [_ {:keys [value parameter]}]]
|
|
(let [name (:name parameter)]
|
|
(commands/set-command-parameter db name value))))
|
|
|
|
(register-handler :next-command-parameter
|
|
(fn [db _]
|
|
(commands/next-command-parameter db)))
|
|
|
|
(register-handler :check-suggestions-trigger!
|
|
(u/side-effect!
|
|
(fn [_ [_ {:keys [command]}]]
|
|
(let [suggestions-trigger (keyword (:suggestions-trigger command))]
|
|
(if (= :on-send suggestions-trigger)
|
|
(do
|
|
(dispatch [:invoke-commands-suggestions!])
|
|
(react-comp/dismiss-keyboard!))
|
|
(do
|
|
(dispatch [:set-chat-ui-props :sending-disabled? true])
|
|
(dispatch [:validate-command])))))))
|
|
|
|
(defn fib-lazy
|
|
([] (fib-lazy 0 1))
|
|
([x1 x2] (cons x1 (lazy-seq (fib-lazy x2 (+ x1 x2))))))
|