remove commands before replacement by gui commands

Signed-off-by: yenda <eric@status.im>
This commit is contained in:
yenda 2019-11-13 18:07:18 +01:00
parent 0b802ec456
commit 94c7953f30
No known key found for this signature in database
GPG Key ID: 0095623C0069DCE6
46 changed files with 89 additions and 2026 deletions

View File

@ -1,154 +0,0 @@
(ns status-im.chat.commands.core
(:require [clojure.set :as set]
[status-im.chat.constants :as chat.constants]
[status-im.chat.commands.protocol :as protocol]
[status-im.chat.commands.impl.transactions :as transactions]
[status-im.ethereum.core :as ethereum]
[status-im.utils.handlers :as handlers]
[status-im.utils.fx :as fx]))
(def register
"Register of all commands. Whenever implementing a new command,
provide the implementation in the `status-im.chat.commands.impl.*` ns,
and add its instance here."
#{(transactions/PersonalSendCommand.)
(transactions/PersonalRequestCommand.)})
(def command-id (juxt protocol/id protocol/scope))
(defn command-name
"Given the command instance, returns command name as displayed in chat input,
with leading `/` character."
[type]
(str chat.constants/command-char (protocol/id type)))
(defn command-description
"Returns description for the command."
[type]
(protocol/description type))
(defn accessibility-label
"Returns accessibility button label for command, derived from its id"
[type]
(keyword (str (protocol/id type) "-button")))
(defn- contact->address [contact]
(str "0x" (ethereum/public-key->address contact)))
(defn add-chat-contacts
"Enrich command-message by adding contact list of the current private or group chat"
[contacts {:keys [public? group-chat] :as command-message}]
(cond
public? command-message
group-chat (assoc command-message :contacts (map contact->address contacts))
:else (assoc command-message :contact (contact->address (first contacts)))))
(defn enrich-command-message-for-events
"adds new pairs to command-message to be consumed by extension events"
[db {:keys [chat-id] :as command-message}]
(let [{:keys [contacts public? group-chat]} (get-in db [:chats chat-id])]
(add-chat-contacts contacts (assoc command-message :public? public? :group-chat group-chat))))
(defn generate-short-preview
"Returns short preview for command"
[{:keys [type]} command-message]
(protocol/short-preview type command-message))
(defn generate-preview
"Returns preview for command"
[{:keys [type]} command-message]
(protocol/preview type command-message))
(defn- add-exclusive-choices [initial-scope exclusive-choices]
(reduce (fn [scopes-set exclusive-choices]
(reduce (fn [scopes-set scope]
(let [exclusive-match (set/intersection scope exclusive-choices)]
(if (seq exclusive-match)
(reduce conj
(disj scopes-set scope)
(map (partial conj
(set/difference scope exclusive-match))
exclusive-match))
scopes-set)))
scopes-set
scopes-set))
#{initial-scope}
exclusive-choices))
(defn index-commands
"Takes collecton of things implementing the command protocol, and
correctly indexes them by their composite ids and access scopes."
[commands]
(let [id->command (reduce (fn [acc command]
(assoc acc (command-id command)
{:type command
:params (into [] (protocol/parameters command))}))
{}
commands)
access-scope->command-id (reduce-kv (fn [acc command-id {:keys [type]}]
(let [access-scopes (add-exclusive-choices
(protocol/scope type)
protocol/or-scopes)]
(reduce (fn [acc access-scope]
(update acc
access-scope
(fnil conj #{})
command-id))
acc
access-scopes)))
{}
id->command)]
{:id->command id->command
:access-scope->command-id access-scope->command-id}))
(fx/defn load-commands
"Takes collection of things implementing the command protocol and db,
correctly indexes them and adds them to db in a way that preserves existing commands"
[{:keys [db]} commands]
(let [{:keys [id->command access-scope->command-id]} (index-commands commands)]
{:db (-> db
(update :id->command merge id->command)
(update :access-scope->command-id #(merge-with (fnil into #{}) % access-scope->command-id)))}))
(defn remove-command
"Remove command form db, correctly updating all indexes"
[command {:keys [db]}]
(let [id (command-id command)]
{:db (-> db
(update :id->command dissoc id)
(update :access-scope->command-id (fn [access-scope->command-id]
(reduce (fn [acc [scope command-ids-set]]
(if (command-ids-set id)
(if (= 1 (count command-ids-set))
acc
(assoc acc scope (disj command-ids-set id)))
(assoc acc scope command-ids-set)))
{}
access-scope->command-id))))}))
(handlers/register-handler-fx
:load-commands
(fn [cofx [_ commands]]
(load-commands commands cofx)))
(defn chat-commands
"Takes `id->command`, `access-scope->command-id` and `chat` parameters and returns
entries map of `id->command` map eligible for given chat.
Note that the result map is keyed just by `protocol/id` of command entries,
not the unique composite ids of the global `id->command` map.
That's because this function is already returning local commands for particular
chat and locally, they should always have unique `protocol/id`."
[id->command access-scope->command-id {:keys [chat-id group-chat public?]}]
(let [global-access-scope (cond-> #{}
(not group-chat) (conj :personal-chats)
(and group-chat (not public?)) (conj :group-chats)
public? (conj :public-chats))
chat-access-scope #{chat-id}]
;;TODO disable commands temporary for v1
{}
#_(reduce (fn [acc command-id]
(let [{:keys [type] :as command-props} (get id->command command-id)]
(assoc acc (protocol/id type) command-props)))
{}
(concat (get access-scope->command-id global-access-scope)
(get access-scope->command-id chat-access-scope)))))

View File

@ -1,465 +0,0 @@
(ns status-im.chat.commands.impl.transactions
(:require [clojure.string :as string]
[re-frame.core :as re-frame]
[reagent.core :as reagent]
[status-im.chat.commands.impl.transactions.styles
:as
transactions-styles]
[status-im.utils.fx :as fx]
[status-im.chat.commands.protocol :as protocol]
[status-im.data-store.messages :as messages-store]
[status-im.ethereum.core :as ethereum]
[status-im.ethereum.tokens :as tokens]
[status-im.i18n :as i18n]
[status-im.ui.components.animation :as animation]
[status-im.ui.components.chat-icon.screen :as chat-icon]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.icons.vector-icons :as vector-icons]
[status-im.ui.components.list.views :as list]
[status-im.ui.components.react :as react]
[status-im.ui.components.svgimage :as svgimage]
[status-im.wallet.utils :as wallet.utils]
[status-im.utils.datetime :as datetime]
[status-im.utils.money :as money]
[status-im.utils.platform :as platform]
[status-im.wallet.db :as wallet.db]
[status-im.signing.core :as signing]
[status-im.ethereum.abi-spec :as abi-spec])
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
;; common `send/request` functionality
(defn- render-asset [{:keys [name symbol amount decimals icon color] :as asset}]
[react/touchable-highlight
{:on-press #(re-frame/dispatch [:chat.ui/set-command-parameter (wallet.utils/display-symbol asset)])}
[react/view transactions-styles/asset-container
[react/view transactions-styles/asset-main
(if icon
[react/image {:source (:source icon)
:style transactions-styles/asset-icon}]
[react/view {:style transactions-styles/asset-icon}
[chat-icon/custom-icon-view-list name color 30]])
[react/text (wallet.utils/display-symbol asset)]
[react/text {:style transactions-styles/asset-name} name]]
;;TODO(goranjovic) : temporarily disabled to fix https://github.com/status-im/status-react/issues/4963
;;until the resolution of https://github.com/status-im/status-react/issues/4972
#_[react/text {:style transactions-styles/asset-balance}
(str (money/internal->formatted amount symbol decimals))]]])
(defn- render-nft-asset [{:keys [name symbol amount] :as asset}]
[react/touchable-highlight
{:on-press #(re-frame/dispatch [:chat.ui/set-command-parameter (clojure.core/name symbol)])}
[react/view transactions-styles/asset-container
[react/view transactions-styles/asset-main
[react/image {:source (-> asset :icon :source)
:style transactions-styles/asset-icon}]
[react/text name]]
[react/text {:style {:font-size 16
:color colors/gray
:padding-right 14}}
(money/to-fixed amount)]]])
(def assets-separator [react/view transactions-styles/asset-separator])
(defn choose-asset [nft?] [react/view])
;;TODO we'll need to specify address here
#_(letsubs [assets [:wallet/visible-assets-with-amount]]
[react/view
[list/flat-list {:data (filter #(if nft?
(:nft? %)
(not (:nft? %)))
assets)
:key-fn (comp name :symbol)
:render-fn (if nft?
render-nft-asset
render-asset)
:enableEmptySections true
:separator assets-separator
:keyboardShouldPersistTaps :always
:bounces false}]])
(defn choose-asset-suggestion []
[choose-asset false])
(defn personal-send-request-short-preview
[label-key {:keys [content]}]
(let [{:keys [amount coin]} (:params content)]
[react/text {:number-of-lines 1}
(i18n/label label-key {:amount amount
:asset (wallet.utils/display-symbol coin)})]))
(def personal-send-request-params
[{:id :asset
:type :text
:placeholder (i18n/label :t/send-request-currency)
:suggestions choose-asset-suggestion}
{:id :amount
:type :number
:placeholder (i18n/label :t/send-request-amount)}])
(defview choose-nft-token []
(letsubs [{:keys [input-params]} [:chats/selected-chat-command]
collectibles [:collectibles]]
(let [collectible-tokens (get collectibles (keyword (:symbol input-params)))]
[react/view {:flex-direction :row
:align-items :center
:padding-vertical 11}
(map
(fn [[id {:keys [name image_url]}]]
[react/touchable-highlight
{:key id
:on-press #(re-frame/dispatch [:chat.ui/set-command-parameter (str id)])}
[react/view {:flex-direction :column
:align-items :center
:margin-left 10
:border-radius 2
:border-width 1
:border-color colors/gray}
[svgimage/svgimage {:style {:width 100
:height 100
:margin-left 20
:margin-right 20}
:source {:uri image_url}}]
[react/text name]]])
collectible-tokens)])))
(defview nft-token [{{:keys [name image_url]} :token}]
[react/view {:flex-direction :column
:align-items :center}
[svgimage/svgimage {:style {:width 100
:height 100}
:source {:uri image_url}}]
[react/text name]])
;;TODO(goranjovic): currently we only allow tokens which are enabled in Manage assets here
;; because balances are only fetched for them. Revisit this decision with regard to battery/network consequences
;; if we were to update all balances.
(defn- allowed-assets [{:keys [chain multiaccount] :as db}]
(let [all-tokens (:wallet/all-tokens db)
chain-keyword (keyword chain)
{:keys [symbol symbol-display decimals]} (tokens/native-currency chain-keyword)
visible-tokens (get-in multiaccount [:settings :wallet :visible-tokens chain-keyword])]
(into {(name (or symbol-display symbol)) decimals}
(comp (filter #(and (not (:nft? %))
(contains? visible-tokens (:symbol %))))
(map (juxt (comp name :symbol) :decimals)))
(tokens/tokens-for all-tokens chain-keyword))))
(defn- personal-send-request-validation [{:keys [asset amount]} {:keys [db]}]
(let [asset-decimals (get (allowed-assets db) asset)]
(cond
(not asset-decimals)
{:title (i18n/label :t/send-request-invalid-asset)
:description (i18n/label :t/send-request-unknown-token {:asset asset})}
(not amount)
{:title (i18n/label :t/send-request-amount)
:description (i18n/label :t/send-request-amount-must-be-specified)}
:else
(let [sanitised-str (string/replace amount #"," ".")
portions (string/split sanitised-str ".")
decimals (count (get portions 1))
amount-string (str amount)
amount (js/Number sanitised-str)]
(cond
(or (js/isNaN amount)
(> (count portions) 2)
(re-matches #".+(\.|,)$" amount-string)
;; check if non-decimal number
(re-matches #"0[\dbxo][\d\.]*" amount-string))
{:title (i18n/label :t/send-request-amount)
:description (i18n/label :t/send-request-amount-invalid-number)}
(and decimals (> decimals asset-decimals))
{:title (i18n/label :t/send-request-amount)
:description (i18n/label :t/send-request-amount-max-decimals
{:asset-decimals asset-decimals})})))))
;; `/send` command
(defview send-status [tx-hash outgoing]
(letsubs [{:keys [exists? confirmed?]} [:chats/transaction-status tx-hash]]
[react/touchable-highlight {:on-press #(when exists?
(re-frame/dispatch [:wallet.ui/show-transaction-details tx-hash]))}
[react/view transactions-styles/command-send-status-container
[vector-icons/tiny-icon (if confirmed?
:tiny-icons/tiny-check
:tiny-icons/tiny-pending)
{:color (if outgoing
colors/blue-light
colors/blue)
:container-style (transactions-styles/command-send-status-icon outgoing)}]
[react/view
[react/text {:style (transactions-styles/command-send-status-text outgoing)}
(i18n/label (cond
confirmed? :status-confirmed
exists? :status-pending
:else :status-tx-not-found))]]]]))
(defn transaction-status [{:keys [tx-hash outgoing]}]
[send-status tx-hash outgoing])
(defview send-preview
[{:keys [content timestamp-str outgoing group-chat]}]
(letsubs [network [:chain-name]
all-tokens [:wallet/all-tokens]]
(let [{{:keys [amount fiat-amount tx-hash asset currency] send-network :network} :params} content
recipient-name (get-in content [:params :bot-db :public :recipient])
network-mismatch? (and (seq send-network) (not= network send-network))
token (tokens/asset-for all-tokens (keyword send-network) (keyword asset))]
[react/view transactions-styles/command-send-message-view
[react/view
[react/view transactions-styles/command-send-amount-row
[react/view transactions-styles/command-send-amount
[react/nested-text {:style (transactions-styles/command-send-amount-text outgoing)}
amount
[{:style (transactions-styles/command-amount-currency-separator outgoing)}
"."]
[{:style (transactions-styles/command-send-currency-text outgoing)}
(wallet.utils/display-symbol token)]]]]
(when (and fiat-amount
platform/mobile?
;;NOTE(goranjovic) - have to hide cross network asset fiat value until we can support
;; multiple chain prices simultaneously
(not network-mismatch?))
[react/view transactions-styles/command-send-fiat-amount
[react/text {:style (transactions-styles/command-send-fiat-amount-text outgoing)}
(str "~ " fiat-amount " " (or currency (i18n/label :usd-currency)))]])
(when (and group-chat
recipient-name)
[react/text {:style transactions-styles/command-send-recipient-text}
(i18n/label :send-sending-to {:recipient-name recipient-name})])
[react/view
[react/text {:style (transactions-styles/command-send-timestamp outgoing)}
(str (i18n/label :sent-at) " " timestamp-str)]]
(when platform/mobile?
[send-status tx-hash outgoing])
(when network-mismatch?
[react/text send-network])]])))
;; TODO(goranjovic) - update to include tokens in https://github.com/status-im/status-react/issues/3233
(defn- transaction-details [contact symbol]
(-> contact
(select-keys [:name :address :public-key])
(assoc :symbol symbol
:gas (ethereum/estimate-gas symbol)
:from-chat? true)))
(defn- inject-network-info [parameters {:keys [db]}]
(assoc parameters :network (:chain db)))
(defn- inject-coin-info [{:keys [network asset] :as parameters} {:keys [db]}]
(let [all-tokens (:wallet/all-tokens db)
coin (when (and network asset)
(tokens/asset-for all-tokens (keyword network) (keyword asset)))]
(assoc parameters :coin coin)))
(defn get-currency [db]
(or (get-in db [:multiaccount :settings :wallet :currency]) :usd))
(defn- inject-price-info [{:keys [amount asset] :as parameters} {:keys [db]}]
(let [currency (-> db
get-currency
name
string/upper-case)]
(assoc parameters
:fiat-amount (money/fiat-amount-value (string/replace amount #"," ".")
(keyword asset)
(keyword currency)
(:prices db))
:currency currency)))
(defn- params-unchanged? [send-message request-message]
(and (= (get-in send-message [:content :params :asset])
(get-in request-message [:content :params :asset]))
(= (get-in send-message [:content :params :amount])
(get-in request-message [:content :params :amount]))))
(deftype PersonalSendCommand []
protocol/Command
(id [_] "send")
(scope [_] #{:personal-chats})
(description [_] (i18n/label :t/send-command-payment))
(parameters [_] personal-send-request-params)
(validate [_ parameters cofx]
;; Only superficial/formatting validation, "real validation" will be performed
;; 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] :as cofx}]
(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)]
(fx/merge cofx
{:db (assoc-in db [:chats chat-id :messages responding-to] updated-request-message)}
(messages-store/save-message updated-request-message)))))))
(on-receive [_ command-message cofx])
(short-preview [_ command-message]
(personal-send-request-short-preview :command-sending command-message))
(preview [_ command-message]
(send-preview command-message))
protocol/Yielding
(yield-control [_ {{{amount :amount asset :asset} :params} :content} {:keys [db] :as cofx}]
(let [{:keys [symbol decimals address]} (tokens/asset-for (:wallet/all-tokens db) (keyword (:chain db)) (keyword asset))
{:keys [value]} (wallet.db/parse-amount amount decimals)
current-chat-id (:current-chat-id db)
amount-hex (str "0x" (abi-spec/number-to-hex (money/formatted->internal value symbol decimals)))
to (ethereum/public-key->address current-chat-id)
to-norm (ethereum/normalized-hex (if (= symbol :ETH) to address))
tx-obj (if (= symbol :ETH)
{:to to-norm
:value amount-hex}
{:to to-norm
:data (abi-spec/encode "transfer(address,uint256)" [to amount-hex])})]
(signing/sign cofx {:tx-obj tx-obj
:on-result [:chat/send-transaction-result current-chat-id {:address to-norm
:asset (name symbol)
:amount amount}]})))
protocol/EnhancedParameters
(enhance-send-parameters [_ parameters cofx]
(-> parameters
(inject-network-info cofx)
(inject-coin-info cofx)
(inject-price-info cofx)))
(enhance-receive-parameters [_ parameters cofx]
(-> parameters
(inject-coin-info cofx)
(inject-price-info cofx))))
;; `/request` command
(def request-message-icon-scale-delay 600)
(def min-scale 1)
(def max-scale 1.3)
(defn button-animation [val to-value loop? answered?]
(animation/anim-sequence
[(animation/anim-delay
(if (and @loop? (not @answered?))
request-message-icon-scale-delay
0))
(animation/spring val {:toValue to-value
:useNativeDriver true})]))
(defn request-button-animation-logic
[{:keys [to-value val loop? answered?] :as context}]
(animation/start
(button-animation val to-value loop? answered?)
#(if (and @loop? (not @answered?))
(let [new-value (if (= to-value min-scale) max-scale min-scale)
context' (assoc context :to-value new-value)]
(request-button-animation-logic context'))
(animation/start
(button-animation val min-scale loop? answered?)))))
(defn request-button-label
"The request button label will be in the form of `request-the-command-name`"
[command-name]
(keyword (str "request-" (name command-name))))
(defn request-button [message-id _ on-press-handler]
(let [scale-anim-val (animation/create-value min-scale)
answered? (re-frame/subscribe [:is-request-answered? message-id])
loop? (reagent/atom true)
context {:to-value max-scale
:val scale-anim-val
:answered? answered?
:loop? loop?}]
(reagent/create-class
{:display-name "request-button"
:component-did-mount
(if (or (nil? on-press-handler) @answered?) (fn []) #(request-button-animation-logic context))
:component-will-unmount
#(reset! loop? false)
:reagent-render
(fn [message-id {command-icon :icon :as command} on-press-handler]
(when command
[react/touchable-highlight
{:on-press on-press-handler
:style transactions-styles/command-request-image-touchable
:accessibility-label (request-button-label (:name command))}
[react/animated-view {:style (transactions-styles/command-request-image-view command scale-anim-val)}
(when command-icon
[react/icon command-icon transactions-styles/command-request-image])]]))})))
(defview request-preview
[{:keys [message-id content outgoing timestamp timestamp-str group-chat]}]
(letsubs [id->command [:chats/id->command]
network [:chain-name]
prices [:prices]]
(let [{:keys [amount asset fiat-amount currency answered?] request-network :network} (:params content)
network-mismatch? (and request-network (not= request-network network))
command (get id->command ["send" #{:personal-chats}])
markup [react/view (transactions-styles/command-request-message-view outgoing)
[react/view
[react/text {:style (transactions-styles/command-request-header-text outgoing)}
(i18n/label :transaction-request)]]
[react/view transactions-styles/command-request-row
[react/nested-text {:style (transactions-styles/command-request-amount-text outgoing)}
amount
[{:style (transactions-styles/command-amount-currency-separator outgoing)}
"."]
[{:style (transactions-styles/command-request-currency-text outgoing)}
asset]]]
(when (and platform/mobile?
;;NOTE(goranjovic) - have to hide cross network asset fiat value until we can support
;; multiple chain prices simultaneously
(not network-mismatch?))
[react/view transactions-styles/command-request-fiat-amount-row
[react/text {:style (transactions-styles/command-request-fiat-amount-text outgoing)}
(str "~ " fiat-amount " " (or currency (i18n/label :usd-currency)))]])
(when network-mismatch?
[react/text {:style transactions-styles/command-request-network-text}
(str (i18n/label :on) " " request-network)])
[react/view transactions-styles/command-request-timestamp-row
[react/text {:style (transactions-styles/command-request-timestamp-text outgoing)}
(str
(datetime/timestamp->mini-date timestamp)
" "
(i18n/label :at)
" "
timestamp-str)]]
(when (and (not outgoing)
platform/mobile?)
[react/view
[react/view transactions-styles/command-request-separator-line]
[react/view transactions-styles/command-request-button
[react/text {:style (transactions-styles/command-request-button-text answered?)}
(i18n/label (if answered? :command-button-sent :command-button-send))]]])]]
(if (and (not network-mismatch?)
(not outgoing)
(not answered?))
[react/touchable-highlight
{:on-press #(re-frame/dispatch [:chat.ui/select-chat-input-command
command [(or asset "ETH") amount] message-id])}
markup]
markup))))
(deftype PersonalRequestCommand []
protocol/Command
(id [_] "request")
(scope [_] #{:personal-chats})
(description [_] (i18n/label :t/request-command-payment))
(parameters [_] personal-send-request-params)
(validate [_ parameters cofx]
(personal-send-request-validation parameters cofx))
(on-send [_ _ _])
(on-receive [_ _ _])
(short-preview [_ command-message]
(personal-send-request-short-preview :command-requesting command-message))
(preview [_ command-message]
(request-preview command-message))
protocol/EnhancedParameters
(enhance-send-parameters [_ parameters cofx]
(-> parameters
(inject-network-info cofx)
(inject-coin-info cofx)
(inject-price-info cofx)))
(enhance-receive-parameters [_ parameters cofx]
(-> parameters
(inject-coin-info cofx)
(inject-price-info cofx))))

View File

@ -1,185 +0,0 @@
(ns status-im.chat.commands.impl.transactions.styles
(:require [status-im.ui.components.colors :as colors]))
(def asset-container
{:flex-direction :row
:align-items :center
:justify-content :space-between
:padding-vertical 11})
(def asset-main
{:flex 1
:flex-direction :row
:align-items :center})
(def asset-icon
{:width 30
:height 30
:margin-left 14
:margin-right 12})
(def asset-name
{:color colors/gray
:padding-left 4})
(def asset-balance
{:color colors/gray
:padding-right 14})
(def asset-separator
{:height 1
:background-color colors/black-transparent
:margin-left 56})
(def command-send-status-container
{:margin-top 6
:flex-direction :row})
(defn command-send-status-icon
[outgoing]
{:background-color (if outgoing
colors/black-transparent
colors/blue-light)
:width 24
:height 24
:border-radius 16
:padding-top 4
:padding-left 4})
(defn command-send-status-text
[outgoing]
{:typography :caption
:color (if outgoing
colors/white-transparent
colors/blue)
:margin-top 4
:margin-left 6})
(def command-send-message-view
{:flex-direction :column
:align-items :flex-start})
(def command-send-amount-row
{:flex-direction :row
:justify-content :space-between})
(def command-send-amount
{:flex-direction :column
:align-items :flex-end
:max-width 250})
(defn command-send-amount-text
[outgoing]
{:font-size 22
:line-height 28
:font-weight "600"
:color (if outgoing colors/white colors/blue)})
(def command-send-currency
{:flex-direction :column
:align-items :flex-end})
(defn command-amount-currency-separator [outgoing]
{:opacity 0
:color (if outgoing colors/blue colors/blue-light)})
(defn command-send-currency-text [outgoing]
{:font-size 22
:margin-left 4
:color (if outgoing colors/white-transparent colors/gray)})
(defn command-request-currency-text [outgoing]
{:font-size 22
:color (if outgoing colors/white-transparent colors/gray)})
(defn command-request-timestamp-text [outgoing]
{:font-size 12
:color (if outgoing colors/white-transparent colors/gray)})
(def command-send-fiat-amount
{:flex-direction :column
:justify-content :flex-end
:margin-top 6})
(defn command-send-fiat-amount-text [outgoing]
{:typography :caption
:color (if outgoing colors/white colors/black)})
(def command-send-recipient-text
{:color colors/blue
:font-size 14})
(defn command-send-timestamp [outgoing]
{:color (if outgoing colors/white-transparent colors/gray)
:margin-top 6
:font-size 12})
(def command-request-image-touchable
{:position :absolute
:top 0
:right -8
:align-items :center
:justify-content :center
:width 48
:height 48})
(defn command-request-image-view [command scale]
{:width 32
:height 32
:border-radius 16
:background-color (:color command)
:transform [{:scale scale}]})
(def command-request-image
{:position :absolute
:top 9
:left 10
:width 12
:height 13})
(defn command-request-message-view [outgoing]
{:border-radius 14
:padding-vertical 4
:background-color (if outgoing colors/blue colors/blue-light)})
(defn command-request-header-text [outgoing]
{:font-size 12
:color (if outgoing colors/white-transparent colors/gray)})
(def command-request-row
{:flex-direction :row
:margin-top 6})
(defn command-request-amount-text [outgoing]
{:font-size 22
:color (if outgoing colors/white colors/black)})
(def command-request-separator-line
{:background-color colors/black-transparent
:height 1
:border-radius 8
:margin-top 10})
(def command-request-button
{:align-items :center
:padding-top 8})
(defn command-request-button-text [answered?]
{:color (if answered? colors/gray colors/blue)})
(def command-request-fiat-amount-row
{:margin-top 6})
(defn command-request-fiat-amount-text [outgoing]
{:font-size 12
:color (if outgoing colors/white colors/black)})
(def command-request-recipient-text
{:color colors/blue
:font-size 14})
(def command-request-network-text
{:color colors/red})
(def command-request-timestamp-row
{:margin-top 6})

View File

@ -1,177 +0,0 @@
(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]
[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 (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)
(even? wrapping-count))
param-index
(dec param-index))))))
(defn- command-completion [input-params params]
(let [input-params-count (count input-params)
params-count (count params)]
(cond
(= input-params-count params-count) :complete
(< input-params-count params-count) :less-then-needed
(> input-params-count params-count) :more-than-needed)))
(defn selected-chat-command
"Takes input text, text-selection and `protocol/id->command-props` map (result of
the `chat-commands` fn) and returns the corresponding `command-props` entry,
or nothing if input text doesn't match any available command.
Besides keys `:params` and `:type`, the returned map contains:
* `:input-params` - parsed parameters from the input text as map of `param-id->entered-value`
# `:current-param-position` - index of the parameter the user is currently focused on (cursor position
in relation to parameters), could be nil if the input is not selected
# `: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 (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 {}
(keep-indexed (fn [idx input-value]
(when (not (string/blank? input-value))
(when-let [param-name (get-in params [idx :id])]
[param-name input-value]))))
input-params)]
(assoc command-props
:input-params input-params
:current-param-position (current-param-position input-text text-selection)
:cursor-in-the-end? (= (count 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])
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
(join-command-args new-params))
(and (not last-param?)
(= param-index param-count))
(str chat.constants/spacing-char))]
{:db (-> db
(chat/set-chat-ui-props {:validation-messages nil})
(assoc-in [:chats current-chat-id :input-text] input-text))}))
(fx/defn select-chat-input-command
"Takes command and (optional) map of input-parameters map and sets it as current-chat input"
[{:keys [db]} {:keys [type params]} input-params]
(let [{:keys [current-chat-id chat-ui-props]} db]
{:db (-> db
(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
(join-command-args input-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)}))
(fx/defn clean-custom-params
[{:keys [db] :as cofx}]
(let [current-chat-id (:current-chat-id db)]
{:db (assoc-in db [:chats current-chat-id :custom-params] nil)}))

View File

@ -1,70 +0,0 @@
(ns status-im.chat.commands.protocol)
(def or-scopes
"Scope contexts representing OR choices"
[#{:personal-chats :group-chats :public-chats}])
(defprotocol Command
"Protocol for defining command message behaviour"
(id [this] "Identifier of the command, used to look-up command display name as well")
(scope [this]
"Scope of the command, defined as set of values representing contexts
where in which command is available, together with `id` it forms unique
identifier for each command.
Available values for the set are:
`id-of-the-any-chat` - command if available only for the specified chat
`:personal-chats` - command is available for any personal 1-1 chat
`:group-chats` - command is available for any group chat
`:public-chats` - command is available for any public chat ")
(description [this] "Description of the command")
(parameters [this]
"Ordered sequence of command parameter templates, where each parameter
is defined as map consisting of mandatory `:id`, `:title` and `:type` keys,
and optional `:suggestions` field.
When used, `:suggestions` contains reference to any generic helper component
rendering suggestions for the argument (input code will handle when and where
to render it)")
(validate [this parameters cofx]
"Function validating the parameters once command is send. Takes parameters map
and `cofx` map as argument, returns either `nil` meaning that no errors were
found and command send workflow can proceed, or one/more errors to display.
Each error is represented by the map containing `:title` and `:description` keys.")
(on-send [this command-message cofx]
"Function which can provide any extra effects to be produced in addition to
normal message effects which happen whenever message is sent")
(on-receive [this command-message cofx]
"Function which can provide any extra effects to be produced in addition to
normal message effects which happen when particular command message is received")
(short-preview [this command-message]
"Function rendering the short-preview of the command message, used when
displaying the last message in list of chats on home tab.
There is no argument names `parameters` anymore, as the message object
contains everything needed for short-preview/preview to render.")
(preview [this command-message]
"Function rendering preview of the command message in message stream"))
(defprotocol Yielding
"Protocol for defining commands yielding control back to application during the send flow"
(yield-control [this parameters cofx]
"Function, which steps out of the normal command workflow (`validate-and-send`)
and yields control back to application before sending.
Useful for cases where we want to use command input handling (parameters) and/or
validating, but we don't want to send message before yielding control elsewhere."))
(defprotocol EnhancedParameters
"Protocol for command messages which wish to modify/inject additional data into parameters,
other then those collected from the chat input.
Good example would be the `/send` and `/request` commands - we would like to indicate
network selected in sender's device, but we of course don't want to force user to type
it in when it could be effortlessly looked up from context.
Another usage would be for example command where one of the input parameters will be
hashed after validation and we would want to avoid the original unhashed parameter
to be ever saved on the sender device, nor to be sent over the wire.
For maximal flexibility, parameters can be enhanced both on the sending side and receiving
side, as sometimes thing needs to be added/enhanced in parameters map which are depending
on the receiver context - as for example calculated fiat price values for the `/request`
command"
(enhance-send-parameters [this parameters cofx]
"Function which takes original parameters + cofx map and returns new map of parameters")
(enhance-receive-parameters [this parameters cofx]
"Function which takes original parameters + cofx map and returns new map of parameters"))

View File

@ -1,31 +0,0 @@
(ns status-im.chat.commands.receiving
(:require [status-im.chat.commands.protocol :as protocol]
[status-im.chat.commands.core :as commands]
[status-im.utils.fx :as fx]))
(defn lookup-command-by-ref
"Function which takes message object and looks up associated entry from the
`id->command` map if it can be found"
[{:keys [content]} id->command]
(get id->command (:command-path content)))
(fx/defn receive
"Performs receive effects for command message. Does nothing
when message is not of the command type or command can't be found."
[{:keys [db] :as cofx} message]
(let [id->command (:id->command db)]
(when-let [{:keys [type]} (lookup-command-by-ref message id->command)]
(protocol/on-receive type (commands/enrich-command-message-for-events db message) cofx))))
(defn enhance-receive-parameters
"Enhances parameters for the received command message.
If the message is not of the command type, or command doesn't implement the
`EnhancedParameters` protocol, returns unaltered message, otherwise updates
its parameters."
[{:keys [content] :as message} {:keys [db] :as cofx}]
(let [id->command (:id->command db)
{:keys [type]} (lookup-command-by-ref message id->command)]
(if-let [new-params (and (satisfies? protocol/EnhancedParameters type)
(protocol/enhance-receive-parameters type (:params content) cofx))]
(assoc-in message [:content :params] new-params)
message)))

View File

@ -1,51 +0,0 @@
(ns status-im.chat.commands.sending
(: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]
[status-im.chat.models.message :as chat.message]
[status-im.utils.fx :as fx]))
(defn- create-command-message
"Create message map from chat-id, command & input parameters"
[chat-id type parameter-map cofx]
(let [command-path (commands/command-id type)
new-parameter-map (and (satisfies? protocol/EnhancedParameters type)
(protocol/enhance-send-parameters type parameter-map cofx))
params (or new-parameter-map parameter-map)]
{:chat-id chat-id
:content-type constants/content-type-command
:content {:chat-id chat-id
:command-path command-path
:params params}}))
(fx/defn validate-and-send
"Validates and sends command in current chat"
[{:keys [db] :as cofx} input-text {:keys [type params]} custom-params]
(let [chat-id (:current-chat-id db)
parameter-map (merge (commands.input/parse-parameters params input-text) custom-params)]
(if-let [validation-error (protocol/validate type parameter-map cofx)]
;; errors during validation
{:db (chat/set-chat-ui-props db {:validation-messages validation-error})}
;; no errors
(let [command-message (commands/enrich-command-message-for-events db (create-command-message chat-id type parameter-map cofx))]
(if (satisfies? protocol/Yielding type)
;; yield control implemented, don't send the message
(protocol/yield-control type command-message cofx)
;; no yield control, proceed with sending the command message
(fx/merge cofx
#(protocol/on-send type command-message %)
(commands.input/set-command-reference nil)
(commands.input/clean-custom-params)
(chat.message/send-message command-message)))))))
(fx/defn send
"Sends command with given parameters in particular chat"
[{:keys [db] :as cofx} chat-id {:keys [type]} parameter-map]
(let [command-message (create-command-message chat-id type parameter-map cofx)]
(fx/merge cofx
#(protocol/on-send type (commands/enrich-command-message-for-events db command-message) %)
(commands.input/set-command-reference nil)
(commands.input/clean-custom-params)
(chat.message/send-message command-message))))

View File

@ -1,5 +0,0 @@
(ns status-im.chat.commands.specs
(:require [cljs.spec.alpha :as spec]))
(spec/def :chat/id->command (spec/nilable map?))
(spec/def :chat/access-scope->command-id (spec/nilable map?))

View File

@ -1,8 +1,6 @@
(ns status-im.chat.db
(:require [clojure.set :as clojure.set]
[clojure.string :as string]
[status-im.chat.commands.core :as commands]
[status-im.chat.commands.input :as commands.input]
[status-im.multiaccounts.core :as multiaccounts]
[status-im.contact.db :as contact.db]
[status-im.group-chats.db :as group-chats.db]
@ -179,11 +177,3 @@
(def map->sorted-seq
(comp (partial map second) (partial sort-by first)))
(defn available-commands
[commands {:keys [input-text]}]
(->> commands
map->sorted-seq
(filter (fn [{:keys [type]}]
(when (commands.input/starts-as-command? input-text)
(string/includes? (commands/command-name type) input-text))))))

View File

@ -233,8 +233,7 @@
"Takes chat-id and coeffects map, returns effects necessary when navigating to chat"
[{:keys [db] :as cofx} chat-id]
(fx/merge cofx
{:db (-> (assoc db :current-chat-id chat-id)
(set-chat-ui-props {:validation-messages nil}))}
{:db (assoc db :current-chat-id chat-id)}
;; Group chat don't need this to load as all the loading of topics
;; happens on membership changes
(when-not (group-chat? cofx chat-id)

View File

@ -2,9 +2,6 @@
(:require [clojure.string :as string]
[goog.object :as object]
[re-frame.core :as re-frame]
[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.chat.constants :as chat.constants]
[status-im.chat.models :as chat]
[status-im.chat.models.message-content :as message-content]
@ -30,8 +27,7 @@
"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/set-chat-ui-props db {:validation-messages nil})
(assoc-in [:chats current-chat-id :input-text] (text->emoji new-input)))})
{:db (assoc-in db [:chats current-chat-id :input-text] (text->emoji new-input))})
(defn- start-cooldown [{:keys [db]} cooldowns]
{:dispatch-later [{:dispatch [:chat/disable-cooldown]
@ -71,21 +67,6 @@
(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]
@ -103,24 +84,8 @@
{: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"
[cofx input-text command custom-params]
(fx/merge cofx
(commands.sending/validate-and-send input-text command custom-params)
(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"
"when not empty, proceed by sending text message"
[input-text current-chat-id {:keys [db] :as cofx}]
(when-not (string/blank? input-text)
(let [{:keys [message-id]}
@ -141,7 +106,6 @@
(assoc :response-to message-id)
preferred-name
(assoc :name preferred-name))})
(commands.input/set-command-reference nil)
(set-chat-input-text nil)
(process-cooldown)))))
@ -157,23 +121,16 @@
(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 [{:keys [input-text custom-params]} (get-in db [:chats current-chat-id])
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 cofx input-text command custom-params)
(command-not-complete-fx input-text current-chat-id cofx))
(plain-text-message-fx input-text current-chat-id cofx))))
[{{:keys [current-chat-id] :as db} :db :as cofx}]
(let [{:keys [input-text]} (get-in db [:chats current-chat-id])]
(plain-text-message-fx input-text current-chat-id cofx)))
(fx/defn send-transaction-result
{:events [:chat/send-transaction-result]}
[cofx chat-id params result]
(commands.sending/send cofx chat-id (get-in cofx [:db :id->command ["send" #{:personal-chats}]]) (assoc params :tx-hash result)))
;;TODO: should be implemented on status-go side
;;see https://github.com/status-im/team-core/blob/6c3d67d8e8bd8500abe52dab06a59e976ec942d2/rfc-001.md#status-gostatus-react-interface
)
;; effects

View File

@ -3,7 +3,6 @@
[status-im.constants :as constants]
[status-im.data-store.chats :as data-store.chats]
[status-im.data-store.messages :as data-store.messages]
[status-im.chat.commands.core :as commands]
[status-im.transport.filters.core :as filters]
[status-im.chat.models :as chat-model]
[status-im.ethereum.json-rpc :as json-rpc]
@ -30,8 +29,7 @@
(fx/merge cofx
{:db (assoc db :chats chats
:chats/loading? false)}
(filters/load-filters)
(commands/load-commands commands/register))))
(filters/load-filters))))
(fx/defn initialize-chats
"Initialize persisted chats on startup"

View File

@ -1,7 +1,6 @@
(ns status-im.chat.models.message
(:require [re-frame.core :as re-frame]
[status-im.multiaccounts.model :as multiaccounts.model]
[status-im.chat.commands.receiving :as commands-receiving]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.chat.db :as chat.db]
[status-im.chat.models :as chat-model]
@ -120,20 +119,16 @@
(fx/defn add-received-message
[{:keys [db] :as cofx}
{:keys [from message-id chat-id content metadata] :as raw-message}]
{:keys [from message-id chat-id content metadata] :as message}]
(let [{:keys [current-chat-id view-id]} db
current-public-key (multiaccounts.model/current-public-key cofx)
current-chat? (and (or (= :chat view-id)
(= :chat-modal view-id))
(= current-chat-id chat-id))
message (-> raw-message
(commands-receiving/enhance-receive-parameters cofx))]
(fx/merge cofx
(add-message {:batch? true
:message message
:metadata metadata
:current-chat? current-chat?})
(commands-receiving/receive message))))
(= current-chat-id chat-id))]
(add-message cofx {:batch? true
:message message
:metadata metadata
:current-chat? current-chat?})))
(defn- add-to-chat?
[{:keys [db]} {:keys [chat-id clock-value message-id from]}]

View File

@ -1,6 +1,5 @@
(ns status-im.chat.specs
(:require [cljs.spec.alpha :as s]
status-im.chat.commands.specs))
(:require [cljs.spec.alpha :as s]))
(s/def :chat/chats (s/nilable map?)) ; {id (string) chat (map)} active chats on chat's tab
(s/def :chat/current-chat-id (s/nilable string?)) ; current or last opened chat-id

View File

@ -10,8 +10,6 @@
(def content-type-text "text/plain")
(def content-type-sticker "sticker")
(def content-type-status "status")
(def content-type-command "command")
(def content-type-command-request "command-request")
(def content-type-emoji "emoji")
(def desktop-content-types

View File

@ -13,8 +13,6 @@
[status-im.bootnodes.core :as bootnodes]
[status-im.browser.core :as browser]
[status-im.browser.permissions :as browser.permissions]
[status-im.chat.commands.core :as commands]
[status-im.chat.commands.input :as commands.input]
[status-im.chat.db :as chat.db]
[status-im.chat.models :as chat]
[status-im.chat.models.input :as chat.input]
@ -66,7 +64,6 @@
[status-im.wallet.custom-tokens.core :as custom-tokens]
[status-im.wallet.db :as wallet.db]
[taoensso.timbre :as log]
[status-im.chat.commands.sending :as commands.sending]
[status-im.utils.money :as money]
status-im.popover.core))
@ -572,16 +569,6 @@
(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 _]
@ -597,20 +584,6 @@
(fn [cofx _]
(chat.input/send-current-message cofx)))
(handlers/register-handler-fx
:chat.ui/set-command-parameter
(fn [{{:keys [chats current-chat-id chat-ui-props id->command access-scope->command-id]} :db :as cofx} [_ value]]
(let [current-chat (get chats current-chat-id)
selection (get-in chat-ui-props [current-chat-id :selection])
commands (commands/chat-commands id->command access-scope->command-id current-chat)
{:keys [current-param-position params]} (commands.input/selected-chat-command
(:input-text current-chat) selection commands)
last-param-idx (dec (count params))]
(commands.input/set-command-parameter cofx
(= current-param-position last-param-idx)
current-param-position
value))))
(defn- mark-messages-seen
[{:keys [db] :as cofx}]
(let [{:keys [current-chat-id]} db]
@ -1589,7 +1562,8 @@
(fx/merge cofx
(navigation/navigate-back)
(chat/start-chat public-key nil)
(commands.sending/send public-key
request-command
{:asset (name symbol)
:amount (str (money/internal->formatted amount symbol decimals))})))))
;; TODO send
#_(commands.sending/send public-key
request-command
{:asset (name symbol)
:amount (str (money/internal->formatted amount symbol decimals))})))))

