diff --git a/.re-natal b/.re-natal index 76261cfccd..7c0d7e5cca 100644 --- a/.re-natal +++ b/.re-natal @@ -38,7 +38,6 @@ "web3", "eccjs", "chance", - "react-native-emoji-picker", "react-native-autolink", "instabug-reactnative", "nfc-react-native", diff --git a/package.json b/package.json index 869df56bb3..42bfe57560 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,6 @@ "react-native-config": "0.9.0", "react-native-crypto": "2.1.1", "react-native-dialogs": "0.0.20", - "react-native-emoji-picker": "git+https://github.com/status-im/react-native-emoji-picker.git", "react-native-fcm": "10.0.3", "react-native-fs": "2.8.1", "react-native-http": "github:tradle/react-native-http#834492d", diff --git a/react-native/src/status_im/react_native/js_dependencies.cljs b/react-native/src/status_im/react_native/js_dependencies.cljs index 66efbb4b1e..3441e95f30 100644 --- a/react-native/src/status_im/react_native/js_dependencies.cljs +++ b/react-native/src/status_im/react_native/js_dependencies.cljs @@ -6,7 +6,6 @@ (def config (js/require "react-native-config")) (def dialogs (js/require "react-native-dialogs")) (def dismiss-keyboard (js/require "dismissKeyboard")) -(def emoji-picker (js/require "react-native-emoji-picker")) (def fs (js/require "react-native-fs")) (def http-bridge (js/require "react-native-http-bridge")) ;; i18n is now exported in default object of the module diff --git a/resources/icons/commands_list.svg b/resources/icons/commands_list.svg deleted file mode 100644 index a2832a539e..0000000000 --- a/resources/icons/commands_list.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/resources/icons/input_commands.svg b/resources/icons/input_commands.svg new file mode 100644 index 0000000000..7188dbdfe8 --- /dev/null +++ b/resources/icons/input_commands.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/icons/input_send.svg b/resources/icons/input_send.svg new file mode 100644 index 0000000000..309b13351f --- /dev/null +++ b/resources/icons/input_send.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/icons/smile.svg b/resources/icons/smile.svg deleted file mode 100644 index 6b219e3c4b..0000000000 --- a/resources/icons/smile.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/resources/js/bots/console/bot.js b/resources/js/bots/console/bot.js index ee8998b0a8..e72c03383d 100644 --- a/resources/js/bots/console/bot.js +++ b/resources/js/bots/console/bot.js @@ -1,17 +1,11 @@ -function jsSuggestionsContainerStyle(suggestionsCount) { - return { - marginVertical: 1, - marginHorizontal: 0, - keyboardShouldPersistTaps: "always", - //height: Math.min(150, (56 * suggestionsCount)), - backgroundColor: "white", - borderRadius: 5, - keyboardShouldPersistTaps: "always" - }; -} +var jsSuggestionsContainerStyle = { + keyboardShouldPersistTaps: "always", + backgroundColor: "white", + flexGrow: 1, + bounces: false +}; var jsSuggestionContainerStyle = { - paddingLeft: 16, backgroundColor: "white" }; @@ -22,22 +16,34 @@ var jsSubContainerStyle = { borderBottomColor: "#0000001f" }; +function jsSuggestionSubContainerStyle(isLast) { + var borderBottomWidth = (isLast ? 0 : 1); + + return { + paddingTop: 14, + paddingBottom: 14, + paddingRight: 14, + marginLeft: 14, + borderBottomWidth: borderBottomWidth, + borderBottomColor: "#e8ebec" + }; +} + var jsValueStyle = { - fontSize: 14, + fontSize: 15, fontFamily: "font", color: "#000000de" }; var jsBoldValueStyle = { - fontSize: 14, + fontSize: 15, fontFamily: "font", color: "#000000de", fontWeight: "bold" }; var jsDescriptionStyle = { - marginTop: 1.5, - paddingBottom: 9, + marginTop: 2, fontSize: 14, fontFamily: "font", color: "#838c93de" @@ -324,7 +330,7 @@ function jsSuggestions(params, context) { suggestion.title = createMarkupText(suggestion.title); } var suggestionMarkup = status.components.view(jsSuggestionContainerStyle, - [status.components.view(jsSubContainerStyle, + [status.components.view(jsSuggestionSubContainerStyle(i == suggestions.length - 1), [ status.components.text({style: jsValueStyle}, suggestion.title), @@ -342,9 +348,7 @@ function jsSuggestions(params, context) { } if (sugestionsMarkup.length > 0) { - var view = status.components.scrollView(jsSuggestionsContainerStyle(sugestionsMarkup.length), - sugestionsMarkup - ); + var view = status.components.scrollView(jsSuggestionsContainerStyle, sugestionsMarkup); return {markup: view}; } else { return {markup: null}; @@ -371,39 +375,41 @@ function jsHandler(params, context) { return result; } -function suggestionsContainerStyle(suggestionsCount) { - return { - marginVertical: 1, - marginHorizontal: 0, - keyboardShouldPersistTaps: "always", - height: Math.min(150, (56 * suggestionsCount)), - backgroundColor: "white", - borderRadius: 5, - flexGrow: 1 - }; +var suggestionsContainerStyle = { + keyboardShouldPersistTaps: "always", + backgroundColor: "white", + flexGrow: 1, + bounces: false } var suggestionContainerStyle = { - paddingLeft: 16, backgroundColor: "white" }; -var suggestionSubContainerStyle = { - height: 56, - borderBottomWidth: 1, - borderBottomColor: "#0000001f" -}; +function suggestionSubContainerStyle(isTwoLineEntry, isLast) { + var height = (isTwoLineEntry ? 64 : 48); + var borderBottomWidth = (isLast ? 0 : 1); + + return { + paddingTop: 14, + paddingBottom: 14, + paddingRight: 14, + marginLeft: 14, + height: height, + borderBottomWidth: borderBottomWidth, + borderBottomColor: "#e8ebec" + }; +} var valueStyle = { - marginTop: 9, - fontSize: 14, + fontSize: 15, fontFamily: "font", color: "#000000de" }; var descriptionStyle = { - marginTop: 1.5, - fontSize: 14, + marginTop: 2, + fontSize: 13, fontFamily: "font", color: "#838c93de" }; @@ -443,13 +449,15 @@ function getFaucets(networkId) { var faucets = getFaucets(status.ethereumNetworkId); function faucetSuggestions(params) { - var suggestions = faucets.map(function (entry) { + var subContainerStyle = suggestionSubContainerStyle(true, false); + + var suggestions = faucets.map(function (entry, index) { return status.components.touchable( - {onPress: status.components.dispatch([status.events.SET_COMMAND_ARGUMENT, [0, entry.url, false]])}, + {onPress: status.components.dispatch([status.events.SET_COMMAND_ARGUMENT, [0, entry.url, true]])}, status.components.view( suggestionContainerStyle, [status.components.view( - suggestionSubContainerStyle, + (index == faucets.length - 1 ? suggestionSubContainerStyle(true, true) : subContainerStyle), [ status.components.text( {style: valueStyle}, @@ -466,11 +474,13 @@ function faucetSuggestions(params) { }); var view = status.components.scrollView( - suggestionsContainerStyle(faucets.length), + suggestionsContainerStyle, suggestions ); - return {markup: view}; + var entryHeight = subContainerStyle.height + subContainerStyle.borderBottomWidth; + + return {markup: view, height: entryHeight * faucets.length}; } var faucetCommandConfig ={ @@ -523,13 +533,16 @@ if (faucets.length > 0) { } function debugSuggestions(params) { - var suggestions = ["On", "Off"].map(function (entry) { + var values = ["On", "Off"]; + var subContainerStyle = suggestionSubContainerStyle(false, false); + + var suggestions = values.map(function (entry, index) { return status.components.touchable( - {onPress: status.components.dispatch([status.events.SET_COMMAND_ARGUMENT, [0, entry, false]])}, + {onPress: status.components.dispatch([status.events.SET_COMMAND_ARGUMENT, [0, entry, true]])}, status.components.view( suggestionContainerStyle, [status.components.view( - suggestionSubContainerStyle, + (index == values.length - 1 ? suggestionSubContainerStyle(false, true) : subContainerStyle), [ status.components.text( {style: valueStyle}, @@ -542,11 +555,13 @@ function debugSuggestions(params) { }); var view = status.components.scrollView( - suggestionsContainerStyle(faucets.length), + suggestionsContainerStyle, suggestions ); - return {markup: view}; + var entryHeight = subContainerStyle.height + subContainerStyle.borderBottomWidth; + + return {markup: view, height: entryHeight * values.length}; } status.command({ diff --git a/src/status_im/chat/events.cljs b/src/status_im/chat/events.cljs index 0581ad25ca..5076d9fbfe 100644 --- a/src/status_im/chat/events.cljs +++ b/src/status_im/chat/events.cljs @@ -132,7 +132,6 @@ [re-frame/trim-v] (fn [db [details]] (model/set-chat-ui-props db {:show-bottom-info? true - :show-emoji? false :bottom-info details}))) (def index-messages (partial into {} (map (juxt :message-id identity)))) diff --git a/src/status_im/chat/events/input.cljs b/src/status_im/chat/events/input.cljs index 61a5e1e4cd..0ab840022f 100644 --- a/src/status_im/chat/events/input.cljs +++ b/src/status_im/chat/events/input.cljs @@ -186,7 +186,6 @@ (bots-events/clear-bot-db owner-id) clear-seq-arguments (model/set-chat-ui-props {:show-suggestions? false - :show-emoji? false :result-box nil :validation-messages nil :prev-command name}) diff --git a/src/status_im/chat/screen.cljs b/src/status_im/chat/screen.cljs index f684759a26..6c3dc4a387 100644 --- a/src/status_im/chat/screen.cljs +++ b/src/status_im/chat/screen.cljs @@ -97,9 +97,8 @@ (defview chat [] (letsubs [{:keys [group-chat input-text]} [:get-current-chat] show-bottom-info? [:get-current-chat-ui-prop :show-bottom-info?] - show-emoji? [:get-current-chat-ui-prop :show-emoji?] + layout-height [:get :layout-height] current-view [:get :view-id]] - {:component-will-unmount #(re-frame/dispatch [:set-chat-ui-props {:show-emoji? false}])} [react/view {:style style/chat-view} [chat-toolbar] (when (= :chat current-view) diff --git a/src/status_im/chat/styles/animations.cljs b/src/status_im/chat/styles/animations.cljs index 3072eececf..2d6447cfa3 100644 --- a/src/status_im/chat/styles/animations.cljs +++ b/src/status_im/chat/styles/animations.cljs @@ -4,16 +4,6 @@ (def color-root-border "rgba(192, 198, 202, 0.28)") (def header-draggable-icon "rgba(73, 84, 93, 0.23)") -(defn result-box-overlay [max-height opacity-anim-value] - {:background-color common/color-black - :position :absolute - :opacity opacity-anim-value - :height max-height - :elevation 2 - :bottom 0 - :left 0 - :right 0}) - (def overlap-container {:position :absolute :left 0 @@ -23,14 +13,14 @@ (defn expandable-container [anim-value bottom] {:background-color common/color-white - :border-top-color color-root-border - :border-top-width 1 - :elevation 2 :height anim-value :left 0 :right 0 :bottom bottom - :position :absolute}) + :position :absolute + :border-top-color color-root-border + :border-top-width 1 + :elevation 2}) (def header-container {:min-height 19 diff --git a/src/status_im/chat/styles/input/emoji.cljs b/src/status_im/chat/styles/input/emoji.cljs deleted file mode 100644 index 42cb7232a5..0000000000 --- a/src/status_im/chat/styles/input/emoji.cljs +++ /dev/null @@ -1,16 +0,0 @@ -(ns status-im.chat.styles.input.emoji - (:require [status-im.ui.components.styles :as common])) - -(def container-height 250) - -(defn container [height] - {:flex-direction :column - :height (or height container-height) - :background-color common/color-white}) - -(def emoji-container - {:flex 1}) - -(def emoji-picker - {:flex 1 - :background-color common/color-white}) diff --git a/src/status_im/chat/styles/input/input.cljs b/src/status_im/chat/styles/input/input.cljs index 66cbb5a4c8..691a82d921 100644 --- a/src/status_im/chat/styles/input/input.cljs +++ b/src/status_im/chat/styles/input/input.cljs @@ -1,69 +1,52 @@ (ns status-im.chat.styles.input.input (:require-macros [status-im.utils.styles :refer [defstyle defnstyle]]) (:require [status-im.ui.components.styles :as common] - [status-im.utils.platform :as platform] - [taoensso.timbre :as log])) + [status-im.ui.components.colors :as colors] + [status-im.utils.platform :as platform])) -(def color-root-border "#e8eaeb") -(def color-input "#edf1f3") -(def color-input-helper-text "rgb(182, 189, 194)") -(def color-input-helper-placeholder "rgb(182, 189, 194)") -(def color-command "#70777d") -(def color-send "rgb(98, 143, 227)") - -(def min-input-height 38) +(def min-input-height 36) +(def padding-vertical 8) +(def border-height 1) (def max-input-height (* 4 min-input-height)) (defnstyle root [margin-bottom] - {:flex-direction :column - :elevation 2 - :margin-bottom margin-bottom - :border-top-width 1 - :border-top-color color-root-border}) - -(defn container [container-anim-margin bottom-anim-margin] {:background-color common/color-white + :margin-bottom margin-bottom :flex-direction :column - :padding-left container-anim-margin - :padding-right container-anim-margin - :padding-top 8 - :padding-bottom bottom-anim-margin}) - -(defstyle input-container-view - {:ios {:z-index 1}}) + :border-top-width border-height + :border-top-color colors/light-gray + :elevation 2}) (def input-container - {:flex-direction :row - :align-items :flex-end}) + {:flex-direction :row + :align-items :flex-end + :padding-left 14 + :padding-right 14}) -(defn input-root [content-height anim-margin] +(def input-root + {:padding-top padding-vertical + :padding-bottom padding-vertical + :flex 1}) + +(defn input-animated [content-height] {:align-items :flex-start - :background-color color-input :flex-direction :row :flex-grow 1 - :height (min (max min-input-height content-height) max-input-height) - :margin-top anim-margin - :padding-left 10 - :padding-right 10 - :border-radius 8}) - -(defnstyle input-touch-handler-view [container-width] - {:position :absolute - :width container-width - :height min-input-height}) + :height (min (max min-input-height content-height) max-input-height)}) (defnstyle input-view [content-height single-line-input?] {:flex 1 - :font-size 14 + :font-size 15 :padding-top 9 :padding-bottom 5 + :padding-right 12 :height (if single-line-input? min-input-height (+ (min (max min-input-height content-height) max-input-height))) :android {:padding-top 3}}) (def invisible-input-text - {:font-size 14 + {:font-size 15 :position :absolute :left 0 :background-color :transparent @@ -72,7 +55,7 @@ (defnstyle invisible-input-text-height [container-width] {:width container-width :flex 1 - :font-size 14 + :font-size 15 :padding-top 5 :padding-bottom 5 :android {:padding-top 3} @@ -81,45 +64,44 @@ :background-color :transparent :color :transparent}) +(defnstyle input-helper-view [left opacity] + {:opacity opacity + :position :absolute + :height min-input-height + :android {:left (+ 4 left)} + :ios {:left left}}) + (defnstyle input-helper-text [left] - {:color color-input-helper-text - :font-size 14 - :position :absolute + {:color colors/gray + :font-size 15 :text-align-vertical :center - :height min-input-height - :align-items :center - :android {:left (+ 15 left) - :top -1} - :ios {:line-height min-input-height - :left (+ 10 left)}}) + :flex 1 + :android {:top -1} + :ios {:line-height min-input-height}}) (defnstyle seq-input-text [left container-width] {:min-width (- container-width left) - :font-size 14 + :font-size 15 :position :absolute :text-align-vertical :center - :height min-input-height :align-items :center - :android {:left (+ 10 left) - :top 0.5} + :android {:left (+ 2 left) + :height (+ 2 min-input-height) + :top 0.5} :ios {:line-height min-input-height - :left (+ 9 left)}}) + :height min-input-height + :left left}}) -(def input-emoji-icon - {:margin-top 7 - :height 24 - :width 24}) +(def input-commands-icon + {:margin-bottom 14 + :height 24 + :width 24}) (def input-clear-container - {:width 24 - :height 24 - :margin-top 7 - :align-items :center}) - -(def input-clear-icon - {:width 24 - :height 24 - :margin-top 0}) + {:width 24 + :height 24 + :margin-top 7 + :align-items :center}) (def commands-root {:flex-direction :row @@ -133,30 +115,3 @@ (def commands-list-icon {:height 24 :width 24}) - -(def close-commands-list-icon - {:height 24 - :width 24}) - -(def send-message-container - {:background-color color-send - :width 38 - :height 38 - :border-radius 19 - :padding 7 - :margin-left 8}) - -(def send-message-icon - {:height 24 - :width 24}) - -(def commands - {:flex-direction :row - :margin-right 16}) - -(defn command [first?] - {:color color-command - :font-size 14 - :margin-left (if first? 10 16) - :padding-top 8 - :padding-bottom 8}) diff --git a/src/status_im/chat/styles/input/input_actions.cljs b/src/status_im/chat/styles/input/input_actions.cljs deleted file mode 100644 index aaf40e894d..0000000000 --- a/src/status_im/chat/styles/input/input_actions.cljs +++ /dev/null @@ -1,27 +0,0 @@ -(ns status-im.chat.styles.input.input-actions - (:require-macros [status-im.utils.styles :refer [defnstyle]]) - (:require [status-im.ui.components.styles :as common])) - -(def actions-container - {:flex-direction :row - :margin-left 10}) - -(defn action-view [enabled?] - {:width 38 - :height 38 - :opacity (if enabled? 1 0.5) - :justify-content :center - :align-items :center}) - -(def action-view-icon - {:width 24 - :height 24}) - -(def action-view-icon-tinted - {:width 24 - :height 24 - :tint-color "black"}) - -(def action-view-fullscreen-expand-icon - {:width 16 - :height 16}) \ No newline at end of file diff --git a/src/status_im/chat/styles/input/parameter_box.cljs b/src/status_im/chat/styles/input/parameter_box.cljs new file mode 100644 index 0000000000..e3c7d6c6d6 --- /dev/null +++ b/src/status_im/chat/styles/input/parameter_box.cljs @@ -0,0 +1,9 @@ +(ns status-im.chat.styles.input.parameter-box + (:require [status-im.ui.components.styles :as common] + [status-im.ui.components.colors :as colors])) + +(def root + {:background-color common/color-white + :border-top-color colors/light-gray + :border-top-width 1}) + diff --git a/src/status_im/chat/styles/input/result_box.cljs b/src/status_im/chat/styles/input/result_box.cljs index 93e5427dc9..4165a5366d 100644 --- a/src/status_im/chat/styles/input/result_box.cljs +++ b/src/status_im/chat/styles/input/result_box.cljs @@ -1,11 +1,10 @@ (ns status-im.chat.styles.input.result-box - (:require [status-im.ui.components.styles :as common])) - -(def color-root-border "rgba(192, 198, 202, 0.5)") + (:require [status-im.ui.components.styles :as common] + [status-im.ui.components.colors :as colors])) (defn root [height bottom] {:background-color common/color-white - :border-top-color color-root-border + :border-top-color colors/light-gray :border-top-width 1 :flex-direction :column :height height diff --git a/src/status_im/chat/styles/input/send_button.cljs b/src/status_im/chat/styles/input/send_button.cljs new file mode 100644 index 0000000000..c3989fbf68 --- /dev/null +++ b/src/status_im/chat/styles/input/send_button.cljs @@ -0,0 +1,16 @@ +(ns status-im.chat.styles.input.send-button + (:require [status-im.ui.components.colors :as colors])) + +(defn send-message-container [rotation] + {:background-color colors/blue + :width 30 + :height 30 + :border-radius 15 + :padding 4 + :margin-left 8 + :margin-bottom 11 + :transform [{:rotate rotation}]}) + +(def send-message-icon + {:height 22 + :width 22}) \ No newline at end of file diff --git a/src/status_im/chat/styles/input/suggestions.cljs b/src/status_im/chat/styles/input/suggestions.cljs index ed38e09496..e34438d35a 100644 --- a/src/status_im/chat/styles/input/suggestions.cljs +++ b/src/status_im/chat/styles/input/suggestions.cljs @@ -1,46 +1,32 @@ (ns status-im.chat.styles.input.suggestions (:require-macros [status-im.utils.styles :refer [defnstyle]]) (:require [status-im.ui.components.styles :as common] + [status-im.ui.components.colors :as colors] [status-im.utils.platform :as platform])) -(def color-item-title-text "rgb(147, 155, 161)") -(def color-item-suggestion-name "rgb(98, 143, 227)") -(def color-item-border "#e8eaeb") +(def item-height 52) +(def border-height 1) -(defn item-title-container [top-padding?] - {:margin-left 16 - :align-items :center - :flex-direction :row - :height 44}) +(def root + {:background-color common/color-white + :border-top-color colors/light-gray + :border-top-width 1}) -(def item-title-text - {:font-size 14 - :color color-item-title-text}) - -(defnstyle item-suggestion-container [last?] - {:flex-direction :row - :align-items :center - :height 56 - :margin-left 16 - :ios {:border-bottom-color color-item-border - :border-bottom-width (if last? 0 1)}}) +(defn item-suggestion-container [last?] + {:flex-direction :row + :align-items :center + :height item-height + :margin-left 14 + :padding-right 14 + :border-bottom-color colors/light-gray + :border-bottom-width (if last? 0 border-height)}) (def item-suggestion-name - {:background-color color-item-suggestion-name - :height 28 - :flex-direction :row - :align-items :center - :border-radius 4 - :padding-left 7 - :padding-right 7}) - -(def item-suggestion-name-text - {:color common/color-white - :font-size 14}) + {:color common/color-black + :font-size 15}) (def item-suggestion-description - {:flex 1 - :font-size 14 - :margin-left 16 - :margin-right 16 - :color color-item-title-text}) + {:flex 1 + :font-size 15 + :margin-left 10 + :color colors/gray}) diff --git a/src/status_im/chat/subs.cljs b/src/status_im/chat/subs.cljs index 5ad243b0ff..60130d862a 100644 --- a/src/status_im/chat/subs.cljs +++ b/src/status_im/chat/subs.cljs @@ -1,14 +1,15 @@ (ns status-im.chat.subs - (:require [re-frame.core :refer [reg-sub subscribe]] + (:require [clojure.string :as string] + [re-frame.core :refer [reg-sub subscribe]] [status-im.constants :as constants] + [status-im.chat.constants :as chat-constants] [status-im.chat.models.input :as input-model] [status-im.chat.models.commands :as commands-model] [status-im.chat.views.input.utils :as input-utils] [status-im.commands.utils :as commands-utils] [status-im.utils.datetime :as time] [status-im.utils.platform :as platform] - [status-im.i18n :as i18n] - [clojure.string :as string])) + [status-im.i18n :as i18n])) (reg-sub :get-chats :chats) @@ -169,7 +170,9 @@ (defn- available-commands-responses [[commands-responses {:keys [input-text]}]] (->> commands-responses map->sorted-seq - (filter #(string/includes? (commands-model/command-name %) (or input-text ""))))) + (filter (fn [item] + (when (input-model/starts-as-command? input-text) + (string/includes? (commands-model/command-name item) input-text)))))) (reg-sub :get-available-commands @@ -198,6 +201,24 @@ (fn [[chat commands responses]] (input-model/selected-chat-command chat commands responses))) +(reg-sub + :chat-input-placeholder + :<- [:chat :input-text] + :<- [:selected-chat-command] + (fn [[input-text command]] + (when (and (string/ends-with? (or input-text "") chat-constants/spacing-char) + (not (get-in command [:command :sequential-params]))) + (let [input (string/trim (or input-text "")) + real-args (remove string/blank? (:args command))] + (cond + (and command (empty? real-args)) + (get-in command [:command :params 0 :placeholder]) + + (and command + (= (count real-args) 1) + (input-model/text-ends-with-space? input)) + (get-in command [:command :params 1 :placeholder])))))) + (reg-sub :current-chat-argument-position :<- [:selected-chat-command] @@ -242,16 +263,22 @@ input-model/command-completion) (reg-sub - :show-suggestions? + :show-suggestions-view? :<- [:get-current-chat-ui-prop :show-suggestions?] :<- [:get-current-chat] :<- [:selected-chat-command] :<- [:get-available-commands-responses] (fn [[show-suggestions? {:keys [input-text]} selected-command commands-responses]] (and (or show-suggestions? (input-model/starts-as-command? (string/trim (or input-text "")))) - (not (:command selected-command)) (seq commands-responses)))) +(reg-sub + :show-suggestions? + :<- [:show-suggestions-view?] + :<- [:selected-chat-command] + (fn [[show-suggestions-box? selected-command]] + (and show-suggestions-box? (not (:command selected-command))))) + (reg-sub :is-request-answered? :<- [:get-current-chat] diff --git a/src/status_im/chat/views/api/choose_contact.cljs b/src/status_im/chat/views/api/choose_contact.cljs index 5e367aee0e..cc9abac56f 100644 --- a/src/status_im/chat/views/api/choose_contact.cljs +++ b/src/status_im/chat/views/api/choose_contact.cljs @@ -1,5 +1,5 @@ (ns status-im.chat.views.api.choose-contact - (:require-macros [status-im.utils.views :refer [defview]]) + (:require-macros [status-im.utils.views :refer [defview letsubs]]) (:require [re-frame.core :as re-frame] [status-im.ui.components.contact.contact :refer [contact-view]] [status-im.ui.components.list.views :as list] @@ -17,17 +17,17 @@ (defview choose-contact-view [{title :title arg-index :index bot-db-key :bot-db-key}] - [contacts [:people-in-current-chat]] - [react/view {:flex 1} - [react/text {:style {:font-size 14 - :color "rgb(147, 155, 161)" - :padding-top 12 - :padding-left 16 - :padding-right 16 - :padding-bottom 12}} - title] - [list/flat-list {:data contacts - :render-fn (render-contact arg-index bot-db-key) - :enableEmptySections true - :keyboardShouldPersistTaps :always - :bounces false}]]) + (letsubs [contacts [:people-in-current-chat]] + [react/view + [react/text {:style {:font-size 14 + :color "rgb(147, 155, 161)" + :padding-top 12 + :padding-left 16 + :padding-right 16 + :padding-bottom 12}} + title] + [list/flat-list {:data contacts + :render-fn (render-contact arg-index bot-db-key) + :enableEmptySections true + :keyboardShouldPersistTaps :always + :bounces false}]])) diff --git a/src/status_im/chat/views/input/animations/expandable.cljs b/src/status_im/chat/views/input/animations/expandable.cljs index 9b032ca09a..33a8e452ef 100644 --- a/src/status_im/chat/views/input/animations/expandable.cljs +++ b/src/status_im/chat/views/input/animations/expandable.cljs @@ -1,92 +1,92 @@ (ns status-im.chat.views.input.animations.expandable (:require-macros [status-im.utils.views :refer [defview letsubs]]) - (:require [reagent.core :as r] - [reagent.impl.component :as rc] - [re-frame.core :refer [dispatch subscribe]] - [status-im.ui.components.animation :as anim] + (:require [reagent.core :as reagent] + [re-frame.core :as re-frame] + [status-im.ui.components.animation :as animation] [status-im.ui.components.drag-drop :as drag] - [status-im.ui.components.react :refer [view - animated-view]] - [status-im.chat.views.input.animations.responder :as resp] + [status-im.ui.components.react :as react] + [status-im.chat.views.input.animations.responder :as responder] [status-im.chat.views.input.utils :as input-utils] [status-im.chat.styles.animations :as style] - [taoensso.timbre :as log] - [status-im.utils.platform :as p])) + [status-im.chat.styles.input.input :as input-style])) (defn header [key container-height custom-header] - (let [set-container-height (subscribe [:chat-animations key :height]) - max-container-height (subscribe [:get-max-container-area-height]) - pan-responder (resp/pan-responder container-height - max-container-height - :fix-expandable-height - key)] + (let [set-container-height (re-frame/subscribe [:chat-animations key :height]) + max-container-height (re-frame/subscribe [:get-max-container-area-height]) + pan-responder (responder/pan-responder container-height + max-container-height + :fix-expandable-height + key)] (fn [_] - [view (merge (drag/pan-handlers pan-responder) - {:style style/header-container}) - [view style/header-icon] + [react/view (merge (drag/pan-handlers pan-responder) + {:style style/header-container}) + [react/view style/header-icon] (when (and custom-header (or (= @set-container-height :max) (> @set-container-height (:min-height style/header-container)))) [custom-header])]))) (defn expandable-view-on-update [{:keys [anim-value to-changed-height max-height chat-input-margin height]}] - (let [to-default-height (subscribe [:get-default-container-area-height]) - layout-height (subscribe [:get :layout-height])] - (fn [_] - (let [to-change-height (if (= @to-changed-height :max) - (input-utils/max-container-area-height @chat-input-margin @layout-height) - @to-changed-height) - to-value (min (or @to-changed-height (or height @to-default-height)) - @max-height)] - (dispatch [:set :expandable-view-height-to-value to-value]) - (anim/start - (anim/spring anim-value {:toValue to-value - :friction 10 - :tension 60})))))) + (let [to-default-height (re-frame/subscribe [:get-default-container-area-height]) + layout-height (re-frame/subscribe [:get :layout-height])] + (fn [component] + ;; we're going to change the height here -(defview overlay-view [] - (letsubs [max-height [:get-max-container-area-height] - layout-height [:get :layout-height] - view-height-to [:get :expandable-view-height-to-value]] - (let [related-height (/ view-height-to max-height)] - (when (> related-height 0.6) - [animated-view {:style (style/result-box-overlay layout-height (- related-height (/ 0.4 related-height)))}])))) + ;; by default the height can be modified by dispatching :set-expandable-height, + ;; but there is also a way to make the height adjusted automatically — in this + ;; case you just need to set :dynamic-height? to true + (let [{:keys [dynamic-height?] dynamic-height :height} (reagent/props component) + to-changed-height (if dynamic-height? + dynamic-height + @to-changed-height) + to-change-height (if (= to-changed-height :max) + (input-utils/max-container-area-height @chat-input-margin @layout-height) + to-changed-height) + to-value (min (or to-change-height (or height @to-default-height)) + @max-height)] + (re-frame/dispatch [:set :expandable-view-height-to-value to-value]) + (animation/start + (animation/spring anim-value {:toValue to-value + :friction 10 + :tension 60})))))) (defn expandable-view [{:keys [key height hide-overlay?]} & _] - (let [anim-value (anim/create-value 0) - input-height (subscribe [:get-current-chat-ui-prop :input-height]) - max-height (subscribe [:get-max-container-area-height]) - fullscreen? (subscribe [:get-current-chat-ui-prop :fullscreen?]) - chat-input-margin (subscribe [:chat-input-margin]) - to-changed-height (subscribe [:chat-animations key :height]) - changes-counter (subscribe [:chat-animations key :changes-counter]) - on-update (expandable-view-on-update {:anim-value anim-value - :to-changed-height to-changed-height - :max-height max-height - :chat-input-margin chat-input-margin - :height height})] - (r/create-class + (let [anim-value (animation/create-value 0) + input-height (re-frame/subscribe [:get-current-chat-ui-prop :input-height]) + max-height (re-frame/subscribe [:get-max-container-area-height]) + fullscreen? (re-frame/subscribe [:get-current-chat-ui-prop :fullscreen?]) + chat-input-margin (re-frame/subscribe [:chat-input-margin]) + to-changed-height (re-frame/subscribe [:chat-animations key :height]) + changes-counter (re-frame/subscribe [:chat-animations key :changes-counter]) + on-update (expandable-view-on-update {:anim-value anim-value + :to-changed-height to-changed-height + :max-height max-height + :chat-input-margin chat-input-margin + :height height})] + (reagent/create-class {:component-did-mount on-update :component-did-update on-update :component-will-unmount (fn [] - (dispatch [:set-chat-ui-props {:fullscreen? false}]) + (re-frame/dispatch [:set-chat-ui-props {:fullscreen? false}]) (if height - (dispatch [:set-expandable-height key height]) - (dispatch [:choose-predefined-expandable-height key :default]))) - :display-name "expandable-view" + (re-frame/dispatch [:set-expandable-height key height]) + (re-frame/dispatch [:choose-predefined-expandable-height key :default]))) + :display-name + "expandable-view" :reagent-render (fn [{:keys [draggable? custom-header]} & elements] @to-changed-height @changes-counter @max-height - (let [bottom (+ @input-height @chat-input-margin) - height (if @fullscreen? @max-height anim-value)] - [view style/overlap-container - (when (and (not hide-overlay?) - (not @fullscreen?)) - [overlay-view]) - (into [animated-view {:style (style/expandable-container height bottom)} + (let [input-height (or @input-height (+ input-style/padding-vertical + input-style/min-input-height + input-style/padding-vertical + input-style/border-height)) + bottom (+ input-height @chat-input-margin) + height (if @fullscreen? @max-height anim-value)] + [react/view style/overlap-container + (into [react/animated-view {:style (style/expandable-container height bottom)} (when (and draggable? (not @fullscreen?)) [header key anim-value custom-header])] diff --git a/src/status_im/chat/views/input/emoji.cljs b/src/status_im/chat/views/input/emoji.cljs deleted file mode 100644 index e0abdb5149..0000000000 --- a/src/status_im/chat/views/input/emoji.cljs +++ /dev/null @@ -1,17 +0,0 @@ -(ns status-im.chat.views.input.emoji - (:require-macros [status-im.utils.views :refer [defview]]) - (:require [re-frame.core :refer [subscribe dispatch]] - [status-im.ui.components.react :refer [view - text - icon - emoji-picker]] - [status-im.chat.styles.input.emoji :as style] - [status-im.i18n :refer [label]])) - -(defview emoji-view [] - [keyboard-max-height [:get :keyboard-max-height]] - [view {:style (style/container keyboard-max-height)} - [view style/emoji-container - [emoji-picker {:style style/emoji-picker - :hideClearButton true - :onEmojiSelected #(dispatch [:add-to-chat-input-text %])}]]]) diff --git a/src/status_im/chat/views/input/input.cljs b/src/status_im/chat/views/input/input.cljs index 40635794fe..8950fe3a83 100644 --- a/src/status_im/chat/views/input/input.cljs +++ b/src/status_im/chat/views/input/input.cljs @@ -1,138 +1,104 @@ (ns status-im.chat.views.input.input (:require-macros [status-im.utils.views :refer [defview letsubs]]) - (:require [clojure.string :as str] - [reagent.core :as r] - [re-frame.core :refer [subscribe dispatch]] - [taoensso.timbre :as log] - [status-im.chat.constants :as const] - [status-im.chat.models.input :as input-model] - [status-im.chat.models.commands :as commands-model] - [status-im.chat.styles.input.input :as style] - [status-im.chat.views.input.emoji :as emoji] + (:require [clojure.string :as string] + [reagent.core :as reagent] + [re-frame.core :as re-frame] + [status-im.chat.constants :as constants] + [status-im.chat.styles.input.input :as style] [status-im.chat.views.input.parameter-box :as parameter-box] - [status-im.chat.views.input.input-actions :as input-actions] [status-im.chat.views.input.result-box :as result-box] + [status-im.chat.views.input.send-button :as send-button] [status-im.chat.views.input.suggestions :as suggestions] [status-im.chat.views.input.validation-messages :as validation-messages] - [status-im.ui.components.animation :as anim] + [status-im.ui.components.animation :as animation] + [status-im.ui.components.colors :as colors] [status-im.ui.components.react :as react] [status-im.ui.components.icons.vector-icons :as vi] - [status-im.i18n :as i18n] - [status-im.utils.platform :as platform] [status-im.utils.utils :as utils])) -(defn command-view [first? command] - [react/touchable-highlight {:on-press #(dispatch [:select-chat-input-command command nil])} - [react/view - [react/text {:style (style/command first?) - :font :roboto-mono} - (commands-model/command-name command)]]]) +(defview basic-text-input [{:keys [set-layout-height-fn set-container-width-fn height single-line-input?]}] + (letsubs [input-text [:chat :input-text] + command [:selected-chat-command] + input-focused? [:get-current-chat-ui-prop :input-focused?] + input-ref (atom nil)] + [react/text-input + {:ref #(when % + (re-frame/dispatch [:set-chat-ui-props {:input-ref %}]) + (reset! input-ref %)) + :accessibility-label :chat-message-input + :multiline (not single-line-input?) + :default-value (or input-text "") + :editable true + :blur-on-submit false + :on-focus #(re-frame/dispatch [:set-chat-ui-props {:input-focused? true}]) + :on-blur #(re-frame/dispatch [:set-chat-ui-props {:input-focused? false}]) + :on-submit-editing (fn [e] + (if single-line-input? + (re-frame/dispatch [:send-current-message]) + (.setNativeProps @input-ref (clj->js {:text (str input-text "\n")})))) + :on-layout (fn [e] + (set-container-width-fn (.-width (.-layout (.-nativeEvent e))))) + :on-change (fn [e] + (let [native-event (.-nativeEvent e) + text (.-text native-event) + content-size (.. native-event -contentSize)] + (when (and (not single-line-input?) + content-size) + (set-layout-height-fn (.-height content-size))) + (when (not= text input-text) + (re-frame/dispatch [:set-chat-input-text text]) + (when command + (re-frame/dispatch [:load-chat-parameter-box (:command command)])) + (re-frame/dispatch [:update-input-data])))) + :on-content-size-change (when (and (not input-focused?) + (not single-line-input?)) + #(let [h (-> (.-nativeEvent %) + (.-contentSize) + (.-height))] + (set-layout-height-fn h))) + :on-selection-change #(let [s (-> (.-nativeEvent %) + (.-selection)) + end (.-end s)] + (re-frame/dispatch [:update-text-selection end])) + :style (style/input-view height single-line-input?) + :placeholder-text-color colors/gray + :auto-capitalize :sentences}])) -(defview commands-view [] - [all-commands-responses [:get-available-commands-responses] - show-suggestions? [:show-suggestions?]] - [react/view style/commands-root - [react/view style/command-list-icon-container - [react/touchable-highlight {:on-press #(dispatch [:show-suggestions])} - [react/view style/commands-list-icon - (if show-suggestions? - [vi/icon :icons/close] - [vi/icon :icons/commands-list])]]] - [react/scroll-view {:horizontal true - :showsHorizontalScrollIndicator false - :keyboardShouldPersistTaps :always} - [react/view style/commands - (for [[index command] (map-indexed vector all-commands-responses)] - ^{:key (str "command-" index)} - [command-view (= index 0) command])]]]) - -(defn- basic-text-input [_] - (let [input-text (subscribe [:chat :input-text]) - command (subscribe [:selected-chat-command]) - input-focused? (subscribe [:get-current-chat-ui-prop :input-focused?]) - input-ref (atom nil)] - (fn [{:keys [set-layout-height-fn set-container-width-fn height single-line-input?]}] - [react/text-input - {:ref #(when % - (dispatch [:set-chat-ui-props {:input-ref %}]) - (reset! input-ref %)) - :accessibility-label :chat-message-input - :multiline (not single-line-input?) - :default-value (or @input-text "") - :editable true - :blur-on-submit false - :on-focus #(dispatch [:set-chat-ui-props {:input-focused? true - :show-emoji? false}]) - :on-blur #(dispatch [:set-chat-ui-props {:input-focused? false}]) - :on-submit-editing (fn [e] - (if single-line-input? - (dispatch [:send-current-message]) - (.setNativeProps @input-ref (clj->js {:text (str @input-text "\n")})))) - :on-layout (fn [e] - (set-container-width-fn (.-width (.-layout (.-nativeEvent e))))) - :on-change (fn [e] - (let [native-event (.-nativeEvent e) - text (.-text native-event) - content-size (.. native-event -contentSize)] - (when (and (not single-line-input?) - content-size) - (set-layout-height-fn (.-height content-size))) - (when (not= text @input-text) - (dispatch [:set-chat-input-text text]) - (when @command - (dispatch [:load-chat-parameter-box (:command @command)])) - (dispatch [:update-input-data])))) - :on-content-size-change (when (and (not @input-focused?) - (not single-line-input?)) - #(let [h (-> (.-nativeEvent %) - (.-contentSize) - (.-height))] - (set-layout-height-fn h))) - :on-selection-change #(let [s (-> (.-nativeEvent %) - (.-selection)) - end (.-end s)] - (dispatch [:update-text-selection end])) - :style (style/input-view height single-line-input?) - :placeholder-text-color style/color-input-helper-placeholder - :auto-capitalize :sentences}]))) - -(defn- invisible-input [{:keys [set-layout-width-fn value]}] - (let [input-text (subscribe [:chat :input-text])] +(defview invisible-input [{:keys [set-layout-width-fn value]}] + (letsubs [input-text [:chat :input-text]] [react/text {:style style/invisible-input-text :on-layout #(let [w (-> (.-nativeEvent %) (.-layout) (.-width))] (set-layout-width-fn w))} - (or @input-text "")])) + (or input-text "")])) -(defn- invisible-input-height [{:keys [set-layout-height-fn container-width]}] - (let [input-text (subscribe [:chat :input-text])] +(defview invisible-input-height [{:keys [set-layout-height-fn container-width]}] + (letsubs [input-text [:chat :input-text]] [react/text {:style (style/invisible-input-text-height container-width) :on-layout #(let [h (-> (.-nativeEvent %) (.-layout) (.-height))] (set-layout-height-fn h))} - (or @input-text "")])) + (or input-text "")])) -(defn- input-helper [_] - (let [input-text (subscribe [:chat :input-text])] - (fn [{:keys [command width]}] - (when-not (get-in command [:command :sequential-params]) - (let [input (str/trim (or @input-text "")) - real-args (remove str/blank? (:args command))] - (when-let [placeholder (cond - (= const/command-char input) - (i18n/label :t/type-a-command) +(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}))))) - (and command (empty? real-args)) - (get-in command [:command :params 0 :placeholder]) +(defview input-helper [{:keys [width]}] + (letsubs [placeholder [:chat-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]])) - (and command - (= (count real-args) 1) - (input-model/text-ends-with-space? input)) - (get-in command [:command :params 1 :placeholder]))] - [react/text {:style (style/input-helper-text width)} - placeholder])))))) (defn get-options [type] (case (keyword type) @@ -141,149 +107,80 @@ :number {:keyboard-type "numeric"} nil)) -(defn- seq-input [_] - (let [command (subscribe [:selected-chat-command]) - arg-pos (subscribe [:current-chat-argument-position]) - seq-arg-input-text (subscribe [:chat :seq-argument-input-text])] - (fn [{:keys [command-width container-width]}] - (when (get-in @command [:command :sequential-params]) - (let [{:keys [placeholder hidden type]} (get-in @command [:command :params @arg-pos])] - [react/text-input (merge {:ref #(dispatch [:set-chat-ui-props {:seq-input-ref %}]) - :style (style/seq-input-text command-width container-width) - :default-value (or @seq-arg-input-text "") - :on-change-text #(do (dispatch [:set-chat-seq-arg-input-text %]) - (dispatch [:load-chat-parameter-box (:command @command)]) - (dispatch [:set-chat-ui-props {:validation-messages nil}])) - :placeholder placeholder - :accessibility-label :chat-request-input - :blur-on-submit false - :editable true - :on-focus #(dispatch [:set-chat-ui-props {:show-emoji? false}]) - :on-submit-editing (fn [] - (when-not (or (str/blank? @seq-arg-input-text) - (get-in @command [:command :hide-send-button])) - (dispatch [:send-seq-argument])) - (utils/set-timeout - #(dispatch [:chat-input-focus :seq-input-ref]) - 100))} - (get-options type))]))))) +(defview seq-input [{:keys [command-width container-width]}] + (letsubs [command [:selected-chat-command] + arg-pos [:current-chat-argument-position] + seq-arg-input-text [:chat :seq-argument-input-text]] + (when (get-in command [:command :sequential-params]) + (let [{:keys [placeholder hidden type]} (get-in command [:command :params arg-pos])] + [react/text-input (merge {:ref #(re-frame/dispatch [:set-chat-ui-props {:seq-input-ref %}]) + :style (style/seq-input-text command-width container-width) + :default-value (or seq-arg-input-text "") + :on-change-text #(do (re-frame/dispatch [:set-chat-seq-arg-input-text %]) + (re-frame/dispatch [:load-chat-parameter-box (:command command)]) + (re-frame/dispatch [:set-chat-ui-props {:validation-messages nil}])) + :placeholder placeholder + :accessibility-label :chat-request-input + :blur-on-submit false + :editable true + :on-submit-editing (fn [] + (when-not (or (string/blank? seq-arg-input-text) + (get-in @command [:command :hide-send-button])) + (re-frame/dispatch [:send-seq-argument])) + (utils/set-timeout + #(re-frame/dispatch [:chat-input-focus :seq-input-ref]) + 100))} + (get-options type))])))) -(defn input-view [_] - (let [component (r/current-component) - set-layout-width-fn #(r/set-state component {:width %}) - set-layout-height-fn #(r/set-state component {:height %}) - set-container-width-fn #(r/set-state component {:container-width %}) - command (subscribe [:selected-chat-command])] - (r/create-class - {:display-name "input-view" - :reagent-render - (fn [{:keys [anim-margin single-line-input?]}] - (let [{:keys [width height container-width]} (r/state component) - command @command] - [react/animated-view {:style (style/input-root height anim-margin)} - [invisible-input {:set-layout-width-fn set-layout-width-fn}] - [invisible-input-height {:set-layout-height-fn set-layout-height-fn - :container-width container-width}] - [basic-text-input {:set-layout-height-fn set-layout-height-fn - :set-container-width-fn set-container-width-fn - :height height - :single-line-input? single-line-input?}] - [input-helper {:command command - :width width}] - [seq-input {:command-width width - :container-width container-width}] - (if-not command - [react/touchable-highlight - {:on-press #(do (dispatch [:toggle-chat-ui-props :show-emoji?]) - (react/dismiss-keyboard!))} - [react/view - [vi/icon :icons/smile {:container-style style/input-emoji-icon}]]] - (when-not single-line-input? - [react/touchable-highlight - {:on-press #(do (dispatch [:set-chat-input-text nil]) - (dispatch [:set-chat-input-metadata nil]) - (dispatch [:set-chat-ui-props {:result-box nil - :validation-messages nil}]) - (dispatch [:clear-seq-arguments]))} - [react/view style/input-clear-container - [vi/icon :icons/close]]]))]))}))) +(defview input-view [{:keys [single-line-input?]}] + (letsubs [command [:selected-chat-command]] + (let [component (reagent/current-component) + set-layout-width-fn #(reagent/set-state component {:width %}) + set-layout-height-fn #(reagent/set-state component {:height %}) + set-container-width-fn #(reagent/set-state component {:container-width %}) + {:keys [width height container-width]} (reagent/state component)] + [react/view {:style style/input-root} + [react/animated-view {:style (style/input-animated height)} + [invisible-input {:set-layout-width-fn set-layout-width-fn}] + [invisible-input-height {:set-layout-height-fn set-layout-height-fn + :container-width container-width}] + [basic-text-input {:set-layout-height-fn set-layout-height-fn + :set-container-width-fn set-container-width-fn + :height height + :single-line-input? single-line-input?}] + [input-helper {:width width}] + [seq-input {:command-width width + :container-width container-width}]]]))) -(defview input-container [{:keys [anim-margin]}] - (letsubs [command-completion [:command-completion] - selected-command [:selected-chat-command] - input-text [:chat :input-text] - seq-arg-input-text [:chat :seq-argument-input-text] - result-box [:get-current-chat-ui-prop :result-box]] - (let [single-line-input? (:singleLineInput result-box) - {:keys [hide-send-button sequential-params]} (:command selected-command)] - [react/view style/input-container - [input-view {:anim-margin anim-margin - :single-line-input? single-line-input?}] - (if (:actions result-box) - [input-actions/input-actions-view] - (when (and (not (str/blank? input-text)) - (or (not selected-command) - (some #{:complete :less-than-needed} [command-completion])) - (not hide-send-button)) - [react/touchable-highlight {:on-press #(if sequential-params - (do - (when-not (str/blank? seq-arg-input-text) - (dispatch [:send-seq-argument])) - (utils/set-timeout - (fn [] (dispatch [:chat-input-focus :seq-input-ref])) - 100)) - (dispatch [:send-current-message]))} - [react/view {:style style/send-message-container - :accessibility-label :send-message-button} - [react/icon :arrow_top style/send-message-icon]]]))]))) +(defn commands-button [] + [react/touchable-highlight + {:on-press #(do (re-frame/dispatch [:set-chat-input-text constants/command-char]) + (react/dismiss-keyboard!))} + [react/view + [vi/icon :icons/input-commands {:container-style style/input-commands-icon + :color :dark}]]]) + +(defview input-container [] + (letsubs [margin [:chat-input-margin] + input-text [:chat :input-text] + result-box [:get-current-chat-ui-prop :result-box]] + (let [single-line-input? (:singleLineInput result-box)] + [react/view {:style (style/root margin) + :on-layout #(let [h (-> (.-nativeEvent %) + (.-layout) + (.-height))] + (when (> h 0) + (re-frame/dispatch [:set-chat-ui-props {:input-height h}])))} + [react/view {:style style/input-container} + [input-view {:single-line-input? single-line-input?}] + (when (string/blank? input-text) + [commands-button]) + [send-button/send-button-view]]]))) (defn container [] - (let [margin (subscribe [:chat-input-margin]) - show-emoji? (subscribe [:get-current-chat-ui-prop :show-emoji?]) - input-text (subscribe [:chat :input-text]) - anim-margin (anim/create-value 10) - container-anim-margin (anim/create-value 16) - bottom-anim-margin (anim/create-value 14)] - (r/create-class - {:display-name "input-container" - :component-did-mount - (fn [] - (when-not (str/blank? @input-text) - (.setValue anim-margin 0) - (.setValue container-anim-margin 8) - (.setValue bottom-anim-margin 8))) - :component-did-update - (fn [component] - (let [{:keys [text-empty?]} (reagent.core/props component)] - (let [to-anim-value (if text-empty? 10 0) - to-container-anim-value (if text-empty? 16 8) - to-bottom-anim-value (if text-empty? 14 8)] - (anim/start - (anim/timing anim-margin {:toValue to-anim-value - :duration 100})) - (anim/start - (anim/timing container-anim-margin {:toValue to-container-anim-value - :duration 100})) - (anim/start - (anim/timing bottom-anim-margin {:toValue to-bottom-anim-value - :duration 100}))))) - - :reagent-render - (fn [] - [react/view style/input-container-view - [parameter-box/parameter-box-view] - [result-box/result-box-view] - [suggestions/suggestions-view] - [validation-messages/validation-messages-view] - [react/view {:style (style/root @margin) - :on-layout #(let [h (-> (.-nativeEvent %) - (.-layout) - (.-height))] - (when (> h 0) - (dispatch [:set-chat-ui-props {:input-height h}])))} - [react/animated-view {:style (style/container container-anim-margin bottom-anim-margin)} - (when (str/blank? @input-text) - [commands-view]) - [input-container {:anim-margin anim-margin}]] - (when @show-emoji? - [emoji/emoji-view])]])}))) + [react/view + [parameter-box/parameter-box-view] + [result-box/result-box-view] + [suggestions/suggestions-view] + [validation-messages/validation-messages-view] + [input-container]]) diff --git a/src/status_im/chat/views/input/input_actions.cljs b/src/status_im/chat/views/input/input_actions.cljs deleted file mode 100644 index e2802674d8..0000000000 --- a/src/status_im/chat/views/input/input_actions.cljs +++ /dev/null @@ -1,25 +0,0 @@ -(ns status-im.chat.views.input.input-actions - (:require [re-frame.core :as re-frame] - [taoensso.timbre :as log] - [status-im.ui.components.react :as react] - [status-im.chat.styles.input.input-actions :as style])) - -(defmulti action-view (fn [{:keys [type]}] (keyword type))) - -(defmethod action-view :default - [{:keys [image executeJs]}] - [react/touchable-highlight - {:on-press #(re-frame/dispatch [:chat-webview-bridge/send-to-bridge - {:event "actions-execute-js" - :js executeJs}])} - [react/view (style/action-view true) - [react/icon (str "action_" image) style/action-view-icon]]]) - -(defn input-actions-view [] - (let [result-box (re-frame/subscribe [:get-current-chat-ui-prop :result-box])] - (fn [] - (let [{:keys [actions]} @result-box] - [react/view style/actions-container - (for [{:keys [type] :as action} actions] - ^{:key type} - [action-view action])])))) \ No newline at end of file diff --git a/src/status_im/chat/views/input/parameter_box.cljs b/src/status_im/chat/views/input/parameter_box.cljs index 7f813229ae..8624d0a773 100644 --- a/src/status_im/chat/views/input/parameter_box.cljs +++ b/src/status_im/chat/views/input/parameter_box.cljs @@ -1,23 +1,32 @@ (ns status-im.chat.views.input.parameter-box - (:require-macros [status-im.utils.views :refer [defview]]) + (:require-macros [status-im.utils.views :refer [defview letsubs]]) (:require [re-frame.core :refer [subscribe dispatch]] - [status-im.chat.views.input.animations.expandable :refer [expandable-view]] + [status-im.chat.views.input.animations.expandable :as expandable] [status-im.chat.views.input.box-header :as box-header] + [status-im.chat.styles.input.parameter-box :as style] [status-im.commands.utils :as command-utils] + [status-im.ui.components.react :as react] [taoensso.timbre :as log])) (defview parameter-box-container [] [{:keys [markup]} [:chat-parameter-box] bot-id-bot-db [:current-bot-db]] (when markup - (command-utils/generate-hiccup markup (first bot-id-bot-db) (second bot-id-bot-db)))) + [react/view style/root + (command-utils/generate-hiccup markup (first bot-id-bot-db) (second bot-id-bot-db))])) (defview parameter-box-view [] - [show-parameter-box? [:show-parameter-box?] - {:keys [title]} [:chat-parameter-box]] - (when show-parameter-box? - [expandable-view {:key :parameter-box - :draggable? true - :custom-header (when title - (box-header/get-header :parameter-box))} - [parameter-box-container]])) + (letsubs [show-parameter-box? [:show-parameter-box?] + parameter-box [:chat-parameter-box]] + (let [{:keys [title height]} parameter-box + draggable? (not height)] + (when show-parameter-box? + [expandable/expandable-view + {:key :parameter-box + :draggable? draggable? + :custom-header (when title + (box-header/get-header :parameter-box)) + :height (when-not draggable? + (+ height (:border-top-width style/root))) + :dynamic-height? (not draggable?)} + [parameter-box-container]])))) diff --git a/src/status_im/chat/views/input/send_button.cljs b/src/status_im/chat/views/input/send_button.cljs new file mode 100644 index 0000000000..e2c729d173 --- /dev/null +++ b/src/status_im/chat/views/input/send_button.cljs @@ -0,0 +1,47 @@ +(ns status-im.chat.views.input.send-button + (:require-macros [status-im.utils.views :refer [defview letsubs]]) + (:require [clojure.string :as string] + [reagent.core :as reagent] + [re-frame.core :as re-frame] + [status-im.chat.styles.input.send-button :as style] + [status-im.ui.components.animation :as animation] + [status-im.ui.components.react :as react] + [status-im.ui.components.icons.vector-icons :as vi] + [status-im.utils.utils :as utils])) + +(defn send-button-view-on-update [{:keys [spin-value opacity-value command-completion]}] + (fn [_] + (let [to-spin-value (if (some #{:complete :no-command} [@command-completion]) 1 0)] + (animation/start + (animation/timing spin-value {:toValue to-spin-value + :duration 300}))))) + +(defview send-button-view [] + (letsubs [command-completion [:command-completion] + selected-command [:selected-chat-command] + input-text [:chat :input-text] + seq-arg-input-text [:chat :seq-argument-input-text] + spin-value (animation/create-value 1) + on-update (send-button-view-on-update {:spin-value spin-value + :command-completion command-completion})] + {:component-did-update on-update} + (let [{:keys [hide-send-button sequential-params]} (:command selected-command)] + (when (and (not (string/blank? input-text)) + (or (not selected-command) + (some #{:complete :less-than-needed} [command-completion])) + (not hide-send-button)) + [react/touchable-highlight {:on-press #(if sequential-params + (do + (when-not (string/blank? seq-arg-input-text) + (re-frame/dispatch [:send-seq-argument])) + (utils/set-timeout + (fn [] (re-frame/dispatch [:chat-input-focus :seq-input-ref])) + 100)) + (re-frame/dispatch [:send-current-message]))} + (let [spin (.interpolate spin-value (clj->js {:inputRange [0 1] + :outputRange ["0deg" "90deg"]}))] + [react/animated-view + {:style (style/send-message-container spin) + :accessibility-label :send-message-button} + [vi/icon :icons/input-send {:container-style style/send-message-icon + :color :white}]])])))) \ No newline at end of file diff --git a/src/status_im/chat/views/input/suggestions.cljs b/src/status_im/chat/views/input/suggestions.cljs index 7c999efbc6..a52ce93b1d 100644 --- a/src/status_im/chat/views/input/suggestions.cljs +++ b/src/status_im/chat/views/input/suggestions.cljs @@ -1,18 +1,18 @@ (ns status-im.chat.views.input.suggestions - (:require-macros [status-im.utils.views :refer [defview]]) + (: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.chat.styles.input.suggestions :as style] [status-im.chat.views.input.animations.expandable :as expandable] - [status-im.chat.models.commands :as commands-model] - [status-im.i18n :as i18n])) + [status-im.chat.models.commands :as commands-model] + [status-im.i18n :as i18n] + [taoensso.timbre :as log])) (defn suggestion-item [{:keys [on-press name description last?]}] [react/touchable-highlight {:on-press on-press} [react/view (style/item-suggestion-container last?) - [react/view {:style style/item-suggestion-name} - [react/text {:style style/item-suggestion-name-text - :font :roboto-mono} name]] + [react/text {:style style/item-suggestion-name} + name] [react/text {:style style/item-suggestion-description :number-of-lines 2} description]]]) @@ -34,30 +34,25 @@ :description description :last? last?}]) -(defn item-title [top-padding? title] - [react/view (style/item-title-container top-padding?) - [react/text {:style style/item-title-text} - title]]) - (defview suggestions-view [] - [show-suggestions? [:show-suggestions?] - responses [:get-available-responses] - commands [:get-available-commands]] - (when show-suggestions? - [expandable/expandable-view {:key :suggestions - :draggable? false - :height 212} - [react/view {:flex 1} - [react/scroll-view {:keyboardShouldPersistTaps :always} - (when (seq responses) - [react/view - [item-title false (i18n/label :t/suggestions-requests)] - (for [[i response] (map-indexed vector responses)] - ^{:key i} - [response-item response (= i (dec (count responses)))])]) - (when (seq commands) - [react/view - [item-title (seq responses) (i18n/label :t/suggestions-commands)] - (for [[i command] (map-indexed vector commands)] - ^{:key i} - [command-item command (= i (dec (count commands)))])])]]])) + (letsubs [show-suggestions-view? [:show-suggestions-view?] + responses [:get-available-responses] + commands [:get-available-commands]] + (let [number-of-entries (+ (count responses) (count commands))] + [expandable/expandable-view {:key :suggestions + :draggable? false + :height (* number-of-entries + (+ style/item-height + style/border-height)) + :dynamic-height? true} + [react/view + [react/scroll-view {:keyboard-should-persist-taps :always + :bounces false} + (when (seq responses) + (for [[i response] (map-indexed vector responses)] + ^{:key i} + [response-item response (= i (dec (count responses)))])) + (when (seq commands) + (for [[i command] (map-indexed vector commands)] + ^{:key i} + [command-item command (= i (dec (count commands)))]))]]]))) diff --git a/src/status_im/chat/views/message/message.cljs b/src/status_im/chat/views/message/message.cljs index e97f47ec4b..224443131a 100644 --- a/src/status_im/chat/views/message/message.cljs +++ b/src/status_im/chat/views/message/message.cljs @@ -305,8 +305,6 @@ (fn [{:keys [outgoing group-chat content-type content] :as message}] [message-container message [react/touchable-highlight {:on-press #(when platform/ios? - (re-frame/dispatch [:set-chat-ui-props - {:show-emoji? false}]) (react/dismiss-keyboard!)) :on-long-press #(when (= content-type constants/text-content-type) (list-selection/share content (i18n/label :t/message)))} diff --git a/src/status_im/i18n.cljs b/src/status_im/i18n.cljs index 0820877a0a..a0719b1385 100644 --- a/src/status_im/i18n.cljs +++ b/src/status_im/i18n.cljs @@ -206,7 +206,7 @@ :transactions-filter-type :next :recent :open-on-etherscan :share :status :from :wrong-password :search-chats :transactions-sign-later :in-contacts - :transactions-sign :sharing-share :type-a-message :type-a-command + :transactions-sign :sharing-share :type-a-message :usd-currency :existing-networks :node-unavailable :url :shake-your-phone :add-network :unknown-status-go-error :contacts-group-new-chat :and-you :wallets :clear-history :wallet-choose-from-contacts diff --git a/src/status_im/ui/components/colors.cljs b/src/status_im/ui/components/colors.cljs index 79c21735bb..cb41b5a66c 100644 --- a/src/status_im/ui/components/colors.cljs +++ b/src/status_im/ui/components/colors.cljs @@ -11,5 +11,5 @@ (def gray-lighter "#eef2f5") ;; Used as a background or shadow (def blue "#4360df") ;; Used as main wallet color (def red "#ff2d55") ;; Used to highlight errors or "dangerous" actions -(def light-gray "#eef2f5") ;; Used as a background or shadow +(def light-gray "#e8ebec") ;; Used as a background or shadow (def text-light-gray "#212121") ;; Used for labels (home items) diff --git a/src/status_im/ui/components/icons/vector_icons.cljs b/src/status_im/ui/components/icons/vector_icons.cljs index 1da1a32c8a..9f22eb890e 100644 --- a/src/status_im/ui/components/icons/vector_icons.cljs +++ b/src/status_im/ui/components/icons/vector_icons.cljs @@ -62,8 +62,8 @@ :icons/public (slurp/slurp-svg "./resources/icons/public.svg") :icons/public-chat (slurp/slurp-svg "./resources/icons/public_chat.svg") :icons/qr (slurp/slurp-svg "./resources/icons/QR.svg") - :icons/smile (slurp/slurp-svg "./resources/icons/smile.svg") - :icons/commands-list (slurp/slurp-svg "./resources/icons/commands_list.svg") + :icons/input-commands (slurp/slurp-svg "./resources/icons/input_commands.svg") + :icons/input-send (slurp/slurp-svg "./resources/icons/input_send.svg") :icons/back (slurp/slurp-svg "./resources/icons/back.svg") :icons/forward (slurp/slurp-svg "./resources/icons/forward.svg") :icons/dropdown-up (slurp/slurp-svg "./resources/icons/dropdown_up.svg") diff --git a/src/status_im/ui/components/react.cljs b/src/status_im/ui/components/react.cljs index 5937f2d4e9..ddcffd33f3 100644 --- a/src/status_im/ui/components/react.cljs +++ b/src/status_im/ui/components/react.cljs @@ -161,14 +161,6 @@ (.then clipboard-contents #(clbk %)))) -;; Emoji - -(def emoji-picker-class js-dependencies/emoji-picker) - -(def emoji-picker - (let [emoji-picker (.-default emoji-picker-class)] - (reagent/adapt-react-class emoji-picker))) - ;; Autolink (def autolink-class (reagent/adapt-react-class (.-default js-dependencies/autolink)))