diff --git a/src/status_im/chat/events.cljs b/src/status_im/chat/events.cljs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/status_im/chat/events/commands.cljs b/src/status_im/chat/events/commands.cljs new file mode 100644 index 0000000000..d92dad9025 --- /dev/null +++ b/src/status_im/chat/events/commands.cljs @@ -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]})))))) diff --git a/src/status_im/chat/events/input.cljs b/src/status_im/chat/events/input.cljs new file mode 100644 index 0000000000..5210293f28 --- /dev/null +++ b/src/status_im/chat/events/input.cljs @@ -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]})))) diff --git a/src/status_im/chat/handlers.cljs b/src/status_im/chat/handlers.cljs index 4a9136bf18..d961551264 100644 --- a/src/status_im/chat/handlers.cljs +++ b/src/status_im/chat/handlers.cljs @@ -28,8 +28,8 @@ [status-im.utils.types :refer [json->clj]] [status-im.chat.utils :refer [console? not-console? safe-trim]] [status-im.utils.gfycat.core :refer [generate-gfy]] - status-im.chat.handlers.input - status-im.chat.handlers.commands + status-im.chat.events.input + status-im.chat.events.commands status-im.chat.handlers.animation status-im.chat.handlers.requests status-im.chat.handlers.unviewed-messages @@ -186,14 +186,7 @@ (defn load-messages! ([db] (load-messages! db nil)) ([{:keys [current-chat-id] :as db} _] - (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)]))) + (let [messages (messages/get-by-chat-id current-chat-id)] (assoc db :messages messages)))) (defn init-chat diff --git a/src/status_im/chat/handlers/commands.cljs b/src/status_im/chat/handlers/commands.cljs deleted file mode 100644 index 53a7d40c27..0000000000 --- a/src/status_im/chat/handlers/commands.cljs +++ /dev/null @@ -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])))))))) diff --git a/src/status_im/chat/handlers/input.cljs b/src/status_im/chat/handlers/input.cljs deleted file mode 100644 index 2c47ce233a..0000000000 --- a/src/status_im/chat/handlers/input.cljs +++ /dev/null @@ -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])))))) diff --git a/src/status_im/chat/models/input.cljs b/src/status_im/chat/models/input.cljs index 5fb1622520..bb500df57c 100644 --- a/src/status_im/chat/models/input.cljs +++ b/src/status_im/chat/models/input.cljs @@ -267,9 +267,8 @@ (defmethod validation-handler :phone [_] - (fn [[number] set-errors proceed] - (if (phone-number/valid-mobile-number? number) - (proceed) - (set-errors [validation-message - {:title (i18n/label :t/phone-number) - :description (i18n/label :t/invalid-phone)}])))) + (fn [[number] error-events-creator] + (when-not (phone-number/valid-mobile-number? number) + (error-events-creator [validation-message + {:title (i18n/label :t/phone-number) + :description (i18n/label :t/invalid-phone)}])))) diff --git a/src/status_im/chat/subs.cljs b/src/status_im/chat/subs.cljs index e795c202ce..855aa41862 100644 --- a/src/status_im/chat/subs.cljs +++ b/src/status_im/chat/subs.cljs @@ -1,23 +1,30 @@ (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.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.views.input.utils :as input-utils] [status-im.constants :refer [response-suggesstion-resize-duration content-type-status console-chat-id]] + [status-im.commands.utils :as commands-utils] [status-im.models.commands :as commands] [status-im.utils.platform :refer [platform-specific ios?]] [taoensso.timbre :as log] [clojure.string :as str])) - + (reg-sub :chat-ui-props (fn [db [_ ui-element 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])))) + (let [current-chat-id (subscribe [:get-current-chat-id]) + 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 :chat-input-margin @@ -181,10 +188,16 @@ (filter :show?) (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]] - (let [last-message (subscribe [:get-last-message chat-id])] - (get-in db [:message-data :short-preview (:message-id @last-message)])))) + (let [last-message (subscribe [:get-last-message chat-id]) + 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 :<- [:chat-ui-props :input-height] @@ -213,3 +226,13 @@ (filter :outgoing) (sort-by :clock-value >) (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))))) diff --git a/src/status_im/chat/views/message/message.cljs b/src/status_im/chat/views/message/message.cljs index 7da092d17b..a3447b60f7 100644 --- a/src/status_im/chat/views/message/message.cljs +++ b/src/status_im/chat/views/message/message.cljs @@ -136,7 +136,7 @@ global-commands [:get :global-commands] current-chat-id [:get-current-chat-id] 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) {:keys [command params]} (parse-command-message-content commands global-commands content) {:keys [name type] @@ -386,7 +386,7 @@ (defn chat-message [{:keys [outgoing message-id chat-id user-statuses from] :as message}] (let [my-identity (subscribe [:get :current-public-key]) 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 {:display-name "chat-message" :component-will-mount diff --git a/src/status_im/chat/views/message/request_message.cljs b/src/status_im/chat/views/message/request_message.cljs index 27cf84939a..9f7603424b 100644 --- a/src/status_im/chat/views/message/request_message.cljs +++ b/src/status_im/chat/views/message/request_message.cljs @@ -71,7 +71,7 @@ (let [commands-atom (subscribe [:get-commands-and-responses chat-id]) answered? (subscribe [:is-request-answered? message-id]) 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]}] (let [commands @commands-atom {:keys [prefill prefill-bot-db prefillBotDb params] diff --git a/src/status_im/commands/handlers/jail.cljs b/src/status_im/commands/handlers/jail.cljs index 7d52778389..206c78d516 100644 --- a/src/status_im/commands/handlers/jail.cljs +++ b/src/status_im/commands/handlers/jail.cljs @@ -9,8 +9,7 @@ [status-im.models.commands :as cm] [status-im.constants :refer [console-chat-id]] [status-im.i18n :refer [get-contact-translated]] - [taoensso.timbre :as log] - [status-im.commands.utils :as cu] + [taoensso.timbre :as log] [status-im.data-store.local-storage :as local-storage])) (defn command-handler! @@ -22,7 +21,7 @@ (cond 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 (let [command' (assoc command :handler-data returned) diff --git a/src/status_im/ui/screens/chats_list/views/inner_item.cljs b/src/status_im/ui/screens/chats_list/views/inner_item.cljs index 43362de276..abe694fb9d 100644 --- a/src/status_im/ui/screens/chats_list/views/inner_item.cljs +++ b/src/status_im/ui/screens/chats_list/views/inner_item.cljs @@ -50,7 +50,7 @@ (:content content)] (:command content) - (:markup preview) + preview :else [text {:style st/last-message-text diff --git a/src/status_im/ui/screens/contacts/events.cljs b/src/status_im/ui/screens/contacts/events.cljs index bb02042923..49c7772607 100644 --- a/src/status_im/ui/screens/contacts/events.cljs +++ b/src/status_im/ui/screens/contacts/events.cljs @@ -443,4 +443,4 @@ [:add-pending-contact id] [:add-new-contact-and-open-chat {:name (generate-gfy) :photo-path (identicon id) - :whisper-identity id}])}))) \ No newline at end of file + :whisper-identity id}])})))