View File

@ -4,8 +4,6 @@
[taoensso.timbre :as log]
[re-frame.core :as re-frame]
[status-im.browser.core :as browser]
[status-im.chat.commands.core :as commands]
[status-im.chat.commands.input :as commands.input]
[status-im.chat.constants :as chat.constants]
[status-im.chat.db :as chat.db]
[status-im.chat.models :as chat.models]
@ -119,9 +117,7 @@
;;chat
(reg-root-key-sub ::cooldown-enabled? :chat/cooldown-enabled?)
(reg-root-key-sub ::chats :chats)
(reg-root-key-sub ::access-scope->command-id :access-scope->command-id)
(reg-root-key-sub ::chat-ui-props :chat-ui-props)
(reg-root-key-sub :chats/id->command :id->command)
(reg-root-key-sub :chats/current-chat-id :current-chat-id)
(reg-root-key-sub :public-group-topic :public-group-topic)
(reg-root-key-sub :chats/loading? :chats/loading?)
@ -523,31 +519,6 @@
(fn [collectibles [_ {:keys [symbol token]}]]
(get-in collectibles [(keyword symbol) (js/parseInt token)])))
(re-frame/reg-sub
::show-suggestions-view?
:<- [:chats/current-chat-ui-prop :show-suggestions?]
:<- [:chats/current-chat-input-text]
:<- [:chats/all-available-commands]
(fn [[show-suggestions? input-text commands]]
(and (or show-suggestions?
(commands.input/starts-as-command? (string/trim (or input-text ""))))
(seq commands))))
(re-frame/reg-sub
::show-suggestions?
:<- [::show-suggestions-view?]
:<- [:chats/selected-chat-command]
(fn [[show-suggestions-box? selected-command]]
(and show-suggestions-box? (not selected-command))))
(re-frame/reg-sub
::get-commands-for-chat
:<- [:chats/id->command]
:<- [::access-scope->command-id]
:<- [:chats/current-raw-chat]
(fn [[id->command access-scope->command-id chat]]
(commands/chat-commands id->command access-scope->command-id chat)))
(re-frame/reg-sub
:chats/chat
:<- [:chats/active-chats]
@ -593,12 +564,6 @@
(fn [ui-props [_ prop]]
(get ui-props prop)))
(re-frame/reg-sub
:chats/validation-messages
:<- [:chats/current-chat-ui-props]
(fn [ui-props]
(some-> ui-props :validation-messages)))
(re-frame/reg-sub
:chats/input-margin
:<- [:keyboard-height]
@ -817,55 +782,6 @@
:empty
:messages))))
(re-frame/reg-sub
:chats/available-commands
:<- [::get-commands-for-chat]
:<- [:chats/current-chat]
(fn [[commands chat]]
(chat.db/available-commands commands chat)))
(re-frame/reg-sub
:chats/all-available-commands
:<- [::get-commands-for-chat]
(fn [commands]
(chat.db/map->sorted-seq commands)))
(re-frame/reg-sub
:chats/selected-chat-command
:<- [:chats/current-chat-input-text]
:<- [:chats/current-chat-ui-prop :selection]
:<- [::get-commands-for-chat]
(fn [[input-text selection commands]]
(commands.input/selected-chat-command input-text selection commands)))
(re-frame/reg-sub
:chats/input-placeholder
:<- [:chats/current-chat]
:<- [:chats/selected-chat-command]
(fn [[{:keys [input-text]} {:keys [params current-param-position cursor-in-the-end?]}]]
(when (and cursor-in-the-end? (string/ends-with? (or input-text "") chat.constants/spacing-char))
(get-in params [current-param-position :placeholder]))))
(re-frame/reg-sub
:chats/parameter-box
:<- [:chats/current-chat]
:<- [:chats/selected-chat-command]
(fn [[_ {:keys [current-param-position params]}]]
(when (and params current-param-position)
(get-in params [current-param-position :suggestions]))))
(re-frame/reg-sub
:chats/show-parameter-box?
:<- [:chats/parameter-box]
:<- [::show-suggestions?]
:<- [:chats/validation-messages]
:<- [:chats/selected-chat-command]
(fn [[chat-parameter-box show-suggestions? validation-messages {:keys [command-completion]}]]
(and chat-parameter-box
(not validation-messages)
(not show-suggestions?)
(not (= :complete command-completion)))))
(re-frame/reg-sub
:chats/unviewed-messages-count
(fn [[_ chat-id]]

View File

@ -53,14 +53,13 @@
(spec/def :message.content/text (spec/and string? (complement s/blank?)))
(spec/def :message.content/response-to string?)
(spec/def :message.content/command-path (spec/tuple string? (spec/coll-of (spec/or :scope keyword? :chat-id string?) :kind set? :min-count 1)))
(spec/def :message.content/uri (spec/and string? (complement s/blank?)))
(spec/def :message.content/pack (spec/and string? (complement s/blank?)))
(spec/def :message.content/params (spec/map-of keyword? any?))
(spec/def ::content-type #{constants/content-type-text constants/content-type-command
(spec/def ::content-type #{constants/content-type-text
constants/content-type-emoji
constants/content-type-command-request constants/content-type-sticker})
constants/content-type-sticker})
(spec/def ::message-type #{:group-user-message :public-group-user-message :user-message})
(spec/def ::clock-value (spec/and pos-int?
utils.clocks/safe-timestamp?))
@ -91,20 +90,11 @@
(spec/def :message/message-common (spec/keys :req-un [::content-type ::message-type ::clock-value ::timestamp]))
(spec/def :message.text/content (spec/keys :req-un [:message.content/text]
:req-opt [:message.content/response-to]))
(spec/def :message.command/content (spec/keys :req-un [:message.content/command-path :message.content/params]))
(spec/def :message.sticker/content (spec/keys :req-un [:message.content/hash]))
(defmulti content-type :content-type)
(defmethod content-type constants/content-type-command [_]
(spec/merge :message/message-common
(spec/keys :req-un [:message.command/content])))
(defmethod content-type constants/content-type-command-request [_]
(spec/merge :message/message-common
(spec/keys :req-un [:message.command/content])))
(defmethod content-type constants/content-type-sticker [_]
(spec/merge :message/message-common
(spec/keys :req-un [:message.sticker/content])))

View File

@ -40,24 +40,6 @@
(rep [this {:keys [name profile-image address]}]
#js [name profile-image address nil nil]))
;; It's necessary to support old clients understanding only older, verbose command content (`release/0.9.25` and older)
(defn- new->legacy-command-data [{:keys [command-path params] :as content}]
(get {["send" #{:personal-chats}] [{:command-ref ["transactor" :command 83 "send"]
:command "send"
:bot "transactor"
:command-scope-bitmask 83}
constants/content-type-command]
["request" #{:personal-chats}] [{:command-ref ["transactor" :command 83 "request"]
:request-command-ref ["transactor" :command 83 "send"]
:command "request"
:request-command "send"
:bot "transactor"
:command-scope-bitmask 83
:prefill [(get params :asset)
(get params :amount)]}
constants/content-type-command-request]}
command-path))
(deftype MessageHandler []
Object
(tag [this v] "c4")
@ -65,9 +47,6 @@
(condp = content-type
constants/content-type-text ;; append new content add the end, still pass content the old way at the old index
#js [(:text content) content-type message-type clock-value timestamp content]
constants/content-type-command ;; handle command compatibility issues
(let [[legacy-content legacy-content-type] (new->legacy-command-data content)]
#js [(merge content legacy-content) (or legacy-content-type content-type) message-type clock-value timestamp])
;; no need for legacy conversions for rest of the content types
#js [content content-type message-type clock-value timestamp])))
@ -103,17 +82,6 @@
;; Reader handlers
;;
(def ^:private legacy-ref->new-path
{["transactor" :command 83 "send"] ["send" #{:personal-chats}]
["transactor" :command 83 "request"] ["request" #{:personal-chats}]})
(defn- legacy->new-command-content [{:keys [command-path command-ref] :as content}]
(if command-path
;; `:command-path` set, message produced by newer app version, nothing to do
content
;; we have to look up `:command-path` based on legacy `:command-ref` value (`release/0.9.25` and older) and assoc it to content
(assoc content :command-path (get legacy-ref->new-path command-ref))))
(defn- legacy->new-message-data [content content-type]
;; handling only the text content case
(cond
@ -123,9 +91,6 @@
[content content-type]
;; create safe `{:text string-content}` value from anything else
[{:text (str content)} content-type])
(or (= content-type constants/content-type-command)
(= content-type constants/content-type-command-request))
[(legacy->new-command-content content) constants/content-type-command]
:else
[content content-type]))

View File

@ -5,10 +5,7 @@
[re-frame.core :as re-frame]
[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.ui.screens.chat.utils :as chat-utils]
[status-im.i18n :as i18n]
@ -100,24 +97,6 @@
(set-layout-width-fn w))}
(or input-text "")]))
(defn- input-helper-view-on-update [{:keys [opacity-value placeholder]}]
(fn [_]
(let [to-value (if @placeholder 1 0)]
(animation/start
(animation/timing opacity-value {:toValue to-value
:duration 300
:useNativeDriver true})))))
(defview input-helper [{:keys [width]}]
(letsubs [placeholder [:chats/input-placeholder]
opacity-value (animation/create-value 0)
on-update (input-helper-view-on-update {:opacity-value opacity-value
:placeholder placeholder})]
{:component-did-update on-update}
[react/animated-view {:style (style/input-helper-view width opacity-value)}
[react/text {:style (style/input-helper-text width)}
placeholder]]))
(defn get-options [type]
(case (keyword type)
:phone {:keyboard-type "phone-pad"}
@ -126,33 +105,20 @@
nil))
(defview input-view [{:keys [single-line-input? set-text state-text]}]
(letsubs [command [:chats/selected-chat-command]]
(let [component (reagent/current-component)
set-layout-width-fn #(reagent/set-state component {:width %})
set-container-width-fn #(reagent/set-state component {:container-width %})
{:keys [width]} (reagent/state component)]
[react/view {:style style/input-root}
[react/animated-view {:style style/input-animated}
[invisible-input {:set-layout-width-fn set-layout-width-fn}]
(if platform/desktop?
[basic-text-input-desktop {:set-container-width-fn set-container-width-fn
:single-line-input? single-line-input?
:set-text set-text
:state-text state-text}]
[basic-text-input {:set-container-width-fn set-container-width-fn
:single-line-input? single-line-input?}])
[input-helper {:width width}]]])))
(defview commands-button []
(letsubs [commands [:chats/all-available-commands]
reply-message [:chats/reply-message]]
(when (and (not reply-message) (seq commands))
[react/touchable-highlight
{:on-press #(re-frame/dispatch [:chat.ui/set-command-prefix])
:accessibility-label :chat-commands-button}
[react/view
[vector-icons/icon :main-icons/commands {:container-style style/input-commands-icon
:color colors/gray}]]])))
(let [component (reagent/current-component)
set-layout-width-fn #(reagent/set-state component {:width %})
set-container-width-fn #(reagent/set-state component {:container-width %})
{:keys [width]} (reagent/state component)]
[react/view {:style style/input-root}
[react/animated-view {:style style/input-animated}
[invisible-input {:set-layout-width-fn set-layout-width-fn}]
(if platform/desktop?
[basic-text-input-desktop {:set-container-width-fn set-container-width-fn
:single-line-input? single-line-input?
:set-text set-text
:state-text state-text}]
[basic-text-input {:set-container-width-fn set-container-width-fn
:single-line-input? single-line-input?}])]]))
(defview reply-message [from alias message-text]
(letsubs [{:keys [ens-name]} [:contacts/contact-name-by-identity from]
@ -183,7 +149,7 @@
:height 19
:color colors/white}]]]]])))
(defview input-container []
(defview container []
(letsubs [margin [:chats/input-margin]
mainnet? [:mainnet?]
input-text [:chats/current-chat-input-text]
@ -221,10 +187,3 @@
(set-text ""))]
[send-button/send-button-view {:input-text input-text}
#(re-frame/dispatch [:chat.ui/send-current-message])]))]])))
(defn container []
[react/view
[parameter-box/parameter-box-view]
[suggestions/suggestions-view]
[validation-messages/validation-messages-view]
[input-container]])

View File

@ -1,18 +0,0 @@
(ns status-im.ui.screens.chat.input.parameter-box
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [status-im.ui.screens.chat.input.animations.expandable :as expandable]
[status-im.ui.screens.chat.styles.input.parameter-box :as style]
[status-im.ui.components.react :as react]))
(defview parameter-box-container []
(letsubs [parameter-box [:chats/parameter-box]]
(when parameter-box
[react/view
[parameter-box]])))
(defview parameter-box-view []
(letsubs [show-box? [:chats/show-parameter-box?]]
(when show-box?
[expandable/expandable-view {:key :parameter-box}
;; TODO need to add the whole payload (and details about previous parameters?)
[parameter-box-container]])))

View File

@ -9,17 +9,13 @@
(defn sendable? [input-text disconnected? login-processing?]
(let [trimmed (string/trim input-text)]
(not (or (string/blank? trimmed)
(= trimmed "/")
login-processing?
disconnected?))))
(defview send-button-view [{:keys [input-text]} on-send-press]
(letsubs [{:keys [command-completion]} [:chats/selected-chat-command]
disconnected? [:disconnected?]
(letsubs [disconnected? [:disconnected?]
{:keys [processing]} [:multiaccounts/login]]
(when (and (sendable? input-text disconnected? processing)
(or (not command-completion)
(#{:complete :less-than-needed} command-completion)))
(when (sendable? input-text disconnected? processing)
[react/touchable-highlight
{:on-press on-send-press}
[vector-icons/icon :main-icons/arrow-up

View File

@ -1,33 +0,0 @@
(ns status-im.ui.screens.chat.input.suggestions
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [re-frame.core :as re-frame]
[status-im.ui.components.react :as react]
[status-im.ui.screens.chat.styles.input.suggestions :as style]
[status-im.ui.screens.chat.input.animations.expandable :as expandable]
[status-im.chat.commands.core :as commands]))
(defn suggestion-item [{:keys [on-press name description last? accessibility-label]}]
[react/touchable-highlight (cond-> {:on-press on-press}
accessibility-label (assoc :accessibility-label accessibility-label))
[react/view style/item-suggestion-container
[react/text name]
[react/text {:style style/item-suggestion-description
:number-of-lines 2}
description]]])
(defview suggestions-view []
(letsubs [available-commands [:chats/available-commands]]
(when (seq available-commands)
[expandable/expandable-view {:key :suggestions}
[react/view
[react/scroll-view {:keyboard-should-persist-taps :always
:bounces false}
(map-indexed
(fn [i {:keys [type] :as command}]
^{:key i}
[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)))
:accessibility-label (commands/accessibility-label type)}])
available-commands)]]])))

View File

@ -1,29 +0,0 @@
(ns status-im.ui.screens.chat.input.validation-messages
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [re-frame.core :as re-frame]
[status-im.ui.components.react :as react]
[status-im.ui.screens.chat.styles.input.validation-message :as style]
[status-im.i18n :as i18n]))
(defn validation-message [{:keys [title description]}]
[react/view style/message-container
[react/text {:style style/message-title}
title]
[react/text {:style style/message-description}
description]])
(defn- messages-list [markup]
[react/view {:flex 1}
markup])
(defview validation-messages-view []
(letsubs [chat-input-margin [:chats/input-margin]
input-height [:chats/current-chat-ui-prop :input-height]
validation-result [:chats/validation-messages]]
(when validation-result
(let [message (if (string? validation-result)
{:title (i18n/label :t/error)
:description validation-result}
validation-result)]
[react/view (style/root (+ input-height chat-input-margin))
[messages-list [validation-message message]]]))))

View File

@ -1,6 +1,5 @@
(ns status-im.ui.screens.chat.message.message
(:require [re-frame.core :as re-frame]
[status-im.chat.commands.receiving :as commands-receiving]
[status-im.constants :as constants]
[status-im.utils.http :as http]
[status-im.i18n :as i18n]
@ -19,41 +18,35 @@
[status-im.utils.platform :as platform])
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
(defview message-content-command
[command-message]
(letsubs [id->command [:chats/id->command]
{:keys [contacts]} [:chats/current-chat]]
(let [{:keys [type] :as command} (commands-receiving/lookup-command-by-ref command-message id->command)]
;;TODO temporary disable commands for v1
[react/text (str "Unhandled command: " (-> command-message :content :command-path first))])))
(defview message-timestamp
[t justify-timestamp? outgoing command? content content-type]
(when-not command?
[react/text {:style (style/message-timestamp-text
justify-timestamp?
outgoing
(:rtl? content)
(= content-type constants/content-type-emoji))} t]))
(defn message-timestamp
[t justify-timestamp? outgoing content content-type]
[react/text {:style (style/message-timestamp-text
justify-timestamp?
outgoing
(:rtl? content)
(= content-type constants/content-type-emoji))} t])
(defn message-view
[{:keys [timestamp-str outgoing content content-type] :as message} message-content {:keys [justify-timestamp?]}]
[{:keys [timestamp-str outgoing content content-type] :as message}
message-content {:keys [justify-timestamp?]}]
[react/view (style/message-view message)
message-content
[message-timestamp timestamp-str justify-timestamp? outgoing (or (get content :command-path)
(get content :command-ref))
[message-timestamp timestamp-str justify-timestamp? outgoing
content content-type]])
(defview quoted-message [message-id {:keys [from text]} outgoing current-public-key]
(letsubs [{:keys [quote
ens-name
alias]}
(defview quoted-message
[message-id {:keys [from text]} outgoing current-public-key]
(letsubs [{:keys [quote ens-name alias]}
[:messages/quote-info message-id]]
(when (or quote text)
[react/view {:style (style/quoted-message-container outgoing)}
[react/view {:style style/quoted-message-author-container}
[vector-icons/tiny-icon :tiny-icons/tiny-reply {:color (if outgoing colors/white-transparent colors/gray)}]
(chat.utils/format-reply-author (or from (:from quote)) alias ens-name current-public-key (partial style/quoted-message-author outgoing))]
[vector-icons/tiny-icon :tiny-icons/tiny-reply
{:color (if outgoing colors/white-transparent colors/gray)}]
(chat.utils/format-reply-author
(or from (:from quote))
alias ens-name current-public-key
(partial style/quoted-message-author outgoing))]
[react/text {:style (style/quoted-message-text outgoing)
:number-of-lines 5}
@ -183,17 +176,6 @@
[wrapper message]
[wrapper message [message-content-status message]])
(defmethod message-content constants/content-type-command
[wrapper message]
[wrapper message
[message-view message [message-content-command message]]])
;; Todo remove after couple of releases
(defmethod message-content constants/content-type-command-request
[wrapper message]
[wrapper message
[message-view message [message-content-command message]]])
(defmethod message-content constants/content-type-emoji
[wrapper message]
[wrapper message [emoji-message message]])
@ -234,15 +216,6 @@
[react/view style/not-sent-icon
[vector-icons/icon :main-icons/warning {:color colors/red}]]]])
(defview command-status [{{:keys [network]} :params}]
(letsubs [current-network [:chain-name]]
(when (and network (not= current-network network))
[react/view style/not-sent-view
[react/text {:style style/not-sent-text}
(i18n/label :network-mismatch)]
[react/view style/not-sent-icon
[vector-icons/icon :main-icons/warning {:color colors/red}]]])))
(defn message-delivery-status
[{:keys [chat-id message-id outgoing-status
first-outgoing?
@ -255,9 +228,7 @@
[react/view style/delivery-view
[react/text {:style style/delivery-text}
(i18n/label :t/status-sent)]])
(when (and (not outgoing-status)
(:command content))
[command-status content]))))
nil)))
(defview message-author-name [from alias]
(letsubs [{:keys [ens-name]} [:contacts/contact-name-by-identity from]]

View File

@ -152,26 +152,8 @@
:height min-input-height
:left left}})
(def input-commands-icon
{:margin 14
:height 24
:width 24})
(def input-clear-container
{:width 24
:height 24
:margin-top 7
:align-items :center})
(def commands-root
{:flex-direction :row
:align-items :center})
(def command-list-icon-container
{:width 32
:height 32
:padding 4})
(def commands-list-icon
{:height 24
:width 24})

View File

@ -1,8 +0,0 @@
(ns status-im.ui.screens.chat.styles.input.parameter-box
(:require [status-im.ui.components.colors :as colors]))
(def root
{:background-color colors/white
:border-bottom-color colors/black-transparent
:border-bottom-width 1})

View File

@ -1,23 +0,0 @@
(ns status-im.ui.screens.chat.styles.input.suggestions
(:require [status-im.ui.components.colors :as colors]))
(def item-height 52)
(def border-height 1)
(def root
{:background-color colors/white
:border-top-color colors/black-transparent
:border-top-width 1})
(def item-suggestion-container
{:flex-direction :row
:align-items :center
:height item-height
:padding-horizontal 14
:border-top-color colors/black-transparent
:border-top-width border-height})
(def item-suggestion-description
{:flex 1
:margin-left 10
:color colors/gray})

View File

@ -1,23 +0,0 @@
(ns status-im.ui.screens.chat.styles.input.validation-message
(:require [status-im.ui.components.styles :as common]
[status-im.ui.components.colors :as colors]))
(defn root [bottom]
{:flex-direction :column
:left 0
:right 0
:bottom bottom
:position :absolute})
(def message-container
{:background-color colors/red
:padding 16})
(def message-title
{:color colors/white
:font-size 12})
(def message-description
{:color colors/white
:font-size 12
:opacity 0.9})

View File

@ -152,10 +152,7 @@
4)}
(if (= content-type constants/content-type-emoji)
{:flex-direction :row}
{:background-color (if outgoing colors/blue colors/blue-light)})
(when (= content-type constants/content-type-command)
{:padding-top 12
:padding-bottom 10})))
{:background-color (if outgoing colors/blue colors/blue-light)})))
(def play-image
{:width 33

View File

@ -3,6 +3,9 @@
[status-im.utils.fx :as fx]
[status-im.wallet.core :as wallet]))
(defn get-currency [db]
(or (get-in db [:multiaccount :settings :wallet :currency]) :usd))
(fx/defn set-currency
[{:keys [db] :as cofx} currency]
(let [settings (get-in db [:multiaccount :settings])

View File

@ -193,7 +193,6 @@
:contacts/click-action
:contacts/click-params
:pairing/installations
:commands/stored-command
:group/selected-contacts
:multiaccounts/multiaccounts
:multiaccounts/recover
@ -303,8 +302,6 @@
:chat/last-clock-value
:chat/loaded-chats
:chat/bot-db
:chat/id->command
:chat/access-scope->command-id
:ens/registration
:wallet/wallet
:prices/prices

View File

@ -27,16 +27,6 @@
:margin-right 16
:width 60})
(def message-command-container
{:align-self :flex-start
:border-radius 8
:border-color colors/black-transparent
:border-width 1
:padding-horizontal 12
:padding-vertical 10
:align-items :flex-start
:width 230})
(def author
{:font-weight "500"
:font-size 14})

View File

@ -161,17 +161,6 @@
(defmulti message (fn [_ _ {:keys [content-type]}] content-type))
(defmethod message constants/content-type-command
[_ _ {:keys [from] :as message}]
[react/view
[react/view {:style {:flex-direction :row :align-items :center :margin-top 15}}
[member-photo from]
[message-author-name message]]
[react/view {:style styles/not-first-in-group-wrapper}
[photo-placeholder]
[react/view {:style styles/message-command-container}
[message/message-content-command message]]]])
(defmethod message constants/content-type-sticker
[_ _ {:keys [content] :as message}]
[message-wrapper message

View File

@ -60,10 +60,8 @@
[react/text {:ellipsize-mode :tail
:number-of-lines 1
:style styles/chat-last-message}
(if (= constants/content-type-command (:content-type last-message))
[chat-item/command-short-preview last-message]
(or (:text last-message-content)
(i18n/label :no-messages-yet)))]))]
(or (:text last-message-content)
(i18n/label :no-messages-yet))]))]
[react/view {:style styles/timestamp}
[chat-item/message-timestamp (:timestamp last-message)]
(when (pos? unviewed-messages-count)

View File

@ -1,8 +1,6 @@
(ns status-im.ui.screens.home.views.inner-item
(:require [clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.chat.commands.core :as commands]
[status-im.chat.commands.receiving :as commands-receiving]
[status-im.constants :as constants]
[status-im.i18n :as i18n]
[status-im.ui.components.chat-icon.screen :as chat-icon.screen]
@ -16,12 +14,6 @@
[status-im.utils.datetime :as time])
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
(defview command-short-preview [message]
(letsubs [id->command [:chats/id->command]
{:keys [contacts]} [:chats/current-chat]]
(when-let [command (commands-receiving/lookup-command-by-ref message id->command)]
(commands/generate-short-preview command (commands/add-chat-contacts contacts message)))))
(defn message-content-text [{:keys [content content-type] :as message}]
[react/view styles/last-message-container
(cond
@ -31,9 +23,6 @@
:accessibility-label :no-messages-text}
(i18n/label :t/no-messages)]
(= constants/content-type-command content-type)
[command-short-preview message]
(= constants/content-type-sticker content-type)
[react/image {:style {:margin 1 :width 20 :height 20}
:source {:uri (contenthash/url (:hash content))}}]

View File

@ -65,16 +65,16 @@
:title :t/recover-with-keycard
:disabled? (not config/hardwallet-enabled?)
:accessibility-label :recover-with-keycard-button
:icon [react/view {:border-width 1
:border-radius 20
:border-color colors/blue-light
:background-color colors/blue-light
:justify-content :center
:align-items :center
:width 40
:height 40}
[react/image {:source (resources/get-image :keycard-logo-blue)
:style {:width 24 :height 24}}]]
:icon [react/view {:border-width 1
:border-radius 20
:border-color colors/blue-light
:background-color colors/blue-light
:justify-content :center
:align-items :center
:width 40
:height 40}
[react/image {:source (resources/get-image :keycard-logo-blue)
:style {:width 24 :height 24}}]]
:on-press #(re-frame/dispatch [::hardwallet/recover-with-keycard-pressed])}])]])
(defn bottom-sheet []

View File

@ -5,7 +5,6 @@
[status-im.ui.screens.profile.navigation]
[status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im.chat.models :as chat-models]
[status-im.chat.commands.input :as commands-input]
[status-im.utils.image-processing :as image-processing]
[taoensso.timbre :as log]
[status-im.utils.fx :as fx]))
@ -23,10 +22,8 @@
"photo"))
(defn send-transaction [chat-id {:keys [db] :as cofx}]
(let [send-command (get-in db [:id->command ["send" #{:personal-chats}]])]
(fx/merge cofx
(chat-models/start-chat chat-id {:navigation-reset? true})
(commands-input/select-chat-input-command send-command nil))))
;;TODO start send transaction command flow
(chat-models/start-chat chat-id {:navigation-reset? true}))
(defn- valid-name? [name]
(spec/valid? :profile/name name))

View File

@ -63,7 +63,10 @@
[chat-icon/custom-icon-view-list (:name token) color 32])]}]
[separator]]))
(defn header [{:keys [in-progress?] :as sign} {:keys [contact amount token approve?] :as tx} display-symbol fee fee-display-symbol]
(defn header
[{:keys [in-progress?] :as sign}
{:keys [contact amount token approve?] :as tx}
display-symbol fee fee-display-symbol]
[react/view styles/header
(when sign
[react/touchable-highlight (when-not in-progress? {:on-press #(re-frame/dispatch [:set :signing/sign nil])})

View File

@ -12,6 +12,7 @@
[re-frame.core :as re-frame]
[taoensso.timbre :as log]
[status-im.utils.platform :as platform]
[status-im.ui.screens.wallet.send.views :as wallet.send]
[status-im.ui.screens.mobile-network-settings.view :as mobile-network-settings]
[status-im.ui.screens.keycard.views :as keycard]
[status-im.ui.screens.home.sheet.views :as home.sheet]

View File

@ -33,13 +33,4 @@
[button/button
{:on-press #(re-frame/dispatch [:wallet.accounts/share address])
:label :t/share-address
:accessibility-label :share-address-button}]]
;;TODO temporary hide for v1
#_[button/button
{:on-press
#(do
(re-frame/dispatch [:hide-popover])
(re-frame/dispatch [:navigate-to :wallet-send-transaction-request address]))
:accessibility-label :sent-transaction-request-button
:label :t/send-transaction-request
:type :secondary}]]))
:accessibility-label :share-address-button}]]]))

