Use scrollable multiline text-input in contact request form (#19383)

* tweak: augment bottom-sheet component to be aware of keyboard height

* tweak: augment quo input component to allow for styling of the input, the input container, and container

* tweak: augment contact request input to grow until max screen size is reached for the bottom-sheet

* tweak: handle blurring the contact request input when keyboard is dismissed on android

* tidy: refactor with the use-keyboard hook

* tweak: use on-layout for measuring sheet max-height

* tweak: remove use of input-style and flex-direction column

* tidy: remove unused requires

* tidy: use container-style prop

* tidy: fix formatting

* fix: default layout-height to window-height
This commit is contained in:
Sean Hagstrom 2024-04-04 15:54:22 +01:00 committed by GitHub
parent 3dfd9df935
commit d24eb96c9d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 49 additions and 23 deletions

View File

@ -60,11 +60,11 @@
"Custom properties that must be removed from properties map passed to InputText." "Custom properties that must be removed from properties map passed to InputText."
[:type :blur? :error? :right-icon :left-icon :disabled? :small? :button [:type :blur? :error? :right-icon :left-icon :disabled? :small? :button
:label :char-limit :on-char-limit-reach :icon-name :multiline? :on-focus :on-blur :label :char-limit :on-char-limit-reach :icon-name :multiline? :on-focus :on-blur
:container-style :ref]) :container-style :input-container-style :ref])
(defn- base-input (defn- base-input
[{:keys [blur? error? right-icon left-icon disabled? small? button [{:keys [blur? error? right-icon left-icon disabled? small? button
label char-limit multiline? clearable? on-focus on-blur container-style label char-limit multiline? clearable? on-focus on-blur container-style input-container-style
on-change-text on-char-limit-reach weight default-value on-clear] on-change-text on-char-limit-reach weight default-value on-clear]
:as props}] :as props}]
(let [theme (quo.theme/use-theme-value) (let [theme (quo.theme/use-theme-value)
@ -119,7 +119,8 @@
:current-chars char-count :current-chars char-count
:char-limit char-limit :char-limit char-limit
:theme theme}]) :theme theme}])
[rn/view {:style (style/input-container colors-by-status small? disabled?)} [rn/view
{:style (merge (style/input-container colors-by-status small? disabled?) input-container-style)}
(when-let [{:keys [icon-name]} left-icon] (when-let [{:keys [icon-name]} left-icon]
[left-accessory [left-accessory
{:variant-colors variant-colors {:variant-colors variant-colors

View File

@ -10,6 +10,7 @@
[react-native.hooks :as hooks] [react-native.hooks :as hooks]
[react-native.reanimated :as reanimated] [react-native.reanimated :as reanimated]
[status-im.common.bottom-sheet.style :as style] [status-im.common.bottom-sheet.style :as style]
[utils.number]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
(def duration 450) (def duration 450)
@ -66,19 +67,23 @@
gradient-cover? customization-color hide-handle? blur-radius] gradient-cover? customization-color hide-handle? blur-radius]
:or {border-radius 12}}] :or {border-radius 12}}]
(let [theme (quo.theme/use-theme-value) (let [theme (quo.theme/use-theme-value)
{window-height :height} (rn/get-window)
[sheet-height set-sheet-height] (rn/use-state 0) [sheet-height set-sheet-height] (rn/use-state 0)
[layout-height set-layout-height] (rn/use-state window-height)
handle-sheet-height (rn/use-callback (fn [e] handle-sheet-height (rn/use-callback (fn [e]
(when (= sheet-height 0) (when (= sheet-height 0)
(set-sheet-height (set-sheet-height
(get-layout-height e)))) (get-layout-height e))))
[sheet-height]) [sheet-height])
handle-layout-height (rn/use-callback (fn [e]
(-> (get-layout-height e)
(set-layout-height))))
[item-height set-item-height] (rn/use-state 0) [item-height set-item-height] (rn/use-state 0)
handle-item-height (rn/use-callback (fn [e] handle-item-height (rn/use-callback (fn [e]
(when (= item-height 0) (when (= item-height 0)
(set-item-height (set-item-height
(get-layout-height e)))) (get-layout-height e))))
[item-height]) [item-height])
{window-height :height} (rn/get-window)
bg-opacity (reanimated/use-shared-value 0) bg-opacity (reanimated/use-shared-value 0)
translate-y (reanimated/use-shared-value window-height) translate-y (reanimated/use-shared-value window-height)
sheet-gesture (rn/use-memo #(get-sheet-gesture translate-y sheet-gesture (rn/use-memo #(get-sheet-gesture translate-y
@ -96,7 +101,8 @@
bottom (if selected-item-smaller-than-sheet? bottom (if selected-item-smaller-than-sheet?
(+ sheet-height bottom-margin) (+ sheet-height bottom-margin)
(:bottom insets)) (:bottom insets))
sheet-max-height (- window-height (:top insets)) sheet-max-height (- layout-height
(:top insets))
content-padding-bottom (or padding-bottom-override content-padding-bottom (or padding-bottom-override
(+ (:bottom insets) bottom-margin))] (+ (:bottom insets) bottom-margin))]
(rn/use-effect (rn/use-effect
@ -109,7 +115,9 @@
(on-close)) (on-close))
(rf/dispatch [:hide-bottom-sheet]) (rf/dispatch [:hide-bottom-sheet])
true)) true))
[rn/view {:style {:flex 1}} [rn/view
{:style {:flex 1}
:on-layout handle-layout-height}
;; backdrop ;; backdrop
[rn/pressable [rn/pressable
{:on-press #(rf/dispatch [:hide-bottom-sheet]) {:on-press #(rf/dispatch [:hide-bottom-sheet])

View File

@ -7,4 +7,5 @@
(def message-input-wrapper (def message-input-wrapper
{:padding-vertical 8 {:padding-vertical 8
:flex-shrink 1
:padding-horizontal 20}) :padding-horizontal 20})

View File

@ -2,6 +2,7 @@
(:require [clojure.string :as string] (:require [clojure.string :as string]
[quo.core :as quo] [quo.core :as quo]
[react-native.core :as rn] [react-native.core :as rn]
[react-native.platform :as platform]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.contexts.profile.contact.contact-request.style :as style] [status-im.contexts.profile.contact.contact-request.style :as style]
[status-im.contexts.profile.utils :as profile.utils] [status-im.contexts.profile.utils :as profile.utils]
@ -15,6 +16,7 @@
customization-color customization-color customization-color customization-color
full-name (profile.utils/displayed-name profile) full-name (profile.utils/displayed-name profile)
profile-picture (profile.utils/photo profile) profile-picture (profile.utils/photo profile)
input-ref (rn/use-ref-atom nil)
[message set-message] (rn/use-state "") [message set-message] (rn/use-state "")
on-message-change (rn/use-callback #(set-message %)) on-message-change (rn/use-callback #(set-message %))
on-message-submit (rn/use-callback (fn [] on-message-submit (rn/use-callback (fn []
@ -27,6 +29,14 @@
:text (i18n/label :text (i18n/label
:t/contact-request-was-sent)}])) :t/contact-request-was-sent)}]))
[public-key message])] [public-key message])]
(rn/use-mount
(fn []
(let [listener (.addListener rn/keyboard
"keyboardDidHide"
(fn [_event]
(when (and platform/android? @input-ref)
(.blur ^js @input-ref))))]
#(.remove ^js listener))))
[:<> [:<>
[quo/drawer-top [quo/drawer-top
{:type :context-tag {:type :context-tag
@ -39,17 +49,21 @@
(i18n/label :t/contact-request-message-prompt)] (i18n/label :t/contact-request-message-prompt)]
[rn/view {:style style/message-input-wrapper} [rn/view {:style style/message-input-wrapper}
[quo/input [quo/input
{:type :text {:type :text
:multiline? true :ref #(reset! input-ref %)
:char-limit constants/contact-request-message-max-length :multiline? true
:max-length constants/contact-request-message-max-length :char-limit constants/contact-request-message-max-length
:placeholder (i18n/label :t/type-something) :max-length constants/contact-request-message-max-length
:auto-focus true :placeholder (i18n/label :t/type-something)
:accessibility-label :contact-request-message :auto-focus true
:label (i18n/label :t/message) :accessibility-label :contact-request-message
:on-change-text on-message-change}]] :label (i18n/label :t/message)
:on-change-text on-message-change
:container-style {:flex-shrink 1}
:input-container-style {:flex-shrink 1}}]]
[quo/bottom-actions [quo/bottom-actions
{:actions :one-action {:container-style {:style {:flex 1}}
:actions :one-action
:button-one-props {:disabled? (string/blank? message) :button-one-props {:disabled? (string/blank? message)
:accessibility-label :send-contact-request :accessibility-label :send-contact-request
:customization-color customization-color :customization-color customization-color

View File

@ -92,19 +92,21 @@
(def bottom-sheet (def bottom-sheet
(reagent/reactify-component (reagent/reactify-component
(fn [] (fn []
(let [{:keys [sheets hide?]} (rf/sub [:bottom-sheet]) (let [{:keys [sheets hide?]} (rf/sub [:bottom-sheet])
sheet (last sheets) sheet (last sheets)
{:keys [theme]} sheet {:keys [theme]} sheet
insets (safe-area/get-insets) insets (safe-area/get-insets)
user-theme (theme/get-theme)] user-theme (theme/get-theme)
keyboard-vertical-offset (- (max 20 (:bottom insets)))]
^{:key (str "sheet" @reloader/cnt)} ^{:key (str "sheet" @reloader/cnt)}
[theme/provider {:theme (or theme user-theme)} [theme/provider {:theme (or theme user-theme)}
[inactive] [inactive]
[rn/keyboard-avoiding-view [rn/keyboard-avoiding-view
{:style {:position :relative :flex 1} {:style {:position :relative :flex 1}
:keyboard-vertical-offset (- (max 20 (:bottom insets)))} :keyboard-vertical-offset keyboard-vertical-offset}
(when sheet (when sheet
[bottom-sheet/view {:insets insets :hide? hide?} [bottom-sheet/view
{:insets insets :hide? hide?}
sheet])]])) sheet])]]))
functional-compiler)) functional-compiler))