[#11383] Status Updates : Add reactions and copy to clipboard
Signed-off-by: andrey <motor4ik@gmail.com>
This commit is contained in:
parent
cf911aa246
commit
7b46c445a7
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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])]))
|
||||
|
|
|
@ -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})))
|
||||
|
||||
|
|
|
@ -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}))
|
||||
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue