Refactor command data loading + chat input handling
Also accomplished was removal of redundant preview loading and command markup is now stored as cljs data in app-db, only being translated to RN components in subscriptions
This commit is contained in:
parent
7506689fe5
commit
e3f27ee5ee
|
@ -0,0 +1,122 @@
|
||||||
|
(ns status-im.chat.events.commands
|
||||||
|
(:require [cljs.reader :as reader]
|
||||||
|
[clojure.string :as str]
|
||||||
|
[re-frame.core :refer [reg-fx reg-cofx inject-cofx dispatch trim-v]]
|
||||||
|
[taoensso.timbre :as log]
|
||||||
|
[status-im.data-store.messages :as msg-store]
|
||||||
|
[status-im.utils.handlers :refer [register-handler-fx]]
|
||||||
|
[status-im.components.status :as status]
|
||||||
|
[status-im.chat.constants :as const]
|
||||||
|
[status-im.commands.utils :as commands-utils]
|
||||||
|
[status-im.i18n :as i18n]
|
||||||
|
[status-im.utils.platform :as platform]))
|
||||||
|
|
||||||
|
;;;; Helper fns
|
||||||
|
|
||||||
|
(defn generate-context
|
||||||
|
"Generates context for jail call"
|
||||||
|
[{:keys [current-account-id chats]} chat-id to]
|
||||||
|
(merge {:platform platform/platform
|
||||||
|
:from current-account-id
|
||||||
|
:to to
|
||||||
|
:chat {:chat-id chat-id
|
||||||
|
:group-chat (get-in chats [chat-id :group-chat])}}
|
||||||
|
i18n/delimeters))
|
||||||
|
|
||||||
|
;;;; Coeffects
|
||||||
|
|
||||||
|
(reg-cofx
|
||||||
|
::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)))
|
||||||
|
|
||||||
|
|
||||||
|
(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))))))))
|
||||||
|
|
||||||
|
;;;; 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)))))
|
||||||
|
|
||||||
|
(register-handler-fx
|
||||||
|
:request-command-data
|
||||||
|
[trim-v]
|
||||||
|
(fn [{:keys [db]}
|
||||||
|
[{{:keys [command content-command params type]} :content
|
||||||
|
:keys [chat-id jail-id] :as message}
|
||||||
|
data-type]]
|
||||||
|
(let [{:keys [current-account-id chats]
|
||||||
|
: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)}]
|
||||||
|
{: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))))
|
||||||
|
|
||||||
|
(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]}))))))
|
|
@ -0,0 +1,493 @@
|
||||||
|
(ns status-im.chat.events.input
|
||||||
|
(:require [clojure.string :as str]
|
||||||
|
[re-frame.core :refer [reg-fx reg-cofx inject-cofx dispatch trim-v]]
|
||||||
|
[taoensso.timbre :as log]
|
||||||
|
[status-im.chat.constants :as const]
|
||||||
|
[status-im.chat.utils :as chat-utils]
|
||||||
|
[status-im.chat.models.input :as input-model]
|
||||||
|
[status-im.chat.models.suggestions :as suggestions]
|
||||||
|
[status-im.components.react :as react-comp]
|
||||||
|
[status-im.components.status :as status]
|
||||||
|
[status-im.utils.datetime :as time]
|
||||||
|
[status-im.utils.handlers :refer [register-handler-db register-handler-fx]]
|
||||||
|
[status-im.utils.random :as random]
|
||||||
|
[status-im.i18n :as i18n]))
|
||||||
|
|
||||||
|
;;;; Coeffects
|
||||||
|
|
||||||
|
(reg-cofx
|
||||||
|
:now
|
||||||
|
(fn [coeffects _]
|
||||||
|
(assoc coeffects :now (time/now-ms))))
|
||||||
|
|
||||||
|
(reg-cofx
|
||||||
|
:random-id
|
||||||
|
(fn [coeffects _]
|
||||||
|
(assoc coeffects :random-id (random/id))))
|
||||||
|
|
||||||
|
;;;; Effects
|
||||||
|
|
||||||
|
(reg-fx
|
||||||
|
::focus-rn-component
|
||||||
|
(fn [ref]
|
||||||
|
(try
|
||||||
|
(.focus ref)
|
||||||
|
(catch :default e
|
||||||
|
(log/debug "Cannot focus the reference")))))
|
||||||
|
|
||||||
|
(reg-fx
|
||||||
|
::blur-rn-component
|
||||||
|
(fn [ref]
|
||||||
|
(try
|
||||||
|
(.blur ref)
|
||||||
|
(catch :default e
|
||||||
|
(log/debug "Cannot blur the reference")))))
|
||||||
|
|
||||||
|
(reg-fx
|
||||||
|
::dismiss-keyboard
|
||||||
|
(fn [_]
|
||||||
|
(react-comp/dismiss-keyboard!)))
|
||||||
|
|
||||||
|
(reg-fx
|
||||||
|
::set-native-props
|
||||||
|
(fn [{:keys [ref props]}]
|
||||||
|
(.setNativeProps ref (clj->js props))))
|
||||||
|
|
||||||
|
(reg-fx
|
||||||
|
:chat-fx/call-jail-function
|
||||||
|
(fn [{:keys [chat-id function callback-events-creator] :as opts}]
|
||||||
|
(let [path [:functions function]
|
||||||
|
params (select-keys opts [:parameters :context])]
|
||||||
|
(status/call-jail
|
||||||
|
{:jail-id chat-id
|
||||||
|
:path path
|
||||||
|
:params params
|
||||||
|
:callback (fn [jail-response]
|
||||||
|
(doseq [event (if callback-events-creator
|
||||||
|
(callback-events-creator jail-response)
|
||||||
|
[[:received-bot-response
|
||||||
|
{:chat-id chat-id}
|
||||||
|
jail-response]])
|
||||||
|
:when event]
|
||||||
|
(dispatch event)))}))))
|
||||||
|
|
||||||
|
;;;; Handlers
|
||||||
|
|
||||||
|
(register-handler-db
|
||||||
|
:update-input-data
|
||||||
|
(fn [db]
|
||||||
|
(input-model/modified-db-after-change db)))
|
||||||
|
|
||||||
|
(register-handler-fx
|
||||||
|
:set-chat-input-text
|
||||||
|
[trim-v]
|
||||||
|
(fn [{{:keys [current-chat-id chats chat-ui-props] :as db} :db} [text chat-id]]
|
||||||
|
(let [chat-id (or chat-id current-chat-id)]
|
||||||
|
{:db (assoc-in db
|
||||||
|
[:chats chat-id :input-text]
|
||||||
|
(input-model/text->emoji text))
|
||||||
|
:dispatch [:update-suggestions chat-id text]})))
|
||||||
|
|
||||||
|
(register-handler-fx
|
||||||
|
:add-to-chat-input-text
|
||||||
|
[trim-v]
|
||||||
|
(fn [{{:keys [chats current-chat-id]} :db} [text-to-add]]
|
||||||
|
(let [input-text (get-in chats [current-chat-id :input-text])]
|
||||||
|
{:dispatch [:set-chat-input-text (str input-text text-to-add)]})))
|
||||||
|
|
||||||
|
(register-handler-fx
|
||||||
|
:select-chat-input-command
|
||||||
|
[trim-v]
|
||||||
|
(fn [{{:keys [current-chat-id chat-ui-props]} :db}
|
||||||
|
[{:keys [prefill prefill-bot-db sequential-params name] :as command} metadata prevent-auto-focus?]]
|
||||||
|
(cond-> {:dispatch-n [[:set-chat-input-text (str (chat-utils/command-name command)
|
||||||
|
const/spacing-char
|
||||||
|
(when-not sequential-params
|
||||||
|
(input-model/join-command-args prefill)))]
|
||||||
|
[:clear-bot-db]
|
||||||
|
[:set-chat-input-metadata metadata]
|
||||||
|
[:set-chat-ui-props {:show-suggestions? false
|
||||||
|
:result-box nil
|
||||||
|
:validation-messages nil
|
||||||
|
:prev-command name}]
|
||||||
|
[:load-chat-parameter-box command 0]]}
|
||||||
|
|
||||||
|
prefill-bot-db
|
||||||
|
(update :dispatch-n conj [:update-bot-db {:bot current-chat-id
|
||||||
|
:db prefill-bot-db}])
|
||||||
|
|
||||||
|
(not (and sequential-params
|
||||||
|
prevent-auto-focus?))
|
||||||
|
(update :dispatch-n conj [:chat-input-focus :input-ref])
|
||||||
|
|
||||||
|
sequential-params
|
||||||
|
(assoc :dispatch-later (cond-> [{:ms 100 :dispatch [:set-chat-seq-arg-input-text
|
||||||
|
(str/join const/spacing-char prefill)]}]
|
||||||
|
(not prevent-auto-focus?)
|
||||||
|
(conj {:ms 100 :dispatch [:chat-input-focus :seq-input-ref]}))))))
|
||||||
|
|
||||||
|
(register-handler-db
|
||||||
|
:set-chat-input-metadata
|
||||||
|
[trim-v]
|
||||||
|
(fn [{:keys [current-chat-id] :as db} [data chat-id]]
|
||||||
|
(let [chat-id (or chat-id current-chat-id)]
|
||||||
|
(assoc-in db [:chats chat-id :input-metadata] data))))
|
||||||
|
|
||||||
|
(register-handler-fx
|
||||||
|
:set-command-argument
|
||||||
|
[trim-v]
|
||||||
|
(fn [{{:keys [current-chat-id] :as db} :db} [[index arg move-to-next?]]]
|
||||||
|
(let [command (-> (get-in db [:chats current-chat-id :input-text])
|
||||||
|
(input-model/split-command-args))
|
||||||
|
seq-params? (-> (input-model/selected-chat-command db current-chat-id)
|
||||||
|
(get-in [:command :sequential-params]))
|
||||||
|
event-to-dispatch (if seq-params?
|
||||||
|
[:set-chat-seq-arg-input-text arg]
|
||||||
|
(let [arg (str/replace arg (re-pattern const/arg-wrapping-char) "")
|
||||||
|
command-name (first command)
|
||||||
|
command-args (into [] (rest command))
|
||||||
|
command-args (if (< index (count command-args))
|
||||||
|
(assoc command-args index arg)
|
||||||
|
(conj command-args arg))]
|
||||||
|
[:set-chat-input-text (str command-name
|
||||||
|
const/spacing-char
|
||||||
|
(input-model/join-command-args command-args)
|
||||||
|
(when (and move-to-next?
|
||||||
|
(= index (dec (count command-args))))
|
||||||
|
const/spacing-char))]))]
|
||||||
|
{:dispatch event-to-dispatch})))
|
||||||
|
|
||||||
|
(register-handler-fx
|
||||||
|
:chat-input-focus
|
||||||
|
[trim-v]
|
||||||
|
(fn [{{:keys [current-chat-id chat-ui-props]} :db} [ref]]
|
||||||
|
(when-let [cmp-ref (get-in chat-ui-props [current-chat-id ref])]
|
||||||
|
{::focus-rn-component cmp-ref})))
|
||||||
|
|
||||||
|
(register-handler-fx
|
||||||
|
:chat-input-blur
|
||||||
|
[trim-v]
|
||||||
|
(fn [{{:keys [current-chat-id chat-ui-props]} :db} [ref]]
|
||||||
|
(when-let [cmp-ref (get-in chat-ui-props [current-chat-id ref])]
|
||||||
|
{::blur-rn-component cmp-ref})))
|
||||||
|
|
||||||
|
(register-handler-fx
|
||||||
|
:update-suggestions
|
||||||
|
[trim-v]
|
||||||
|
(fn [{{:keys [current-chat-id] :as db} :db} [chat-id text]]
|
||||||
|
(let [chat-id (or chat-id current-chat-id)
|
||||||
|
chat-text (str/trim (or text (get-in db [:chats chat-id :input-text]) ""))
|
||||||
|
requests (->> (suggestions/get-request-suggestions db chat-text)
|
||||||
|
(remove (fn [{:keys [type]}]
|
||||||
|
(= type :grant-permissions))))
|
||||||
|
commands (suggestions/get-command-suggestions db chat-text)
|
||||||
|
global-commands (suggestions/get-global-command-suggestions db chat-text)
|
||||||
|
all-commands (->> (into global-commands commands)
|
||||||
|
(remove (fn [[k {:keys [hidden?]}]] hidden?))
|
||||||
|
(into {}))
|
||||||
|
{:keys [dapp?]} (get-in db [:contacts/contacts chat-id])
|
||||||
|
new-db (cond-> db
|
||||||
|
true (assoc-in [:chats chat-id :request-suggestions] requests)
|
||||||
|
true (assoc-in [:chats chat-id :command-suggestions] all-commands)
|
||||||
|
(and dapp?
|
||||||
|
(str/blank? chat-text))
|
||||||
|
(assoc-in [:chats chat-id :parameter-boxes :message] nil))
|
||||||
|
new-event (when (and dapp?
|
||||||
|
(not (str/blank? chat-text))
|
||||||
|
(every? empty? [requests commands]))
|
||||||
|
[::check-dapp-suggestions chat-id chat-text])]
|
||||||
|
(cond-> {:db new-db}
|
||||||
|
new-event (assoc :dispatch new-event)))))
|
||||||
|
|
||||||
|
(register-handler-fx
|
||||||
|
:load-chat-parameter-box
|
||||||
|
[trim-v]
|
||||||
|
(fn [{{:keys [current-chat-id bot-db current-account-id] :as db} :db}
|
||||||
|
[{:keys [name type bot owner-id] :as command}]]
|
||||||
|
(let [parameter-index (input-model/argument-position db current-chat-id)]
|
||||||
|
(when (and command (> parameter-index -1))
|
||||||
|
(let [data (get-in db [:local-storage current-chat-id])
|
||||||
|
bot-db (get bot-db (or bot current-chat-id))
|
||||||
|
path [(if (= :command type) :commands :responses)
|
||||||
|
name
|
||||||
|
:params
|
||||||
|
parameter-index
|
||||||
|
:suggestions]
|
||||||
|
args (-> (get-in db [:chats current-chat-id :input-text])
|
||||||
|
(input-model/split-command-args)
|
||||||
|
(rest))
|
||||||
|
seq-arg (get-in db [:chats current-chat-id :seq-argument-input-text])
|
||||||
|
to (get-in db [:contacts/contacts current-chat-id :address])
|
||||||
|
params {:parameters {:args args
|
||||||
|
:bot-db bot-db
|
||||||
|
:seq-arg seq-arg}
|
||||||
|
:context (merge {:data data
|
||||||
|
:from current-account-id
|
||||||
|
:to to}
|
||||||
|
(input-model/command-dependent-context-params current-chat-id command))}]
|
||||||
|
{:chat-fx/call-jail {:jail-id (or bot owner-id current-chat-id)
|
||||||
|
:path path
|
||||||
|
:params params
|
||||||
|
:callback-events-creator (fn [jail-response]
|
||||||
|
[[:received-bot-response
|
||||||
|
{:chat-id current-chat-id
|
||||||
|
:command command
|
||||||
|
:parameter-index parameter-index}
|
||||||
|
jail-response]])}})))))
|
||||||
|
|
||||||
|
(register-handler-fx
|
||||||
|
::send-message
|
||||||
|
[trim-v]
|
||||||
|
(fn [{{:keys [current-public-key current-account-id] :as db} :db} [command chat-id]]
|
||||||
|
(let [text (get-in db [:chats chat-id :input-text])
|
||||||
|
data {:message text
|
||||||
|
:command command
|
||||||
|
:chat-id chat-id
|
||||||
|
:identity current-public-key
|
||||||
|
:address current-account-id}
|
||||||
|
events [[:set-chat-input-text nil chat-id]
|
||||||
|
[:set-chat-input-metadata nil chat-id]
|
||||||
|
[:set-chat-ui-props {:sending-in-progress? false}]]]
|
||||||
|
{:dispatch-n (if command
|
||||||
|
(conj events [:check-commands-handlers! data])
|
||||||
|
(if (str/blank? text)
|
||||||
|
events
|
||||||
|
(conj events [:prepare-message data])))})))
|
||||||
|
|
||||||
|
(register-handler-fx
|
||||||
|
:proceed-command
|
||||||
|
[trim-v]
|
||||||
|
(fn [_ [{{:keys [bot]} :command :as content} chat-id]]
|
||||||
|
(let [params {:content content
|
||||||
|
:chat-id chat-id
|
||||||
|
:jail-id (or bot chat-id)}
|
||||||
|
on-send-params (merge params
|
||||||
|
{:data-type :on-send
|
||||||
|
:event-after-creator (fn [_ jail-response]
|
||||||
|
[::send-command jail-response content chat-id])})
|
||||||
|
after-validation-events [[::request-command-data on-send-params]]
|
||||||
|
validation-params (merge params
|
||||||
|
{:data-type :validator
|
||||||
|
:event-after-creator (fn [_ jail-response]
|
||||||
|
[::proceed-validation
|
||||||
|
jail-response
|
||||||
|
after-validation-events])})]
|
||||||
|
{:dispatch [::request-command-data validation-params]})))
|
||||||
|
|
||||||
|
(register-handler-fx
|
||||||
|
::proceed-validation
|
||||||
|
[trim-v]
|
||||||
|
(fn [_ [{:keys [markup validationHandler parameters]} proceed-events]]
|
||||||
|
(let [error-events-creator (fn [validator-result]
|
||||||
|
[[:set-chat-ui-props {:validation-messages validator-result
|
||||||
|
:sending-in-progress? false}]])
|
||||||
|
events (cond
|
||||||
|
markup
|
||||||
|
(error-events-creator markup)
|
||||||
|
|
||||||
|
validationHandler
|
||||||
|
[[::execute-validation-handler
|
||||||
|
validationHandler parameters error-events-creator proceed-events]
|
||||||
|
[:set-chat-ui-props {:sending-in-progress? false}]]
|
||||||
|
|
||||||
|
:default
|
||||||
|
proceed-events)]
|
||||||
|
{:dispatch-n events})))
|
||||||
|
|
||||||
|
(register-handler-fx
|
||||||
|
::execute-validation-handler
|
||||||
|
[trim-v]
|
||||||
|
(fn [_ [validation-handler-name params error-events-creator proceed-events]]
|
||||||
|
(let [error-events (when-let [validator (input-model/validation-handler validation-handler-name)]
|
||||||
|
(validator params error-events-creator))]
|
||||||
|
{:dispatch-n (or error-events proceed-events)})))
|
||||||
|
|
||||||
|
(register-handler-fx
|
||||||
|
::send-command
|
||||||
|
[trim-v]
|
||||||
|
(fn [_ [on-send {{:keys [fullscreen bot]} :command :as content} chat-id]]
|
||||||
|
(if on-send
|
||||||
|
{:dispatch-n (cond-> [[:set-chat-ui-props {:result-box on-send
|
||||||
|
:sending-in-progress? false}]]
|
||||||
|
fullscreen
|
||||||
|
(conj [:choose-predefined-expandable-height :result-box :max]))
|
||||||
|
::dismiss-keyboard nil}
|
||||||
|
{:dispatch [::request-command-data
|
||||||
|
{:content content
|
||||||
|
:chat-id chat-id
|
||||||
|
:jail-id (or bot chat-id)
|
||||||
|
:data-type :preview
|
||||||
|
:event-after-creator (fn [command-message _]
|
||||||
|
[::send-message command-message chat-id])}]})))
|
||||||
|
|
||||||
|
(register-handler-fx
|
||||||
|
::request-command-data
|
||||||
|
[trim-v (inject-cofx :random-id) (inject-cofx :now)]
|
||||||
|
(fn [{{:keys [bot-db] :contacts/keys [contacts]} :db
|
||||||
|
message-id :random-id
|
||||||
|
current-time :now}
|
||||||
|
[{{:keys [command
|
||||||
|
metadata
|
||||||
|
args]
|
||||||
|
:as content} :content
|
||||||
|
:keys [chat-id jail-id data-type event-after-creator]}]]
|
||||||
|
(let [{:keys [dapp? dapp-url name]} (get contacts chat-id)
|
||||||
|
metadata (merge metadata
|
||||||
|
(when dapp?
|
||||||
|
{:url (i18n/get-contact-translated chat-id :dapp-url dapp-url)
|
||||||
|
:name (i18n/get-contact-translated chat-id :name name)}))
|
||||||
|
owner-id (:owner-id command)
|
||||||
|
bot-db (get bot-db chat-id)
|
||||||
|
params (merge (input-model/args->params content)
|
||||||
|
{:bot-db bot-db
|
||||||
|
:metadata metadata})
|
||||||
|
|
||||||
|
command-message {:command command
|
||||||
|
:params params
|
||||||
|
:to-message (:to-message-id metadata)
|
||||||
|
:created-at current-time
|
||||||
|
:id message-id
|
||||||
|
:chat-id chat-id
|
||||||
|
:jail-id (or owner-id jail-id)}
|
||||||
|
|
||||||
|
request-data {:message-id message-id
|
||||||
|
:chat-id chat-id
|
||||||
|
:jail-id (or owner-id jail-id)
|
||||||
|
:content {:command (:name command)
|
||||||
|
:params params
|
||||||
|
:type (:type command)}
|
||||||
|
:on-requested (fn [jail-response]
|
||||||
|
(event-after-creator command-message jail-response))}]
|
||||||
|
{:dispatch [:request-command-data request-data data-type]})))
|
||||||
|
|
||||||
|
(register-handler-fx
|
||||||
|
:send-current-message
|
||||||
|
(fn [{{:keys [current-chat-id] :as db} :db} _]
|
||||||
|
(let [chat-command (input-model/selected-chat-command db current-chat-id)
|
||||||
|
seq-command? (get-in chat-command [:command :sequential-params])
|
||||||
|
chat-command (if seq-command?
|
||||||
|
(let [args (get-in db [:chats current-chat-id :seq-arguments])]
|
||||||
|
(assoc chat-command :args args))
|
||||||
|
(update chat-command :args #(remove str/blank? %)))
|
||||||
|
set-chat-ui-props-event [:set-chat-ui-props {:sending-in-progress? true}]
|
||||||
|
additional-events (if (:command chat-command)
|
||||||
|
(if (= :complete (input-model/command-completion chat-command))
|
||||||
|
[[:proceed-command chat-command current-chat-id]
|
||||||
|
[:clear-seq-arguments current-chat-id]]
|
||||||
|
(let [text (get-in db [:chats current-chat-id :input-text])]
|
||||||
|
[[:set-chat-ui-props {:sending-in-progress? false}]
|
||||||
|
(when-not (input-model/text-ends-with-space? text)
|
||||||
|
[:set-chat-input-text (str text const/spacing-char)])]))
|
||||||
|
[[::send-message nil current-chat-id]])]
|
||||||
|
{:dispatch-n (into [set-chat-ui-props-event]
|
||||||
|
(remove nil? additional-events))})))
|
||||||
|
|
||||||
|
(register-handler-fx
|
||||||
|
::check-dapp-suggestions
|
||||||
|
[trim-v]
|
||||||
|
(fn [{{:keys [current-account-id] :as db} :db} [chat-id text]]
|
||||||
|
(let [data (get-in db [:local-storage chat-id])]
|
||||||
|
{:chat-fx/call-jail-function {:chat-id chat-id
|
||||||
|
:function :on-message-input-change
|
||||||
|
:parameters {:message text}
|
||||||
|
:context {:data data
|
||||||
|
:from current-account-id}}})))
|
||||||
|
|
||||||
|
(register-handler-db
|
||||||
|
:clear-seq-arguments
|
||||||
|
[trim-v]
|
||||||
|
(fn [{:keys [current-chat-id chats] :as db} [chat-id]]
|
||||||
|
(let [chat-id (or chat-id current-chat-id)]
|
||||||
|
(-> db
|
||||||
|
(assoc-in [:chats chat-id :seq-arguments] [])
|
||||||
|
(assoc-in [:chats chat-id :seq-argument-input-text] nil)))))
|
||||||
|
|
||||||
|
(register-handler-db
|
||||||
|
::update-seq-arguments
|
||||||
|
[trim-v]
|
||||||
|
(fn [{:keys [current-chat-id chats] :as db} [chat-id]]
|
||||||
|
(let [chat-id (or chat-id current-chat-id)
|
||||||
|
text (get-in chats [chat-id :seq-argument-input-text])]
|
||||||
|
(-> db
|
||||||
|
(update-in [:chats chat-id :seq-arguments] #(into [] (conj % text)))
|
||||||
|
(assoc-in [:chats chat-id :seq-argument-input-text] nil)))))
|
||||||
|
|
||||||
|
(register-handler-fx
|
||||||
|
:send-seq-argument
|
||||||
|
[trim-v]
|
||||||
|
(fn [{{:keys [current-chat-id chats] :as db} :db} [chat-id]]
|
||||||
|
(let [chat-id (or chat-id current-chat-id)
|
||||||
|
text (get-in chats [chat-id :seq-argument-input-text])
|
||||||
|
seq-arguments (get-in chats [chat-id :seq-arguments])
|
||||||
|
command (-> (input-model/selected-chat-command db chat-id)
|
||||||
|
(assoc :args (into [] (conj seq-arguments text))))]
|
||||||
|
{:dispatch [::request-command-data
|
||||||
|
{:content command
|
||||||
|
:chat-id chat-id
|
||||||
|
:jail-id (or (get-in command [:command :bot]) chat-id)
|
||||||
|
:data-type :validator
|
||||||
|
:event-after-creator (fn [_ jail-response]
|
||||||
|
[::proceed-validation
|
||||||
|
jail-response
|
||||||
|
[[::update-seq-arguments chat-id]
|
||||||
|
[:send-current-message]]])}]})))
|
||||||
|
|
||||||
|
(register-handler-db
|
||||||
|
:set-chat-seq-arg-input-text
|
||||||
|
[trim-v]
|
||||||
|
(fn [{:keys [current-chat-id] :as db} [text chat-id]]
|
||||||
|
(let [chat-id (or chat-id current-chat-id)]
|
||||||
|
(assoc-in db [:chats chat-id :seq-argument-input-text] text))))
|
||||||
|
|
||||||
|
(register-handler-fx
|
||||||
|
:update-text-selection
|
||||||
|
[trim-v]
|
||||||
|
(fn [{{:keys [current-chat-id] :as db} :db} [selection]]
|
||||||
|
(let [input-text (get-in db [:chats current-chat-id :input-text])
|
||||||
|
command (input-model/selected-chat-command db current-chat-id input-text)]
|
||||||
|
(cond-> {:dispatch-n [[:set-chat-ui-props {:selection selection}]
|
||||||
|
[:load-chat-parameter-box (:command command)]]}
|
||||||
|
|
||||||
|
(and (= selection (+ (count const/command-char)
|
||||||
|
(count (get-in command [:command :name]))
|
||||||
|
(count const/spacing-char)))
|
||||||
|
(get-in command [:command :sequential-params]))
|
||||||
|
(update :dispatch-n conj [:chat-input-focus :seq-input-ref])))))
|
||||||
|
|
||||||
|
(register-handler-fx
|
||||||
|
:select-prev-argument
|
||||||
|
(fn [{{:keys [chat-ui-props current-chat-id] :as db} :db} _]
|
||||||
|
(let [input-text (get-in db [:chats current-chat-id :input-text])
|
||||||
|
command (input-model/selected-chat-command db current-chat-id input-text)]
|
||||||
|
(if (get-in command [:command :sequential-params])
|
||||||
|
{:dispatch-n [[:set-command-argument [0 "" false]]
|
||||||
|
[:set-chat-seq-arg-input-text ""]
|
||||||
|
[:load-chat-parameter-box (:command command)]]}
|
||||||
|
(let [arg-pos (input-model/argument-position db current-chat-id)]
|
||||||
|
(when (pos? arg-pos)
|
||||||
|
(let [input-text (get-in db [:chats current-chat-id :input-text])
|
||||||
|
new-sel (->> (input-model/split-command-args input-text)
|
||||||
|
(take (inc arg-pos))
|
||||||
|
(input-model/join-command-args)
|
||||||
|
(count))
|
||||||
|
ref (get-in chat-ui-props [current-chat-id :input-ref])]
|
||||||
|
{::set-native-props {:ref ref
|
||||||
|
:props {:selection {:start new-sel :end new-sel}}}
|
||||||
|
:dispatch [:update-text-selection new-sel]})))))))
|
||||||
|
|
||||||
|
(register-handler-fx
|
||||||
|
:select-next-argument
|
||||||
|
(fn [{{:keys [chat-ui-props current-chat-id] :as db} :db} _]
|
||||||
|
(let [arg-pos (input-model/argument-position db current-chat-id)]
|
||||||
|
(let [input-text (get-in db [:chats current-chat-id :input-text])
|
||||||
|
command-args (cond-> (input-model/split-command-args input-text)
|
||||||
|
(input-model/text-ends-with-space? input-text) (conj ""))
|
||||||
|
new-sel (->> command-args
|
||||||
|
(take (+ 3 arg-pos))
|
||||||
|
(input-model/join-command-args)
|
||||||
|
count
|
||||||
|
(min (count input-text)))
|
||||||
|
ref (get-in chat-ui-props [current-chat-id :input-ref])]
|
||||||
|
{::set-native-props {:ref ref
|
||||||
|
:props {:selection {:start new-sel :end new-sel}}}
|
||||||
|
:dispatch [:update-text-selection new-sel]}))))
|
|
@ -28,8 +28,8 @@
|
||||||
[status-im.utils.types :refer [json->clj]]
|
[status-im.utils.types :refer [json->clj]]
|
||||||
[status-im.chat.utils :refer [console? not-console? safe-trim]]
|
[status-im.chat.utils :refer [console? not-console? safe-trim]]
|
||||||
[status-im.utils.gfycat.core :refer [generate-gfy]]
|
[status-im.utils.gfycat.core :refer [generate-gfy]]
|
||||||
status-im.chat.handlers.input
|
status-im.chat.events.input
|
||||||
status-im.chat.handlers.commands
|
status-im.chat.events.commands
|
||||||
status-im.chat.handlers.animation
|
status-im.chat.handlers.animation
|
||||||
status-im.chat.handlers.requests
|
status-im.chat.handlers.requests
|
||||||
status-im.chat.handlers.unviewed-messages
|
status-im.chat.handlers.unviewed-messages
|
||||||
|
@ -186,14 +186,7 @@
|
||||||
(defn load-messages!
|
(defn load-messages!
|
||||||
([db] (load-messages! db nil))
|
([db] (load-messages! db nil))
|
||||||
([{:keys [current-chat-id] :as db} _]
|
([{:keys [current-chat-id] :as db} _]
|
||||||
(let [messages (messages/get-by-chat-id current-chat-id)]
|
(let [messages (messages/get-by-chat-id current-chat-id)]
|
||||||
(doseq [{:keys [content] :as message} messages]
|
|
||||||
(when (and (:command content)
|
|
||||||
(not (:content content)))
|
|
||||||
;; todo rewrite it so that commands defined outside chat's context
|
|
||||||
;; (bots' commands in group chats and global commands in all chats)
|
|
||||||
;; could be rendered properly
|
|
||||||
(dispatch [:request-command-data (assoc message :chat-id current-chat-id)])))
|
|
||||||
(assoc db :messages messages))))
|
(assoc db :messages messages))))
|
||||||
|
|
||||||
(defn init-chat
|
(defn init-chat
|
||||||
|
|
|
@ -1,84 +0,0 @@
|
||||||
(ns status-im.chat.handlers.commands
|
|
||||||
(:require [cljs.reader :as reader]
|
|
||||||
[clojure.string :as str]
|
|
||||||
[re-frame.core :refer [enrich after dispatch]]
|
|
||||||
[status-im.data-store.messages :as messages]
|
|
||||||
[status-im.utils.handlers :as handlers]
|
|
||||||
[status-im.components.status :as status]
|
|
||||||
[status-im.chat.constants :as const]
|
|
||||||
[status-im.commands.utils :as cu]
|
|
||||||
[status-im.i18n :as i18n]
|
|
||||||
[status-im.utils.platform :as platform]
|
|
||||||
[taoensso.timbre :as log]))
|
|
||||||
|
|
||||||
(defn generate-context [{:keys [current-account-id chats] :as db} chat-id to]
|
|
||||||
(merge {:platform platform/platform
|
|
||||||
:from current-account-id
|
|
||||||
:to to
|
|
||||||
:chat {:chat-id chat-id
|
|
||||||
:group-chat (get-in chats [chat-id :group-chat])}}
|
|
||||||
i18n/delimeters))
|
|
||||||
|
|
||||||
(handlers/register-handler :request-command-data
|
|
||||||
(handlers/side-effect!
|
|
||||||
(fn [{:keys [current-account-id chats]
|
|
||||||
:contacts/keys [contacts] :as db}
|
|
||||||
[_ {{:keys [command params content-command type]} :content
|
|
||||||
:keys [message-id chat-id jail-id on-requested from] :as message} data-type]]
|
|
||||||
(let [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-not (get-in contacts [jail-id' :commands-loaded?])
|
|
||||||
(do (dispatch [:add-commands-loading-callback
|
|
||||||
jail-id'
|
|
||||||
#(dispatch [:request-command-data message data-type])])
|
|
||||||
(dispatch [:load-commands! jail-id']))
|
|
||||||
(let [path [(if (= :response (keyword type)) :responses :commands)
|
|
||||||
(if content-command content-command command)
|
|
||||||
data-type]
|
|
||||||
to (get-in contacts [chat-id :address])
|
|
||||||
params {:parameters params
|
|
||||||
:context (generate-context db chat-id to)}
|
|
||||||
callback #(let [result (get-in % [:result :returned])
|
|
||||||
result' (if (:markup result)
|
|
||||||
(update result :markup cu/generate-hiccup)
|
|
||||||
result)]
|
|
||||||
;; don't fill message data with nil results
|
|
||||||
(when result'
|
|
||||||
(dispatch [:set-in [:message-data data-type message-id] result']))
|
|
||||||
(when (and result (= :preview data-type))
|
|
||||||
;; update message in realm with serialized preview
|
|
||||||
(messages/update {:message-id message-id
|
|
||||||
:preview (prn-str result)}))
|
|
||||||
(when on-requested (on-requested result')))]
|
|
||||||
;chat-id path params callback lock? type
|
|
||||||
(status/call-jail {:jail-id jail-id'
|
|
||||||
:path path
|
|
||||||
:params params
|
|
||||||
:callback callback})))))))
|
|
||||||
|
|
||||||
(handlers/register-handler :execute-command-immediately
|
|
||||||
(handlers/side-effect!
|
|
||||||
(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)))))
|
|
||||||
|
|
||||||
(handlers/register-handler :request-command-preview
|
|
||||||
(handlers/side-effect!
|
|
||||||
(fn [db [_ {:keys [message-id] :as message}]]
|
|
||||||
(let [previews (get-in db [:message-data :preview])]
|
|
||||||
(when-not (contains? previews message-id)
|
|
||||||
(let [{serialized-preview :preview} (messages/get-by-id 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
|
|
||||||
(dispatch [:set-in [:message-data :preview message-id]
|
|
||||||
(-> serialized-preview
|
|
||||||
reader/read-string
|
|
||||||
(update :markup cu/generate-hiccup))])
|
|
||||||
(dispatch [:request-command-data message :preview]))))))))
|
|
|
@ -1,396 +0,0 @@
|
||||||
(ns status-im.chat.handlers.input
|
|
||||||
(:require [re-frame.core :refer [enrich after dispatch]]
|
|
||||||
[taoensso.timbre :as log]
|
|
||||||
[status-im.chat.constants :as const]
|
|
||||||
[status-im.chat.utils :as chat-utils]
|
|
||||||
[status-im.chat.models.input :as input-model]
|
|
||||||
[status-im.chat.models.suggestions :as suggestions]
|
|
||||||
[status-im.components.react :as react-comp]
|
|
||||||
[status-im.components.status :as status]
|
|
||||||
[status-im.utils.datetime :as time]
|
|
||||||
[status-im.utils.handlers :as handlers]
|
|
||||||
[status-im.utils.random :as random]
|
|
||||||
[status-im.i18n :as i18n]
|
|
||||||
[clojure.string :as str]))
|
|
||||||
|
|
||||||
(handlers/register-handler
|
|
||||||
:update-input-data
|
|
||||||
(fn [db]
|
|
||||||
(input-model/modified-db-after-change db)))
|
|
||||||
|
|
||||||
(handlers/register-handler :set-chat-input-text
|
|
||||||
(fn [{:keys [current-chat-id chats chat-ui-props] :as db} [_ text chat-id]]
|
|
||||||
(let [chat-id (or chat-id current-chat-id)
|
|
||||||
ends-with-space? (input-model/text-ends-with-space? text)]
|
|
||||||
(dispatch [:update-suggestions chat-id text])
|
|
||||||
(->> text
|
|
||||||
(input-model/text->emoji)
|
|
||||||
(assoc-in db [:chats chat-id :input-text])))))
|
|
||||||
|
|
||||||
(handlers/register-handler :add-to-chat-input-text
|
|
||||||
(handlers/side-effect!
|
|
||||||
(fn [{:keys [chats current-chat-id]} [_ text-to-add]]
|
|
||||||
(let [input-text (get-in chats [current-chat-id :input-text])]
|
|
||||||
(dispatch [:set-chat-input-text (str input-text text-to-add)])))))
|
|
||||||
|
|
||||||
(handlers/register-handler :select-chat-input-command
|
|
||||||
(handlers/side-effect!
|
|
||||||
(fn [{:keys [current-chat-id chat-ui-props] :as db}
|
|
||||||
[_ {:keys [prefill prefill-bot-db sequential-params name] :as command} metadata prevent-auto-focus?]]
|
|
||||||
(dispatch [:set-chat-input-text (str (chat-utils/command-name command)
|
|
||||||
const/spacing-char
|
|
||||||
(when-not sequential-params
|
|
||||||
(input-model/join-command-args prefill)))])
|
|
||||||
(dispatch [:clear-bot-db])
|
|
||||||
(when prefill-bot-db
|
|
||||||
(dispatch [:update-bot-db {:bot current-chat-id
|
|
||||||
:db prefill-bot-db}]))
|
|
||||||
(dispatch [:set-chat-input-metadata metadata])
|
|
||||||
(dispatch [:set-chat-ui-props {:show-suggestions? false
|
|
||||||
:result-box nil
|
|
||||||
:validation-messages nil
|
|
||||||
:prev-command name}])
|
|
||||||
(dispatch [:load-chat-parameter-box command 0])
|
|
||||||
(if sequential-params
|
|
||||||
(js/setTimeout
|
|
||||||
#(do (when-not prevent-auto-focus?
|
|
||||||
(dispatch [:chat-input-focus :seq-input-ref]))
|
|
||||||
(dispatch [:set-chat-seq-arg-input-text (str/join const/spacing-char prefill)]))
|
|
||||||
100)
|
|
||||||
(when-not prevent-auto-focus?
|
|
||||||
(dispatch [:chat-input-focus :input-ref]))))))
|
|
||||||
|
|
||||||
(handlers/register-handler :set-chat-input-metadata
|
|
||||||
(fn [{:keys [current-chat-id] :as db} [_ data chat-id]]
|
|
||||||
(let [chat-id (or chat-id current-chat-id)]
|
|
||||||
(assoc-in db [:chats chat-id :input-metadata] data))))
|
|
||||||
|
|
||||||
(handlers/register-handler :set-command-argument
|
|
||||||
(handlers/side-effect!
|
|
||||||
(fn [{:keys [current-chat-id] :as db} [_ [index arg move-to-next?]]]
|
|
||||||
(let [command (-> (get-in db [:chats current-chat-id :input-text])
|
|
||||||
(input-model/split-command-args))
|
|
||||||
seq-params? (-> (input-model/selected-chat-command db current-chat-id)
|
|
||||||
(get-in [:command :sequential-params]))]
|
|
||||||
(if seq-params?
|
|
||||||
(dispatch [:set-chat-seq-arg-input-text arg])
|
|
||||||
(let [arg (str/replace arg (re-pattern const/arg-wrapping-char) "")
|
|
||||||
command-name (first command)
|
|
||||||
command-args (into [] (rest command))
|
|
||||||
command-args (if (< index (count command-args))
|
|
||||||
(assoc command-args index arg)
|
|
||||||
(conj command-args arg))]
|
|
||||||
(dispatch [:set-chat-input-text (str command-name
|
|
||||||
const/spacing-char
|
|
||||||
(input-model/join-command-args command-args)
|
|
||||||
(when (and move-to-next?
|
|
||||||
(= index (dec (count command-args))))
|
|
||||||
const/spacing-char))])))))))
|
|
||||||
|
|
||||||
(handlers/register-handler :chat-input-focus
|
|
||||||
(handlers/side-effect!
|
|
||||||
(fn [{:keys [current-chat-id chat-ui-props] :as db} [_ ref]]
|
|
||||||
(try
|
|
||||||
(when-let [ref (get-in chat-ui-props [current-chat-id ref])]
|
|
||||||
(.focus ref))
|
|
||||||
(catch :default e
|
|
||||||
(log/debug "Cannot focus the reference"))))))
|
|
||||||
|
|
||||||
(handlers/register-handler :chat-input-blur
|
|
||||||
(handlers/side-effect!
|
|
||||||
(fn [{:keys [current-chat-id chat-ui-props] :as db} [_ ref]]
|
|
||||||
(try
|
|
||||||
(when-let [ref (get-in chat-ui-props [current-chat-id ref])]
|
|
||||||
(.blur ref))
|
|
||||||
(catch :default e
|
|
||||||
(log/debug "Cannot blur the reference"))))))
|
|
||||||
|
|
||||||
(handlers/register-handler :update-suggestions
|
|
||||||
(fn [{:keys [current-chat-id] :as db} [_ chat-id text]]
|
|
||||||
(let [chat-id (or chat-id current-chat-id)
|
|
||||||
chat-text (str/trim (or text (get-in db [:chats chat-id :input-text]) ""))
|
|
||||||
requests (->> (suggestions/get-request-suggestions db chat-text)
|
|
||||||
(remove (fn [{:keys [type]}]
|
|
||||||
(= type :grant-permissions))))
|
|
||||||
commands (suggestions/get-command-suggestions db chat-text)
|
|
||||||
global-commands (suggestions/get-global-command-suggestions db chat-text)
|
|
||||||
all-commands (->> (into global-commands commands)
|
|
||||||
(remove (fn [[k {:keys [hidden?]}]] hidden?))
|
|
||||||
(into {}))
|
|
||||||
{:keys [dapp?]} (get-in db [:contacts/contacts chat-id])]
|
|
||||||
(when dapp?
|
|
||||||
(if (str/blank? chat-text)
|
|
||||||
(dispatch [:set-in [:chats chat-id :parameter-boxes :message] nil])
|
|
||||||
(when (every? empty? [requests commands])
|
|
||||||
(dispatch [::check-dapp-suggestions chat-id chat-text]))))
|
|
||||||
(-> db
|
|
||||||
(assoc-in [:chats chat-id :request-suggestions] requests)
|
|
||||||
(assoc-in [:chats chat-id :command-suggestions] all-commands)))))
|
|
||||||
|
|
||||||
(handlers/register-handler :load-chat-parameter-box
|
|
||||||
(handlers/side-effect!
|
|
||||||
(fn [{:keys [current-chat-id bot-db current-account-id] :as db}
|
|
||||||
[_ {:keys [name type bot owner-id] :as command}]]
|
|
||||||
(let [parameter-index (input-model/argument-position db current-chat-id)]
|
|
||||||
(when (and command (> parameter-index -1))
|
|
||||||
(let [data (get-in db [:local-storage current-chat-id])
|
|
||||||
bot-db (get bot-db (or bot current-chat-id))
|
|
||||||
path [(if (= :command type) :commands :responses)
|
|
||||||
name
|
|
||||||
:params
|
|
||||||
parameter-index
|
|
||||||
:suggestions]
|
|
||||||
args (-> (get-in db [:chats current-chat-id :input-text])
|
|
||||||
(input-model/split-command-args)
|
|
||||||
(rest))
|
|
||||||
seq-arg (get-in db [:chats current-chat-id :seq-argument-input-text])
|
|
||||||
to (get-in db [:contacts/contacts current-chat-id :address])
|
|
||||||
params {:parameters {:args args
|
|
||||||
:bot-db bot-db
|
|
||||||
:seq-arg seq-arg}
|
|
||||||
:context (merge {:data data
|
|
||||||
:from current-account-id
|
|
||||||
:to to}
|
|
||||||
(input-model/command-dependent-context-params current-chat-id command))}]
|
|
||||||
(status/call-jail
|
|
||||||
{:jail-id (or bot owner-id current-chat-id)
|
|
||||||
:path path
|
|
||||||
:params params
|
|
||||||
:callback #(dispatch [:received-bot-response
|
|
||||||
{:chat-id current-chat-id
|
|
||||||
:command command
|
|
||||||
:parameter-index parameter-index}
|
|
||||||
%])})))))))
|
|
||||||
|
|
||||||
(handlers/register-handler ::send-message
|
|
||||||
(handlers/side-effect!
|
|
||||||
(fn [{:keys [current-public-key current-account-id] :as db} [_ command chat-id]]
|
|
||||||
(let [text (get-in db [:chats chat-id :input-text])
|
|
||||||
data {:message text
|
|
||||||
:command command
|
|
||||||
:chat-id chat-id
|
|
||||||
:identity current-public-key
|
|
||||||
:address current-account-id}]
|
|
||||||
(dispatch [:set-chat-input-text nil chat-id])
|
|
||||||
(dispatch [:set-chat-input-metadata nil chat-id])
|
|
||||||
(dispatch [:set-chat-ui-props {:sending-in-progress? false}])
|
|
||||||
(cond
|
|
||||||
command
|
|
||||||
(dispatch [:check-commands-handlers! data])
|
|
||||||
(not (str/blank? text))
|
|
||||||
(dispatch [:prepare-message data]))))))
|
|
||||||
|
|
||||||
(handlers/register-handler :proceed-command
|
|
||||||
(handlers/side-effect!
|
|
||||||
(fn [db [_ {{:keys [bot]} :command :as content} chat-id]]
|
|
||||||
(let [params {:content content
|
|
||||||
:chat-id chat-id
|
|
||||||
:jail-id (or bot chat-id)}
|
|
||||||
on-send-params (merge params
|
|
||||||
{:data-type :on-send
|
|
||||||
:after #(dispatch [::send-command %2 content chat-id])})
|
|
||||||
after-validation #(dispatch [::request-command-data on-send-params])
|
|
||||||
validation-params (merge params
|
|
||||||
{:data-type :validator
|
|
||||||
:after #(dispatch [::proceed-validation %2 after-validation])})]
|
|
||||||
|
|
||||||
(dispatch [::request-command-data validation-params])))))
|
|
||||||
|
|
||||||
(handlers/register-handler ::proceed-validation
|
|
||||||
(handlers/side-effect!
|
|
||||||
(fn [db [_ {:keys [markup validationHandler parameters]} proceed-fn]]
|
|
||||||
(let [set-errors #(do (dispatch [:set-chat-ui-props {:validation-messages %
|
|
||||||
:sending-in-progress? false}]))]
|
|
||||||
(cond
|
|
||||||
markup
|
|
||||||
(set-errors markup)
|
|
||||||
|
|
||||||
validationHandler
|
|
||||||
(do (dispatch [::execute-validation-handler validationHandler parameters set-errors proceed-fn])
|
|
||||||
(dispatch [:set-chat-ui-props {:sending-in-progress? false}]))
|
|
||||||
|
|
||||||
:default
|
|
||||||
(proceed-fn))))))
|
|
||||||
|
|
||||||
(handlers/register-handler ::execute-validation-handler
|
|
||||||
(handlers/side-effect!
|
|
||||||
(fn [_ [_ name params set-errors proceed]]
|
|
||||||
(when-let [validator (input-model/validation-handler name)]
|
|
||||||
(validator params set-errors proceed)))))
|
|
||||||
|
|
||||||
(handlers/register-handler ::send-command
|
|
||||||
(handlers/side-effect!
|
|
||||||
(fn [db [_ on-send {{:keys [fullscreen bot]} :command :as content} chat-id]]
|
|
||||||
(if on-send
|
|
||||||
(do
|
|
||||||
(when fullscreen
|
|
||||||
(dispatch [:choose-predefined-expandable-height :result-box :max]))
|
|
||||||
(dispatch [:set-chat-ui-props {:result-box on-send
|
|
||||||
:sending-in-progress? false}])
|
|
||||||
(react-comp/dismiss-keyboard!))
|
|
||||||
(dispatch [::request-command-data
|
|
||||||
{:content content
|
|
||||||
:chat-id chat-id
|
|
||||||
:jail-id (or bot chat-id)
|
|
||||||
:data-type :preview
|
|
||||||
:after #(dispatch [::send-message % chat-id])}])))))
|
|
||||||
|
|
||||||
(handlers/register-handler ::request-command-data
|
|
||||||
(handlers/side-effect!
|
|
||||||
(fn [{:keys [bot-db]
|
|
||||||
:contacts/keys [contacts] :as db}
|
|
||||||
[_ {{:keys [command
|
|
||||||
metadata
|
|
||||||
args]
|
|
||||||
:as content} :content
|
|
||||||
:keys [chat-id jail-id data-type after]}]]
|
|
||||||
(let [{:keys [dapp? dapp-url name]} (get contacts chat-id)
|
|
||||||
message-id (random/id)
|
|
||||||
metadata (merge metadata
|
|
||||||
(when dapp?
|
|
||||||
{:url (i18n/get-contact-translated chat-id :dapp-url dapp-url)
|
|
||||||
:name (i18n/get-contact-translated chat-id :name name)}))
|
|
||||||
owner-id (:owner-id command)
|
|
||||||
bot-db (get bot-db chat-id)
|
|
||||||
params (merge (input-model/args->params content)
|
|
||||||
{:bot-db bot-db
|
|
||||||
:metadata metadata})
|
|
||||||
|
|
||||||
command-message {:command command
|
|
||||||
:params params
|
|
||||||
:to-message (:to-message-id metadata)
|
|
||||||
:created-at (time/now-ms)
|
|
||||||
:id message-id
|
|
||||||
:chat-id chat-id
|
|
||||||
:jail-id (or owner-id jail-id)}
|
|
||||||
|
|
||||||
request-data {:message-id message-id
|
|
||||||
:chat-id chat-id
|
|
||||||
:jail-id (or owner-id jail-id)
|
|
||||||
:content {:command (:name command)
|
|
||||||
:params params
|
|
||||||
:type (:type command)}
|
|
||||||
:on-requested #(after command-message %)}]
|
|
||||||
(dispatch [:request-command-data request-data data-type])))))
|
|
||||||
|
|
||||||
(handlers/register-handler :send-current-message
|
|
||||||
(handlers/side-effect!
|
|
||||||
(fn [{:keys [current-chat-id] :as db} [_ chat-id]]
|
|
||||||
(dispatch [:set-chat-ui-props {:sending-in-progress? true}])
|
|
||||||
(let [chat-id (or chat-id current-chat-id)
|
|
||||||
chat-command (input-model/selected-chat-command db chat-id)
|
|
||||||
seq-command? (get-in chat-command [:command :sequential-params])
|
|
||||||
chat-command (if seq-command?
|
|
||||||
(let [args (get-in db [:chats chat-id :seq-arguments])]
|
|
||||||
(assoc chat-command :args args))
|
|
||||||
(update chat-command :args #(remove str/blank? %)))]
|
|
||||||
(if (:command chat-command)
|
|
||||||
(if (= :complete (input-model/command-completion chat-command))
|
|
||||||
(do
|
|
||||||
(dispatch [:proceed-command chat-command chat-id])
|
|
||||||
(dispatch [:clear-seq-arguments chat-id]))
|
|
||||||
(let [text (get-in db [:chats chat-id :input-text])]
|
|
||||||
(dispatch [:set-chat-ui-props {:sending-in-progress? false}])
|
|
||||||
(when-not (input-model/text-ends-with-space? text)
|
|
||||||
(dispatch [:set-chat-input-text (str text const/spacing-char)]))))
|
|
||||||
(dispatch [::send-message nil chat-id]))))))
|
|
||||||
|
|
||||||
(handlers/register-handler ::check-dapp-suggestions
|
|
||||||
(handlers/side-effect!
|
|
||||||
(fn [{:keys [current-account-id] :as db} [_ chat-id text]]
|
|
||||||
(let [data (get-in db [:local-storage chat-id])]
|
|
||||||
(status/call-function!
|
|
||||||
{:chat-id chat-id
|
|
||||||
:function :on-message-input-change
|
|
||||||
:parameters {:message text}
|
|
||||||
:context {:data data
|
|
||||||
:from current-account-id}})))))
|
|
||||||
|
|
||||||
(handlers/register-handler :clear-seq-arguments
|
|
||||||
(fn [{:keys [current-chat-id chats] :as db} [_ chat-id]]
|
|
||||||
(let [chat-id (or chat-id current-chat-id)]
|
|
||||||
(-> db
|
|
||||||
(assoc-in [:chats chat-id :seq-arguments] [])
|
|
||||||
(assoc-in [:chats chat-id :seq-argument-input-text] nil)))))
|
|
||||||
|
|
||||||
(handlers/register-handler :update-seq-arguments
|
|
||||||
(fn [{:keys [current-chat-id chats] :as db} [_ chat-id]]
|
|
||||||
(let [chat-id (or chat-id current-chat-id)
|
|
||||||
text (get-in chats [chat-id :seq-argument-input-text])]
|
|
||||||
(-> db
|
|
||||||
(update-in [:chats chat-id :seq-arguments] #(into [] (conj % text)))
|
|
||||||
(assoc-in [:chats chat-id :seq-argument-input-text] nil)))))
|
|
||||||
|
|
||||||
(handlers/register-handler :send-seq-argument
|
|
||||||
(handlers/side-effect!
|
|
||||||
(fn [{:keys [current-chat-id chats] :as db} [_ chat-id]]
|
|
||||||
(let [chat-id (or chat-id current-chat-id)
|
|
||||||
text (get-in chats [chat-id :seq-argument-input-text])
|
|
||||||
seq-arguments (get-in db [:chats chat-id :seq-arguments])
|
|
||||||
command (-> (input-model/selected-chat-command db chat-id)
|
|
||||||
(assoc :args (into [] (conj seq-arguments text))))
|
|
||||||
args (get-in chats [chat-id :seq-arguments])
|
|
||||||
after-validation #(do
|
|
||||||
(dispatch [:update-seq-arguments chat-id])
|
|
||||||
(dispatch [:send-current-message]))]
|
|
||||||
(dispatch [::request-command-data
|
|
||||||
{:content command
|
|
||||||
:chat-id chat-id
|
|
||||||
:jail-id (or (get-in command [:command :bot]) chat-id)
|
|
||||||
:data-type :validator
|
|
||||||
:after #(dispatch [::proceed-validation %2 after-validation])}])))))
|
|
||||||
|
|
||||||
(handlers/register-handler :set-chat-seq-arg-input-text
|
|
||||||
(fn [{:keys [current-chat-id] :as db} [_ text chat-id]]
|
|
||||||
(let [chat-id (or chat-id current-chat-id)]
|
|
||||||
(assoc-in db [:chats chat-id :seq-argument-input-text] text))))
|
|
||||||
|
|
||||||
(handlers/register-handler :update-text-selection
|
|
||||||
(handlers/side-effect!
|
|
||||||
(fn [{:keys [current-chat-id] :as db} [_ selection]]
|
|
||||||
(let [input-text (get-in db [:chats current-chat-id :input-text])
|
|
||||||
command (input-model/selected-chat-command db current-chat-id input-text)]
|
|
||||||
(when (and (= selection (+ (count const/command-char)
|
|
||||||
(count (get-in command [:command :name]))
|
|
||||||
(count const/spacing-char)))
|
|
||||||
(get-in command [:command :sequential-params]))
|
|
||||||
(dispatch [:chat-input-focus :seq-input-ref]))
|
|
||||||
(dispatch [:set-chat-ui-props {:selection selection}])
|
|
||||||
(dispatch [:load-chat-parameter-box (:command command)])))))
|
|
||||||
|
|
||||||
(handlers/register-handler :select-prev-argument
|
|
||||||
(handlers/side-effect!
|
|
||||||
(fn [{:keys [chat-ui-props current-chat-id] :as db} _]
|
|
||||||
(let [input-text (get-in db [:chats current-chat-id :input-text])
|
|
||||||
command (input-model/selected-chat-command db current-chat-id input-text)]
|
|
||||||
(if (get-in command [:command :sequential-params])
|
|
||||||
(do
|
|
||||||
(dispatch [:set-command-argument [0 "" false]])
|
|
||||||
(dispatch [:set-chat-seq-arg-input-text ""])
|
|
||||||
(dispatch [:load-chat-parameter-box (:command command)]))
|
|
||||||
(let [arg-pos (input-model/argument-position db current-chat-id)]
|
|
||||||
(when (pos? arg-pos)
|
|
||||||
(let [input-text (get-in db [:chats current-chat-id :input-text])
|
|
||||||
new-sel (->> (input-model/split-command-args input-text)
|
|
||||||
(take (inc arg-pos))
|
|
||||||
(input-model/join-command-args)
|
|
||||||
(count))
|
|
||||||
ref (get-in chat-ui-props [current-chat-id :input-ref])]
|
|
||||||
(.setNativeProps ref (clj->js {:selection {:start new-sel :end new-sel}}))
|
|
||||||
(dispatch [:update-text-selection new-sel])))))))))
|
|
||||||
|
|
||||||
(handlers/register-handler :select-next-argument
|
|
||||||
(handlers/side-effect!
|
|
||||||
(fn [{:keys [chat-ui-props current-chat-id] :as db} _]
|
|
||||||
(let [arg-pos (input-model/argument-position db current-chat-id)]
|
|
||||||
(let [input-text (get-in db [:chats current-chat-id :input-text])
|
|
||||||
command-args (cond-> (input-model/split-command-args input-text)
|
|
||||||
(input-model/text-ends-with-space? input-text) (conj ""))
|
|
||||||
new-sel (->> command-args
|
|
||||||
(take (+ 3 arg-pos))
|
|
||||||
(input-model/join-command-args)
|
|
||||||
count
|
|
||||||
(min (count input-text)))
|
|
||||||
ref (get-in chat-ui-props [current-chat-id :input-ref])]
|
|
||||||
(.setNativeProps ref (clj->js {:selection {:start new-sel :end new-sel}}))
|
|
||||||
(dispatch [:update-text-selection new-sel]))))))
|
|
|
@ -267,9 +267,8 @@
|
||||||
|
|
||||||
(defmethod validation-handler :phone
|
(defmethod validation-handler :phone
|
||||||
[_]
|
[_]
|
||||||
(fn [[number] set-errors proceed]
|
(fn [[number] error-events-creator]
|
||||||
(if (phone-number/valid-mobile-number? number)
|
(when-not (phone-number/valid-mobile-number? number)
|
||||||
(proceed)
|
(error-events-creator [validation-message
|
||||||
(set-errors [validation-message
|
{:title (i18n/label :t/phone-number)
|
||||||
{:title (i18n/label :t/phone-number)
|
:description (i18n/label :t/invalid-phone)}]))))
|
||||||
:description (i18n/label :t/invalid-phone)}]))))
|
|
||||||
|
|
|
@ -1,23 +1,30 @@
|
||||||
(ns status-im.chat.subs
|
(ns status-im.chat.subs
|
||||||
(:require [re-frame.core :refer [reg-sub dispatch subscribe path]]
|
(:require [re-frame.core :refer [reg-sub dispatch subscribe path]]
|
||||||
[status-im.data-store.chats :as chats]
|
[status-im.data-store.chats :as chats]
|
||||||
[status-im.chat.constants :as const]
|
[status-im.chat.constants :as const]
|
||||||
[status-im.chat.models.input :as input-model]
|
[status-im.chat.models.input :as input-model]
|
||||||
[status-im.chat.utils :as chat-utils]
|
[status-im.chat.utils :as chat-utils]
|
||||||
[status-im.chat.views.input.utils :as input-utils]
|
[status-im.chat.views.input.utils :as input-utils]
|
||||||
[status-im.constants :refer [response-suggesstion-resize-duration
|
[status-im.constants :refer [response-suggesstion-resize-duration
|
||||||
content-type-status
|
content-type-status
|
||||||
console-chat-id]]
|
console-chat-id]]
|
||||||
|
[status-im.commands.utils :as commands-utils]
|
||||||
[status-im.models.commands :as commands]
|
[status-im.models.commands :as commands]
|
||||||
[status-im.utils.platform :refer [platform-specific ios?]]
|
[status-im.utils.platform :refer [platform-specific ios?]]
|
||||||
[taoensso.timbre :as log]
|
[taoensso.timbre :as log]
|
||||||
[clojure.string :as str]))
|
[clojure.string :as str]))
|
||||||
|
|
||||||
(reg-sub
|
(reg-sub
|
||||||
:chat-ui-props
|
:chat-ui-props
|
||||||
(fn [db [_ ui-element chat-id]]
|
(fn [db [_ ui-element chat-id]]
|
||||||
(let [current-chat-id (subscribe [:get-current-chat-id])]
|
(let [current-chat-id (subscribe [:get-current-chat-id])
|
||||||
(get-in db [:chat-ui-props (or chat-id @current-chat-id) ui-element]))))
|
data (get-in db [:chat-ui-props (or chat-id @current-chat-id) ui-element])]
|
||||||
|
(cond-> data
|
||||||
|
(:markup data)
|
||||||
|
(update :markup commands-utils/generate-hiccup)
|
||||||
|
|
||||||
|
(and (= ui-element :validation-messages) data)
|
||||||
|
commands-utils/generate-hiccup))))
|
||||||
|
|
||||||
(reg-sub
|
(reg-sub
|
||||||
:chat-input-margin
|
:chat-input-margin
|
||||||
|
@ -181,10 +188,16 @@
|
||||||
(filter :show?)
|
(filter :show?)
|
||||||
(first)))))
|
(first)))))
|
||||||
|
|
||||||
(reg-sub :get-last-message-short-preview
|
(reg-sub :get-message-short-preview-markup
|
||||||
|
(fn [db [_ message-id]]
|
||||||
|
(get-in db [:message-data :short-preview message-id :markup])))
|
||||||
|
|
||||||
|
(reg-sub :get-last-message-short-preview
|
||||||
(fn [db [_ chat-id]]
|
(fn [db [_ chat-id]]
|
||||||
(let [last-message (subscribe [:get-last-message chat-id])]
|
(let [last-message (subscribe [:get-last-message chat-id])
|
||||||
(get-in db [:message-data :short-preview (:message-id @last-message)]))))
|
preview (subscribe [:get-message-short-preview-markup (:message-id @last-message)])]
|
||||||
|
(when-let [markup @preview]
|
||||||
|
(commands-utils/generate-hiccup markup)))))
|
||||||
|
|
||||||
(reg-sub :get-default-container-area-height
|
(reg-sub :get-default-container-area-height
|
||||||
:<- [:chat-ui-props :input-height]
|
:<- [:chat-ui-props :input-height]
|
||||||
|
@ -213,3 +226,13 @@
|
||||||
(filter :outgoing)
|
(filter :outgoing)
|
||||||
(sort-by :clock-value >)
|
(sort-by :clock-value >)
|
||||||
(first))))
|
(first))))
|
||||||
|
|
||||||
|
(reg-sub :get-message-preview-markup
|
||||||
|
(fn [db [_ message-id]]
|
||||||
|
(get-in db [:message-data :preview message-id :markup])))
|
||||||
|
|
||||||
|
(reg-sub :get-message-preview
|
||||||
|
(fn [db [_ message-id]]
|
||||||
|
(let [preview (subscribe [:get-message-preview-markup message-id])]
|
||||||
|
(when-let [markup @preview]
|
||||||
|
(commands-utils/generate-hiccup markup)))))
|
||||||
|
|
|
@ -136,7 +136,7 @@
|
||||||
global-commands [:get :global-commands]
|
global-commands [:get :global-commands]
|
||||||
current-chat-id [:get-current-chat-id]
|
current-chat-id [:get-current-chat-id]
|
||||||
contact-chat [:get-in [:chats (if outgoing to from)]]
|
contact-chat [:get-in [:chats (if outgoing to from)]]
|
||||||
preview [:get-in [:message-data :preview message-id :markup]]]
|
preview [:get-message-preview message-id]]
|
||||||
(let [commands (merge commands from-commands)
|
(let [commands (merge commands from-commands)
|
||||||
{:keys [command params]} (parse-command-message-content commands global-commands content)
|
{:keys [command params]} (parse-command-message-content commands global-commands content)
|
||||||
{:keys [name type]
|
{:keys [name type]
|
||||||
|
@ -386,7 +386,7 @@
|
||||||
(defn chat-message [{:keys [outgoing message-id chat-id user-statuses from] :as message}]
|
(defn chat-message [{:keys [outgoing message-id chat-id user-statuses from] :as message}]
|
||||||
(let [my-identity (subscribe [:get :current-public-key])
|
(let [my-identity (subscribe [:get :current-public-key])
|
||||||
status (subscribe [:get-in [:message-data :user-statuses message-id my-identity]])
|
status (subscribe [:get-in [:message-data :user-statuses message-id my-identity]])
|
||||||
preview (subscribe [:get-in [:message-data :preview message-id :markup]])]
|
preview (subscribe [:get-message-preview message-id])]
|
||||||
(r/create-class
|
(r/create-class
|
||||||
{:display-name "chat-message"
|
{:display-name "chat-message"
|
||||||
:component-will-mount
|
:component-will-mount
|
||||||
|
|
|
@ -71,7 +71,7 @@
|
||||||
(let [commands-atom (subscribe [:get-commands-and-responses chat-id])
|
(let [commands-atom (subscribe [:get-commands-and-responses chat-id])
|
||||||
answered? (subscribe [:is-request-answered? message-id])
|
answered? (subscribe [:is-request-answered? message-id])
|
||||||
status-initialized? (subscribe [:get :status-module-initialized?])
|
status-initialized? (subscribe [:get :status-module-initialized?])
|
||||||
markup (subscribe [:get-in [:message-data :preview message-id :markup]])]
|
markup (subscribe [:get-message-preview message-id])]
|
||||||
(fn [{:keys [message-id content from incoming-group]}]
|
(fn [{:keys [message-id content from incoming-group]}]
|
||||||
(let [commands @commands-atom
|
(let [commands @commands-atom
|
||||||
{:keys [prefill prefill-bot-db prefillBotDb params]
|
{:keys [prefill prefill-bot-db prefillBotDb params]
|
||||||
|
|
|
@ -9,8 +9,7 @@
|
||||||
[status-im.models.commands :as cm]
|
[status-im.models.commands :as cm]
|
||||||
[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.commands.utils :as cu]
|
|
||||||
[status-im.data-store.local-storage :as local-storage]))
|
[status-im.data-store.local-storage :as local-storage]))
|
||||||
|
|
||||||
(defn command-handler!
|
(defn command-handler!
|
||||||
|
@ -22,7 +21,7 @@
|
||||||
(cond
|
(cond
|
||||||
handler-error
|
handler-error
|
||||||
(when-let [markup (:markup handler-error)]
|
(when-let [markup (:markup handler-error)]
|
||||||
(dispatch [:set-chat-ui-props {:validation-messages (cu/generate-hiccup markup)}]))
|
(dispatch [:set-chat-ui-props {:validation-messages markup}]))
|
||||||
|
|
||||||
result
|
result
|
||||||
(let [command' (assoc command :handler-data returned)
|
(let [command' (assoc command :handler-data returned)
|
||||||
|
|
|
@ -50,7 +50,7 @@
|
||||||
(:content content)]
|
(:content content)]
|
||||||
|
|
||||||
(:command content)
|
(:command content)
|
||||||
(:markup preview)
|
preview
|
||||||
|
|
||||||
:else
|
:else
|
||||||
[text {:style st/last-message-text
|
[text {:style st/last-message-text
|
||||||
|
|
|
@ -443,4 +443,4 @@
|
||||||
[:add-pending-contact id]
|
[:add-pending-contact id]
|
||||||
[:add-new-contact-and-open-chat {:name (generate-gfy)
|
[:add-new-contact-and-open-chat {:name (generate-gfy)
|
||||||
:photo-path (identicon id)
|
:photo-path (identicon id)
|
||||||
:whisper-identity id}])})))
|
:whisper-identity id}])})))
|
||||||
|
|
Loading…
Reference in New Issue