refactor and move composer to status-im2 (#14758)

refactor and move composer to status-im2
This commit is contained in:
flexsurfer 2023-01-16 17:20:10 +01:00 committed by GitHub
parent d6c899be3d
commit 685c95591c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 583 additions and 616 deletions

View File

@ -134,7 +134,7 @@
:style {:width outer-dimensions :style {:width outer-dimensions
:height outer-dimensions :height outer-dimensions
:border-radius outer-dimensions}} :border-radius outer-dimensions}}
(when (and ring? identicon?) (when (and false (and ring? identicon?)) ;;TODO not implemented yet
[icons/icon :i/identicon-ring [icons/icon :i/identicon-ring
{:size outer-dimensions {:size outer-dimensions
:no-color true}]) :no-color true}])

View File

@ -4,7 +4,6 @@
(def container (def container
{:height 40 {:height 40
:flex 1
:background-color colors/primary-50-opa-20 :background-color colors/primary-50-opa-20
:flex-direction :row :flex-direction :row
:align-items :center :align-items :center

View File

@ -0,0 +1,5 @@
(ns react-native.blur
(:require ["@react-native-community/blur" :as blur]
[reagent.core :as reagent]))
(def view (reagent/adapt-react-class (.-BlurView blur)))

View File

@ -1,6 +1,5 @@
(ns react-native.core (ns react-native.core
(:require ["@react-native-community/blur" :as blur] (:require ["react" :as react]
["react" :as react]
["react-native" :as react-native] ["react-native" :as react-native]
[cljs-bean.core :as bean] [cljs-bean.core :as bean]
[oops.core :as oops] [oops.core :as oops]
@ -10,7 +9,6 @@
[reagent.core :as reagent])) [reagent.core :as reagent]))
(def app-state ^js (.-AppState ^js react-native)) (def app-state ^js (.-AppState ^js react-native))
(def blur-view (reagent/adapt-react-class (.-BlurView blur)))
(def view (reagent/adapt-react-class (.-View ^js react-native))) (def view (reagent/adapt-react-class (.-View ^js react-native)))
(def scroll-view (reagent/adapt-react-class (.-ScrollView ^js react-native))) (def scroll-view (reagent/adapt-react-class (.-ScrollView ^js react-native)))
@ -90,13 +88,22 @@
props)] props)]
children)) children))
(defn use-effect (def create-ref react/createRef)
([effect] (use-effect effect []))
([effect deps]
(react/useEffect effect (bean/->js deps))))
(def use-ref react/useRef) (def use-ref react/useRef)
(defn use-effect-once [effect] (use-effect effect))
(defn use-effect
([effect-fn]
(react/useEffect
#(let [ret (effect-fn)]
(if (fn? ret) ret js/undefined))))
([effect-fn deps]
(react/useEffect effect-fn (bean/->js deps))))
(defn use-effect-once
[effect-fn]
(use-effect effect-fn))
(defn use-unmount (defn use-unmount
[f] [f]
(let [fn-ref (use-ref f)] (let [fn-ref (use-ref f)]

View File

@ -5,12 +5,11 @@
(def ^:private consumer-raw (reagent/adapt-react-class (.-Consumer ^js SafeAreaInsetsContext))) (def ^:private consumer-raw (reagent/adapt-react-class (.-Consumer ^js SafeAreaInsetsContext)))
(def provider (reagent/adapt-react-class SafeAreaProvider))
(defn consumer (defn consumer
[component] [component]
[consumer-raw [consumer-raw
(fn [^js insets] (fn [^js insets]
(reagent/as-element (reagent/as-element
[component (js->clj insets :keywordize-keys true)]))]) [component (js->clj insets :keywordize-keys true)]))])
(def safe-area-provider (reagent/adapt-react-class SafeAreaProvider))
(def safe-area-consumer consumer-raw)

View File

@ -13,7 +13,8 @@
[i18n.i18n :as i18n] [i18n.i18n :as i18n]
[utils.datetime :as datetime] [utils.datetime :as datetime]
[status-im.utils.utils :as utils] [status-im.utils.utils :as utils]
[taoensso.timbre :as log])) [taoensso.timbre :as log]
[status-im.ui.screens.chat.components.input :as input]))
(defn text->emoji (defn text->emoji
"Replaces emojis in a specified `text`" "Replaces emojis in a specified `text`"
@ -117,37 +118,36 @@
(rf/defn reply-to-message (rf/defn reply-to-message
"Sets reference to previous chat message and focuses on input" "Sets reference to previous chat message and focuses on input"
{:events [:chat.ui/reply-to-message]} {:events [:chat.ui/reply-to-message]}
[{:keys [db] :as cofx} message] [{:keys [db]} message]
(let [current-chat-id (:current-chat-id db)] (let [current-chat-id (:current-chat-id db)]
(rf/merge cofx {:db (-> db
{:db (-> db (assoc-in [:chat/inputs current-chat-id :metadata :responding-to-message]
(assoc-in [:chat/inputs current-chat-id :metadata :responding-to-message] message)
message) (assoc-in [:chat/inputs current-chat-id :metadata :editing-message] nil)
(assoc-in [:chat/inputs current-chat-id :metadata :editing-message] nil) (update-in [:chat/inputs current-chat-id :metadata]
(update-in [:chat/inputs current-chat-id :metadata] dissoc
dissoc :sending-image))}))
:sending-image))})))
(rf/defn edit-message (rf/defn edit-message
"Sets reference to previous chat message and focuses on input" "Sets reference to previous chat message and focuses on input"
{:events [:chat.ui/edit-message]} {:events [:chat.ui/edit-message]}
[{:keys [db] :as cofx} message] [{:keys [db] :as cofx} message]
(let [current-chat-id (:current-chat-id db) (let [current-chat-id (:current-chat-id db)
text (get-in message [:content :text])] text (get-in message [:content :text])]
{:dispatch [:chat.ui.input/set-chat-input-text text current-chat-id] (rf/merge cofx
:db (-> db {:db (-> db
(assoc-in [:chat/inputs current-chat-id :metadata :editing-message] (assoc-in [:chat/inputs current-chat-id :metadata :editing-message]
message) message)
(assoc-in [:chat/inputs current-chat-id :metadata :responding-to-message] nil) (assoc-in [:chat/inputs current-chat-id :metadata :responding-to-message] nil)
(update-in [:chat/inputs current-chat-id :metadata] (update-in [:chat/inputs current-chat-id :metadata]
dissoc dissoc
:sending-image))})) :sending-image))}
(input/set-input-text text current-chat-id))))
(rf/defn show-contact-request-input (rf/defn show-contact-request-input
"Sets reference to previous chat message and focuses on input" "Sets reference to previous chat message and focuses on input"
{:events [:chat.ui/send-contact-request]} {:events [:chat.ui/send-contact-request]}
[{:keys [db] :as cofx}] [{:keys [db]}]
(let [current-chat-id (:current-chat-id db)] (let [current-chat-id (:current-chat-id db)]
{:db (-> db {:db (-> db
(assoc-in [:chat/inputs current-chat-id :metadata :sending-contact-request] (assoc-in [:chat/inputs current-chat-id :metadata :sending-contact-request]

View File

@ -53,7 +53,7 @@
(defn contact-request-message (defn contact-request-message
[their-public-key] [their-public-key]
(let [{:keys [input-text]} @(re-frame/subscribe [:chats/current-chat-inputs])] (let [{:keys [input-text]} @(re-frame/subscribe [:chats/current-chat-input])]
[rn/view {:style {:flex-direction :row}} [rn/view {:style {:flex-direction :row}}
[rn/view {:style (styles/contact-request-content)} [rn/view {:style (styles/contact-request-content)}
[quo/button [quo/button

View File

@ -212,7 +212,7 @@
"Set input text for current-chat. Takes db and input text and cofx "Set input text for current-chat. Takes db and input text and cofx
as arguments and returns new fx. Always clear all validation messages." as arguments and returns new fx. Always clear all validation messages."
{:events [:chat.ui.input/set-chat-input-text]} {:events [:chat.ui.input/set-chat-input-text]}
[{:keys [db] :as cofx} text chat-id] [{:keys [db]} text chat-id]
(let [text-with-mentions (mentions/->input-field text) (let [text-with-mentions (mentions/->input-field text)
all-contacts (:contacts/contacts db) all-contacts (:contacts/contacts db)
chat (get-in db [:chats chat-id]) chat (get-in db [:chats chat-id])

View File

@ -8,7 +8,7 @@
[status-im.ui2.screens.chat.components.edit.style :as style])) [status-im.ui2.screens.chat.components.edit.style :as style]))
(defn edit-message (defn edit-message
[reset-composer-callback] [on-cancel]
[rn/view [rn/view
{:style style/container {:style style/container
:accessibility-label :edit-message} :accessibility-label :edit-message}
@ -24,7 +24,7 @@
(i18n/label :t/editing-message)]]] (i18n/label :t/editing-message)]]]
[gesture-handler/touchable-without-feedback [gesture-handler/touchable-without-feedback
{:accessibility-label :reply-cancel-button {:accessibility-label :reply-cancel-button
:on-press #(do (reset-composer-callback true) :on-press #(do (on-cancel)
(rf/dispatch [:chat.ui/cancel-message-edit]))} (rf/dispatch [:chat.ui/cancel-message-edit]))}
[quo/button [quo/button
{:width 24 {:width 24

View File

@ -10,7 +10,7 @@
[utils.re-frame :as rf] [utils.re-frame :as rf]
[status-im.ui.screens.chat.sheets :refer [hide-sheet-and-dispatch]] [status-im.ui.screens.chat.sheets :refer [hide-sheet-and-dispatch]]
[status-im.ui.components.toolbar :as toolbar] [status-im.ui.components.toolbar :as toolbar]
[status-im.ui2.screens.common.contact-list.view :as contact-list] [status-im2.common.contact-list.view :as contact-list]
[quo2.components.markdown.text :as text] [quo2.components.markdown.text :as text]
[status-im.ui.components.invite.events :as invite.events] [status-im.ui.components.invite.events :as invite.events]
[status-im.ui2.screens.chat.components.new-chat.styles :as style])) [status-im.ui2.screens.chat.components.new-chat.styles :as style]))

View File

@ -0,0 +1,17 @@
(ns status-im.ui2.screens.chat.components.reply.style)
(defn reply-content
[pin?]
{:padding-horizontal (when-not pin? 10)
:flex 1
:flex-direction :row})
(defn quoted-message
[pin?]
(merge {:flex-direction :row
:align-items :center
:width "45%"}
(when-not pin?
{:position :absolute
:left 34
:top 3})))

View File

@ -1,4 +1,4 @@
(ns status-im.ui2.screens.chat.components.reply (ns status-im.ui2.screens.chat.components.reply.view
(:require [clojure.string :as string] (:require [clojure.string :as string]
[i18n.i18n :as i18n] [i18n.i18n :as i18n]
[quo.react-native :as rn] [quo.react-native :as rn]
@ -10,8 +10,8 @@
[status-im.ethereum.stateofus :as stateofus] [status-im.ethereum.stateofus :as stateofus]
[status-im.ui.components.icons.icons :as icons] [status-im.ui.components.icons.icons :as icons]
[status-im.ui.screens.chat.photos :as photos] [status-im.ui.screens.chat.photos :as photos]
[status-im.ui2.screens.chat.composer.style :as styles] [utils.re-frame :as rf]
[utils.re-frame :as rf])) [status-im.ui2.screens.chat.components.reply.style :as style]))
(defn get-quoted-text-with-mentions (defn get-quoted-text-with-mentions
[parsed-text] [parsed-text]
@ -73,16 +73,16 @@
{:style {:flex-direction :row {:style {:flex-direction :row
:height (when-not pin? 24) :height (when-not pin? 24)
:accessibility-label :reply-message}} :accessibility-label :reply-message}}
[rn/view {:style (styles/reply-content pin?)} [rn/view {:style (style/reply-content pin?)}
(when-not pin? (when-not pin?
;;TODO quo2 icon should be used ;;TODO quo2 icon should be used
[icons/icon :main-icons/connector [icons/icon :main-icons/connector
{:color (colors/theme-colors colors/neutral-40 colors/neutral-60) {:color (colors/theme-colors colors/neutral-40 colors/neutral-60)
:container-style {:position :absolute :left 10 :bottom -4 :width 16 :height 16}}]) :container-style {:position :absolute :left 10 :bottom -4 :width 16 :height 16}}])
(if (or deleted? deleted-for-me?) (if (or deleted? deleted-for-me?)
[rn/view {:style (styles/quoted-message pin?)} [rn/view {:style (style/quoted-message pin?)}
[reply-deleted-message]] [reply-deleted-message]]
[rn/view {:style (styles/quoted-message pin?)} [rn/view {:style (style/quoted-message pin?)}
[photos/member-photo from identicon 16] [photos/member-photo from identicon 16]
[quo2.text/text [quo2.text/text
{:weight :semi-bold {:weight :semi-bold

View File

@ -16,8 +16,8 @@
(defn edit-message-auto-focus-wrapper (defn edit-message-auto-focus-wrapper
[text-input-ref _] [text-input-ref _]
(let [had-edit (atom nil)] (let [had-edit (atom nil)]
(fn [_ edit cleanup-composer-callback] (fn [_ edit on-cancel]
(focus-input-on-edit edit had-edit text-input-ref) (focus-input-on-edit edit had-edit text-input-ref)
(when edit (when edit
[rn/view {:style style/container} [rn/view {:style style/container}
[edit/edit-message cleanup-composer-callback]])))) [edit/edit-message on-cancel]]))))

View File

@ -0,0 +1,18 @@
(ns status-im.ui2.screens.chat.composer.images.style
(:require [quo2.foundations.colors :as colors]))
(def remove-photo-container
{:width 14
:height 14
:border-radius 7
:background-color colors/neutral-50
:position :absolute
:top 5
:right 5
:justify-content :center
:align-items :center})
(def small-image
{:width 56
:height 56
:border-radius 8})

View File

@ -1,32 +1,33 @@
(ns status-im.ui2.screens.chat.composer.images.view (ns status-im.ui2.screens.chat.composer.images.view
(:require [quo2.core :as quo2] (:require [quo2.core :as quo]
[quo2.foundations.colors :as colors] [quo2.foundations.colors :as colors]
[re-frame.core :as rf] [re-frame.core :as rf]
[react-native.core :as rn] [react-native.core :as rn]
[status-im.ui2.screens.chat.composer.style :as style])) [status-im.ui2.screens.chat.composer.images.style :as style]))
(defn image (defn image
[item] [item]
[rn/view ;; there is a bug on Android https://github.com/facebook/react-native/issues/12534
;; so we need some magic here with paddings so close button isn't cut
[rn/view {:padding-top 12 :padding-bottom 8 :padding-right 12}
[rn/image [rn/image
{:source {:uri (first item)} {:source {:uri (first item)}
:style style/small-image}] :style style/small-image}]
[rn/touchable-opacity [rn/touchable-opacity
{:on-press (fn [] (rf/dispatch [:chat.ui/image-unselected (first item)])) {:on-press #(rf/dispatch [:chat.ui/image-unselected (first item)])
:style style/remove-photo-container :style style/remove-photo-container
:hit-slop {:right 5 :hit-slop {:right 5
:left 5 :left 5
:top 10 :top 10
:bottom 10}} :bottom 10}}
[quo2/icon :i/close {:color colors/white :size 12}]]]) [quo/icon :i/close {:color colors/white :size 12}]]])
(defn images-list (defn images-list
[images] [images]
[rn/flat-list (when (seq images)
{:key-fn first [rn/flat-list
:render-fn image {:key-fn first
:data images :render-fn image
:horizontal true :data images
:style {:bottom 50 :position :absolute :z-index 5 :elevation 5} :horizontal true
:content-container-style {:padding-horizontal 20 :margin-top 12 :padding-top 8} :keyboard-should-persist-taps :handled}]))
:separator [rn/view {:style {:width 12}}]}])

View File

@ -12,13 +12,14 @@
[status-im.chat.models.mentions :as mentions] [status-im.chat.models.mentions :as mentions]
[i18n.i18n :as i18n] [i18n.i18n :as i18n]
[status-im.ui.components.react :as react] [status-im.ui.components.react :as react]
[status-im.ui2.screens.chat.composer.style :as style]
[utils.re-frame :as rf] [utils.re-frame :as rf]
[status-im.utils.platform :as platform] [status-im.utils.platform :as platform]
[status-im.utils.utils :as utils.utils] [status-im.utils.utils :as utils.utils]
[utils.transforms :as transforms])) [utils.transforms :as transforms]
[quo2.foundations.typography :as typography]))
(defonce input-texts (atom {})) (defonce input-texts (atom {}))
(defonce input-text-content-heights (atom {}))
(defonce mentions-enabled? (reagent/atom {})) (defonce mentions-enabled? (reagent/atom {}))
(defonce chat-input-key (reagent/atom 1)) (defonce chat-input-key (reagent/atom 1))
(defonce text-input-ref (reagent/atom nil)) (defonce text-input-ref (reagent/atom nil))
@ -29,6 +30,7 @@
:chat.ui/clear-inputs :chat.ui/clear-inputs
(fn [] (fn []
(reset! input-texts {}) (reset! input-texts {})
(reset! input-text-content-heights {})
(reset! mentions-enabled? {}) (reset! mentions-enabled? {})
(reset! chat-input-key 1))) (reset! chat-input-key 1)))
@ -58,7 +60,8 @@
(some-> ^js (quo.react/current-ref (:text-input-ref refs)) (some-> ^js (quo.react/current-ref (:text-input-ref refs))
.clear) .clear)
(swap! mentions-enabled? update :render not) (swap! mentions-enabled? update :render not)
(swap! input-texts dissoc chat-id)) (swap! input-texts dissoc chat-id)
(swap! input-text-content-heights dissoc chat-id))
(defn clear-input (defn clear-input
[chat-id refs] [chat-id refs]
@ -75,6 +78,7 @@
(defn on-text-change (defn on-text-change
[val chat-id] [val chat-id]
(println "on=text-change" val)
(swap! input-texts assoc chat-id val) (swap! input-texts assoc chat-id val)
;;we still store it in app-db for mentions, we don't have reactions in views ;;we still store it in app-db for mentions, we don't have reactions in views
(rf/dispatch [:chat.ui/set-chat-input-text val])) (rf/dispatch [:chat.ui/set-chat-input-text val]))
@ -157,16 +161,31 @@
(when platform/android? (when platform/android?
(rf/dispatch [::mentions/calculate-suggestions mentionable-users])))) (rf/dispatch [::mentions/calculate-suggestions mentionable-users]))))
(defn text-input-style
[]
(merge typography/font-regular
typography/paragraph-1
{:flex 1
:min-height 34
:margin 0
:flex-shrink 1
:color (:text-01 @quo.colors/theme)
:margin-horizontal 20}
(if platform/android?
{:padding-vertical 8
:text-align-vertical :top}
{:margin-top 8
:margin-bottom 8})))
(defn text-input (defn text-input
[{:keys [set-active-panel refs chat-id sending-image on-content-size-change initial-value]}] [{:keys [refs chat-id sending-image on-content-size-change]}]
(let [_ (reset! text-input-ref (:text-input-ref refs)) (let [cooldown-enabled? (rf/sub [:chats/current-chat-cooldown-enabled?])
cooldown-enabled? (rf/sub [:chats/current-chat-cooldown-enabled?])
mentionable-users (rf/sub [:chats/mentionable-users]) mentionable-users (rf/sub [:chats/mentionable-users])
timeout-id (reagent/atom nil) timeout-id (reagent/atom nil)
last-text-change (reagent/atom nil) last-text-change (reagent/atom nil)
mentions-enabled? (get @mentions-enabled? chat-id) mentions-enabled? (get @mentions-enabled? chat-id)
props props
{:style (style/text-input) {:style (text-input-style)
:ref (:text-input-ref refs) :ref (:text-input-ref refs)
:max-font-size-multiplier 1 :max-font-size-multiplier 1
:accessibility-label :chat-message-input :accessibility-label :chat-message-input
@ -175,13 +194,12 @@
:editable (not cooldown-enabled?) :editable (not cooldown-enabled?)
:blur-on-submit false :blur-on-submit false
:auto-focus false :auto-focus false
:default-value initial-value
:on-focus #(set-active-panel nil)
:max-length chat.constants/max-text-size :max-length chat.constants/max-text-size
:placeholder-text-color (:text-02 @quo.colors/theme) :placeholder-text-color (:text-02 @quo.colors/theme)
:placeholder (if cooldown-enabled? :placeholder (if cooldown-enabled?
(i18n/label :cooldown/text-input-disabled) (i18n/label :cooldown/text-input-disabled)
(i18n/label :t/type-a-message)) (i18n/label :t/type-a-message))
:default-value (get @input-texts chat-id)
:underline-color-android :transparent :underline-color-android :transparent
:auto-capitalize :sentences :auto-capitalize :sentences
:auto-correct false :auto-correct false
@ -232,13 +250,12 @@
(fn [[chat-id text local-text-input-ref]] (fn [[chat-id text local-text-input-ref]]
(when local-text-input-ref (when local-text-input-ref
(reset! text-input-ref local-text-input-ref)) (reset! text-input-ref local-text-input-ref))
(on-text-change text chat-id)
(if platform/ios? (if platform/ios?
(.setNativeProps ^js (quo.react/current-ref @text-input-ref) (clj->js {:text text})) (.setNativeProps ^js (quo.react/current-ref @text-input-ref) (clj->js {:text text}))
(do (if (string/blank? text)
(on-text-change text chat-id) (.clear ^js (quo.react/current-ref @text-input-ref))
(if (string/blank? text) (.setNativeProps ^js (quo.react/current-ref @text-input-ref) (clj->js {:text text}))))))
(.clear ^js (quo.react/current-ref @text-input-ref))
(.setNativeProps ^js (quo.react/current-ref @text-input-ref) (clj->js {:text text})))))))
(defn calculate-input-text (defn calculate-input-text
[{:keys [full-text selection-start selection-end]} content] [{:keys [full-text selection-start selection-end]} content]
@ -318,7 +335,7 @@
(action params))) (action params)))
(defn selectable-text-input (defn selectable-text-input
[chat-id {:keys [style ref on-selection-change] :as props} children] [_ _ _]
(let [text-input-ref (reagent/atom nil) (let [text-input-ref (reagent/atom nil)
menu-items (reagent/atom first-level-menu-items) menu-items (reagent/atom first-level-menu-items)
first-level (reagent/atom true) first-level (reagent/atom true)
@ -338,7 +355,7 @@
(oops/ocall manager :startActionMode text-input-handle)))) (oops/ocall manager :startActionMode text-input-handle))))
:reagent-render :reagent-render
(fn [_] (fn [chat-id {:keys [style ref on-selection-change] :as props} children]
(let [ref #(do (reset! text-input-ref %) (let [ref #(do (reset! text-input-ref %)
(when ref (when ref
(quo.react/set-ref-val! ref %))) (quo.react/set-ref-val! ref %)))
@ -361,7 +378,7 @@
(let [native-event (.-nativeEvent event) (let [native-event (.-nativeEvent event)
native-event (transforms/js->clj native-event) native-event (transforms/js->clj native-event)
{:keys [eventType content selectionStart selectionEnd]} native-event {:keys [eventType content selectionStart selectionEnd]} native-event
full-text (:input-text (rf/sub [:chats/current-chat-inputs]))] full-text (:input-text (rf/sub [:chats/current-chat-input]))]
(on-menu-item-touched {:first-level first-level (on-menu-item-touched {:first-level first-level
:event-type eventType :event-type eventType
:content content :content content

View File

@ -3,7 +3,6 @@
[quo.components.text :as text] [quo.components.text :as text]
[quo.react] [quo.react]
[quo.react-native :as rn] [quo.react-native :as rn]
[react-native.reanimated :as reanimated]
[status-im.ui.components.list.views :as list] [status-im.ui.components.list.views :as list]
[status-im.ui.screens.chat.photos :as photos] [status-im.ui.screens.chat.photos :as photos]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
@ -42,20 +41,11 @@
(defn autocomplete-mentions (defn autocomplete-mentions
[suggestions text-input-ref] [suggestions text-input-ref]
[:f> [list/flat-list
(fn [] {:keyboardShouldPersistTaps :always
(let [animation (reanimated/use-shared-value 0)] :data suggestions
(quo.react/effect! :key-fn first
#(reanimated/set-shared-value animation (reanimated/with-timing (if (seq suggestions) 0 200)))) :render-fn mention-item
[reanimated/view :render-data text-input-ref
{:style (reanimated/apply-animations-to-style :content-container-style {:padding-bottom 12}
{:transform [{:translateY animation}]} :accessibility-label :mentions-list}])
{:bottom 0 :position :absolute :z-index 5 :elevation 5 :max-height 180})}
[list/flat-list
{:keyboardShouldPersistTaps :always
:data suggestions
:key-fn first
:render-fn mention-item
:render-data text-input-ref
:content-container-style {:padding-bottom 12}
:accessibility-label :mentions-list}]]))])

View File

@ -1,6 +1,6 @@
(ns status-im.ui2.screens.chat.composer.reply (ns status-im.ui2.screens.chat.composer.reply
(:require [quo.react-native :as rn] (:require [quo.react-native :as rn]
[status-im.ui2.screens.chat.components.reply :as reply] [status-im.ui2.screens.chat.components.reply.view :as reply]
[status-im.ui2.screens.chat.composer.input :as input])) [status-im.ui2.screens.chat.composer.input :as input]))
(defn focus-input-on-reply (defn focus-input-on-reply
@ -21,4 +21,4 @@
[rn/view [rn/view
{:style {:padding-horizontal 15 {:style {:padding-horizontal 15
:padding-vertical 8}} :padding-vertical 8}}
[reply/reply-message reply true]])))) [reply/reply-message reply true]]))))

View File

@ -1,106 +0,0 @@
(ns status-im.ui2.screens.chat.composer.style
(:require [quo.design-system.colors :as quo.colors]
[quo2.foundations.colors :as colors]
[quo2.foundations.typography :as quo2.typography]
[status-im.utils.platform :as platform]))
(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 @quo.colors/theme)
:margin-horizontal 20}
(if platform/android?
{:padding-vertical 8
:text-align-vertical :top}
{:margin-top 8
:margin-bottom 8})))
(defn 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 (colors/theme-colors colors/white colors/neutral-90)
:z-index 2}
(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 bottom-sheet-handle
[]
{:width 32
:height 4
:background-color (colors/theme-colors colors/neutral-100 colors/white)
:opacity 0.05
:border-radius 100
:align-self :center
:margin-top 8})
(defn bottom-sheet-controls
[insets]
{:flex-direction :row
:padding-horizontal 20
:elevation 2
:z-index 3
:position :absolute
:background-color (colors/theme-colors colors/white 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 bottom-sheet-background
[window-height]
{:position :absolute
:left 0
:right 0
:bottom 0
:height window-height
:background-color colors/neutral-95-opa-70
:z-index 1})
(defn reply-content
[pin?]
{:padding-horizontal (when-not pin? 10)
:flex 1
:flex-direction :row})
(defn quoted-message
[pin?]
(merge {:flex-direction :row
:align-items :center
:width "45%"}
(when-not pin?
{:position :absolute
:left 34
:top 3})))
(def remove-photo-container
{:width 14
:height 14
:border-radius 7
:background-color colors/neutral-50
:position :absolute
:top -7
:right -7
:justify-content :center
:align-items :center})
(def small-image
{:width 56
:height 56
:border-radius 8
:margin-bottom 20})

View File

@ -1,345 +0,0 @@
(ns status-im.ui2.screens.chat.composer.view
(:require [clojure.string :as string]
[i18n.i18n :as i18n]
[oops.core :refer [oget]]
[quo.components.safe-area :as safe-area]
[quo.react]
[quo.react-native :as rn :refer [navigation-const]]
[quo2.components.buttons.button :as quo2.button]
[react-native.gesture :as gesture]
[react-native.reanimated :as reanimated]
[status-im.ui.components.permissions :as permissions]
[status-im.ui2.screens.chat.composer.edit.view :as edit]
[status-im.ui2.screens.chat.composer.images.view :as composer-images]
[status-im.ui2.screens.chat.composer.input :as input]
[status-im.ui2.screens.chat.composer.mentions :as mentions]
[status-im.ui2.screens.chat.composer.reply :as reply]
[status-im.ui2.screens.chat.composer.style :as style]
[status-im2.contexts.chat.photo-selector.view :as photo-selector]
[utils.re-frame :as rf]
[status-im.utils.utils :as utils]
[status-im2.contexts.chat.messages.list.view :refer [scroll-to-bottom]]
[status-im.utils.platform :as platform]
[status-im2.common.not-implemented :as not-implemented]))
(defn calculate-y
[context min-y max-y added-value chat-id set-bg-opacity]
(let [input-text (:input-text (get (rf/sub [:chat/inputs]) chat-id))
num-lines (count (string/split input-text "\n"))]
(if (<= 5 num-lines)
(do (when-not (:minimized-from-handlebar? @context)
(swap! context assoc :state :max)
(set-bg-opacity 1))
max-y)
(when (:minimized-from-handlebar? @context)
(swap! context assoc :state :min :minimized-from-handlebar? false)
(set-bg-opacity 0)
min-y))
(if (= (:state @context) :max)
(do
(set-bg-opacity 1)
max-y)
(when (< (:y @context) max-y)
(+ (:y @context) added-value)))))
(defn calculate-y-with-mentions
[y max-y max-height chat-id suggestions reply]
(let [input-text (:input-text (get (rf/sub [:chat/inputs]) chat-id))
num-lines (count (string/split input-text "\n"))
text-height (* num-lines 22)
mentions-height (min 132 (+ 16 (* 46 (- (count suggestions) 1))))
should-translate? (if (< (- max-height text-height) mentions-height) true false)
min-value (if-not reply mentions-height (+ mentions-height 44))
; translate value when mentions list appear while at bottom of expanded input sheet
mentions-translate-value (if should-translate?
(min min-value (- mentions-height (- max-height text-height)))
mentions-height)]
(when (or (< y max-y) should-translate?) mentions-translate-value)))
(defn get-y-value
[context min-y max-y added-value max-height chat-id suggestions reply edit images set-bg-opacity]
(let [y (calculate-y context min-y max-y added-value chat-id set-bg-opacity)
y-with-mentions (calculate-y-with-mentions y max-y max-height chat-id suggestions reply)]
(+ y (when (seq suggestions) y-with-mentions) (when (seq images) 80) (when edit 38))))
(defn- clean-and-minimize-composer
([context chat-id refs min-y]
(clean-and-minimize-composer context chat-id refs min-y false))
([context chat-id refs min-y edit?]
(input/clear-input chat-id refs)
(swap! context assoc
:y
(if edit?
(- min-y 38)
min-y))
(swap! context assoc :clear true :state :min)))
(defn get-bottom-sheet-gesture
[context translate-y text-input-ref keyboard-shown min-y max-y shared-height max-height set-bg-opacity]
(-> (gesture/gesture-pan)
(gesture/on-start
(fn [_]
(if keyboard-shown
(swap! context assoc :pan-y (reanimated/get-shared-value translate-y))
(input/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 :minimized-from-handlebar? false)
(swap! context assoc :state :max)
(input/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))
(set-bg-opacity 1))
(do
(swap! context assoc :minimized-from-handlebar? true)
(reanimated/set-shared-value translate-y (reanimated/with-timing (- min-y)))
(reanimated/set-shared-value shared-height (reanimated/with-timing min-y))
(set-bg-opacity 0)
(rf/dispatch [:dismiss-keyboard]))))))))
(defn get-input-content-change
[context translate-y shared-height max-height set-bg-opacity keyboard-shown min-y max-y blank-composer?
initial-value]
(fn [evt]
(when-not (or blank-composer? (some? initial-value))
(swap! context assoc :clear false))
(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))
(set-bg-opacity 0))
(when (not= (:state @context) :max)
(let [offset-value (if platform/ios? 22 40)
new-y (+ min-y
(- (max (oget evt "nativeEvent" "contentSize" "height") offset-value)
offset-value))]
(if (< new-y max-y)
(do
(if (> (- max-y new-y) 120)
(do
(swap! context assoc :state :custom-chat-available)
(set-bg-opacity 0))
(do
(set-bg-opacity 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)
(if keyboard-shown
(do (set-bg-opacity 1)
(reanimated/set-shared-value
translate-y
(reanimated/with-timing (- max-y))))
(set-bg-opacity 0)))))))))
(defn composer
[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
:minimized-from-handlebar? false})
keyboard-was-shown? (atom false)
text-input-ref (quo.react/create-ref)
send-ref (quo.react/create-ref)
refs {:send-ref send-ref
:text-input-ref text-input-ref}]
(fn []
[:f>
(fn []
(let [reply (rf/sub [:chats/reply-message])
edit (rf/sub [:chats/edit-message])
suggestions (rf/sub [:chat/mention-suggestions])
images (get-in (rf/sub [:chat/inputs])
[chat-id :metadata :sending-image])
{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)
(:status-bar-height @navigation-const)) ; 360
; -
; default
; height
max-height (Math/abs (- max-y 56 (:bottom insets))) ; 56
; -
; top-bar
; height
added-value (if (and (not (seq suggestions))
(or edit reply))
38
0) ; increased height
; of input box
; needed when reply
min-y (+ min-y (when (or edit reply) 38))
bg-opacity (reanimated/use-shared-value 0)
bg-bottom (reanimated/use-shared-value (-
window-height))
set-bg-opacity
(fn [value]
(reanimated/set-shared-value bg-bottom (if (= value 1) 0 (- window-height)))
(reanimated/set-shared-value bg-opacity (reanimated/with-timing value)))
y (get-y-value
context
min-y
max-y
added-value
max-height
chat-id
suggestions
reply
edit
images
set-bg-opacity)
translate-y (reanimated/use-shared-value 0)
shared-height (reanimated/use-shared-value min-y)
clean-and-minimize-composer-fn
#(clean-and-minimize-composer context chat-id refs min-y %)
blank-composer? (string/blank? (get @input/input-texts
chat-id))
initial-value (or (get @input/input-texts chat-id) nil)
input-content-change (get-input-content-change
context
translate-y
shared-height
max-height
set-bg-opacity
keyboard-shown
min-y
max-y
blank-composer?
initial-value)
bottom-sheet-gesture (get-bottom-sheet-gesture
context
translate-y
text-input-ref
keyboard-shown
min-y
max-y
shared-height
max-height
set-bg-opacity)]
(quo.react/effect!
(fn []
(let [input-text (-> [:chat/inputs]
rf/sub
(get chat-id)
:input-text)
edited? (not= input-text (-> edit :content :text))]
(when (and @keyboard-was-shown? (not keyboard-shown))
(swap! context assoc :state :min)
(set-bg-opacity 0))
(when (and blank-composer? (empty? images) (not edit))
(clean-and-minimize-composer-fn false))
(if (or (seq images) (and (not blank-composer?) edited?))
(input/show-send refs)
(input/hide-send refs))
(reset! keyboard-was-shown? keyboard-shown)
(if (#{:max :custom-chat-unavailable} (:state @context))
(set-bg-opacity 1)
(set-bg-opacity 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}
{:z-index 2})}
;;INPUT MESSAGE bottom sheet
[gesture/gesture-detector {:gesture bottom-sheet-gesture}
[reanimated/view
{:style (reanimated/apply-animations-to-style
{:transform [{:translateY translate-y}]}
(style/input-bottom-sheet window-height))}
;handle
[rn/view {:style (style/bottom-sheet-handle)}]
[edit/edit-message-auto-focus-wrapper text-input-ref edit
clean-and-minimize-composer-fn]
[reply/reply-message-auto-focus-wrapper text-input-ref reply]
[rn/view {:style {:height (- max-y 80 added-value)}}
[input/text-input
{:chat-id chat-id
:on-content-size-change input-content-change
:sending-image (seq images)
:initial-value initial-value
:refs refs
:set-active-panel #()}]]]]
;CONTROLS
(when-not (seq suggestions)
[rn/view {:style (style/bottom-sheet-controls insets)}
[quo2.button/button
{:on-press (fn []
(permissions/request-permissions
{:permissions [:read-external-storage :write-external-storage]
:on-allowed #(rf/dispatch
[:bottom-sheet/show-sheet
{:content (fn []
(photo-selector/photo-selector chat-id))}])
:on-denied (fn []
(utils/set-timeout
#(utils/show-popup (i18n/label :t/error)
(i18n/label
:t/external-storage-denied))
50))}))
:icon true
:type :outline
:size 32} :i/image]
[rn/view {:width 12}]
[not-implemented/not-implemented
[quo2.button/button
{:icon true
:type :outline
:size 32} :i/reaction]]
[rn/view {:flex 1}]
;;SEND button
[rn/view
{:ref send-ref
:style (when (seq images)
{:width 0
:right -100})}
[quo2.button/button
{:icon true
:size 32
:accessibility-label :send-message-button
:on-press #(do (clean-and-minimize-composer-fn (some? edit))
(scroll-to-bottom)
(rf/dispatch [:chat.ui/send-current-message]))}
:i/arrow-up]]])
;black background
[reanimated/view
{:style (reanimated/apply-animations-to-style
{:opacity bg-opacity
:transform [{:translateY bg-bottom}]}
(style/bottom-sheet-background window-height))}]
[composer-images/images-list images]
[mentions/autocomplete-mentions suggestions text-input-ref]]))])))])

View File

@ -23,7 +23,7 @@
[status-im.ui.screens.chat.styles.message.message :as style] [status-im.ui.screens.chat.styles.message.message :as style]
[status-im.ui.screens.chat.utils :as chat.utils] [status-im.ui.screens.chat.utils :as chat.utils]
[status-im.ui.screens.communities.icon :as communities.icon] [status-im.ui.screens.communities.icon :as communities.icon]
[status-im.ui2.screens.chat.components.reply :as components.reply] [status-im.ui2.screens.chat.components.reply.view :as components.reply]
[status-im.utils.config :as config] [status-im.utils.config :as config]
[utils.datetime :as datetime] [utils.datetime :as datetime]
[status-im.utils.utils :as utils] [status-im.utils.utils :as utils]

View File

@ -1,4 +1,4 @@
(ns status-im.ui2.screens.common.contact-list.view (ns status-im2.common.contact-list.view
(:require [quo2.core :as quo] (:require [quo2.core :as quo]
[react-native.core :as rn] [react-native.core :as rn]
[status-im2.common.contact-list-item.view :as contact-list-item] [status-im2.common.contact-list-item.view :as contact-list-item]

View File

@ -3,6 +3,7 @@
[quo2.core :as quo] [quo2.core :as quo]
[quo2.foundations.colors :as colors] [quo2.foundations.colors :as colors]
[react-native.core :as rn] [react-native.core :as rn]
[react-native.blur :as blur]
[react-native.platform :as platform] [react-native.platform :as platform]
[reagent.core :as reagent] [reagent.core :as reagent]
[status-im2.common.scroll-page.style :as style] [status-im2.common.scroll-page.style :as style]
@ -68,7 +69,7 @@
{:source cover {:source cover
:position :absolute :position :absolute
:style (style/image-slider (get-header-size @scroll-height))}] :style (style/image-slider (get-header-size @scroll-height))}]
[rn/blur-view (style/blur-slider (get-header-size @scroll-height))]] [blur/view (style/blur-slider (get-header-size @scroll-height))]]
[rn/view {:style {:z-index 6 :margin-top (if platform/ios? 44 0)}} [rn/view {:style {:z-index 6 :margin-top (if platform/ios? 44 0)}}
[quo/page-nav [quo/page-nav
{:horizontal-description? true {:horizontal-description? true

View File

@ -6,7 +6,7 @@
[react-native.core :as rn] [react-native.core :as rn]
[status-im.chat.models :as chat.models] [status-im.chat.models :as chat.models]
[status-im2.contexts.chat.group-details.style :as style] [status-im2.contexts.chat.group-details.style :as style]
[status-im.ui2.screens.common.contact-list.view :as contact-list] [status-im2.common.contact-list.view :as contact-list]
[status-im2.common.contact-list-item.view :as contact-list-item] [status-im2.common.contact-list-item.view :as contact-list-item]
[status-im2.common.home.actions.view :as actions] [status-im2.common.home.actions.view :as actions]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))

View File

@ -4,7 +4,7 @@
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[react-native.core :as rn] [react-native.core :as rn]
[reagent.core :as reagent] [reagent.core :as reagent]
[status-im.ui2.screens.common.contact-list.view :as contact-list] [status-im2.common.contact-list.view :as contact-list]
[status-im2.common.home.view :as common.home] [status-im2.common.home.view :as common.home]
[status-im2.contexts.chat.home.chat-list-item.view :as chat-list-item] [status-im2.contexts.chat.home.chat-list-item.view :as chat-list-item]
[status-im2.contexts.chat.home.contact-request.view :as contact-request] [status-im2.contexts.chat.home.contact-request.view :as contact-request]

View File

@ -0,0 +1,14 @@
(ns status-im2.contexts.chat.messages.composer.controls.style
(:require [quo2.foundations.colors :as colors]))
(defn controls
[insets]
{:padding-horizontal 20
:elevation 4
:z-index 3
:position :absolute
:background-color (colors/theme-colors colors/white colors/neutral-90)
:padding-bottom (+ 12 (:bottom insets))
:bottom 0
:left 0
:right 0})

View File

@ -0,0 +1,71 @@
(ns status-im2.contexts.chat.messages.composer.controls.view
(:require [react-native.core :as rn]
[react-native.background-timer :as background-timer]
[utils.re-frame :as rf]
[i18n.i18n :as i18n]
[quo2.core :as quo]
[status-im2.common.not-implemented :as not-implemented]
[status-im2.contexts.chat.photo-selector.view :as photo-selector]
[status-im2.contexts.chat.messages.composer.controls.style :as style]
[status-im2.contexts.chat.messages.list.view :as messages.list]
[status-im.ui.components.permissions :as permissions]
[status-im.ui2.screens.chat.composer.images.view :as composer-images]
[status-im.utils.utils :as utils-old]
[status-im.ui2.screens.chat.composer.input :as input]))
(defn send-button
[send-ref {:keys [chat-id images]} on-send]
[rn/view
{:ref send-ref
:style (when-not (or (seq (get @input/input-texts chat-id)) (seq images))
{:width 0
:right -100})}
[quo/button
{:icon true
:size 32
:accessibility-label :send-message-button
:on-press (fn []
(on-send)
(messages.list/scroll-to-bottom)
(rf/dispatch [:chat.ui/send-current-message]))}
:i/arrow-up]])
(defn reactions-button
[]
[not-implemented/not-implemented
[quo/button
{:icon true
:type :outline
:size 32} :i/reaction]])
(defn image-button
[chat-id]
[quo/button
{:on-press (fn []
(permissions/request-permissions
{:permissions [:read-external-storage :write-external-storage]
:on-allowed #(rf/dispatch
[:bottom-sheet/show-sheet
{:content (fn []
(photo-selector/photo-selector chat-id))}])
:on-denied (fn []
(background-timer/set-timeout
#(utils-old/show-popup (i18n/label :t/error)
(i18n/label
:t/external-storage-denied))
50))}))
:icon true
:type :outline
:size 32}
:i/image])
(defn view
[send-ref params insets chat-id images on-send]
[rn/view {:style (style/controls insets)}
[composer-images/images-list images]
[rn/view {:style {:flex-direction :row :margin-top 12}}
[image-button chat-id]
[rn/view {:width 12}]
[reactions-button]
[rn/view {:flex 1}]
[send-button send-ref params on-send]]])

View File

@ -0,0 +1,42 @@
(ns status-im2.contexts.chat.messages.composer.style
(:require [quo2.foundations.colors :as colors]
[status-im.utils.platform :as platform]))
(defn 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 (colors/theme-colors colors/white colors/neutral-90)
:z-index 2}
(if platform/ios?
{:shadow-radius 16
:shadow-opacity 1
:shadow-color "rgba(9, 16, 28, 0.04)"
:shadow-offset {:width 0 :height -2}}
{:elevation 4})))
(defn bottom-sheet-handle
[]
{:width 32
:height 4
:background-color (colors/theme-colors colors/neutral-100 colors/white)
:opacity 0.05
:border-radius 100
:align-self :center
:margin-top 8})
(defn bottom-sheet-background
[window-height]
{:position :absolute
:left 0
:right 0
:bottom 0
:height window-height
:background-color colors/neutral-95-opa-70
:z-index 1})

View File

@ -0,0 +1,250 @@
(ns status-im2.contexts.chat.messages.composer.view
(:require [clojure.string :as string]
[oops.core :refer [oget]]
[react-native.core :as rn]
[react-native.hooks :as hooks]
[react-native.gesture :as gesture]
[react-native.reanimated :as reanimated]
[react-native.background-timer :as background-timer]
[utils.re-frame :as rf]
[status-im2.contexts.chat.messages.composer.style :as style]
[status-im2.contexts.chat.messages.composer.controls.view :as controls]
[status-im.ui2.screens.chat.composer.edit.view :as edit]
[status-im.ui2.screens.chat.composer.input :as input]
[status-im.ui2.screens.chat.composer.mentions :as mentions]
[status-im.ui2.screens.chat.composer.reply :as reply]))
(def initial-content-height (atom nil))
(def keyboard-hiding? (atom false))
(defn minimize
[{:keys [min-y set-bg-opacity set-translate-y set-parent-height]}]
(set-bg-opacity 0)
(set-translate-y (- min-y))
(set-parent-height min-y))
(defn maximize
[{:keys [max-y set-bg-opacity set-translate-y set-parent-height max-parent-height]}]
(set-bg-opacity 1)
(set-translate-y (- max-y))
(set-parent-height max-parent-height))
(defn clean-and-minimize
[{:keys [chat-id refs] :as params}]
(input/clear-input chat-id refs)
(minimize params))
(def gesture-values (atom {}))
(defn get-bottom-sheet-gesture
[{:keys [translate-y refs keyboard-shown min-y max-y] :as params}]
(let [{:keys [text-input-ref]} refs]
(-> (gesture/gesture-pan)
(gesture/on-start
(fn [_]
(if keyboard-shown
(swap! gesture-values assoc :pan-y (reanimated/get-shared-value translate-y))
(input/input-focus text-input-ref))))
(gesture/on-update
(fn [evt]
(when keyboard-shown
(let [tY (oget evt "translationY")]
(swap! gesture-values assoc :dy (- tY (:pdy @gesture-values)))
(swap! gesture-values assoc :pdy tY)
(reanimated/set-shared-value
translate-y
(max (min (+ tY (:pan-y @gesture-values)) (- min-y)) (- max-y)))))))
(gesture/on-end
(fn [_]
(when keyboard-shown
(if (< (:dy @gesture-values) 0)
(maximize params)
(do
(reset! keyboard-hiding? true)
(minimize params)
(background-timer/set-timeout #(rf/dispatch [:dismiss-keyboard]) 200)))))))))
(defn update-y
[{:keys [max-parent-height set-bg-opacity keyboard-shown max-y set-translate-y set-parent-height
min-y chat-id]
:as params}]
(let [content-height (get @input/input-text-content-heights chat-id)
new-y (+ min-y
(if (and content-height @initial-content-height)
(- content-height
@initial-content-height)
0))]
(if (< new-y max-y)
(when keyboard-shown
(if (> new-y max-parent-height)
(set-bg-opacity 1)
(set-bg-opacity 0))
(set-translate-y (- new-y))
(set-parent-height (min new-y max-parent-height)))
(if keyboard-shown
(maximize params)
(set-bg-opacity 0)))))
(defn get-input-content-change
[{:keys [chat-id] :as params}]
(fn [evt]
(let [new-height (oget evt "nativeEvent.contentSize.height")]
(swap! input/input-text-content-heights assoc chat-id new-height)
(if @initial-content-height
(do
;;on Android when last symbol removed, height value is wrong, like 300px
(when (string/blank? (get @input/input-texts chat-id))
(swap! input/input-text-content-heights assoc chat-id @initial-content-height))
(update-y params))
(reset! initial-content-height new-height)))))
(defn mentions
[{:keys [refs suggestions max-y]} insets]
[:f>
(fn []
(let [translate-y (reanimated/use-shared-value 0)]
(rn/use-effect
(fn []
(reanimated/set-shared-value translate-y
(reanimated/with-timing (if (seq suggestions) 0 200)))))
[reanimated/view
{:style (reanimated/apply-animations-to-style
{:transform [{:translateY translate-y}]}
{:bottom (or (:bottom insets) 0)
:position :absolute
:z-index 5
:elevation 5
:max-height (/ max-y 2)})}
[mentions/autocomplete-mentions suggestions (:text-input-ref refs)]]))])
(defn effect!
[{:keys [keyboard-shown] :as params}]
(rn/use-effect
(fn []
(when (or (not @keyboard-hiding?)
(and @keyboard-hiding? (not keyboard-shown)))
(reset! keyboard-hiding? false)
(if (not keyboard-shown)
(minimize params)
(update-y params))))))
(defn prepare-params
[[refs window-height translate-y bg-opacity bg-bottom min-y max-y parent-height
max-parent-height chat-id suggestions reply edit images keyboard-shown]]
{:chat-id chat-id
:translate-y translate-y
:min-y min-y
:max-y max-y
:max-parent-height max-parent-height
:parent-height parent-height
:suggestions suggestions
:reply reply
:edit edit
:images images
:refs refs
:keyboard-shown keyboard-shown
:bg-opacity bg-opacity
:bg-bottom bg-bottom
:window-height window-height
:set-bg-opacity (fn [value]
(reanimated/set-shared-value bg-bottom (if (= value 1) 0 (- window-height)))
(reanimated/set-shared-value bg-opacity (reanimated/with-timing value)))
:set-translate-y (fn [value]
(reanimated/set-shared-value translate-y (reanimated/with-timing value)))
:set-parent-height (fn [value]
(reanimated/set-shared-value parent-height (reanimated/with-timing value)))})
;; IMPORTANT! PLEASE READ BEFORE MAKING CHANGES
;; 1) insets are not stable so we can't rely on the first values and we have to use it from render
;; function, so effect will be triggered a few times, for example first time it will be 0 and second
;; time 38 on ios, and 400 and 0 on Android
;; 2) for keyboard we use hooks, for some reason it triggers effect a few times with the same values,
;; also it changes value only when keyboard completely closed or opened, that's why its not visually
;; smooth, there is a small visible delay between keyboard and sheet animations
;; 3) when we minimize sheet and hide keyboard in `sheet-gesture` function, keyboard hook triggers
;; effect and there is a visual glitch when we have more than one line of text, so we use
;; `keyboard-hiding?` flag
;; 4) we store input content height in `input/input-text-content-heights` atom , we need it when chat
;; screen is reopened
(defn composer
[_ _]
(let [text-input-ref (rn/create-ref)
send-ref (rn/create-ref)
refs {:send-ref send-ref
:text-input-ref text-input-ref}]
(fn [chat-id insets]
[:f>
(fn []
(let [reply (rf/sub [:chats/reply-message])
edit (rf/sub [:chats/edit-message])
suggestions (rf/sub [:chat/mention-suggestions])
images (rf/sub [:chats/sending-image])
{window-height :height} (rn/use-window-dimensions)
{:keys [keyboard-shown keyboard-height]} (hooks/use-keyboard)
translate-y (reanimated/use-shared-value 0)
bg-opacity (reanimated/use-shared-value 0)
bg-bottom (reanimated/use-shared-value (- window-height))
suggestions? (and (seq suggestions)
keyboard-shown
(not @keyboard-hiding?))
max-y (- window-height
(- (if (> keyboard-height 0)
keyboard-height
360)
(:bottom insets))
46)
min-y (+ 108
(:bottom insets)
(if suggestions?
(min (/ max-y 2)
(+ 16
(* 46 (dec (count suggestions)))))
(+ 0
(when (or edit reply) 38)
(when (seq images) 80))))
parent-height (reanimated/use-shared-value min-y)
max-parent-height (Math/abs (- max-y 110 (:bottom insets)))
params
(prepare-params
[refs window-height translate-y bg-opacity bg-bottom min-y max-y parent-height
max-parent-height chat-id suggestions reply edit images keyboard-shown])
input-content-change (get-input-content-change params)
bottom-sheet-gesture (get-bottom-sheet-gesture params)]
(effect! params)
[reanimated/view
{:style (reanimated/apply-animations-to-style
{:height parent-height}
{})}
;;;;input
[gesture/gesture-detector {:gesture bottom-sheet-gesture}
[reanimated/view
{:style (reanimated/apply-animations-to-style
{:transform [{:translateY translate-y}]}
(style/input-bottom-sheet window-height))}
[rn/view {:style (style/bottom-sheet-handle)}]
[edit/edit-message-auto-focus-wrapper text-input-ref edit #(clean-and-minimize params)]
[reply/reply-message-auto-focus-wrapper text-input-ref reply]
[rn/view
{:style {:height (- max-y (- min-y 38))}}
[input/text-input
{:chat-id chat-id
:on-content-size-change input-content-change
:sending-image (seq images)
:refs refs}]]]]
(if suggestions?
[mentions params insets]
[controls/view send-ref params insets chat-id images #(clean-and-minimize params)])
;;;;black background
[reanimated/view
{:style (reanimated/apply-animations-to-style
{:opacity bg-opacity
:transform [{:translateY bg-bottom}]}
(style/bottom-sheet-background window-height))}]]))])))

View File

@ -6,7 +6,7 @@
[i18n.i18n :as i18n] [i18n.i18n :as i18n]
[status-im2.setup.config :as config] [status-im2.setup.config :as config]
[status-im.ui.components.react :as react] [status-im.ui.components.react :as react]
[status-im.ui2.screens.chat.components.reply :as components.reply] [status-im.ui2.screens.chat.components.reply.view :as components.reply]
[status-im2.common.not-implemented :as not-implemented])) [status-im2.common.not-implemented :as not-implemented]))
(defn pin-message (defn pin-message

View File

@ -2,19 +2,19 @@
(:require [quo2.core :as quo] (:require [quo2.core :as quo]
[re-frame.db] [re-frame.db]
[react-native.core :as rn] [react-native.core :as rn]
[react-native.safe-area :as safe-area]
[reagent.core :as reagent] [reagent.core :as reagent]
[status-im.ui2.screens.chat.composer.view :as composer] [status-im2.contexts.chat.messages.composer.view :as composer]
[status-im.ui2.screens.chat.pin-limit-popover.view :as pin-limit-popover]
[status-im2.common.constants :as constants] [status-im2.common.constants :as constants]
[status-im2.contexts.chat.messages.list.view :as messages.list] [status-im2.contexts.chat.messages.list.view :as messages.list]
[status-im2.contexts.chat.messages.contact-requests.bottom-drawer :as [status-im2.contexts.chat.messages.contact-requests.bottom-drawer :as
contact-requests.bottom-drawer] contact-requests.bottom-drawer]
[status-im2.contexts.chat.messages.pin.banner.view :as pin.banner] ;;TODO move to status-im2 [status-im2.contexts.chat.messages.pin.banner.view :as pin.banner]
[status-im2.navigation.state :as navigation.state] [status-im2.navigation.state :as navigation.state]
[status-im2.common.not-implemented :as not-implemented]
[utils.debounce :as debounce] [utils.debounce :as debounce]
[utils.re-frame :as rf] [utils.re-frame :as rf]
[status-im2.common.not-implemented :as not-implemented] [status-im.ui2.screens.chat.pin-limit-popover.view :as pin-limit-popover]))
[quo2.foundations.colors :as colors]))
(defn navigate-back-handler (defn navigate-back-handler
[] []
@ -64,30 +64,25 @@
(defn chat-render (defn chat-render
[] []
(let [{:keys [chat-id (let [;;NOTE: we want to react only on these fields, do not use full chat map here
contact-request-state {:keys [chat-id contact-request-state show-input?] :as chat}
show-input?]
:as chat}
(rf/sub [:chats/current-chat-chat-view])] (rf/sub [:chats/current-chat-chat-view])]
[rn/keyboard-avoiding-view {:style {:position :relative :flex 1}} [safe-area/consumer
[rn/view (fn [insets]
{:style {:position :absolute [rn/keyboard-avoiding-view
:top 56 {:style {:position :relative :flex 1}
:z-index 2 :keyboardVerticalOffset (- (:bottom insets))}
:background-color (colors/theme-colors colors/white colors/neutral-100) [page-nav]
:width "100%"}} [pin.banner/banner chat-id]
[not-implemented/not-implemented
[pin-limit-popover/pin-limit-popover chat-id]]
[messages.list/messages-list {:chat chat :show-input? show-input?}]
(cond (and (not show-input?)
contact-request-state)
[contact-requests.bottom-drawer/view chat-id contact-request-state]
[pin.banner/banner chat-id] show-input?
[not-implemented/not-implemented [composer/composer chat-id insets])])]))
[pin-limit-popover/pin-limit-popover chat-id]]]
[page-nav]
[messages.list/messages-list {:chat chat :show-input? show-input?}]
(cond (and (not show-input?)
contact-request-state)
[contact-requests.bottom-drawer/view chat-id contact-request-state]
show-input?
[composer/composer chat-id])]))
(defn chat (defn chat
[] []

View File

@ -4,6 +4,7 @@
[quo2.core :as quo] [quo2.core :as quo]
[quo2.foundations.colors :as colors] [quo2.foundations.colors :as colors]
[react-native.core :as rn] [react-native.core :as rn]
[react-native.blur :as blur]
[react-native.platform :as platform] [react-native.platform :as platform]
[reagent.core :as reagent] [reagent.core :as reagent]
[status-im2.common.constants :as constants] [status-im2.common.constants :as constants]
@ -307,7 +308,7 @@
[channel-heights first-channel-height] [channel-heights first-channel-height]
(fn [scroll-height] (fn [scroll-height]
(when (> scroll-height @first-channel-height) (when (> scroll-height @first-channel-height)
[rn/blur-view [blur/view
{:blur-amount 32 {:blur-amount 32
:blur-type :xlight :blur-type :xlight
:overlay-color (if platform/ios? colors/white-opa-70 :transparent) :overlay-color (if platform/ios? colors/white-opa-70 :transparent)

View File

@ -4,8 +4,8 @@
[react-native.reanimated :as reanimated] [react-native.reanimated :as reanimated]
[reagent.core :as reagent] [reagent.core :as reagent]
[status-im.async-storage.core :as async-storage] [status-im.async-storage.core :as async-storage]
[status-im2.contexts.shell.constants :as shell.constants] ;;TODO remove when not used anymore [status-im2.contexts.shell.constants :as shell.constants])) ;;TODO remove when not used anymore
))
;; Atoms ;; Atoms
(def selected-stack-id (atom nil)) (def selected-stack-id (atom nil))

View File

@ -1,6 +1,5 @@
(ns status-im2.navigation.view (ns status-im2.navigation.view
(:require [oops.core :refer [oget]] (:require [quo2.foundations.colors :as colors]
[quo2.foundations.colors :as colors]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[react-native.core :as rn] [react-native.core :as rn]
[react-native.safe-area :as safe-area] [react-native.safe-area :as safe-area]
@ -38,19 +37,14 @@
(concat screens/components))) (concat screens/components)))
(defn wrapped-screen-style (defn wrapped-screen-style
[{:keys [insets style]} insets-obj] [screen-insets safe-insets]
(merge (merge
{:flex 1 {:flex 1
:background-color (colors/theme-colors colors/white colors/neutral-100)} :background-color (colors/theme-colors colors/white colors/neutral-100)}
style (when (get screen-insets :bottom)
(when (get insets :bottom) {:padding-bottom (:bottom safe-insets)})
{:padding-bottom (+ (oget insets-obj "bottom") (when (get screen-insets :top true)
(get style :padding-bottom) {:padding-top (:top safe-insets)})))
(get style :padding-vertical))})
(when (get insets :top true)
{:padding-top (+ (oget insets-obj "top")
(get style :padding-top)
(get style :padding-vertical))})))
(defn inactive (defn inactive
[] []
@ -75,16 +69,13 @@
screens) screens)
(keyword key))] (keyword key))]
^{:key (str "root" key @reloader/cnt)} ^{:key (str "root" key @reloader/cnt)}
[safe-area/safe-area-provider [safe-area/provider
[safe-area/safe-area-consumer [safe-area/consumer
(fn [safe-insets] (fn [safe-insets]
(reagent/as-element [rn/view
[rn/view {:style (wrapped-screen-style insets safe-insets)}
{:style (wrapped-screen-style [inactive]
{:insets insets} [component]])]
safe-insets)}
[inactive]
[component]]))]
(when js/goog.DEBUG (when js/goog.DEBUG
[reloader/reload-view])])))) [reloader/reload-view])]))))
@ -99,7 +90,7 @@
(reagent/reactify-component (reagent/reactify-component
(fn [] (fn []
^{:key (str "popover" @reloader/cnt)} ^{:key (str "popover" @reloader/cnt)}
[safe-area/safe-area-provider [safe-area/provider
[inactive] [inactive]
[popover/popover] [popover/popover]
(when js/goog.DEBUG (when js/goog.DEBUG
@ -116,7 +107,7 @@
(reagent/reactify-component (reagent/reactify-component
(fn [] (fn []
^{:key (str "visibility-status-popover" @reloader/cnt)} ^{:key (str "visibility-status-popover" @reloader/cnt)}
[safe-area/safe-area-provider [safe-area/provider
[inactive] [inactive]
[visibility-status-views/visibility-status-popover] [visibility-status-views/visibility-status-popover]
(when js/goog.DEBUG (when js/goog.DEBUG
@ -126,7 +117,7 @@
(reagent/reactify-component (reagent/reactify-component
(fn [] (fn []
^{:key (str "sheet" @reloader/cnt)} ^{:key (str "sheet" @reloader/cnt)}
[safe-area/safe-area-provider [safe-area/provider
[inactive] [inactive]
[bottom-sheets/bottom-sheet] [bottom-sheets/bottom-sheet]
(when js/goog.DEBUG (when js/goog.DEBUG
@ -138,7 +129,7 @@
(reagent/reactify-component (reagent/reactify-component
(fn [] (fn []
^{:key (str "signing-sheet" @reloader/cnt)} ^{:key (str "signing-sheet" @reloader/cnt)}
[safe-area/safe-area-provider [safe-area/provider
[inactive] [inactive]
[signing/signing] [signing/signing]
(when js/goog.DEBUG (when js/goog.DEBUG
@ -148,7 +139,7 @@
(reagent/reactify-component (reagent/reactify-component
(fn [] (fn []
^{:key (str "select-acc-sheet" @reloader/cnt)} ^{:key (str "select-acc-sheet" @reloader/cnt)}
[safe-area/safe-area-provider [safe-area/provider
[inactive] [inactive]
[wallet.send.views/select-account] [wallet.send.views/select-account]
(when js/goog.DEBUG (when js/goog.DEBUG
@ -158,7 +149,7 @@
(reagent/reactify-component (reagent/reactify-component
(fn [] (fn []
^{:key (str "wallet-connect-sheet" @reloader/cnt)} ^{:key (str "wallet-connect-sheet" @reloader/cnt)}
[safe-area/safe-area-provider [safe-area/provider
[inactive] [inactive]
[wallet-connect/wallet-connect-proposal-sheet] [wallet-connect/wallet-connect-proposal-sheet]
(when js/goog.DEBUG (when js/goog.DEBUG
@ -168,7 +159,7 @@
(reagent/reactify-component (reagent/reactify-component
(fn [] (fn []
^{:key (str "wallet-connect-success-sheet" @reloader/cnt)} ^{:key (str "wallet-connect-success-sheet" @reloader/cnt)}
[safe-area/safe-area-provider [safe-area/provider
[inactive] [inactive]
[wallet-connect/wallet-connect-success-sheet-view] [wallet-connect/wallet-connect-success-sheet-view]
(when js/goog.DEBUG (when js/goog.DEBUG
@ -178,7 +169,7 @@
(reagent/reactify-component (reagent/reactify-component
(fn [] (fn []
^{:key (str "wallet-connect-app-management-sheet" @reloader/cnt)} ^{:key (str "wallet-connect-app-management-sheet" @reloader/cnt)}
[safe-area/safe-area-provider [safe-area/provider
[inactive] [inactive]
[wallet-connect/wallet-connect-app-management-sheet-view] [wallet-connect/wallet-connect-app-management-sheet-view]
(when js/goog.DEBUG (when js/goog.DEBUG

View File

@ -132,7 +132,7 @@
(get chats current-chat-id))) (get chats current-chat-id)))
(re-frame/reg-sub (re-frame/reg-sub
:chats/current-chat-inputs :chats/current-chat-input
:<- [:chats/current-chat-id] :<- [:chats/current-chat-id]
:<- [:chat/inputs] :<- [:chat/inputs]
(fn [[chat-id inputs]] (fn [[chat-id inputs]]
@ -285,19 +285,19 @@
(re-frame/reg-sub (re-frame/reg-sub
:chats/reply-message :chats/reply-message
:<- [:chats/current-chat-inputs] :<- [:chats/current-chat-input]
(fn [{:keys [metadata]}] (fn [{:keys [metadata]}]
(:responding-to-message metadata))) (:responding-to-message metadata)))
(re-frame/reg-sub (re-frame/reg-sub
:chats/edit-message :chats/edit-message
:<- [:chats/current-chat-inputs] :<- [:chats/current-chat-input]
(fn [{:keys [metadata]}] (fn [{:keys [metadata]}]
(:editing-message metadata))) (:editing-message metadata)))
(re-frame/reg-sub (re-frame/reg-sub
:chats/sending-contact-request :chats/sending-contact-request
:<- [:chats/current-chat-inputs] :<- [:chats/current-chat-input]
(fn [{:keys [metadata]}] (fn [{:keys [metadata]}]
(:sending-contact-request metadata))) (:sending-contact-request metadata)))