Show message timestamps on tap, remove from bubbles

Signed-off-by: andrey <motor4ik@gmail.com>
This commit is contained in:
Ajay Sivan 2021-12-14 12:39:10 +05:30 committed by andrey
parent b18d7b6a2a
commit f3533c5f99
No known key found for this signature in database
GPG Key ID: 89B67245FD2F0272
4 changed files with 172 additions and 113 deletions

View File

@ -181,7 +181,7 @@
:message-id @current-player-message-id})) :message-id @current-player-message-id}))
nil) nil)
(defview message-content [{:keys [audio audio-duration-ms message-id outgoing]} timestamp-view] (defview message-content [{:keys [audio audio-duration-ms message-id outgoing]}]
(letsubs [state (reagent/atom nil) (letsubs [state (reagent/atom nil)
progress (reagent/atom 0) progress (reagent/atom 0)
progress-anim (anim/create-value 0) progress-anim (anim/create-value 0)
@ -223,5 +223,4 @@
(#{:playing :paused :seeking} (:general @state)) @progress (#{:playing :paused :seeking} (:general @state)) @progress
:else (:duration @state)) :else (:duration @state))
s (quot time 1000)] s (quot time 1000)]
(gstring/format "%02d:%02d" (quot s 60) (mod s 60)))] (gstring/format "%02d:%02d" (quot s 60) (mod s 60)))]]]))))
timestamp-view]]))))

View File

