[#3014]: Updated chat input

Signed-off-by: Andrey Shovkoplyas <motor4ik@gmail.com>
This commit is contained in:
alwxndr 2018-02-08 08:14:05 +01:00 committed by Andrey Shovkoplyas
parent 058e6c5412
commit 2aa6c1992f
No known key found for this signature in database
GPG Key ID: EAAB7C8622D860A4
33 changed files with 532 additions and 688 deletions

View File

@ -38,7 +38,6 @@
"web3",
"eccjs",
"chance",
"react-native-emoji-picker",
"react-native-autolink",
"instabug-reactnative",
"nfc-react-native",

View File

@ -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",

View File

@ -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

View File

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path fill="" fill-rule="evenodd" d="M9,7 C9,6.44771525 9.4556644,6 9.99539757,6 L18.0046024,6 C18.5543453,6 19,6.44386482 19,7 C19,7.55228475 18.5443356,8 18.0046024,8 L9.99539757,8 C9.44565467,8 9,7.55613518 9,7 Z M9,12 C9,11.4477153 9.45303631,11 9.99703014,11 L16.0029699,11 C16.5536144,11 17,11.4438648 17,12 C17,12.5522847 16.5469637,13 16.0029699,13 L9.99703014,13 C9.4463856,13 9,12.5561352 9,12 Z M9,17 C9,16.4477153 9.4556644,16 9.99539757,16 L18.0046024,16 C18.5543453,16 19,16.4438648 19,17 C19,17.5522847 18.5443356,18 18.0046024,18 L9.99539757,18 C9.44565467,18 9,17.5561352 9,17 Z M6,8.29999971 C5.28202985,8.29999971 4.70000005,7.71796991 4.70000005,6.99999976 C4.70000005,6.28202961 5.28202985,5.69999981 6,5.69999981 C6.71797015,5.69999981 7.29999995,6.28202961 7.29999995,6.99999976 C7.29999995,7.71796991 6.71797015,8.29999971 6,8.29999971 Z M6,13.2999997 C5.28202985,13.2999997 4.70000005,12.7179699 4.70000005,11.9999998 C4.70000005,11.2820296 5.28202985,10.6999998 6,10.6999998 C6.71797015,10.6999998 7.29999995,11.2820296 7.29999995,11.9999998 C7.29999995,12.7179699 6.71797015,13.2999997 6,13.2999997 Z M6,18.2999997 C5.28202985,18.2999997 4.70000005,17.7179699 4.70000005,16.9999998 C4.70000005,16.2820296 5.28202985,15.6999998 6,15.6999998 C6.71797015,15.6999998 7.29999995,16.2820296 7.29999995,16.9999998 C7.29999995,17.7179699 6.71797015,18.2999997 6,18.2999997 Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path fill="#6E777E" fill-rule="nonzero" d="M7 5a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2H7zm0-2h10a4 4 0 0 1 4 4v10a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4V7a4 4 0 0 1 4-4zm6.618 5H14a.618.618 0 0 1 .553.894l-3.277 6.553a1 1 0 0 1-.894.553H10a.618.618 0 0 1-.553-.894l3.277-6.553A1 1 0 0 1 13.618 8z"/>
</svg>

After

Width:  |  Height:  |  Size: 412 B

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 22 22">
<path fill="#FFF" fill-rule="evenodd" d="M11.882 8.686l2.047 2.057a.87.87 0 1 0 1.227-1.233l-3.53-3.547a.865.865 0 0 0-.537-.251.866.866 0 0 0-.718.251L6.841 9.51a.88.88 0 0 0 0 1.234.864.864 0 0 0 1.228-.001l2.049-2.059v6.732a.88.88 0 0 0 .882.878c.49 0 .882-.393.882-.878v-6.73z"/>
</svg>

After

Width:  |  Height:  |  Size: 379 B

View File

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path fill="" fill-rule="evenodd" d="M11.9908333,2.83333333 C6.93083333,2.83333333 2.83333333,6.94 2.83333333,12 C2.83333333,17.06 6.93083333,21.1666667 11.9908333,21.1666667 C17.06,21.1666667 21.1666667,17.06 21.1666667,12 C21.1666667,6.94 17.06,2.83333333 11.9908333,2.83333333 L11.9908333,2.83333333 Z M12,19.3333333 C7.94833333,19.3333333 4.66666667,16.0516667 4.66666667,12 C4.66666667,7.94833333 7.94833333,4.66666667 12,4.66666667 C16.0516667,4.66666667 19.3333333,7.94833333 19.3333333,12 C19.3333333,16.0516667 16.0516667,19.3333333 12,19.3333333 L12,19.3333333 Z M15.2083333,11.0833333 C15.9691667,11.0833333 16.5833333,10.4691667 16.5833333,9.70833333 C16.5833333,8.9475 15.9691667,8.33333333 15.2083333,8.33333333 C14.4475,8.33333333 13.8333333,8.9475 13.8333333,9.70833333 C13.8333333,10.4691667 14.4475,11.0833333 15.2083333,11.0833333 L15.2083333,11.0833333 Z M8.79166667,11.0833333 C9.5525,11.0833333 10.1666667,10.4691667 10.1666667,9.70833333 C10.1666667,8.9475 9.5525,8.33333333 8.79166667,8.33333333 C8.03083333,8.33333333 7.41666667,8.9475 7.41666667,9.70833333 C7.41666667,10.4691667 8.03083333,11.0833333 8.79166667,11.0833333 L8.79166667,11.0833333 Z M15.5330465,14.6132288 C16.1687926,13.8879527 15.9015413,13.3000002 14.9440105,13.3000002 L9.05598946,13.3000002 C8.09492777,13.3000002 7.8208627,13.9096537 8.46695345,14.6132288 C8.46695345,14.6132288 10,16.8000002 12,16.8000002 C14,16.8000002 15.5330465,14.6132288 15.5330465,14.6132288 Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -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({

View File

@ -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))))

View File

@ -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})

View File

@ -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)

View File

@ -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

View File

@ -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})

View File

@ -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})

View File

@ -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})

View File

@ -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})

View File

@ -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

View File

@ -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})

View File

@ -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})

View File

@ -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]

View File

@ -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}]]))

View File

@ -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])]

View File

@ -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 %])}]]])

View File

@ -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]])

View File

@ -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])]))))

View File

@ -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]]))))

View File

@ -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}]])]))))

View File

@ -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)))]))]]])))

View File

@ -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)))}

View File

@ -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

View File

@ -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)

View File

@ -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")

View File

@ -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)))