View File

@ -1,140 +0,0 @@
(ns status-im.test.chat.commands.core
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.chat.commands.core :as core]
[status-im.chat.commands.protocol :as protocol]))
(defn- fake-suggestion
[selected-event-creator value]
(selected-event-creator value))
(def test-command-parameters
[{:id :first-param
:type :text
;; pass function as mock-up for suggestions component, so we can
;; just test the correct injection of `:set-command-parameter` event
:suggestions fake-suggestion}
{:id :second-param
:type :text}
{:id :last-param
:type :text
:suggestions fake-suggestion}])
(deftype TestCommand []
protocol/Command
(id [_] "test-command")
(scope [_] #{:personal-chats :group-chats :public-chats})
(description [_] "Another test command")
(parameters [_] test-command-parameters)
(validate [_ parameters _]
(when-not (every? (comp string? second) parameters)
"Not all parameters are filled and of the correct type"))
(on-send [_ _ _])
(on-receive [_ _ _])
(short-preview [_ command-message]
[:text (str "Test-command, first-param: "
(get-in command-message [:content :params :first-param]))])
(preview [_ command-message]
[:text (str "Test-command, params: "
(apply str (map [:first-param :second-param :last-param]
(get-in command-message [:content :params]))))]))
(def another-test-command-parameters
[{:id :first-param
:type :text}])
(deftype AnotherTestCommand []
protocol/Command
(id [_] "another-test-command")
(scope [_] #{:public-chats})
(description [_] "Another test command")
(parameters [_] another-test-command-parameters)
(validate [_ parameters _]
(when-not (every? (comp string? second) parameters)
"Not all parameters are filled and of the correct type"))
(on-send [_ _ _])
(on-receive [_ _ _])
(short-preview [_ command-message]
[:text (str "Test-command, first-param: "
(get-in command-message [:content :params :first-param]))])
(preview [_ command-message]
[:text (str "Test-command, params: "
(apply str (map [:first-param]
(get-in command-message [:content :params]))))]))
(def TestCommandInstance (TestCommand.))
(def AnotherTestCommandInstance (AnotherTestCommand.))
(deftest load-commands-test
(let [fx (core/load-commands {:db {}} #{TestCommandInstance AnotherTestCommandInstance})]
(testing "Primary composite key index for command is correctly created"
(is (= TestCommandInstance
(get-in fx [:db :id->command
(core/command-id TestCommandInstance) :type]))))
(testing "Access scope indexes are correctly created"
(is (contains? (get-in fx [:db :access-scope->command-id #{:personal-chats}])
(core/command-id TestCommandInstance)))
(is (not (contains? (get-in fx [:db :access-scope->command-id #{:personal-chats}])
(core/command-id AnotherTestCommandInstance))))
(is (contains? (get-in fx [:db :access-scope->command-id #{:group-chats}])
(core/command-id TestCommandInstance)))
(is (contains? (get-in fx [:db :access-scope->command-id #{:public-chats}])
(core/command-id TestCommandInstance)))
(is (contains? (get-in fx [:db :access-scope->command-id #{:public-chats}])
(core/command-id AnotherTestCommandInstance))))))
#_(deftest chat-commands-test
(let [fx (core/load-commands {:db {}} #{TestCommandInstance AnotherTestCommandInstance})]
(testing "That relevant commands are looked up for chat"
(is (= #{TestCommandInstance AnotherTestCommandInstance}
(into #{}
(map (comp :type second))
(core/chat-commands (get-in fx [:db :id->command])
(get-in fx [:db :access-scope->command-id])
{:chat-id "topic"
:group-chat true
:public? true}))))
(is (= #{TestCommandInstance}
(into #{}
(map (comp :type second))
(core/chat-commands (get-in fx [:db :id->command])
(get-in fx [:db :access-scope->command-id])
{:chat-id "group"
:group-chat true}))))
(is (= #{TestCommandInstance}
(into #{}
(map (comp :type second))
(core/chat-commands (get-in fx [:db :id->command])
(get-in fx [:db :access-scope->command-id])
{:chat-id "contact"})))))))
(def contacts #{"0x0471b2be1e8b971f75b571ba047baa58e2f40f67dad38f6381b2382df43f7176b1813bf372af4cd8451ed9063213029378b9fbc7db792d496e1a6161c42d999edf"
"0x04b790f2c3f4079f35a1fa396465ceb243cc446c9af211d0a1774f869eb9632a67a6e664e24075ec5c5a8a95a509a2a8173dbfeb88af372e784a37fecc1b5c0ba5"
"0x04cc3cec3f88dc1a39e224388f0304023fc78c2a7d05e4ebd61638192cc592d2c13d8f081b5d9995dbfcbe45a4ca7eb80d5c505eee660e8fee0df2da222f047287"})
(def contacts_addresses '("0x5adf1b9e1fa4bd4889fecd598b45079045d98f0e"
"0x21631d18d9681d4ffdd460fc45fa52159fcd95c8"
"0x5541e3be81b76d76cdbf968516caa5a5b773763b"))
(deftest enrich-command-message-for-events-test-public
(let [db {:chats {"1" {:contacts nil :public? true :group-chat false}}}
msg {:chat-id "1"}
enriched-msg (core/enrich-command-message-for-events db msg)]
(testing "command-message correctly (not) enriched - public chat"
(is (= enriched-msg
(assoc msg :public? true :group-chat false))))))
(deftest enrich-command-message-for-events-test-groupchat
(let [db {:chats {"1" {:contacts contacts :public? false :group-chat true}}}
msg {:chat-id "1"}
enriched-msg (core/enrich-command-message-for-events db msg)]
(testing "command-message correctly enriched - group chat"
(is (= enriched-msg
(assoc msg :public? false :group-chat true :contacts contacts_addresses))))))
(deftest enrich-command-message-for-events-test-1on1-chat
(let [db {:chats {"1" {:contacts contacts :public? false :group-chat false}}}
msg {:chat-id "1"}
enriched-msg (core/enrich-command-message-for-events db msg)]
(testing "command-message correctly enriched - 1on1 chat"
(is (= enriched-msg
(assoc msg :public? false :group-chat false :contact (first contacts_addresses)))))))

View File

@ -1,82 +0,0 @@
(ns status-im.test.chat.commands.impl.transactions
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.i18n :as i18n]
[status-im.chat.commands.impl.transactions :as transactions]
[status-im.chat.commands.protocol :as protocol]))
(def public-key "0x04f96bc2229a0ba4125815451e47491d9ab923b8b03f205f6ff11d731c0f5759079c1aa0f3b73233c114372695c30a8e20ce18f73fafa23f924736cc39e726c3de")
(def address "f86b3cefae5851c19abfc48b7fb034b1dfa70b52")
(def cofx {:db {:multiaccount {:settings {:wallet {:visible-tokens {:mainnet #{:SNT}}}}
:wallet-set-up-passed? true}
:chain "mainnet"
:current-chat-id public-key
:contacts/contacts {public-key {:name "Recipient"
:address address
:public-key public-key}}
:wallet/all-tokens {:mainnet {"0x744d70fdbe2ba4cf95131626614a1763df805b9e" {:address "0x744d70fdbe2ba4cf95131626614a1763df805b9e"
:name "Status Network Token"
:symbol :SNT
:decimals 18}}}}})
;; testing the `/send` command
(def personal-send-command (transactions/PersonalSendCommand.))
(deftest personal-send-command-test
(testing "That correct parameters are defined"
(is (= (into #{} (map :id) (protocol/parameters personal-send-command))
#{:asset :amount})))
(testing "Parameters validation"
(is (= (protocol/validate personal-send-command {:asset "TST"} cofx)
{:title (i18n/label :t/send-request-invalid-asset)
:description (i18n/label :t/send-request-unknown-token {:asset "TST"})}))
(is (= (protocol/validate personal-send-command {:asset "SNT"} cofx)
{:title (i18n/label :t/send-request-amount)
:description (i18n/label :t/send-request-amount-must-be-specified)}))
(is (= (protocol/validate personal-send-command {:asset "SNT" :amount "a"} cofx)
{:title (i18n/label :t/send-request-amount)
:description (i18n/label :t/send-request-amount-invalid-number)}))
(is (= (protocol/validate personal-send-command {:asset "ETH" :amount "0.54354353454353453453454353453445345545"} cofx)
{:title (i18n/label :t/send-request-amount)
:description (i18n/label :t/send-request-amount-max-decimals {:asset-decimals 18})}))
(is (= (protocol/validate personal-send-command {:asset "ETH" :amount "0.01"} cofx)
nil))))
;; testing the `/request` command
(def personal-request-command (transactions/PersonalRequestCommand.))
(deftest personal-request-command-test
(testing "That correct parameters are defined"
(is (= (into #{} (map :id) (protocol/parameters personal-request-command))
#{:asset :amount})))
(testing "Parameters validation"
(is (= (protocol/validate personal-request-command {:asset "TST"} cofx)
{:title (i18n/label :t/send-request-invalid-asset)
:description (i18n/label :t/send-request-unknown-token {:asset "TST"})}))
(is (= (protocol/validate personal-request-command {:asset "SNT"} cofx)
{:title (i18n/label :t/send-request-amount)
:description (i18n/label :t/send-request-amount-must-be-specified)}))
(is (= (protocol/validate personal-request-command {:asset "SNT" :amount "a"} cofx)
{:title (i18n/label :t/send-request-amount)
:description (i18n/label :t/send-request-amount-invalid-number)}))
(is (= (protocol/validate personal-request-command {:asset "ETH" :amount "0,1Aaa"} cofx)
{:title (i18n/label :t/send-request-amount)
:description (i18n/label :t/send-request-amount-invalid-number)}))
(is (= (protocol/validate personal-request-command {:asset "ETH" :amount "1-45"} cofx)
{:title (i18n/label :t/send-request-amount)
:description (i18n/label :t/send-request-amount-invalid-number)}))
(is (= (protocol/validate personal-request-command {:asset "SNT" :amount "1$#@8"} cofx)
{:title (i18n/label :t/send-request-amount)
:description (i18n/label :t/send-request-amount-invalid-number)}))
(is (= (protocol/validate personal-request-command {:asset "SNT" :amount "20,"} cofx)
{:title (i18n/label :t/send-request-amount)
:description (i18n/label :t/send-request-amount-invalid-number)}))
(is (= (protocol/validate personal-request-command {:asset "SNT" :amount "20."} cofx)
{:title (i18n/label :t/send-request-amount)
:description (i18n/label :t/send-request-amount-invalid-number)}))
(is (= (protocol/validate personal-request-command {:asset "ETH" :amount "0.54354353454353453453454353453445345545"} cofx)
{:title (i18n/label :t/send-request-amount)
:description (i18n/label :t/send-request-amount-max-decimals {:asset-decimals 18})}))
(is (= (protocol/validate personal-request-command {:asset "ETH" :amount "0.01"} cofx)
nil))))

View File

@ -1,80 +0,0 @@
(ns status-im.test.chat.commands.input
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.test.chat.commands.core :as test-core]
[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])
(get-in fx [:db :access-scope->command-id])
{:chat-id "contact"})]
(testing "Text not beggining with the command special charactes `/` is recognised"
(is (not (input/selected-chat-command "test-command 1" nil commands))))
(testing "Command not matching any available commands is not recognised as well"
(is (not (input/selected-chat-command "/another-test-command" nil commands))))
(testing "Available correctly entered command is recognised"
(is (= test-core/TestCommandInstance
(get (input/selected-chat-command "/test-command" nil commands) :type))))
(testing "Command completion and param position are determined as well"
(let [{:keys [current-param-position command-completion]}
(input/selected-chat-command "/test-command 1 " 17 commands)]
(is (= 1 current-param-position))
(is (= :less-then-needed command-completion)))
(let [{:keys [current-param-position command-completion]}
(input/selected-chat-command "/test-command 1 2 3" 20 commands)]
(is (= 2 current-param-position))
(is (= :complete command-completion))))))
(deftest set-command-parameter-test
(testing "Setting command parameter correctly updates the text input"
(let [create-cofx (fn [input-text]
{:db {:chats {"test" {:input-text input-text}}
:current-chat-id "test"}})]
(is (= "/test-command first-value "
(get-in (input/set-command-parameter (create-cofx "/test-command")
false 0 "first-value")
[:db :chats "test" :input-text])))
(is (= "/test-command first-value second-value \"last value\""
(get-in (input/set-command-parameter (create-cofx "/test-command first-value edited \"last value\"")
false 1 "second-value")
[:db :chats "test" :input-text])))
(is (= "/test-command first-value second-value \"last value\""
(get-in (input/set-command-parameter (create-cofx "/test-command first-value second-value")
true 2 "last value")
[:db :chats "test" :input-text]))))))
(deftest parse-parameters-test
(testing "testing that parse-parameters work correctly"
(is (= {:first-param "1"
:second-param "2"
:last-param "3"}
(input/parse-parameters test-core/test-command-parameters "/test-command 1 2 3")))
(is (= {:first-param "1"
:second-param "2 2"
:last-param "3"}
(input/parse-parameters test-core/test-command-parameters "/test-command 1 \"2 2\" 3")))
(is (= {:first-param "1"
:second-param "2"}
(input/parse-parameters test-core/test-command-parameters "/test-command 1 2")))
(is (= {}
(input/parse-parameters test-core/test-command-parameters "/test-command ")))))

View File

@ -2,9 +2,6 @@
(:require [doo.runner :refer-macros [doo-tests]]
[status-im.test.browser.core]
[status-im.test.browser.permissions]
[status-im.test.chat.commands.core]
[status-im.test.chat.commands.impl.transactions]
[status-im.test.chat.commands.input]
[status-im.test.chat.db]
[status-im.test.chat.models.input]
[status-im.test.chat.models.message]
@ -78,9 +75,6 @@
(doo-tests
'status-im.test.browser.core
'status-im.test.browser.permissions
'status-im.test.chat.commands.core
'status-im.test.chat.commands.impl.transactions
'status-im.test.chat.commands.input
'status-im.test.chat.db
'status-im.test.chat.models
'status-im.test.chat.models.input

View File

@ -1,13 +1,12 @@
(ns status-im.test.ui.screens.currency-settings.models
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.chat.commands.impl.transactions :as txs]
[status-im.ui.screens.currency-settings.models :as models]))
(deftest get-currency
(is (= :usd (txs/get-currency {:multiaccount {:settings {:wallet {:currency :usd}}}})))
(is (= :usd (txs/get-currency {:multiaccount {:settings {:wallet {:currency nil}}}})))
(is (= :usd (txs/get-currency {:multiaccount {:settings {:wallet {}}}})))
(is (= :aud (txs/get-currency {:multiaccount {:settings {:wallet {:currency :aud}}}}))))
(is (= :usd (models/get-currency {:multiaccount {:settings {:wallet {:currency :usd}}}})))
(is (= :usd (models/get-currency {:multiaccount {:settings {:wallet {:currency nil}}}})))
(is (= :usd (models/get-currency {:multiaccount {:settings {:wallet {}}}})))
(is (= :aud (models/get-currency {:multiaccount {:settings {:wallet {:currency :aud}}}}))))
(deftest set-currency
(let [cofx (models/set-currency {:db {:multiaccount {:settings {:wallet {}}}}} :usd)]