@ -24,42 +24,64 @@
[status-im.ui.screens.chat.components.reply :as components.reply] [status-im.ui.screens.chat.components.reply :as components.reply]
[status-im.ui.screens.chat.message.link-preview :as link-preview] [status-im.ui.screens.chat.message.link-preview :as link-preview]
[status-im.ui.screens.communities.icon :as communities.icon] [status-im.ui.screens.communities.icon :as communities.icon]
[status-im.ui.components.animation :as animation]
[status-im.chat.models.pin-message :as models.pin-message]) [status-im.chat.models.pin-message :as models.pin-message])
(:require-macros [status-im.utils.views :refer [defview letsubs]])) (:require-macros [status-im.utils.views :refer [defview letsubs]]))
(defn message-timestamp-anim
[anim-opacity show-timestamp?]
(animation/start
(animation/anim-sequence
[(animation/timing
anim-opacity
{:toValue 1
:duration 100
:easing (.-ease ^js animation/easing)
:useNativeDriver true})
(animation/timing
anim-opacity
{:toValue 0
:delay 2000
:duration 100
:easing (.-ease ^js animation/easing)
:useNativeDriver true})]) #(reset! show-timestamp? false)))
(defview mention-element [from] (defview mention-element [from]
(letsubs [contact-name [:contacts/contact-name-by-identity from]] (letsubs [contact-name [:contacts/contact-name-by-identity from]]
contact-name)) contact-name))
(def edited-at-text (str " ⌫ " (i18n/label :t/edited))) (def edited-at-text (str " ⌫ " (i18n/label :t/edited)))
(defn message-status [{:keys [outgoing content outgoing-status pinned edited-at in-popover?]}]
(when-not in-popover? ;; We keep track if showing this message in a list in pin-limit-popover
[react/view
{:align-self :flex-end
:position :absolute
:bottom 9 ; 6 Bubble bottom, 3 message baseline
(if (:rtl? content) :left :right) 0
:flex-direction :row
:align-items :flex-end}
(when outgoing
[icons/icon (case outgoing-status
:sending :tiny-icons/tiny-pending
:sent :tiny-icons/tiny-sent
:not-sent :tiny-icons/tiny-warning
:delivered :tiny-icons/tiny-delivered
:tiny-icons/tiny-pending)
{:width 16
:height 12
:color (if pinned colors/gray colors/white)
:accessibility-label (name outgoing-status)}])
(when edited-at [react/text {:style (style/message-status-text (and outgoing (not pinned)))} edited-at-text])]))
(defn message-timestamp (defn message-timestamp
([message] [{:keys [timestamp-str in-popover?] :as message} show-timestamp?]
[message-timestamp message false]) (when-not in-popover? ;; We keep track if showing this message in a list in pin-limit-popover
([{:keys [timestamp-str outgoing content outgoing-status pinned edited-at in-popover?]} justify-timestamp?] (let [anim-opacity (animation/create-value 0)]
(when-not in-popover? ;; We keep track if showing this message in a list in pin-limit-popover [react/animated-view {:style (style/message-timestamp-wrapper message) :opacity anim-opacity}
[react/view (when justify-timestamp? (when @show-timestamp? (message-timestamp-anim anim-opacity show-timestamp?))
{:align-self :flex-end [react/text {:style (style/message-timestamp-text)}
:position :absolute timestamp-str]])))
:bottom 9 ; 6 Bubble bottom, 3 message baseline
(if (:rtl? content) :left :right) 0
:flex-direction :row
:align-items :flex-end})
(when (and outgoing justify-timestamp?)
[icons/icon (case outgoing-status
:sending :tiny-icons/tiny-pending
:sent :tiny-icons/tiny-sent
:not-sent :tiny-icons/tiny-warning
:delivered :tiny-icons/tiny-delivered
:tiny-icons/tiny-pending)
{:width 16
:height 12
:color (if pinned colors/gray colors/white)
:accessibility-label (name outgoing-status)}])
[react/text {:style (style/message-timestamp-text (and outgoing (not pinned)))}
(str
timestamp-str
(when edited-at edited-at-text))]])))
(defview quoted-message (defview quoted-message
[_ {:keys [from parsed-text image]} outgoing current-public-key public? pinned] [_ {:keys [from parsed-text image]} outgoing current-public-key public? pinned]
@ -168,19 +190,19 @@
(defn render-parsed-text [message tree] (defn render-parsed-text [message tree]
(reduce (fn [acc e] (render-block message acc e)) [:<>] tree)) (reduce (fn [acc e] (render-block message acc e)) [:<>] tree))
(defn render-parsed-text-with-timestamp [{:keys [timestamp-str outgoing edited-at in-popover?] :as message} tree] (defn render-parsed-text-with-message-status [{:keys [outgoing edited-at in-popover?] :as message} tree]
(let [elements (render-parsed-text message tree) (let [elements (render-parsed-text message tree)
timestamp [react/text {:style (style/message-timestamp-placeholder)} message-status [react/text {:style (style/message-status-placeholder)}
(str (if (and outgoing (not in-popover?)) " " " ") (when-not in-popover? (str timestamp-str (when edited-at edited-at-text))))] (str (if (and outgoing (not in-popover?)) " " " ") (when (and (not in-popover?) edited-at) edited-at-text))]
last-element (peek elements)] last-element (peek elements)]
;; Using `nth` here as slightly faster than `first`, roughly 30% ;; Using `nth` here as slightly faster than `first`, roughly 30%
;; It's worth considering pure js structures for this code path as ;; It's worth considering pure js structures for this code path as
;; it's perfomance critical ;; it's perfomance critical
(if (= react/text-class (nth last-element 0)) (if (= react/text-class (nth last-element 0))
;; Append timestamp to last text ;; Append message status to last text
(conj (pop elements) (conj last-element timestamp)) (conj (pop elements) (conj last-element message-status))
;; Append timestamp to new block ;; Append message status to new block
(conj elements timestamp)))) (conj elements message-status))))
(defn unknown-content-type (defn unknown-content-type
[{:keys [outgoing content-type content] :as message}] [{:keys [outgoing content-type content] :as message}]
@ -409,7 +431,8 @@
(defn collapsible-text-message [{:keys [mentioned]} _] (defn collapsible-text-message [{:keys [mentioned]} _]
(let [collapsed? (reagent/atom false) (let [collapsed? (reagent/atom false)
collapsible? (reagent/atom false)] collapsible? (reagent/atom false)
show-timestamp? (reagent/atom false)]
(fn [{:keys [content outgoing current-public-key public? pinned in-popover?] :as message} on-long-press modal] (fn [{:keys [content outgoing current-public-key public? pinned in-popover?] :as message} on-long-press modal]
(let [max-height (when-not (or outgoing modal) (let [max-height (when-not (or outgoing modal)
(if @collapsible? (if @collapsible?
@ -418,7 +441,8 @@
[react/touchable-highlight [react/touchable-highlight
(when-not modal (when-not modal
{:on-press (fn [_] {:on-press (fn [_]
(react/dismiss-keyboard!)) (react/dismiss-keyboard!)
(reset! show-timestamp? true))
:delay-long-press 100 :delay-long-press 100
:on-long-press (fn [] :on-long-press (fn []
(if @collapsed? (if @collapsed?
@ -426,40 +450,41 @@
(js/setTimeout #(on-long-press-fn on-long-press message content) 200)) (js/setTimeout #(on-long-press-fn on-long-press message content) 200))
(on-long-press-fn on-long-press message content))) (on-long-press-fn on-long-press message content)))
:disabled in-popover?}) :disabled in-popover?})
[react/view {:style (style/message-view message)} [react/view (style/message-view-wrapper outgoing)
[react/view {:style (style/message-view-content) [message-timestamp message show-timestamp?]
:max-height max-height} [react/view {:style (style/message-view message)}
(let [response-to (:response-to content)] [react/view {:style (style/message-view-content)
[react/view {:on-layout :max-height max-height}
#(when (and (> (.-nativeEvent.layout.height ^js %) max-message-height-px) (let [response-to (:response-to content)]
(not @collapsible?) [react/view {:on-layout
(not outgoing) #(when (and (> (.-nativeEvent.layout.height ^js %) max-message-height-px)
(not modal)) (not @collapsible?)
(reset! collapsed? true) (not outgoing)
(reset! collapsible? true))} (not modal))
(when (and (seq response-to) (:quoted-message message)) (reset! collapsed? true)
[quoted-message response-to (:quoted-message message) outgoing current-public-key public? pinned]) (reset! collapsible? true))}
[render-parsed-text-with-timestamp message (:parsed-text content)]]) (when (and (seq response-to) (:quoted-message message))
(when-not @collapsed? [quoted-message response-to (:quoted-message message) outgoing current-public-key public? pinned])
[message-timestamp message true]) [render-parsed-text-with-message-status message (:parsed-text content)]])
(when (and @collapsible? (not modal)) (when-not @collapsible? [message-status message])
(if @collapsed? (when (and @collapsible? (not modal))
(let [color (if pinned colors/pin-background (if mentioned colors/mentioned-background colors/blue-light))] (if @collapsed?
[react/touchable-highlight (let [color (if pinned colors/pin-background (if mentioned colors/mentioned-background colors/blue-light))]
{:on-press #(swap! collapsed? not) [react/touchable-highlight
:style {:position :absolute :bottom 0 :left 0 :right 0 :height 72}} {:on-press #(swap! collapsed? not)
[react/linear-gradient {:colors [(str color "00") color] :style {:position :absolute :bottom 0 :left 0 :right 0 :height 72}}
:start {:x 0 :y 0} :end {:x 0 :y 0.9}} [react/linear-gradient {:colors [(str color "00") color]
[react/view {:height 72 :align-self :center :justify-content :flex-end :start {:x 0 :y 0} :end {:x 0 :y 0.9}}
:padding-bottom 10} [react/view {:height 72 :align-self :center :justify-content :flex-end
[react/view (style/collapse-button) :padding-bottom 10}
[icons/icon :main-icons/dropdown [react/view (style/collapse-button)
{:color colors/white}]]]]]) [icons/icon :main-icons/dropdown
[react/touchable-highlight {:on-press #(swap! collapsed? not) {:color colors/white}]]]]])
:style {:align-self :center :margin 5}} [react/touchable-highlight {:on-press #(swap! collapsed? not)
[react/view (style/collapse-button) :style {:align-self :center :margin 5}}
[icons/icon :main-icons/dropdown-up [react/view (style/collapse-button)
{:color colors/white}]]]))]]])))) [icons/icon :main-icons/dropdown-up
{:color colors/white}]]]))]]]]))))
(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}]
@ -484,33 +509,37 @@
(defmethod ->message constants/content-type-emoji (defmethod ->message constants/content-type-emoji
[{:keys [content current-public-key outgoing public? pinned in-popover? message-pin-enabled] :as message} {:keys [on-long-press modal] [{:keys [content current-public-key outgoing public? pinned in-popover? message-pin-enabled] :as message} {:keys [on-long-press modal]
:as reaction-picker}] :as reaction-picker}]
(let [response-to (:response-to content)] (let [response-to (:response-to content)
[message-content-wrapper message show-timestamp? (reagent/atom false)]
[react/touchable-highlight (when-not modal (fn [] [message-content-wrapper message
{:disabled in-popover? [react/touchable-highlight (when-not modal
:on-press (fn [] {:disabled in-popover?
(react/dismiss-keyboard!)) :on-press (fn []
:delay-long-press 100 (react/dismiss-keyboard!)
:on-long-press (fn [] (reset! show-timestamp? true))
(on-long-press :delay-long-press 100
(concat :on-long-press (fn []
[{:on-press #(re-frame/dispatch [:chat.ui/reply-to-message message]) (on-long-press
:id :reply (concat
:label (i18n/label :t/message-reply)} [{:on-press #(re-frame/dispatch [:chat.ui/reply-to-message message])
{:on-press #(react/copy-to-clipboard (get content :text)) :id :reply
:id :copy :label (i18n/label :t/message-reply)}
:label (i18n/label :t/sharing-copy-to-clipboard)}] {:on-press #(react/copy-to-clipboard (get content :text))
(when message-pin-enabled [{:on-press #(pin-message message) :id :copy
:label (if pinned (i18n/label :t/unpin) (i18n/label :t/pin))}]))))}) :label (i18n/label :t/sharing-copy-to-clipboard)}]
[react/view (style/message-view message) (when message-pin-enabled [{:on-press #(pin-message message)
[react/view {:style (style/message-view-content)} :label (if pinned (i18n/label :t/unpin) (i18n/label :t/pin))}]))))})
[react/view {:style (style/style-message-text outgoing)} [react/view (style/message-view-wrapper outgoing)
(when (and (seq response-to) (:quoted-message message)) [message-timestamp message show-timestamp?]
[quoted-message response-to (:quoted-message message) outgoing current-public-key public? pinned]) [react/view (style/message-view message)
[react/text {:style (style/emoji-message message)} [react/view {:style (style/message-view-content)}
(:text content)]] [react/view {:style (style/style-message-text outgoing)}
[message-timestamp message]]]] (when (and (seq response-to) (:quoted-message message))
reaction-picker])) [quoted-message response-to (:quoted-message message) outgoing current-public-key public? pinned])
[react/text {:style (style/emoji-message message)}
(: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?]
@ -556,14 +585,19 @@
(defmethod ->message constants/content-type-audio [message {:keys [on-long-press modal] (defmethod ->message constants/content-type-audio [message {:keys [on-long-press modal]
:as reaction-picker}] :as reaction-picker}]
[message-content-wrapper message (let [show-timestamp? (reagent/atom false)]
[react/touchable-highlight (when-not modal (fn [] [message-content-wrapper message
{:on-long-press [react/touchable-highlight (when-not modal
(fn [] (on-long-press []))}) {:on-long-press
[react/view {:style (style/message-view message) :accessibility-label :audio-message} (fn [] (on-long-press []))
[react/view {:style (style/message-view-content)} :on-press (fn []
[message.audio/message-content message [message-timestamp message false]]]]] (reset! show-timestamp? true))})
reaction-picker]) [react/view (style/message-view-wrapper (:outgoing message))
[message-timestamp message show-timestamp?]
[react/view {:style (style/message-view message) :accessibility-label :audio-message}
[react/view {:style (style/message-view-content)}
[message.audio/message-content message] [message-status message]]]]]
reaction-picker])))
(defmethod ->message :default [message] (defmethod ->message :default [message]
[message-content-wrapper message [message-content-wrapper message

View File

@ -43,5 +43,5 @@
:justify-content :space-between}) :justify-content :space-between})
(defn timestamp [outgoing] (defn timestamp [outgoing]
(merge (message.style/message-timestamp-text outgoing) (merge (message.style/audio-message-timestamp-text outgoing)
{:margin-left 40})) {:margin-left 40}))

View File

@ -29,11 +29,33 @@
(def message-timestamp (def message-timestamp
{:font-size 10}) {:font-size 10})
(defn message-timestamp-placeholder (defn message-status-placeholder
[] []
(merge message-timestamp {:opacity 0 :color "rgba(0,0,0,0)"})) (merge message-timestamp {:opacity 0 :color "rgba(0,0,0,0)"}))
(defn message-timestamp-text (defn message-timestamp-wrapper [{:keys [last-in-group? outgoing group-chat]}]
{:justify-content :center
(if outgoing :margin-right :margin-left) 12 ;; horizontal margin is only required at the adjust side of the message.
:margin-top (if (and last-in-group?
(or outgoing
(not group-chat)))
16
0) ;; Add gap between message groups
:opacity 0})
(defn message-timestamp-text []
(merge message-timestamp
{:color colors/gray
:text-align :center}))
(defn message-status-text [outgoing]
{:font-size 10
:line-height 10
:color (if outgoing
colors/white-transparent-70-persist
colors/gray)})
(defn audio-message-timestamp-text
[outgoing] [outgoing]
(merge message-timestamp (merge message-timestamp
{:line-height 10 {:line-height 10
@ -44,7 +66,7 @@
(defn message-wrapper [{:keys [outgoing in-popover?]}] (defn message-wrapper [{:keys [outgoing in-popover?]}]
(if (and outgoing (not in-popover?)) (if (and outgoing (not in-popover?))
{:margin-left 96} {:margin-left 96}
{:margin-right 52})) {:margin-right 96}))
(defn message-author-wrapper (defn message-author-wrapper
[outgoing display-photo? in-popover?] [outgoing display-photo? in-popover?]
@ -156,10 +178,10 @@
:padding-left 3}) :padding-left 3})
(defn emoji-message (defn emoji-message
[{:keys [incoming-group]}] [{:keys [incoming-group outgoing]}]
{:font-size 28 {:font-size 28
:line-height 34 ;TODO: Smaller crops the icon on the top :line-height 34 ;TODO: Smaller crops the icon on the top
:margin-right 12 :margin-right (if outgoing 18 0) ;; Margin to display outgoing message status
:margin-top (if incoming-group 4 0)}) :margin-top (if incoming-group 4 0)})
(defn collapse-button [] (defn collapse-button []
@ -171,6 +193,10 @@
:shadow-color (:shadow-01 @colors/theme) :shadow-color (:shadow-01 @colors/theme)
:shadow-offset {:width 0 :height 4}}) :shadow-offset {:width 0 :height 4}})
(defn message-view-wrapper [outgoing]
{:align-self :flex-end
:flex-direction (if outgoing :row :row-reverse)})
(defn message-view (defn message-view
[{:keys [content-type outgoing group-chat last-in-group? mentioned pinned]}] [{:keys [content-type outgoing group-chat last-in-group? mentioned pinned]}]
(merge (merge