diff --git a/resources/images/icons2/20x20/add-reaction@2x.png b/resources/images/icons2/20x20/add-reaction@2x.png index 936fef2660..207c3b4e25 100644 Binary files a/resources/images/icons2/20x20/add-reaction@2x.png and b/resources/images/icons2/20x20/add-reaction@2x.png differ diff --git a/resources/images/icons2/20x20/add-reaction@3x.png b/resources/images/icons2/20x20/add-reaction@3x.png index 71d0bf4946..4ed6e3db35 100644 Binary files a/resources/images/icons2/20x20/add-reaction@3x.png and b/resources/images/icons2/20x20/add-reaction@3x.png differ diff --git a/src/quo2/components/reactions/reaction.cljs b/src/quo2/components/reactions/reaction.cljs index fa5fbfa20e..df3be50cfc 100644 --- a/src/quo2/components/reactions/reaction.cljs +++ b/src/quo2/components/reactions/reaction.cljs @@ -13,7 +13,7 @@ {:on-press on-press :accessibility-label :emoji-reaction-add :style (style/add-reaction)} - [icons/icon :i/add + [icons/icon :i/add-reaction {:size 20 :color (if dark? colors/white @@ -21,10 +21,11 @@ (defn reaction "Add your emoji as a param here" - [{:keys [emoji clicks neutral? on-press accessibility-label]}] + [{:keys [emoji clicks neutral? on-press accessibility-label on-long-press]}] (let [numeric-value (int clicks)] [rn/touchable-opacity {:on-press on-press + :on-long-press on-long-press :accessibility-label accessibility-label :style (style/reaction neutral?)} [icons/icon emoji diff --git a/src/quo2/components/tabs/tabs.cljs b/src/quo2/components/tabs/tabs.cljs index e0e19f5fae..8824927a5b 100644 --- a/src/quo2/components/tabs/tabs.cljs +++ b/src/quo2/components/tabs/tabs.cljs @@ -6,7 +6,8 @@ [react-native.masked-view :as masked-view] [reagent.core :as reagent] [utils.collection :as utils.collection] - [utils.number :as utils.number])) + [utils.number :as utils.number] + [react-native.gesture :as gesture])) (def default-tab-size 32) (def unread-count-offset 3) @@ -131,7 +132,8 @@ style size blur? - override-theme] + override-theme + in-scroll-view?] :or {fade-end-percentage fade-end-percentage fade-end? false scrollable? false @@ -142,7 +144,9 @@ [rn/view {:style {:margin-top (- (dec unread-count-offset))}} [masked-view-wrapper {:fade-end-percentage (get @fading :fade-end-percentage) :fade-end? fade-end?} - [rn/flat-list + [(if in-scroll-view? + gesture/flat-list + rn/flat-list) (merge (dissoc props :default-active diff --git a/src/status_im/chat/models/reactions.cljs b/src/status_im/chat/models/reactions.cljs index a8ae004d25..dab685e28d 100644 --- a/src/status_im/chat/models/reactions.cljs +++ b/src/status_im/chat/models/reactions.cljs @@ -4,7 +4,8 @@ [status-im.data-store.reactions :as data-store.reactions] [status-im.transport.message.protocol :as message.protocol] [utils.re-frame :as rf] - [taoensso.timbre :as log])) + [taoensso.timbre :as log] + [utils.transforms :as transforms])) (defn update-reaction [acc retracted chat-id message-id emoji-id emoji-reaction-id reaction] @@ -94,3 +95,42 @@ acc)) [] reactions)) + +(defn- <-rpc + [{compressed-key :compressedKey + emoji-id :emojiId + :keys [from]}] + {:compressed-key compressed-key + :emoji-id emoji-id + :from from}) + + +(defn- format-response + [response] + (->> (transforms/js->clj response) + (map <-rpc) + (group-by :emoji-id))) + +(rf/defn save-emoji-reaction-details + {:events [:chat/save-emoji-reaction-details]} + [{:keys [db]} message-reactions long-pressed-emoji] + {:db (assoc db + :chat/reactions-authors + {:reaction-authors-list message-reactions + :selected-reaction long-pressed-emoji})}) + +(rf/defn clear-emoji-reaction-details + {:events [:chat/clear-emoji-reaction-author-details]} + [{:keys [db]} message-reactions] + {:db (update db dissoc :chat/reactions-authors)}) + +(rf/defn emoji-reactions-by-message-id + {:events [:chat.ui/emoji-reactions-by-message-id]} + [{:keys [db]} {:keys [message-id long-pressed-emoji]}] + {:json-rpc/call [{:method "wakuext_emojiReactionsByChatIDMessageID" + :params [(:current-chat-id db) message-id] + :js-response true + :on-error #(log/error "failed to fetch emoji reaction by message-id: " + {:message-id message-id :error %}) + :on-success #(rf/dispatch [:chat/save-emoji-reaction-details + (format-response %) long-pressed-emoji])}]}) diff --git a/src/status_im/profile/core.cljs b/src/status_im/profile/core.cljs index 37e4fa2587..902b0d3a88 100644 --- a/src/status_im/profile/core.cljs +++ b/src/status_im/profile/core.cljs @@ -89,8 +89,9 @@ {:events [:chat.ui/show-profile]} [{:keys [db]} identity ens-name] (let [my-public-key (get-in db [:multiaccount :public-key])] - (when (not= my-public-key identity) + (if (not= my-public-key identity) {:db (-> db (assoc :contacts/identity identity) (assoc :contacts/ens-name ens-name)) - :dispatch [:contacts/build-contact identity ens-name true]}))) + :dispatch [:contacts/build-contact identity ens-name true]} + {:dispatch [:navigate-to :my-profile]}))) diff --git a/src/status_im2/common/bottom_sheet/styles.cljs b/src/status_im2/common/bottom_sheet/styles.cljs index d22e5d72ba..917415b7fe 100644 --- a/src/status_im2/common/bottom_sheet/styles.cljs +++ b/src/status_im2/common/bottom_sheet/styles.cljs @@ -13,7 +13,7 @@ :margin-vertical 8}) (defn sheet - [{:keys [top bottom]} window-height override-theme shell?] + [{:keys [top bottom]} window-height override-theme padding-bottom-override shell?] {:position :absolute :max-height (- window-height top 20) :z-index 1 @@ -22,9 +22,9 @@ :right 0 :border-top-left-radius 20 :border-top-right-radius 20 - :overflow :hidden + :overflow (when shell? :hidden) :flex 1 - :padding-bottom (max 20 bottom) + :padding-bottom (or padding-bottom-override (max 20 bottom)) :background-color (if shell? :transparent (colors/theme-colors colors/white colors/neutral-90 override-theme))}) diff --git a/src/status_im2/common/bottom_sheet/view.cljs b/src/status_im2/common/bottom_sheet/view.cljs index e791a408ae..08dce17aec 100644 --- a/src/status_im2/common/bottom_sheet/view.cljs +++ b/src/status_im2/common/bottom_sheet/view.cljs @@ -13,7 +13,9 @@ (def timing-options #js {:duration duration}) (defn hide - [translate-y bg-opacity window-height] + [translate-y bg-opacity window-height on-close] + (when (fn? on-close) + (on-close)) ;; it will be better to use animation callback, but it doesn't work ;; so we have to use timeout, also we add 50ms for safety (js/setTimeout #(rf/dispatch [:bottom-sheet-hidden]) (+ duration 50)) @@ -29,7 +31,7 @@ (def gesture-values (atom {})) (defn get-sheet-gesture - [translate-y bg-opacity window-height] + [translate-y bg-opacity window-height on-close] (-> (gesture/gesture-pan) (gesture/on-start (fn [_] @@ -47,17 +49,22 @@ (fn [_] (if (< (:dy @gesture-values) 0) (show translate-y bg-opacity) - (hide translate-y bg-opacity window-height)))))) + (hide translate-y bg-opacity window-height on-close)))))) (defn view - [{:keys [hide? insets]} {:keys [content override-theme selected-item shell?]}] + [{:keys [hide? insets]} + {:keys [content override-theme selected-item padding-bottom-override on-close shell?]}] (let [{window-height :height} (rn/get-window) bg-opacity (reanimated/use-shared-value 0) translate-y (reanimated/use-shared-value window-height) - sheet-gesture (get-sheet-gesture translate-y bg-opacity window-height)] - (rn/use-effect #(if hide? (hide translate-y bg-opacity window-height) (show translate-y bg-opacity)) - [hide?]) - (hooks/use-back-handler #(do (rf/dispatch [:hide-bottom-sheet]) true)) + sheet-gesture (get-sheet-gesture translate-y bg-opacity window-height on-close)] + (rn/use-effect + #(if hide? (hide translate-y bg-opacity window-height on-close) (show translate-y bg-opacity)) + [hide?]) + (hooks/use-back-handler #(do (when (fn? on-close) + (on-close)) + (rf/dispatch [:hide-bottom-sheet]) + true)) [rn/view {:flex 1} ;; backdrop [rn/touchable-without-feedback {:on-press #(rf/dispatch [:hide-bottom-sheet])} @@ -70,7 +77,7 @@ [reanimated/view {:style (reanimated/apply-animations-to-style {:transform [{:translateY translate-y}]} - (styles/sheet insets window-height override-theme shell?))} + (styles/sheet insets window-height override-theme padding-bottom-override shell?))} (when shell? [blur/ios-view diff --git a/src/status_im2/contexts/chat/messages/content/reactions/view.cljs b/src/status_im2/contexts/chat/messages/content/reactions/view.cljs index 6e69265492..288fc301fd 100644 --- a/src/status_im2/contexts/chat/messages/content/reactions/view.cljs +++ b/src/status_im2/contexts/chat/messages/content/reactions/view.cljs @@ -5,31 +5,69 @@ [utils.re-frame :as rf] [status-im2.contexts.chat.messages.drawers.view :as drawers])) +(defn- on-press + [own message-id emoji-id emoji-reaction-id] + (if own + (rf/dispatch [:models.reactions/send-emoji-reaction-retraction + {:message-id message-id + :emoji-id emoji-id + :emoji-reaction-id emoji-reaction-id}]) + (rf/dispatch [:models.reactions/send-emoji-reaction + {:message-id message-id + :emoji-id emoji-id}]))) + +(defn- on-long-press + [message-id emoji-id] + (rf/dispatch [:chat.ui/emoji-reactions-by-message-id + {:message-id message-id + :long-pressed-emoji emoji-id}])) + +(defn show-authors-sheet + [user-message-content reactions] + (rf/dispatch [:dismiss-keyboard]) + (rf/dispatch + [:show-bottom-sheet + {:on-close (fn [] + (rf/dispatch + [:chat/clear-emoji-reaction-author-details])) + :content (fn [] + [drawers/reaction-authors + (map :emoji-id reactions)]) + :selected-item (fn [] + user-message-content) + :padding-bottom-override 0}])) + (defn message-reactions-row - [chat-id message-id] + [{:keys [message-id chat-id]} user-message-content] (let [reactions (rf/sub [:chats/message-reactions message-id chat-id])] - (when (seq reactions) - [rn/view {:margin-left 52 :margin-bottom 12 :flex-direction :row} - (for [{:keys [own emoji-id quantity emoji-reaction-id] :as emoji-reaction} reactions] - ^{:key (str emoji-reaction)} - [rn/view {:style {:margin-right 6}} - [quo/reaction - {:emoji (get constants/reactions emoji-id) - :neutral? own - :clicks quantity - :on-press (if own - #(rf/dispatch [:models.reactions/send-emoji-reaction-retraction - {:message-id message-id - :emoji-id emoji-id - :emoji-reaction-id emoji-reaction-id}]) - #(rf/dispatch [:models.reactions/send-emoji-reaction - {:message-id message-id - :emoji-id emoji-id}])) - :accessibility-label (str "emoji-reaction-" emoji-id)}]]) - [quo/add-reaction - {:on-press (fn [] - (rf/dispatch [:dismiss-keyboard]) - (rf/dispatch - [:show-bottom-sheet - {:content (fn [] [drawers/reactions - {:chat-id chat-id :message-id message-id}])}]))}]]))) + [:<> + (when (seq reactions) + [rn/view + {:style {:margin-left 52 + :margin-bottom 12 + :flex-direction :row}} + (for [{:keys [own emoji-id quantity emoji-reaction-id] + :as emoji-reaction} reactions] + ^{:key emoji-reaction} + [rn/view {:style {:margin-right 6}} + [quo/reaction + {:emoji (get constants/reactions emoji-id) + :neutral? own + :clicks quantity + :on-press #(on-press own message-id emoji-id emoji-reaction-id) + :on-long-press (fn [] + (on-long-press message-id + emoji-id) + (show-authors-sheet user-message-content + reactions)) + :accessibility-label (str "emoji-reaction-" emoji-id)}]]) + [quo/add-reaction + {:on-press (fn [] + (rf/dispatch [:dismiss-keyboard]) + (rf/dispatch + [:show-bottom-sheet + {:content (fn [] [drawers/reactions + {:chat-id chat-id + :message-id message-id}]) + :selected-item (fn [] + user-message-content)}]))}]])])) diff --git a/src/status_im2/contexts/chat/messages/content/view.cljs b/src/status_im2/contexts/chat/messages/content/view.cljs index a35eaca3af..dca6787d40 100644 --- a/src/status_im2/contexts/chat/messages/content/view.cljs +++ b/src/status_im2/contexts/chat/messages/content/view.cljs @@ -23,16 +23,18 @@ [status-im2.common.not-implemented :as not-implemented] [utils.datetime :as datetime] [reagent.core :as reagent] - [utils.address :as address])) + [utils.address :as address] + [react-native.gesture :as gesture])) (def delivery-state-showing-time-ms 3000) (defn avatar-container - [{:keys [content last-in-group? pinned-by quoted-message from]}] + [{:keys [content last-in-group? pinned-by quoted-message from]} message-reaction-view?] (if (or (and (seq (:response-to content)) quoted-message) last-in-group? - pinned-by) + pinned-by + message-reaction-view?) [avatar/avatar from :small] [rn/view {:padding-top 2 :width 32}])) @@ -43,8 +45,9 @@ pinned-by quoted-message from - timestamp]}] - (when (or (and (seq response-to) quoted-message) last-in-group? pinned-by) + timestamp]} + message-reaction-view?] + (when (or (and (seq response-to) quoted-message) last-in-group? pinned-by message-reaction-view?) (let [[primary-name secondary-name] (rf/sub [:contacts/contact-two-names-by-identity from]) {:keys [ens-verified added?]} (rf/sub [:contacts/contact-by-address from])] [quo/author @@ -82,7 +85,8 @@ (let [show-delivery-state? (reagent/atom false)] (fn [{:keys [content-type quoted-message content outgoing outgoing-status] :as message-data} context - keyboard-shown] + keyboard-shown + message-reaction-view?] (let [first-image (first (:album message-data)) outgoing-status (if (= content-type constants/content-type-album) (:outgoing-status first-image) @@ -91,7 +95,8 @@ (:outgoing first-image) outgoing) context (assoc context :on-long-press #(on-long-press message-data context)) - response-to (:response-to content)] + response-to (:response-to content) + height (rf/sub [:dimensions/window-height])] [rn/touchable-highlight {:accessibility-label (if (and outgoing (= outgoing-status :sending)) :message-sending @@ -115,37 +120,41 @@ [rn/view {:style {:padding-horizontal 12 :flex-direction :row}} - [avatar-container message-data] - [rn/view - {:style {:margin-left 8 - :flex 1}} - [author message-data] - (case content-type + [avatar-container message-data message-reaction-view?] + (into + (if message-reaction-view? + [gesture/scroll-view] + [rn/view]) + [{:style {:margin-left 8 + :flex 1 + :max-height (when message-reaction-view? (* 0.4 height))}} + [author message-data message-reaction-view?] + (case content-type - constants/content-type-text [content.text/text-content message-data] + constants/content-type-text [content.text/text-content message-data context] - constants/content-type-emoji - [not-implemented/not-implemented [old-message/emoji message-data]] + constants/content-type-emoji + [not-implemented/not-implemented [old-message/emoji message-data]] - constants/content-type-sticker - [not-implemented/not-implemented [old-message/sticker message-data]] + constants/content-type-sticker + [not-implemented/not-implemented [old-message/sticker message-data]] - constants/content-type-audio - [audio/audio-message message-data context] + constants/content-type-audio + [audio/audio-message message-data context] - constants/content-type-image - [image/image-message 0 message-data on-long-press] + constants/content-type-image + [image/image-message 0 message-data context on-long-press] - constants/content-type-album - [album/album-message message-data context on-long-press] + constants/content-type-album + [album/album-message message-data context on-long-press] - [not-implemented/not-implemented [content.unknown/unknown-content message-data]]) - (when @show-delivery-state? - [status/status outgoing-status])]]]])))) + [not-implemented/not-implemented [content.unknown/unknown-content message-data]]) + (when @show-delivery-state? + [status/status outgoing-status])])]]])))) (defn message-with-reactions - [{:keys [pinned-by mentioned in-pinned-view? content-type last-in-group? message-id] :as message-data} - {:keys [chat-id] :as context} + [{:keys [pinned-by mentioned in-pinned-view? content-type last-in-group?] :as message-data} + context keyboard-shown] [rn/view {:style (style/message-container in-pinned-view? pinned-by mentioned last-in-group?) @@ -153,9 +162,10 @@ (when pinned-by [pin/pinned-by-view pinned-by]) (if (#{constants/content-type-system-text constants/content-type-community - constants/content-type-system-pinned-message - constants/content-type-contact-request} + constants/content-type-contact-request + constants/content-type-system-pinned-message} content-type) [system-message-content message-data] - [user-message-content message-data context keyboard-shown]) - [reactions/message-reactions-row chat-id message-id]]) + [user-message-content message-data context keyboard-shown false]) + [reactions/message-reactions-row message-data + [user-message-content message-data context keyboard-shown true]]]) diff --git a/src/status_im2/contexts/chat/messages/drawers/style.cljs b/src/status_im2/contexts/chat/messages/drawers/style.cljs new file mode 100644 index 0000000000..a48b6abe60 --- /dev/null +++ b/src/status_im2/contexts/chat/messages/drawers/style.cljs @@ -0,0 +1,24 @@ +(ns status-im2.contexts.chat.messages.drawers.style + (:require [quo2.foundations.colors :as colors])) + +(def tab + {:flex-direction :row + :align-items :center + :justify-content :center}) + +(def tab-icon {:margin-right 4}) + +(defn tab-count + [active?] + {:color (if (or active? (colors/dark?)) colors/white colors/neutral-100)}) + +(def tabs-container + {:flex 1 + :align-self :stretch + :padding-left 20 + :padding-right 8 + :margin-bottom 12}) + +(def authors-list + {:height 320 + :flex 1}) diff --git a/src/status_im2/contexts/chat/messages/drawers/view.cljs b/src/status_im2/contexts/chat/messages/drawers/view.cljs index e0a51eef93..45f725ecb1 100644 --- a/src/status_im2/contexts/chat/messages/drawers/view.cljs +++ b/src/status_im2/contexts/chat/messages/drawers/view.cljs @@ -6,7 +6,72 @@ [status-im2.common.not-implemented :as not-implemented] [status-im2.constants :as constants] [utils.i18n :as i18n] - [utils.re-frame :as rf])) + [utils.re-frame :as rf] + [reagent.core :as reagent] + [status-im2.common.contact-list-item.view :as contact-list-item] + [status-im2.contexts.chat.messages.drawers.style :as style] + [react-native.gesture :as gesture])) + +(defn contact-list-item-fn + [{:keys [from compressed-key]}] + (let [[primary-name secondary-name] (rf/sub [:contacts/contact-two-names-by-identity + from]) + {:keys [ens-verified added?]} (rf/sub [:contacts/contact-by-address from])] + ^{:key compressed-key} + [contact-list-item/contact-list-item + {:on-press #(rf/dispatch [:chat.ui/show-profile from])} + {:primary-name primary-name + :secondary-name secondary-name + :public-key from + :compressed-key compressed-key + :ens-verified ens-verified + :added? added?}])) + +(defn get-tabs-data + [reaction-authors selected-tab reactions-order] + (map (fn [reaction-type-int] + (let [author-details (get reaction-authors reaction-type-int)] + {:id reaction-type-int + :accessibility-label (keyword (str "authors-for-reaction-" reaction-type-int)) + :label [rn/view {:style style/tab} + [quo/icon + (get constants/reactions reaction-type-int) + {:no-color true + :container-style style/tab-icon}] + [quo/text + {:weight :medium + :size :paragraph-1 + :style (style/tab-count (= selected-tab + reaction-type-int))} + (count author-details)]]})) + reactions-order)) + +(defn reaction-authors-comp + [selected-tab reaction-authors reactions-order] + [:<> + [rn/view style/tabs-container + [quo/tabs + {:size 32 + :scrollable? true + :in-scroll-view? true + :on-change #(reset! selected-tab %) + :default-active @selected-tab + :data (get-tabs-data reaction-authors @selected-tab reactions-order)}]] + [gesture/flat-list + {:data (for [contact (get reaction-authors @selected-tab)] + contact) + :render-fn contact-list-item-fn + :key-fn :from + :style style/authors-list}]]) + +(defn reaction-authors + [reactions-order] + (let [{:keys [reaction-authors-list + selected-reaction]} (rf/sub [:chat/reactions-authors]) + selected-tab (reagent/atom (or selected-reaction + (first (keys reaction-authors-list))))] + (fn [] + [reaction-authors-comp selected-tab reaction-authors-list reactions-order]))) (defn pin-message [{:keys [chat-id pinned pinned-by] :as message-data}] diff --git a/src/status_im2/subs/root.cljs b/src/status_im2/subs/root.cljs index d496bf2441..07bca624fb 100644 --- a/src/status_im2/subs/root.cljs +++ b/src/status_im2/subs/root.cljs @@ -120,6 +120,7 @@ (reg-root-key-sub :chat/inputs-with-mentions :chat/inputs-with-mentions) (reg-root-key-sub :chats-home-list :chats-home-list) (reg-root-key-sub :chats/recording? :chats/recording?) +(reg-root-key-sub :chat/reactions-authors :chat/reactions-authors) ;;lightbox (reg-root-key-sub :lightbox/exit-signal :lightbox/exit-signal)