[#13517] UI Component - Message Input (#13620)

This commit is contained in:
flexsurfer 2022-09-06 11:19:05 +02:00 committed by GitHub
parent 7f50b56c26
commit 1d87957b04
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 444 additions and 187 deletions

View File

@ -330,8 +330,8 @@ PODS:
- SDWebImageWebPCoder (~> 0.8.4)
- RNFS (2.16.6):
- React
- RNGestureHandler (1.8.0):
- React
- RNGestureHandler (2.5.0):
- React-Core
- RNHoleView (2.1.1):
- React-Core
- RNImageCropPicker (0.36.2):
@ -691,7 +691,7 @@ SPEC CHECKSUMS:
RNCPushNotificationIOS: c145c6253ea016e5efeff604f2720736b4a596f7
RNFastImage: 1f2cab428712a4baaf78d6169eaec7f622556dd7
RNFS: 2bd9eb49dc82fa9676382f0585b992c424cd59df
RNGestureHandler: 7a5833d0f788dbd107fbb913e09aa0c1ff333c39
RNGestureHandler: bad495418bcbd3ab47017a38d93d290ebd406f50
RNHoleView: 07572d21c97fad71fdc47f7248a8513e15a38949
RNImageCropPicker: 35a3ceb837446fa11547704709bb22b5fac6d584
RNKeychain: 216f37338fcb9e5c3a2530f1e3295f737a690cb1

View File

@ -49,7 +49,7 @@
"react-native-fast-image": "^8.5.11",
"react-native-fetch-polyfill": "^1.1.2",
"react-native-fs": "^2.14.1",
"react-native-gesture-handler": "^1.8.0",
"react-native-gesture-handler": "^2.5.0",
"react-native-haptic-feedback": "^1.9.0",
"react-native-hole-view": "git+https://github.com/status-im/react-native-hole-view.git#refs/tags/v2.1.1-status",
"react-native-image-crop-picker": "git+https://github.com/status-im/react-native-image-crop-picker.git#refs/tags/v0.36.2-status.0",

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 964 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 994 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -219,6 +219,8 @@
:FlatList #js {}
:ScrollView #js {}
:TouchableOpacity #js {}
:GestureDetector #js {}
:Gesture #js {:Pan nil}
:createNativeWrapper identity})
(def react-native-redash #js {:clamp nil})

View File

@ -1,13 +1,11 @@
(ns quo.gesture-handler
(:require [oops.core :refer [oget]]
["react-native-reanimated" :default animated]
[reagent.core :as reagent]
[quo.design-system.colors :as colors]
["react-native-gesture-handler"
:refer (TapGestureHandler PanGestureHandler LongPressGestureHandler
PureNativeButton TouchableWithoutFeedback TouchableOpacity
TouchableHighlight
createNativeWrapper State NativeViewGestureHandler
TouchableWithoutFeedback TouchableOpacity
TouchableHighlight State NativeViewGestureHandler
FlatList ScrollView)]))
(def flat-list-raw FlatList)
@ -25,8 +23,6 @@
(def long-press-gesture-handler
(reagent/adapt-react-class LongPressGestureHandler))
(def pure-native-button PureNativeButton)
(def touchable-without-feedback-class TouchableWithoutFeedback)
(def touchable-without-feedback
@ -42,12 +38,6 @@
(def touchable-opacity
(reagent/adapt-react-class TouchableOpacity))
(def raw-button
(reagent/adapt-react-class
(createNativeWrapper (.createAnimatedComponent animated PureNativeButton)
#js {:shouldActivateOnStart true
:shouldCancelWhenOutside true})))
(def native-view-gesture-handler (reagent/adapt-react-class NativeViewGestureHandler))
(def states {:began (oget State "BEGAN")

13
src/quo2/gesture.cljs Normal file
View File

@ -0,0 +1,13 @@
(ns quo2.gesture
(:require ["react-native-gesture-handler" :refer (GestureDetector Gesture)]
[reagent.core :as reagent]))
(def gesture-detector (reagent/adapt-react-class GestureDetector))
(defn gesture-pan [] (.Pan ^js Gesture))
(defn on-update [^js pan handler] (.onUpdate pan handler))
(defn on-start [^js pan handler] (.onStart pan handler))
(defn on-end [^js pan handler] (.onEnd pan handler))

View File

@ -3,7 +3,7 @@
[reagent.core :as reagent]
[clojure.string :as string]
["react-native-reanimated" :default reanimated
:refer (useSharedValue useAnimatedStyle withTiming withDelay Easing Keyframe)]))
:refer (useSharedValue useAnimatedStyle withTiming withDelay withSpring Easing Keyframe)]))
;; Animated Components
(def create-animated-component (comp reagent/adapt-react-class (.-createAnimatedComponent reanimated)))
@ -19,6 +19,7 @@
;; Animations
(def with-timing withTiming)
(def with-delay withDelay)
(def with-spring withSpring)
(def key-frame Keyframe)
;; Easings

View File

