diff --git a/resources/icons/reply.svg b/resources/icons/reply.svg new file mode 100644 index 0000000000..fd8e05b0c7 --- /dev/null +++ b/resources/icons/reply.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/status_im/bootnodes/core.cljs b/src/status_im/bootnodes/core.cljs index 44055bb70b..687aea9752 100644 --- a/src/status_im/bootnodes/core.cljs +++ b/src/status_im/bootnodes/core.cljs @@ -59,16 +59,16 @@ {:success-event (when (custom-bootnodes-in-use? cofx) [:accounts.update.callback/save-settings-success])})))) -(fx/defn upsert [{{:bootnodes/keys [manage] :account/keys [account] :as db} :db :as cofx}] - (let [{:keys [name - id - url]} manage - network (:network db) - bootnode (build - (or (:value id) (:random-id cofx)) - (:value name) - (:value url) - network) +(fx/defn upsert + [{{:bootnodes/keys [manage] :account/keys [account] :as db} :db + random-id-generator :random-id-generator :as cofx}] + (let [{:keys [name id url]} manage + network (:network db) + bootnode (build + (or (:value id) (random-id-generator)) + (:value name) + (:value url) + network) new-bootnodes (assoc-in (:bootnodes account) [network (:id bootnode)] diff --git a/src/status_im/browser/core.cljs b/src/status_im/browser/core.cljs index 5eb342aed5..48b9c5db09 100644 --- a/src/status_im/browser/core.cljs +++ b/src/status_im/browser/core.cljs @@ -311,7 +311,7 @@ (fx/defn open-chat-from-browser [cofx host] (let [topic (string/lower-case (apply str (map filter-letters-numbers-and-replace-dot-on-dash host)))] - {:dispatch [:create-new-public-chat topic true]})) + {:dispatch [:chat.ui/start-public-chat topic true]})) (re-frame/reg-fx :browser/resolve-ens-multihash diff --git a/src/status_im/chat/commands/core.cljs b/src/status_im/chat/commands/core.cljs index d42c8bddab..92b8dbc665 100644 --- a/src/status_im/chat/commands/core.cljs +++ b/src/status_im/chat/commands/core.cljs @@ -55,7 +55,7 @@ (if suggestions (update param :suggestions partial (fn [value] - [:set-command-parameter + [:chat.ui/set-command-parameter (= idx last-param-idx) idx value])) param)) parameters)))) diff --git a/src/status_im/chat/commands/impl/transactions.cljs b/src/status_im/chat/commands/impl/transactions.cljs index 57348de777..35c46c7bb1 100644 --- a/src/status_im/chat/commands/impl/transactions.cljs +++ b/src/status_im/chat/commands/impl/transactions.cljs @@ -275,7 +275,7 @@ ;; by the wallet, where we yield control in the next step (personal-send-request-validation parameters cofx)) (on-send [_ {:keys [chat-id] :as send-message} {:keys [db]}] - (when-let [{:keys [responding-to]} (get-in db [:chats chat-id :input-metadata])] + (when-let [responding-to (get-in db [:chats chat-id :metadata :responding-to-command])] (when-let [request-message (get-in db [:chats chat-id :messages responding-to])] (when (params-unchanged? send-message request-message) (let [updated-request-message (assoc-in request-message [:content :params :answered?] true)] @@ -430,10 +430,9 @@ status-initialized? (not outgoing) (not answered?)) - [react/touchable-highlight {:on-press #(re-frame/dispatch [:select-chat-input-command - command - [(or asset "ETH") amount] - {:responding-to message-id}])} + [react/touchable-highlight + {:on-press #(re-frame/dispatch [:chat.ui/select-chat-input-command + command [(or asset "ETH") amount] message-id])} markup] markup)))) diff --git a/src/status_im/chat/commands/input.cljs b/src/status_im/chat/commands/input.cljs index 62b0c7901f..f691c49a38 100644 --- a/src/status_im/chat/commands/input.cljs +++ b/src/status_im/chat/commands/input.cljs @@ -1,18 +1,87 @@ (ns status-im.chat.commands.input (:require [clojure.string :as string] + [status-im.chat.constants :as constants] [status-im.chat.commands.core :as commands] - [status-im.chat.constants :as chat-constants] - [status-im.chat.models :as chat-model] - [status-im.chat.models.input :as input-model] + [status-im.chat.constants :as chat.constants] + [status-im.chat.models :as chat] [status-im.utils.fx :as fx])) +(defn command-ends-with-space? [text] + (and text (string/ends-with? text constants/spacing-char))) + +(defn starts-as-command? + "Returns true if `text` may be treated as a command. + To make sure that text is command we need to use `possible-chat-actions` function." + [text] + (and text (string/starts-with? text constants/command-char))) + +(defn split-command-args + "Returns a list of command's arguments including the command's name. + + Examples: + Input: '/send Jarrad 1.0' + Output: ['/send' 'Jarrad' '1.0'] + + Input: '/send \"Complex name with space in between\" 1.0' + Output: ['/send' 'Complex name with space in between' '1.0'] + + All the complex logic inside this function aims to support wrapped arguments." + [command-text] + (when command-text + (let [space? (command-ends-with-space? command-text) + command-text (if space? + (str command-text ".") + command-text) + command-text-normalized (if command-text + (string/replace (string/trim command-text) #" +" " ") + command-text) + splitted (cond-> (string/split command-text-normalized constants/spacing-char) + space? (drop-last))] + (->> splitted + (reduce (fn [[list command-started?] arg] + (let [quotes-count (count (filter #(= % constants/arg-wrapping-char) arg)) + has-quote? (and (= quotes-count 1) + (string/index-of arg constants/arg-wrapping-char)) + arg (string/replace arg (re-pattern constants/arg-wrapping-char) "") + new-list (if command-started? + (let [index (dec (count list))] + (update list index str constants/spacing-char arg)) + (conj list arg)) + command-continues? (or (and command-started? (not has-quote?)) + (and (not command-started?) has-quote?))] + [new-list command-continues?])) + [[] false]) + (first))))) + +(defn join-command-args + "Transforms a list of args to a string. The opposite of `split-command-args`. + + Examples: + Input: ['/send' 'Jarrad' '1.0'] + Output: '/send Jarrad 1.0' + + Input: ['/send' '\"Jarrad\"' '1.0'] + Output: '/send Jarrad 1.0' + + Input: ['/send' 'Complex name with space in between' '1.0'] + Output: '/send \"Complex name with space in between\" 1.0'" + [args] + (when args + (->> args + (map (fn [arg] + (let [arg (string/replace arg (re-pattern constants/arg-wrapping-char) "")] + (if (not (string/index-of arg constants/spacing-char)) + arg + (str constants/arg-wrapping-char arg constants/arg-wrapping-char))))) + (string/join constants/spacing-char)))) + (defn- current-param-position [input-text selection] (when selection (when-let [subs-input-text (subs input-text 0 selection)] - (let [input-params (input-model/split-command-args subs-input-text) + (let [input-params (split-command-args subs-input-text) param-index (dec (count input-params)) - wrapping-count (get (frequencies subs-input-text) chat-constants/arg-wrapping-char 0)] - (if (and (string/ends-with? subs-input-text chat-constants/spacing-char) + wrapping-count (get (frequencies subs-input-text) chat.constants/arg-wrapping-char 0)] + (if (and (string/ends-with? subs-input-text chat.constants/spacing-char) (even? wrapping-count)) param-index (dec param-index)))))) @@ -36,8 +105,8 @@ # `:command-completion` - indication of command completion, possible values are `:complete`, `:less-then-needed` and `more-then-needed`" [input-text text-selection id->command-props] - (when (input-model/starts-as-command? input-text) - (let [[command-name & input-params] (input-model/split-command-args input-text)] + (when (starts-as-command? input-text) + (let [[command-name & input-params] (split-command-args input-text)] (when-let [{:keys [params] :as command-props} (get id->command-props (subs command-name 1))] ;; trim leading `/` for lookup command-props (let [input-params (into {} @@ -51,27 +120,37 @@ :current-param-position (current-param-position input-text text-selection) :command-completion (command-completion input-params params))))))) +(defn parse-parameters + "Parses parameters from input for defined command params, + returns map of `param-name->param-value`" + [params input-text] + (let [input-params (->> (split-command-args input-text) rest (into []))] + (into {} + (keep-indexed (fn [idx {:keys [id]}] + (when-let [value (get input-params idx)] + [id value]))) + params))) + (fx/defn set-command-parameter "Set value as command parameter for the current chat" [{:keys [db]} last-param? param-index value] (let [{:keys [current-chat-id chats]} db [command & params] (-> (get-in chats [current-chat-id :input-text]) - input-model/split-command-args) + split-command-args) param-count (count params) ;; put the new value at the right place in parameters array new-params (cond-> (into [] params) (< param-index param-count) (assoc param-index value) (>= param-index param-count) (conj value)) ;; if the parameter is not the last one for the command, add space - input-text (cond-> (str command chat-constants/spacing-char - (input-model/join-command-args - new-params)) + input-text (cond-> (str command chat.constants/spacing-char + (join-command-args new-params)) (and (not last-param?) (or (= 0 param-count) (= param-index (dec param-count)))) - (str chat-constants/spacing-char))] + (str chat.constants/spacing-char))] {:db (-> db - (chat-model/set-chat-ui-props {:validation-messages nil}) + (chat/set-chat-ui-props {:validation-messages nil}) (assoc-in [:chats current-chat-id :input-text] input-text))})) (fx/defn select-chat-input-command @@ -79,20 +158,15 @@ [{:keys [db]} {:keys [type params]} input-params] (let [{:keys [current-chat-id chat-ui-props]} db] {:db (-> db - (chat-model/set-chat-ui-props {:show-suggestions? false - :validation-messages nil}) + (chat/set-chat-ui-props {:show-suggestions? false + :validation-messages nil}) (assoc-in [:chats current-chat-id :input-text] (str (commands/command-name type) - chat-constants/spacing-char - (input-model/join-command-args input-params))))})) + chat.constants/spacing-char + (join-command-args input-params))))})) -(defn parse-parameters - "Parses parameters from input for defined command params, - returns map of `param-name->param-value`" - [params input-text] - (let [input-params (->> (input-model/split-command-args input-text) rest (into []))] - (into {} - (keep-indexed (fn [idx {:keys [id]}] - (when-let [value (get input-params idx)] - [id value]))) - params))) +(fx/defn set-command-reference + "Set reference to previous command message" + [{:keys [db] :as cofx} command-message-id] + (let [current-chat-id (:current-chat-id db)] + {:db (assoc-in db [:chats current-chat-id :metadata :responding-to-command] command-message-id)})) diff --git a/src/status_im/chat/commands/sending.cljs b/src/status_im/chat/commands/sending.cljs index 64e11de515..b48309f470 100644 --- a/src/status_im/chat/commands/sending.cljs +++ b/src/status_im/chat/commands/sending.cljs @@ -2,10 +2,9 @@ (:require [status-im.constants :as constants] [status-im.chat.commands.protocol :as protocol] [status-im.chat.commands.core :as commands] - [status-im.chat.commands.input :as commands-input] - [status-im.chat.models :as chat-model] - [status-im.chat.models.input :as input-model] - [status-im.chat.models.message :as message-model] + [status-im.chat.commands.input :as commands.input] + [status-im.chat.models :as chat] + [status-im.chat.models.message :as chat.message] [status-im.utils.fx :as fx])) ;; TODO(janherich) remove after couple of releases when danger of messages @@ -45,30 +44,22 @@ (fx/defn validate-and-send "Validates and sends command in current chat" - [{:keys [db now random-id] :as cofx} input-text {:keys [type params] :as command}] + [{:keys [db now] :as cofx} input-text {:keys [type params] :as command}] (let [chat-id (:current-chat-id db) - parameter-map (commands-input/parse-parameters params input-text)] + parameter-map (commands.input/parse-parameters params input-text)] (if-let [validation-error (protocol/validate type parameter-map cofx)] ;; errors during validation - {:db (chat-model/set-chat-ui-props db {:validation-messages validation-error - :sending-in-progress? false})} - ;; no errors, define clean-up effects which will have to be performed in all cases - (let [cleanup-fx (fx/merge cofx - {:db (chat-model/set-chat-ui-props - db {:sending-in-progress? false})} - (input-model/set-chat-input-text nil))] - (if (satisfies? protocol/Yielding type) - ;; yield control implemented, don't send the message + {:db (chat/set-chat-ui-props db {:validation-messages validation-error})} + ;; no errors + (if (satisfies? protocol/Yielding type) + ;; yield control implemented, don't send the message + (protocol/yield-control type parameter-map cofx) + ;; no yield control, proceed with sending the command message + (let [command-message (create-command-message chat-id type parameter-map cofx)] (fx/merge cofx - cleanup-fx - #(protocol/yield-control type parameter-map %)) - ;; no yield control, proceed with sending the command message - (let [command-message (create-command-message chat-id type parameter-map cofx)] - (fx/merge cofx - cleanup-fx - #(protocol/on-send type command-message %) - (input-model/set-chat-input-metadata nil) - (message-model/send-message command-message)))))))) + #(protocol/on-send type command-message %) + (commands.input/set-command-reference nil) + (chat.message/send-message command-message))))))) (fx/defn send "Sends command with given parameters in particular chat" @@ -76,5 +67,5 @@ (let [command-message (create-command-message chat-id type parameter-map cofx)] (fx/merge cofx #(protocol/on-send type command-message %) - (input-model/set-chat-input-metadata nil) - (message-model/send-message command-message)))) + (commands.input/set-command-reference nil) + (chat.message/send-message command-message)))) diff --git a/src/status_im/chat/events.cljs b/src/status_im/chat/events.cljs deleted file mode 100644 index db151e395d..0000000000 --- a/src/status_im/chat/events.cljs +++ /dev/null @@ -1,170 +0,0 @@ -(ns status-im.chat.events - (:require status-im.chat.events.input - status-im.chat.events.send-message - status-im.chat.events.receive-message - [clojure.string :as string] - [re-frame.core :as re-frame] - [status-im.chat.models :as models] - [status-im.chat.models.loading :as chat-loading] - [status-im.chat.models.message :as models.message] - [status-im.data-store.user-statuses :as user-statuses-store] - [status-im.i18n :as i18n] - [status-im.transport.message.core :as transport.message] - [status-im.transport.message.v1.core :as protocol] - [status-im.transport.message.v1.public-chat :as public-chat] - [status-im.ui.screens.navigation :as navigation] - [status-im.utils.fx :as fx] - [status-im.group-chats.core :as group-chats] - [status-im.utils.handlers :as handlers] - [status-im.utils.utils :as utils])) - -;;;; Effects - - -(re-frame/reg-fx - :show-cooldown-warning - (fn [_] - (utils/show-popup nil - (i18n/label :cooldown/warning-message) - #()))) - -;;;; Handlers - -(handlers/register-handler-fx - :set-chat-ui-props - (fn [{:keys [db]} [_ kvs]] - {:db (models/set-chat-ui-props db kvs)})) - -(handlers/register-handler-fx - :toggle-chat-ui-props - (fn [{:keys [db]} [_ ui-element]] - {:db (models/toggle-chat-ui-prop db ui-element)})) - -(handlers/register-handler-fx - :show-message-details - (fn [{:keys [db]} [_ details]] - {:db (models/set-chat-ui-props db {:show-bottom-info? true - :bottom-info details})})) - -(handlers/register-handler-fx - :show-message-options - (fn [{:keys [db]} [_ options]] - {:db (models/set-chat-ui-props db {:show-message-options? true - :message-options options})})) - -(handlers/register-handler-fx - :update-message-status - (fn [{:keys [db]} [_ chat-id message-id user-id status]] - (let [new-status {:chat-id chat-id - :message-id message-id - :whisper-identity user-id - :status status}] - {:db (assoc-in db - [:chats chat-id :message-statuses message-id user-id] - new-status) - :data-store/tx [(user-statuses-store/save-status-tx new-status)]}))) - -(handlers/register-handler-fx - :navigate-to-chat - (fn [cofx [_ chat-id opts]] - (models/navigate-to-chat cofx chat-id opts))) - -(handlers/register-handler-fx - :load-more-messages - [(re-frame/inject-cofx :data-store/get-messages) - (re-frame/inject-cofx :data-store/get-user-statuses)] - (fn [cofx _] - (chat-loading/load-more-messages cofx))) - -(handlers/register-handler-fx - :start-chat - (fn [cofx [_ contact-id opts]] - (models/start-chat cofx contact-id opts))) - -(defn remove-chat-and-navigate-home [cofx [_ chat-id]] - (fx/merge cofx - (models/remove-chat chat-id) - (navigation/navigate-to-cofx :home {}))) - -(handlers/register-handler-fx - :remove-chat-and-navigate-home - remove-chat-and-navigate-home) - -(handlers/register-handler-fx - :remove-chat-and-navigate-home? - (fn [_ [_ chat-id group?]] - {:ui/show-confirmation {:title (i18n/label :t/delete-confirmation) - :content (i18n/label :t/delete-chat-confirmation) - :confirm-button-text (i18n/label :t/delete) - :on-accept #(re-frame/dispatch [:remove-chat-and-navigate-home chat-id])}})) - -(handlers/register-handler-fx - :clear-history - (fn [{{:keys [current-chat-id]} :db :as cofx} _] - (models/clear-history cofx current-chat-id))) - -(handlers/register-handler-fx - :clear-history? - (fn [_ _] - {:ui/show-confirmation {:title (i18n/label :t/clear-history-confirmation) - :content (i18n/label :t/clear-history-confirmation-content) - :confirm-button-text (i18n/label :t/clear) - :on-accept #(re-frame/dispatch [:clear-history])}})) - -(fx/defn create-new-public-chat [cofx topic modal?] - (fx/merge cofx - (models/add-public-chat topic) - (models/navigate-to-chat topic {:modal? modal? - :navigation-reset? true}) - (public-chat/join-public-chat topic))) - -(handlers/register-handler-fx - :create-new-public-chat - (fn [cofx [_ topic modal?]] - (create-new-public-chat cofx topic modal?))) - -(defn- group-name-from-contacts [selected-contacts all-contacts username] - (->> selected-contacts - (map (comp :name (partial get all-contacts))) - (cons username) - (string/join ", "))) - -(fx/defn send-group-update [cofx group-update chat-id] - (transport.message/send group-update chat-id cofx)) - -(handlers/register-handler-fx - :create-new-group-chat-and-open - [(re-frame/inject-cofx :random-id)] - (fn [{:keys [db random-id] :as cofx} [_ group-name]] - (let [my-public-key (:current-public-key db) - selected-contacts (conj (:group/selected-contacts db) - my-public-key) - group-update (protocol/GroupMembershipUpdate. random-id group-name my-public-key selected-contacts nil nil nil)] - (fx/merge cofx - {:db (assoc db :group/selected-contacts #{})} - (models/navigate-to-chat random-id {}) - (group-chats/handle-membership-update group-update my-public-key) - (send-group-update group-update random-id))))) - -(fx/defn show-profile [{:keys [db]} identity] - (navigation/navigate-to-cofx {:db (assoc db :contacts/identity identity)} :profile nil)) - -(handlers/register-handler-fx - :show-profile - (fn [cofx [_ identity]] - (show-profile cofx identity))) - -(handlers/register-handler-fx - :resend-message - (fn [cofx [_ chat-id message-id]] - (models.message/resend-message cofx chat-id message-id))) - -(handlers/register-handler-fx - :delete-message - (fn [cofx [_ chat-id message-id]] - (models.message/delete-message cofx chat-id message-id))) - -(handlers/register-handler-fx - :disable-cooldown - (fn [{:keys [db]}] - {:db (assoc db :chat/cooldown-enabled? false)})) diff --git a/src/status_im/chat/events/input.cljs b/src/status_im/chat/events/input.cljs deleted file mode 100644 index 17651c2a4e..0000000000 --- a/src/status_im/chat/events/input.cljs +++ /dev/null @@ -1,137 +0,0 @@ -(ns status-im.chat.events.input - (:require [clojure.string :as string] - [re-frame.core :as re-frame] - [taoensso.timbre :as log] - [status-im.constants :as constants] - [status-im.chat.constants :as chat-constants] - [status-im.chat.models :as model] - [status-im.chat.models.input :as input-model] - [status-im.chat.models.message :as message-model] - [status-im.chat.commands.core :as commands] - [status-im.chat.commands.input :as commands-input] - [status-im.chat.commands.sending :as commands-sending] - [status-im.ui.components.react :as react-comp] - [status-im.utils.handlers :as handlers] - [status-im.utils.fx :as fx])) - -;;;; Effects - -(re-frame/reg-fx - ::focus-rn-component - (fn [ref] - (try - (.focus ref) - (catch :default e - (log/debug "Cannot focus the reference"))))) - -(re-frame/reg-fx - ::blur-rn-component - (fn [ref] - (try - (.blur ref) - (catch :default e - (log/debug "Cannot blur the reference"))))) - -(re-frame/reg-fx - ::dismiss-keyboard - (fn [_] - (react-comp/dismiss-keyboard!))) - -(re-frame/reg-fx - ::set-native-props - (fn [{:keys [ref props]}] - (.setNativeProps ref (clj->js props)))) - -;;;; Helper functions - -(fx/defn chat-input-focus - "Returns fx for focusing on active chat input reference" - [{{: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})) - -;;;; Handlers - -(handlers/register-handler-fx - :set-chat-input-text - (fn [cofx [_ text]] - (input-model/set-chat-input-text cofx text))) - -(handlers/register-handler-fx - :select-chat-input-command - (fn [{:keys [db] :as cofx} [_ command params metadata]] - (fx/merge cofx - (input-model/set-chat-input-metadata metadata) - (commands-input/select-chat-input-command command params) - (chat-input-focus :input-ref)))) - -(handlers/register-handler-fx - :set-command-parameter - (fn [cofx [_ last-param? index value]] - (commands-input/set-command-parameter cofx last-param? index value))) - -(handlers/register-handler-fx - :chat-input-focus - (fn [cofx [_ ref]] - (chat-input-focus cofx ref))) - -(handlers/register-handler-fx - :chat-input-blur - (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}))) - -(defn command-complete-fx - "command is complete, set `:sending-in-progress?` flag and proceed with command processing" - [input-text command {:keys [db now random-id] :as cofx}] - (fx/merge cofx - {:db (model/set-chat-ui-props db {:sending-in-progress? true})} - (commands-sending/validate-and-send input-text command))) - -(defn command-not-complete-fx - "command is not complete, just add space after command if necessary" - [input-text current-chat-id {:keys [db]}] - {:db (cond-> db - (not (input-model/text-ends-with-space? input-text)) - (assoc-in [:chats current-chat-id :input-text] - (str input-text chat-constants/spacing-char)))}) - -(defn plain-text-message-fx - "no command detected, when not empty, proceed by sending text message without command processing" - [input-text current-chat-id {:keys [db] :as cofx}] - (when-not (string/blank? input-text) - (fx/merge cofx - (input-model/set-chat-input-text nil) - (input-model/set-chat-input-metadata nil) - (message-model/send-message {:chat-id current-chat-id - :content-type constants/text-content-type - :content input-text})))) - -(handlers/register-handler-fx - :send-current-message - message-model/send-interceptors - (fn [{{:keys [current-chat-id id->command access-scope->command-id] :as db} :db :as cofx} _] - (when-not (get-in db [:chat-ui-props current-chat-id :sending-in-progress?]) - (let [input-text (get-in db [:chats current-chat-id :input-text]) - command (commands-input/selected-chat-command - input-text nil (commands/chat-commands id->command - access-scope->command-id - (get-in db [:chats current-chat-id])))] - (if command - ;; Returns true if current input contains command - (if (= :complete (:command-completion command)) - (command-complete-fx input-text command cofx) - (command-not-complete-fx input-text current-chat-id cofx)) - (plain-text-message-fx input-text current-chat-id cofx)))))) - -(handlers/register-handler-fx - :update-text-selection - (fn [{:keys [db]} [_ selection]] - {:db (model/set-chat-ui-props db {:selection selection})})) - -(handlers/register-handler-fx - :show-suggestions - (fn [{:keys [db]} _] - {:db (-> db - (model/toggle-chat-ui-prop :show-suggestions?) - (model/set-chat-ui-props {:validation-messages nil}))})) diff --git a/src/status_im/chat/events/receive_message.cljs b/src/status_im/chat/events/receive_message.cljs deleted file mode 100644 index 20dc7b18c2..0000000000 --- a/src/status_im/chat/events/receive_message.cljs +++ /dev/null @@ -1,32 +0,0 @@ -(ns status-im.chat.events.receive-message - (:require [re-frame.core :as re-frame] - [taoensso.timbre :as log] - [status-im.chat.models.message :as message-model] - [status-im.utils.handlers :as handlers] - [status-im.models.transactions :as wallet.transactions])) - -;;;; Handlers - -(re-frame.core/reg-fx - :chat-received-message/add-fx - (fn [messages] - (re-frame/dispatch [:chat-received-message/add messages]))) - -(defn- filter-messages [cofx messages] - (:accumulated (reduce (fn [{:keys [seen-ids] :as acc} - {:keys [message-id] :as message}] - (if (and (message-model/add-to-chat? cofx message) - (not (seen-ids message-id))) - (-> acc - (update :seen-ids conj message-id) - (update :accumulated conj message)) - acc)) - {:seen-ids #{} - :accumulated []} - messages))) - -(handlers/register-handler-fx - :chat-received-message/add - message-model/receive-interceptors - (fn [cofx [_ messages]] - (message-model/receive-many cofx (filter-messages cofx messages)))) diff --git a/src/status_im/chat/events/send_message.cljs b/src/status_im/chat/events/send_message.cljs deleted file mode 100644 index 8ad40fcd1e..0000000000 --- a/src/status_im/chat/events/send_message.cljs +++ /dev/null @@ -1,15 +0,0 @@ -(ns status-im.chat.events.send-message - (:require [taoensso.timbre :as log] - [re-frame.core :as re-frame] - [status-im.chat.models.message :as message-model] - [status-im.native-module.core :as status] - [status-im.utils.handlers :as handlers] - [status-im.utils.types :as types])) - -(re-frame/reg-fx - :send-notification - (fn [{:keys [message payload tokens]}] - (let [payload-json (types/clj->json payload) - tokens-json (types/clj->json tokens)] - (log/debug "send-notification message: " message " payload-json: " payload-json " tokens-json: " tokens-json) - (status/notify-users {:message message :payload payload-json :tokens tokens-json} #(log/debug "send-notification cb result: " %))))) diff --git a/src/status_im/chat/models.cljs b/src/status_im/chat/models.cljs index 8ca7cfc62c..d1ea6a2900 100644 --- a/src/status_im/chat/models.cljs +++ b/src/status_im/chat/models.cljs @@ -1,13 +1,18 @@ (ns status-im.chat.models - (:require [status-im.data-store.chats :as chats-store] + (:require [clojure.string :as string] + [re-frame.core :as re-frame] + [status-im.data-store.chats :as chats-store] [status-im.data-store.messages :as messages-store] [status-im.data-store.user-statuses :as user-statuses-store] [status-im.transport.message.core :as transport.message] [status-im.transport.message.v1.protocol :as protocol] [status-im.transport.message.v1.core :as transport] + [status-im.transport.message.v1.public-chat :as public-chat] [status-im.transport.utils :as transport.utils] [status-im.ui.components.styles :as styles] [status-im.ui.screens.navigation :as navigation] + [status-im.i18n :as i18n] + [status-im.utils.utils :as utils] [status-im.utils.clocks :as utils.clocks] [status-im.utils.datetime :as time] [status-im.utils.gfycat.core :as gfycat] @@ -129,7 +134,8 @@ #(when (multi-user-chat? % chat-id) (remove-transport % chat-id)) (deactivate-chat chat-id) - (clear-history chat-id))) + (clear-history chat-id) + (navigation/navigate-to-cofx :home {}))) (fx/defn send-messages-seen [{:keys [db] :as cofx} chat-id message-ids] @@ -200,3 +206,25 @@ (upsert-chat {:chat-id chat-id :is-active true}) (navigate-to-chat chat-id opts)))) + +(fx/defn start-public-chat + "Starts a new public chat" + [cofx topic modal?] + (fx/merge cofx + (add-public-chat topic) + (navigate-to-chat topic {:modal? modal? + :navigation-replace? true}) + (public-chat/join-public-chat topic))) + +(fx/defn disable-chat-cooldown + "Turns off chat cooldown (protection against message spamming)" + [{:keys [db]}] + {:db (assoc db :chat/cooldown-enabled? false)}) + +;; effects +(re-frame/reg-fx + :show-cooldown-warning + (fn [_] + (utils/show-popup nil + (i18n/label :cooldown/warning-message) + #()))) diff --git a/src/status_im/chat/models/group_chat.cljs b/src/status_im/chat/models/group_chat.cljs index 3dd3b6786c..71e1eb4742 100644 --- a/src/status_im/chat/models/group_chat.cljs +++ b/src/status_im/chat/models/group_chat.cljs @@ -1,13 +1,16 @@ (ns status-im.chat.models.group-chat - (:require - [clojure.set :as set] - [status-im.i18n :as i18n] - [status-im.transport.utils :as transport.utils] - [status-im.ui.screens.group.core :as group] - [status-im.chat.models :as models.chat] - [status-im.transport.message.core :as message] - [status-im.chat.models.message :as models.message] - [status-im.utils.fx :as fx])) + (:require [clojure.set :as set] + [clojure.string :as string] + [status-im.i18n :as i18n] + [status-im.transport.utils :as transport.utils] + [status-im.transport.message.core :as transport] + [status-im.transport.message.v1.core :as transport.message] + [status-im.ui.screens.group.core :as group] + [status-im.group-chats.core :as group-chat] + [status-im.chat.models :as models.chat] + [status-im.transport.message.core :as message] + [status-im.chat.models.message :as models.message] + [status-im.utils.fx :as fx])) (defn- participants-diff [existing-participants-set new-participants-set] {:removed (set/difference existing-participants-set new-participants-set) @@ -29,15 +32,10 @@ (seq removed-participants) (str admin-name " " (i18n/label :t/removed) " " (apply str (interpose ", " removed-participants-names)))))) -(defn handle-group-chat-create [{:keys [chat-name participants chat-id]} signature {:keys [now db random-id] :as cofx}] - (models.chat/add-group-chat chat-id - chat-name - signature - participants - cofx)) - -(defn handle-group-admin-update [{:keys [chat-name participants chat-id]} chat-id signature {:keys [now db random-id] :as cofx}] - (let [me (:current-public-key db)] +(fx/defn handle-group-admin-update [{:keys [now db random-id-generator] :as cofx} + {:keys [chat-name participants]} chat-id signature] + (let [me (:current-public-key db) + system-message-id (random-id-generator)] ;; we have to check if we already have a chat, or it's a new one (if-let [{:keys [group-admin contacts] :as chat} (get-in db [:chats chat-id])] ;; update for existing group chat @@ -49,7 +47,7 @@ (if (removed me) ;; we were removed (fx/merge cofx (models.message/receive - (models.message/system-message chat-id random-id now + (models.message/system-message chat-id system-message-id now (str admin-name " " (i18n/label :t/removed-from-chat)))) (models.chat/upsert-chat {:chat-id chat-id :removed-from-at now @@ -57,7 +55,7 @@ (transport.utils/unsubscribe-from-chat chat-id)) (fx/merge cofx (models.message/receive - (models.message/system-message chat-id random-id now + (models.message/system-message chat-id system-message-id now (prepare-system-message admin-name added removed @@ -69,8 +67,9 @@ (models.chat/add-group-chat cofx chat-id chat-name signature participants))))) (fx/defn handle-group-leave - [{:keys [db random-id now] :as cofx} chat-id signature] + [{:keys [db random-id-generator now] :as cofx} chat-id signature] (let [me (:current-public-key db) + system-message-id (random-id-generator) participant-leaving-name (or (get-in db [:contacts/contacts signature :name]) signature)] (when (and @@ -79,6 +78,29 @@ (fx/merge cofx (models.message/receive - (models.message/system-message chat-id random-id now + (models.message/system-message chat-id system-message-id now (str participant-leaving-name " " (i18n/label :t/left)))) (group/participants-removed chat-id #{signature}))))) + +(defn- group-name-from-contacts [selected-contacts all-contacts username] + (->> selected-contacts + (map (comp :name (partial get all-contacts))) + (cons username) + (string/join ", "))) + +(fx/defn send-group-update [cofx group-update chat-id] + (transport/send group-update chat-id cofx)) + +(fx/defn start-group-chat + "Starts a new group chat" + [{:keys [db random-id-generator] :as cofx} group-name] + (let [my-public-key (:current-public-key db) + chat-id (random-id-generator) + selected-contacts (conj (:group/selected-contacts db) + my-public-key) + group-update (transport.message/GroupMembershipUpdate. chat-id group-name my-public-key selected-contacts nil nil nil)] + (fx/merge cofx + {:db (assoc db :group/selected-contacts #{})} + (models.chat/navigate-to-chat chat-id {}) + (group-chat/handle-membership-update group-update my-public-key) + (send-group-update group-update chat-id)))) diff --git a/src/status_im/chat/models/input.cljs b/src/status_im/chat/models/input.cljs index e24e65e415..be6149023d 100644 --- a/src/status_im/chat/models/input.cljs +++ b/src/status_im/chat/models/input.cljs @@ -1,129 +1,60 @@ (ns status-im.chat.models.input - (:require [clojure.string :as str] + (:require [clojure.string :as string] + [re-frame.core :as re-frame] [goog.object :as object] - [status-im.chat.constants :as const] - [status-im.chat.models :as chat-model] - [status-im.utils.config :as config] + [status-im.constants :as constants] + [status-im.chat.constants :as chat.constants] + [status-im.chat.models :as chat] + [status-im.chat.models.message :as chat.message] + [status-im.chat.commands.core :as commands] + [status-im.chat.commands.input :as commands.input] + [status-im.chat.commands.sending :as commands.sending] [status-im.utils.datetime :as datetime] [status-im.js-dependencies :as dependencies] - [status-im.utils.fx :as fx])) - -(def space-char " ") + [status-im.utils.fx :as fx] + [taoensso.timbre :as log])) (defn text->emoji "Replaces emojis in a specified `text`" [text] (when text - (str/replace text - #":([a-z_\-+0-9]*):" - (fn [[original emoji-id]] - (if-let [emoji-map (object/get (object/get dependencies/emojis "lib") emoji-id)] - (object/get emoji-map "char") - original))))) - -(defn text-ends-with-space? [text] - (and text (str/ends-with? text const/spacing-char))) - -(defn starts-as-command? - "Returns true if `text` may be treated as a command. - To make sure that text is command we need to use `possible-chat-actions` function." - [text] - (and text (str/starts-with? text const/command-char))) - -(defn split-command-args - "Returns a list of command's arguments including the command's name. - - Examples: - Input: '/send Jarrad 1.0' - Output: ['/send' 'Jarrad' '1.0'] - - Input: '/send \"Complex name with space in between\" 1.0' - Output: ['/send' 'Complex name with space in between' '1.0'] - - All the complex logic inside this function aims to support wrapped arguments." - [command-text] - (when command-text - (let [space? (text-ends-with-space? command-text) - command-text (if space? - (str command-text ".") - command-text) - command-text-normalized (if command-text - (str/replace (str/trim command-text) #" +" " ") - command-text) - splitted (cond-> (str/split command-text-normalized const/spacing-char) - space? (drop-last))] - (->> splitted - (reduce (fn [[list command-started?] arg] - (let [quotes-count (count (filter #(= % const/arg-wrapping-char) arg)) - has-quote? (and (= quotes-count 1) - (str/index-of arg const/arg-wrapping-char)) - arg (str/replace arg (re-pattern const/arg-wrapping-char) "") - new-list (if command-started? - (let [index (dec (count list))] - (update list index str const/spacing-char arg)) - (conj list arg)) - command-continues? (or (and command-started? (not has-quote?)) - (and (not command-started?) has-quote?))] - [new-list command-continues?])) - [[] false]) - (first))))) - -(defn join-command-args - "Transforms a list of args to a string. The opposite of `split-command-args`. - - Examples: - Input: ['/send' 'Jarrad' '1.0'] - Output: '/send Jarrad 1.0' - - Input: ['/send' '\"Jarrad\"' '1.0'] - Output: '/send Jarrad 1.0' - - Input: ['/send' 'Complex name with space in between' '1.0'] - Output: '/send \"Complex name with space in between\" 1.0'" - [args] - (when args - (->> args - (map (fn [arg] - (let [arg (str/replace arg (re-pattern const/arg-wrapping-char) "")] - (if (not (str/index-of arg const/spacing-char)) - arg - (str const/arg-wrapping-char arg const/arg-wrapping-char))))) - (str/join const/spacing-char)))) + (string/replace text + #":([a-z_\-+0-9]*):" + (fn [[original emoji-id]] + (if-let [emoji-map (object/get (object/get dependencies/emojis "lib") emoji-id)] + (object/get emoji-map "char") + original))))) (fx/defn set-chat-input-text "Set input text for current-chat. Takes db and input text and cofx as arguments and returns new fx. Always clear all validation messages." [{{:keys [current-chat-id] :as db} :db} new-input] - {:db (-> (chat-model/set-chat-ui-props db {:validation-messages nil}) + {:db (-> (chat/set-chat-ui-props db {:validation-messages nil}) (assoc-in [:chats current-chat-id :input-text] (text->emoji new-input)))}) -(fx/defn set-chat-input-metadata - "Sets user invisible chat input metadata for current-chat" - [{:keys [db] :as cofx} metadata] - (let [current-chat-id (:current-chat-id db)] - {:db (assoc-in db [:chats current-chat-id :input-metadata] metadata)})) - (defn- start-cooldown [{:keys [db]} cooldowns] - {:dispatch-later [{:dispatch [:disable-cooldown] - :ms (const/cooldown-periods-ms cooldowns - const/default-cooldown-period-ms)}] + {:dispatch-later [{:dispatch [:chat/disable-cooldown] + :ms (chat.constants/cooldown-periods-ms + cooldowns + chat.constants/default-cooldown-period-ms)}] :show-cooldown-warning nil :db (assoc db - :chat/cooldowns (if (= const/cooldown-reset-threshold cooldowns) + :chat/cooldowns (if (= chat.constants/cooldown-reset-threshold cooldowns) 0 cooldowns) :chat/spam-messages-frequency 0 :chat/cooldown-enabled? true)}) (fx/defn process-cooldown + "Process cooldown to protect against message spammers" [{{:keys [chat/last-outgoing-message-sent-at chat/cooldowns chat/spam-messages-frequency current-chat-id] :as db} :db :as cofx}] - (when (chat-model/public-chat? current-chat-id cofx) + (when (chat/public-chat? current-chat-id cofx) (let [spamming-fast? (< (- (datetime/timestamp) last-outgoing-message-sent-at) - (+ const/spam-interval-ms (* 1000 cooldowns))) - spamming-frequently? (= const/spam-message-frequency-threshold spam-messages-frequency)] + (+ chat.constants/spam-interval-ms (* 1000 cooldowns))) + spamming-frequently? (= chat.constants/spam-message-frequency-threshold spam-messages-frequency)] (cond-> {:db (assoc db :chat/last-outgoing-message-sent-at (datetime/timestamp) :chat/spam-messages-frequency (if spamming-fast? @@ -132,3 +63,97 @@ (and spamming-fast? spamming-frequently?) (start-cooldown (inc cooldowns)))))) + +(fx/defn chat-input-focus + "Returns fx for focusing on active chat input reference" + [{{: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})) + +(fx/defn select-chat-input-command + "Sets chat command and focuses on input" + [{:keys [db] :as cofx} command params previous-command-message] + (fx/merge cofx + (commands.input/set-command-reference previous-command-message) + (commands.input/select-chat-input-command command params) + (chat-input-focus :input-ref))) + +(fx/defn set-command-prefix + "Sets command prefix character and focuses on input" + [{:keys [db] :as cofx}] + (fx/merge cofx + (set-chat-input-text chat.constants/command-char) + (chat-input-focus :input-ref))) + +(fx/defn reply-to-message + "Sets reference to previous chat message and focuses on input" + [{:keys [db] :as cofx} message-id] + (let [current-chat-id (:current-chat-id db)] + (fx/merge cofx + {:db (assoc-in db [:chats current-chat-id :metadata :responding-to-message] message-id)} + (chat-input-focus :input-ref)))) + +(fx/defn cancel-message-reply + "Cancels stage message reply" + [{:keys [db] :as cofx}] + (let [current-chat-id (:current-chat-id db)] + (fx/merge cofx + {:db (assoc-in db [:chats current-chat-id :metadata :responding-to-message] nil)} + (chat-input-focus :input-ref)))) + +(defn command-complete-fx + "command is complete, proceed with command processing" + [input-text command {:keys [db now] :as cofx}] + (fx/merge cofx + (commands.sending/validate-and-send input-text command) + (set-chat-input-text nil) + (process-cooldown))) + +(defn command-not-complete-fx + "command is not complete, just add space after command if necessary" + [input-text current-chat-id {:keys [db]}] + {:db (cond-> db + (not (commands.input/command-ends-with-space? input-text)) + (assoc-in [:chats current-chat-id :input-text] + (str input-text chat.constants/spacing-char)))}) + +(defn plain-text-message-fx + "no command detected, when not empty, proceed by sending text message without command processing" + [input-text current-chat-id {:keys [db] :as cofx}] + (when-not (string/blank? input-text) + (let [reply-to-message (get-in db [:chats current-chat-id :metadata :responding-to-message])] + (fx/merge cofx + {:db (assoc-in db [:chats current-chat-id :metadata :responding-to-message] nil)} + (chat.message/send-message {:chat-id current-chat-id + :content-type constants/text-content-type + :content (cond-> {:text input-text} + reply-to-message + (assoc :response-to reply-to-message))}) + (commands.input/set-command-reference nil) + (set-chat-input-text nil) + (process-cooldown))))) + +(fx/defn send-current-message + "Sends message from current chat input" + [{{:keys [current-chat-id id->command access-scope->command-id] :as db} :db :as cofx}] + (let [input-text (get-in db [:chats current-chat-id :input-text]) + command (commands.input/selected-chat-command + input-text nil (commands/chat-commands id->command + access-scope->command-id + (get-in db [:chats current-chat-id])))] + (if command + ;; Returns true if current input contains command + (if (= :complete (:command-completion command)) + (command-complete-fx input-text command cofx) + (command-not-complete-fx input-text current-chat-id cofx)) + (plain-text-message-fx input-text current-chat-id cofx)))) + +;; effects + +(re-frame/reg-fx + ::focus-rn-component + (fn [ref] + (try + (.focus ref) + (catch :default e + (log/debug "Cannot focus the reference"))))) diff --git a/src/status_im/chat/models/message.cljs b/src/status_im/chat/models/message.cljs index 19ef600119..31bb97d732 100644 --- a/src/status_im/chat/models/message.cljs +++ b/src/status_im/chat/models/message.cljs @@ -1,5 +1,7 @@ (ns status-im.chat.models.message - (:require [re-frame.core :as re-frame] + (:require [clojure.string :as string] + [re-frame.core :as re-frame] + [status-im.native-module.core :as status] [status-im.constants :as constants] [status-im.i18n :as i18n] [status-im.utils.core :as utils] @@ -9,21 +11,18 @@ [status-im.group-chats.core :as group-chats] [status-im.chat.models :as chat-model] [status-im.chat.models.loading :as chat-loading] - [status-im.chat.models.input :as input] [status-im.chat.commands.receiving :as commands-receiving] [status-im.utils.clocks :as utils.clocks] [status-im.utils.money :as money] + [status-im.utils.types :as types] [status-im.notifications.core :as notifications] [status-im.transport.utils :as transport.utils] [status-im.transport.message.core :as transport] [status-im.transport.message.v1.protocol :as protocol] [status-im.data-store.messages :as messages-store] [status-im.data-store.user-statuses :as user-statuses-store] - [clojure.string :as string] - [status-im.utils.fx :as fx])) - -(def receive-interceptors - [(re-frame/inject-cofx :random-id)]) + [status-im.utils.fx :as fx] + [taoensso.timbre :as log])) (defn- emoji-only-content? [content] @@ -73,7 +72,7 @@ (assoc message :outgoing (= from (:current-public-key db)))) (fx/defn add-message - [{:keys [db] :as cofx} batch? {:keys [chat-id message-id clock-value content] :as message} current-chat?] + [{:keys [db] :as cofx} batch? {:keys [chat-id message-id clock-value] :as message} current-chat?] (let [prepared-message (-> message (prepare-message chat-id current-chat?) (add-outgoing-status cofx))] @@ -102,7 +101,7 @@ message (assoc message :clock-value (utils.clocks/send last-clock-value)))) -(defn- update-legacy-type [{:keys [content-type] :as message}] +(defn- update-legacy-data [{:keys [content-type content] :as message}] (cond-> message (= constants/content-type-command-request content-type) (assoc :content-type constants/content-type-command))) @@ -133,7 +132,7 @@ ;; TODO (cammellos): Refactor so it's not computed twice (add-outgoing-status cofx) ;; TODO (janherich): Remove after couple of releases - update-legacy-type)] + update-legacy-data)] (fx/merge cofx {:confirm-messages-processed [{:web3 web3 :js-obj js-obj}]} @@ -165,15 +164,37 @@ (re-index-message-groups chat-id) (chat-loading/group-chat-messages chat-id (get chat->message chat-id)))) +(defn- add-to-chat? + [{:keys [db]} {:keys [chat-id clock-value message-id] :as message}] + (let [{:keys [deleted-at-clock-value messages not-loaded-message-ids]} + (get-in db [:chats chat-id])] + (not (or (get messages message-id) + (get not-loaded-message-ids message-id) + (>= deleted-at-clock-value clock-value))))) + +(defn- filter-messages [cofx messages] + (:accumulated (reduce (fn [{:keys [seen-ids] :as acc} + {:keys [message-id] :as message}] + (if (and (add-to-chat? cofx message) + (not (seen-ids message-id))) + (-> acc + (update :seen-ids conj message-id) + (update :accumulated conj message)) + acc)) + {:seen-ids #{} + :accumulated []} + messages))) + (fx/defn receive-many [{:keys [now] :as cofx} messages] - (let [chat->message (group-by :chat-id messages) - chat-ids (keys chat->message) - chats-fx-fns (map #(chat-model/upsert-chat {:chat-id % - :is-active true - :timestamp now}) - chat-ids) - messages-fx-fns (map #(add-received-message true %) messages) + (let [deduped-messages (filter-messages cofx messages) + chat->message (group-by :chat-id deduped-messages) + chat-ids (keys chat->message) + chats-fx-fns (map #(chat-model/upsert-chat {:chat-id % + :is-active true + :timestamp now}) + chat-ids) + messages-fx-fns (map #(add-received-message true %) deduped-messages) groups-fx-fns (map #(update-group-messages chat->message %) chat-ids)] (apply fx/merge cofx (concat chats-fx-fns messages-fx-fns groups-fx-fns)))) @@ -190,25 +211,13 @@ (defn group-message? [{:keys [message-type]}] (#{:group-user-message :public-group-user-message} message-type)) -(defn add-to-chat? - [{:keys [db]} {:keys [chat-id clock-value message-id] :as message}] - (let [{:keys [deleted-at-clock-value messages not-loaded-message-ids]} - (get-in db [:chats chat-id])] - (not (or (get messages message-id) - (get not-loaded-message-ids message-id) - (>= deleted-at-clock-value clock-value))))) - ;;;; Send message -(def send-interceptors - [(re-frame/inject-cofx :random-id) - (re-frame/inject-cofx :random-id-seq)]) - (fx/defn send - [{{:keys [network-status current-public-key]} :db :as cofx} chat-id message-id send-record] + [{{:keys [network-status]} :db :as cofx} chat-id message-id send-record] (if (= network-status :offline) {:dispatch-later [{:ms 10000 - :dispatch [:update-message-status chat-id message-id current-public-key :not-sent]}]} + :dispatch [:message/update-message-status chat-id message-id :not-sent]}]} (let [wrapped-record (if (= (:message-type send-record) :group-user-message) (group-chats/wrap-group-message cofx chat-id send-record) send-record)] @@ -231,7 +240,6 @@ message-id (transport.utils/message-id send-record) message-with-id (assoc message :message-id message-id)] (fx/merge cofx - (input/process-cooldown) (chat-model/upsert-chat {:chat-id chat-id :timestamp now}) (add-message false message-with-id true) @@ -247,8 +255,9 @@ :sound notifications/sound-name} :tokens [fcm-token]}})) -(fx/defn update-message-status [{:keys [db]} {:keys [chat-id message-id from]} status] - (let [updated-status (-> db +(fx/defn update-message-status [{:keys [db]} chat-id message-id status] + (let [from (get-in db [:chats chat-id :messages message-id :from]) + updated-status (-> db (get-in [:chats chat-id :message-statuses message-id from]) (assoc :status status))] {:db (assoc-in db @@ -264,7 +273,7 @@ protocol/map->Message)] (fx/merge cofx (send chat-id message-id send-record) - (update-message-status message :sending)))) + (update-message-status chat-id message-id :sending)))) (fx/defn remove-message-from-group [{:keys [db]} chat-id {:keys [timestamp message-id]}] @@ -300,3 +309,18 @@ :show? true) (add-message-type chat))] (upsert-and-send cofx message-data))) + +;; effects + +(re-frame.core/reg-fx + :chat-received-message/add-fx + (fn [messages] + (re-frame/dispatch [:message/add messages]))) + +(re-frame/reg-fx + :send-notification + (fn [{:keys [message payload tokens]}] + (let [payload-json (types/clj->json payload) + tokens-json (types/clj->json tokens)] + (log/debug "send-notification message: " message " payload-json: " payload-json " tokens-json: " tokens-json) + (status/notify-users {:message message :payload payload-json :tokens tokens-json} #(log/debug "send-notification cb result: " %))))) diff --git a/src/status_im/chat/subs.cljs b/src/status_im/chat/subs.cljs index 04e827bc65..aa6b7c66cb 100644 --- a/src/status_im/chat/subs.cljs +++ b/src/status_im/chat/subs.cljs @@ -1,10 +1,9 @@ (ns status-im.chat.subs (:require [clojure.string :as string] [re-frame.core :refer [reg-sub subscribe]] - [status-im.chat.constants :as chat-constants] - [status-im.chat.models.input :as input-model] + [status-im.chat.constants :as chat.constants] [status-im.chat.commands.core :as commands] - [status-im.chat.commands.input :as commands-input] + [status-im.chat.commands.input :as commands.input] [status-im.utils.datetime :as time] [status-im.utils.platform :as platform] [status-im.utils.gfycat.core :as gfycat] @@ -122,12 +121,19 @@ (or message-statuses {}))) (defn sort-message-groups - "Sorts message groups according to timestamp of first message in group " + "Sorts message groups according to timestamp of first message in group" [message-groups messages] (sort-by (comp unchecked-negate :timestamp (partial get messages) :message-id first second) message-groups)) +(defn quoted-message-data + "Selects certain data from quoted message which must be available in the view" + [message-id messages] + (let [{:keys [from content]} (get messages message-id)] + {:from from + :text (:text content)})) + (defn messages-with-datemarks-and-statuses "Converts message groups into sequence of messages interspersed with datemarks, with correct user statuses associated into message" @@ -136,10 +142,13 @@ (into (list {:value datemark :type :datemark}) (map (fn [{:keys [message-id timestamp-str]}] - (assoc (get messages message-id) - :datemark datemark - :timestamp-str timestamp-str - :user-statuses (get message-statuses message-id)))) + (let [{:keys [content] :as message} (get messages message-id)] + (cond-> (assoc message + :datemark datemark + :timestamp-str timestamp-str + :user-statuses (get message-statuses message-id)) + (:response-to content) ;; quoted message reference + (assoc-in [:content :response-to] (quoted-message-data (:response-to content) messages)))))) message-references)) message-groups)) @@ -232,7 +241,7 @@ (->> commands map->sorted-seq (filter (fn [{:keys [type]}] - (when (input-model/starts-as-command? input-text) + (when (commands.input/starts-as-command? input-text) (string/includes? (commands/command-name type) input-text)))))) (reg-sub @@ -253,14 +262,14 @@ :<- [:get-current-chat-ui-prop :selection] :<- [:get-commands-for-chat] (fn [[{:keys [input-text]} selection commands]] - (commands-input/selected-chat-command input-text selection commands))) + (commands.input/selected-chat-command input-text selection commands))) (reg-sub :chat-input-placeholder :<- [:get-current-chat] :<- [:selected-chat-command] (fn [[{:keys [input-text]} {:keys [params current-param-position]}]] - (when (string/ends-with? (or input-text "") chat-constants/spacing-char) + (when (string/ends-with? (or input-text "") chat.constants/spacing-char) (get-in params [current-param-position :placeholder])))) (reg-sub @@ -290,7 +299,7 @@ :<- [:get-all-available-commands] (fn [[show-suggestions? {:keys [input-text]} commands]] (and (or show-suggestions? - (input-model/starts-as-command? (string/trim (or input-text "")))) + (commands.input/starts-as-command? (string/trim (or input-text "")))) (seq commands)))) (reg-sub @@ -310,8 +319,11 @@ (reg-sub :get-photo-path :<- [:get-contacts] - (fn [contacts [_ id]] - (:photo-path (contacts id)))) + :<- [:get-current-account] + (fn [[contacts account] [_ id]] + (or (:photo-path (contacts id)) + (when (= id (:public-key account)) + (:photo-path account))))) (reg-sub :get-last-message @@ -361,3 +373,9 @@ (fn [[{:keys [public?]} cooldown-enabled?]] (and public? cooldown-enabled?))) + +(reg-sub + :get-reply-message + :<- [:get-current-chat] + (fn [{:keys [metadata messages]}] + (get messages (:responding-to-message metadata)))) diff --git a/src/status_im/constants.cljs b/src/status_im/constants.cljs index 8f6ceb5936..c01e4d7b31 100644 --- a/src/status_im/constants.cljs +++ b/src/status_im/constants.cljs @@ -7,20 +7,14 @@ (def ethereum-rpc-url "http://localhost:8545") (def text-content-type "text/plain") -(def content-type-log-message "log-message") (def content-type-command "command") (def content-type-command-request "command-request") (def content-type-status "status") -(def content-type-placeholder "placeholder") (def content-type-emoji "emoji") (def desktop-content-types #{text-content-type content-type-emoji}) -(def command-send "send") -(def command-request "request") -(def command-send-status-update-interval-ms 60000) - (def min-password-length 6) (def max-chat-name-length 20) (def response-suggesstion-resize-duration 100) diff --git a/src/status_im/data_store/messages.cljs b/src/status_im/data_store/messages.cljs index eea8ab758e..86810b25e7 100644 --- a/src/status_im/data_store/messages.cljs +++ b/src/status_im/data_store/messages.cljs @@ -1,20 +1,14 @@ (ns status-im.data-store.messages - (:require [cljs.reader :as reader] + (:require [cljs.tools.reader.edn :as edn] [re-frame.core :as re-frame] [status-im.constants :as constants] [status-im.data-store.realm.core :as core] [status-im.utils.core :as utils])) -(defn- command-type? - [type] - (contains? - #{constants/content-type-command constants/content-type-command-request} - type)) - -(defn- transform-message [{:keys [content-type] :as message}] - (cond-> (update message :message-type keyword) - (command-type? content-type) - (update :content reader/read-string))) +(defn- transform-message [message] + (-> message + (update :message-type keyword) + (update :content edn/read-string))) (defn- get-by-chat-id ([chat-id] @@ -26,13 +20,6 @@ (core/all-clj :message))] (map transform-message messages)))) -;; TODO janherich: define as cofx once debug handlers are refactored -(defn get-log-messages - [chat-id] - (->> (get-by-chat-id chat-id 0) - (filter #(= (:content-type %) constants/content-type-log-message)) - (map #(select-keys % [:content :timestamp])))) - (def default-values {:to nil}) @@ -78,10 +65,7 @@ (defn- prepare-content [content] (if (string? content) content - (pr-str - ;; TODO janherich: this is ugly and not systematic, define something like `:not-persisent` - ;; option for command params instead - (update content :params dissoc :password :password-confirmation)))) + (pr-str content))) (defn- prepare-message [message] (utils/update-if-present message :content prepare-content)) diff --git a/src/status_im/data_store/realm/schemas/account/core.cljs b/src/status_im/data_store/realm/schemas/account/core.cljs index 16739f2c5d..f6e9c15116 100644 --- a/src/status_im/data_store/realm/schemas/account/core.cljs +++ b/src/status_im/data_store/realm/schemas/account/core.cljs @@ -122,6 +122,16 @@ browser/v8 dapp-permissions/v9]) +(def v12 [chat/v5 + transport/v6 + contact/v1 + message/v7 + mailserver/v11 + user-status/v1 + local-storage/v1 + browser/v8 + dapp-permissions/v9]) + ;; put schemas ordered by version (def schemas [{:schema v1 :schemaVersion 1 @@ -155,4 +165,7 @@ :migration migrations/v10} {:schema v11 :schemaVersion 11 - :migration migrations/v11}]) + :migration migrations/v11} + {:schema v12 + :schemaVersion 12 + :migration migrations/v12}]) diff --git a/src/status_im/data_store/realm/schemas/account/migrations.cljs b/src/status_im/data_store/realm/schemas/account/migrations.cljs index 52521be3fc..1a586dfd78 100644 --- a/src/status_im/data_store/realm/schemas/account/migrations.cljs +++ b/src/status_im/data_store/realm/schemas/account/migrations.cljs @@ -66,3 +66,21 @@ (let [mailservers (.objects new-realm "mailserver")] (dotimes [i (.-length mailservers)] (aset (aget mailservers i) "fleet" "eth.beta")))) + +(defn v12 [old-realm new-realm] + (log/debug "migrating v12 account database") + (some-> new-realm + (.objects "message") + (.filtered (str "content-type = \"text/plain\"")) + (.map (fn [message _ _] + (let [content (aget message "content") + new-content {:text content}] + (aset message "content" (pr-str new-content)))))) + (some-> new-realm + (.objects "message") + (.filtered (str "content-type = \"emoji\"")) + (.map (fn [message _ _] + (let [content (aget message "content") + new-content {:text content}] + (aset message "content" (pr-str new-content))))))) + diff --git a/src/status_im/data_store/user_statuses.cljs b/src/status_im/data_store/user_statuses.cljs index 6fec7a9443..a85b779d1e 100644 --- a/src/status_im/data_store/user_statuses.cljs +++ b/src/status_im/data_store/user_statuses.cljs @@ -1,6 +1,5 @@ (ns status-im.data-store.user-statuses (:require [clojure.string :as string] - [cljs.reader :as reader] [re-frame.core :as re-frame] [status-im.data-store.realm.core :as core])) diff --git a/src/status_im/dev_server/events.cljs b/src/status_im/dev_server/events.cljs index 3c1519cd6d..1bbc35de9e 100644 --- a/src/status_im/dev_server/events.cljs +++ b/src/status_im/dev_server/events.cljs @@ -26,7 +26,7 @@ (handlers/register-handler-fx :process-http-request - [(re-frame/inject-cofx :random-id)] + [(re-frame/inject-cofx :random-id-generator)] (fn [cofx [_ url type data]] (try (models.dev-server/process-request! {:cofx cofx diff --git a/src/status_im/events.cljs b/src/status_im/events.cljs index 753f8214e5..c201b89a9d 100644 --- a/src/status_im/events.cljs +++ b/src/status_im/events.cljs @@ -9,6 +9,12 @@ [status-im.bootnodes.core :as bootnodes] [status-im.browser.core :as browser] [status-im.browser.permissions :as browser.permissions] + [status-im.chat.models :as chat] + [status-im.chat.models.group-chat :as chat.group] + [status-im.chat.models.message :as chat.message] + [status-im.chat.models.loading :as chat.loading] + [status-im.chat.models.input :as chat.input] + [status-im.chat.commands.input :as commands.input] [status-im.data-store.core :as data-store] [status-im.fleet.core :as fleet] [status-im.hardwallet.core :as hardwallet] @@ -254,7 +260,7 @@ (handlers/register-handler-fx :mailserver.ui/save-pressed - [(re-frame/inject-cofx :random-id)] + [(re-frame/inject-cofx :random-id-generator)] (fn [cofx _] (mailserver/upsert cofx))) @@ -292,7 +298,7 @@ (handlers/register-handler-fx :network.ui/save-network-pressed - [(re-frame/inject-cofx :random-id)] + [(re-frame/inject-cofx :random-id-generator)] (fn [cofx] (network/save-network cofx))) @@ -377,7 +383,7 @@ (handlers/register-handler-fx :bootnodes.ui/save-pressed - [(re-frame/inject-cofx :random-id)] + [(re-frame/inject-cofx :random-id-generator)] (fn [cofx _] (bootnodes/upsert cofx))) @@ -441,18 +447,138 @@ (handlers/register-handler-fx :chat.ui/clear-history-pressed (fn [_ _] - {:ui/show-confirmation {:title (i18n/label :t/clear-history-title) - :content (i18n/label :t/clear-history-confirmation-content) + {:ui/show-confirmation {:title (i18n/label :t/clear-history-title) + :content (i18n/label :t/clear-history-confirmation-content) :confirm-button-text (i18n/label :t/clear-history-action) - :on-accept #(re-frame/dispatch [:clear-history])}})) + :on-accept #(re-frame/dispatch [:chat.ui/clear-history])}})) (handlers/register-handler-fx - :chat.ui/delete-chat-pressed - (fn [_ [_ chat-id]] - {:ui/show-confirmation {:title (i18n/label :t/delete-chat-confirmation) - :content "" - :confirm-button-text (i18n/label :t/delete-chat-action) - :on-accept #(re-frame/dispatch [:remove-chat-and-navigate-home chat-id])}})) + :chat.ui/remove-chat-pressed + (fn [_ [_ chat-id group?]] + {:ui/show-confirmation {:title (i18n/label :t/delete-confirmation) + :content (i18n/label :t/delete-chat-confirmation) + :confirm-button-text (i18n/label :t/delete) + :on-accept #(re-frame/dispatch [:chat.ui/remove-chat chat-id])}})) + +(handlers/register-handler-fx + :chat.ui/set-chat-ui-props + (fn [{:keys [db]} [_ kvs]] + {:db (chat/set-chat-ui-props db kvs)})) + +(handlers/register-handler-fx + :chat.ui/show-message-details + (fn [{:keys [db]} [_ details]] + {:db (chat/set-chat-ui-props db {:show-bottom-info? true + :bottom-info details})})) + +(handlers/register-handler-fx + :chat.ui/show-message-options + (fn [{:keys [db]} [_ options]] + {:db (chat/set-chat-ui-props db {:show-message-options? true + :message-options options})})) + +(handlers/register-handler-fx + :chat.ui/navigate-to-chat + (fn [cofx [_ chat-id opts]] + (chat/navigate-to-chat cofx chat-id opts))) + +(handlers/register-handler-fx + :chat.ui/load-more-messages + [(re-frame/inject-cofx :data-store/get-messages) + (re-frame/inject-cofx :data-store/get-user-statuses)] + (fn [cofx _] + (chat.loading/load-more-messages cofx))) + +(handlers/register-handler-fx + :chat.ui/start-chat + (fn [cofx [_ contact-id opts]] + (chat/start-chat cofx contact-id opts))) + +(handlers/register-handler-fx + :chat.ui/start-public-chat + (fn [cofx [_ topic modal?]] + (chat/start-public-chat cofx topic modal?))) + +(handlers/register-handler-fx + :chat.ui/start-group-chat + (fn [cofx [_ group-name]] + (chat.group/start-group-chat cofx group-name))) + +(handlers/register-handler-fx + :chat.ui/remove-chat + (fn [cofx [_ chat-id]] + (chat/remove-chat cofx chat-id))) + +(handlers/register-handler-fx + :chat.ui/clear-history + (fn [{{:keys [current-chat-id]} :db :as cofx} _] + (chat/clear-history cofx current-chat-id))) + +(handlers/register-handler-fx + :chat.ui/resend-message + (fn [cofx [_ chat-id message-id]] + (chat.message/resend-message cofx chat-id message-id))) + +(handlers/register-handler-fx + :chat.ui/delete-message + (fn [cofx [_ chat-id message-id]] + (chat.message/delete-message cofx chat-id message-id))) + +(handlers/register-handler-fx + :chat.ui/show-profile + (fn [cofx [_ identity]] + (navigation/navigate-to-cofx + (assoc-in cofx [:db :contacts/identity] identity) :profile nil))) + +(handlers/register-handler-fx + :chat.ui/set-chat-input-text + (fn [cofx [_ text]] + (chat.input/set-chat-input-text cofx text))) + +(handlers/register-handler-fx + :chat.ui/select-chat-input-command + (fn [cofx [_ command params previous-command-message]] + (chat.input/select-chat-input-command cofx command params previous-command-message))) + +(handlers/register-handler-fx + :chat.ui/set-command-prefix + (fn [cofx _] + (chat.input/set-command-prefix cofx))) + +(handlers/register-handler-fx + :chat.ui/cancel-message-reply + (fn [cofx _] + (chat.input/cancel-message-reply cofx))) + +(handlers/register-handler-fx + :chat.ui/reply-to-message + (fn [cofx [_ message-id]] + (chat.input/reply-to-message cofx message-id))) + +(handlers/register-handler-fx + :chat.ui/set-command-parameter + (fn [cofx [_ last-param? index value]] + (commands.input/set-command-parameter cofx last-param? index value))) + +(handlers/register-handler-fx + :chat.ui/send-current-message + (fn [cofx _] + (chat.input/send-current-message cofx))) + +(handlers/register-handler-fx + :chat/disable-cooldown + (fn [cofx _] + (chat/disable-chat-cooldown cofx))) + +(handlers/register-handler-fx + :message/add + (fn [cofx [_ messages]] + (chat.message/receive-many cofx messages))) + +(handlers/register-handler-fx + :message/update-message-status + (fn [cofx [_ chat-id message-id status]] + (chat.message/update-message-status cofx chat-id message-id status))) ;; signal module diff --git a/src/status_im/group_chats/core.cljs b/src/status_im/group_chats/core.cljs index 521174f588..5d0a23d39d 100644 --- a/src/status_im/group_chats/core.cljs +++ b/src/status_im/group_chats/core.cljs @@ -1,14 +1,13 @@ (ns status-im.group-chats.core - (:require - [status-im.utils.config :as config] - [status-im.transport.utils :as transport.utils] - [status-im.transport.db :as transport.db] - [status-im.transport.utils :as transport.utils] - [status-im.transport.message.core :as protocol.message] - [status-im.transport.message.v1.core :as transport] - [status-im.transport.message.v1.protocol :as transport.protocol] - [status-im.utils.fx :as fx] - [status-im.chat.models :as models.chat])) + (:require [status-im.utils.config :as config] + [status-im.transport.utils :as transport.utils] + [status-im.transport.db :as transport.db] + [status-im.transport.utils :as transport.utils] + [status-im.transport.message.core :as protocol.message] + [status-im.transport.message.v1.core :as transport] + [status-im.transport.message.v1.protocol :as transport.protocol] + [status-im.utils.fx :as fx] + [status-im.chat.models :as models.chat])) (defn wrap-group-message [cofx chat-id message] (when-let [chat (get-in cofx [:db :chats chat-id])] diff --git a/src/status_im/i18n.cljs b/src/status_im/i18n.cljs index 193a5d602a..56c876e7ee 100644 --- a/src/status_im/i18n.cljs +++ b/src/status_im/i18n.cljs @@ -65,7 +65,7 @@ :datetime-ago-format :close-app-button :block :camera-access-error :wallet-invalid-address :address-explication :remove :transactions-delete-content :transactions-unsigned-empty - :transaction-moved-text :add-members :sign-later-title :sharing-cancel + :transaction-moved-text :add-members :sign-later-title :yes :dapps :popular-tags :network-settings :twelve-words-in-correct-order :transaction-moved-title :photos-access-error :hash :removed-from-chat :done :remove-from-contacts :delete-chat :new-group-chat diff --git a/src/status_im/mailserver/core.cljs b/src/status_im/mailserver/core.cljs index 0609143898..439cc14b09 100644 --- a/src/status_im/mailserver/core.cljs +++ b/src/status_im/mailserver/core.cljs @@ -138,12 +138,13 @@ (navigation/navigate-to-cofx :edit-mailserver nil)))) (fx/defn upsert - [{{:mailservers/keys [manage] :account/keys [account] :as db} :db :as cofx}] + [{{:mailservers/keys [manage] :account/keys [account] :as db} :db + random-id-generator :random-id-generator :as cofx}] (let [{:keys [name url id]} manage current-fleet (fleet/current-fleet db) mailserver (build (or (:value id) - (keyword (string/replace (:random-id cofx) "-" ""))) + (keyword (string/replace (random-id-generator) "-" ""))) (:value name) (:value url)) current (connected? cofx (:id mailserver))] diff --git a/src/status_im/network/core.cljs b/src/status_im/network/core.cljs index e7a32ed494..2f8b9fd120 100644 --- a/src/status_im/network/core.cljs +++ b/src/status_im/network/core.cljs @@ -68,28 +68,26 @@ (when handler (handler data cofx)))) -(defn save - ([cofx] - (save cofx nil)) - ([{{:network/keys [manage] - :account/keys [account] :as db} :db :as cofx} - {:keys [data success-event on-success on-failure]}] - (let [data (or data manage)] - (if (valid-manage? data) - (let [{:keys [name url chain network-id]} data - network (new-network (:random-id cofx) - (:value name) - (:value url) - (:value chain) - (:value network-id)) - new-networks (merge {(:id network) network} (:networks account))] - (fx/merge cofx - {:db (dissoc db :networks/manage)} - #(action-handler on-success (:id network) %) - (accounts.update/account-update - {:networks new-networks} - {:success-event success-event}))) - (action-handler on-failure))))) +(fx/defn save + [{{:network/keys [manage] :account/keys [account] :as db} :db + random-id-generator :random-id-generator :as cofx} + {:keys [data success-event on-success on-failure]}] + (let [data (or data manage)] + (if (valid-manage? data) + (let [{:keys [name url chain network-id]} data + network (new-network (random-id-generator) + (:value name) + (:value url) + (:value chain) + (:value network-id)) + new-networks (merge {(:id network) network} (:networks account))] + (fx/merge cofx + {:db (dissoc db :networks/manage)} + #(action-handler on-success (:id network) %) + (accounts.update/account-update + {:networks new-networks} + {:success-event success-event}))) + (action-handler on-failure)))) ;; No edit functionality actually implemented (fx/defn edit diff --git a/src/status_im/transport/handlers.cljs b/src/status_im/transport/handlers.cljs index d09236d006..9acdb3de22 100644 --- a/src/status_im/transport/handlers.cljs +++ b/src/status_im/transport/handlers.cljs @@ -38,7 +38,7 @@ (aget array i))) (fx/defn receive-whisper-messages - [{:keys [now] :as cofx} [_ js-error js-messages chat-id]] + [{:keys [now] :as cofx} js-error js-messages chat-id] (if (and (not js-error) js-messages) (let [now-in-s (quot now 1000) @@ -50,9 +50,9 @@ (handlers/register-handler-fx :protocol/receive-whisper-message - [handlers/logged-in - (re-frame/inject-cofx :random-id)] - receive-whisper-messages) + [handlers/logged-in (re-frame/inject-cofx :random-id-generator)] + (fn [cofx [_ js-error js-messages chat-id]] + (receive-whisper-messages cofx js-error js-messages chat-id))) (handlers/register-handler-fx :protocol/send-status-message-error @@ -61,7 +61,8 @@ (handlers/register-handler-fx :contact/send-new-sym-key - (fn [{:keys [db random-id] :as cofx} [_ {:keys [chat-id topic message sym-key sym-key-id]}]] + (fn [{:keys [db] :as cofx} + [_ {:keys [chat-id topic message sym-key sym-key-id]}]] (let [{:keys [web3 current-public-key]} db chat-transport-info (-> (get-in db [:transport/chats chat-id]) (assoc :sym-key-id sym-key-id @@ -162,11 +163,11 @@ (remove-hash envelope-hash) (update-resend-contact-message chat-id))) - (when-let [message (get-in db [:chats chat-id :messages message-id])] + (when-let [{:keys [from]} (get-in db [:chats chat-id :messages message-id])] (let [{:keys [fcm-token]} (get-in db [:contacts/contacts chat-id])] (fx/merge cofx (remove-hash envelope-hash) - (models.message/update-message-status message status) + (models.message/update-message-status chat-id message-id status) (models.message/send-push-notification fcm-token status))))))) (defn- own-info [db] diff --git a/src/status_im/transport/impl/receive.cljs b/src/status_im/transport/impl/receive.cljs index b17d120044..7905f52b5f 100644 --- a/src/status_im/transport/impl/receive.cljs +++ b/src/status_im/transport/impl/receive.cljs @@ -15,7 +15,7 @@ (extend-type transport.protocol/GroupLeave message/StatusMessage (receive [this chat-id signature _ cofx] - (models.group-chat/handle-group-leave chat-id signature cofx))) + (models.group-chat/handle-group-leave cofx chat-id signature))) (extend-type transport.contact/ContactRequest message/StatusMessage diff --git a/src/status_im/transport/message/transit.cljs b/src/status_im/transport/message/transit.cljs index 7f11b33052..96f90d4ccc 100644 --- a/src/status_im/transport/message/transit.cljs +++ b/src/status_im/transport/message/transit.cljs @@ -3,6 +3,7 @@ (:require [status-im.transport.message.v1.contact :as v1.contact] [status-im.transport.message.v1.protocol :as v1.protocol] [status-im.transport.message.v1.core :as v1] + [status-im.constants :as constants] [cognitect.transit :as transit])) ;; When adding a new reccord implenting the StatusMessage protocol it is required to implement: @@ -45,7 +46,7 @@ (deftype MessageHandler [] Object - (tag [this v] "c4") + (tag [this v] "c7") (rep [this {:keys [content content-type message-type clock-value timestamp]}] #js [content content-type message-type clock-value timestamp])) @@ -82,6 +83,16 @@ ;; Reader handlers ;; +(defn- safe-content-parse [content-type content] + ;; handling only the text content case + (if (= content-type constants/text-content-type) + (if (and (map? content) (string? (:text content))) + ;; correctly formatted map + content + ;; create safe `{:text string-content}` value from anything else + {:text (str content)}) + content)) + ;; Here we only need to call the record with the arguments parsed from the clojure datastructures (def reader (transit/reader :json {:handlers @@ -92,7 +103,9 @@ "c3" (fn [[name profile-image address fcm-token]] (v1.contact/ContactRequestConfirmed. name profile-image address fcm-token)) "c4" (fn [[content content-type message-type clock-value timestamp]] - (v1.protocol/Message. content content-type message-type clock-value timestamp)) + (v1.protocol/Message. (safe-content-parse content-type content) content-type message-type clock-value timestamp)) + "c7" (fn [[content content-type message-type clock-value timestamp]] + (v1.protocol/Message. (safe-content-parse content-type content) content-type message-type clock-value timestamp)) "c5" (fn [message-ids] (v1.protocol/MessagesSeen. message-ids)) "c6" (fn [[name profile-image address fcm-token]] diff --git a/src/status_im/transport/message/v1/contact.cljs b/src/status_im/transport/message/v1/contact.cljs index c81dc413ab..2a1a600e82 100644 --- a/src/status_im/transport/message/v1/contact.cljs +++ b/src/status_im/transport/message/v1/contact.cljs @@ -9,8 +9,8 @@ (defrecord ContactRequest [name profile-image address fcm-token] message/StatusMessage - (send [this chat-id {:keys [db random-id] :as cofx}] - (let [topic (transport.utils/get-topic random-id) + (send [this chat-id {:keys [db random-id-generator] :as cofx}] + (let [topic (transport.utils/get-topic (random-id-generator)) on-success (fn [sym-key sym-key-id] (re-frame/dispatch [:contact/send-new-sym-key {:sym-key-id sym-key-id diff --git a/src/status_im/ui/components/colors.cljs b/src/status_im/ui/components/colors.cljs index bc8ab36acb..b88e10dd21 100644 --- a/src/status_im/ui/components/colors.cljs +++ b/src/status_im/ui/components/colors.cljs @@ -25,6 +25,7 @@ (def blue-dark "#3147ac") ;; Used as secondary wallet color (icon background) (def hawkes-blue "#dce2fb") ;; Outgoing chat messages background (def wild-blue-yonder "#707caf") ;; Text color for outgoing messages timestamp +(def blue-light "#cad1ed") ;; Text and bottom border color for own quoted messages (def red "#ff2d55") ;; Used to highlight errors or "dangerous" actions (def red-light "#ffe5ea") ;; error tooltip (def text-light-gray "#212121") ;; Used for labels (home items) diff --git a/src/status_im/ui/components/icons/vector_icons.cljs b/src/status_im/ui/components/icons/vector_icons.cljs index d7753c9225..fcb3b37c55 100644 --- a/src/status_im/ui/components/icons/vector_icons.cljs +++ b/src/status_im/ui/components/icons/vector_icons.cljs @@ -95,7 +95,8 @@ :icons/info (js/require "./resources/icons/info.svg") :icons/hardwallet (js/require "./resources/icons/hardwallet.svg") :icons/password (js/require "./resources/icons/password.svg") - :icons/nfc (js/require "./resources/icons/nfc.svg")} + :icons/nfc (js/require "./resources/icons/nfc.svg") + :icons/reply (js/require "./resources/icons/reply.svg")} {:icons/discover (components.svg/slurp-svg "./resources/icons/bottom/discover_gray.svg") :icons/contacts (components.svg/slurp-svg "./resources/icons/bottom/contacts_gray.svg") :icons/home (components.svg/slurp-svg "./resources/icons/bottom/home_gray.svg") @@ -164,7 +165,8 @@ :icons/info (components.svg/slurp-svg "./resources/icons/info.svg") :icons/hardwallet (components.svg/slurp-svg "./resources/icons/hardwallet.svg") :icons/password (components.svg/slurp-svg "./resources/icons/password.svg") - :icons/nfc (components.svg/slurp-svg "./resources/icons/nfc.svg")})) + :icons/nfc (components.svg/slurp-svg "./resources/icons/nfc.svg") + :icons/reply (components.svg/slurp-svg "./resources/icons/reply.svg")})) (defn normalize-property-name [n] (if (= n :icons/options) diff --git a/src/status_im/ui/components/list_selection.cljs b/src/status_im/ui/components/list_selection.cljs index e44f2aa896..d4861ad568 100644 --- a/src/status_im/ui/components/list_selection.cljs +++ b/src/status_im/ui/components/list_selection.cljs @@ -12,8 +12,10 @@ (:url content)) (.share react/sharing (clj->js content)))) -(defn share-options [text] - [{:label (i18n/label :t/sharing-copy-to-clipboard) +(defn- message-options [message-id text] + [{:label (i18n/label :t/message-reply) + :action #(re-frame/dispatch [:chat.ui/reply-to-message message-id])} + {:label (i18n/label :t/sharing-copy-to-clipboard) :action #(react/copy-to-clipboard text)} {:label (i18n/label :t/sharing-share) :action #(open-share {:message text})}]) @@ -23,10 +25,10 @@ (action-sheet/show options) (dialog/show options))) -(defn share [text dialog-title] +(defn chat-message [message-id text dialog-title] (show {:title dialog-title - :options (share-options text) - :cancel-text (i18n/label :t/sharing-cancel)})) + :options (message-options message-id text) + :cancel-text (i18n/label :t/message-options-cancel)})) (defn browse [link] (show {:title (i18n/label :t/browsing-title) diff --git a/src/status_im/ui/screens/add_new/new_chat/views.cljs b/src/status_im/ui/screens/add_new/new_chat/views.cljs index bf2ff5c418..999ee912e8 100644 --- a/src/status_im/ui/screens/add_new/new_chat/views.cljs +++ b/src/status_im/ui/screens/add_new/new_chat/views.cljs @@ -15,7 +15,7 @@ (defn- render-row [row _ _] [contact-view/contact-view {:contact row - :on-press #(re-frame/dispatch [:start-chat (:whisper-identity %) {:navigation-reset? true}]) + :on-press #(re-frame/dispatch [:chat.ui/start-chat (:whisper-identity %) {:navigation-reset? true}]) :show-forward? true}]) (views/defview new-chat [] diff --git a/src/status_im/ui/screens/add_new/new_public_chat/view.cljs b/src/status_im/ui/screens/add_new/new_public_chat/view.cljs index 3f6f25febb..0886af7c5a 100644 --- a/src/status_im/ui/screens/add_new/new_public_chat/view.cljs +++ b/src/status_im/ui/screens/add_new/new_public_chat/view.cljs @@ -30,7 +30,7 @@ (i18n/label :t/topic-name-error))]) (re-frame/dispatch [:set :public-group-topic %])) :on-submit-editing #(when (and topic (spec/valid? ::v/topic topic)) - (re-frame/dispatch [:create-new-public-chat topic])) + (re-frame/dispatch [:chat.ui/start-public-chat topic])) :auto-capitalize :none :auto-focus false :accessibility-label :chat-name-input @@ -47,7 +47,7 @@ (first topic)]]) (defn- render-topic [topic] - [react/touchable-highlight {:on-press #(re-frame/dispatch [:create-new-public-chat topic]) + [react/touchable-highlight {:on-press #(re-frame/dispatch [:chat.ui/start-public-chat topic]) :accessibility-label :chat-item} [react/view [list/item diff --git a/src/status_im/ui/screens/browser/views.cljs b/src/status_im/ui/screens/browser/views.cljs index 805946cd78..ce6cec7c02 100644 --- a/src/status_im/ui/screens/browser/views.cljs +++ b/src/status_im/ui/screens/browser/views.cljs @@ -1,5 +1,5 @@ (ns status-im.ui.screens.browser.views - (:require [cljs.reader :as reader] + (:require [cljs.tools.reader.edn :as edn] [re-frame.core :as re-frame] [reagent.core :as reagent] [status-im.browser.core :as browser] @@ -24,7 +24,7 @@ [status-im.utils.views :as views])) (def browser-config - (reader/read-string (slurp "./src/status_im/utils/browser_config.edn"))) + (edn/read-string (slurp "./src/status_im/utils/browser_config.edn"))) (defn toolbar-content [url {:keys [secure?] :as browser} url-editing?] (let [url-text (atom url)] diff --git a/src/status_im/ui/screens/chat/actions.cljs b/src/status_im/ui/screens/chat/actions.cljs index 9260ac2a5b..0a59b94690 100644 --- a/src/status_im/ui/screens/chat/actions.cljs +++ b/src/status_im/ui/screens/chat/actions.cljs @@ -8,7 +8,7 @@ (defn view-profile [chat-id] {:label (i18n/label :t/view-profile) - :action #(re-frame/dispatch [:show-profile chat-id])}) + :action #(re-frame/dispatch [:chat.ui/show-profile chat-id])}) (defn group-info [chat-id] {:label (i18n/label :t/group-info) @@ -16,11 +16,11 @@ (defn- clear-history [] {:label (i18n/label :t/clear-history) - :action #(re-frame/dispatch [:clear-history?])}) + :action #(re-frame/dispatch [:chat.ui/clear-history-pressed])}) (defn- delete-chat [chat-id group?] {:label (i18n/label :t/delete-chat) - :action #(re-frame/dispatch [:remove-chat-and-navigate-home? chat-id group?])}) + :action #(re-frame/dispatch [:chat.ui/remove-chat-pressed chat-id group?])}) (defn- chat-actions [chat-id] [view-my-wallet diff --git a/src/status_im/ui/screens/chat/bottom_info.cljs b/src/status_im/ui/screens/chat/bottom_info.cljs index 20054cb553..1990c0e289 100644 --- a/src/status_im/ui/screens/chat/bottom_info.cljs +++ b/src/status_im/ui/screens/chat/bottom_info.cljs @@ -74,7 +74,7 @@ :status message-status}])) (into {})) statuses (vals (merge participants user-statuses))] - [overlay {:on-click-outside #(re-frame/dispatch [:set-chat-ui-props {:show-bottom-info? false}])} + [overlay {:on-click-outside #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:show-bottom-info? false}])} [container (* styles/item-height (count statuses)) [list/flat-list {:contentContainerStyle styles/bottom-info-list-container :data statuses diff --git a/src/status_im/ui/screens/chat/input/input.cljs b/src/status_im/ui/screens/chat/input/input.cljs index 3a5d22e5b3..6f43921f7b 100644 --- a/src/status_im/ui/screens/chat/input/input.cljs +++ b/src/status_im/ui/screens/chat/input/input.cljs @@ -3,18 +3,20 @@ (:require [clojure.string :as string] [reagent.core :as reagent] [re-frame.core :as re-frame] - [status-im.chat.constants :as constants] [status-im.ui.screens.chat.styles.input.input :as style] + [status-im.ui.screens.chat.styles.message.message :as message-style] [status-im.ui.screens.chat.input.parameter-box :as parameter-box] [status-im.ui.screens.chat.input.send-button :as send-button] [status-im.ui.screens.chat.input.suggestions :as suggestions] [status-im.ui.screens.chat.input.validation-messages :as validation-messages] + [status-im.ui.screens.chat.photos :as photos] [status-im.i18n :as i18n] [status-im.ui.components.animation :as animation] [status-im.ui.components.colors :as colors] [status-im.ui.components.react :as react] - [status-im.ui.components.icons.vector-icons :as vi] + [status-im.ui.components.icons.vector-icons :as vector-icons] [status-im.utils.platform :as platform] + [status-im.utils.gfycat.core :as gfycat] [status-im.utils.utils :as utils])) (defview basic-text-input [{:keys [set-container-width-fn height single-line-input?]}] @@ -22,23 +24,23 @@ cooldown-enabled? [:chat-cooldown-enabled?]] [react/text-input (merge - {:ref #(when % (re-frame/dispatch [:set-chat-ui-props {:input-ref %}])) + {:ref #(when % (re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-ref %}])) :accessibility-label :chat-message-input :multiline (not single-line-input?) :default-value (or input-text "") :editable (not cooldown-enabled?) :blur-on-submit false - :on-focus #(re-frame/dispatch [:set-chat-ui-props {:input-focused? true - :messages-focused? false}]) - :on-blur #(re-frame/dispatch [:set-chat-ui-props {:input-focused? false}]) + :on-focus #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-focused? true + :messages-focused? false}]) + :on-blur #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-focused? false}]) :on-submit-editing #(when single-line-input? - (re-frame/dispatch [:send-current-message])) + (re-frame/dispatch [:chat.ui/send-current-message])) :on-layout #(set-container-width-fn (.-width (.-layout (.-nativeEvent %)))) - :on-change #(re-frame/dispatch [:set-chat-input-text (.-text (.-nativeEvent %))]) + :on-change #(re-frame/dispatch [:chat.ui/set-chat-input-text (.-text (.-nativeEvent %))]) :on-selection-change #(let [s (-> (.-nativeEvent %) (.-selection)) end (.-end s)] - (re-frame/dispatch [:update-text-selection end])) + (re-frame/dispatch [:chat.ui/set-chat-ui-props {:selection end}])) :style (style/input-view single-line-input?) :placeholder-text-color colors/gray :auto-capitalize :sentences} @@ -92,15 +94,40 @@ [input-helper {:width width}]]]))) (defview commands-button [] - (letsubs [commands [:get-all-available-commands]] - (when (seq commands) + (letsubs [commands [:get-all-available-commands] + reply-message [:get-reply-message]] + (when (and (not reply-message) (seq commands)) [react/touchable-highlight - {:on-press #(do (re-frame/dispatch [:set-chat-input-text constants/command-char]) - (re-frame/dispatch [:chat-input-focus :input-ref])) + {:on-press #(re-frame/dispatch [:chat.ui/set-command-prefix]) :accessibility-label :chat-commands-button} [react/view - [vi/icon :icons/input-commands {:container-style style/input-commands-icon - :color :dark}]]]))) + [vector-icons/icon :icons/input-commands {:container-style style/input-commands-icon + :color :dark}]]]))) + +(defview reply-message [from message-text] + (letsubs [username [:get-contact-name-by-identity from] + current-public-key [:get-current-public-key]] + [react/view {:style style/reply-message-content} + [react/text {:style style/reply-message-author} (or (and (= from current-public-key) + (i18n/label :t/You)) + username + (gfycat/generate-gfy from))] + [react/text {:style message-style/style-message-text} message-text]])) + +(defview reply-message-view [] + (letsubs [{:keys [content from] :as message} [:get-reply-message]] + (when message + [react/view {:style style/reply-message-container} + [react/view {:style style/reply-message} + [photos/member-photo from] + [reply-message from (:text content)]] + [react/touchable-highlight + {:style style/cancel-reply-highlight + :on-press #(re-frame/dispatch [:chat.ui/cancel-message-reply]) + :accessibility-label :cancel-message-reply} + [react/view {:style style/cancel-reply-container} + [vector-icons/icon :icons/close {:container-style style/cancel-reply-icon + :color colors/white}]]]]))) (defview input-container [] (letsubs [margin [:chat-input-margin] @@ -112,7 +139,8 @@ (.-layout) (.-height))] (when (> h 0) - (re-frame/dispatch [:set-chat-ui-props {:input-height h}])))} + (re-frame/dispatch [:chat.ui/set-chat-ui-props {:input-height h}])))} + [reply-message-view] [react/view {:style style/input-container} [input-view {:single-line-input? single-line-input?}] (if (string/blank? input-text) diff --git a/src/status_im/ui/screens/chat/input/send_button.cljs b/src/status_im/ui/screens/chat/input/send_button.cljs index fe63b97ad7..ed9736892b 100644 --- a/src/status_im/ui/screens/chat/input/send_button.cljs +++ b/src/status_im/ui/screens/chat/input/send_button.cljs @@ -30,7 +30,7 @@ (when (and (sendable? input-text network-status) (or (not command-completion) (#{:complete :less-than-needed} command-completion))) - [react/touchable-highlight {:on-press #(re-frame/dispatch [:send-current-message])} + [react/touchable-highlight {:on-press #(re-frame/dispatch [:chat.ui/send-current-message])} (let [spin (.interpolate spin-value (clj->js {:inputRange [0 1] :outputRange ["0deg" "90deg"]}))] [react/animated-view diff --git a/src/status_im/ui/screens/chat/input/suggestions.cljs b/src/status_im/ui/screens/chat/input/suggestions.cljs index 702aef8c99..b10f639530 100644 --- a/src/status_im/ui/screens/chat/input/suggestions.cljs +++ b/src/status_im/ui/screens/chat/input/suggestions.cljs @@ -26,7 +26,7 @@ (map-indexed (fn [i {:keys [type] :as command}] ^{:key i} - [suggestion-item {:on-press #(re-frame/dispatch [:select-chat-input-command command nil]) + [suggestion-item {:on-press #(re-frame/dispatch [:chat.ui/select-chat-input-command command nil]) :name (commands/command-name type) :description (commands/command-description type) :last? (= i (dec (count available-commands))) diff --git a/src/status_im/ui/screens/chat/message/message.cljs b/src/status_im/ui/screens/chat/message/message.cljs index a9a77d5cb9..534c68f2c0 100644 --- a/src/status_im/ui/screens/chat/message/message.cljs +++ b/src/status_im/ui/screens/chat/message/message.cljs @@ -57,7 +57,7 @@ (defview message-timestamp [t justify-timestamp? outgoing command? content] (when-not command? - (let [rtl? (right-to-left-text? content)] + (let [rtl? (right-to-left-text? (:text content))] [react/text {:style (style/message-timestamp-text justify-timestamp? outgoing rtl?)} t]))) (defn message-view @@ -165,12 +165,25 @@ :on-press on-press} (i18n/label (if @collapsed? :show-more :show-less))]) +(defview quoted-message [{:keys [from text]} outgoing current-public-key] + (letsubs [username [:get-contact-name-by-identity from]] + [react/view {:style (style/quoted-message-container outgoing)} + [react/view {:style style/quoted-message-author-container} + [vector-icons/icon :icons/reply {:color (if outgoing colors/wild-blue-yonder colors/gray)}] + [react/text {:style (style/quoted-message-author outgoing)} (or (and (= from current-public-key) + (i18n/label :t/You)) + username + (gfycat/generate-gfy from))]] + [react/text {:style (style/quoted-message-text outgoing) + :number-of-lines 5} + text]])) + (defn text-message - [{:keys [content timestamp-str group-chat outgoing] :as message}] + [{:keys [content timestamp-str group-chat outgoing current-public-key] :as message}] [message-view message - (let [parsed-text (cached-parse-text content :browser.ui/message-link-pressed) + (let [parsed-text (cached-parse-text (:text content) :browser.ui/message-link-pressed) ref (reagent/atom nil) - collapsible? (should-collapse? content group-chat) + collapsible? (should-collapse? (:text content) group-chat) collapsed? (reagent/atom collapsible?) on-press (when collapsible? #(do @@ -180,6 +193,8 @@ number-of-lines)})) (reset! collapsed? (not @collapsed?))))] [react/view + (when (:response-to content) + [quoted-message (:response-to content) outgoing current-public-key]) [react/text {:style (style/text-message collapsible?) :number-of-lines (when collapsible? number-of-lines) :ref (partial reset! ref)} @@ -192,7 +207,7 @@ (defn emoji-message [{:keys [content] :as message}] [message-view message - [react/text {:style (style/emoji-message message)} content]]) + [react/text {:style (style/emoji-message message)} (:text content)]]) (defmulti message-content (fn [_ message _] (message :content-type))) @@ -200,10 +215,6 @@ [wrapper message] [wrapper message [text-message message]]) -(defmethod message-content constants/content-type-log-message - [wrapper message] - [wrapper message [text-message message]]) - (defmethod message-content constants/content-type-status [_ _] [message-content-status]) @@ -248,9 +259,9 @@ (if (or seen-by-everyone (zero? delivery-statuses-count)) [text-status (or seen-by-everyone outgoing-status)] [react/touchable-highlight - {:on-press #(re-frame/dispatch [:show-message-details {:message-status outgoing-status - :user-statuses delivery-statuses - :participants participants}])} + {:on-press #(re-frame/dispatch [:chat.ui/show-message-details {:message-status outgoing-status + :user-statuses delivery-statuses + :participants participants}])} [react/view style/delivery-view (for [[whisper-identity] (take 3 delivery-statuses)] ^{:key whisper-identity} @@ -272,13 +283,13 @@ [react/touchable-highlight {:on-press (fn [] (if platform/ios? (action-sheet/show {:title (i18n/label :message-not-sent) :options [{:label (i18n/label :resend-message) - :action #(re-frame/dispatch [:resend-message chat-id message-id])} + :action #(re-frame/dispatch [:chat.ui/resend-message chat-id message-id])} {:label (i18n/label :delete-message) :destructive? true - :action #(re-frame/dispatch [:delete-message chat-id message-id])}]}) + :action #(re-frame/dispatch [:chat.ui/delete-message chat-id message-id])}]}) (re-frame/dispatch - [:show-message-options {:chat-id chat-id - :message-id message-id}])))} + [:chat.ui/show-message-options {:chat-id chat-id + :message-id message-id}])))} [react/view style/not-sent-view [react/text {:style style/not-sent-text} (i18n/message-status-label (if platform/desktop? @@ -333,7 +344,7 @@ (when display-photo? [react/view style/message-author (when last-in-group? - [react/touchable-highlight {:on-press #(when-not modal? (re-frame/dispatch [:show-profile from]))} + [react/touchable-highlight {:on-press #(when-not modal? (re-frame/dispatch [:chat.ui/show-profile from]))} [react/view [photos/member-photo from]]])]) [react/view (style/group-message-view outgoing) @@ -344,13 +355,13 @@ [react/view (style/delivery-status outgoing) [message-delivery-status message]]]) -(defn chat-message [{:keys [outgoing group-chat modal? current-public-key content-type content] :as message}] +(defn chat-message [{:keys [message-id outgoing group-chat modal? current-public-key content-type content] :as message}] [react/view [react/touchable-highlight {:on-press (fn [_] - (re-frame/dispatch [:set-chat-ui-props {:messages-focused? true}]) + (re-frame/dispatch [:chat.ui/set-chat-ui-props {:messages-focused? true}]) (react/dismiss-keyboard!)) :on-long-press #(when (= content-type constants/text-content-type) - (list-selection/share content (i18n/label :t/message)))} + (list-selection/chat-message message-id (:text content) (i18n/label :t/message)))} [react/view {:accessibility-label :chat-item} (let [incoming-group (and group-chat (not outgoing))] [message-content message-body (merge message diff --git a/src/status_im/ui/screens/chat/message/options.cljs b/src/status_im/ui/screens/chat/message/options.cljs index 98bd1eea09..535828b7d9 100644 --- a/src/status_im/ui/screens/chat/message/options.cljs +++ b/src/status_im/ui/screens/chat/message/options.cljs @@ -19,7 +19,7 @@ (defn view [] (let [{:keys [chat-id message-id]} @(re-frame/subscribe [:get-current-chat-ui-prop :message-options]) - close-message-options-fn #(re-frame/dispatch [:set-chat-ui-props {:show-message-options? false}])] + close-message-options-fn #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:show-message-options? false}])] [bottom-info/overlay {:on-click-outside close-message-options-fn} [bottom-info/container (* styles/item-height 2) [react/view @@ -29,10 +29,10 @@ :icon :icons/refresh :on-press #(do (close-message-options-fn) - (re-frame/dispatch [:resend-message chat-id message-id]))}] + (re-frame/dispatch [:chat.ui/resend-message chat-id message-id]))}] [action-item {:label :delete-message :icon :icons/delete :style {:color colors/red} :on-press #(do (close-message-options-fn) - (re-frame/dispatch [:delete-message chat-id message-id]))}]]]])) + (re-frame/dispatch [:chat.ui/delete-message chat-id message-id]))}]]]])) diff --git a/src/status_im/ui/screens/chat/styles/input/input.cljs b/src/status_im/ui/screens/chat/styles/input/input.cljs index b367524735..201577dca2 100644 --- a/src/status_im/ui/screens/chat/styles/input/input.cljs +++ b/src/status_im/ui/screens/chat/styles/input/input.cljs @@ -15,6 +15,51 @@ :border-top-color colors/gray-light :elevation 2}) +(def reply-message + {:flex-direction :row + :align-items :flex-start + :border-width 1 + :border-radius 10 + :border-color colors/gray-light + :padding-top 10 + :padding-bottom 10 + :padding-right 14 + :padding-left 7 + :margin-top 12 + :margin-left 12 + :margin-right 12}) + +(def reply-message-content + {:flex-direction :column + :padding-left 7 + :margin-right 30 + :max-height 140 + :overflow :scroll}) + +(def reply-message-author + {:font-size 12 + :color colors/gray + :padding-bottom 6}) + +(def reply-message-container + {:flex-direction :column-reverse}) + +(def cancel-reply-highlight + {:position :absolute + :z-index 5 + :top 0 + :right 0 + :height 26}) + +(def cancel-reply-container + {:flex-direction :row + :justify-content :flex-end + :margin-right 12}) + +(def cancel-reply-icon + {:background-color colors/gray + :border-radius 12}) + (def input-container {:flex-direction :row :align-items :flex-end diff --git a/src/status_im/ui/screens/chat/styles/message/message.cljs b/src/status_im/ui/screens/chat/styles/message/message.cljs index b317ef3335..a5c4f3824c 100644 --- a/src/status_im/ui/screens/chat/styles/message/message.cljs +++ b/src/status_im/ui/screens/chat/styles/message/message.cljs @@ -200,3 +200,31 @@ {:font-size 12 :padding-top 6 :color colors/gray}) + +(defn quoted-message-container [outgoing] + {:margin-bottom 6 + :padding-bottom 6 + :border-bottom-color (if outgoing + colors/blue-light + colors/gray-lighter) + :border-bottom-width 2 + :border-bottom-left-radius 2 + :border-bottom-right-radius 2}) + +(def quoted-message-author-container + {:flex-direction :row + :align-items :center + :justify-content :flex-start}) + +(defn quoted-message-author [outgoing] + {:font-size 12 + :padding-bottom 5 + :padding-top 4 + :color (if outgoing + colors/wild-blue-yonder + colors/gray)}) + +(defn quoted-message-text [outgoing] + {:color (if outgoing + colors/wild-blue-yonder + colors/gray)}) diff --git a/src/status_im/ui/screens/chat/views.cljs b/src/status_im/ui/screens/chat/views.cljs index eb7eb5127c..3176dbf2cb 100644 --- a/src/status_im/ui/screens/chat/views.cljs +++ b/src/status_im/ui/screens/chat/views.cljs @@ -95,7 +95,7 @@ :preview [react/view style/message-view-preview]} [react/touchable-without-feedback {:on-press (fn [_] - (re-frame/dispatch [:set-chat-ui-props {:messages-focused? true}]) + (re-frame/dispatch [:chat.ui/set-chat-ui-props {:messages-focused? true}]) (react/dismiss-keyboard!))} [react/animated-view {:style (style/message-view-animated opacity)} message-view]]])) @@ -118,8 +118,8 @@ (letsubs [messages [:get-current-chat-messages-stream] chat [:get-current-chat] current-public-key [:get-current-public-key]] - {:component-did-mount #(re-frame/dispatch [:set-chat-ui-props {:messages-focused? true - :input-focused? false}])} + {:component-did-mount #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:messages-focused? true + :input-focused? false}])} (if (empty? messages) [empty-chat-container chat] [list/flat-list {:data messages @@ -130,7 +130,7 @@ :current-public-key current-public-key :row message}]) :inverted true - :onEndReached #(re-frame/dispatch [:load-more-messages]) + :onEndReached #(re-frame/dispatch [:chat.ui/load-more-messages]) :enableEmptySections true :keyboardShouldPersistTaps :handled}]))) diff --git a/src/status_im/ui/screens/contacts/events.cljs b/src/status_im/ui/screens/contacts/events.cljs index 5dd6b4ebda..6329f8e62d 100644 --- a/src/status_im/ui/screens/contacts/events.cljs +++ b/src/status_im/ui/screens/contacts/events.cljs @@ -29,7 +29,7 @@ (handlers/register-handler-fx :add-contact - [(re-frame/inject-cofx :random-id)] + [(re-frame/inject-cofx :random-id-generator)] (fn [cofx [_ whisper-id]] (models.contact/add-contact cofx whisper-id))) @@ -41,7 +41,7 @@ (handlers/register-handler-fx :set-contact-identity-from-qr - [(re-frame/inject-cofx :random-id)] + [(re-frame/inject-cofx :random-id-generator)] (fn [{:keys [db] :as cofx} [_ _ contact-identity]] (let [current-account (:account/account db) fx {:db (assoc db :contacts/new-identity contact-identity)} @@ -63,13 +63,13 @@ (handlers/register-handler-fx :open-chat-with-contact - [(re-frame/inject-cofx :random-id)] + [(re-frame/inject-cofx :random-id-generator)] (fn [cofx [_ {:keys [whisper-identity]}]] (add-contact-and-open-chat cofx whisper-identity))) (handlers/register-handler-fx :add-contact-handler - [(re-frame/inject-cofx :random-id)] + [(re-frame/inject-cofx :random-id-generator)] (fn [{{:contacts/keys [new-identity]} :db :as cofx} _] (when (seq new-identity) (add-contact-and-open-chat cofx new-identity)))) diff --git a/src/status_im/ui/screens/desktop/main/add_new/views.cljs b/src/status_im/ui/screens/desktop/main/add_new/views.cljs index 9fa633379d..4037fccbf8 100644 --- a/src/status_im/ui/screens/desktop/main/add_new/views.cljs +++ b/src/status_im/ui/screens/desktop/main/add_new/views.cljs @@ -119,7 +119,7 @@ :on-press #(when-not topic-error (do (re-frame/dispatch [:set :public-group-topic nil]) - (re-frame/dispatch [:create-new-public-chat topic])))} + (re-frame/dispatch [:chat.ui/start-public-chat topic])))} [react/view {:style (styles/add-contact-button disable?)} [react/text {:style (styles/add-contact-button-text disable?)} (i18n/label :new-public-group-chat)]]]]) @@ -131,7 +131,7 @@ ^{:key topic} [react/touchable-highlight {:on-press #(do (re-frame/dispatch [:set :public-group-topic nil]) - (re-frame/dispatch [:create-new-public-chat topic]))} + (re-frame/dispatch [:chat.ui/start-public-chat topic]))} [react/view {:style styles/suggested-contact-view} [react/view {:style styles/suggested-topic-image} [react/text {:style styles/suggested-topic-text} (string/capitalize (first topic))]] diff --git a/src/status_im/ui/screens/desktop/main/chat/views.cljs b/src/status_im/ui/screens/desktop/main/chat/views.cljs index f4bd68f274..5aabf741cb 100644 --- a/src/status_im/ui/screens/desktop/main/chat/views.cljs +++ b/src/status_im/ui/screens/desktop/main/chat/views.cljs @@ -63,7 +63,7 @@ :on-press #(re-frame/dispatch [:chat.ui/clear-history-pressed])} (i18n/label :t/clear-history)] [react/text {:style (styles/profile-actions-text colors/black) - :on-press #(re-frame/dispatch [:chat.ui/delete-chat-pressed chat-id])} + :on-press #(re-frame/dispatch [:chat.ui/remove-chat-pressed chat-id])} (i18n/label :t/delete-chat)]]])) (views/defview message-author-name [{:keys [outgoing from] :as message}] @@ -177,14 +177,14 @@ y (.-y (.-contentOffset ne))] (when (<= y 0) (when @scroll-timer (js/clearTimeout @scroll-timer)) - (reset! scroll-timer (js/setTimeout #(re-frame/dispatch [:load-more-messages]) 300))) + (reset! scroll-timer (js/setTimeout #(re-frame/dispatch [:chat.ui/load-more-messages]) 300))) (reset! scroll-height (+ y (.-height (.-layoutMeasurement ne)))))) :ref #(reset! scroll-ref %)} [react/view (doall (for [[index {:keys [from content message-id type value] :as message-obj}] (map-indexed vector messages)] ^{:key message-obj} - [message content (= from current-public-key) + [message (:text content) (= from current-public-key) (assoc message-obj :group-chat group-chat :current-public-key current-public-key)]))]] [connectivity/error-view]]))) @@ -216,17 +216,17 @@ (when should-send (.clear @inp-ref) (.focus @inp-ref) - (re-frame/dispatch [:send-current-message])))) + (re-frame/dispatch [:chat.ui/send-current-message])))) :on-change (fn [e] (let [native-event (.-nativeEvent e) text (.-text native-event)] (reagent/set-state component {:empty? (= "" text)}) - (re-frame/dispatch [:set-chat-input-text text])))}] + (re-frame/dispatch [:chat.ui/set-chat-input-text text])))}] [react/touchable-highlight {:style styles/send-button :on-press (fn [] (.clear @inp-ref) (.focus @inp-ref) - (re-frame/dispatch [:send-current-message]))} + (re-frame/dispatch [:chat.ui/send-current-message]))} [react/view {:style (styles/send-icon empty?)} [icons/icon :icons/arrow-left {:style (styles/send-icon-arrow empty?)}]]]]))) diff --git a/src/status_im/ui/screens/desktop/main/tabs/home/views.cljs b/src/status_im/ui/screens/desktop/main/tabs/home/views.cljs index 153ecd890e..50f7b7b516 100644 --- a/src/status_im/ui/screens/desktop/main/tabs/home/views.cljs +++ b/src/status_im/ui/screens/desktop/main/tabs/home/views.cljs @@ -57,12 +57,12 @@ :style styles/chat-last-message} (if (= constants/content-type-command (:content-type last-message)) [chat-item/command-short-preview last-message] - (or (:content last-message) (i18n/label :no-messages-yet)))]] + (or (get-in last-message [:content :text]) (i18n/label :no-messages-yet)))]] [react/view {:style styles/timestamp} [chat-item/message-timestamp (:timestamp last-message)]]]))) (defn chat-list-item [[chat-id chat]] - [react/touchable-highlight {:on-press #(re-frame/dispatch [:navigate-to-chat chat-id])} + [react/touchable-highlight {:on-press #(re-frame/dispatch [:chat.ui/navigate-to-chat chat-id])} [chat-list-item-inner-view (assoc chat :chat-id chat-id)]]) (views/defview chat-list-view [] diff --git a/src/status_im/ui/screens/events.cljs b/src/status_im/ui/screens/events.cljs index 0cc6dc8bc3..2fd63b6f8f 100644 --- a/src/status_im/ui/screens/events.cljs +++ b/src/status_im/ui/screens/events.cljs @@ -1,6 +1,5 @@ (ns status-im.ui.screens.events (:require status-im.events - status-im.chat.events status-im.dev-server.events status-im.ui.screens.add-new.events status-im.ui.screens.add-new.new-chat.events diff --git a/src/status_im/ui/screens/group/chat_settings/events.cljs b/src/status_im/ui/screens/group/chat_settings/events.cljs index ffb277b5c2..934efd683e 100644 --- a/src/status_im/ui/screens/group/chat_settings/events.cljs +++ b/src/status_im/ui/screens/group/chat_settings/events.cljs @@ -20,9 +20,11 @@ (handlers/register-handler-fx :add-new-group-chat-participants - [(re-frame/inject-cofx :random-id)] - (fn [{{:keys [current-chat-id selected-participants] :as db} :db now :now message-id :random-id :as cofx} _] - (let [participants (concat (get-in db [:chats current-chat-id :contacts]) selected-participants) + [(re-frame/inject-cofx :random-id-generator)] + (fn [{{:keys [current-chat-id selected-participants] :as db} :db + now :now random-id-generator :random-id-generator :as cofx} _] + (let [message-id (random-id-generator) + participants (concat (get-in db [:chats current-chat-id :contacts]) selected-participants) contacts (:contacts/contacts db) added-participants-names (map #(get-in contacts [% :name]) selected-participants)] (fx/merge cofx @@ -37,9 +39,11 @@ (handlers/register-handler-fx :remove-group-chat-participants - [(re-frame/inject-cofx :random-id)] - (fn [{{:keys [current-chat-id] :as db} :db now :now message-id :random-id :as cofx} [_ removed-participants]] - (let [participants (remove removed-participants (get-in db [:chats current-chat-id :contacts])) + [(re-frame/inject-cofx :random-id-generator)] + (fn [{{:keys [current-chat-id] :as db} :db now :now random-id-generator :random-id-generator :as cofx} + [_ removed-participants]] + (let [message-id (random-id-generator) + participants (remove removed-participants (get-in db [:chats current-chat-id :contacts])) contacts (:contacts/contacts db) removed-participants-names (map #(get-in contacts [% :name]) removed-participants)] (fx/merge cofx diff --git a/src/status_im/ui/screens/group/views.cljs b/src/status_im/ui/screens/group/views.cljs index 3fffa166bb..0125acd028 100644 --- a/src/status_im/ui/screens/group/views.cljs +++ b/src/status_im/ui/screens/group/views.cljs @@ -33,7 +33,7 @@ toolbar/default-nav-back [toolbar/content-title (i18n/label :t/group-chat)] (when save-btn-enabled? - (let [handler #(re-frame/dispatch [:create-new-group-chat-and-open group-name])] + (let [handler #(re-frame/dispatch [:chat.ui/start-group-chat group-name])] (if platform/android? [toolbar/actions [{:icon :icons/ok :icon-opts {:color :blue diff --git a/src/status_im/ui/screens/home/views.cljs b/src/status_im/ui/screens/home/views.cljs index dafab421e6..042b431d25 100644 --- a/src/status_im/ui/screens/home/views.cljs +++ b/src/status_im/ui/screens/home/views.cljs @@ -40,7 +40,7 @@ (views/defview home-list-item [[home-item-id home-item]] (views/letsubs [swiped? [:delete-swipe-position home-item-id]] (let [delete-action (if (:chat-id home-item) - :remove-chat-and-navigate-home + :chat.ui/remove-chat :browser.ui/remove-browser-pressed) inner-item-view (if (:chat-id home-item) inner-item/home-list-chat-item-inner-view diff --git a/src/status_im/ui/screens/home/views/inner_item.cljs b/src/status_im/ui/screens/home/views/inner_item.cljs index e42cb35ae6..a29710359a 100644 --- a/src/status_im/ui/screens/home/views/inner_item.cljs +++ b/src/status_im/ui/screens/home/views/inner_item.cljs @@ -38,11 +38,11 @@ [react/text {:style styles/last-message-text} ""] - (:content content) + (:text content) [react/text {:style styles/last-message-text :number-of-lines 1 :accessibility-label :chat-message-text} - (:content content)] + (:text content)] (contains? #{constants/content-type-command constants/content-type-command-request} @@ -91,7 +91,7 @@ (letsubs [last-message [:get-last-message chat-id] chat-name [:get-chat-name chat-id]] (let [truncated-chat-name (utils/truncate-str chat-name 30)] - [react/touchable-highlight {:on-press #(re-frame/dispatch [:navigate-to-chat chat-id])} + [react/touchable-highlight {:on-press #(re-frame/dispatch [:chat.ui/navigate-to-chat chat-id])} [react/view styles/chat-container [react/view styles/chat-icon-container [chat-icon.screen/chat-icon-view-chat-list chat-id group-chat truncated-chat-name color online false]] diff --git a/src/status_im/ui/screens/profile/group_chat/views.cljs b/src/status_im/ui/screens/profile/group_chat/views.cljs index c706f9f54d..2577342486 100644 --- a/src/status_im/ui/screens/profile/group_chat/views.cljs +++ b/src/status_im/ui/screens/profile/group_chat/views.cljs @@ -51,7 +51,7 @@ :accessibility-label :delete-chat-button}])) (defn contact-actions [contact] - [{:action #(re-frame/dispatch [:show-profile (:whisper-identity contact)]) + [{:action #(re-frame/dispatch [:chat.ui/show-profile (:whisper-identity contact)]) :label (i18n/label :t/view-profile)} #_{:action #(re-frame/dispatch [:remove-group-chat-participants #{(:whisper-identity contact)}]) :label (i18n/label :t/remove-from-chat)}]) @@ -66,7 +66,7 @@ :accessibility-label :member-item :inner-props {:accessibility-label :member-name-text} :on-press (when (not= whisper-identity current-user-identity) - #(re-frame/dispatch [:show-profile whisper-identity]))}]]) + #(re-frame/dispatch [:chat.ui/show-profile whisper-identity]))}]]) (defview chat-group-contacts-view [admin? group-admin-identity current-user-identity] (letsubs [contacts [:get-current-chat-contacts]] diff --git a/src/status_im/ui/screens/wallet/send/events.cljs b/src/status_im/ui/screens/wallet/send/events.cljs index 0858de4887..83e3263257 100644 --- a/src/status_im/ui/screens/wallet/send/events.cljs +++ b/src/status_im/ui/screens/wallet/send/events.cljs @@ -154,7 +154,7 @@ (handlers/register-handler-fx :send-transaction-message - (concat models.message/send-interceptors + (concat [(re-frame/inject-cofx :random-id-generator)] navigation/navigation-interceptors) (fn [{:keys [db] :as cofx} [_ chat-id params]] ;;NOTE(goranjovic): we want to send the payment message only when we have a whisper id diff --git a/src/status_im/utils/random.cljs b/src/status_im/utils/random.cljs index d63dae6de8..6a4bbb7a47 100644 --- a/src/status_im/utils/random.cljs +++ b/src/status_im/utils/random.cljs @@ -28,11 +28,6 @@ (assoc coeffects :random-guid-generator guid))) (re-frame/reg-cofx - :random-id + :random-id-generator (fn [coeffects _] - (assoc coeffects :random-id (id)))) - -(re-frame/reg-cofx - :random-id-seq - (fn [coeffects _] - (assoc coeffects :random-id-seq (repeatedly id)))) + (assoc coeffects :random-id-generator id))) diff --git a/src/status_im/utils/universal_links/core.cljs b/src/status_im/utils/universal_links/core.cljs index f940e90bf4..26c444ec28 100644 --- a/src/status_im/utils/universal_links/core.cljs +++ b/src/status_im/utils/universal_links/core.cljs @@ -2,7 +2,7 @@ (:require [cljs.spec.alpha :as spec] [re-frame.core :as re-frame] [status-im.accounts.db :as accounts.db] - [status-im.chat.events :as chat.events] + [status-im.chat.models :as chat] [status-im.ui.components.list-selection :as list-selection] [status-im.ui.components.react :as react] [status-im.ui.screens.add-new.new-chat.db :as new-chat.db] @@ -42,13 +42,13 @@ (fx/defn handle-public-chat [cofx public-chat] (log/info "universal-links: handling public chat " public-chat) - (chat.events/create-new-public-chat cofx public-chat false)) + (chat/start-public-chat cofx public-chat false)) (fx/defn handle-view-profile [{:keys [db] :as cofx} profile-id] (log/info "universal-links: handling view profile" profile-id) (if (new-chat.db/own-whisper-identity? db profile-id) (navigation/navigate-to-cofx cofx :my-profile nil) - (chat.events/show-profile cofx profile-id))) + (navigation/navigate-to-cofx (assoc-in cofx [:db :contacts/identity] profile-id) :profile nil))) (fx/defn handle-extension [cofx url] (log/info "universal-links: handling url profile" url) diff --git a/test/cljs/status_im/test/chat/commands/core.cljs b/test/cljs/status_im/test/chat/commands/core.cljs index 63f12f53de..61ec697990 100644 --- a/test/cljs/status_im/test/chat/commands/core.cljs +++ b/test/cljs/status_im/test/chat/commands/core.cljs @@ -71,12 +71,12 @@ (get-in fx [:db :id->command (core/command-id TestCommandInstance) :type])))) (testing "Suggestions for parameters are injected with correct selection events" - (is (= [:set-command-parameter false 0 "first-value"] + (is (= [:chat.ui/set-command-parameter false 0 "first-value"] ((get-in fx [:db :id->command (core/command-id TestCommandInstance) :params 0 :suggestions]) "first-value"))) - (is (= [:set-command-parameter true 2 "last-value"] + (is (= [:chat.ui/set-command-parameter true 2 "last-value"] ((get-in fx [:db :id->command (core/command-id TestCommandInstance) :params 2 :suggestions]) diff --git a/test/cljs/status_im/test/chat/commands/input.cljs b/test/cljs/status_im/test/chat/commands/input.cljs index 6b4a9e4586..741a13357c 100644 --- a/test/cljs/status_im/test/chat/commands/input.cljs +++ b/test/cljs/status_im/test/chat/commands/input.cljs @@ -4,6 +4,25 @@ [status-im.chat.commands.core :as core] [status-im.chat.commands.input :as input])) +(deftest starts-as-command?-test + (is (not (input/starts-as-command? nil))) + (is (not (input/command-ends-with-space? ""))) + (is (not (input/command-ends-with-space? "word1 word2 word3"))) + (is (input/command-ends-with-space? "word1 word2 "))) + +(deftest split-command-args-test + (is (nil? (input/split-command-args nil))) + (is (= [""] (input/split-command-args ""))) + (is (= ["@browse" "google.com"] (input/split-command-args "@browse google.com"))) + (is (= ["@browse" "google.com"] (input/split-command-args " @browse google.com "))) + (is (= ["/send" "1.0" "John Doe"] (input/split-command-args "/send 1.0 \"John Doe\""))) + (is (= ["/send" "1.0" "John Doe"] (input/split-command-args "/send 1.0 \"John Doe\" ")))) + +(deftest join-command-args-test + (is (nil? (input/join-command-args nil))) + (is (= "" (input/join-command-args [""]))) + (is (= "/send 1.0 \"John Doe\"" (input/join-command-args ["/send" "1.0" "John Doe"])))) + (deftest selected-chat-command-test (let [fx (core/load-commands {:db {}} #{test-core/TestCommandInstance test-core/AnotherTestCommandInstance}) commands (core/chat-commands (get-in fx [:db :id->command]) diff --git a/test/cljs/status_im/test/chat/events.cljs b/test/cljs/status_im/test/chat/events.cljs deleted file mode 100644 index 8bbb001111..0000000000 --- a/test/cljs/status_im/test/chat/events.cljs +++ /dev/null @@ -1,10 +0,0 @@ -(ns status-im.test.chat.events - (:require [cljs.test :refer [deftest is testing]] - [status-im.chat.events :as chat-events])) - -(deftest show-profile-test - (testing "Dafault behaviour: navigate to profile" - (let [{:keys [db]} (chat-events/show-profile - {:db {:navigation-stack '(:home)}} - "a")] - (is (= "a" (:contacts/identity db)))))) diff --git a/test/cljs/status_im/test/chat/models/input.cljs b/test/cljs/status_im/test/chat/models/input.cljs index 950e2945a8..480701afbb 100644 --- a/test/cljs/status_im/test/chat/models/input.cljs +++ b/test/cljs/status_im/test/chat/models/input.cljs @@ -11,25 +11,6 @@ (is (= "test" (input/text->emoji "test"))) (is (= "word1 \uD83D\uDC4D word2" (input/text->emoji "word1 :+1: word2")))) -(deftest starts-as-command? - (is (not (input/starts-as-command? nil))) - (is (not (input/text-ends-with-space? ""))) - (is (not (input/text-ends-with-space? "word1 word2 word3"))) - (is (input/text-ends-with-space? "word1 word2 "))) - -(deftest split-command-args - (is (nil? (input/split-command-args nil))) - (is (= [""] (input/split-command-args ""))) - (is (= ["@browse" "google.com"] (input/split-command-args "@browse google.com"))) - (is (= ["@browse" "google.com"] (input/split-command-args " @browse google.com "))) - (is (= ["/send" "1.0" "John Doe"] (input/split-command-args "/send 1.0 \"John Doe\""))) - (is (= ["/send" "1.0" "John Doe"] (input/split-command-args "/send 1.0 \"John Doe\" ")))) - -(deftest join-command-args - (is (nil? (input/join-command-args nil))) - (is (= "" (input/join-command-args [""]))) - (is (= "/send 1.0 \"John Doe\"" (input/join-command-args ["/send" "1.0" "John Doe"])))) - (deftest process-cooldown-fx (let [db {:current-chat-id "chat" :chats {"chat" {:public? true}} @@ -61,7 +42,7 @@ :chat/spam-messages-frequency 0 :chat/cooldown-enabled? true) :show-cooldown-warning nil - :dispatch-later [{:dispatch [:disable-cooldown] + :dispatch-later [{:dispatch [:chat/disable-cooldown] :ms (constants/cooldown-periods-ms 1)}]} actual (input/process-cooldown {:db db})] (is (= expected actual)))) @@ -77,7 +58,7 @@ :chat/spam-messages-frequency 0 :chat/cooldown-enabled? true) :show-cooldown-warning nil - :dispatch-later [{:dispatch [:disable-cooldown] + :dispatch-later [{:dispatch [:chat/disable-cooldown] :ms (constants/cooldown-periods-ms 2)}]} actual (input/process-cooldown {:db db})] (is (= expected actual)))) @@ -93,7 +74,7 @@ :chat/spam-messages-frequency 0 :chat/cooldown-enabled? true) :show-cooldown-warning nil - :dispatch-later [{:dispatch [:disable-cooldown] + :dispatch-later [{:dispatch [:chat/disable-cooldown] :ms (constants/cooldown-periods-ms 3)}]} actual (input/process-cooldown {:db db})] (is (= expected actual))))))) diff --git a/test/cljs/status_im/test/mailserver/core.cljs b/test/cljs/status_im/test/mailserver/core.cljs index c91bddfc44..652183721f 100644 --- a/test/cljs/status_im/test/mailserver/core.cljs +++ b/test/cljs/status_im/test/mailserver/core.cljs @@ -182,7 +182,7 @@ (deftest delete-mailserver (testing "the user is not connected to the mailserver" - (let [cofx {:random-id "random-id" + (let [cofx {:random-id-generator (constantly "random-id") :db {:inbox/wnodes {:eth.beta {"a" {:id "a" :name "old-name" :user-defined true @@ -193,7 +193,7 @@ (testing "it stores it in the db" (is (= 1 (count (:data-store/tx actual))))))) (testing "the mailserver is not user-defined" - (let [cofx {:random-id "random-id" + (let [cofx {:random-id-generator (constantly "random-id") :db {:inbox/wnodes {:eth.beta {"a" {:id "a" :name "old-name" :address "enode://old-id:old-password@url:port"}}}}} @@ -201,7 +201,7 @@ (testing "it does not delete the mailserver" (is (= {:dispatch [:navigate-back]} actual))))) (testing "the user is connected to the mailserver" - (let [cofx {:random-id "random-id" + (let [cofx {:random-id-generator (constantly "random-id") :db {:inbox/wnodes {:eth.beta {"a" {:id "a" :name "old-name" :address "enode://old-id:old-password@url:port"}}}}} @@ -211,7 +211,7 @@ (deftest upsert-mailserver (testing "new mailserver" - (let [cofx {:random-id "random-id" + (let [cofx {:random-id-generator (constantly "random-id") :db {:mailservers/manage {:name {:value "test-name"} :url {:value "enode://test-id:test-password@url:port"}} @@ -231,7 +231,7 @@ (testing "it stores it in the db" (is (= 1 (count (:data-store/tx actual))))))) (testing "existing mailserver" - (let [cofx {:random-id "random-id" + (let [cofx {:random-id-generator (constantly "random-id") :db {:mailservers/manage {:id {:value :a} :name {:value "new-name"} :url {:value "enode://new-id:new-password@url:port"}} diff --git a/test/cljs/status_im/test/models/bootnode.cljs b/test/cljs/status_im/test/models/bootnode.cljs index e42077d996..f9013118af 100644 --- a/test/cljs/status_im/test/models/bootnode.cljs +++ b/test/cljs/status_im/test/models/bootnode.cljs @@ -16,7 +16,7 @@ :chain "mainnet_rpc" :id "someid"}}} actual (model/upsert - {:random-id "some-id" + {:random-id-generator (constantly "some-id") :db {:bootnodes/manage new-bootnode :network "mainnet_rpc" :account/account {}}})] @@ -30,7 +30,7 @@ :chain "mainnet_rpc" :id "a"}}} actual (model/upsert - {:random-id "some-id" + {:random-id-generator (constantly "some-id") :db {:bootnodes/manage new-bootnode :network "mainnet_rpc" :account/account {:bootnodes diff --git a/test/cljs/status_im/test/models/network.cljs b/test/cljs/status_im/test/models/network.cljs index b5852ad5ac..19f0aca021 100644 --- a/test/cljs/status_im/test/models/network.cljs +++ b/test/cljs/status_im/test/models/network.cljs @@ -96,8 +96,9 @@ (deftest save (testing "it does not save a network with an invalid url" - (is (nil? (model/save {:random-id "random" + (is (nil? (model/save {:random-id-generator (constantly "random") :db {:networks/manage {:url {:value "wrong"} :chain {:value "1"} :name {:value "empty"}} - :account/account {}}}))))) + :account/account {}}} + {}))))) diff --git a/test/cljs/status_im/test/runner.cljs b/test/cljs/status_im/test/runner.cljs index 09dbf81773..3626500bbe 100644 --- a/test/cljs/status_im/test/runner.cljs +++ b/test/cljs/status_im/test/runner.cljs @@ -1,6 +1,5 @@ (ns status-im.test.runner (:require [doo.runner :refer-macros [doo-tests]] - [status-im.test.chat.events] [status-im.test.contacts.events] [status-im.test.contacts.subs] [status-im.test.data-store.realm.core] @@ -68,7 +67,6 @@ (doo-tests 'status-im.test.utils.async - 'status-im.test.chat.events 'status-im.test.chat.subs 'status-im.test.chat.models 'status-im.test.contacts.events diff --git a/test/cljs/status_im/test/transport/handlers.cljs b/test/cljs/status_im/test/transport/handlers.cljs index 06f6e7b445..634383add8 100644 --- a/test/cljs/status_im/test/transport/handlers.cljs +++ b/test/cljs/status_im/test/transport/handlers.cljs @@ -15,10 +15,10 @@ (deftest receive-whisper-messages-test (testing "an error is reported" - (is (nil? (handlers/receive-whisper-messages {:db {}} [nil "error" #js [] nil])))) + (is (nil? (handlers/receive-whisper-messages {:db {}} "error" #js [] nil)))) (testing "messages is undefined" - (is (nil? (handlers/receive-whisper-messages {:db {}} [nil nil js/undefined nil])))) + (is (nil? (handlers/receive-whisper-messages {:db {}} nil js/undefined nil)))) (testing "happy path" - (let [actual (handlers/receive-whisper-messages {:db {}} [nil nil messages sig])] + (let [actual (handlers/receive-whisper-messages {:db {}} nil messages sig)] (testing "it add an fx for the message" (is (:chat-received-message/add-fx actual)))))) diff --git a/translations/af.json b/translations/af.json index a4b3ec56e8..ccc3b17998 100644 --- a/translations/af.json +++ b/translations/af.json @@ -25,7 +25,6 @@ "address-explication": "Miskien moet hier 'n bietjie teks wees wat verduidelik wat 'n adres is en waar om daarvoor te soek.", "remove": "Verwyder", "add-members": "Voeg lede by", - "sharing-cancel": "Kanselleer", "popular-tags": "Gewilde oortjies", "twelve-words-in-correct-order": "12 woorde in korrekte volgorde", "phone-number": "Telefoonnommer", diff --git a/translations/bel.json b/translations/bel.json index 10625ebac1..3b00c63209 100644 --- a/translations/bel.json +++ b/translations/bel.json @@ -23,7 +23,6 @@ "address-explication": "Можа быць, тут павінен быць нейкі тэкст, які тлумачыць адрас і тое, дзе яго шукаць", "remove": "Выдаліць", "add-members": "Дадаць членаў", - "sharing-cancel": "Адмена", "popular-tags": "Папулярныя тэгі", "phone-number": "Нумар тэлефона", "removed-from-chat": "выдаліў(ла) вас з групавога чата", diff --git a/translations/cs.json b/translations/cs.json index 49619d2913..1d8bc82065 100644 --- a/translations/cs.json +++ b/translations/cs.json @@ -28,7 +28,6 @@ "remove": "Odstranit", "transactions-unsigned-empty": "Nemáš žádné nepodepsané transakce", "add-members": "Přidat členy", - "sharing-cancel": "Storno", "popular-tags": "Populární tagy", "twelve-words-in-correct-order": "12 anglických slov ve správném pořadí", "phone-number": "Telefonní číslo", diff --git a/translations/da.json b/translations/da.json index e6904cce48..d2e756cb27 100644 --- a/translations/da.json +++ b/translations/da.json @@ -27,7 +27,6 @@ "address-explication": "Maybe here should be some text explaining what an address is and where to look for it", "remove": "Fjern", "add-members": "Tilføj medlemmere", - "sharing-cancel": "Annullér", "popular-tags": "Populære tags", "twelve-words-in-correct-order": "12 ord i den korrekte rækkefølge", "phone-number": "Telefonnummer", diff --git a/translations/de.json b/translations/de.json index 8d8effab63..70462deb6b 100644 --- a/translations/de.json +++ b/translations/de.json @@ -40,7 +40,6 @@ "transaction-moved-text": "Die Transaktion wird für die nächsten 5 Minuten in der 'Unsigniert' Liste bleiben", "add-members": "Mitglieder hinzufügen", "sign-later-title": "Transaktion später signieren?", - "sharing-cancel": "Abbrechen", "yes": "Ja", "dapps": "ÐApps", "popular-tags": "Beliebte #hashtags", diff --git a/translations/el.json b/translations/el.json index c7879a086f..3fb74935cd 100644 --- a/translations/el.json +++ b/translations/el.json @@ -43,7 +43,6 @@ "transaction-moved-text": "Η συναλλαγή θα παραμείνει στη λίστα των 'Μη επικυρωμένων' για τα επόμενα 5 λεπτά", "add-members": "Προσθήκη μελών", "sign-later-title": "Επικυρώστε τη συναλλαγή αργότερα;", - "sharing-cancel": "Ακύρωση", "yes": "Ναι", "dapps": "ÐApps", "popular-tags": "Δημοφιλή #hashtags", diff --git a/translations/en.json b/translations/en.json index 1267517b93..b3d3b2c96e 100644 --- a/translations/en.json +++ b/translations/en.json @@ -89,7 +89,8 @@ "leave-public-chat": "Leave public chat", "sign-later-title": "Sign transaction later?", "manage-permissions": "Manage permissions", - "sharing-cancel": "Cancel", + "message-reply" : "Reply", + "message-options-cancel": "Cancel", "currency-display-name-pln": "Poland Zloty", "about-app": "About", "yes": "Yes", diff --git a/translations/es.json b/translations/es.json index aa1c960a3d..1680ed346d 100644 --- a/translations/es.json +++ b/translations/es.json @@ -40,7 +40,6 @@ "transaction-moved-text": "La transacción permanecerá en la lista 'Sin firmar' durante los siguientes 5 minutos", "add-members": "Añadir miembros", "sign-later-title": "¿Firmar transacción mas tarde?", - "sharing-cancel": "Cancelar", "yes": "Sí", "dapps": "ÐApps", "popular-tags": "#hashtags populares", diff --git a/translations/es_419.json b/translations/es_419.json index dec5e7d821..1a92e14ec8 100644 --- a/translations/es_419.json +++ b/translations/es_419.json @@ -508,7 +508,6 @@ "shake-your-phone": "¿Encontraste un bug o tienes una sugerencia? ¡~Agíta tu teléfono~!", "share": "Compartir...", "share-contact-code": "Compartir mi código de contacto", - "sharing-cancel": "Cancelar", "sharing-copied-to-clipboard": "Copiado al portapapeles", "sharing-copy-to-clipboard": "Copiar", "sharing-share": "Compartir...", diff --git a/translations/es_ar.json b/translations/es_ar.json index fc280faea7..b76d7c90ea 100644 --- a/translations/es_ar.json +++ b/translations/es_ar.json @@ -25,7 +25,6 @@ "address-explication": "Tal vez, aquí debería haber alguna indicación explicando qué es una dirección y dónde buscarla", "remove": "Eliminar", "add-members": "Agregar miembros", - "sharing-cancel": "Cancelar", "popular-tags": "Etiquetas populares", "twelve-words-in-correct-order": "12 palabras en el orden correcto", "phone-number": "Número telefónico", diff --git a/translations/es_mx.json b/translations/es_mx.json index 335dd2b773..28e0ddefd8 100644 --- a/translations/es_mx.json +++ b/translations/es_mx.json @@ -27,7 +27,6 @@ "address-explication": "Tal vez aquí debería haber algún texto explicando qué es una dirección y dónde encontrarla", "remove": "Remover", "add-members": "Agregar miembros", - "sharing-cancel": "Cancelar", "popular-tags": "Etiquetas populares", "twelve-words-in-correct-order": "12 palabras en orden correcto", "phone-number": "Número de teléfono", diff --git a/translations/fi.json b/translations/fi.json index d3a78b3f62..ef0aa6aeab 100644 --- a/translations/fi.json +++ b/translations/fi.json @@ -27,7 +27,6 @@ "address-explication": "Ehkä tässä pitäisi olla jokin teksti, jossa selitetään, mikä osoite on ja mistä etsiä sitä", "remove": "Poista", "add-members": "Lisää käyttäjiä", - "sharing-cancel": "Peruuta", "popular-tags": "Suositut tunnisteet", "twelve-words-in-correct-order": "12 sanaa oikeassa järjestyksessä", "phone-number": "Puhelinnumero", diff --git a/translations/fr.json b/translations/fr.json index 383629dcdc..5e20e9b6d1 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -42,7 +42,6 @@ "transaction-moved-text": "La transaction va rester dans la liste 'Non-signées' pendant 5 mins", "add-members": "Ajouter des membres", "sign-later-title": "Voulez vous signer la transaction ultérieurement?", - "sharing-cancel": "Annuler", "yes": "Oui", "dapps": "ÐApps", "popular-tags": "Mots-clés populaires", diff --git a/translations/fr_ch.json b/translations/fr_ch.json index a34d670482..8fdd0680ff 100644 --- a/translations/fr_ch.json +++ b/translations/fr_ch.json @@ -25,7 +25,6 @@ "address-explication": "On pourrait mettre ici un texte qui explique ce qu'est une adresse et comment la rechercher", "remove": "Supprimer", "add-members": "Ajouter des membres", - "sharing-cancel": "Annuler", "popular-tags": "Clés populaires", "twelve-words-in-correct-order": "12 mots dans le bon ordre", "phone-number": "Numéro de téléphone", diff --git a/translations/fy.json b/translations/fy.json index 8a4a696282..294e7d02ed 100644 --- a/translations/fy.json +++ b/translations/fy.json @@ -27,7 +27,6 @@ "address-explication": "Maybe here should be some text explaining what an address is and where to look for it", "remove": "Fuortsmite", "add-members": "Foegje leden ta", - "sharing-cancel": "Ôfbrekke", "popular-tags": "Populêre tags", "twelve-words-in-correct-order": "12 wurden yn de goeie folchoarder", "phone-number": "Telefoannûmer", diff --git a/translations/he.json b/translations/he.json index 5228b525e3..f320415e1a 100644 --- a/translations/he.json +++ b/translations/he.json @@ -27,7 +27,6 @@ "address-explication": "Maybe here should be some text explaining what an address is and where to look for it", "remove": "הסר", "add-members": "הוסף מספרים", - "sharing-cancel": "בטל", "popular-tags": "תגים פופולאריים", "twelve-words-in-correct-order": "12 מילים בסדר הנכון", "phone-number": "מספר טלפון", diff --git a/translations/hi.json b/translations/hi.json index c9ae5549dd..800c73231a 100644 --- a/translations/hi.json +++ b/translations/hi.json @@ -25,7 +25,6 @@ "address-explication": "संभवतः यहाँ कुछ टेक्स्ट विवरण होना चाहिए कि पता क्या है और इसे कहाँ खोजा जाए", "remove": "निकालें", "add-members": "सदस्य जोड़ें", - "sharing-cancel": "रद्द करें", "popular-tags": "लोकप्रिय टैग", "twelve-words-in-correct-order": "सही क्रम में 12 शब्द", "phone-number": "फ़ोन नंबर", diff --git a/translations/hu.json b/translations/hu.json index db04f80751..8828efe41c 100644 --- a/translations/hu.json +++ b/translations/hu.json @@ -25,7 +25,6 @@ "address-explication": "Itt talán szükség lenne egy kis szövegre, ami elmagyarázná, mi is az a cím és hol lehet megtalálni", "remove": "Eltávolítás", "add-members": "Tagok hozzáadása", - "sharing-cancel": "Mégse", "popular-tags": "Népszerű címkék", "twelve-words-in-correct-order": "12 szó helyes sorrendben", "phone-number": "Telefonszám", diff --git a/translations/it.json b/translations/it.json index d636d62c86..3d5166bd80 100644 --- a/translations/it.json +++ b/translations/it.json @@ -40,7 +40,6 @@ "transaction-moved-text": "La transazione rimarrà nella lista delle transazioni 'Non Firmate' per i prossimi 5 minuti", "add-members": "Aggiungi membri", "sign-later-title": "Firmare la transazione dopo?", - "sharing-cancel": "Annulla", "yes": "Sì", "dapps": "ÐApps", "popular-tags": "#hashtags popolari", diff --git a/translations/it_ch.json b/translations/it_ch.json index e393095e00..e0b16862ac 100644 --- a/translations/it_ch.json +++ b/translations/it_ch.json @@ -25,7 +25,6 @@ "address-explication": "Forse qui dovremmo spiegare cos'è un indirizzo e dove cercarlo", "remove": "Rimuovi", "add-members": "Aggiungi membri", - "sharing-cancel": "Annulla", "popular-tags": "Tag popolari", "twelve-words-in-correct-order": "12 parole in ordine corretto", "phone-number": "Numero di telefono", diff --git a/translations/ja.json b/translations/ja.json index def6d9eca2..e7d30d1b6c 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -42,7 +42,6 @@ "transaction-moved-text": "取引は'未署名'リストに5分間を残ります", "add-members": "メンバーを追加", "sign-later-title": "後で取引に署名する?", - "sharing-cancel": "キャンセル", "yes": "はい", "dapps": "ÐApps", "popular-tags": "人気のタグ", diff --git a/translations/ko.json b/translations/ko.json index 2a48a919bd..ac0985a34d 100644 --- a/translations/ko.json +++ b/translations/ko.json @@ -521,7 +521,6 @@ "shake-your-phone": "버그나 건의사항이 있나요? 폰을 ~흔들어~ 보세요!", "share": "공유", "share-contact-code": "연락처 코드 공유하기", - "sharing-cancel": "취소", "sharing-copied-to-clipboard": "클립보드에 복사 됨", "sharing-copy-to-clipboard": "클립보드로 복사", "sharing-share": "공유...", diff --git a/translations/lt.json b/translations/lt.json index b2d952f30b..54a42ec3f7 100644 --- a/translations/lt.json +++ b/translations/lt.json @@ -40,7 +40,6 @@ "transaction-moved-text": "Sandėris sėkmingai perkeltas į “Nepasirašytos”", "add-members": "Pridėti narių", "sign-later-title": "Pasirašyti sandėrį vėliau?", - "sharing-cancel": "Atšaukti", "yes": "Taip", "dapps": "DApp'ai", "popular-tags": "Populiarūs hashtagai", diff --git a/translations/lv.json b/translations/lv.json index 16be928d41..57118e8da3 100644 --- a/translations/lv.json +++ b/translations/lv.json @@ -27,7 +27,6 @@ "address-explication": "Maybe here should be some text explaining what an address is and where to look for it", "remove": "Noņemt", "add-members": "Pievienot biedrus", - "sharing-cancel": "Atcelt", "popular-tags": "Popular tags", "twelve-words-in-correct-order": "12 vārdi", "phone-number": "Telefona numurs", diff --git a/translations/ms.json b/translations/ms.json index 0b82a82760..ce3cdce4bd 100644 --- a/translations/ms.json +++ b/translations/ms.json @@ -27,7 +27,6 @@ "address-explication": "Mungkin disini sepatutnya terdapat sedikit teks menjelaskan apa itu address dan dimana untuk melihatnya", "remove": "Buang", "add-members": "Tambah ahli", - "sharing-cancel": "Batal", "popular-tags": "Popular", "twelve-words-in-correct-order": "12 perkataan dalam susunan yang betul", "phone-number": "Nombor telefon", diff --git a/translations/nb.json b/translations/nb.json index 970511bb3e..f774846f7c 100644 --- a/translations/nb.json +++ b/translations/nb.json @@ -29,7 +29,6 @@ "remove": "Fjern", "transactions-unsigned-empty": "Du har ingen usignerte transaksjoner", "add-members": "Legg til medlemmer", - "sharing-cancel": "Avbryt", "popular-tags": "Populære etiketter", "twelve-words-in-correct-order": "12 ord i riktig rekkefølge", "phone-number": "Telefonnummer", diff --git a/translations/ne.json b/translations/ne.json index 4c6d2a08d7..a0a473d512 100644 --- a/translations/ne.json +++ b/translations/ne.json @@ -27,7 +27,6 @@ "address-explication": "सायद यहां ठेगाना बारे बयान गर्ने अनि त्यो कहाँ खोज्ने भन्ने कुराको कुनै पाठ हुनुपर्छ ।", "remove": "हटाउनुहोस्", "add-members": "सदस्यहरु जोड्नुहोस्", - "sharing-cancel": "रद्द गर्नुहोस्", "popular-tags": "लोकप्रिय ट्यागहरु", "twelve-words-in-correct-order": "१२ सब्दहरु सही क्रममा", "phone-number": "फोन नम्बर", diff --git a/translations/nl.json b/translations/nl.json index 3a50fe1c28..655fa7d408 100644 --- a/translations/nl.json +++ b/translations/nl.json @@ -26,7 +26,6 @@ "address-explication": "Misschien zou hier wat tekst moeten staan waarin wordt uitgelegd wat een adres is en waar je deze kunt vinden", "remove": "Verwijderen", "add-members": "Voeg leden toe", - "sharing-cancel": "Annuleren", "popular-tags": "Populaire tags", "twelve-words-in-correct-order": "12 woorden in goede volgorde", "phone-number": "Telefoonnummer", diff --git a/translations/pl.json b/translations/pl.json index 37856e8cea..36260c8b44 100644 --- a/translations/pl.json +++ b/translations/pl.json @@ -517,7 +517,6 @@ "shake-your-phone": "Znalazłeś błąd lub masz pytanie? Wystarczy ~potrząsnąć~ telefonem!", "share": "Udostępnij", "share-contact-code": "Udostępnij mój kod kontaktowy", - "sharing-cancel": "Anuluj", "sharing-copied-to-clipboard": "Skopiowane do schowka", "sharing-copy-to-clipboard": "Skopiuj do schowka", "sharing-share": "Udostępnij...", diff --git a/translations/pt_br.json b/translations/pt_br.json index 2946d0c3e2..e8edbe3dbe 100644 --- a/translations/pt_br.json +++ b/translations/pt_br.json @@ -25,7 +25,6 @@ "address-explication": "Talvez aqui deveria haver algum texto explicando o que é um endereço e onde procurá-lo", "remove": "Remover", "add-members": "Adicionar membros", - "sharing-cancel": "Cancelar", "popular-tags": "Tags populares", "twelve-words-in-correct-order": "12 palavras na ordem correta", "phone-number": "Número de telefone", diff --git a/translations/pt_pt.json b/translations/pt_pt.json index bb03a67819..674f833e63 100644 --- a/translations/pt_pt.json +++ b/translations/pt_pt.json @@ -25,7 +25,6 @@ "address-explication": "Talvez aqui devesse aparecer algum texto a explicar o que é um endereço e onde procurá-lo", "remove": "Remover", "add-members": "Adicionar Membros", - "sharing-cancel": "Cancelar", "popular-tags": "Tags populares", "twelve-words-in-correct-order": "12 palavras na ordem correta", "phone-number": "Número de telefone", diff --git a/translations/ro.json b/translations/ro.json index 413a3f99f7..06aea9431e 100644 --- a/translations/ro.json +++ b/translations/ro.json @@ -25,7 +25,6 @@ "address-explication": "Poate că aici ar trebui să fie un text care să explice ce este o adresă și unde să o cauți", "remove": "Elimină", "add-members": "Adăugare membri", - "sharing-cancel": "Anulare", "popular-tags": "Etichete populare", "twelve-words-in-correct-order": "12 de cuvinte în ordinea corectă", "phone-number": "Număr de telefon", diff --git a/translations/ru.json b/translations/ru.json index 9a193f9b2b..3c3bb622c5 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -40,7 +40,6 @@ "transaction-moved-text": "Транзакция будет находиться в списке 'Неподписанные' в течение 5 минут", "add-members": "Добавить членов", "sign-later-title": "Подписать транзакцию позже?", - "sharing-cancel": "Отмена", "yes": "Да", "dapps": "ÐApps", "popular-tags": "Популярные теги", diff --git a/translations/sl.json b/translations/sl.json index 4dc136012b..61a413379d 100644 --- a/translations/sl.json +++ b/translations/sl.json @@ -25,7 +25,6 @@ "address-explication": "Sem morda sodi besedilo, ki razlaga, kaj je naslov ter kje ga najti", "remove": "Odstrani", "add-members": "Dodaj člane", - "sharing-cancel": "Prekliči", "popular-tags": "Priljubljene oznake", "twelve-words-in-correct-order": "12 besed v pravilnem vrstnem redu", "phone-number": "Telefonska številka", diff --git a/translations/sr_rs_cyrl.json b/translations/sr_rs_cyrl.json index 705ecccbd3..6658277c81 100644 --- a/translations/sr_rs_cyrl.json +++ b/translations/sr_rs_cyrl.json @@ -40,7 +40,6 @@ "transaction-moved-text": "Ова трансакција ће остати у листи непотписаних током наредних 5 мин", "add-members": "Додај чланове", "sign-later-title": "Потпишите трансакцију касније?", - "sharing-cancel": "Одустани", "yes": "Да", "dapps": "ÐApps", "popular-tags": "Популарни #тагови", diff --git a/translations/sr_rs_latn.json b/translations/sr_rs_latn.json index 92a1bc1859..c25f8202b9 100644 --- a/translations/sr_rs_latn.json +++ b/translations/sr_rs_latn.json @@ -40,7 +40,6 @@ "transaction-moved-text": "Ova transakcija će ostati u listi nepotpisanih tokom narednih 5 min", "add-members": "Dodaj članove", "sign-later-title": "Potpišite transakciju kasnije?", - "sharing-cancel": "Odustani", "yes": "Da", "dapps": "ÐApps", "popular-tags": "Popularni #tagovi", diff --git a/translations/sv.json b/translations/sv.json index abf3ad5aa7..9c94e82e3f 100644 --- a/translations/sv.json +++ b/translations/sv.json @@ -40,7 +40,6 @@ "transaction-moved-text": "Transaktionen står kvar på listan 'Ej underskrivna' i 5 minuter", "add-members": "Lägg till medlemmar", "sign-later-title": "Skriv under transaktionen senare?", - "sharing-cancel": "Avbryt", "yes": "Ja", "dapps": "ÐApps", "popular-tags": "Populära taggar", diff --git a/translations/sw.json b/translations/sw.json index 020bb5926b..dc53f22d88 100644 --- a/translations/sw.json +++ b/translations/sw.json @@ -19,7 +19,6 @@ "address-explication": "Labda hapa kunapaswa kuwa na baadhi ya maandishi kueleza anwani ni nini na ni wapi pa kuitafuta", "remove": "Ondoa", "add-members": "Ongeza wanachama", - "sharing-cancel": "Ghairi", "popular-tags": "Vitambulisho maarufu", "phone-number": "Namba ya Simu", "removed-from-chat": "uliondolewa kwenye kikundi cha gumzo", diff --git a/translations/th.json b/translations/th.json index 4b461cc9c3..1e1e2ca880 100644 --- a/translations/th.json +++ b/translations/th.json @@ -25,7 +25,6 @@ "address-explication": "บางที ในที่นี้คุณควรกรอกข้อความสักเล็กน้อยเพื่อแสดงที่อยู่หรือสถานที่ที่จะมองหามันได้", "remove": "ลบ", "add-members": "เพิ่มสมาชิก", - "sharing-cancel": "ยกเลิก", "popular-tags": "แท็กยอดนิยม", "twelve-words-in-correct-order": "12 คำตามลำดับที่ถูกต้อง", "phone-number": "หมายเลขโทรศัพท์", diff --git a/translations/tr.json b/translations/tr.json index 3b2badaa55..bea5e360d2 100644 --- a/translations/tr.json +++ b/translations/tr.json @@ -25,7 +25,6 @@ "address-explication": "Burada belki de bir adresin ne olduğu ve bunu bulmak için nereye bakılmasıyla ilgili bazı metinler yer alabilir", "remove": "Kaldır", "add-members": "Üye ekle", - "sharing-cancel": "İptal Et", "popular-tags": "Popüler etiketler", "twelve-words-in-correct-order": "Doğru sırayla 12 kelime", "phone-number": "Telefon Numarası'", diff --git a/translations/uk.json b/translations/uk.json index af3ca7bb94..8be663773d 100644 --- a/translations/uk.json +++ b/translations/uk.json @@ -40,7 +40,6 @@ "transaction-moved-text": "Транзакція буде знаходитися в списку 'Непідписані' протягом 5 хвилин", "add-members": "Додати учасників", "sign-later-title": "Підписати транзакцію пізніше?", - "sharing-cancel": "Скасувати", "yes": "Так", "dapps": "ÐApps", "popular-tags": "Популярні #хештеги", diff --git a/translations/ur.json b/translations/ur.json index 8fbd7ae6bc..886f37bb25 100644 --- a/translations/ur.json +++ b/translations/ur.json @@ -25,7 +25,6 @@ "address-explication": "شائد یہاں آپ کو کچھ لکھنا چاہیے دیکھنے کے لیے کہ پتہ کیا ہے اور اسے کہاں ہونا چاہیے", "remove": "نکالیں", "add-members": "ممبران شامل کریں", - "sharing-cancel": "منسوخ کریں", "popular-tags": "مشہور ٹیگ", "twelve-words-in-correct-order": "درست ترتیب میں 12 الفاظ", "phone-number": "فون نمبر", diff --git a/translations/vi.json b/translations/vi.json index fb96950441..fc797fd1fb 100644 --- a/translations/vi.json +++ b/translations/vi.json @@ -25,7 +25,6 @@ "address-explication": "Có lẽ ở đây nên có một vài nội dung để giải thích địa chỉ này là gì và phải tìm nó ở đâu", "remove": "Xóa", "add-members": "Thêm Thành viên", - "sharing-cancel": "Hủy", "popular-tags": "Các thẻ phổ biến", "twelve-words-in-correct-order": "12 từ theo đúng thứ tự", "phone-number": "Số điện thoại", diff --git a/translations/zh_Hans_CN.json b/translations/zh_Hans_CN.json index 8316be765d..725f17cdbb 100644 --- a/translations/zh_Hans_CN.json +++ b/translations/zh_Hans_CN.json @@ -500,7 +500,6 @@ "shake-your-phone": "发现错误或有建议?只要 ~摇一摇~ 你的手机!", "share": "分享", "share-contact-code": "共享我的联系人代码", - "sharing-cancel": "取消", "sharing-copied-to-clipboard": "复制到剪贴板", "sharing-copy-to-clipboard": "复制到剪贴板", "sharing-share": "分享...", diff --git a/translations/zh_hans.json b/translations/zh_hans.json index 8d05c67ae0..17fdd5c7db 100644 --- a/translations/zh_hans.json +++ b/translations/zh_hans.json @@ -40,7 +40,6 @@ "transaction-moved-text": "本交易将在接下来的5分钟保持在“未签名”列表中", "add-members": "添加成员", "sign-later-title": "稍后对交易进行签名?", - "sharing-cancel": "取消", "yes": "是", "dapps": "ÐApps", "popular-tags": "热门标签", diff --git a/translations/zh_hant.json b/translations/zh_hant.json index 656d02d7bd..22d3fce4c1 100644 --- a/translations/zh_hant.json +++ b/translations/zh_hant.json @@ -40,7 +40,6 @@ "transaction-moved-text": "本交易將在接下來的5分鐘保持在“未簽名”列表中", "add-members": "添加成員", "sign-later-title": "稍後對交易進行簽名?", - "sharing-cancel": "取消", "yes": "是", "dapps": "ÐApps", "popular-tags": "熱門標籤", diff --git a/translations/zh_hant_hk.json b/translations/zh_hant_hk.json index 656d02d7bd..22d3fce4c1 100644 --- a/translations/zh_hant_hk.json +++ b/translations/zh_hant_hk.json @@ -40,7 +40,6 @@ "transaction-moved-text": "本交易將在接下來的5分鐘保持在“未簽名”列表中", "add-members": "添加成員", "sign-later-title": "稍後對交易進行簽名?", - "sharing-cancel": "取消", "yes": "是", "dapps": "ÐApps", "popular-tags": "熱門標籤", diff --git a/translations/zh_hant_sg.json b/translations/zh_hant_sg.json index 2b82b0be74..c0ecb387d6 100644 --- a/translations/zh_hant_sg.json +++ b/translations/zh_hant_sg.json @@ -40,7 +40,6 @@ "transaction-moved-text": "本交易將在接下來的5分鐘保持在“未簽名”列表中", "add-members": "新增成員", "sign-later-title": "稍後對交易進行簽名?", - "sharing-cancel": "取消", "yes": "是", "dapps": "ÐApps", "popular-tags": "熱門標籤", diff --git a/translations/zh_hant_tw.json b/translations/zh_hant_tw.json index 2b82b0be74..c0ecb387d6 100644 --- a/translations/zh_hant_tw.json +++ b/translations/zh_hant_tw.json @@ -40,7 +40,6 @@ "transaction-moved-text": "本交易將在接下來的5分鐘保持在“未簽名”列表中", "add-members": "新增成員", "sign-later-title": "稍後對交易進行簽名?", - "sharing-cancel": "取消", "yes": "是", "dapps": "ÐApps", "popular-tags": "熱門標籤", diff --git a/translations/zh_wuu.json b/translations/zh_wuu.json index d93058e588..fea9abb36e 100644 --- a/translations/zh_wuu.json +++ b/translations/zh_wuu.json @@ -27,7 +27,6 @@ "address-explication": "也许这应该有一个文本,解释地址是什么以及在哪里寻找它", "remove": "移动", "add-members": "添加会员", - "sharing-cancel": "取消", "popular-tags": "热门标签", "twelve-words-in-correct-order": "正确排序12个字", "phone-number": "电话号码", diff --git a/translations/zh_yue.json b/translations/zh_yue.json index 253067e0e9..089e90eec3 100644 --- a/translations/zh_yue.json +++ b/translations/zh_yue.json @@ -27,7 +27,6 @@ "address-explication": "請注意: 此文本解釋地址是什麼,以及在何處可找到它。", "remove": "刪除", "add-members": "增添成員", - "sharing-cancel": "取消", "popular-tags": "熱門標籤", "twelve-words-in-correct-order": "正確排序12個字", "phone-number": "電話號碼",