Show message timestamps on tap, remove from bubbles
Signed-off-by: andrey <motor4ik@gmail.com>
This commit is contained in:
parent
b18d7b6a2a
commit
f3533c5f99
|
@ -181,7 +181,7 @@
|
|||
:message-id @current-player-message-id}))
|
||||
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)
|
||||
progress (reagent/atom 0)
|
||||
progress-anim (anim/create-value 0)
|
||||
|
@ -223,5 +223,4 @@
|
|||
(#{:playing :paused :seeking} (:general @state)) @progress
|
||||
:else (:duration @state))
|
||||
s (quot time 1000)]
|
||||
(gstring/format "%02d:%02d" (quot s 60) (mod s 60)))]
|
||||
timestamp-view]]))))
|
||||
(gstring/format "%02d:%02d" (quot s 60) (mod s 60)))]]]))))
|
||||
|
|
|
@ -24,42 +24,64 @@
|
|||
[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.communities.icon :as communities.icon]
|
||||
[status-im.ui.components.animation :as animation]
|
||||
[status-im.chat.models.pin-message :as models.pin-message])
|
||||
(: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]
|
||||
(letsubs [contact-name [:contacts/contact-name-by-identity from]]
|
||||
contact-name))
|
||||
|
||||
(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
|
||||
([message]
|
||||
[message-timestamp message false])
|
||||
([{:keys [timestamp-str outgoing content outgoing-status pinned edited-at in-popover?]} justify-timestamp?]
|
||||
(when-not in-popover? ;; We keep track if showing this message in a list in pin-limit-popover
|
||||
[react/view (when justify-timestamp?
|
||||
{: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 (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))]])))
|
||||
[{:keys [timestamp-str in-popover?] :as message} show-timestamp?]
|
||||
(when-not in-popover? ;; We keep track if showing this message in a list in pin-limit-popover
|
||||
(let [anim-opacity (animation/create-value 0)]
|
||||
[react/animated-view {:style (style/message-timestamp-wrapper message) :opacity anim-opacity}
|
||||
(when @show-timestamp? (message-timestamp-anim anim-opacity show-timestamp?))
|
||||
[react/text {:style (style/message-timestamp-text)}
|
||||
timestamp-str]])))
|
||||
|
||||
(defview quoted-message
|
||||
[_ {:keys [from parsed-text image]} outgoing current-public-key public? pinned]
|
||||
|
@ -168,19 +190,19 @@
|
|||
(defn render-parsed-text [message 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)
|
||||
timestamp [react/text {:style (style/message-timestamp-placeholder)}
|
||||
(str (if (and outgoing (not in-popover?)) " " " ") (when-not in-popover? (str timestamp-str (when edited-at edited-at-text))))]
|
||||
message-status [react/text {:style (style/message-status-placeholder)}
|
||||
(str (if (and outgoing (not in-popover?)) " " " ") (when (and (not in-popover?) edited-at) edited-at-text))]
|
||||
last-element (peek elements)]
|
||||
;; Using `nth` here as slightly faster than `first`, roughly 30%
|
||||
;; It's worth considering pure js structures for this code path as
|
||||
;; it's perfomance critical
|
||||
(if (= react/text-class (nth last-element 0))
|
||||
;; Append timestamp to last text
|
||||
(conj (pop elements) (conj last-element timestamp))
|
||||
;; Append timestamp to new block
|
||||
(conj elements timestamp))))
|
||||
;; Append message status to last text
|
||||
(conj (pop elements) (conj last-element message-status))
|
||||
;; Append message status to new block
|
||||
(conj elements message-status))))
|
||||
|
||||
(defn unknown-content-type
|
||||
[{:keys [outgoing content-type content] :as message}]
|
||||
|
@ -409,7 +431,8 @@
|
|||
|
||||
(defn collapsible-text-message [{:keys [mentioned]} _]
|
||||
(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]
|
||||
(let [max-height (when-not (or outgoing modal)
|
||||
(if @collapsible?
|
||||
|
@ -418,7 +441,8 @@
|
|||
[react/touchable-highlight
|
||||
(when-not modal
|
||||
{:on-press (fn [_]
|
||||
(react/dismiss-keyboard!))
|
||||
(react/dismiss-keyboard!)
|
||||
(reset! show-timestamp? true))
|
||||
:delay-long-press 100
|
||||
:on-long-press (fn []
|
||||
(if @collapsed?
|
||||
|
@ -426,40 +450,41 @@
|
|||
(js/setTimeout #(on-long-press-fn on-long-press message content) 200))
|
||||
(on-long-press-fn on-long-press message content)))
|
||||
:disabled in-popover?})
|
||||
[react/view {:style (style/message-view message)}
|
||||
[react/view {:style (style/message-view-content)
|
||||
:max-height max-height}
|
||||
(let [response-to (:response-to content)]
|
||||
[react/view {:on-layout
|
||||
#(when (and (> (.-nativeEvent.layout.height ^js %) max-message-height-px)
|
||||
(not @collapsible?)
|
||||
(not outgoing)
|
||||
(not modal))
|
||||
(reset! collapsed? true)
|
||||
(reset! collapsible? true))}
|
||||
(when (and (seq response-to) (:quoted-message message))
|
||||
[quoted-message response-to (:quoted-message message) outgoing current-public-key public? pinned])
|
||||
[render-parsed-text-with-timestamp message (:parsed-text content)]])
|
||||
(when-not @collapsed?
|
||||
[message-timestamp message true])
|
||||
(when (and @collapsible? (not modal))
|
||||
(if @collapsed?
|
||||
(let [color (if pinned colors/pin-background (if mentioned colors/mentioned-background colors/blue-light))]
|
||||
[react/touchable-highlight
|
||||
{:on-press #(swap! collapsed? not)
|
||||
:style {:position :absolute :bottom 0 :left 0 :right 0 :height 72}}
|
||||
[react/linear-gradient {:colors [(str color "00") color]
|
||||
:start {:x 0 :y 0} :end {:x 0 :y 0.9}}
|
||||
[react/view {:height 72 :align-self :center :justify-content :flex-end
|
||||
:padding-bottom 10}
|
||||
[react/view (style/collapse-button)
|
||||
[icons/icon :main-icons/dropdown
|
||||
{:color colors/white}]]]]])
|
||||
[react/touchable-highlight {:on-press #(swap! collapsed? not)
|
||||
:style {:align-self :center :margin 5}}
|
||||
[react/view (style/collapse-button)
|
||||
[icons/icon :main-icons/dropdown-up
|
||||
{:color colors/white}]]]))]]]))))
|
||||
[react/view (style/message-view-wrapper outgoing)
|
||||
[message-timestamp message show-timestamp?]
|
||||
[react/view {:style (style/message-view message)}
|
||||
[react/view {:style (style/message-view-content)
|
||||
:max-height max-height}
|
||||
(let [response-to (:response-to content)]
|
||||
[react/view {:on-layout
|
||||
#(when (and (> (.-nativeEvent.layout.height ^js %) max-message-height-px)
|
||||
(not @collapsible?)
|
||||
(not outgoing)
|
||||
(not modal))
|
||||
(reset! collapsed? true)
|
||||
(reset! collapsible? true))}
|
||||
(when (and (seq response-to) (:quoted-message message))
|
||||
[quoted-message response-to (:quoted-message message) outgoing current-public-key public? pinned])
|
||||
[render-parsed-text-with-message-status message (:parsed-text content)]])
|
||||
(when-not @collapsible? [message-status message])
|
||||
(when (and @collapsible? (not modal))
|
||||
(if @collapsed?
|
||||
(let [color (if pinned colors/pin-background (if mentioned colors/mentioned-background colors/blue-light))]
|
||||
[react/touchable-highlight
|
||||
{:on-press #(swap! collapsed? not)
|
||||
:style {:position :absolute :bottom 0 :left 0 :right 0 :height 72}}
|
||||
[react/linear-gradient {:colors [(str color "00") color]
|
||||
:start {:x 0 :y 0} :end {:x 0 :y 0.9}}
|
||||
[react/view {:height 72 :align-self :center :justify-content :flex-end
|
||||
:padding-bottom 10}
|
||||
[react/view (style/collapse-button)
|
||||
[icons/icon :main-icons/dropdown
|
||||
{:color colors/white}]]]]])
|
||||
[react/touchable-highlight {:on-press #(swap! collapsed? not)
|
||||
:style {:align-self :center :margin 5}}
|
||||
[react/view (style/collapse-button)
|
||||
[icons/icon :main-icons/dropdown-up
|
||||
{:color colors/white}]]]))]]]]))))
|
||||
|
||||
(defmethod ->message constants/content-type-text
|
||||
[message {:keys [on-long-press modal] :as reaction-picker}]
|
||||
|
@ -484,33 +509,37 @@
|
|||
(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]
|
||||
:as reaction-picker}]
|
||||
(let [response-to (:response-to content)]
|
||||
[message-content-wrapper message
|
||||
[react/touchable-highlight (when-not modal
|
||||
{:disabled in-popover?
|
||||
:on-press (fn []
|
||||
(react/dismiss-keyboard!))
|
||||
:delay-long-press 100
|
||||
:on-long-press (fn []
|
||||
(on-long-press
|
||||
(concat
|
||||
[{:on-press #(re-frame/dispatch [:chat.ui/reply-to-message message])
|
||||
:id :reply
|
||||
:label (i18n/label :t/message-reply)}
|
||||
{:on-press #(react/copy-to-clipboard (get content :text))
|
||||
:id :copy
|
||||
:label (i18n/label :t/sharing-copy-to-clipboard)}]
|
||||
(when message-pin-enabled [{:on-press #(pin-message message)
|
||||
:label (if pinned (i18n/label :t/unpin) (i18n/label :t/pin))}]))))})
|
||||
[react/view (style/message-view message)
|
||||
[react/view {:style (style/message-view-content)}
|
||||
[react/view {:style (style/style-message-text outgoing)}
|
||||
(when (and (seq response-to) (:quoted-message message))
|
||||
[quoted-message response-to (:quoted-message message) outgoing current-public-key public? pinned])
|
||||
[react/text {:style (style/emoji-message message)}
|
||||
(:text content)]]
|
||||
[message-timestamp message]]]]
|
||||
reaction-picker]))
|
||||
(let [response-to (:response-to content)
|
||||
show-timestamp? (reagent/atom false)]
|
||||
(fn [] [message-content-wrapper message
|
||||
[react/touchable-highlight (when-not modal
|
||||
{:disabled in-popover?
|
||||
:on-press (fn []
|
||||
(react/dismiss-keyboard!)
|
||||
(reset! show-timestamp? true))
|
||||
:delay-long-press 100
|
||||
:on-long-press (fn []
|
||||
(on-long-press
|
||||
(concat
|
||||
[{:on-press #(re-frame/dispatch [:chat.ui/reply-to-message message])
|
||||
:id :reply
|
||||
:label (i18n/label :t/message-reply)}
|
||||
{:on-press #(react/copy-to-clipboard (get content :text))
|
||||
:id :copy
|
||||
:label (i18n/label :t/sharing-copy-to-clipboard)}]
|
||||
(when message-pin-enabled [{:on-press #(pin-message message)
|
||||
:label (if pinned (i18n/label :t/unpin) (i18n/label :t/pin))}]))))})
|
||||
[react/view (style/message-view-wrapper outgoing)
|
||||
[message-timestamp message show-timestamp?]
|
||||
[react/view (style/message-view message)
|
||||
[react/view {:style (style/message-view-content)}
|
||||
[react/view {:style (style/style-message-text outgoing)}
|
||||
(when (and (seq response-to) (:quoted-message message))
|
||||
[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
|
||||
[{:keys [content from outgoing in-popover?]
|
||||
|
@ -556,14 +585,19 @@
|
|||
|
||||
(defmethod ->message constants/content-type-audio [message {:keys [on-long-press modal]
|
||||
:as reaction-picker}]
|
||||
[message-content-wrapper message
|
||||
[react/touchable-highlight (when-not modal
|
||||
{:on-long-press
|
||||
(fn [] (on-long-press []))})
|
||||
[react/view {:style (style/message-view message) :accessibility-label :audio-message}
|
||||
[react/view {:style (style/message-view-content)}
|
||||
[message.audio/message-content message [message-timestamp message false]]]]]
|
||||
reaction-picker])
|
||||
(let [show-timestamp? (reagent/atom false)]
|
||||
(fn [] [message-content-wrapper message
|
||||
[react/touchable-highlight (when-not modal
|
||||
{:on-long-press
|
||||
(fn [] (on-long-press []))
|
||||
:on-press (fn []
|
||||
(reset! show-timestamp? true))})
|
||||
[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]
|
||||
[message-content-wrapper message
|
||||
|
|
|
@ -43,5 +43,5 @@
|
|||
:justify-content :space-between})
|
||||
|
||||
(defn timestamp [outgoing]
|
||||
(merge (message.style/message-timestamp-text outgoing)
|
||||
(merge (message.style/audio-message-timestamp-text outgoing)
|
||||
{:margin-left 40}))
|
|
@ -29,11 +29,33 @@
|
|||
(def message-timestamp
|
||||
{:font-size 10})
|
||||
|
||||
(defn message-timestamp-placeholder
|
||||
(defn message-status-placeholder
|
||||
[]
|
||||
(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]
|
||||
(merge message-timestamp
|
||||
{:line-height 10
|
||||
|
@ -44,7 +66,7 @@
|
|||
(defn message-wrapper [{:keys [outgoing in-popover?]}]
|
||||
(if (and outgoing (not in-popover?))
|
||||
{:margin-left 96}
|
||||
{:margin-right 52}))
|
||||
{:margin-right 96}))
|
||||
|
||||
(defn message-author-wrapper
|
||||
[outgoing display-photo? in-popover?]
|
||||
|
@ -156,10 +178,10 @@
|
|||
:padding-left 3})
|
||||
|
||||
(defn emoji-message
|
||||
[{:keys [incoming-group]}]
|
||||
[{:keys [incoming-group outgoing]}]
|
||||
{:font-size 28
|
||||
: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)})
|
||||
|
||||
(defn collapse-button []
|
||||
|
@ -171,6 +193,10 @@
|
|||
:shadow-color (:shadow-01 @colors/theme)
|
||||
: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
|
||||
[{:keys [content-type outgoing group-chat last-in-group? mentioned pinned]}]
|
||||
(merge
|
||||
|
|
Loading…
Reference in New Issue