@ -27,10 +27,10 @@
(def switcher-bottom-positions
{:android
{:home-stack 15
:chat 57}
:chat 140}
:ios
{:home-stack 40
:chat 67}})
:chat 140}})
(defn switcher-bottom-position [view-id]
(get-in

View File

@ -8,7 +8,8 @@
[status-im.switcher.animation :as animation]
[status-im.ui.components.icons.icons :as icons]
[status-im.react-native.resources :as resources]
[status-im.switcher.switcher-container :as switcher-container]))
[status-im.switcher.switcher-container :as switcher-container]
[quo.react-native :as rn]))
(defn switcher-button [view-id toggle-switcher-screen-fn shared-values]
[:f>
@ -71,7 +72,9 @@
:switcher-container-scale (reanimated/use-shared-value 0.9)
:close-button-opacity (animation/switcher-close-button-opacity switcher-button-opacity)
:switcher-container-bottom (animation/switcher-container-bottom-position switcher-screen-bottom)}
toggle-switcher-screen-fn #(animation/switcher-touchable-on-press-out switcher-opened? view-id shared-values)]
[:<>
[switcher-screen toggle-switcher-screen-fn shared-values]
[switcher-button view-id toggle-switcher-screen-fn shared-values]]))])
toggle-switcher-screen-fn #(animation/switcher-touchable-on-press-out switcher-opened? view-id shared-values)
{:keys [keyboard-shown]} (rn/use-keyboard)]
(when-not keyboard-shown
[:<>
[switcher-screen toggle-switcher-screen-fn shared-values]
[switcher-button view-id toggle-switcher-screen-fn shared-values]])))])

View File

@ -248,6 +248,14 @@
(update props :keyboardVerticalOffset + 44 (:status-bar-height @navigation-const))))]
children))
(defn keyboard-avoiding-view-new [props & children]
(into [keyboard-avoiding-view-class
(merge (when platform/ios? {:behavior :padding})
(if (:ignore-offset props)
props
(update props :keyboardVerticalOffset + 44)))]
children))
(defn scroll-view [props & children]
(vec (conj children props scroll-view-class)))

View File

