From 78e70571dfbc4bea385672c12756493371d733f4 Mon Sep 17 00:00:00 2001 From: alwx Date: Mon, 23 Jan 2017 18:57:40 +0300 Subject: [PATCH] Generating message preview on the fly, localization of request data and previews (#645, #754) --- resources/commands.js | 16 +++--- resources/status.js | 5 ++ resources/wallet.js | 8 --- src/status_im/chat/handlers.cljs | 7 ++- src/status_im/chat/handlers/commands.cljs | 55 +++++++++++-------- src/status_im/chat/handlers/console.cljs | 2 +- .../chat/handlers/receive_message.cljs | 16 ++---- src/status_im/chat/handlers/send_message.cljs | 12 ++-- src/status_im/chat/views/message.cljs | 33 +++++++---- src/status_im/chat/views/request_message.cljs | 22 +++++--- .../chats_list/views/inner_item.cljs | 13 +++-- src/status_im/commands/handlers/jail.cljs | 14 ----- src/status_im/components/status.cljs | 6 +- src/status_im/data_store/messages.cljs | 14 +---- src/status_im/i18n.cljs | 24 +++++++- src/status_im/protocol/handlers.cljs | 4 +- src/status_im/transactions/screen.cljs | 1 - .../transactions/views/transaction_page.cljs | 6 +- src/status_im/translations/en.cljs | 1 + 19 files changed, 136 insertions(+), 123 deletions(-) diff --git a/resources/commands.js b/resources/commands.js index ed4d5aa686..8c3652ac53 100644 --- a/resources/commands.js +++ b/resources/commands.js @@ -825,7 +825,7 @@ var send = { style: amountStyle, font: "light" }, - params.amount + status.localizeNumber(params.amount, context.delimiter, context.separator) )]); var currency = status.components.view( @@ -877,12 +877,6 @@ status.command({ name: "amount", type: status.types.NUMBER }], - preview: function (params) { - return status.components.text( - {}, - params.amount + " ETH" - ); - }, handler: function (params) { return { event: "request", @@ -891,11 +885,15 @@ status.command({ command: "send", params: { amount: params.amount - }, - content: I18n.t('request_requesting') + params.amount + "ETH" + } } }; }, + preview: function (params, context) { + return I18n.t('request_requesting') + + status.localizeNumber(params.amount, context.delimiter, context.separator) + + " ETH"; + }, validator: function(params) { try { var val = web3.toWei(params.amount, "ether"); diff --git a/resources/status.js b/resources/status.js index 7c7f9db2be..c5eda97794 100644 --- a/resources/status.js +++ b/resources/status.js @@ -167,6 +167,11 @@ var status = { autorun: function (commandName) { _status_catalog.autorun = commandName; }, + localizeNumber: function (num, del, sep) { + return I18n.toNumber( + num.replace(",", "."), + {precision: 10, strip_insignificant_zeros: true, delimiter: del, separator: sep}); + }, types: { TEXT: 'text', NUMBER: 'number', diff --git a/resources/wallet.js b/resources/wallet.js index fef4d3540a..169caaa36b 100644 --- a/resources/wallet.js +++ b/resources/wallet.js @@ -1,11 +1,3 @@ - -I18n.translations = { - en: { - browse_title: 'Browser', - browse_description: 'Launch the browser' - } -}; - function wallet(params, context) { var url = 'https://status.im/dapps/wallet'; diff --git a/src/status_im/chat/handlers.cljs b/src/status_im/chat/handlers.cljs index 6f5c66434c..1cff5d5ba7 100644 --- a/src/status_im/chat/handlers.cljs +++ b/src/status_im/chat/handlers.cljs @@ -297,7 +297,12 @@ (defn load-messages! ([db] (load-messages! db nil)) ([{:keys [current-chat-id] :as db} _] - (assoc db :messages (messages/get-by-chat-id current-chat-id)))) + (let [messages (messages/get-by-chat-id current-chat-id)] + (doseq [{:keys [content] :as message} messages] + (when (and (:command content) + (not (:content content))) + (dispatch [:request-command-preview (assoc message :chat-id current-chat-id)]))) + (assoc db :messages messages)))) (defn init-chat ([db] (init-chat db nil)) diff --git a/src/status_im/chat/handlers/commands.cljs b/src/status_im/chat/handlers/commands.cljs index 8ad5dede25..2ec4c6d099 100644 --- a/src/status_im/chat/handlers/commands.cljs +++ b/src/status_im/chat/handlers/commands.cljs @@ -10,7 +10,8 @@ [status-im.i18n :as i18n] [status-im.utils.datetime :as time] [status-im.utils.random :as random] - [status-im.utils.platform :as platform])) + [status-im.utils.platform :as platform] + [taoensso.timbre :as log])) (defn content-by-command [{:keys [type]} content] @@ -82,30 +83,12 @@ (str cu/command-prefix content) content)]))))) -(defn invoke-command-preview! - [db command-message [_ command-input chat-id]] - (let [{:keys [command]} command-message - {:keys [name type]} command - parameters (:params (or command-input (commands/get-command-input db))) - path [(if (= :command type) :commands :responses) - name - :preview] - params {:parameters parameters - :context {:platform platform/platform}}] - (if (and (console? chat-id) (= name "js")) - (dispatch [:send-chat-message command-message]) - (status/call-jail chat-id - path - params - #(dispatch [:command-preview command-message %]))))) - (defn command-input ([{:keys [current-chat-id] :as db}] (command-input db current-chat-id)) ([db chat-id] (get-in db [:chats chat-id :command-input]))) - (register-handler ::validate! (u/side-effect! (fn [_ [_ command-input {:keys [chat-id handler]} {:keys [error result]}]] @@ -146,14 +129,21 @@ (fn [db [_ command-input chat-id :as parameters]] (let [db (assoc-in db [:chats chat-id :input-text] nil) {:keys [command to-message-id params]} (or command-input (command-input db)) + message-id (random/id) command-info {:command command :params params :to-message to-message-id :created-at (time/now-ms) - :id (random/id) - :chat-id chat-id}] + :id message-id + :chat-id chat-id} + request-data {:message-id message-id + :chat-id chat-id + :content {:command (:name command) + :params params + :type (:type command)} + :on-requested #(dispatch [:send-chat-message command-info])}] (dispatch [:set-in [:command->chat (:id command-info)] chat-id]) - (invoke-command-preview! db command-info parameters))))) + (dispatch [:request-command-preview request-data]))))) (defn set-chat-command [{:keys [current-chat-id] :as db} [_ command-key type]] @@ -274,6 +264,23 @@ parameters #(dispatch [::validate! command-input data %])))))) +(register-handler :request-command-preview + (u/side-effect! + (fn [_ [_ {{:keys [command params content-command type]} :content + :keys [message-id chat-id on-requested] :as message}]] + (let [path [(if (= :response (keyword type)) :responses :commands) + (if content-command content-command command) + :preview] + params {:parameters params + :context (merge {:platform platform/platform} i18n/delimeters)} + callback #(do (when-let [result (get-in % [:result :returned])] + (dispatch [:set-in [:message-data :preview message-id] + (if (string? result) + result + (cu/generate-hiccup result))])) + (when on-requested (on-requested %)))] + (status/call-jail chat-id path params callback))))) + (register-handler :set-command-parameter (fn [db [_ {:keys [value parameter]}]] (let [name (:name parameter)] @@ -292,3 +299,7 @@ (do (dispatch [:set-chat-ui-props :sending-disabled? true]) (dispatch [:validate-command]))))))) + +(defn fib-lazy + ([] (fib-lazy 0 1)) + ([x1 x2] (cons x1 (lazy-seq (fib-lazy x2 (+ x1 x2)))))) \ No newline at end of file diff --git a/src/status_im/chat/handlers/console.cljs b/src/status_im/chat/handlers/console.cljs index 858ffba427..c62e1f8967 100644 --- a/src/status_im/chat/handlers/console.cljs +++ b/src/status_im/chat/handlers/console.cljs @@ -43,7 +43,7 @@ (messages/update {:message-id message-id :message-status status}))) (fn [db [_ message-id status]] - (assoc-in db [:message-statuses message-id] {:status status}))) + (assoc-in db [:message-data :statuses message-id] {:status status}))) (register-handler :console-respond-command (u/side-effect! diff --git a/src/status_im/chat/handlers/receive_message.cljs b/src/status_im/chat/handlers/receive_message.cljs index fa48e3a229..73184efcfd 100644 --- a/src/status_im/chat/handlers/receive_message.cljs +++ b/src/status_im/chat/handlers/receive_message.cljs @@ -13,16 +13,8 @@ [status-im.data-store.chats :as chats] [status-im.utils.scheduler :as s])) -(defn check-preview [{:keys [content] :as message}] - (if-let [preview (:preview content)] - (let [rendered-preview (generate-hiccup (read-string preview))] - (assoc message - :preview preview - :rendered-preview rendered-preview)) - message)) - (defn store-message [{chat-id :chat-id :as message}] - (messages/save chat-id (dissoc message :rendered-preview :new?))) + (messages/save chat-id (dissoc message :new?))) (defn get-current-identity [{:keys [current-account-id accounts]}] @@ -49,15 +41,15 @@ (or (not exists?) active?)) (let [group-chat? (not (nil? group-id)) previous-message (messages/get-last-message chat-id') - message' (assoc (->> message - (cu/check-author-direction previous-message) - (check-preview)) + message' (assoc (cu/check-author-direction previous-message message) :chat-id chat-id' :timestamp (or timestamp (random/timestamp)) :clock-value clock-value)] (store-message message') (dispatch [:upsert-chat! {:chat-id chat-id' :group-chat group-chat?}]) + (when (get-in message [:content :command]) + (dispatch [:request-command-preview message])) (dispatch [::add-message chat-id' message']) (when (= (:content-type message') content-type-command-request) (dispatch [:add-request chat-id' message'])) diff --git a/src/status_im/chat/handlers/send_message.cljs b/src/status_im/chat/handlers/send_message.cljs index 1252f8e221..aa9ded558e 100644 --- a/src/status_im/chat/handlers/send_message.cljs +++ b/src/status_im/chat/handlers/send_message.cljs @@ -29,16 +29,14 @@ :from identity :to chat-id :timestamp (time/now-ms) - :content (assoc content :preview preview-string - :handler-data handler-data - :type (name (:type command))) + :content (assoc content :handler-data handler-data + :type (name (:type command)) + :content-command (:name command)) :content-type (or content-type (if request content-type-command-request content-type-command)) :outgoing true - :preview preview-string - :rendered-preview preview :to-message to-message :type (:type command) :has-handler (:has-handler command) @@ -142,7 +140,7 @@ (fn [_ [_ chat-id {:keys [command]} hidden-params]] (let [command (-> command (update-in [:content :params] #(apply dissoc % hidden-params)) - (dissoc :rendered-preview :to-message :has-handler))] + (dissoc :to-message :has-handler))] (messages/save chat-id command))))) (register-handler ::dispatch-responded-requests! @@ -288,7 +286,7 @@ (register-handler ::send-command-protocol! (u/side-effect! (fn [{:keys [web3 current-public-key chats network-status] :as db} - [_ {:keys [chat-id command]}]] + [_ {:keys [chat-id command command-message]}]] (log/debug "sending command: " command) (when (cu/not-console? chat-id) (let [{:keys [public-key private-key]} (chats chat-id) diff --git a/src/status_im/chat/views/message.cljs b/src/status_im/chat/views/message.cljs index 1fa508e909..a1b1503748 100644 --- a/src/status_im/chat/views/message.cljs +++ b/src/status_im/chat/views/message.cljs @@ -36,7 +36,8 @@ get-contact-translated]] [status-im.chat.utils :as cu] [clojure.string :as str] - [status-im.chat.handlers.console :as console])) + [status-im.chat.handlers.console :as console] + [taoensso.timbre :as log])) (def window-width (:width (get-dimensions "window"))) @@ -112,13 +113,14 @@ (str params))])) (defview message-content-command - [{:keys [content content-type rendered-preview chat-id to from outgoing] :as message}] + [{:keys [message-id content content-type chat-id to from outgoing] :as message}] [commands [(if (= (:type content) "response") :get-responses :get-commands) chat-id] current-chat-id [:get-current-chat-id] - contact-chat [:get-in [:chats (if outgoing to from)]]] + contact-chat [:get-in [:chats (if outgoing to from)]] + preview [:get-in [:message-data :preview message-id]]] (let [{:keys [command params]} (parse-command-message-content commands content) {:keys [name type] icon-path :icon} command] @@ -135,7 +137,7 @@ :content-type content-type :params params :outgoing? outgoing - :preview rendered-preview + :preview preview :contact-chat contact-chat :contact-address (if outgoing to from) :current-chat-id current-chat-id}]])) @@ -237,8 +239,8 @@ :content-type content-type}]]]) (defview group-message-delivery-status [{:keys [message-id group-id message-status user-statuses] :as msg}] - [app-db-message-user-statuses [:get-in [:message-user-statuses message-id]] - app-db-message-status-value [:get-in [:message-statuses message-id :status]] + [app-db-message-user-statuses [:get-in [:message-data :user-statuses message-id]] + app-db-message-status-value [:get-in [:message-data :statuses message-id :status]] chat [:get-chat-by-id group-id] contacts [:get-contacts]] (let [status (or message-status app-db-message-status-value :sending) @@ -281,7 +283,7 @@ (defview message-delivery-status [{:keys [message-id chat-id message-status user-statuses content]}] - [app-db-message-status-value [:get-in [:message-statuses message-id :status]]] + [app-db-message-status-value [:get-in [:message-data :statuses message-id :status]]] (let [delivery-status (get-in user-statuses [chat-id :status]) command-name (keyword (:command content)) status (cond (and (not (console/commands-with-delivery-status command-name)) @@ -372,11 +374,18 @@ children)])})) (into [view] children))) -(defn chat-message [{:keys [outgoing message-id chat-id user-statuses from]}] - (let [my-identity (subscribe [:get :current-public-key]) - status (subscribe [:get-in [:message-user-statuses message-id my-identity]])] +(defn chat-message [{:keys [outgoing message-id chat-id user-statuses from content] :as message}] + (let [my-identity (subscribe [:get :current-public-key]) + status (subscribe [:get-in [:message-data :user-statuses message-id my-identity]]) + preview (subscribe [:get-in [:message-data :preview message-id]])] (r/create-class - {:component-did-mount + {:component-will-mount + (fn [] + (when (and (get-in message [:content :command]) + (not @preview)) + (dispatch [:request-command-preview message]))) + + :component-did-mount (fn [] (when (and (not outgoing) (not= :seen (keyword @status)) @@ -388,7 +397,7 @@ (fn [{:keys [outgoing group-chat content-type content] :as message}] [message-container message [touchable-highlight {:on-long-press (when (= content-type text-content-type) - #(share content (label :t/message)))} + #(share content (label :t/message)))} [view (let [incoming-group (and group-chat (not outgoing))] [message-content diff --git a/src/status_im/chat/views/request_message.cljs b/src/status_im/chat/views/request_message.cljs index 4e9f7be246..065178a793 100644 --- a/src/status_im/chat/views/request_message.cljs +++ b/src/status_im/chat/views/request_message.cljs @@ -76,7 +76,8 @@ (let [top-offset (r/atom {:specified? false}) commands-atom (subscribe [:get-responses]) answered? (subscribe [:is-request-answered? message-id]) - status-initialized? (subscribe [:get :status-module-initialized?])] + status-initialized? (subscribe [:get :status-module-initialized?]) + preview (subscribe [:get-in [:message-data :preview message-id]])] (fn [{:keys [message-id content from incoming-group]}] (let [commands @commands-atom params (:params content) @@ -93,14 +94,17 @@ [text {:style st/command-request-from-text :font :default} from]) - [text {:style st/style-message-text - :on-layout #(reset! top-offset {:specified? true - :value (-> (.-nativeEvent %) - (.-layout) - (.-height) 
 - (> 25))}) - :font :default} - content]]] + (if (and @preview + (not (string? @preview))) + [view @preview] + [text {:style st/style-message-text + :on-layout #(reset! top-offset {:specified? true + :value (-> (.-nativeEvent %) + (.-layout) + (.-height) 
 + (> 25))}) + :font :default} + (or @preview content)])]] (when (:request-text command) [view st/command-request-text-view [text {:style st/style-sub-text diff --git a/src/status_im/chats_list/views/inner_item.cljs b/src/status_im/chats_list/views/inner_item.cljs index 19f66d437b..d3c8d373d4 100644 --- a/src/status_im/chats_list/views/inner_item.cljs +++ b/src/status_im/chats_list/views/inner_item.cljs @@ -12,13 +12,14 @@ [status-im.utils.gfycat.core :refer [generate-gfy]] [status-im.constants :refer [console-chat-id content-type-command - content-type-command-request] :as c])) + content-type-command-request] :as c] + [taoensso.timbre :as log])) (defmulti message-content (fn [{:keys [content-type]}] content-type)) (defn command-content - [{{:keys [command params]} :content}] - (let [kw (keyword (str "t/command-text-" (name command)))] + [{{:keys [command content-command params]} :content}] + (let [kw (keyword (str "t/command-text-" (name (or content-command command))))] (label kw params))) (defmethod message-content content-type-command @@ -30,8 +31,8 @@ (command-content message)) (defmethod message-content content-type-command-request - [{{:keys [content]} :content}] - content) + [message] + (command-content message)) (defmethod message-content :default [{:keys [content]}] @@ -48,7 +49,7 @@ (defview message-status [{:keys [chat-id contacts]} {:keys [message-id message-status user-statuses message-type outgoing] :as msg}] - [app-db-message-status-value [:get-in [:message-statuses message-id :status]]] + [app-db-message-status-value [:get-in [:message-data :statuses message-id :status]]] (let [delivery-status (get-in user-statuses [chat-id :status])] (when (and outgoing (or (some #(= (keyword %) :seen) [delivery-status diff --git a/src/status_im/commands/handlers/jail.cljs b/src/status_im/commands/handlers/jail.cljs index 6ce7a405be..b87f16b6a4 100644 --- a/src/status_im/commands/handlers/jail.cljs +++ b/src/status_im/commands/handlers/jail.cljs @@ -67,16 +67,6 @@ ;; todo show error? nil))) -(defn command-preview - [_ [command-message {:keys [result]}]] - (let [result' (:returned result)] - (dispatch [:send-chat-message - (if result' - (assoc command-message - :preview (generate-hiccup result') - :preview-string (str result')) - command-message)]))) - (defn print-error-message! [message] (fn [_ params] (when (:error (last params)) @@ -100,10 +90,6 @@ suggestions-handler!) (reg-handler :suggestions-event! (u/side-effect! suggestions-events-handler!)) -(reg-handler :command-preview - (after (print-error-message! "Error on command preview")) - (u/side-effect! command-preview)) - (reg-handler :set-local-storage (fn [{:keys [current-chat-id] :as db} [{:keys [data] :as event}]] (log/debug "Got event: " event) diff --git a/src/status_im/components/status.cljs b/src/status_im/components/status.cljs index 06b63c124a..cf29264e38 100644 --- a/src/status_im/components/status.cljs +++ b/src/status_im/components/status.cljs @@ -131,9 +131,9 @@ (when status (call-module #(do - (log/debug :chat-id chat-id) - (log/debug :path path) - (log/debug :params params) + (log/debug :call-jail :chat-id chat-id) + (log/debug :call-jail :path path) + (log/debug :call-jail :params params) (let [params' (update params :context assoc :debug js/goog.DEBUG :locale i/i18n.locale) diff --git a/src/status_im/data_store/messages.cljs b/src/status_im/data_store/messages.cljs index 381a91bb12..f7ea680e87 100644 --- a/src/status_im/data_store/messages.cljs +++ b/src/status_im/data_store/messages.cljs @@ -53,13 +53,9 @@ (mapv #(clojure.core/update % :user-statuses user-statuses-to-map)) (into '()) reverse - (keep (fn [{:keys [content-type preview] :as message}] + (keep (fn [{:keys [content-type] :as message}] (if (command-type? content-type) - (-> message - (clojure.core/update :content str-to-map) - (assoc :rendered-preview - (when preview - (generate-hiccup (read-string preview))))) + (clojure.core/update message :content str-to-map) message))))) (defn get-count-by-chat-id @@ -76,11 +72,7 @@ reverse (keep (fn [{:keys [content-type preview] :as message}] (if (command-type? content-type) - (-> message - (clojure.core/update :content str-to-map) - (assoc :rendered-preview - (when preview - (generate-hiccup (read-string preview))))) + (clojure.core/update message :content str-to-map) message)))))) (defn get-last-message diff --git a/src/status_im/i18n.cljs b/src/status_im/i18n.cljs index 44207ca737..13977ec5a6 100644 --- a/src/status_im/i18n.cljs +++ b/src/status_im/i18n.cljs @@ -33,7 +33,9 @@ [status-im.translations.zh-hant :as zh-hant] [status-im.translations.zh-wuu :as zh-wuu] [status-im.translations.zh-yue :as zh-yue] - [status-im.utils.js-resources :refer [default-contacts]])) + [status-im.utils.js-resources :refer [default-contacts]] + [taoensso.timbre :as log] + [clojure.string :as str])) (def i18n (js/require "react-native-i18n")) (set! (.-fallbacks i18n) true) @@ -73,11 +75,29 @@ :zh-wuu zh-wuu/translations :zh-yue zh-yue/translations})) +(def delimeters + "This function is a hack: mobile Safari doesn't support toLocaleString(), so we need to pass + this map to WKWebView to make number formatting work." + (let [n (.toLocaleString (js/Number 1000.1))] + {:delimiter (subs n 1 2) + :separator (subs n 5 6)})) + +(defn label-number [number] + (when number + (let [{:keys [delimiter separator]} delimeters] + (.toNumber i18n + (str/replace number #"," ".") + (clj->js {:precision 10 + :strip_insignificant_zeros true + :delimiter delimiter + :separator separator}))))) + (defn label ([path] (label path {})) ([path options] (if (exists? i18n.t) - (.t i18n (name path) (clj->js options)) + (let [options (update options :amount label-number)] + (.t i18n (name path) (clj->js options))) (name path)))) (defn label-pluralize [count path & options] diff --git a/src/status_im/protocol/handlers.cljs b/src/status_im/protocol/handlers.cljs index ea990bf3b6..5d0cc27e35 100644 --- a/src/status_im/protocol/handlers.cljs +++ b/src/status_im/protocol/handlers.cljs @@ -310,8 +310,8 @@ (let [message-id' (or ack-of-message message-id) group? (boolean group-id) status-path (if (and group? (not= status :sent)) - [:message-user-statuses message-id' from] - [:message-statuses message-id']) + [:message-data :user-statuses message-id' from] + [:message-data :statuses message-id']) {current-status :status} (get-in db status-path)] (if-not (= :seen current-status) (assoc-in db status-path {:whisper-identity from diff --git a/src/status_im/transactions/screen.cljs b/src/status_im/transactions/screen.cljs index 8337877dcb..4ca0103404 100644 --- a/src/status_im/transactions/screen.cljs +++ b/src/status_im/transactions/screen.cljs @@ -20,7 +20,6 @@ [status-im.i18n :refer [label label-pluralize]] [clojure.string :as s])) - (defview confirm [] [transactions [:transactions] {:keys [password]} [:get :confirm-transactions] diff --git a/src/status_im/transactions/views/transaction_page.cljs b/src/status_im/transactions/views/transaction_page.cljs index 46e948a17a..b72158f6f3 100644 --- a/src/status_im/transactions/views/transaction_page.cljs +++ b/src/status_im/transactions/views/transaction_page.cljs @@ -10,7 +10,7 @@ touchable-opacity]] [status-im.components.styles :refer [icon-close]] [status-im.transactions.styles :as st] - [status-im.i18n :refer [label label-pluralize]])) + [status-im.i18n :refer [label label-pluralize label-number]])) (defn title-bar [title id] [view st/title-bar @@ -35,10 +35,10 @@ (defview transaction-page [{:keys [id from to value] :as transaction}] [{:keys [name] :as contact} [:contact-by-address to]] (let [eth-value (.fromWei js/Web3.prototype value "ether") - title (str eth-value " ETH to " (or name to)) + title (str (label-number eth-value) " ETH to " (or name to)) transactions-info [[(label :t/status) (label :t/pending-confirmation)] [(label :t/recipient) (or name to)] - [(label :t/value) (str eth-value " ETH")]]] + [(label :t/value) (str (label-number eth-value) " ETH")]]] [view {:style st/transaction-page :key id} [title-bar title id] diff --git a/src/status_im/translations/en.cljs b/src/status_im/translations/en.cljs index dfbe79438b..6449738d00 100644 --- a/src/status_im/translations/en.cljs +++ b/src/status_im/translations/en.cljs @@ -178,6 +178,7 @@ :command-text-send "Transaction: {{amount}} ETH" :command-text-help "Help" :command-text-faucet "Faucet: {{url}}" + :command-text-request "Request: {{amount}} ETH" ;new-group :group-chat-name "Chat name"