Reply component in chat input & quoted message redesign

Signed-off-by: Brian Sztamfater <brian@status.im>
This commit is contained in:
Brian Sztamfater 2022-07-21 18:43:17 -03:00
parent a08c7ff22e
commit 647dfc7e21
No known key found for this signature in database
GPG Key ID: 59EB921E0706B48F
11 changed files with 169 additions and 108 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 468 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 897 B

View File

@ -16,7 +16,7 @@
[rn/view {:style {:flex-direction :row}} [rn/view {:style {:flex-direction :row}}
[rn/view {} [rn/view {}
[icons/icon :tiny-icons/tiny-edit {:container-style {:margin-top 5}}]] [icons/icon :tiny-icons/tiny-edit {:container-style {:margin-top 5}}]]
[rn/view {:style (styles/reply-content)} [rn/view {:style (styles/reply-content-old)}
[quo/text {:weight :medium [quo/text {:weight :medium
:number-of-lines 1} :number-of-lines 1}
(i18n/label :t/editing-message)]] (i18n/label :t/editing-message)]]

View File

@ -9,14 +9,19 @@
[status-im.ethereum.stateofus :as stateofus] [status-im.ethereum.stateofus :as stateofus]
[status-im.ui.screens.chat.components.style :as styles] [status-im.ui.screens.chat.components.style :as styles]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[clojure.string :as string])) [clojure.string :as string]
[quo2.foundations.colors :as quo2.colors :refer [theme-colors]]
[quo2.components.button :as quo2.button]
[quo2.components.text :as quo2.text]
[status-im.ui.screens.chat.photos :as photos]
[status-im.constants :as constants]))
(def ^:private reply-symbol "↪ ") (def ^:private reply-symbol "↪ ")
(defn input-focus [text-input-ref] (defn input-focus [text-input-ref]
(some-> ^js (quo.react/current-ref text-input-ref) .focus)) (some-> ^js (quo.react/current-ref text-input-ref) .focus))
(defn format-author [contact-name] (defn format-author-old [contact-name]
(let [author (if (or (= (aget contact-name 0) "@") (let [author (if (or (= (aget contact-name 0) "@")
;; in case of replies ;; in case of replies
(= (aget contact-name 1) "@")) (= (aget contact-name 1) "@"))
@ -25,10 +30,24 @@
contact-name)] contact-name)]
(i18n/label :replying-to {:author author}))) (i18n/label :replying-to {:author author})))
(defn format-reply-author [from username current-public-key] (defn format-author [contact-name]
(let [author (if (or (= (aget contact-name 0) "@")
;; in case of replies
(= (aget contact-name 1) "@"))
(or (stateofus/username contact-name)
(subs contact-name 0 81))
contact-name)]
author))
(defn format-reply-author-old [from username current-public-key]
(or (and (= from current-public-key) (or (and (= from current-public-key)
(str reply-symbol (i18n/label :t/You))) (str reply-symbol (i18n/label :t/You)))
(str reply-symbol (format-author username)))) (str reply-symbol (format-author-old username))))
(defn format-reply-author [from username current-public-key]
(or (and (= from current-public-key)
(i18n/label :t/You))
(format-author username)))
(defn get-quoted-text-with-mentions [parsed-text] (defn get-quoted-text-with-mentions [parsed-text]
(string/join (string/join
@ -47,21 +66,62 @@
literal)) literal))
parsed-text))) parsed-text)))
(defn reply-message [{:keys [from]}] (defn reply-message-old [{:keys [from]}]
(let [contact-name @(re-frame/subscribe [:contacts/contact-name-by-identity from]) (let [contact-name @(re-frame/subscribe [:contacts/contact-name-by-identity from])
current-public-key @(re-frame/subscribe [:multiaccount/public-key])] current-public-key @(re-frame/subscribe [:multiaccount/public-key])]
[rn/view {:style {:flex-direction :row}} [rn/view {:style {:flex-direction :row}}
[rn/view {:style (styles/reply-content)} [rn/view {:style (styles/reply-content-old)}
[quo/text {:weight :medium [quo/text {:weight :medium
:number-of-lines 1 :number-of-lines 1
:style {:line-height 18}} :style {:line-height 18}}
(format-reply-author from contact-name current-public-key)]] (format-reply-author-old from contact-name current-public-key)]]
[rn/view [rn/view
[pressable/pressable {:on-press #(re-frame/dispatch [:chat.ui/cancel-message-reply]) [pressable/pressable {:on-press #(re-frame/dispatch [:chat.ui/cancel-message-reply])
:accessibility-label :cancel-message-reply} :accessibility-label :cancel-message-reply}
[icons/icon :main-icons/close-circle {:container-style (styles/close-button) [icons/icon :main-icons/close-circle {:container-style (styles/close-button)
:color (:icon-02 @quo.colors/theme)}]]]])) :color (:icon-02 @quo.colors/theme)}]]]]))
(defn reply-message [{:keys [from identicon content-type contentType parsed-text content]} in-chat-input?]
(let [contact-name @(re-frame/subscribe [:contacts/contact-name-by-identity from])
current-public-key @(re-frame/subscribe [:multiaccount/public-key])
content-type (or content-type contentType)]
[rn/view {:style {:flex-direction :row :height 24}}
[rn/view {:style (styles/reply-content)}
[icons/icon :main-icons/connector {:color (theme-colors quo2.colors/neutral-40 quo2.colors/neutral-60)
:container-style {:position :absolute :left 10 :bottom -4 :width 16 :height 16}}]
[rn/view {:style {:position :absolute :left 34 :right 54 :top 3 :flex-direction :row :align-items :center}}
[photos/member-photo from identicon 16]
[quo2.text/text {:weight :semi-bold
:size :paragraph-2
:number-of-lines 1
:style {:margin-left 4}}
(format-reply-author from contact-name current-public-key)]
[quo2.text/text {:number-of-lines 1
:size :label
:weight :regular
:style (merge {:ellipsize-mode :tail
:text-transform :none
:margin-left 4
:margin-top 2}
(when (or (= constants/content-type-image content-type)
(= constants/content-type-sticker content-type)
(= constants/content-type-audio content-type))
{:color (theme-colors quo2.colors/neutral-50 quo2.colors/neutral-40)}))}
(case (or content-type contentType)
constants/content-type-image "Image"
constants/content-type-sticker "Sticker"
constants/content-type-audio "Audio"
(get-quoted-text-with-mentions (or parsed-text (:parsed-text content))))]]]
(when in-chat-input?
[quo2.button/button {:width 24
:size 24
:type :outline
:accessibility-label :reply-cancel-button
:on-press #(re-frame/dispatch [:chat.ui/cancel-message-reply])}
[icons/icon :main-icons/close {:width 16
:height 16
:color (theme-colors quo2.colors/black quo2.colors/neutral-40)}]])]))
(defn send-image [images] (defn send-image [images]
[rn/view {:style (styles/reply-container-image)} [rn/view {:style (styles/reply-container-image)}
[rn/scroll-view {:horizontal true [rn/scroll-view {:horizontal true
@ -86,12 +146,27 @@
(when reply (when reply
(js/setTimeout #(input-focus text-input-ref) 250)))) (js/setTimeout #(input-focus text-input-ref) 250))))
(defn reply-message-wrapper-old [reply]
[rn/view {:style {:padding-horizontal 15
:border-top-width 1
:border-top-color (:ui-01 @quo.colors/theme)
:padding-vertical 8}}
[reply-message-old reply]])
(defn reply-message-wrapper [reply] (defn reply-message-wrapper [reply]
[rn/view {:style {:padding-horizontal 15 [rn/view {:style {:padding-horizontal 15
:border-top-width 1 :border-top-width 1
:border-top-color (:ui-01 @quo.colors/theme) :border-top-color (:ui-01 @quo.colors/theme)
:padding-vertical 8}} :padding-vertical 8}}
[reply-message reply]]) [reply-message reply true]])
(defn reply-message-auto-focus-wrapper-old [text-input-ref]
(let [had-reply (atom nil)]
(fn []
(let [reply @(re-frame/subscribe [:chats/reply-message])]
(focus-input-on-reply reply had-reply text-input-ref)
(when reply
[reply-message-wrapper-old reply])))))
(defn reply-message-auto-focus-wrapper [text-input-ref] (defn reply-message-auto-focus-wrapper [text-input-ref]
(let [had-reply (atom nil)] (let [had-reply (atom nil)]

View File

@ -87,11 +87,16 @@
(defn reply-container [] (defn reply-container []
{:flex-direction :row}) {:flex-direction :row})
(defn reply-content [] (defn reply-content-old []
{:padding-vertical 6 {:padding-vertical 6
:padding-horizontal 10 :padding-horizontal 10
:flex 1}) :flex 1})
(defn reply-content []
{:padding-horizontal 10
:flex 1
:flex-direction :row})
(defn contact-request-content [] (defn contact-request-content []
{:flex 1 {:flex 1
:flex-direction :row :flex-direction :row

View File

@ -87,27 +87,9 @@
timestamp-str]]))) timestamp-str]])))
(defview quoted-message (defview quoted-message
[_ {:keys [from parsed-text image]} current-public-key public? pinned] [_ reply]
(letsubs [contact-name [:contacts/contact-name-by-identity from]] [react/view {:style (style/quoted-message-container)}
[react/view {:style (style/quoted-message-container)} [components.reply/reply-message reply false]])
[react/view {:style style/quoted-message-author-container}
[chat.utils/format-reply-author
from
contact-name
current-public-key
(partial style/quoted-message-author (not pinned))
false]]
(if (and image
;; Disabling images for public-chats
(not public?))
[fast-image/fast-image {:style {:width 56
:height 56
:background-color :black
:border-radius 4}
:source {:uri image}}]
[react/text {:style (style/quoted-message-text)
:number-of-lines 5}
(components.reply/get-quoted-text-with-mentions parsed-text)])]))
(defn render-inline [message-text content-type acc {:keys [type literal destination]}] (defn render-inline [message-text content-type acc {:keys [type literal destination]}]
(case type (case type
@ -312,37 +294,40 @@
identicon identicon
from in-popover? timestamp-str] from in-popover? timestamp-str]
:as message} content {:keys [modal close-modal]}] :as message} content {:keys [modal close-modal]}]
[react/view {:style (style/message-wrapper message) (let [response-to (:response-to (:content message))]
:pointer-events :box-none [react/view {:style (style/message-wrapper message)
:accessibility-label :chat-item} :pointer-events :box-none
[react/view {:style (style/message-body) :accessibility-label :chat-item}
:pointer-events :box-none} (when (and (seq response-to) (:quoted-message message))
[react/view (style/message-author-userpic) [quoted-message response-to (:quoted-message message)])
(when last-in-group? [react/view {:style (style/message-body)
[react/touchable-highlight {:on-press #(do (when modal (close-modal)) :pointer-events :box-none}
(re-frame/dispatch [:chat.ui/show-profile from]))} [react/view (style/message-author-userpic)
[photos/member-photo from identicon]])] (when (or (and (seq response-to) (:quoted-message message)) last-in-group?)
[react/view {:style (style/message-author-wrapper)} [react/touchable-highlight {:on-press #(do (when modal (close-modal))
(when last-in-group? (re-frame/dispatch [:chat.ui/show-profile from]))}
[react/view {:style {:flex-direction :row :align-items :center}} [photos/member-photo from identicon]])]
[react/touchable-opacity {:style style/message-author-touchable [react/view {:style (style/message-author-wrapper)}
:disabled in-popover? (when (or (and (seq response-to) (:quoted-message message)) last-in-group?)
:on-press #(do (when modal (close-modal)) [react/view {:style {:flex-direction :row :align-items :center}}
(re-frame/dispatch [:chat.ui/show-profile from]))} [react/touchable-opacity {:style style/message-author-touchable
[message-author-name from {:modal modal}]] :disabled in-popover?
[react/text :on-press #(do (when modal (close-modal))
{:style (merge (re-frame/dispatch [:chat.ui/show-profile from]))}
{:padding-left 5 [message-author-name from {:modal modal}]]
:margin-top 2} [react/text
(style/message-timestamp-text)) {:style (merge
:accessibility-label :message-timestamp} {:padding-left 5
timestamp-str]]) :margin-top 2}
(style/message-timestamp-text))
:accessibility-label :message-timestamp}
timestamp-str]])
;;MESSAGE CONTENT ;;MESSAGE CONTENT
content content
[link-preview/link-preview-wrapper (:links (:content message)) false false]]] [link-preview/link-preview-wrapper (:links (:content message)) false false]]]
; delivery status ; delivery status
[react/view (style/delivery-status) [react/view (style/delivery-status)
[message-delivery-status message]]]) [message-delivery-status message]]]))
(def image-max-width 260) (def image-max-width 260)
(def image-max-height 192) (def image-max-height 192)
@ -449,7 +434,7 @@
(defn collapsible-text-message [_ _] (defn collapsible-text-message [_ _]
(let [collapsed? (reagent/atom false) (let [collapsed? (reagent/atom false)
show-timestamp? (reagent/atom false)] show-timestamp? (reagent/atom false)]
(fn [{:keys [content current-public-key public? pinned in-popover?] :as message} on-long-press modal] (fn [{:keys [content in-popover?] :as message} on-long-press modal]
[react/touchable-highlight [react/touchable-highlight
(when-not modal (when-not modal
{:on-press (fn [_] {:on-press (fn [_]
@ -466,11 +451,8 @@
[message-timestamp message show-timestamp?] [message-timestamp message show-timestamp?]
[react/view {:style (style/message-view message)} [react/view {:style (style/message-view message)}
[react/view {:style (style/message-view-content)} [react/view {:style (style/message-view-content)}
(let [response-to (:response-to content)] [react/view
[react/view [render-parsed-text-with-message-status message (:parsed-text content)]]]]]])))
(when (and (seq response-to) (:quoted-message message))
[quoted-message response-to (:quoted-message message) current-public-key public? pinned])
[render-parsed-text-with-message-status message (:parsed-text content)]])]]]])))
(defmethod ->message constants/content-type-text (defmethod ->message constants/content-type-text
[message {:keys [on-long-press modal] :as reaction-picker}] [message {:keys [on-long-press modal] :as reaction-picker}]
@ -494,39 +476,36 @@
(defmethod ->message constants/content-type-emoji [] (defmethod ->message constants/content-type-emoji []
(let [show-timestamp? (reagent/atom false)] (let [show-timestamp? (reagent/atom false)]
(fn [{:keys [content current-public-key outgoing public? pinned in-popover? message-pin-enabled] :as message} (fn [{:keys [content pinned in-popover? message-pin-enabled] :as message}
{:keys [on-long-press modal] {:keys [on-long-press modal]
:as reaction-picker}] :as reaction-picker}]
(let [response-to (:response-to content)] [message-content-wrapper message
[message-content-wrapper message [react/touchable-highlight (when-not modal
[react/touchable-highlight (when-not modal {:disabled in-popover?
{:disabled in-popover? :on-press (fn []
:on-press (fn [] (react/dismiss-keyboard!)
(react/dismiss-keyboard!) (reset! show-timestamp? true))
(reset! show-timestamp? true)) :delay-long-press 100
:delay-long-press 100 :on-long-press (fn []
:on-long-press (fn [] (on-long-press
(on-long-press (concat
(concat [{:on-press #(re-frame/dispatch [:chat.ui/reply-to-message message])
[{:on-press #(re-frame/dispatch [:chat.ui/reply-to-message message]) :id :reply
:id :reply :label (i18n/label :t/message-reply)}
:label (i18n/label :t/message-reply)} {:on-press #(react/copy-to-clipboard (get content :text))
{:on-press #(react/copy-to-clipboard (get content :text)) :id :copy
:id :copy :label (i18n/label :t/sharing-copy-to-clipboard)}]
:label (i18n/label :t/sharing-copy-to-clipboard)}] (when message-pin-enabled [{:on-press #(pin-message message)
(when message-pin-enabled [{:on-press #(pin-message message) :label (if pinned (i18n/label :t/unpin) (i18n/label :t/pin))}]))))})
:label (if pinned (i18n/label :t/unpin) (i18n/label :t/pin))}]))))}) [react/view style/message-view-wrapper
[react/view style/message-view-wrapper [message-timestamp message show-timestamp?]
[message-timestamp message show-timestamp?] [react/view (style/message-view message)
[react/view (style/message-view message) [react/view {:style (style/message-view-content)}
[react/view {:style (style/message-view-content)} [react/view {:style (style/style-message-text)}
[react/view {:style (style/style-message-text)} [react/text {:style (style/emoji-message message)}
(when (and (seq response-to) (:quoted-message message)) (:text content)]]
[quoted-message response-to (:quoted-message message) outgoing current-public-key public? pinned]) [message-status message]]]]]
[react/text {:style (style/emoji-message message)} reaction-picker])))
(:text content)]]
[message-status message]]]]]
reaction-picker]))))
(defmethod ->message constants/content-type-sticker (defmethod ->message constants/content-type-sticker
[{:keys [content from outgoing in-popover?] [{:keys [content from outgoing in-popover?]

View File

@ -24,10 +24,15 @@
[memo-photo-rend photo-path size accessibility-label (colors/dark?)]) [memo-photo-rend photo-path size accessibility-label (colors/dark?)])
;; We optionally pass identicon for perfomance reason, so it does not have to be calculated for each message ;; We optionally pass identicon for perfomance reason, so it does not have to be calculated for each message
(defn member-photo [pub-key identicon] (defn member-photo
(let [path @(re-frame/subscribe [:chats/photo-path pub-key identicon])] ([pub-key]
[photo path {:size style/default-size (member-photo pub-key nil))
:accessibility-label :member-photo}])) ([pub-key identicon]
(member-photo pub-key identicon style/default-size))
([pub-key identicon size]
(let [path @(re-frame/subscribe [:chats/photo-path pub-key identicon])]
[photo path {:size size
:accessibility-label :member-photo}])))
(defn account-photo [account] (defn account-photo [account]
(let [path (multiaccounts/displayed-photo account)] (let [path (multiaccounts/displayed-photo account)]

View File

@ -209,12 +209,9 @@
:text-align-vertical :center}) :text-align-vertical :center})
(defn quoted-message-container [] (defn quoted-message-container []
{:margin-bottom 6 {:margin-bottom 6
:padding-bottom 6 :margin-top 5
:border-bottom-color colors/black-transparent :padding-horizontal 15})
:border-bottom-width 2
:border-bottom-left-radius 2
:border-bottom-right-radius 2})
(def quoted-message-author-container (def quoted-message-author-container
{:flex-direction :row {:flex-direction :row

View File

@ -588,7 +588,7 @@
:on-update-inset on-update} :on-update-inset on-update}
[react/view [react/view
[edit/edit-message-auto-focus-wrapper text-input-ref] [edit/edit-message-auto-focus-wrapper text-input-ref]
[reply/reply-message-auto-focus-wrapper text-input-ref] [reply/reply-message-auto-focus-wrapper-old text-input-ref]
;; We set the key so we can force a re-render as ;; We set the key so we can force a re-render as
;; it does not rely on ratom but just atoms ;; it does not rely on ratom but just atoms
^{:key (str @components/chat-input-key "chat-input")} ^{:key (str @components/chat-input-key "chat-input")}