@ -1,6 +1,7 @@
(ns status-im.ui.screens.chat.components.input
(:require [status-im.ui.components.icons.icons :as icons]
[quo.react-native :as rn]
[oops.core :refer [oget]]
[quo.react :as react]
[quo.platform :as platform]
[quo.components.text :as text]
@ -19,7 +20,11 @@
[quo.components.list.item :as list-item]
[status-im.ui.screens.chat.photos :as photos]
[reagent.core :as reagent]
[clojure.string :as string]))
[clojure.string :as string]
[quo2.components.button :as quo2.button]
[quo2.reanimated :as reanimated]
[quo2.gesture :as gesture]
[quo.components.safe-area :as safe-area]))
(defn input-focus [text-input-ref]
(some-> ^js (react/current-ref text-input-ref) .focus))
@ -58,8 +63,9 @@
:on-denied
#(utils.utils/set-timeout
(fn []
(utils.utils/show-popup (i18n/label :t/audio-recorder-error)
(i18n/label :t/audio-recorder-permissions-error)))
(utils.utils/show-popup
(i18n/label :t/audio-recorder-error)
(i18n/label :t/audio-recorder-permissions-error)))
50)}]))
(defn touchable-audio-icon [{:keys [panel active set-active accessibility-label input-focus]}]
@ -82,6 +88,15 @@
:accessibility-label :send-message-button
:color (styles/send-icon-color)}])]])
(defn send-button-old [on-send contact-request]
[rn/touchable-opacity {:on-press-in on-send}
[rn/view {:style (styles/send-message-button)}
(when-not contact-request
[icons/icon :main-icons/arrow-up
{:container-style (styles/send-message-container contact-request)
:accessibility-label :send-message-button
:color (styles/send-icon-color)}])]])
(defn on-selection-change [timeout-id last-text-change mentionable-users args]
(let [selection (.-selection ^js (.-nativeEvent ^js args))
start (.-start selection)
@ -128,14 +143,18 @@
(swap! chat-input-key inc))
(defn show-send [{:keys [actions-ref send-ref sticker-ref]}]
(quo.react/set-native-props actions-ref #js {:width 0 :left -88})
(when actions-ref
(quo.react/set-native-props actions-ref #js {:width 0 :left -88}))
(quo.react/set-native-props send-ref #js {:width nil :right nil})
(quo.react/set-native-props sticker-ref #js {:width 0 :right -100}))
(when sticker-ref
(quo.react/set-native-props sticker-ref #js {:width 0 :right -100})))
(defn hide-send [{:keys [actions-ref send-ref sticker-ref]}]
(quo.react/set-native-props actions-ref #js {:width nil :left nil})
(when actions-ref
(quo.react/set-native-props actions-ref #js {:width nil :left nil}))
(quo.react/set-native-props send-ref #js {:width 0 :right -100})
(quo.react/set-native-props sticker-ref #js {:width nil :right nil}))
(when sticker-ref
(quo.react/set-native-props sticker-ref #js {:width nil :right nil})))
(defn reset-input [refs chat-id]
(some-> ^js (react/current-ref (:text-input-ref refs)) .clear)
@ -235,7 +254,7 @@
(when platform/android?
(re-frame/dispatch [::mentions/calculate-suggestions mentionable-users]))))
(defn text-input [{:keys [set-active-panel refs chat-id sending-image]}]
(defn text-input-old [{:keys [set-active-panel refs chat-id sending-image]}]
(let [cooldown-enabled? @(re-frame/subscribe [:chats/cooldown-enabled?])
mentionable-users @(re-frame/subscribe [:chats/mentionable-users])
timeout-id (atom nil)
@ -244,7 +263,7 @@
contact-request @(re-frame/subscribe [:chats/sending-contact-request])]
[rn/text-input
{:style (styles/text-input contact-request)
{:style (styles/text-input-old contact-request)
:ref (:text-input-ref refs)
:max-font-size-multiplier 1
:accessibility-label :chat-message-input
@ -274,6 +293,47 @@
text])
(get @input-texts chat-id))]))
(defn text-input [{:keys [set-active-panel refs chat-id sending-image on-content-size-change]}]
(let [cooldown-enabled? @(re-frame/subscribe [:chats/cooldown-enabled?])
mentionable-users @(re-frame/subscribe [:chats/mentionable-users])
timeout-id (atom nil)
last-text-change (atom nil)
mentions-enabled (get @mentions-enabled chat-id)]
[rn/text-input
{:style (styles/text-input)
:ref (:text-input-ref refs)
:max-font-size-multiplier 1
:accessibility-label :chat-message-input
:text-align-vertical :center
:multiline true
:editable (not cooldown-enabled?)
:blur-on-submit false
:auto-focus false
:on-focus #(set-active-panel nil)
:max-length chat.constants/max-text-size
:placeholder-text-color (:text-02 @colors/theme)
:placeholder (if cooldown-enabled?
(i18n/label :cooldown/text-input-disabled)
(i18n/label :t/type-a-message))
:underline-color-android :transparent
:auto-capitalize :sentences
:auto-correct false
:spell-check false
:on-content-size-change on-content-size-change
:on-selection-change (partial on-selection-change timeout-id last-text-change mentionable-users)
:on-change (partial on-change last-text-change timeout-id mentionable-users refs chat-id sending-image)
:on-text-input (partial on-text-input mentionable-users chat-id)}
(if mentions-enabled
(for [[idx [type text]] (map-indexed
(fn [idx item]
[idx item])
@(re-frame/subscribe [:chat/input-with-mentions]))]
^{:key (str idx "_" type "_" text)}
[rn/text (when (= type :mention) {:style {:color "#0DA4C9"}})
text])
(get @input-texts chat-id))]))
(defn mention-item
[[public-key {:keys [alias name nickname] :as user}] _ _ text-input-ref]
(let [ens-name? (not= alias name)]
@ -350,12 +410,12 @@
:active active-panel
:set-active set-active-panel}])])
(defn chat-toolbar [{:keys [chat-id]}]
(let [actions-ref (quo.react/create-ref)
send-ref (quo.react/create-ref)
sticker-ref (quo.react/create-ref)
(defn chat-toolbar-old [{:keys [chat-id]}]
(let [actions-ref (quo.react/create-ref)
send-ref (quo.react/create-ref)
sticker-ref (quo.react/create-ref)
toolbar-options (re-frame/subscribe [:chats/chat-toolbar])
show-send (seq (get @input-texts chat-id))]
show-send (seq (get @input-texts chat-id))]
(fn [{:keys [active-panel set-active-panel text-input-ref chat-id]}]
(let [;we want to control components on native level, so instead of RN state we set native props via reference
;we don't react on input text in this view, @input-texts below is a regular atom
@ -364,24 +424,24 @@
:sticker-ref sticker-ref
:text-input-ref text-input-ref}
{:keys [send stickers image extensions audio sending-image]} @toolbar-options
show-send (or show-send sending-image)
show-send (or show-send sending-image)
contact-request @(re-frame/subscribe [:chats/sending-contact-request])]
[rn/view {:style (styles/toolbar)
:on-layout on-chat-toolbar-layout}
;;EXTENSIONS and IMAGE buttons
;;EXTENSIONS and IMAGE buttons
[actions extensions image show-send actions-ref active-panel set-active-panel contact-request]
[rn/view {:style (styles/input-container contact-request)}
[send-image]
[rn/view {:style styles/input-row}
[text-input {:chat-id chat-id
:sending-image sending-image
:refs refs
:set-active-panel set-active-panel}]
[text-input-old {:chat-id chat-id
:sending-image sending-image
:refs refs
:set-active-panel set-active-panel}]
;;SEND button
[rn/view {:ref send-ref :style (when-not show-send {:width 0 :right -100})}
(when send
[send-button #(do (clear-input chat-id refs)
(re-frame/dispatch [:chat.ui/send-current-message]))
[send-button-old #(do (clear-input chat-id refs)
(re-frame/dispatch [:chat.ui/send-current-message]))
contact-request])]
;;STICKERS and AUDIO buttons
@ -400,3 +460,163 @@
:active active-panel
:input-focus #(input-focus text-input-ref)
:set-active set-active-panel}])])]]]))))
(defn calculate-y [context keyboard-shown min-y max-y]
(if keyboard-shown
(if (= (:state @context) :max)
max-y
(if (< (:y @context) max-y)
(:y @context)
(do
(swap! context assoc :state :max)
max-y)))
(do
(swap! context assoc :state :min)
min-y)))
(defn get-bottom-sheet-gesture [context translate-y text-input-ref keyboard-shown min-y max-y shared-height max-height bg-opacity]
(-> (gesture/gesture-pan)
(gesture/on-start
(fn [_]
(if keyboard-shown
(swap! context assoc :pan-y (reanimated/get-shared-value translate-y))
(input-focus text-input-ref))))
(gesture/on-update
(fn [evt]
(when keyboard-shown
(swap! context assoc :dy (- (.-translationY evt) (:pdy @context)))
(swap! context assoc :pdy (.-translationY evt))
(reanimated/set-shared-value
translate-y
(max (min (+ (.-translationY evt) (:pan-y @context)) (- min-y)) (- max-y))))))
(gesture/on-end
(fn [_]
(when keyboard-shown
(if (< (:dy @context) 0)
(do
(swap! context assoc :state :max)
(input-focus text-input-ref)
(reanimated/set-shared-value translate-y (reanimated/with-timing (- max-y)))
(reanimated/set-shared-value shared-height (reanimated/with-timing max-height))
(reanimated/set-shared-value bg-opacity (reanimated/with-timing 1)))
(do
(swap! context assoc :state :min)
(reanimated/set-shared-value translate-y (reanimated/with-timing (- min-y)))
(reanimated/set-shared-value shared-height (reanimated/with-timing min-y))
(reanimated/set-shared-value bg-opacity (reanimated/with-timing 0))
(re-frame/dispatch [:dismiss-keyboard]))))))))
(defn get-input-content-change [context translate-y shared-height max-height bg-opacity keyboard-shown min-y max-y]
(fn [evt]
(if (:clear @context)
(do
(swap! context dissoc :clear)
(swap! context assoc :state :min)
(swap! context assoc :y min-y)
(reanimated/set-shared-value translate-y (reanimated/with-timing (- min-y)))
(reanimated/set-shared-value shared-height (reanimated/with-timing min-y))
(reanimated/set-shared-value bg-opacity (reanimated/with-timing 0)))
(when (not= (:state @context) :max)
(let [new-y (+ min-y (- (max (oget evt "nativeEvent" "contentSize" "height") 22) 22))]
(if (< new-y max-y)
(do
(if (> (- max-y new-y) 120)
(do
(swap! context assoc :state :custom-chat-available)
(reanimated/set-shared-value bg-opacity (reanimated/with-timing 0)))
(do
(reanimated/set-shared-value bg-opacity (reanimated/with-timing 1))
(swap! context assoc :state :custom-chat-unavailable)))
(swap! context assoc :y new-y)
(when keyboard-shown
(reanimated/set-shared-value
translate-y
(reanimated/with-timing (- new-y)))
(reanimated/set-shared-value
shared-height
(reanimated/with-timing (min new-y max-height)))))
(do
(swap! context assoc :state :max)
(swap! context assoc :y max-y)
(when keyboard-shown
(reanimated/set-shared-value bg-opacity (reanimated/with-timing 1))
(reanimated/set-shared-value
translate-y
(reanimated/with-timing (- max-y)))))))))))
(defn chat-input-bottom-sheet [chat-id]
[safe-area/consumer
(fn [insets]
(let [min-y 112
context (atom {:y min-y ;current y value
:min-y min-y ;minimum y value
:dy 0 ;used for gesture
:pdy 0 ;used for gesture
:state :min ;:min, :custom-chat-available, :custom-chat-unavailable, :max
:clear false})
keyboard-was-shown (atom false)]
(fn []
[:f>
(fn []
(let [text-input-ref (quo.react/create-ref)
send-ref (quo.react/create-ref)
refs {:send-ref send-ref
:text-input-ref text-input-ref}
{window-height :height} (rn/use-window-dimensions)
{:keys [keyboard-shown keyboard-height]} (rn/use-keyboard)
max-y (- window-height (if (> keyboard-height 0) keyboard-height 360) (:top insets)) ; 360 - defaul height
max-height (- max-y 56 (:bottom insets)) ; 56 - topbar height
y (calculate-y context keyboard-shown min-y max-y)
translate-y (reanimated/use-shared-value 0)
shared-height (reanimated/use-shared-value min-y)
bg-opacity (reanimated/use-shared-value 0)
bottom-sheet-gesture (get-bottom-sheet-gesture context translate-y text-input-ref keyboard-shown
min-y max-y shared-height max-height bg-opacity)
input-content-change (get-input-content-change context translate-y shared-height max-height
bg-opacity keyboard-shown min-y max-y)]
(quo.react/effect! #(do
(when (and @keyboard-was-shown (not keyboard-shown))
(swap! context assoc :state :min))
(reset! keyboard-was-shown keyboard-shown)
(if (#{:max :custom-chat-unavailable} (:state @context))
(reanimated/set-shared-value bg-opacity (reanimated/with-timing 1))
(reanimated/set-shared-value bg-opacity (reanimated/with-timing 0)))
(reanimated/set-shared-value translate-y (reanimated/with-timing (- y)))
(reanimated/set-shared-value shared-height (reanimated/with-timing (min y max-height)))))
[reanimated/view {:style (reanimated/apply-animations-to-style
{:height shared-height}
{})}
;;INPUT MESSAGE bottom sheet
[gesture/gesture-detector {:gesture bottom-sheet-gesture}
[reanimated/view {:style (reanimated/apply-animations-to-style
{:transform [{:translateY translate-y}]}
(styles/new-input-bottom-sheet window-height))}
;handle
[rn/view {:style (styles/new-bottom-sheet-handle)}]
[rn/view {:style {:height (- max-y 80)}}
[text-input {:chat-id chat-id
:on-content-size-change input-content-change
:sending-image false
:refs refs
:set-active-panel #()}]]]]
;CONTROLS
[rn/view {:style (styles/new-bottom-sheet-controls insets)}
[quo2.button/button {:icon true :type :outline :size 32} :main-icons2/image]
[rn/view {:width 12}]
[quo2.button/button {:icon true :type :outline :size 32} :main-icons2/reaction]
[rn/view {:flex 1}]
;;SEND button
[rn/view {:ref send-ref :style (when-not (seq (get @input-texts chat-id)) {:width 0 :right -100})}
[quo2.button/button {:icon true :size 32 :accessibility-label :send-message-button
:on-press #(do (swap! context assoc :clear true)
(clear-input chat-id refs)
(re-frame/dispatch [:chat.ui/send-current-message]))}
:main-icons2/arrow-up]]]
;black background
[reanimated/view {:style (reanimated/apply-animations-to-style
{:opacity bg-opacity}
(styles/new-bottom-sheet-background window-height))}]]))])))])

View File

@ -1,7 +1,9 @@
(ns status-im.ui.screens.chat.components.style
(:require [quo.platform :as platform]
[quo.design-system.colors :as colors]
[quo.design-system.typography :as typography]))
[quo.design-system.typography :as typography]
[quo2.foundations.colors :as quo2.colors]
[quo2.foundations.typography :as quo2.typography]))
(defn toolbar []
{:min-height 52
@ -35,7 +37,7 @@
(when platform/ios?
{:padding-top 2})))
(defn text-input [contact-request]
(defn text-input-old [contact-request]
(merge typography/font-regular
typography/base
{:flex 1
@ -50,13 +52,27 @@
{:padding-top (if contact-request 10 2)
:padding-bottom (if contact-request 5 6)})))
(defn text-input []
(merge quo2.typography/font-regular
quo2.typography/paragraph-1
{:flex 1
:min-height 34
:margin 0
:flex-shrink 1
:color (:text-01 @colors/theme)
:margin-horizontal 20}
(if platform/android?
{:padding-vertical 8
:text-align-vertical :top}
{:margin-top 8
:margin-bottom 8})))
(defn actions-wrapper [show-send]
(merge
(when show-send
{:width 0 :left -88})
{:flex-direction :row
:padding-left 4
:min-height 34}))
(merge (when show-send
{:width 0 :left -88})
{:flex-direction :row
:padding-left 4
:min-height 34}))
(defn touchable-icon []
{:padding-horizontal 10
@ -85,7 +101,7 @@
:background-color (:ui-03 @colors/theme)})
(defn reply-container []
{:flex-direction :row})
{:flex-direction :row})
(defn reply-content-old []
{:padding-vertical 6
@ -98,9 +114,9 @@
:flex-direction :row})
(defn contact-request-content []
{:flex 1
:flex-direction :row
:justify-content :space-between})
{:flex 1
:flex-direction :row
:justify-content :space-between})
(defn close-button []
{:margin-top 3})
@ -128,3 +144,52 @@
:background-color (colors/get-color :ui-background)
:border-top-width 1
:border-top-color (colors/get-color :ui-01)})
(defn new-input-bottom-sheet [window-height]
(merge {:border-top-left-radius 20
:border-top-right-radius 20
:position :absolute
:left 0
:right 0
:bottom (- window-height)
:height window-height
:flex 1
:background-color (quo2.colors/theme-colors quo2.colors/white quo2.colors/neutral-90)
:z-index 1000}
(if platform/ios?
{:shadow-radius 16
:shadow-opacity 1
:shadow-color "rgba(9, 16, 28, 0.04)"
:shadow-offset {:width 0 :height -2}}
{:elevation 2})))
(defn new-bottom-sheet-handle []
{:width 32
:height 4
:background-color (quo2.colors/theme-colors quo2.colors/black quo2.colors/white)
:opacity 0.05
:border-radius 100
:align-self :center
:margin-top 8})
(defn new-bottom-sheet-controls [insets]
{:flex-direction :row
:padding-horizontal 20
:elevation 2
:z-index 2000
:position :absolute
:background-color (quo2.colors/theme-colors quo2.colors/white quo2.colors/neutral-90)
;these 3 props play together, we need this magic to hide message text in the safe area
:padding-top 10
:padding-bottom (+ 12 (:bottom insets))
:bottom (- 2 (:bottom insets))})
(defn new-bottom-sheet-background [window-height]
{:pointerEvents :none
:position :absolute
:left 0
:right 0
:bottom 0
:height window-height
:background-color quo2.colors/neutral-95-opa-70
:z-index 500})

View File

@ -85,23 +85,23 @@
(let [contact-request @(re-frame/subscribe [:chats/sending-contact-request])]
[react/view {:style style/contact-request}
[react/image {:source (resources/get-image :hand-wave)
:style {:width 112
:height 96.71
:style {:width 112
:height 96.71
:margin-top 17}}]
[quo/text {:style {:margin-top 14}
[quo/text {:style {:margin-top 14}
:weight :bold
:size :large}
(i18n/label :t/say-hi)]
[quo/text {:style {:margin-top 2
[quo/text {:style {:margin-top 2
:margin-bottom 14}}
(i18n/label :t/send-contact-request-message)]
(when-not contact-request
[react/view {:style {:padding-horizontal 16
:padding-bottom 8}}
:padding-bottom 8}}
[quo/button
{:style {:width "100%"}
{:style {:width "100%"}
:accessibility-label :contact-request--button
:on-press #(re-frame/dispatch [:chat.ui/send-contact-request])}
:on-press #(re-frame/dispatch [:chat.ui/send-contact-request])}
(i18n/label :t/contact-request)]])]))
(defn chat-intro [{:keys [chat-id
@ -116,7 +116,7 @@
no-messages?
contact-request-state
emoji]}]
[react/view {:style (style/intro-header-container loading-messages? no-messages?)
[react/view {:style (style/intro-header-container loading-messages? no-messages?)
:accessibility-label :history-chat}
;; Icon section
[react/view {:style {:margin-top 52
@ -133,34 +133,25 @@
(if group-chat chat-name contact-name)]
;; Description section
(if group-chat
[chat.group/group-chat-description-container {:chat-id chat-id
:invitation-admin invitation-admin
[chat.group/group-chat-description-container {:chat-id chat-id
:invitation-admin invitation-admin
:loading-messages? loading-messages?
:chat-name chat-name
:chat-type chat-type
:no-messages? no-messages?}]
[react/text {:style (assoc style/intro-header-description
:margin-bottom 32)}
(str
(i18n/label :t/empty-chat-description-one-to-one)
contact-name)])
(when
(and
mutual-contact-requests-enabled?
(= chat-type constants/one-to-one-chat-type)
(or
(= contact-request-state constants/contact-request-state-none)
(= contact-request-state constants/contact-request-state-received)
(= contact-request-state constants/contact-request-state-dismissed)))
:chat-name chat-name
:chat-type chat-type
:no-messages? no-messages?}]
[react/text {:style (assoc style/intro-header-description :margin-bottom 32)}
(str (i18n/label :t/empty-chat-description-one-to-one) contact-name)])
(when (and mutual-contact-requests-enabled?
(= chat-type constants/one-to-one-chat-type)
(or (= contact-request-state constants/contact-request-state-none)
(= contact-request-state constants/contact-request-state-received)
(= contact-request-state constants/contact-request-state-dismissed)))
[contact-request])])
(defn chat-intro-one-to-one [{:keys [chat-id] :as opts}]
(let [contact @(re-frame/subscribe
[:contacts/contact-by-identity chat-id])
(let [contact @(re-frame/subscribe [:contacts/contact-by-identity chat-id])
mutual-contact-requests-enabled? @(re-frame/subscribe [:mutual-contact-requests/enabled?])
contact-names @(re-frame/subscribe
[:contacts/contact-two-names-by-identity chat-id])]
contact-names @(re-frame/subscribe [:contacts/contact-two-names-by-identity chat-id])]
[chat-intro (assoc opts
:mutual-contact-requests-enabled? mutual-contact-requests-enabled?
:contact-name (first contact-names)
@ -175,21 +166,21 @@
public? emoji]}
no-messages]
[react/touchable-without-feedback
{:style {:flex 1
:align-items :flex-start}
{:style {:flex 1
:align-items :flex-start}
:on-press (fn [_]
(react/dismiss-keyboard!))}
(let [opts
{:chat-id chat-id
:group-chat group-chat
:invitation-admin invitation-admin
:chat-type chat-type
:chat-name chat-name
:public? public?
:color color
{:chat-id chat-id
:group-chat group-chat
:invitation-admin invitation-admin
:chat-type chat-type
:chat-name chat-name
:public? public?
:color color
:loading-messages? (not (pos? synced-to))
:no-messages? no-messages
:emoji emoji}]
:no-messages? no-messages
:emoji emoji}]
(if group-chat
[chat-intro opts]
[chat-intro-one-to-one opts]))])
@ -241,15 +232,15 @@
[toolbar/toolbar {:show-border? true
:right
[quo/button
{:type :secondary
{:type :secondary
:accessibility-label :retry-button
:on-press #(re-frame/dispatch [:group-chats.ui/membership-retry])}
:on-press #(re-frame/dispatch [:group-chats.ui/membership-retry])}
(i18n/label :t/mailserver-retry)]
:left
[quo/button
{:type :secondary
{:type :secondary
:accessibility-label :remove-group-button
:on-press #(re-frame/dispatch [:group-chats.ui/remove-chat-confirmed chat-id])}
:on-press #(re-frame/dispatch [:group-chats.ui/remove-chat-confirmed chat-id])}
(i18n/label :t/remove-group)]}]
:else
[toolbar/toolbar {:show-border? true
@ -442,7 +433,7 @@
(let [{:keys [group-chat chat-type chat-id public? community-id admins]} chat
messages @(re-frame/subscribe [:chats/raw-chat-messages-stream chat-id])
one-to-one? (= chat-type constants/one-to-one-chat-type)
one-to-one? (= chat-type constants/one-to-one-chat-type)
contact-added? (when one-to-one? @(re-frame/subscribe [:contacts/contact-added? chat-id]))
should-send-contact-request?
(and
@ -473,9 +464,9 @@
:on-viewable-items-changed on-viewable-items-changed
:on-end-reached list-on-end-reached
:on-scroll-to-index-failed identity ;;don't remove this
:content-container-style {:padding-top (+ bottom-space 16)
:content-container-style {:padding-top (+ bottom-space 16)
:padding-bottom 16}
:scroll-indicator-insets {:top bottom-space} ;;ios only
:scroll-indicator-insets {:top bottom-space} ;;ios only
:keyboard-dismiss-mode :interactive
:keyboard-should-persist-taps :handled
:onMomentumScrollBegin state/start-scrolling
@ -525,10 +516,10 @@
(defn topbar-old []
;;we don't use topbar component, because we want chat view as simple (fast) as possible
[react/view {:height 56}
[react/touchable-highlight {:on-press-in navigate-back-handler
[react/touchable-highlight {:on-press-in navigate-back-handler
:accessibility-label :back-button
:style {:height 56 :width 40 :align-items :center :justify-content :center
:padding-left 16}}
:style {:height 56 :width 40 :align-items :center :justify-content :center
:padding-left 16}}
[icons/icon :main-icons/arrow-left {:color colors/black}]]
[react/view {:flex 1 :left 52 :right 52 :top 0 :bottom 0 :position :absolute}
[topbar-content-old]]
@ -537,9 +528,9 @@
[sheets/current-chat-actions])
:height 256}])
:accessibility-label :chat-menu-button
:style {:right 0 :top 0 :bottom 0 :position :absolute
:height 56 :width 40 :align-items :center :justify-content :center
:padding-right 16}}
:style {:right 0 :top 0 :bottom 0 :position :absolute
:height 56 :width 40 :align-items :center :justify-content :center
:padding-right 16}}
[icons/icon :main-icons/more {:color colors/black}]]])
(defn chat-render-old []
@ -592,7 +583,7 @@
;; We set the key so we can force a re-render as
;; it does not rely on ratom but just atoms
^{:key (str @components/chat-input-key "chat-input")}
[components/chat-toolbar
[components/chat-toolbar-old
{:chat-id chat-id
:active-panel @active-panel
:set-active-panel set-active-panel
@ -601,70 +592,33 @@
[bottom-sheet @active-panel]])]))))
(defn chat-render []
(let [bottom-space (reagent/atom 0)
panel-space (reagent/atom 52)
active-panel (reagent/atom nil)
position-y (animated/value 0)
pan-state (animated/value 0)
text-input-ref (quo.react/create-ref)
on-update #(when-not (zero? %) (reset! panel-space %))
pan-responder (accessory/create-pan-responder position-y pan-state)
space-keeper (get-space-keeper-ios bottom-space panel-space active-panel text-input-ref)
set-active-panel (get-set-active-panel active-panel)
on-close #(set-active-panel nil)]
(fn []
(let [{:keys [chat-id show-input? group-chat admins invitation-admin] :as chat}
;;we want to react only on these fields, do not use full chat map here
@(re-frame/subscribe [:chats/current-chat-chat-view])
mutual-contact-requests-enabled? @(re-frame/subscribe [:mutual-contact-requests/enabled?])
max-bottom-space (max @bottom-space @panel-space)]
[:<>
;; It is better to not use topbar component because of performance
[topbar/topbar {:navigation :none
:left-component [react/view {:flex-direction :row :margin-left 16}
[back-button]]
:title-component [topbar-content]
:right-component [react/view {:flex-direction :row :margin-right 16}
[search-button]]
:border-bottom false
:new-ui? true}]
[connectivity/loading-indicator]
(when chat-id
(if group-chat
[invitation-requests chat-id admins]
(when-not mutual-contact-requests-enabled? [add-contact-bar chat-id])))
;;MESSAGES LIST
[messages-view {:chat chat
:bottom-space max-bottom-space
:pan-responder pan-responder
:mutual-contact-requests-enabled? mutual-contact-requests-enabled?
:space-keeper space-keeper
:show-input? show-input?}]
(when (and group-chat invitation-admin)
[accessory/view {:y position-y
:on-update-inset on-update}
[invitation-bar chat-id]])
[components/autocomplete-mentions text-input-ref max-bottom-space]
(when show-input?
;; NOTE: this only accepts two children
[accessory/view {:y position-y
:pan-state pan-state
:has-panel (boolean @active-panel)
:on-close on-close
:on-update-inset on-update}
[react/view
[edit/edit-message-auto-focus-wrapper text-input-ref]
[reply/reply-message-auto-focus-wrapper text-input-ref]
;; We set the key so we can force a re-render as
;; it does not rely on ratom but just atoms
^{:key (str @components/chat-input-key "chat-input")}
[components/chat-toolbar
{:chat-id chat-id
:active-panel @active-panel
:set-active-panel set-active-panel
:text-input-ref text-input-ref}]
[contact-request/contact-request-message-auto-focus-wrapper text-input-ref]]
[bottom-sheet @active-panel]])]))))
(let [{:keys [chat-id show-input? group-chat admins] :as chat}
;;we want to react only on these fields, do not use full chat map here
@(re-frame/subscribe [:chats/current-chat-chat-view])
mutual-contact-requests-enabled? @(re-frame/subscribe [:mutual-contact-requests/enabled?])]
[react/keyboard-avoiding-view-new {:style {:flex 1}
:ignore-offset false}
;; It is better to not use topbar component because of performance
[topbar/topbar {:navigation :none
:left-component [react/view {:flex-direction :row :margin-left 16}
[back-button]]
:title-component [topbar-content]
:right-component [react/view {:flex-direction :row :margin-right 16}
[search-button]]
:border-bottom false
:new-ui? true}]
[connectivity/loading-indicator]
(when chat-id
(if group-chat
[invitation-requests chat-id admins]
(when-not mutual-contact-requests-enabled? [add-contact-bar chat-id])))
;;MESSAGES LIST
[messages-view {:chat chat
:mutual-contact-requests-enabled? mutual-contact-requests-enabled?
:show-input? show-input?}]
;;INPUT COMPONENT
(when show-input?
[components/chat-input-bottom-sheet chat-id])]))
(defn chat-old []
(reagent/create-class
@ -676,8 +630,8 @@
(defn chat []
(reagent/create-class
{:component-did-mount (fn []
(react/hw-back-remove-listener navigate-back-handler)
(react/hw-back-add-listener navigate-back-handler))
{:component-did-mount (fn []
(react/hw-back-remove-listener navigate-back-handler)
(react/hw-back-add-listener navigate-back-handler))
:component-will-unmount (fn [] (react/hw-back-remove-listener navigate-back-handler))
:reagent-render chat-render}))
:reagent-render chat-render}))

View File

@ -6133,7 +6133,7 @@ lodash.union@^4.6.0:
resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88"
integrity sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=
lodash@4.17.x:
lodash@4.17.x, lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@ -8026,14 +8026,15 @@ react-native-fs@^2.14.1:
base-64 "^0.1.0"
utf8 "^3.0.0"
react-native-gesture-handler@^1.8.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/react-native-gesture-handler/-/react-native-gesture-handler-1.8.0.tgz#18f61f51da50320f938957b0ee79bc58f47449dc"
integrity sha512-E2FZa0qZ5Bi0Z8Jg4n9DaFomHvedSjwbO2DPmUUHYRy1lH2yxXUpSrqJd6yymu+Efzmjg2+JZzsjFYA2Iq8VEQ==
react-native-gesture-handler@^2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/react-native-gesture-handler/-/react-native-gesture-handler-2.5.0.tgz#61385583570ed0a45a9ed142425e35f8fe8274fb"
integrity sha512-djZdcprFf08PZC332D+AeG5wcGeAPhzfCJtB3otUgOgTlvjVXmg/SLFdPJSpzLBqkRAmrC77tM79QgKbuLxkfw==
dependencies:
"@egjs/hammerjs" "^2.0.17"
hoist-non-react-statics "^3.3.0"
invariant "^2.2.4"
lodash "^4.17.21"
prop-types "^15.7.2"
react-native-haptic-feedback@^1.9.0: