mirror of
https://github.com/status-im/status-react.git
synced 2025-02-24 08:38:28 +00:00
simplify composer (#20125)
This commit is contained in:
parent
bf6c89e263
commit
babcd96fb6
@ -91,14 +91,14 @@
|
|||||||
:on-error #(rf/dispatch [:mention/on-error
|
:on-error #(rf/dispatch [:mention/on-error
|
||||||
{:method method
|
{:method method
|
||||||
:params params} %])}]}))
|
:params params} %])}]}))
|
||||||
|
|
||||||
(rf/defn on-to-input-field-success
|
(rf/defn on-to-input-field-success
|
||||||
{:events [:mention/on-to-input-field-success]}
|
{:events [:mention/on-to-input-field-success]}
|
||||||
[{:keys [db]} result]
|
[{:keys [db]} result]
|
||||||
(log/debug "[mentions] on-to-input-field-success" {:result result})
|
(log/debug "[mentions] on-to-input-field-success" {:result result})
|
||||||
(let [{:keys [input-segments state chat-id new-text]} (transfer-mention-result result)]
|
(let [{:keys [chat-id new-text]} (transfer-mention-result result)]
|
||||||
{:db (-> db
|
{:effects/set-input-text-value [(get-in db [:chat/inputs chat-id :input-ref]) new-text]
|
||||||
(assoc-in [:chats/mentions chat-id :mentions] state)
|
:dispatch [:chat.ui/set-chat-input-text new-text chat-id]}))
|
||||||
(assoc-in [:chat/inputs-with-mentions chat-id] input-segments))}))
|
|
||||||
|
|
||||||
(rf/defn on-change-text
|
(rf/defn on-change-text
|
||||||
{:events [:mention/on-change-text]}
|
{:events [:mention/on-change-text]}
|
||||||
@ -118,49 +118,32 @@
|
|||||||
{:events [:mention/on-change-text-success]}
|
{:events [:mention/on-change-text-success]}
|
||||||
[{:keys [db]} result]
|
[{:keys [db]} result]
|
||||||
(log/debug "[mentions] on-change-text-success" {:result result})
|
(log/debug "[mentions] on-change-text-success" {:result result})
|
||||||
(let [{:keys [state chat-id mentionable-users input-segments]} (transfer-mention-result result)]
|
(let [{:keys [chat-id mentionable-users]} (transfer-mention-result result)]
|
||||||
{:db (-> db
|
{:db (assoc-in db [:chats/mention-suggestions chat-id] mentionable-users)}))
|
||||||
(assoc-in [:chats/mention-suggestions chat-id] mentionable-users)
|
|
||||||
(assoc-in [:chats/mentions chat-id :mentions] state)
|
|
||||||
(assoc-in [:chat/inputs-with-mentions chat-id] input-segments))}))
|
|
||||||
|
|
||||||
(rf/defn on-select-mention-success
|
(rf/defn on-select-mention-success
|
||||||
{:events [:mention/on-select-mention-success]}
|
{:events [:mention/on-select-mention-success]}
|
||||||
[{:keys [db] :as cofx} result primary-name match searched-text public-key]
|
[{:keys [db]} result primary-name match searched-text public-key]
|
||||||
(log/debug "[mentions] on-select-mention-success"
|
(log/debug "[mentions] on-select-mention-success"
|
||||||
{:result result
|
{:result result
|
||||||
:primary-name primary-name
|
:primary-name primary-name
|
||||||
:match match
|
:match match
|
||||||
:searched-text searched-text
|
:searched-text searched-text
|
||||||
:public-key public-key})
|
:public-key public-key})
|
||||||
(let [{:keys [new-text chat-id state input-segments]} (transfer-mention-result result)]
|
(let [{:keys [new-text chat-id]} (transfer-mention-result result)]
|
||||||
{:db (-> db
|
{:db (assoc-in db [:chats/mention-suggestions chat-id] nil)
|
||||||
(assoc-in [:chats/mentions chat-id :mentions] state)
|
:effects/set-input-text-value [(get-in db [:chat/inputs (:current-chat-id db) :input-ref]) new-text]
|
||||||
(assoc-in [:chat/inputs-with-mentions chat-id] input-segments)
|
:dispatch [:chat.ui/set-chat-input-text new-text chat-id]}))
|
||||||
(assoc-in [:chats/mention-suggestions chat-id] nil))
|
|
||||||
:dispatch [:chat.ui/set-chat-input-text new-text chat-id]}))
|
|
||||||
|
|
||||||
(rf/defn clear-suggestions
|
|
||||||
[{:keys [db]}]
|
|
||||||
(log/debug "[mentions] clear suggestions")
|
|
||||||
(let [chat-id (:current-chat-id db)]
|
|
||||||
{:db (update db :chats/mention-suggestions dissoc chat-id)}))
|
|
||||||
|
|
||||||
(rf/defn clear-mentions
|
(rf/defn clear-mentions
|
||||||
[{:keys [db] :as cofx}]
|
[{:keys [db]}]
|
||||||
(log/debug "[mentions] clear mentions")
|
|
||||||
(let [chat-id (:current-chat-id db)]
|
(let [chat-id (:current-chat-id db)]
|
||||||
(rf/merge
|
{:db (update db :chats/mention-suggestions dissoc chat-id)
|
||||||
cofx
|
:json-rpc/call [{:method "wakuext_chatMentionClearMentions"
|
||||||
{:db (-> db
|
:params [chat-id]
|
||||||
(update-in [:chats/mentions chat-id] dissoc :mentions)
|
:on-success #()
|
||||||
(update :chat/inputs-with-mentions dissoc chat-id))
|
:on-error #(log/error "Error while calling wakuext_chatMentionClearMentions"
|
||||||
:json-rpc/call [{:method "wakuext_chatMentionClearMentions"
|
{:error %})}]}))
|
||||||
:params [chat-id]
|
|
||||||
:on-success #()
|
|
||||||
:on-error #(log/error "Error while calling wakuext_chatMentionClearMentions"
|
|
||||||
{:error %})}]}
|
|
||||||
(clear-suggestions))))
|
|
||||||
|
|
||||||
(rf/defn select-mention
|
(rf/defn select-mention
|
||||||
{:events [:chat.ui/select-mention]}
|
{:events [:chat.ui/select-mention]}
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
(ns status-im.contexts.chat.messenger.composer.actions.image.view
|
||||||
|
(:require [quo.core :as quo]
|
||||||
|
[react-native.permissions :as permissions]
|
||||||
|
[react-native.platform :as platform]
|
||||||
|
[status-im.common.alert.effects :as alert.effects]
|
||||||
|
[status-im.common.device-permissions :as device-permissions]
|
||||||
|
[status-im.constants :as constants]
|
||||||
|
[utils.i18n :as i18n]
|
||||||
|
[utils.re-frame :as rf]))
|
||||||
|
|
||||||
|
(defn photo-limit-toast
|
||||||
|
[]
|
||||||
|
(rf/dispatch [:toasts/upsert
|
||||||
|
{:id :random-id
|
||||||
|
:type :negative
|
||||||
|
:text (i18n/label :t/hit-photos-limit
|
||||||
|
{:max-photos constants/max-album-photos})}]))
|
||||||
|
|
||||||
|
(defn go-to-camera
|
||||||
|
[images-count]
|
||||||
|
(device-permissions/camera #(if (>= images-count constants/max-album-photos)
|
||||||
|
(photo-limit-toast)
|
||||||
|
(rf/dispatch [:navigate-to :camera-screen]))))
|
||||||
|
|
||||||
|
(defn camera-button
|
||||||
|
[]
|
||||||
|
(let [images-count (count (vals (rf/sub [:chats/sending-image])))]
|
||||||
|
[quo/composer-button
|
||||||
|
{:on-press #(go-to-camera images-count)
|
||||||
|
:accessibility-label :camera-button
|
||||||
|
:icon :i/camera
|
||||||
|
:container-style {:margin-right 12}}]))
|
||||||
|
|
||||||
|
(defn open-photo-selector
|
||||||
|
[input-ref]
|
||||||
|
(permissions/request-permissions
|
||||||
|
{:permissions [(if platform/is-below-android-13? :read-external-storage :read-media-images)
|
||||||
|
:write-external-storage]
|
||||||
|
:on-allowed (fn []
|
||||||
|
(when (and platform/android? @input-ref)
|
||||||
|
(.blur ^js @input-ref))
|
||||||
|
(when platform/ios?
|
||||||
|
(rf/dispatch [:alert-banners/hide]))
|
||||||
|
(rf/dispatch [:photo-selector/navigate-to-photo-selector]))
|
||||||
|
:on-denied #(alert.effects/show-popup (i18n/label :t/error)
|
||||||
|
(i18n/label :t/external-storage-denied))}))
|
||||||
|
|
||||||
|
(defn image-button
|
||||||
|
[input-ref]
|
||||||
|
[quo/composer-button
|
||||||
|
{:on-press #(open-photo-selector input-ref)
|
||||||
|
:accessibility-label :open-images-button
|
||||||
|
:container-style {:margin-right 12}
|
||||||
|
:icon :i/image}])
|
@ -7,26 +7,14 @@
|
|||||||
{:height constants/actions-container-height
|
{:height constants/actions-container-height
|
||||||
:justify-content :space-between
|
:justify-content :space-between
|
||||||
:align-items :center
|
:align-items :center
|
||||||
:z-index 2
|
|
||||||
:flex-direction :row})
|
:flex-direction :row})
|
||||||
|
|
||||||
(defn send-button
|
(defn send-button
|
||||||
[opacity z-index]
|
[opacity]
|
||||||
(reanimated/apply-animations-to-style
|
(reanimated/apply-animations-to-style
|
||||||
{:opacity opacity}
|
{:opacity opacity}
|
||||||
{:position :absolute
|
{:position :absolute
|
||||||
:right 0
|
:right 0
|
||||||
:z-index z-index
|
|
||||||
:padding-vertical 3
|
:padding-vertical 3
|
||||||
:padding-left 2}))
|
:padding-left 2}))
|
||||||
|
|
||||||
(defn record-audio-container
|
|
||||||
[]
|
|
||||||
{:align-items :center
|
|
||||||
:background-color :transparent
|
|
||||||
:flex-direction :row
|
|
||||||
:position :absolute
|
|
||||||
:left -20
|
|
||||||
:right -20
|
|
||||||
:bottom 0
|
|
||||||
:height constants/composer-default-height})
|
|
||||||
|
@ -2,246 +2,60 @@
|
|||||||
(:require
|
(:require
|
||||||
[quo.core :as quo]
|
[quo.core :as quo]
|
||||||
[react-native.core :as rn]
|
[react-native.core :as rn]
|
||||||
[react-native.permissions :as permissions]
|
|
||||||
[react-native.platform :as platform]
|
|
||||||
[react-native.reanimated :as reanimated]
|
[react-native.reanimated :as reanimated]
|
||||||
[reagent.core :as reagent]
|
|
||||||
[status-im.common.alert.effects :as alert.effects]
|
|
||||||
[status-im.common.device-permissions :as device-permissions]
|
|
||||||
[status-im.config :as config]
|
|
||||||
[status-im.constants :as constants]
|
[status-im.constants :as constants]
|
||||||
|
[status-im.contexts.chat.messenger.composer.actions.image.view :as actions.image]
|
||||||
[status-im.contexts.chat.messenger.composer.actions.style :as style]
|
[status-im.contexts.chat.messenger.composer.actions.style :as style]
|
||||||
[status-im.contexts.chat.messenger.composer.constants :as comp-constants]
|
|
||||||
[utils.i18n :as i18n]
|
|
||||||
[utils.re-frame :as rf]))
|
[utils.re-frame :as rf]))
|
||||||
|
|
||||||
(defn send-message
|
(defn send-message
|
||||||
"Minimize composer, animate-out background overlay, clear input and flush state"
|
[input-ref edit btn-opacity]
|
||||||
[{:keys [sending-images? sending-links?]}
|
(when @input-ref
|
||||||
{:keys [text-value maximized?]}
|
(.clear ^js @input-ref))
|
||||||
{:keys [height saved-height last-height opacity background-y]}
|
(reanimated/animate btn-opacity 0)
|
||||||
window-height
|
|
||||||
edit]
|
|
||||||
(reanimated/animate height comp-constants/input-height)
|
|
||||||
(reanimated/set-shared-value saved-height comp-constants/input-height)
|
|
||||||
(reanimated/set-shared-value last-height comp-constants/input-height)
|
|
||||||
(reanimated/animate opacity 0)
|
|
||||||
(js/setTimeout #(reanimated/set-shared-value background-y
|
|
||||||
(- window-height))
|
|
||||||
300)
|
|
||||||
(rf/dispatch [:chat.ui/send-current-message])
|
(rf/dispatch [:chat.ui/send-current-message])
|
||||||
(rf/dispatch [:chat.ui/set-input-maximized false])
|
|
||||||
(rf/dispatch [:chat.ui/set-input-content-height comp-constants/input-height])
|
|
||||||
(rf/dispatch [:chat.ui/set-chat-input-text nil])
|
(rf/dispatch [:chat.ui/set-chat-input-text nil])
|
||||||
(reset! maximized? false)
|
|
||||||
(reset! text-value "")
|
|
||||||
(reset! sending-links? false)
|
|
||||||
(reset! sending-images? false)
|
|
||||||
(when-not (some? edit)
|
(when-not (some? edit)
|
||||||
(rf/dispatch [:chat.ui/scroll-to-bottom])))
|
(rf/dispatch [:chat.ui/scroll-to-bottom])))
|
||||||
|
|
||||||
(defn f-send-button
|
(defn send-button
|
||||||
[props state animations window-height images? btn-opacity z-index edit]
|
[input-ref edit]
|
||||||
(let [{:keys [text-value]} state
|
(let [btn-opacity (reanimated/use-shared-value 0)
|
||||||
|
chat-input (rf/sub [:chats/current-chat-input])
|
||||||
|
input-text (:input-text chat-input)
|
||||||
|
images? (boolean (seq (rf/sub [:chats/sending-image])))
|
||||||
profile-customization-color (rf/sub [:profile/customization-color])
|
profile-customization-color (rf/sub [:profile/customization-color])
|
||||||
{:keys [chat-id chat-type]
|
{:keys [chat-id chat-type]
|
||||||
chat-color :color} (rf/sub [:chats/current-chat-chat-view])
|
chat-color :color} (rf/sub [:chats/current-chat-chat-view])
|
||||||
contact-customization-color (when (= chat-type constants/one-to-one-chat-type)
|
contact-customization-color (when (= chat-type constants/one-to-one-chat-type)
|
||||||
(rf/sub [:contacts/contact-customization-color-by-address
|
(rf/sub [:contacts/contact-customization-color-by-address
|
||||||
chat-id]))]
|
chat-id]))
|
||||||
|
on-press (rn/use-callback #(send-message input-ref edit btn-opacity) [edit])]
|
||||||
(rn/use-effect (fn []
|
(rn/use-effect (fn []
|
||||||
;; Handle send button opacity animation and z-index when input content changes
|
;; Handle send button opacity animation and z-index when input content changes
|
||||||
(if (or (seq @text-value) images?)
|
(if (or (seq input-text) images?)
|
||||||
(when (or (not= @z-index 1) (not= (reanimated/get-shared-value btn-opacity) 1))
|
(when (not= (reanimated/get-shared-value btn-opacity) 1)
|
||||||
(reset! z-index 1)
|
|
||||||
(js/setTimeout #(reanimated/animate btn-opacity 1) 50))
|
(js/setTimeout #(reanimated/animate btn-opacity 1) 50))
|
||||||
(when (or (not= @z-index 0) (not= (reanimated/get-shared-value btn-opacity) 0))
|
(when (not= (reanimated/get-shared-value btn-opacity) 0)
|
||||||
(reanimated/animate btn-opacity 0)
|
(reanimated/animate btn-opacity 0))))
|
||||||
(js/setTimeout #(when (and (empty? @text-value) (not images?))
|
[(and (empty? input-text) (not images?))])
|
||||||
(reset! z-index 0))
|
|
||||||
300))))
|
|
||||||
[(and (empty? @text-value) (not images?))])
|
|
||||||
[reanimated/view
|
[reanimated/view
|
||||||
{:style (style/send-button btn-opacity @z-index)}
|
{:style (style/send-button btn-opacity)}
|
||||||
[quo/button
|
[quo/button
|
||||||
{:icon-only? true
|
{:icon-only? true
|
||||||
:size 32
|
:size 32
|
||||||
:customization-color (or contact-customization-color chat-color profile-customization-color)
|
:customization-color (or contact-customization-color chat-color profile-customization-color)
|
||||||
:accessibility-label :send-message-button
|
:accessibility-label :send-message-button
|
||||||
:on-press #(send-message props state animations window-height edit)}
|
:on-press on-press}
|
||||||
:i/arrow-up]]))
|
:i/arrow-up]]))
|
||||||
|
|
||||||
(defn send-button
|
|
||||||
[props {:keys [text-value] :as state} animations window-height images? edit btn-opacity]
|
|
||||||
(let [z-index (reagent/atom (if (and (empty? @text-value) (not images?)) 0 1))]
|
|
||||||
[:f> f-send-button props state animations window-height images? btn-opacity z-index edit]))
|
|
||||||
|
|
||||||
(defn disabled-audio-button
|
|
||||||
[opacity]
|
|
||||||
[reanimated/view {:style (reanimated/apply-animations-to-style {:opacity opacity} {})}
|
|
||||||
[quo/composer-button
|
|
||||||
{:on-press (fn []
|
|
||||||
(rf/dispatch [:chat.ui/set-input-focused false])
|
|
||||||
(rn/dismiss-keyboard!)
|
|
||||||
(js/alert "to be implemented"))
|
|
||||||
:icon :i/audio}]])
|
|
||||||
|
|
||||||
(defn audio-button
|
|
||||||
[{:keys [record-reset-fn input-ref]}
|
|
||||||
{:keys [record-permission? recording? gesture-enabled? focused?]}]
|
|
||||||
(let [audio (rf/sub [:chats/sending-audio])]
|
|
||||||
[rn/view
|
|
||||||
{:style (style/record-audio-container)
|
|
||||||
:pointer-events :box-none}
|
|
||||||
[quo/record-audio
|
|
||||||
{:record-audio-permission-granted @record-permission?
|
|
||||||
:on-init (fn [reset-fn]
|
|
||||||
(reset! record-reset-fn reset-fn))
|
|
||||||
:on-start-recording (fn []
|
|
||||||
(rf/dispatch [:chat.ui/set-recording true])
|
|
||||||
(reset! recording? true)
|
|
||||||
(reset! gesture-enabled? false))
|
|
||||||
:audio-file audio
|
|
||||||
:on-lock (fn []
|
|
||||||
(rf/dispatch [:chat.ui/set-recording false]))
|
|
||||||
:on-reviewing-audio (fn [file]
|
|
||||||
(rf/dispatch [:chat.ui/set-recording false])
|
|
||||||
(rf/dispatch [:chat.ui/set-input-audio file]))
|
|
||||||
:on-send (fn [{:keys [file-path duration]}]
|
|
||||||
(rf/dispatch [:chat.ui/set-recording false])
|
|
||||||
(reset! recording? false)
|
|
||||||
(reset! gesture-enabled? true)
|
|
||||||
(rf/dispatch [:chat/send-audio file-path duration])
|
|
||||||
(when @focused?
|
|
||||||
(js/setTimeout #(when @input-ref (.focus ^js @input-ref))
|
|
||||||
300))
|
|
||||||
(rf/dispatch [:chat.ui/set-input-audio nil]))
|
|
||||||
:on-cancel (fn []
|
|
||||||
(when @recording?
|
|
||||||
(rf/dispatch [:chat.ui/set-recording false])
|
|
||||||
(reset! recording? false)
|
|
||||||
(reset! gesture-enabled? true)
|
|
||||||
(when @focused?
|
|
||||||
(js/setTimeout #(when @input-ref
|
|
||||||
(.focus ^js @input-ref))
|
|
||||||
300))
|
|
||||||
(rf/dispatch [:chat.ui/set-input-audio nil])))
|
|
||||||
:on-check-audio-permissions (fn []
|
|
||||||
(permissions/permission-granted?
|
|
||||||
:record-audio
|
|
||||||
#(reset! record-permission? %)
|
|
||||||
#(reset! record-permission? false)))
|
|
||||||
:on-request-record-audio-permission (fn []
|
|
||||||
(rf/dispatch
|
|
||||||
[:request-permissions
|
|
||||||
{:permissions [:record-audio]
|
|
||||||
:on-allowed
|
|
||||||
#(reset! record-permission? true)
|
|
||||||
:on-denied
|
|
||||||
#(js/setTimeout
|
|
||||||
(fn []
|
|
||||||
(alert.effects/show-popup
|
|
||||||
(i18n/label :t/audio-recorder-error)
|
|
||||||
(i18n/label
|
|
||||||
:t/audio-recorder-permissions-error)
|
|
||||||
nil
|
|
||||||
{:text (i18n/label :t/settings)
|
|
||||||
:accessibility-label :settings-button
|
|
||||||
:onPress (fn [] (permissions/open-settings))}))
|
|
||||||
50)}]))
|
|
||||||
:max-duration-ms constants/audio-max-duration-ms}]]))
|
|
||||||
|
|
||||||
(defn photo-limit-toast
|
|
||||||
[]
|
|
||||||
(rf/dispatch [:toasts/upsert
|
|
||||||
{:id :random-id
|
|
||||||
:type :negative
|
|
||||||
:text (i18n/label :t/hit-photos-limit
|
|
||||||
{:max-photos constants/max-album-photos})}]))
|
|
||||||
|
|
||||||
|
|
||||||
(defn go-to-camera
|
|
||||||
[images-count]
|
|
||||||
(device-permissions/camera #(if (>= images-count constants/max-album-photos)
|
|
||||||
(photo-limit-toast)
|
|
||||||
(rf/dispatch [:navigate-to :camera-screen]))))
|
|
||||||
|
|
||||||
(defn camera-button
|
|
||||||
[edit]
|
|
||||||
(let [images-count (count (vals (rf/sub [:chats/sending-image])))]
|
|
||||||
[quo/composer-button
|
|
||||||
{:on-press (if edit
|
|
||||||
#(js/alert "This feature is temporarily unavailable in edit mode.")
|
|
||||||
#(go-to-camera images-count))
|
|
||||||
:accessibility-label :camera-button
|
|
||||||
:icon :i/camera
|
|
||||||
:container-style {:margin-right 12}}]))
|
|
||||||
|
|
||||||
|
|
||||||
(defn open-photo-selector
|
|
||||||
[{:keys [input-ref]}
|
|
||||||
{:keys [height]}]
|
|
||||||
(permissions/request-permissions
|
|
||||||
{:permissions [(if platform/is-below-android-13? :read-external-storage :read-media-images)
|
|
||||||
:write-external-storage]
|
|
||||||
:on-allowed (fn []
|
|
||||||
(when (and platform/android? @input-ref)
|
|
||||||
(.blur ^js @input-ref))
|
|
||||||
(when platform/ios?
|
|
||||||
(rf/dispatch [:alert-banners/hide]))
|
|
||||||
(rf/dispatch [:chat.ui/set-input-content-height
|
|
||||||
(reanimated/get-shared-value height)])
|
|
||||||
(rf/dispatch [:photo-selector/navigate-to-photo-selector]))
|
|
||||||
:on-denied (fn []
|
|
||||||
(alert.effects/show-popup (i18n/label :t/error)
|
|
||||||
(i18n/label
|
|
||||||
:t/external-storage-denied)))}))
|
|
||||||
|
|
||||||
(defn image-button
|
|
||||||
[props animations edit]
|
|
||||||
[quo/composer-button
|
|
||||||
{:on-press (if edit
|
|
||||||
#(js/alert "This feature is temporarily unavailable in edit mode.")
|
|
||||||
#(open-photo-selector props animations))
|
|
||||||
:accessibility-label :open-images-button
|
|
||||||
:container-style {:margin-right 12}
|
|
||||||
:icon :i/image}])
|
|
||||||
|
|
||||||
(defn reaction-button
|
|
||||||
[]
|
|
||||||
[quo/composer-button
|
|
||||||
{:icon :i/reaction
|
|
||||||
:on-press (fn []
|
|
||||||
(rf/dispatch [:chat.ui/set-input-focused false])
|
|
||||||
(rn/dismiss-keyboard!)
|
|
||||||
(js/alert "to be implemented"))
|
|
||||||
:container-style {:margin-right 12}}])
|
|
||||||
|
|
||||||
(defn format-button
|
|
||||||
[]
|
|
||||||
[quo/composer-button
|
|
||||||
{:on-press (fn []
|
|
||||||
(rf/dispatch [:chat.ui/set-input-focused false])
|
|
||||||
(rn/dismiss-keyboard!)
|
|
||||||
(js/alert "to be implemented"))
|
|
||||||
:icon :i/format}])
|
|
||||||
|
|
||||||
(defn view
|
(defn view
|
||||||
[props state animations window-height {:keys [edit images]}]
|
[input-ref]
|
||||||
(let [send-btn-opacity (reanimated/use-shared-value 0)
|
(let [edit (rf/sub [:chats/edit-message])]
|
||||||
audio-btn-opacity (reanimated/interpolate send-btn-opacity [0 1] [1 0])]
|
|
||||||
[rn/view {:style style/actions-container}
|
[rn/view {:style style/actions-container}
|
||||||
[rn/view
|
[rn/view {:style {:flex-direction :row}}
|
||||||
{:style {:flex-direction :row
|
(when-not edit
|
||||||
:display (if @(:recording? state) :none :flex)}}
|
[:<>
|
||||||
[camera-button edit]
|
[actions.image/camera-button]
|
||||||
[image-button props animations edit]
|
[actions.image/image-button input-ref edit]])]
|
||||||
(when config/show-not-implemented-features?
|
[send-button input-ref edit]]))
|
||||||
[reaction-button])
|
|
||||||
(when config/show-not-implemented-features?
|
|
||||||
[format-button])]
|
|
||||||
[:f> send-button props state animations window-height images edit send-btn-opacity]
|
|
||||||
(when (and (not edit) (not images) config/show-not-implemented-features?)
|
|
||||||
;; TODO(alwx): needs to be replaced with an `audio-button` later. See
|
|
||||||
;; https://github.com/status-im/status-mobile/issues/16084 for more details.
|
|
||||||
[:f> disabled-audio-button audio-btn-opacity])]))
|
|
||||||
|
@ -13,10 +13,6 @@
|
|||||||
|
|
||||||
(def ^:const line-height (if platform/ios? 18 (:line-height typography/paragraph-1)))
|
(def ^:const line-height (if platform/ios? 18 (:line-height typography/paragraph-1)))
|
||||||
|
|
||||||
(def ^:const multiline-minimized-height (+ input-height line-height))
|
|
||||||
|
|
||||||
(def ^:const empty-opacity 0.7)
|
|
||||||
|
|
||||||
(def ^:const images-padding-top 12)
|
(def ^:const images-padding-top 12)
|
||||||
(def ^:const images-padding-bottom 8)
|
(def ^:const images-padding-bottom 8)
|
||||||
(def ^:const images-container-height
|
(def ^:const images-container-height
|
||||||
@ -33,16 +29,6 @@
|
|||||||
|
|
||||||
(def ^:const mentions-max-height 240)
|
(def ^:const mentions-max-height 240)
|
||||||
|
|
||||||
(def ^:const extra-content-offset (if platform/ios? 6 -8))
|
|
||||||
|
|
||||||
(def ^:const content-change-threshold 10)
|
|
||||||
|
|
||||||
(def ^:const drag-threshold 30)
|
|
||||||
|
|
||||||
(def ^:const velocity-threshold (if platform/ios? -1000 -500))
|
|
||||||
|
|
||||||
(def ^:const background-threshold 0.75)
|
|
||||||
|
|
||||||
(def ^:const max-text-size 4096)
|
(def ^:const max-text-size 4096)
|
||||||
|
|
||||||
(def ^:const unfurl-debounce-ms
|
(def ^:const unfurl-debounce-ms
|
||||||
|
@ -7,12 +7,13 @@
|
|||||||
[react-native.reanimated :as reanimated]
|
[react-native.reanimated :as reanimated]
|
||||||
[status-im.contexts.chat.messenger.composer.constants :as constants]
|
[status-im.contexts.chat.messenger.composer.constants :as constants]
|
||||||
[status-im.contexts.chat.messenger.composer.edit.style :as style]
|
[status-im.contexts.chat.messenger.composer.edit.style :as style]
|
||||||
|
[status-im.contexts.chat.messenger.composer.effects :as effects]
|
||||||
[status-im.contexts.chat.messenger.composer.utils :as utils]
|
[status-im.contexts.chat.messenger.composer.utils :as utils]
|
||||||
[utils.i18n :as i18n]
|
[utils.i18n :as i18n]
|
||||||
[utils.re-frame :as rf]))
|
[utils.re-frame :as rf]))
|
||||||
|
|
||||||
(defn edit-message
|
(defn edit-message
|
||||||
[{:keys [text-value input-ref input-height]}]
|
[input-ref]
|
||||||
(let [theme (quo.theme/use-theme)]
|
(let [theme (quo.theme/use-theme)]
|
||||||
[rn/view
|
[rn/view
|
||||||
{:style style/container
|
{:style style/container
|
||||||
@ -32,18 +33,16 @@
|
|||||||
{:size 24
|
{:size 24
|
||||||
:icon-only? true
|
:icon-only? true
|
||||||
:accessibility-label :edit-cancel-button
|
:accessibility-label :edit-cancel-button
|
||||||
:on-press #(utils/cancel-edit-message text-value input-ref input-height)
|
:on-press #(utils/cancel-edit-message input-ref)
|
||||||
:type :outline}
|
:type :outline}
|
||||||
:i/close]]))
|
:i/close]]))
|
||||||
|
|
||||||
(defn- f-view
|
(defn view
|
||||||
[props]
|
[input-ref]
|
||||||
(let [edit (rf/sub [:chats/edit-message])
|
(let [edit (rf/sub [:chats/edit-message])
|
||||||
height (reanimated/use-shared-value (if edit constants/edit-container-height 0))]
|
height (reanimated/use-shared-value (if edit constants/edit-container-height 0))]
|
||||||
|
(effects/use-edit input-ref edit)
|
||||||
(rn/use-effect #(reanimated/animate height (if edit constants/edit-container-height 0)) [edit])
|
(rn/use-effect #(reanimated/animate height (if edit constants/edit-container-height 0)) [edit])
|
||||||
[reanimated/view {:style (reanimated/apply-animations-to-style {:height height} {})}
|
[reanimated/view {:style (reanimated/apply-animations-to-style {:height height} {})}
|
||||||
(when edit [edit-message props])]))
|
(when edit
|
||||||
|
[edit-message input-ref])]))
|
||||||
(defn view
|
|
||||||
[props]
|
|
||||||
[:f> f-view props])
|
|
||||||
|
@ -1,225 +1,20 @@
|
|||||||
(ns status-im.contexts.chat.messenger.composer.effects
|
(ns status-im.contexts.chat.messenger.composer.effects
|
||||||
(:require
|
(:require
|
||||||
[clojure.string :as string]
|
|
||||||
[oops.core :as oops]
|
|
||||||
[react-native.async-storage :as async-storage]
|
|
||||||
[react-native.core :as rn]
|
[react-native.core :as rn]
|
||||||
[react-native.platform :as platform]
|
[utils.number]))
|
||||||
[react-native.reanimated :as reanimated]
|
|
||||||
[reagent.core :as reagent]
|
|
||||||
[status-im.contexts.chat.messenger.composer.constants :as constants]
|
|
||||||
[status-im.contexts.chat.messenger.composer.keyboard :as kb]
|
|
||||||
[status-im.contexts.chat.messenger.composer.utils :as utils]
|
|
||||||
[utils.number]
|
|
||||||
[utils.re-frame :as rf]))
|
|
||||||
|
|
||||||
(defn reenter-screen-effect
|
|
||||||
[{:keys [text-value saved-cursor-position maximized?]}
|
|
||||||
{:keys [content-height]}
|
|
||||||
{:keys [input-content-height input-text input-maximized?]}
|
|
||||||
{:keys [height]}]
|
|
||||||
(let [lines (utils/calc-lines input-content-height)
|
|
||||||
minimized-height (if (or (= lines 1) (empty? input-text))
|
|
||||||
constants/input-height
|
|
||||||
constants/multiline-minimized-height)]
|
|
||||||
(when (and (empty? @text-value) (not= input-text nil))
|
|
||||||
(reset! text-value input-text)
|
|
||||||
(reset! content-height input-content-height)
|
|
||||||
(reset! saved-cursor-position (count input-text))
|
|
||||||
(reanimated/set-shared-value height minimized-height))
|
|
||||||
(when input-maximized?
|
|
||||||
(reset! maximized? true))))
|
|
||||||
|
|
||||||
(defn maximized-effect
|
|
||||||
[{:keys [maximized?]}
|
|
||||||
{:keys [height saved-height last-height]}
|
|
||||||
{:keys [max-height]}
|
|
||||||
{:keys [input-content-height]}]
|
|
||||||
(when (or @maximized? (>= input-content-height max-height))
|
|
||||||
(reanimated/animate height max-height)
|
|
||||||
(reanimated/set-shared-value saved-height max-height)
|
|
||||||
(reanimated/set-shared-value last-height max-height)))
|
|
||||||
|
|
||||||
(defn layout-effect
|
|
||||||
[{:keys [lock-layout?]}]
|
|
||||||
(when-not @lock-layout?
|
|
||||||
(js/setTimeout #(reset! lock-layout? true) 500)))
|
|
||||||
|
|
||||||
(defn kb-default-height-effect
|
|
||||||
[{:keys [kb-default-height kb-height]}]
|
|
||||||
(when (zero? @kb-default-height)
|
|
||||||
(async-storage/get-item :kb-default-height
|
|
||||||
(fn [height]
|
|
||||||
(reset! kb-default-height (utils.number/parse-int height 0))
|
|
||||||
(reset! kb-height (utils.number/parse-int height 0))))))
|
|
||||||
|
|
||||||
(defn background-effect
|
|
||||||
[{:keys [maximized?]}
|
|
||||||
{:keys [opacity background-y]}
|
|
||||||
{:keys [max-height]}
|
|
||||||
{:keys [input-content-height]}]
|
|
||||||
(when (or @maximized? (>= input-content-height (* max-height constants/background-threshold)))
|
|
||||||
(reanimated/set-shared-value background-y 0)
|
|
||||||
(reanimated/animate opacity 1)))
|
|
||||||
|
|
||||||
(defn link-preview-effect
|
|
||||||
[{:keys [text-value]}]
|
|
||||||
(let [text @text-value]
|
|
||||||
(when-not (string/blank? text)
|
|
||||||
(rf/dispatch [:link-preview/unfurl-urls text]))))
|
|
||||||
|
|
||||||
(defn audio-effect
|
|
||||||
[{:keys [recording? gesture-enabled?]}
|
|
||||||
audio]
|
|
||||||
(when (and audio (not @recording?))
|
|
||||||
(reset! recording? true)
|
|
||||||
(reset! gesture-enabled? false)))
|
|
||||||
|
|
||||||
(defn empty-effect
|
|
||||||
[{:keys [empty-input?]} subscriptions]
|
|
||||||
(reanimated/set-shared-value
|
|
||||||
empty-input?
|
|
||||||
(utils/empty-input? subscriptions)))
|
|
||||||
|
|
||||||
(defn component-will-unmount
|
|
||||||
[{:keys [keyboard-show-listener keyboard-hide-listener keyboard-frame-listener]}]
|
|
||||||
(.remove ^js @keyboard-show-listener)
|
|
||||||
(.remove ^js @keyboard-hide-listener)
|
|
||||||
(.remove ^js @keyboard-frame-listener))
|
|
||||||
|
|
||||||
(defn initialize
|
|
||||||
[props state animations {:keys [max-height] :as dimensions}
|
|
||||||
{:keys [chat-input audio input-text images link-previews? reply edit] :as subscriptions}]
|
|
||||||
(rn/use-effect
|
|
||||||
(fn []
|
|
||||||
(maximized-effect state animations dimensions chat-input)
|
|
||||||
(layout-effect state)
|
|
||||||
(kb-default-height-effect state)
|
|
||||||
(background-effect state animations dimensions chat-input)
|
|
||||||
(link-preview-effect state)
|
|
||||||
(audio-effect state audio)
|
|
||||||
(kb/add-kb-listeners props state animations dimensions)
|
|
||||||
#(component-will-unmount props))
|
|
||||||
[max-height])
|
|
||||||
(rn/use-effect
|
|
||||||
(fn []
|
|
||||||
(empty-effect animations subscriptions))
|
|
||||||
[input-text images link-previews? reply edit audio])
|
|
||||||
(rn/use-mount #(reenter-screen-effect state dimensions subscriptions animations)))
|
|
||||||
|
|
||||||
(defn use-edit
|
(defn use-edit
|
||||||
[{:keys [input-ref]}
|
[input-ref edit]
|
||||||
{:keys [text-value saved-cursor-position cursor-position]}
|
(rn/use-effect
|
||||||
{:keys [edit input-with-mentions]}
|
(fn []
|
||||||
chat-screen-layout-calculations-complete?]
|
(when (and edit @input-ref)
|
||||||
(let [mention? (some #(= :mention (first %)) (seq input-with-mentions))]
|
(js/setTimeout #(.focus ^js @input-ref) 600)))
|
||||||
(rn/use-effect
|
[(:message-id edit)]))
|
||||||
(fn []
|
|
||||||
(let [mention-text (reduce (fn [acc item]
|
|
||||||
(str acc (second item)))
|
|
||||||
""
|
|
||||||
input-with-mentions)
|
|
||||||
edit-text (cond
|
|
||||||
mention? mention-text
|
|
||||||
;; NOTE: using text-value for cases when the user
|
|
||||||
;; leaves the app with an unfinished edit and re-opens
|
|
||||||
;; the chat.
|
|
||||||
(and (seq @text-value)
|
|
||||||
(not (reanimated/get-shared-value
|
|
||||||
chat-screen-layout-calculations-complete?)))
|
|
||||||
@text-value
|
|
||||||
:else (get-in edit [:content :text]))
|
|
||||||
selection-pos (count edit-text)
|
|
||||||
inject-edit-text (fn []
|
|
||||||
(reset! text-value edit-text)
|
|
||||||
(reset! cursor-position selection-pos)
|
|
||||||
(reset! saved-cursor-position selection-pos)
|
|
||||||
(when @input-ref
|
|
||||||
(.setNativeProps ^js @input-ref
|
|
||||||
(clj->js {:text edit-text}))))]
|
|
||||||
|
|
||||||
(when (and edit @input-ref)
|
|
||||||
;; NOTE: A small setTimeout is necessary to ensure the focus is enqueued and is executed
|
|
||||||
;; ASAP. Check https://github.com/software-mansion/react-native-screens/issues/472
|
|
||||||
;;
|
|
||||||
;; The nested setTimeout is necessary to avoid both `on-focus` and
|
|
||||||
;; `on-content-size-change` handlers triggering the height animation simultaneously, as
|
|
||||||
;; this causes a jump in the
|
|
||||||
;; UI. This way, `on-focus` will trigger first without changing the height, after which
|
|
||||||
;; `on-content-size-change` will animate the height of the input based on the injected
|
|
||||||
;; text.
|
|
||||||
(js/setTimeout #(do (when (reanimated/get-shared-value
|
|
||||||
chat-screen-layout-calculations-complete?)
|
|
||||||
(.focus ^js @input-ref))
|
|
||||||
(reagent/next-tick inject-edit-text))
|
|
||||||
600))))
|
|
||||||
[(:message-id edit)])))
|
|
||||||
|
|
||||||
(defn use-reply
|
(defn use-reply
|
||||||
[{:keys [input-ref]}
|
[input-ref reply]
|
||||||
{:keys [reply]}
|
|
||||||
chat-screen-layout-calculations-complete?]
|
|
||||||
(rn/use-effect
|
(rn/use-effect
|
||||||
(fn []
|
(fn []
|
||||||
(when (and reply @input-ref (reanimated/get-shared-value chat-screen-layout-calculations-complete?))
|
(when (and reply @input-ref)
|
||||||
(js/setTimeout #(.focus ^js @input-ref) 600)))
|
(js/setTimeout #(.focus ^js @input-ref) 600)))
|
||||||
[(:message-id reply)]))
|
[(:message-id reply)]))
|
||||||
|
|
||||||
(defn update-input-mention
|
|
||||||
[{:keys [input-ref]}
|
|
||||||
{:keys [text-value]}
|
|
||||||
{:keys [input-text]}]
|
|
||||||
(rn/use-effect
|
|
||||||
(fn []
|
|
||||||
(when (and input-text (not= @text-value input-text))
|
|
||||||
(when @input-ref
|
|
||||||
(.setNativeProps ^js @input-ref (clj->js {:text input-text})))
|
|
||||||
(reset! text-value input-text)
|
|
||||||
(rf/dispatch [:mention/on-change-text input-text])))
|
|
||||||
[input-text]))
|
|
||||||
|
|
||||||
(defn link-previews
|
|
||||||
[{:keys [sending-links?]}
|
|
||||||
{:keys [text-value maximized?]}
|
|
||||||
{:keys [height saved-height]}
|
|
||||||
{:keys [link-previews?]}]
|
|
||||||
(rn/use-effect
|
|
||||||
(fn []
|
|
||||||
(if-not @maximized?
|
|
||||||
(when (not= @sending-links? link-previews?)
|
|
||||||
(reanimated/animate height (reanimated/get-shared-value saved-height)))
|
|
||||||
(let [curr-text @text-value]
|
|
||||||
(reset! text-value (str @text-value " "))
|
|
||||||
(js/setTimeout #(reset! text-value curr-text) 100)))
|
|
||||||
(reset! sending-links? link-previews?))
|
|
||||||
[link-previews?]))
|
|
||||||
|
|
||||||
(defn use-images
|
|
||||||
[{:keys [sending-images? input-ref]}
|
|
||||||
{:keys [text-value maximized?]}
|
|
||||||
{:keys [height saved-height]}
|
|
||||||
{:keys [images]}]
|
|
||||||
(rn/use-effect
|
|
||||||
(fn []
|
|
||||||
(when (and (not @sending-images?) (seq images) @input-ref)
|
|
||||||
(.focus ^js @input-ref))
|
|
||||||
(if-not @maximized?
|
|
||||||
(when (not= @sending-images? (boolean (seq images)))
|
|
||||||
(reanimated/animate height (reanimated/get-shared-value saved-height)))
|
|
||||||
(let [curr-text @text-value]
|
|
||||||
(reset! text-value (str @text-value " "))
|
|
||||||
(js/setTimeout #(reset! text-value curr-text) 100)))
|
|
||||||
(reset! sending-images? (boolean (seq images))))
|
|
||||||
[(boolean (seq images))]))
|
|
||||||
|
|
||||||
(defn did-mount
|
|
||||||
[{:keys [selectable-input-ref input-ref selection-manager]}]
|
|
||||||
(rn/use-mount
|
|
||||||
(fn []
|
|
||||||
(when platform/android?
|
|
||||||
(let [selectable-text-input-handle (rn/find-node-handle @selectable-input-ref)
|
|
||||||
text-input-handle (rn/find-node-handle @input-ref)]
|
|
||||||
(oops/ocall selection-manager
|
|
||||||
:setupMenuItems
|
|
||||||
selectable-text-input-handle
|
|
||||||
text-input-handle))))))
|
|
||||||
|
@ -21,6 +21,12 @@
|
|||||||
(when (empty? new-input)
|
(when (empty? new-input)
|
||||||
(mentions/clear-mentions)))))
|
(mentions/clear-mentions)))))
|
||||||
|
|
||||||
|
(rf/defn set-chat-input-ref
|
||||||
|
{:events [:chat/set-input-ref]}
|
||||||
|
[{:keys [db]} input-ref]
|
||||||
|
(let [current-chat-id (:current-chat-id db)]
|
||||||
|
{:db (assoc-in db [:chat/inputs current-chat-id :input-ref] input-ref)}))
|
||||||
|
|
||||||
(rf/defn set-input-content-height
|
(rf/defn set-input-content-height
|
||||||
{:events [:chat.ui/set-input-content-height]}
|
{:events [:chat.ui/set-input-content-height]}
|
||||||
[{db :db} content-height chat-id]
|
[{db :db} content-height chat-id]
|
||||||
@ -69,15 +75,14 @@
|
|||||||
[{:keys [db]} message]
|
[{:keys [db]} 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])]
|
||||||
{: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))
|
||||||
:dispatch-n [[:chat.ui/set-chat-input-text nil current-chat-id]
|
:dispatch [:mention/to-input-field text current-chat-id]}))
|
||||||
[:mention/to-input-field text current-chat-id]]}))
|
|
||||||
|
|
||||||
(rf/defn cancel-message-reply
|
(rf/defn cancel-message-reply
|
||||||
"Cancels stage message reply"
|
"Cancels stage message reply"
|
||||||
@ -260,3 +265,8 @@
|
|||||||
(if editing-message
|
(if editing-message
|
||||||
(send-edited-message cofx input-text editing-message)
|
(send-edited-message cofx input-text editing-message)
|
||||||
(send-messages cofx input-text current-chat-id))))
|
(send-messages cofx input-text current-chat-id))))
|
||||||
|
|
||||||
|
(rf/reg-fx :effects/set-input-text-value
|
||||||
|
(fn [[input-ref text-value]]
|
||||||
|
(when (and (not (string/blank? text-value)) input-ref)
|
||||||
|
(.setNativeProps ^js input-ref (clj->js {:text (emoji/text->emoji text-value)})))))
|
||||||
|
@ -1,112 +0,0 @@
|
|||||||
(ns status-im.contexts.chat.messenger.composer.gesture
|
|
||||||
(:require
|
|
||||||
[oops.core :as oops]
|
|
||||||
[react-native.gesture :as gesture]
|
|
||||||
[react-native.reanimated :as reanimated]
|
|
||||||
[status-im.contexts.chat.messenger.composer.constants :as constants]
|
|
||||||
[status-im.contexts.chat.messenger.composer.utils :as utils]
|
|
||||||
[utils.number]
|
|
||||||
[utils.re-frame :as rf]))
|
|
||||||
|
|
||||||
(defn set-opacity
|
|
||||||
[velocity opacity translation expanding? min-height max-height new-height saved-height]
|
|
||||||
(let [remaining-height (if expanding?
|
|
||||||
(- max-height (reanimated/get-shared-value saved-height))
|
|
||||||
(- (reanimated/get-shared-value saved-height) min-height))
|
|
||||||
progress (if (= new-height min-height) 1 (/ translation remaining-height))
|
|
||||||
currently-expanding? (neg? velocity)
|
|
||||||
max-opacity? (and currently-expanding? (= (reanimated/get-shared-value opacity) 1))
|
|
||||||
min-opacity? (and (not currently-expanding?)
|
|
||||||
(= (reanimated/get-shared-value opacity) 0))]
|
|
||||||
(if (>= translation 0)
|
|
||||||
(when (and (not expanding?) (not min-opacity?))
|
|
||||||
(reanimated/set-shared-value opacity (- 1 progress)))
|
|
||||||
(when (and expanding? (not max-opacity?))
|
|
||||||
(reanimated/set-shared-value opacity (Math/abs progress))))))
|
|
||||||
|
|
||||||
(defn maximize
|
|
||||||
[{:keys [maximized?]}
|
|
||||||
{:keys [height saved-height background-y opacity]}
|
|
||||||
{:keys [max-height]}]
|
|
||||||
(reanimated/animate height max-height)
|
|
||||||
(reanimated/set-shared-value saved-height max-height)
|
|
||||||
(reanimated/set-shared-value background-y 0)
|
|
||||||
(reanimated/animate opacity 1)
|
|
||||||
(js/setTimeout (fn []
|
|
||||||
(reset! maximized? true)
|
|
||||||
(rf/dispatch [:chat.ui/set-input-maximized true]))
|
|
||||||
300))
|
|
||||||
|
|
||||||
(defn minimize
|
|
||||||
[{:keys [input-ref emoji-kb-extra-height saved-emoji-kb-extra-height]}
|
|
||||||
{:keys [maximized?]}]
|
|
||||||
(when @emoji-kb-extra-height
|
|
||||||
(reset! saved-emoji-kb-extra-height @emoji-kb-extra-height)
|
|
||||||
(reset! emoji-kb-extra-height nil))
|
|
||||||
(reset! maximized? false)
|
|
||||||
(rf/dispatch [:chat.ui/set-input-maximized false])
|
|
||||||
(utils/blur-input input-ref))
|
|
||||||
|
|
||||||
(defn bounce-back
|
|
||||||
[{:keys [height saved-height opacity background-y]}
|
|
||||||
{:keys [window-height]}
|
|
||||||
starting-opacity]
|
|
||||||
(reanimated/animate height (reanimated/get-shared-value saved-height))
|
|
||||||
(when (zero? starting-opacity)
|
|
||||||
(reanimated/animate opacity 0)
|
|
||||||
(reanimated/animate-delay background-y (- window-height) 300)))
|
|
||||||
|
|
||||||
(defn drag-gesture
|
|
||||||
[{:keys [input-ref] :as props}
|
|
||||||
{:keys [gesture-enabled?] :as state}
|
|
||||||
{:keys [height saved-height last-height opacity background-y] :as animations}
|
|
||||||
{:keys [max-height lines] :as dimensions}
|
|
||||||
keyboard-shown]
|
|
||||||
(let [expanding? (atom true)
|
|
||||||
starting-opacity (reanimated/get-shared-value opacity)]
|
|
||||||
(-> (gesture/gesture-pan)
|
|
||||||
(gesture/enabled @gesture-enabled?)
|
|
||||||
(gesture/on-start (fn [event]
|
|
||||||
(if-not keyboard-shown
|
|
||||||
(do ; focus and end
|
|
||||||
(when (< (oops/oget event "velocityY") constants/velocity-threshold)
|
|
||||||
(reanimated/set-shared-value last-height max-height)
|
|
||||||
(maximize state animations dimensions))
|
|
||||||
(when @input-ref
|
|
||||||
(.focus ^js @input-ref))
|
|
||||||
(reset! gesture-enabled? false))
|
|
||||||
(do ; else, will start gesture
|
|
||||||
(reanimated/set-shared-value background-y 0)
|
|
||||||
(reset! expanding? (neg? (oops/oget event "velocityY")))))))
|
|
||||||
(gesture/on-update
|
|
||||||
(fn [event]
|
|
||||||
(let [translation (oops/oget event "translationY")
|
|
||||||
min-height (utils/get-min-height lines)
|
|
||||||
new-height (- (reanimated/get-shared-value saved-height) translation)
|
|
||||||
bounded-height (utils.number/value-in-range new-height min-height max-height)]
|
|
||||||
(when keyboard-shown
|
|
||||||
(if (>= new-height min-height)
|
|
||||||
(do ; expand sheet
|
|
||||||
(reanimated/set-shared-value height bounded-height)
|
|
||||||
(set-opacity (oops/oget event "velocityY")
|
|
||||||
opacity
|
|
||||||
translation
|
|
||||||
@expanding?
|
|
||||||
min-height
|
|
||||||
max-height
|
|
||||||
bounded-height
|
|
||||||
saved-height))
|
|
||||||
; sheet at min-height, collapse keyboard
|
|
||||||
(utils/blur-input input-ref))))))
|
|
||||||
(gesture/on-end (fn []
|
|
||||||
(let [diff (- (reanimated/get-shared-value height)
|
|
||||||
(reanimated/get-shared-value saved-height))]
|
|
||||||
(if @gesture-enabled?
|
|
||||||
(if (and @expanding? (>= diff 0))
|
|
||||||
(if (> diff constants/drag-threshold)
|
|
||||||
(maximize state animations dimensions)
|
|
||||||
(bounce-back animations dimensions starting-opacity))
|
|
||||||
(if (> (Math/abs diff) constants/drag-threshold)
|
|
||||||
(minimize props state)
|
|
||||||
(bounce-back animations dimensions starting-opacity)))
|
|
||||||
(reset! gesture-enabled? true))))))))
|
|
@ -3,139 +3,17 @@
|
|||||||
[clojure.string :as string]
|
[clojure.string :as string]
|
||||||
[oops.core :as oops]
|
[oops.core :as oops]
|
||||||
[react-native.core :as rn]
|
[react-native.core :as rn]
|
||||||
[react-native.reanimated :as reanimated]
|
|
||||||
[reagent.core :as reagent]
|
|
||||||
[status-im.contexts.chat.messenger.composer.constants :as constants]
|
[status-im.contexts.chat.messenger.composer.constants :as constants]
|
||||||
[status-im.contexts.chat.messenger.composer.keyboard :as kb]
|
|
||||||
[status-im.contexts.chat.messenger.composer.selection :as selection]
|
[status-im.contexts.chat.messenger.composer.selection :as selection]
|
||||||
[status-im.contexts.chat.messenger.composer.utils :as utils]
|
|
||||||
[utils.debounce :as debounce]
|
[utils.debounce :as debounce]
|
||||||
[utils.number]
|
[utils.number]
|
||||||
[utils.re-frame :as rf]))
|
[utils.re-frame :as rf]))
|
||||||
|
|
||||||
(defn focus
|
|
||||||
"Animate to the `saved-height`, display background-overlay if needed, and set cursor position"
|
|
||||||
[{:keys [input-ref] :as props}
|
|
||||||
{:keys [text-value focused? lock-selection? saved-cursor-position maximized?]}
|
|
||||||
{:keys [height saved-height last-height opacity background-y composer-focused?]
|
|
||||||
:as animations}
|
|
||||||
{:keys [max-height] :as dimensions}]
|
|
||||||
(reanimated/set-shared-value composer-focused? true)
|
|
||||||
(reset! focused? true)
|
|
||||||
(rf/dispatch [:chat.ui/set-input-focused true])
|
|
||||||
(let [last-height-value (reanimated/get-shared-value last-height)
|
|
||||||
new-height (min max-height last-height-value)]
|
|
||||||
(reanimated/animate height new-height)
|
|
||||||
(reanimated/set-shared-value saved-height new-height)
|
|
||||||
(when (> last-height-value (* constants/background-threshold max-height))
|
|
||||||
(reset! maximized? true)
|
|
||||||
(reanimated/animate opacity 1)
|
|
||||||
(reanimated/set-shared-value background-y 0)))
|
|
||||||
|
|
||||||
(js/setTimeout #(reset! lock-selection? false) 300)
|
|
||||||
(when (and (not-empty @text-value) @input-ref)
|
|
||||||
(.setNativeProps ^js @input-ref
|
|
||||||
(clj->js {:selection {:start @saved-cursor-position :end @saved-cursor-position}})))
|
|
||||||
(kb/handle-refocus-emoji-kb-ios props animations dimensions))
|
|
||||||
|
|
||||||
(defn blur
|
|
||||||
"Save the current height, minimize the composer, animate-out the background, and save cursor position"
|
|
||||||
[{:keys [text-value focused? lock-selection? cursor-position saved-cursor-position gradient-z-index
|
|
||||||
maximized? recording?]}
|
|
||||||
{:keys [height saved-height last-height gradient-opacity opacity background-y composer-focused?]}
|
|
||||||
{:keys [content-height max-height window-height]}]
|
|
||||||
(when-not @recording?
|
|
||||||
(let [lines (utils/calc-lines (- @content-height constants/extra-content-offset))
|
|
||||||
min-height (utils/get-min-height lines)
|
|
||||||
reopen-height (utils/calc-reopen-height text-value
|
|
||||||
min-height
|
|
||||||
max-height
|
|
||||||
content-height
|
|
||||||
saved-height)]
|
|
||||||
(reanimated/set-shared-value composer-focused? false)
|
|
||||||
(reset! focused? false)
|
|
||||||
(rf/dispatch [:chat.ui/set-input-focused false])
|
|
||||||
(reanimated/set-shared-value last-height reopen-height)
|
|
||||||
(reanimated/animate height min-height)
|
|
||||||
(reanimated/set-shared-value saved-height min-height)
|
|
||||||
(reanimated/animate opacity 0)
|
|
||||||
(js/setTimeout #(reanimated/set-shared-value background-y (- window-height)) 300)
|
|
||||||
(reanimated/animate gradient-opacity 0)
|
|
||||||
(reset! lock-selection? true)
|
|
||||||
(reset! saved-cursor-position @cursor-position)
|
|
||||||
(reset! gradient-z-index (if (= (reanimated/get-shared-value gradient-opacity) 1) -1 0))
|
|
||||||
(when (not= reopen-height max-height)
|
|
||||||
(reset! maximized? false)
|
|
||||||
(rf/dispatch [:chat.ui/set-input-maximized false])))))
|
|
||||||
|
|
||||||
(defn content-size-change
|
|
||||||
"Save new text height, expand composer if possible, show background overlay if needed"
|
|
||||||
[event
|
|
||||||
{:keys [maximized? lock-layout? text-value]}
|
|
||||||
{:keys [height saved-height last-height opacity background-y]}
|
|
||||||
{:keys [content-height window-height max-height]}
|
|
||||||
keyboard-shown]
|
|
||||||
(when keyboard-shown
|
|
||||||
(let [event-size (oops/oget event "nativeEvent.contentSize.height")
|
|
||||||
content-size (+ event-size constants/extra-content-offset)
|
|
||||||
lines (utils/calc-lines event-size)
|
|
||||||
content-size (if (or (= lines 1) (empty? @text-value))
|
|
||||||
constants/input-height
|
|
||||||
(if (= lines 2) constants/multiline-minimized-height content-size))
|
|
||||||
new-height (utils.number/value-in-range content-size
|
|
||||||
constants/input-height
|
|
||||||
max-height)
|
|
||||||
new-height (min new-height max-height)]
|
|
||||||
(reset! content-height content-size)
|
|
||||||
(when (utils/update-height? content-size height max-height)
|
|
||||||
(reanimated/animate height new-height)
|
|
||||||
(reanimated/set-shared-value last-height new-height)
|
|
||||||
(reanimated/set-shared-value saved-height new-height))
|
|
||||||
(when (= new-height max-height)
|
|
||||||
(reset! maximized? true)
|
|
||||||
(rf/dispatch [:chat.ui/set-input-maximized true]))
|
|
||||||
(if (utils/show-background? max-height new-height maximized?)
|
|
||||||
(do
|
|
||||||
(reanimated/set-shared-value background-y 0)
|
|
||||||
(reanimated/animate opacity 1))
|
|
||||||
(when (= (reanimated/get-shared-value opacity) 1)
|
|
||||||
(reanimated/animate opacity 0)
|
|
||||||
(js/setTimeout #(reanimated/set-shared-value background-y (- window-height)) 300)))
|
|
||||||
(rf/dispatch [:chat.ui/set-input-content-height content-size])
|
|
||||||
(reset! lock-layout? (> lines 2)))))
|
|
||||||
|
|
||||||
(defn scroll
|
|
||||||
"Hide or show top gradient while scroll"
|
|
||||||
[event
|
|
||||||
{:keys [scroll-y]}
|
|
||||||
{:keys [gradient-z-index focused?]}
|
|
||||||
{:keys [gradient-opacity]}
|
|
||||||
{:keys [lines max-lines]}]
|
|
||||||
(let [y (oops/oget event "nativeEvent.contentOffset.y")]
|
|
||||||
(reset! scroll-y y)
|
|
||||||
(when (utils/show-top-gradient? y lines max-lines gradient-opacity focused?)
|
|
||||||
(reset! gradient-z-index 1)
|
|
||||||
(js/setTimeout #(reanimated/animate gradient-opacity 1) 0))
|
|
||||||
(when (utils/hide-top-gradient? y gradient-opacity)
|
|
||||||
(reanimated/animate gradient-opacity 0)
|
|
||||||
(js/setTimeout #(reset! gradient-z-index 0) 300))))
|
|
||||||
|
|
||||||
(defn change-text
|
(defn change-text
|
||||||
"Update `text-value`, update cursor selection, find links, find mentions"
|
"Update `text-value`, update cursor selection, find links, find mentions"
|
||||||
[text
|
[text]
|
||||||
{:keys [input-ref record-reset-fn]}
|
|
||||||
{:keys [text-value cursor-position recording?]}]
|
|
||||||
(reset! text-value text)
|
|
||||||
(reagent/next-tick #(when @input-ref
|
|
||||||
(.setNativeProps ^js @input-ref
|
|
||||||
(clj->js {:selection {:start @cursor-position
|
|
||||||
:end @cursor-position}}))))
|
|
||||||
(when @recording?
|
|
||||||
(@record-reset-fn)
|
|
||||||
(reset! recording? false))
|
|
||||||
(rf/dispatch [:chat.ui/set-chat-input-text text])
|
(rf/dispatch [:chat.ui/set-chat-input-text text])
|
||||||
(debounce/debounce-and-dispatch [:link-preview/unfurl-urls text]
|
(debounce/debounce-and-dispatch [:link-preview/unfurl-urls text] constants/unfurl-debounce-ms)
|
||||||
constants/unfurl-debounce-ms)
|
|
||||||
(if (string/ends-with? text "@")
|
(if (string/ends-with? text "@")
|
||||||
(rf/dispatch [:mention/on-change-text text])
|
(rf/dispatch [:mention/on-change-text text])
|
||||||
(debounce/debounce-and-dispatch [:mention/on-change-text text] 300)))
|
(debounce/debounce-and-dispatch [:mention/on-change-text text] 300)))
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
[rn/view {:style style/remove-photo-inner-container}
|
[rn/view {:style style/remove-photo-inner-container}
|
||||||
[quo/icon :i/clear {:size 20 :color colors/neutral-50 :color-2 colors/white}]]]])
|
[quo/icon :i/clear {:size 20 :color colors/neutral-50 :color-2 colors/white}]]]])
|
||||||
|
|
||||||
(defn f-images-list
|
(defn images-list
|
||||||
[]
|
[]
|
||||||
(let [theme (quo.theme/use-theme)
|
(let [theme (quo.theme/use-theme)
|
||||||
images (rf/sub [:chats/sending-image])
|
images (rf/sub [:chats/sending-image])
|
||||||
@ -48,7 +48,3 @@
|
|||||||
:horizontal true
|
:horizontal true
|
||||||
:shows-horizontal-scroll-indicator false
|
:shows-horizontal-scroll-indicator false
|
||||||
:keyboard-should-persist-taps :handled}]]))
|
:keyboard-should-persist-taps :handled}]]))
|
||||||
|
|
||||||
(defn images-list
|
|
||||||
[]
|
|
||||||
[:f> f-images-list])
|
|
||||||
|
@ -1,87 +0,0 @@
|
|||||||
(ns status-im.contexts.chat.messenger.composer.keyboard
|
|
||||||
(:require
|
|
||||||
[oops.core :as oops]
|
|
||||||
[react-native.async-storage :as async-storage]
|
|
||||||
[react-native.core :as rn]
|
|
||||||
[react-native.platform :as platform]
|
|
||||||
[react-native.reanimated :as reanimated]
|
|
||||||
[status-im.contexts.chat.messenger.composer.utils :as utils]))
|
|
||||||
|
|
||||||
(defn get-kb-height
|
|
||||||
[curr-height default-height]
|
|
||||||
(if (and default-height (< curr-height default-height))
|
|
||||||
default-height
|
|
||||||
curr-height))
|
|
||||||
|
|
||||||
(defn store-kb-height
|
|
||||||
[event {:keys [kb-default-height kb-height]}]
|
|
||||||
(let [height (- (:height (rn/get-window))
|
|
||||||
(oops/oget event "endCoordinates.screenY"))]
|
|
||||||
(reset! kb-height height)
|
|
||||||
(when (zero? @kb-default-height)
|
|
||||||
(async-storage/set-item! :kb-default-height (str height)))))
|
|
||||||
|
|
||||||
(defn handle-emoji-kb-ios
|
|
||||||
"Opening emoji KB on iOS will cause a flicker up and down due to height differences.
|
|
||||||
This method handles that by adding the extra difference between the keyboards. When the input is
|
|
||||||
expanded to a point where the added difference will make the composer go beyond the screen causing a flicker,
|
|
||||||
we're subtracting the difference so it only reaches the allowed max-height. We're not animating these
|
|
||||||
changes to make it appear seamless during transitions between keyboard types when maximized."
|
|
||||||
[event
|
|
||||||
{:keys [emoji-kb-extra-height]}
|
|
||||||
{:keys [text-value kb-height]}
|
|
||||||
{:keys [height saved-height]}
|
|
||||||
{:keys [max-height]}]
|
|
||||||
(let [start-h (oops/oget event "startCoordinates.height")
|
|
||||||
end-h (oops/oget event "endCoordinates.height")
|
|
||||||
diff (- end-h start-h)
|
|
||||||
max-height-diff (- max-height diff)
|
|
||||||
curr-text @text-value
|
|
||||||
bigger-than-default-kb? (> end-h @kb-height)
|
|
||||||
almost-expanded? (> (reanimated/get-shared-value height) max-height-diff)]
|
|
||||||
(if (and almost-expanded? bigger-than-default-kb? (pos? diff))
|
|
||||||
(do
|
|
||||||
(reanimated/set-shared-value height (- (reanimated/get-shared-value height) diff))
|
|
||||||
(reanimated/set-shared-value saved-height (- (reanimated/get-shared-value saved-height) diff))
|
|
||||||
(reset! text-value (str @text-value " "))
|
|
||||||
(js/setTimeout #(reset! text-value curr-text) 0)
|
|
||||||
(reset! emoji-kb-extra-height diff))
|
|
||||||
(when @emoji-kb-extra-height
|
|
||||||
(reanimated/set-shared-value height
|
|
||||||
(+ (reanimated/get-shared-value height) @emoji-kb-extra-height))
|
|
||||||
(reanimated/set-shared-value saved-height
|
|
||||||
(+ (reanimated/get-shared-value saved-height)
|
|
||||||
@emoji-kb-extra-height))
|
|
||||||
(reset! emoji-kb-extra-height nil)))))
|
|
||||||
|
|
||||||
(defn add-kb-listeners
|
|
||||||
[{:keys [keyboard-show-listener keyboard-frame-listener keyboard-hide-listener input-ref] :as props}
|
|
||||||
state animations dimensions]
|
|
||||||
(reset! keyboard-show-listener (.addListener
|
|
||||||
rn/keyboard
|
|
||||||
"keyboardDidShow"
|
|
||||||
#(store-kb-height % state)))
|
|
||||||
(reset! keyboard-frame-listener (.addListener
|
|
||||||
rn/keyboard
|
|
||||||
"keyboardWillChangeFrame"
|
|
||||||
#(handle-emoji-kb-ios % props state animations dimensions)))
|
|
||||||
(reset! keyboard-hide-listener (.addListener rn/keyboard
|
|
||||||
"keyboardDidHide"
|
|
||||||
#(when platform/android?
|
|
||||||
(utils/blur-input input-ref)))))
|
|
||||||
|
|
||||||
(defn handle-refocus-emoji-kb-ios
|
|
||||||
[{:keys [saved-emoji-kb-extra-height]}
|
|
||||||
{:keys [height saved-height last-height]}
|
|
||||||
{:keys [lines max-lines]}]
|
|
||||||
(when @saved-emoji-kb-extra-height
|
|
||||||
(js/setTimeout (fn []
|
|
||||||
(when (> lines max-lines)
|
|
||||||
(reanimated/animate height
|
|
||||||
(+ (reanimated/get-shared-value last-height)
|
|
||||||
@saved-emoji-kb-extra-height))
|
|
||||||
(reanimated/set-shared-value saved-height
|
|
||||||
(+ (reanimated/get-shared-value last-height)
|
|
||||||
@saved-emoji-kb-extra-height)))
|
|
||||||
(reset! saved-emoji-kb-extra-height nil))
|
|
||||||
600)))
|
|
@ -21,7 +21,7 @@
|
|||||||
[previews?])
|
[previews?])
|
||||||
height))
|
height))
|
||||||
|
|
||||||
(defn f-view
|
(defn view
|
||||||
[]
|
[]
|
||||||
(let [previews (rf/sub [:chats/link-previews-unfurled])
|
(let [previews (rf/sub [:chats/link-previews-unfurled])
|
||||||
height (use-animated-height (boolean (seq previews)))]
|
height (use-animated-height (boolean (seq previews)))]
|
||||||
@ -48,7 +48,3 @@
|
|||||||
:thumbnail (:data-uri thumbnail)
|
:thumbnail (:data-uri thumbnail)
|
||||||
:url url})
|
:url url})
|
||||||
previews)}]]))
|
previews)}]]))
|
||||||
|
|
||||||
(defn view
|
|
||||||
[]
|
|
||||||
[:f> f-view])
|
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
[react-native.reanimated :as reanimated]
|
[react-native.reanimated :as reanimated]
|
||||||
[status-im.contexts.chat.messenger.composer.constants :as constants]))
|
[status-im.contexts.chat.messenger.composer.constants :as constants]))
|
||||||
|
|
||||||
|
|
||||||
(defn shadow
|
(defn shadow
|
||||||
[theme]
|
[theme]
|
||||||
(if platform/ios?
|
(if platform/ios?
|
||||||
@ -16,12 +15,12 @@
|
|||||||
{:elevation 10}))
|
{:elevation 10}))
|
||||||
|
|
||||||
(defn container
|
(defn container
|
||||||
[opacity bottom theme]
|
[opacity top theme]
|
||||||
(reanimated/apply-animations-to-style
|
(reanimated/apply-animations-to-style
|
||||||
{:opacity opacity}
|
{:opacity opacity}
|
||||||
(merge
|
(merge
|
||||||
{:position :absolute
|
{:position :absolute
|
||||||
:bottom bottom
|
:top (- (+ 8 top))
|
||||||
:left 8
|
:left 8
|
||||||
:right 8
|
:right 8
|
||||||
:border-radius 16
|
:border-radius 16
|
||||||
|
@ -2,70 +2,45 @@
|
|||||||
(:require
|
(:require
|
||||||
[quo.theme]
|
[quo.theme]
|
||||||
[react-native.core :as rn]
|
[react-native.core :as rn]
|
||||||
[react-native.platform :as platform]
|
|
||||||
[react-native.reanimated :as reanimated]
|
[react-native.reanimated :as reanimated]
|
||||||
[react-native.safe-area :as safe-area]
|
[react-native.safe-area :as safe-area]
|
||||||
[reagent.core :as reagent]
|
|
||||||
[status-im.common.contact-list-item.view :as contact-list-item]
|
[status-im.common.contact-list-item.view :as contact-list-item]
|
||||||
|
[status-im.contexts.chat.messenger.composer.constants :as constants]
|
||||||
[status-im.contexts.chat.messenger.composer.mentions.style :as style]
|
[status-im.contexts.chat.messenger.composer.mentions.style :as style]
|
||||||
[status-im.contexts.chat.messenger.composer.utils :as utils]
|
[status-im.contexts.chat.messenger.messages.constants :as messages.constants]
|
||||||
[utils.re-frame :as rf]))
|
[utils.re-frame :as rf]))
|
||||||
|
|
||||||
(defn update-cursor
|
|
||||||
[user {:keys [cursor-position input-ref]}]
|
|
||||||
(when platform/android?
|
|
||||||
(let [new-cursor-pos (+ (count (:primary-name user)) @cursor-position 1)]
|
|
||||||
(reset! cursor-position new-cursor-pos)
|
|
||||||
(reagent/next-tick #(when @input-ref
|
|
||||||
(.setNativeProps ^js @input-ref
|
|
||||||
(clj->js {:selection {:start new-cursor-pos
|
|
||||||
:end
|
|
||||||
new-cursor-pos}})))))))
|
|
||||||
|
|
||||||
(defn mention-item
|
(defn mention-item
|
||||||
[user _ _ render-data]
|
[user]
|
||||||
[contact-list-item/contact-list-item
|
[contact-list-item/contact-list-item
|
||||||
{:on-press (fn []
|
{:on-press #(rf/dispatch [:chat.ui/select-mention user])}
|
||||||
(rf/dispatch [:chat.ui/select-mention user])
|
|
||||||
(update-cursor user render-data))}
|
|
||||||
user])
|
user])
|
||||||
|
|
||||||
(defn- f-view
|
(defn view
|
||||||
[suggestions-atom props state animations max-height cursor-pos images link-previews? reply edit]
|
[{:keys [layout-height]}]
|
||||||
(let [suggestions (rf/sub [:chat/mention-suggestions])
|
(let [suggestions (rf/sub [:chat/mention-suggestions])
|
||||||
theme (quo.theme/use-theme)
|
suggestions? (seq suggestions)
|
||||||
opacity (reanimated/use-shared-value (if (seq suggestions) 1 0))
|
theme (quo.theme/use-theme)
|
||||||
size (count suggestions)
|
opacity (reanimated/use-shared-value (if suggestions? 1 0))
|
||||||
data {:keyboard-height @(:kb-height state)
|
[suggestions-state set-suggestions-state] (rn/use-state suggestions)
|
||||||
:insets (safe-area/get-insets)
|
top (min constants/mentions-max-height
|
||||||
:curr-height (reanimated/get-shared-value (:height animations))
|
(* (count suggestions-state) 56)
|
||||||
:window-height (:height (rn/get-window))
|
(- @layout-height
|
||||||
:images images
|
(+ (safe-area/get-top)
|
||||||
:link-previews? link-previews?
|
messages.constants/top-bar-height
|
||||||
:reply reply
|
5)))]
|
||||||
:edit edit}
|
|
||||||
mentions-pos
|
|
||||||
(utils/calc-suggestions-position cursor-pos max-height size state data images link-previews?)]
|
|
||||||
(rn/use-effect
|
(rn/use-effect
|
||||||
(fn []
|
(fn []
|
||||||
(if (seq suggestions)
|
(if suggestions?
|
||||||
(reset! suggestions-atom suggestions)
|
(set-suggestions-state suggestions)
|
||||||
(js/setTimeout #(reset! suggestions-atom suggestions) 300))
|
(js/setTimeout #(set-suggestions-state suggestions) 300))
|
||||||
(reanimated/animate opacity (if (seq suggestions) 1 0)))
|
(reanimated/animate opacity (if suggestions? 1 0)))
|
||||||
[(seq suggestions)])
|
[suggestions?])
|
||||||
[reanimated/view
|
[reanimated/view
|
||||||
{:style (style/container opacity mentions-pos theme)}
|
{:style (style/container opacity top theme)}
|
||||||
[rn/flat-list
|
[rn/flat-list
|
||||||
{:keyboard-should-persist-taps :always
|
{:keyboard-should-persist-taps :always
|
||||||
:data (vals @suggestions-atom)
|
:data (vals suggestions-state)
|
||||||
:key-fn :key
|
:key-fn :key
|
||||||
:render-fn mention-item
|
:render-fn mention-item
|
||||||
:render-data {:cursor-position (:cursor-position state)
|
|
||||||
:input-ref (:input-ref props)}
|
|
||||||
:accessibility-label :mentions-list}]]))
|
:accessibility-label :mentions-list}]]))
|
||||||
|
|
||||||
(defn view
|
|
||||||
[props state animations max-height cursor-pos images link-previews? reply edit]
|
|
||||||
(let [suggestions-atom (reagent/atom {})]
|
|
||||||
[:f> f-view suggestions-atom props state animations max-height cursor-pos images link-previews? reply
|
|
||||||
edit]))
|
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
[react-native.reanimated :as reanimated]
|
[react-native.reanimated :as reanimated]
|
||||||
[status-im.constants :as constant]
|
[status-im.constants :as constant]
|
||||||
[status-im.contexts.chat.messenger.composer.constants :as constants]
|
[status-im.contexts.chat.messenger.composer.constants :as constants]
|
||||||
|
[status-im.contexts.chat.messenger.composer.effects :as effects]
|
||||||
[status-im.contexts.chat.messenger.composer.reply.style :as style]
|
[status-im.contexts.chat.messenger.composer.reply.style :as style]
|
||||||
[status-im.contexts.chat.messenger.composer.utils :as utils]
|
[status-im.contexts.chat.messenger.composer.utils :as utils]
|
||||||
[utils.ens.stateofus :as stateofus]
|
[utils.ens.stateofus :as stateofus]
|
||||||
@ -164,14 +165,12 @@
|
|||||||
:end {:x 0.7 :y 0}
|
:end {:x 0.7 :y 0}
|
||||||
:style style/gradient}])]))
|
:style style/gradient}])]))
|
||||||
|
|
||||||
(defn- f-view
|
(defn view
|
||||||
[recording? input-ref]
|
[input-ref]
|
||||||
(let [reply (rf/sub [:chats/reply-message])
|
(let [reply (rf/sub [:chats/reply-message])
|
||||||
height (reanimated/use-shared-value (if reply constants/reply-container-height 0))]
|
height (reanimated/use-shared-value (if reply constants/reply-container-height 0))]
|
||||||
|
(effects/use-reply input-ref reply)
|
||||||
(rn/use-effect #(reanimated/animate height (if reply constants/reply-container-height 0)) [reply])
|
(rn/use-effect #(reanimated/animate height (if reply constants/reply-container-height 0)) [reply])
|
||||||
[reanimated/view {:style (reanimated/apply-animations-to-style {:height height} {})}
|
[reanimated/view {:style (reanimated/apply-animations-to-style {:height height} {})}
|
||||||
(when reply [quoted-message reply true false recording? input-ref])]))
|
(when reply
|
||||||
|
[quoted-message reply true false false input-ref])]))
|
||||||
(defn view
|
|
||||||
[{:keys [recording?]} input-ref]
|
|
||||||
[:f> f-view @recording? input-ref])
|
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
(:require
|
(:require
|
||||||
[quo.foundations.colors :as colors]
|
[quo.foundations.colors :as colors]
|
||||||
[quo.foundations.typography :as typography]
|
[quo.foundations.typography :as typography]
|
||||||
[react-native.platform :as platform]
|
|
||||||
[react-native.reanimated :as reanimated]
|
[react-native.reanimated :as reanimated]
|
||||||
[status-im.contexts.chat.messenger.composer.constants :as constants]
|
[status-im.contexts.chat.messenger.composer.constants :as constants]
|
||||||
[status-im.contexts.shell.jump-to.constants :as shell.constants]
|
[status-im.contexts.shell.jump-to.constants :as shell.constants]
|
||||||
@ -10,34 +9,6 @@
|
|||||||
|
|
||||||
(def border-top-radius 20)
|
(def border-top-radius 20)
|
||||||
|
|
||||||
(defn shadow
|
|
||||||
[theme]
|
|
||||||
(when platform/ios?
|
|
||||||
{:shadow-radius 20
|
|
||||||
:shadow-opacity (colors/theme-colors 0.1 0.7 theme)
|
|
||||||
:shadow-color colors/neutral-100
|
|
||||||
:shadow-offset {:width 0 :height (colors/theme-colors -4 -8 theme)}}))
|
|
||||||
|
|
||||||
(def composer-sheet-and-jump-to-container
|
|
||||||
{:position :absolute
|
|
||||||
:bottom 0
|
|
||||||
:left 0
|
|
||||||
:right 0})
|
|
||||||
|
|
||||||
(defn sheet-container
|
|
||||||
[insets {:keys [container-opacity composer-elevation]} theme]
|
|
||||||
(reanimated/apply-animations-to-style
|
|
||||||
{:opacity container-opacity
|
|
||||||
:elevation composer-elevation}
|
|
||||||
(merge
|
|
||||||
{:border-top-left-radius border-top-radius
|
|
||||||
:border-top-right-radius border-top-radius
|
|
||||||
:padding-horizontal 20
|
|
||||||
:background-color (colors/theme-colors colors/white colors/neutral-95 theme)
|
|
||||||
:z-index 3
|
|
||||||
:padding-bottom (:bottom insets)}
|
|
||||||
(shadow theme))))
|
|
||||||
|
|
||||||
(def bar-container
|
(def bar-container
|
||||||
{:height constants/bar-container-height
|
{:height constants/bar-container-height
|
||||||
:left 0
|
:left 0
|
||||||
@ -54,65 +25,14 @@
|
|||||||
:border-radius 100
|
:border-radius 100
|
||||||
:background-color (colors/theme-colors colors/neutral-100-opa-5 colors/white-opa-10 theme)})
|
:background-color (colors/theme-colors colors/neutral-100-opa-5 colors/white-opa-10 theme)})
|
||||||
|
|
||||||
(defn input-container
|
|
||||||
[height max-height]
|
|
||||||
(reanimated/apply-animations-to-style
|
|
||||||
{:height height}
|
|
||||||
{:max-height max-height
|
|
||||||
:z-index 1}))
|
|
||||||
|
|
||||||
(defn input-view
|
|
||||||
[{:keys [recording?]}]
|
|
||||||
{:overflow :hidden
|
|
||||||
:z-index 1
|
|
||||||
:flex 1
|
|
||||||
:display (if @recording? :none :flex)
|
|
||||||
:min-height constants/input-height})
|
|
||||||
|
|
||||||
(defn input-text
|
(defn input-text
|
||||||
[{:keys [saved-emoji-kb-extra-height]}
|
[theme]
|
||||||
{:keys [focused? maximized?]}
|
|
||||||
{:keys [max-height theme]}]
|
|
||||||
(assoc typography/paragraph-1
|
(assoc typography/paragraph-1
|
||||||
:color (colors/theme-colors :black :white theme)
|
:color (colors/theme-colors :black :white theme)
|
||||||
:text-align-vertical :top
|
:text-align-vertical :top
|
||||||
:position (if @saved-emoji-kb-extra-height :relative :absolute)
|
|
||||||
:top 0
|
:top 0
|
||||||
:left 0
|
:left 0
|
||||||
:right (when (or focused? platform/ios?) 0)
|
:max-height 150))
|
||||||
:max-height max-height
|
|
||||||
:padding-bottom (when @maximized? 0)))
|
|
||||||
|
|
||||||
(defn background
|
|
||||||
[opacity background-y window-height]
|
|
||||||
(reanimated/apply-animations-to-style
|
|
||||||
{:opacity opacity
|
|
||||||
:transform [{:translate-y background-y}]}
|
|
||||||
{:position :absolute
|
|
||||||
:left 0
|
|
||||||
:right 0
|
|
||||||
:bottom 0
|
|
||||||
:height window-height
|
|
||||||
:background-color colors/neutral-95-opa-70}))
|
|
||||||
|
|
||||||
(defn blur-container
|
|
||||||
[composer-default-height {:keys [blur-container-elevation]}]
|
|
||||||
[{:elevation blur-container-elevation}
|
|
||||||
{:position :absolute
|
|
||||||
:left 0
|
|
||||||
:right 0
|
|
||||||
:bottom 0
|
|
||||||
:height composer-default-height
|
|
||||||
:border-top-right-radius border-top-radius
|
|
||||||
:border-top-left-radius border-top-radius
|
|
||||||
:overflow :hidden}])
|
|
||||||
|
|
||||||
(defn blur-view
|
|
||||||
[theme]
|
|
||||||
{:style {:flex 1}
|
|
||||||
:blur-radius (if platform/ios? 20 10)
|
|
||||||
:blur-type theme
|
|
||||||
:blur-amount 20})
|
|
||||||
|
|
||||||
(defn shell-button
|
(defn shell-button
|
||||||
[translate-y opacity]
|
[translate-y opacity]
|
||||||
|
@ -1,48 +0,0 @@
|
|||||||
(ns status-im.contexts.chat.messenger.composer.sub-view
|
|
||||||
(:require
|
|
||||||
[quo.core :as quo]
|
|
||||||
[react-native.core :as rn]
|
|
||||||
[react-native.reanimated :as reanimated]
|
|
||||||
[status-im.contexts.chat.messenger.composer.style :as style]
|
|
||||||
[status-im.feature-flags :as ff]
|
|
||||||
[utils.i18n :as i18n]
|
|
||||||
[utils.re-frame :as rf]
|
|
||||||
[utils.worklets.chat.messenger.composer :as worklets]))
|
|
||||||
|
|
||||||
(defn bar
|
|
||||||
[theme]
|
|
||||||
[rn/view {:style style/bar-container}
|
|
||||||
[rn/view {:style (style/bar theme)}]])
|
|
||||||
|
|
||||||
(defn- f-shell-button
|
|
||||||
[{:keys [composer-focused?]} chat-list-scroll-y window-height]
|
|
||||||
(let [customization-color (rf/sub [:profile/customization-color])
|
|
||||||
scroll-down-button-opacity (worklets/scroll-down-button-opacity
|
|
||||||
chat-list-scroll-y
|
|
||||||
composer-focused?
|
|
||||||
window-height)
|
|
||||||
jump-to-button-opacity (worklets/jump-to-button-opacity
|
|
||||||
scroll-down-button-opacity
|
|
||||||
composer-focused?)
|
|
||||||
jump-to-button-position (worklets/jump-to-button-position
|
|
||||||
scroll-down-button-opacity
|
|
||||||
composer-focused?)]
|
|
||||||
[rn/view {:style (style/shell-button-container)}
|
|
||||||
(when (ff/enabled? ::ff/shell.jump-to)
|
|
||||||
[reanimated/view
|
|
||||||
{:style (style/shell-button jump-to-button-position jump-to-button-opacity)}
|
|
||||||
[quo/floating-shell-button
|
|
||||||
{:jump-to
|
|
||||||
{:on-press #(rf/dispatch [:shell/navigate-to-jump-to])
|
|
||||||
:customization-color customization-color
|
|
||||||
:label (i18n/label :t/jump-to)
|
|
||||||
:style {:align-self :center}}}
|
|
||||||
{}]])
|
|
||||||
[quo/floating-shell-button
|
|
||||||
{:scroll-to-bottom {:on-press #(rf/dispatch [:chat.ui/scroll-to-bottom])}}
|
|
||||||
style/scroll-to-bottom-button
|
|
||||||
scroll-down-button-opacity]]))
|
|
||||||
|
|
||||||
(defn shell-button
|
|
||||||
[shared-values chat-list-scroll-y window-height]
|
|
||||||
[:f> f-shell-button shared-values chat-list-scroll-y window-height])
|
|
@ -1,52 +1,8 @@
|
|||||||
(ns status-im.contexts.chat.messenger.composer.utils
|
(ns status-im.contexts.chat.messenger.composer.utils
|
||||||
(:require
|
(:require
|
||||||
[clojure.string :as string]
|
|
||||||
[react-native.core :as rn]
|
|
||||||
[react-native.platform :as platform]
|
|
||||||
[react-native.reanimated :as reanimated]
|
|
||||||
[reagent.core :as reagent]
|
|
||||||
[status-im.contexts.chat.messenger.composer.constants :as constants]
|
[status-im.contexts.chat.messenger.composer.constants :as constants]
|
||||||
[status-im.contexts.chat.messenger.composer.selection :as selection]
|
|
||||||
[utils.number]
|
[utils.number]
|
||||||
[utils.re-frame :as rf]
|
[utils.re-frame :as rf]))
|
||||||
[utils.worklets.chat.messenger.composer :as worklets]))
|
|
||||||
|
|
||||||
(defn bounded-val
|
|
||||||
[v min-v max-v]
|
|
||||||
(max min-v (min v max-v)))
|
|
||||||
|
|
||||||
(defn update-height?
|
|
||||||
[content-size height max-height]
|
|
||||||
(let [diff (Math/abs (- content-size (reanimated/get-shared-value height)))]
|
|
||||||
(and (not= (reanimated/get-shared-value height) max-height)
|
|
||||||
(> diff constants/content-change-threshold))))
|
|
||||||
|
|
||||||
(defn show-top-gradient?
|
|
||||||
[y lines max-lines gradient-opacity focused?]
|
|
||||||
(and
|
|
||||||
(> y constants/line-height)
|
|
||||||
(>= lines max-lines)
|
|
||||||
(= (reanimated/get-shared-value gradient-opacity) 0)
|
|
||||||
@focused?))
|
|
||||||
|
|
||||||
(defn hide-top-gradient?
|
|
||||||
[y gradient-opacity]
|
|
||||||
(and
|
|
||||||
(<= y constants/line-height)
|
|
||||||
(= (reanimated/get-shared-value gradient-opacity) 1)))
|
|
||||||
|
|
||||||
(defn show-bottom-gradient?
|
|
||||||
[{:keys [text-value focused?]} {:keys [lines]}]
|
|
||||||
(and (not-empty @text-value) (not @focused?) (> lines 2)))
|
|
||||||
|
|
||||||
(defn show-background?
|
|
||||||
[max-height new-height maximized?]
|
|
||||||
(or @maximized?
|
|
||||||
(> new-height (* constants/background-threshold max-height))))
|
|
||||||
|
|
||||||
(defn calc-lines
|
|
||||||
[height]
|
|
||||||
(Math/floor (/ height constants/line-height)))
|
|
||||||
|
|
||||||
(defn calc-top-content-height
|
(defn calc-top-content-height
|
||||||
[reply? edit?]
|
[reply? edit?]
|
||||||
@ -60,35 +16,6 @@
|
|||||||
(seq images) (+ constants/images-container-height)
|
(seq images) (+ constants/images-container-height)
|
||||||
link-previews? (+ constants/links-container-height)))
|
link-previews? (+ constants/links-container-height)))
|
||||||
|
|
||||||
(defn calc-reopen-height
|
|
||||||
[text-value min-height max-height content-height saved-height]
|
|
||||||
(if (empty? @text-value)
|
|
||||||
min-height
|
|
||||||
(let [input-height (min @content-height
|
|
||||||
(reanimated/get-shared-value saved-height))]
|
|
||||||
(min max-height input-height))))
|
|
||||||
|
|
||||||
(defn get-min-height
|
|
||||||
[lines]
|
|
||||||
(if (> lines 1)
|
|
||||||
constants/multiline-minimized-height
|
|
||||||
constants/input-height))
|
|
||||||
|
|
||||||
(defn calc-max-height
|
|
||||||
[{:keys [reply edit images link-previews?]} window-height kb-height insets]
|
|
||||||
(let [margin-top (if platform/ios? (:top insets) (+ 10 (:top insets)))]
|
|
||||||
(- window-height
|
|
||||||
margin-top
|
|
||||||
kb-height
|
|
||||||
constants/bar-container-height
|
|
||||||
constants/actions-container-height
|
|
||||||
(calc-top-content-height reply edit)
|
|
||||||
(calc-bottom-content-height images link-previews?))))
|
|
||||||
|
|
||||||
(defn empty-input?
|
|
||||||
[{:keys [input-text images link-previews? reply audio edit]}]
|
|
||||||
(not (or (not-empty input-text) images link-previews? reply audio edit)))
|
|
||||||
|
|
||||||
(defn blur-input
|
(defn blur-input
|
||||||
[input-ref]
|
[input-ref]
|
||||||
(when @input-ref
|
(when @input-ref
|
||||||
@ -101,36 +28,17 @@
|
|||||||
(rf/dispatch [:chat.ui/cancel-message-reply]))
|
(rf/dispatch [:chat.ui/cancel-message-reply]))
|
||||||
|
|
||||||
(defn cancel-edit-message
|
(defn cancel-edit-message
|
||||||
[text-value input-ref input-height]
|
[input-ref]
|
||||||
(reset! text-value "")
|
|
||||||
;; NOTE: adding a timeout to assure the input is blurred on the next tick
|
;; NOTE: adding a timeout to assure the input is blurred on the next tick
|
||||||
;; after the `text-value` was cleared. Otherwise the height will be calculated
|
;; after the `text-value` was cleared. Otherwise the height will be calculated
|
||||||
;; with the old `text-value`, leading to wrong composer height after blur.
|
;; with the old `text-value`, leading to wrong composer height after blur.
|
||||||
(js/setTimeout
|
(js/setTimeout
|
||||||
(fn []
|
(fn []
|
||||||
(blur-input input-ref)
|
(blur-input input-ref))
|
||||||
(reanimated/set-shared-value input-height constants/input-height))
|
|
||||||
100)
|
100)
|
||||||
(.setNativeProps ^js @input-ref (clj->js {:text ""}))
|
(.setNativeProps ^js @input-ref (clj->js {:text ""}))
|
||||||
(rf/dispatch [:chat.ui/set-input-content-height constants/input-height])
|
|
||||||
(rf/dispatch [:chat.ui/cancel-message-edit]))
|
(rf/dispatch [:chat.ui/cancel-message-edit]))
|
||||||
|
|
||||||
(defn count-lines
|
|
||||||
[s]
|
|
||||||
(-> s
|
|
||||||
(string/split #"\n" -1)
|
|
||||||
(butlast)
|
|
||||||
count))
|
|
||||||
|
|
||||||
(defn cursor-y-position-relative-to-container
|
|
||||||
[{:keys [scroll-y]}
|
|
||||||
{:keys [cursor-position text-value]}]
|
|
||||||
(let [sub-text (subs @text-value 0 @cursor-position)
|
|
||||||
sub-text-lines (count-lines sub-text)
|
|
||||||
scrolled-lines (Math/round (/ @scroll-y constants/line-height))
|
|
||||||
sub-text-lines-in-view (- sub-text-lines scrolled-lines)]
|
|
||||||
(* sub-text-lines-in-view constants/line-height)))
|
|
||||||
|
|
||||||
(defn calc-suggestions-position
|
(defn calc-suggestions-position
|
||||||
[cursor-pos max-height size
|
[cursor-pos max-height size
|
||||||
{:keys [maximized?]}
|
{:keys [maximized?]}
|
||||||
@ -153,85 +61,3 @@
|
|||||||
(let [bottom-content-height (calc-bottom-content-height images link-previews?)]
|
(let [bottom-content-height (calc-bottom-content-height images link-previews?)]
|
||||||
(+ base bottom-content-height))
|
(+ base bottom-content-height))
|
||||||
(+ constants/actions-container-height (:bottom insets) (- curr-height cursor-pos) 18)))))
|
(+ constants/actions-container-height (:bottom insets) (- curr-height cursor-pos) 18)))))
|
||||||
|
|
||||||
(defn init-non-reactive-state
|
|
||||||
[]
|
|
||||||
{:input-ref (atom nil)
|
|
||||||
:selectable-input-ref (atom nil)
|
|
||||||
:keyboard-show-listener (atom nil)
|
|
||||||
:keyboard-frame-listener (atom nil)
|
|
||||||
:keyboard-hide-listener (atom nil)
|
|
||||||
:emoji-kb-extra-height (atom nil)
|
|
||||||
:saved-emoji-kb-extra-height (atom nil)
|
|
||||||
:sending-images? (atom false)
|
|
||||||
:sending-links? (atom false)
|
|
||||||
:record-reset-fn (atom nil)
|
|
||||||
:scroll-y (atom 0)
|
|
||||||
:selection-event (atom nil)
|
|
||||||
:selection-manager (rn/selectable-text-input-manager)})
|
|
||||||
|
|
||||||
(defn init-reactive-state
|
|
||||||
[]
|
|
||||||
{:text-value (reagent/atom "")
|
|
||||||
:cursor-position (reagent/atom 0)
|
|
||||||
:saved-cursor-position (reagent/atom 0)
|
|
||||||
:gradient-z-index (reagent/atom 0)
|
|
||||||
:kb-default-height (reagent/atom 0)
|
|
||||||
:kb-height (reagent/atom 0)
|
|
||||||
:gesture-enabled? (reagent/atom true)
|
|
||||||
:lock-selection? (reagent/atom true)
|
|
||||||
:focused? (reagent/atom false)
|
|
||||||
:lock-layout? (reagent/atom false)
|
|
||||||
:maximized? (reagent/atom false)
|
|
||||||
:record-permission? (reagent/atom true)
|
|
||||||
:recording? (reagent/atom false)
|
|
||||||
:first-level? (reagent/atom true)
|
|
||||||
:menu-items (reagent/atom selection/first-level-menu-items)})
|
|
||||||
|
|
||||||
(defn init-subs
|
|
||||||
[]
|
|
||||||
(let [chat-input (rf/sub [:chats/current-chat-input])]
|
|
||||||
{:images (seq (rf/sub [:chats/sending-image]))
|
|
||||||
:link-previews? (or (rf/sub [:chats/link-previews?])
|
|
||||||
(rf/sub [:chats/status-link-previews?]))
|
|
||||||
:audio (rf/sub [:chats/sending-audio])
|
|
||||||
:reply (rf/sub [:chats/reply-message])
|
|
||||||
:edit (rf/sub [:chats/edit-message])
|
|
||||||
:input-with-mentions (rf/sub [:chat/input-with-mentions])
|
|
||||||
:input-text (:input-text chat-input)
|
|
||||||
:alert-banners-top-margin (rf/sub [:alert-banners/top-margin])
|
|
||||||
:input-content-height (:input-content-height chat-input)}))
|
|
||||||
|
|
||||||
(defn init-shared-values
|
|
||||||
[]
|
|
||||||
(let [composer-focused? (reanimated/use-shared-value false)
|
|
||||||
empty-input-shared-value? (reanimated/use-shared-value true)]
|
|
||||||
{:composer-focused? composer-focused?
|
|
||||||
:empty-input? empty-input-shared-value?
|
|
||||||
:container-opacity (worklets/composer-container-opacity composer-focused?
|
|
||||||
empty-input-shared-value?
|
|
||||||
constants/empty-opacity)
|
|
||||||
:blur-container-elevation (worklets/blur-container-elevation composer-focused?
|
|
||||||
empty-input-shared-value?)
|
|
||||||
:composer-elevation (worklets/composer-elevation composer-focused?
|
|
||||||
empty-input-shared-value?)}))
|
|
||||||
|
|
||||||
(defn init-animations
|
|
||||||
[lines content-height max-height opacity background-y shared-values]
|
|
||||||
(let [initial-height (if (> lines 1)
|
|
||||||
constants/multiline-minimized-height
|
|
||||||
constants/input-height)
|
|
||||||
bottom-content-height 0]
|
|
||||||
(assoc shared-values
|
|
||||||
:gradient-opacity (reanimated/use-shared-value 0)
|
|
||||||
:height (reanimated/use-shared-value
|
|
||||||
initial-height)
|
|
||||||
:saved-height (reanimated/use-shared-value
|
|
||||||
initial-height)
|
|
||||||
:last-height (reanimated/use-shared-value
|
|
||||||
(utils.number/value-in-range
|
|
||||||
(+ @content-height bottom-content-height)
|
|
||||||
constants/input-height
|
|
||||||
max-height))
|
|
||||||
:opacity opacity
|
|
||||||
:background-y background-y)))
|
|
||||||
|
@ -1,183 +1,56 @@
|
|||||||
(ns status-im.contexts.chat.messenger.composer.view
|
(ns status-im.contexts.chat.messenger.composer.view
|
||||||
(:require
|
(:require
|
||||||
[clojure.string :as string]
|
|
||||||
[quo.core :as quo]
|
[quo.core :as quo]
|
||||||
[quo.foundations.colors :as colors]
|
[quo.foundations.colors :as colors]
|
||||||
[quo.theme :as quo.theme]
|
[quo.theme :as quo.theme]
|
||||||
[react-native.core :as rn]
|
[react-native.core :as rn]
|
||||||
[react-native.gesture :as gesture]
|
[react-native.safe-area :as safe-area]
|
||||||
[react-native.hooks :as hooks]
|
|
||||||
[react-native.platform :as platform]
|
|
||||||
[react-native.reanimated :as reanimated]
|
|
||||||
[reagent.core :as reagent]
|
|
||||||
[status-im.contexts.chat.messenger.composer.actions.view :as actions]
|
[status-im.contexts.chat.messenger.composer.actions.view :as actions]
|
||||||
[status-im.contexts.chat.messenger.composer.constants :as constants]
|
[status-im.contexts.chat.messenger.composer.constants :as constants]
|
||||||
[status-im.contexts.chat.messenger.composer.edit.view :as edit]
|
[status-im.contexts.chat.messenger.composer.edit.view :as edit]
|
||||||
[status-im.contexts.chat.messenger.composer.effects :as effects]
|
|
||||||
[status-im.contexts.chat.messenger.composer.gesture :as drag-gesture]
|
|
||||||
[status-im.contexts.chat.messenger.composer.gradients.view :as gradients]
|
|
||||||
[status-im.contexts.chat.messenger.composer.handlers :as handler]
|
[status-im.contexts.chat.messenger.composer.handlers :as handler]
|
||||||
[status-im.contexts.chat.messenger.composer.images.view :as images]
|
[status-im.contexts.chat.messenger.composer.images.view :as images]
|
||||||
[status-im.contexts.chat.messenger.composer.link-preview.view :as link-preview]
|
[status-im.contexts.chat.messenger.composer.link-preview.view :as link-preview]
|
||||||
[status-im.contexts.chat.messenger.composer.mentions.view :as mentions]
|
[status-im.contexts.chat.messenger.composer.mentions.view :as mentions]
|
||||||
[status-im.contexts.chat.messenger.composer.reply.view :as reply]
|
[status-im.contexts.chat.messenger.composer.reply.view :as reply]
|
||||||
[status-im.contexts.chat.messenger.composer.selection :as selection]
|
|
||||||
[status-im.contexts.chat.messenger.composer.style :as style]
|
[status-im.contexts.chat.messenger.composer.style :as style]
|
||||||
[status-im.contexts.chat.messenger.composer.sub-view :as sub-view]
|
|
||||||
[status-im.contexts.chat.messenger.composer.utils :as utils]
|
|
||||||
[status-im.contexts.chat.messenger.messages.contact-requests.bottom-drawer.view :as
|
|
||||||
contact-requests.bottom-drawer]
|
|
||||||
[utils.i18n :as i18n]
|
[utils.i18n :as i18n]
|
||||||
[utils.re-frame :as rf]))
|
[utils.re-frame :as rf]))
|
||||||
|
|
||||||
(defn sheet-component
|
(defn input
|
||||||
[{:keys [insets
|
[_ _]
|
||||||
chat-list-scroll-y
|
(let [default-value (:input-text (rf/sub [:chats/current-chat-input]))]
|
||||||
chat-screen-layout-calculations-complete?
|
(fn [set-ref theme]
|
||||||
opacity
|
[rn/text-input
|
||||||
background-y
|
{:ref set-ref
|
||||||
theme
|
:on-change-text handler/change-text
|
||||||
window-height]} props state shared-values]
|
:keyboard-appearance theme
|
||||||
(let [subscriptions (utils/init-subs)
|
:max-font-size-multiplier 1
|
||||||
top-margin (if (pos? (:alert-banners-top-margin subscriptions))
|
:multiline true
|
||||||
;; top margin increased to avoid composer overlapping with the
|
:placeholder (i18n/label :t/type-something)
|
||||||
;; alert banner
|
:placeholder-text-color (colors/theme-colors colors/neutral-40 colors/neutral-50 theme)
|
||||||
(+ (:alert-banners-top-margin subscriptions) 12)
|
:max-length constants/max-text-size
|
||||||
0)
|
:accessibility-label :chat-message-input
|
||||||
window-height (- window-height top-margin)
|
:style (style/input-text theme)
|
||||||
content-height (reagent/atom (or (:input-content-height ; Actual text height
|
:default-value default-value}])))
|
||||||
subscriptions)
|
|
||||||
constants/input-height))
|
|
||||||
{:keys [keyboard-shown]} (hooks/use-keyboard)
|
|
||||||
max-height (utils/calc-max-height subscriptions ; Max allowed height for the
|
|
||||||
; composer view
|
|
||||||
window-height
|
|
||||||
@(:kb-height state)
|
|
||||||
insets)
|
|
||||||
lines (utils/calc-lines (- @content-height constants/extra-content-offset)) ; Current
|
|
||||||
; lines
|
|
||||||
; count
|
|
||||||
;; Maximum number of lines that can be displayed when composer in maximized
|
|
||||||
max-lines (utils/calc-lines max-height)
|
|
||||||
animations (utils/init-animations
|
|
||||||
lines
|
|
||||||
content-height
|
|
||||||
max-height
|
|
||||||
opacity
|
|
||||||
background-y
|
|
||||||
shared-values)
|
|
||||||
dimensions {:content-height content-height
|
|
||||||
:max-height max-height
|
|
||||||
:window-height window-height
|
|
||||||
:lines lines
|
|
||||||
:max-lines max-lines}
|
|
||||||
show-bottom-gradient? (utils/show-bottom-gradient? state dimensions)
|
|
||||||
;; Cursor position, needed to determine where to display the mentions view
|
|
||||||
cursor-pos (utils/cursor-y-position-relative-to-container
|
|
||||||
props
|
|
||||||
state)]
|
|
||||||
(effects/did-mount props)
|
|
||||||
(effects/initialize props
|
|
||||||
state
|
|
||||||
animations
|
|
||||||
dimensions
|
|
||||||
subscriptions)
|
|
||||||
(effects/use-edit props state subscriptions chat-screen-layout-calculations-complete?)
|
|
||||||
(effects/use-reply props subscriptions chat-screen-layout-calculations-complete?)
|
|
||||||
(effects/update-input-mention props state subscriptions)
|
|
||||||
(effects/link-previews props state animations subscriptions)
|
|
||||||
(effects/use-images props state animations subscriptions)
|
|
||||||
[:<>
|
|
||||||
[mentions/view props state animations max-height cursor-pos
|
|
||||||
(:images subscriptions)
|
|
||||||
(:link-previews? subscriptions)
|
|
||||||
(:reply subscriptions)
|
|
||||||
(:edit subscriptions)]
|
|
||||||
[rn/view
|
|
||||||
{:style style/composer-sheet-and-jump-to-container}
|
|
||||||
[sub-view/shell-button shared-values chat-list-scroll-y window-height]
|
|
||||||
[gesture/gesture-detector
|
|
||||||
{:gesture
|
|
||||||
(drag-gesture/drag-gesture props state animations dimensions keyboard-shown)}
|
|
||||||
[reanimated/view
|
|
||||||
{:style (style/sheet-container insets animations theme)}
|
|
||||||
[sub-view/bar theme]
|
|
||||||
[:<>
|
|
||||||
[reply/view state (:input-ref props)]
|
|
||||||
[edit/view
|
|
||||||
{:text-value (:text-value state)
|
|
||||||
:input-height (:height animations)
|
|
||||||
:input-ref (:input-ref props)}]]
|
|
||||||
[reanimated/touchable-opacity
|
|
||||||
{:active-opacity 1
|
|
||||||
:on-press (fn []
|
|
||||||
(when-let [ref @(:input-ref props)]
|
|
||||||
(.focus ^js ref)))
|
|
||||||
:style (style/input-container (:height animations) max-height)
|
|
||||||
:accessibility-label :message-input-container}
|
|
||||||
[rn/selectable-text-input
|
|
||||||
{:ref #(reset! (:selectable-input-ref props) %)
|
|
||||||
:menu-items @(:menu-items state)
|
|
||||||
:style (style/input-view state)}
|
|
||||||
[rn/text-input
|
|
||||||
{:ref #(reset! (:input-ref props) %)
|
|
||||||
:default-value @(:text-value state)
|
|
||||||
:on-focus #(handler/focus props state animations dimensions)
|
|
||||||
:on-blur #(handler/blur state animations dimensions)
|
|
||||||
:on-content-size-change #(handler/content-size-change %
|
|
||||||
state
|
|
||||||
animations
|
|
||||||
dimensions
|
|
||||||
(or keyboard-shown
|
|
||||||
(:edit subscriptions)))
|
|
||||||
:on-scroll #(handler/scroll % props state animations dimensions)
|
|
||||||
:on-change-text #(handler/change-text % props state)
|
|
||||||
:on-selection-change #(handler/selection-change % props state)
|
|
||||||
:on-selection #(selection/on-selection % props state)
|
|
||||||
:keyboard-appearance theme
|
|
||||||
:max-font-size-multiplier 1
|
|
||||||
:multiline true
|
|
||||||
:placeholder (i18n/label :t/type-something)
|
|
||||||
:placeholder-text-color (colors/theme-colors colors/neutral-40 colors/neutral-50 theme)
|
|
||||||
:style (style/input-text props
|
|
||||||
state
|
|
||||||
{:max-height max-height
|
|
||||||
:theme theme})
|
|
||||||
:max-length constants/max-text-size
|
|
||||||
:accessibility-label :chat-message-input}]]]
|
|
||||||
[:<>
|
|
||||||
[gradients/view props state animations show-bottom-gradient?]
|
|
||||||
[link-preview/view]
|
|
||||||
[images/images-list]]
|
|
||||||
[:f> actions/view props state animations window-height subscriptions]]]]]))
|
|
||||||
|
|
||||||
(defn f-composer
|
(defn view
|
||||||
[props]
|
[props]
|
||||||
(let [theme (quo.theme/use-theme)
|
(let [theme (quo.theme/use-theme)
|
||||||
opacity (reanimated/use-shared-value 0)
|
bottom (safe-area/get-bottom)
|
||||||
window-height (:height (rn/get-window))
|
input-ref (rn/use-ref-atom nil)
|
||||||
background-y (reanimated/use-shared-value (- window-height))
|
set-ref (rn/use-callback (fn [value]
|
||||||
composer-default-height (+ constants/composer-default-height (:bottom (:insets props)))
|
(rf/dispatch [:chat/set-input-ref value])
|
||||||
shared-values (utils/init-shared-values)
|
(reset! input-ref value)))]
|
||||||
extra-params (assoc props
|
[rn/view {:style {:margin-bottom bottom}}
|
||||||
:window-height window-height
|
[mentions/view props]
|
||||||
:opacity opacity
|
[quo/separator]
|
||||||
:background-y background-y
|
[rn/view {:style {:padding-horizontal 20 :padding-top 20}}
|
||||||
:theme theme)
|
[:<>
|
||||||
props (utils/init-non-reactive-state)
|
[reply/view input-ref]
|
||||||
state (utils/init-reactive-state)]
|
[edit/view input-ref]]
|
||||||
[rn/view (when platform/ios? {:style {:z-index 1}})
|
[input set-ref theme]
|
||||||
[reanimated/view {:style (style/background opacity background-y window-height)}]
|
[:<>
|
||||||
[reanimated/view {:style (style/blur-container composer-default-height shared-values)}
|
[link-preview/view]
|
||||||
[quo/blur (style/blur-view theme)]]
|
[images/images-list]]
|
||||||
[:f> sheet-component extra-params props state shared-values]]))
|
[actions/view input-ref]]]))
|
||||||
|
|
||||||
(defn composer
|
|
||||||
[props]
|
|
||||||
(let [current-chat-id (rf/sub [:chats/current-chat-id])
|
|
||||||
able-to-send-message? (rf/sub [:chats/able-to-send-message?])]
|
|
||||||
(when-not (string/blank? current-chat-id)
|
|
||||||
(if able-to-send-message?
|
|
||||||
[:f> f-composer props]
|
|
||||||
[contact-requests.bottom-drawer/view
|
|
||||||
{:contact-id current-chat-id}]))))
|
|
||||||
|
@ -26,9 +26,7 @@
|
|||||||
[utils.worklets.chat.messenger.messages :as worklets]))
|
[utils.worklets.chat.messenger.messages :as worklets]))
|
||||||
|
|
||||||
(defonce ^:const distance-from-last-message 4)
|
(defonce ^:const distance-from-last-message 4)
|
||||||
(defonce ^:const loading-indicator-extra-spacing 250)
|
|
||||||
(defonce ^:const loading-indicator-page-loading-height 100)
|
(defonce ^:const loading-indicator-page-loading-height 100)
|
||||||
(defonce ^:const min-message-height 32)
|
|
||||||
|
|
||||||
(defn list-key-fn [{:keys [message-id value]}] (or message-id value))
|
(defn list-key-fn [{:keys [message-id value]}] (or message-id value))
|
||||||
(defn list-ref [ref] (reset! state/messages-list-ref ref))
|
(defn list-ref [ref] (reset! state/messages-list-ref ref))
|
||||||
@ -336,8 +334,7 @@
|
|||||||
|
|
||||||
(defn list-group-chat-header
|
(defn list-group-chat-header
|
||||||
[{:keys [chat-id invitation-admin]}]
|
[{:keys [chat-id invitation-admin]}]
|
||||||
[rn/view
|
[chat.group/group-chat-footer chat-id invitation-admin])
|
||||||
[chat.group/group-chat-footer chat-id invitation-admin]])
|
|
||||||
|
|
||||||
(defn render-fn
|
(defn render-fn
|
||||||
[{:keys [type value] :as message-data} _ _
|
[{:keys [type value] :as message-data} _ _
|
||||||
@ -410,10 +407,8 @@
|
|||||||
{:key-fn list-key-fn
|
{:key-fn list-key-fn
|
||||||
:ref list-ref
|
:ref list-ref
|
||||||
:bounces false
|
:bounces false
|
||||||
:header [:<>
|
:header (when (= (:chat-type chat) constants/private-group-chat-type)
|
||||||
[list-header insets able-to-send-message?]
|
[list-group-chat-header chat])
|
||||||
(when (= (:chat-type chat) constants/private-group-chat-type)
|
|
||||||
[list-group-chat-header chat])]
|
|
||||||
:footer [list-footer
|
:footer [list-footer
|
||||||
{:theme theme
|
{:theme theme
|
||||||
:chat chat
|
:chat chat
|
||||||
@ -433,15 +428,11 @@
|
|||||||
:distance-from-list-top distance-from-list-top})
|
:distance-from-list-top distance-from-list-top})
|
||||||
:on-end-reached #(list-on-end-reached distance-from-list-top)
|
:on-end-reached #(list-on-end-reached distance-from-list-top)
|
||||||
:on-scroll-to-index-failed identity
|
:on-scroll-to-index-failed identity
|
||||||
:scroll-indicator-insets {:top (if (:able-to-send-message? context)
|
|
||||||
(- composer.constants/composer-default-height 16)
|
|
||||||
0)
|
|
||||||
:right 1}
|
|
||||||
:keyboard-dismiss-mode :interactive
|
:keyboard-dismiss-mode :interactive
|
||||||
:keyboard-should-persist-taps :always
|
:keyboard-should-persist-taps :always
|
||||||
:on-scroll-begin-drag #(do
|
:on-scroll-begin-drag (fn []
|
||||||
(rf/dispatch [:chat.ui/set-input-focused false])
|
(rf/dispatch [:chat.ui/set-input-focused false])
|
||||||
(rn/dismiss-keyboard!))
|
(rn/dismiss-keyboard!))
|
||||||
:on-momentum-scroll-begin state/start-scrolling
|
:on-momentum-scroll-begin state/start-scrolling
|
||||||
:on-momentum-scroll-end state/stop-scrolling
|
:on-momentum-scroll-end state/stop-scrolling
|
||||||
:scroll-event-throttle 16
|
:scroll-event-throttle 16
|
||||||
@ -463,4 +454,5 @@
|
|||||||
:chat-screen-layout-calculations-complete?
|
:chat-screen-layout-calculations-complete?
|
||||||
chat-screen-layout-calculations-complete?})
|
chat-screen-layout-calculations-complete?})
|
||||||
:scroll-enabled (not recording?)
|
:scroll-enabled (not recording?)
|
||||||
:content-inset-adjustment-behavior :never}]]]))
|
:content-inset-adjustment-behavior :never
|
||||||
|
:scroll-indicator-insets {:right 1}}]]]))
|
||||||
|
@ -5,12 +5,13 @@
|
|||||||
|
|
||||||
(defn navigation-view
|
(defn navigation-view
|
||||||
[navigation-view-height pinned-banner-height]
|
[navigation-view-height pinned-banner-height]
|
||||||
{:top 0
|
{:top 0
|
||||||
:left 0
|
:left 0
|
||||||
:right 0
|
:right 0
|
||||||
:position :absolute
|
:position :absolute
|
||||||
:height (+ navigation-view-height pinned-banner-height)
|
:pointer-events :box-none
|
||||||
:z-index 1})
|
:height (+ navigation-view-height pinned-banner-height)
|
||||||
|
:z-index 1})
|
||||||
|
|
||||||
(defn animated-background-view
|
(defn animated-background-view
|
||||||
[background-opacity navigation-view-height]
|
[background-opacity navigation-view-height]
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
(ns status-im.contexts.chat.messenger.messages.scroll-to-bottom.style
|
||||||
|
(:require [status-im.contexts.shell.jump-to.constants :as shell.constants]))
|
||||||
|
|
||||||
|
(def shell-button-container
|
||||||
|
{:z-index 1
|
||||||
|
:bottom shell.constants/floating-shell-button-height})
|
||||||
|
|
||||||
|
(def scroll-to-bottom-button
|
||||||
|
{:position :absolute
|
||||||
|
:right 0
|
||||||
|
:left 0})
|
@ -0,0 +1,20 @@
|
|||||||
|
(ns status-im.contexts.chat.messenger.messages.scroll-to-bottom.view
|
||||||
|
(:require
|
||||||
|
[quo.core :as quo]
|
||||||
|
[react-native.core :as rn]
|
||||||
|
[status-im.contexts.chat.messenger.messages.scroll-to-bottom.style :as style]
|
||||||
|
[utils.re-frame :as rf]
|
||||||
|
[utils.worklets.chat.messenger.composer :as worklets]))
|
||||||
|
|
||||||
|
(defn button
|
||||||
|
[{:keys [chat-list-scroll-y]}]
|
||||||
|
(let [{window-height :height} (rn/get-window)
|
||||||
|
scroll-down-button-opacity (worklets/scroll-down-button-opacity
|
||||||
|
chat-list-scroll-y
|
||||||
|
false
|
||||||
|
window-height)]
|
||||||
|
[rn/view {:style style/shell-button-container}
|
||||||
|
[quo/floating-shell-button
|
||||||
|
{:scroll-to-bottom {:on-press #(rf/dispatch [:chat.ui/scroll-to-bottom])}}
|
||||||
|
style/scroll-to-bottom-button
|
||||||
|
scroll-down-button-opacity]]))
|
@ -1,19 +1,32 @@
|
|||||||
(ns status-im.contexts.chat.messenger.messages.view
|
(ns status-im.contexts.chat.messenger.messages.view
|
||||||
(:require
|
(:require
|
||||||
|
[clojure.string :as string]
|
||||||
[quo.theme :as quo.theme]
|
[quo.theme :as quo.theme]
|
||||||
[react-native.core :as rn]
|
[react-native.core :as rn]
|
||||||
[react-native.platform :as platform]
|
[react-native.platform :as platform]
|
||||||
[react-native.reanimated :as reanimated]
|
[react-native.reanimated :as reanimated]
|
||||||
[react-native.safe-area :as safe-area]
|
[react-native.safe-area :as safe-area]
|
||||||
[reagent.core :as reagent]
|
[reagent.core :as reagent]
|
||||||
[status-im.contexts.chat.messenger.composer.view :as composer.view]
|
[status-im.contexts.chat.messenger.composer.view :as composer]
|
||||||
|
[status-im.contexts.chat.messenger.messages.contact-requests.bottom-drawer.view :as
|
||||||
|
contact-requests.bottom-drawer]
|
||||||
[status-im.contexts.chat.messenger.messages.list.style :as style]
|
[status-im.contexts.chat.messenger.messages.list.style :as style]
|
||||||
[status-im.contexts.chat.messenger.messages.list.view :as list.view]
|
[status-im.contexts.chat.messenger.messages.list.view :as list.view]
|
||||||
[status-im.contexts.chat.messenger.messages.navigation.view :as messages.navigation]
|
[status-im.contexts.chat.messenger.messages.navigation.view :as messages.navigation]
|
||||||
|
[status-im.contexts.chat.messenger.messages.scroll-to-bottom.view :as scroll-to-bottom]
|
||||||
[status-im.contexts.chat.messenger.placeholder.view :as placeholder.view]
|
[status-im.contexts.chat.messenger.placeholder.view :as placeholder.view]
|
||||||
[status-im.feature-flags :as ff]
|
[status-im.feature-flags :as ff]
|
||||||
[utils.re-frame :as rf]))
|
[utils.re-frame :as rf]))
|
||||||
|
|
||||||
|
(defn- footer
|
||||||
|
[props]
|
||||||
|
(let [current-chat-id (rf/sub [:chats/current-chat-id])
|
||||||
|
able-to-send-message? (rf/sub [:chats/able-to-send-message?])]
|
||||||
|
(when-not (string/blank? current-chat-id)
|
||||||
|
(if able-to-send-message?
|
||||||
|
[composer/view props]
|
||||||
|
[contact-requests.bottom-drawer/view {:contact-id current-chat-id}]))))
|
||||||
|
|
||||||
(defn- chat-screen
|
(defn- chat-screen
|
||||||
[{:keys [insets] :as props}]
|
[{:keys [insets] :as props}]
|
||||||
(let [theme (quo.theme/use-theme)
|
(let [theme (quo.theme/use-theme)
|
||||||
@ -23,9 +36,11 @@
|
|||||||
[rn/keyboard-avoiding-view
|
[rn/keyboard-avoiding-view
|
||||||
{:style (style/keyboard-avoiding-container theme)
|
{:style (style/keyboard-avoiding-container theme)
|
||||||
:keyboard-vertical-offset (- (if platform/ios? alert-banners-top-margin 0) (:bottom insets))}
|
:keyboard-vertical-offset (- (if platform/ios? alert-banners-top-margin 0) (:bottom insets))}
|
||||||
[list.view/messages-list-content props]
|
[:<>
|
||||||
|
[list.view/messages-list-content props]
|
||||||
|
[scroll-to-bottom/button props]]
|
||||||
[messages.navigation/view props]
|
[messages.navigation/view props]
|
||||||
[composer.view/composer props]])))
|
[footer props]])))
|
||||||
|
|
||||||
(defn lazy-chat-screen
|
(defn lazy-chat-screen
|
||||||
[chat-screen-layout-calculations-complete? *screen-loaded?*]
|
[chat-screen-layout-calculations-complete? *screen-loaded?*]
|
||||||
|
@ -344,13 +344,6 @@
|
|||||||
(fn [[chat-id mentions]]
|
(fn [[chat-id mentions]]
|
||||||
(take 15 (get mentions chat-id))))
|
(take 15 (get mentions chat-id))))
|
||||||
|
|
||||||
(re-frame/reg-sub
|
|
||||||
:chat/input-with-mentions
|
|
||||||
:<- [:chats/current-chat-id]
|
|
||||||
:<- [:chat/inputs-with-mentions]
|
|
||||||
(fn [[chat-id cursor]]
|
|
||||||
(get cursor chat-id)))
|
|
||||||
|
|
||||||
(re-frame/reg-sub
|
(re-frame/reg-sub
|
||||||
:chats/link-previews-unfurled
|
:chats/link-previews-unfurled
|
||||||
:<- [:chat/link-previews]
|
:<- [:chat/link-previews]
|
||||||
|
@ -110,7 +110,7 @@
|
|||||||
(reg-root-key-sub :chat/memberships :chat/memberships)
|
(reg-root-key-sub :chat/memberships :chat/memberships)
|
||||||
(reg-root-key-sub :group-chat/invitations :group-chat/invitations)
|
(reg-root-key-sub :group-chat/invitations :group-chat/invitations)
|
||||||
(reg-root-key-sub :chats/mention-suggestions :chats/mention-suggestions)
|
(reg-root-key-sub :chats/mention-suggestions :chats/mention-suggestions)
|
||||||
(reg-root-key-sub :chat/inputs-with-mentions :chat/inputs-with-mentions)
|
|
||||||
(reg-root-key-sub :chats-home-list :chats-home-list)
|
(reg-root-key-sub :chats-home-list :chats-home-list)
|
||||||
(reg-root-key-sub :chats/recording? :chats/recording?)
|
(reg-root-key-sub :chats/recording? :chats/recording?)
|
||||||
(reg-root-key-sub :reactions/authors :reactions/authors)
|
(reg-root-key-sub :reactions/authors :reactions/authors)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user