[#11383] Status Updates : Add reactions and copy to clipboard

Signed-off-by: andrey <motor4ik@gmail.com>
This commit is contained in:
andrey 2020-11-09 12:53:05 +01:00
parent cf911aa246
commit 7b46c445a7
No known key found for this signature in database
GPG Key ID: 89B67245FD2F0272
8 changed files with 145 additions and 87 deletions

View File

@ -4,22 +4,28 @@
[status-im.utils.fx :as fx]
[taoensso.timbre :as log]
[status-im.transport.message.protocol :as message.protocol]
[status-im.data-store.reactions :as data-store.reactions]))
[status-im.data-store.reactions :as data-store.reactions]
[status-im.chat.models :as chat]))
(defn process-reactions
[reactions new-reactions]
;; TODO(Ferossgp): handling own reaction in subscription could be expensive,
;; for better performance we can here separate own reaction into 2 maps
(reduce
(fn [acc {:keys [chat-id message-id emoji-id emoji-reaction-id retracted]
:as reaction}]
;; NOTE(Ferossgp): For a better performance, better to not keep in db all retracted reactions
;; retraction will always come after the reaction so there shouldn't be a conflict
(if retracted
(update-in acc [chat-id message-id emoji-id] dissoc emoji-reaction-id)
(assoc-in acc [chat-id message-id emoji-id emoji-reaction-id] reaction)))
reactions
new-reactions))
(defn update-reaction [acc retracted chat-id message-id emoji-id emoji-reaction-id reaction]
;; NOTE(Ferossgp): For a better performance, better to not keep in db all retracted reactions
;; retraction will always come after the reaction so there shouldn't be a conflict
(if retracted
(update-in acc [chat-id message-id emoji-id] dissoc emoji-reaction-id)
(assoc-in acc [chat-id message-id emoji-id emoji-reaction-id] reaction)))
(defn process-reactions [chats]
(fn [reactions new-reactions]
;; TODO(Ferossgp): handling own reaction in subscription could be expensive,
;; for better performance we can here separate own reaction into 2 maps
(reduce
(fn [acc {:keys [chat-id message-id emoji-id emoji-reaction-id retracted]
:as reaction}]
(cond-> (update-reaction acc retracted chat-id message-id emoji-id emoji-reaction-id reaction)
(get-in chats [chat-id :profile-public-key])
(update-reaction retracted chat/timeline-chat-id message-id emoji-id emoji-reaction-id reaction)))
reactions
new-reactions)))
(defn- earlier-than-deleted-at?
[{:keys [db]} {:keys [chat-id clock-value]}]
@ -30,7 +36,7 @@
(fx/defn receive-signal
[{:keys [db] :as cofx} reactions]
(let [reactions (filter (partial earlier-than-deleted-at? cofx) reactions)]
{:db (update db :reactions process-reactions reactions)}))
{:db (update db :reactions (process-reactions (:chats db)) reactions)}))
(fx/defn load-more-reactions
[{:keys [db] :as cofx} cursor]
@ -55,7 +61,7 @@
(not= session-id
(get-in db [:pagination-info current-chat-id :messages-initialized?]))))
(let [reactions-w-chat-id (map #(assoc % :chat-id chat-id) reactions)]
{:db (update db :reactions process-reactions reactions-w-chat-id)})))
{:db (update db :reactions (process-reactions (:chats db)) reactions-w-chat-id)})))
;; Send reactions
@ -63,20 +69,20 @@
(fx/defn send-emoji-reaction
{:events [::send-emoji-reaction]}
[{{:keys [current-chat-id] :as db} :db :as cofx} reaction]
[{{:keys [current-chat-id]} :db :as cofx} reaction]
(message.protocol/send-reaction cofx
(assoc reaction :chat-id current-chat-id)))
(update reaction :chat-id #(or % current-chat-id))))
(fx/defn send-retract-emoji-reaction
{:events [::send-emoji-reaction-retraction]}
[{{:keys [current-chat-id reactions] :as db} :db :as cofx} reaction]
[{{:keys [current-chat-id]} :db :as cofx} reaction]
(message.protocol/send-retract-reaction cofx
(assoc reaction :chat-id current-chat-id)))
(update reaction :chat-id #(or % current-chat-id))))
(fx/defn receive-one
{:events [::receive-one]}
[{:keys [db]} reaction]
{:db (update db :reactions process-reactions [reaction])})
{:db (update db :reactions (process-reactions (:chats db)) [reaction])})
(defn message-reactions [current-public-key reactions]
(reduce

View File

@ -715,10 +715,10 @@
:<- [:multiaccount/public-key]
:<- [::reactions]
:<- [:chats/current-chat-id]
(fn [[current-public-key reactions chat-id] [_ message-id]]
(fn [[current-public-key reactions current-chat-id] [_ message-id chat-id]]
(models.reactions/message-reactions
current-public-key
(get-in reactions [chat-id message-id]))))
(get-in reactions [(or chat-id current-chat-id) message-id]))))
(re-frame/reg-sub
:chats/messages-gaps

View File

@ -38,7 +38,8 @@
visible (reagent/atom false)
actions (reagent/atom nil)
position (reagent/atom {})]
(fn [{:keys [message reactions outgoing outgoing-status render send-emoji retract-emoji picker-on-open picker-on-close]}]
(fn [{:keys [message reactions outgoing outgoing-status render send-emoji retract-emoji picker-on-open
picker-on-close timeline]}]
(let [own-reactions (reduce (fn [acc {:keys [emoji-id own]}]
(if own (conj acc emoji-id) acc))
[] reactions)
@ -70,7 +71,7 @@
(and outgoing (= outgoing-status :sent)))
(reset! actions act)
(get-picker-position ref on-open)))}]]
[reaction-row/message-reactions message reactions]]
[reaction-row/message-reactions message reactions timeline]]
[rn/modal {:visible @visible
:on-request-close on-close
:on-show (fn []
@ -86,6 +87,7 @@
:on-close on-close
:actions @actions
:own-reactions own-reactions
:timeline timeline
:send-emoji (fn [emoji]
(on-close)
(js/setTimeout #(on-emoji-press emoji)

View File

@ -18,8 +18,8 @@
(def translate-x 27)
(def translate-y -24)
(defn picker [{:keys [outgoing actions own-reactions on-close send-emoji]}]
[rn/view {:style (styles/container-style {:outgoing outgoing})}
(defn picker [{:keys [outgoing actions own-reactions on-close send-emoji timeline]}]
[rn/view {:style (styles/container-style {:outgoing outgoing :timeline timeline})}
[rn/view {:style (styles/reactions-picker-row)}
(doall
(for [[id resource] constants/reactions
@ -55,7 +55,8 @@
actions :actions
send-emoji :sendEmoji
own-reactions :ownReactions
children :children}
children :children
timeline :timeline}
(bean/bean props)
{bottom-inset :bottom} (safe-area/use-safe-area)
{window-height :height} (rn/use-window-dimensions)
@ -66,7 +67,7 @@
full-height (+ message-height picker-height top)
max-height (- window-height bottom-inset tabbar-height text-input-height)
top-delta (max 0 (- full-height max-height))
translation-x (if outgoing
translation-x (if (and outgoing (not timeline))
translate-x
(* -1 translate-x))]
(reagent/as-element
@ -93,12 +94,14 @@
[animated/view {:on-layout on-picker-layout
:pointer-events :box-none
:style (merge (styles/picker-wrapper-style {:display-photo? display-photo
:outgoing outgoing})
:timeline timeline
:outgoing (and outgoing (not timeline))})
{:opacity animation
:transform [{:translateX (animated/mix spring translation-x 0)}
{:translateY (animated/mix spring translate-y 0)}
{:scale (animated/mix spring scale 1)}]})}
[picker {:outgoing outgoing
[picker {:outgoing (and outgoing (not timeline))
:timeline timeline
:actions actions
:on-close on-close
:own-reactions (into #{} (js->clj own-reactions))

View File

@ -15,9 +15,9 @@
:style (styles/reaction-quantity-style {:own own})}
quantity]])
(defn message-reactions [message reactions]
(defn message-reactions [message reactions timeline]
(when (seq reactions)
[rn/view {:style (styles/reactions-row message)}
[rn/view {:style (styles/reactions-row message timeline)}
(for [emoji-reaction reactions]
^{:key (str emoji-reaction)}
[reaction message emoji-reaction])]))

View File

@ -3,7 +3,7 @@
[status-im.ui.screens.chat.styles.photos :as photos]
[status-im.ui.components.colors :as components.colors]))
(defn picker-wrapper-style [{:keys [display-photo? outgoing]}]
(defn picker-wrapper-style [{:keys [display-photo? outgoing timeline]}]
(merge {:flex-direction :row
:flex 1
:padding-top 4
@ -11,19 +11,23 @@
(if outgoing
{:justify-content :flex-end}
{:justify-content :flex-start})
(if display-photo?
{:padding-left (+ 16 photos/default-size)}
{:padding-left 8})))
(when-not timeline
(if display-photo?
{:padding-left (+ 16 photos/default-size)}
{:padding-left 8}))))
(defn container-style [{:keys [outgoing]}]
(defn container-style [{:keys [outgoing timeline]}]
(merge {:border-top-left-radius 16
:border-top-right-radius 16
:border-bottom-right-radius 16
:border-bottom-left-radius 16
:background-color (:ui-background @colors/theme)}
(if outgoing
{:border-top-right-radius 4}
{:border-top-left-radius 4})))
(if timeline
{:border-top-left-radius 16
:border-top-right-radius 4}
(if outgoing
{:border-top-right-radius 4}
{:border-top-left-radius 4}))))
(defn reactions-picker-row []
{:flex-direction :row
@ -62,13 +66,13 @@
colors/white
(:text-01 @colors/theme))})
(defn reactions-row [{:keys [outgoing display-photo?]}]
(defn reactions-row [{:keys [outgoing display-photo?]} timeline]
(merge {:flex-direction :row
:padding-right 8}
(if outgoing
(if (and outgoing (not timeline))
{:justify-content :flex-end}
{:justify-content :flex-start})
(if display-photo?
(if (or display-photo? timeline)
{:padding-left (+ 16 photos/default-size)}
{:padding-left 8})))

View File

@ -18,7 +18,11 @@
[status-im.ui.components.tabs :as tabs]
[status-im.utils.contenthash :as contenthash]
[status-im.multiaccounts.core :as multiaccounts]
[status-im.ui.screens.chat.message.link-preview :as link-preview]))
[status-im.ui.screens.chat.message.reactions :as reactions]
[status-im.chat.models.reactions :as models.reactions]
[status-im.ui.screens.chat.components.reply :as components.reply]
[status-im.ui.screens.chat.message.link-preview :as link-preview]
[status-im.chat.models :as chat]))
(defonce messages-list-ref (atom nil))
(def image-max-dimension 260)
@ -54,9 +58,17 @@
:justify-content :center}
[icons/icon :main-icons/close-circle {:color colors/white-persist}]]])])))
(defn on-long-press-fn [on-long-press content image]
(on-long-press
(when-not image
[{:on-press #(react/copy-to-clipboard
(components.reply/get-quoted-text-with-mentions
(get content :parsed-text)))
:label (i18n/label :t/sharing-copy-to-clipboard)}])))
(defn image-message []
(let [visible (reagent/atom false)]
(fn [{:keys [content] :as message}]
(fn [{:keys [content] :as message} on-long-press]
[:<>
[preview/preview-image {:message (assoc message :cant-be-replied true)
:visible @visible
@ -65,49 +77,80 @@
(reagent/flush))}]
[react/touchable-highlight {:on-press (fn [_]
(reset! visible true)
(react/dismiss-keyboard!))}
(react/dismiss-keyboard!))
:on-long-press #(on-long-press-fn on-long-press content true)}
[message-content-image (:image content) false]]])))
(defn message-item [{:keys [content-type content from last-in-group? timestamp identicon outgoing] :as message} timeline? account]
[react/view (when last-in-group?
{:padding-bottom 8
:margin-bottom 8
:border-bottom-width 1
:border-bottom-color colors/gray-lighter})
[react/view {:padding-vertical 8
:flex-direction :row
:background-color (when (and timeline? outgoing) colors/blue-light)
:padding-horizontal 16}
[react/touchable-highlight {:on-press #(re-frame/dispatch [:chat.ui/show-profile-without-adding-contact from])}
[react/view {:padding-top 2 :padding-right 8}
(if outgoing
[photos/member-identicon (multiaccounts/displayed-photo account)]
[photos/member-identicon identicon])]]
[react/view {:flex 1}
[react/view {:flex-direction :row
:justify-content :space-between}
[react/touchable-highlight {:on-press #(re-frame/dispatch [:chat.ui/show-profile-without-adding-contact from])}
(defn message-item [timeline? account]
(fn [{:keys [content-type content from timestamp identicon outgoing] :as message}
{:keys [modal on-long-press close-modal]}]
[react/view (merge {:padding-vertical 8
:flex-direction :row
:background-color (if (and timeline? outgoing) colors/blue-light colors/white)
:padding-horizontal 16}
(when modal
{:border-radius 16}))
[react/touchable-highlight
{:on-press #(do (when modal (close-modal))
(re-frame/dispatch [:chat.ui/show-profile-without-adding-contact from]))}
[react/view {:padding-top 2 :padding-right 8}
(if outgoing
[message/message-my-name {:profile? true :you? false}]
[message/message-author-name from {:profile? true}])]
[react/text {:style {:font-size 10 :color colors/gray}}
(datetime/time-ago (datetime/to-date timestamp))]]
(if (= content-type constants/content-type-image)
[image-message message]
[react/view
[message/render-parsed-text (assoc message :outgoing false) (:parsed-text content)]
[link-preview/link-preview-wrapper (:links content) outgoing]])]]])
[photos/member-identicon (multiaccounts/displayed-photo account)]
[photos/member-identicon identicon])]]
[react/view {:flex 1}
[react/view {:flex-direction :row
:justify-content :space-between}
[react/touchable-highlight
{:on-press #(do (when modal (close-modal))
(re-frame/dispatch [:chat.ui/show-profile-without-adding-contact from]))}
(if outgoing
[message/message-my-name {:profile? true :you? false}]
[message/message-author-name from {:profile? true}])]
[react/text {:style {:font-size 10 :color colors/gray}}
(datetime/time-ago (datetime/to-date timestamp))]]
[react/view
(if (= content-type constants/content-type-image)
[image-message message on-long-press]
[react/touchable-highlight (when-not modal
{:on-long-press #(on-long-press-fn on-long-press content false)})
[message/render-parsed-text (assoc message :outgoing false) (:parsed-text content)]])
[link-preview/link-preview-wrapper (:links content) outgoing]]]]))
(defn render-message [timeline? account]
(fn [{:keys [type] :as message} idx]
(if (= type :datemark)
nil
(if (= type :gap)
(if timeline?
nil
[gap/gap message idx messages-list-ref])
; message content
[message-item message timeline? account]))))
[(fn []
(if (= type :datemark)
nil
(if (= type :gap)
(if timeline?
nil
[gap/gap message idx messages-list-ref])
; message content
(let [chat-id (chat/profile-chat-topic (:from message))]
[react/view (merge {:accessibility-label :chat-item}
(when (:last-in-group? message)
{:padding-bottom 8
:margin-bottom 8
:border-bottom-width 1
:border-bottom-color colors/gray-lighter}))
[reactions/with-reaction-picker
{:message message
:timeline true
:reactions @(re-frame/subscribe [:chats/message-reactions (:message-id message)])
:picker-on-open (fn [])
:picker-on-close (fn [])
:send-emoji (fn [{:keys [emoji-id]}]
(re-frame/dispatch [::models.reactions/send-emoji-reaction
{:message-id (:message-id message)
:chat-id chat-id
:emoji-id emoji-id}]))
:retract-emoji (fn [{:keys [emoji-id emoji-reaction-id]}]
(re-frame/dispatch [::models.reactions/send-emoji-reaction-retraction
{:message-id (:message-id message)
:chat-id chat-id
:emoji-id emoji-id
:emoji-reaction-id emoji-reaction-id}]))
:render (message-item timeline? account)}]]))))]))
(def state (reagent/atom {:tab :timeline}))

View File

@ -2,7 +2,7 @@
"_comment": "DO NOT EDIT THIS FILE BY HAND. USE 'scripts/update-status-go.sh <tag>' instead",
"owner": "status-im",
"repo": "status-go",
"version": "v0.63.7",
"commit-sha1": "4026841dc1516865f385e0a6c2b57fead2aad773",
"src-sha256": "1sr9aaf9by2lzfphakqqpcir101zfgxa59wag2grsb2077k0gxj5"
"version": "v0.63.8",
"commit-sha1": "e8dbc66227b98fcf91b0626c7747c58f3d8b31fb",
"src-sha256": "16952l1zyj8bqzgb979w274d55nsihr5l0papvwkdk5i2xyps9q7"
}