From a39e0f6fbdcf73779f4cd618b02f24691c1ea737 Mon Sep 17 00:00:00 2001 From: flexsurfer Date: Fri, 23 Dec 2022 15:33:54 +0100 Subject: [PATCH] move messages to status-im2 (#14573) * move messages to status-im2 --- src/quo2/components/dividers/date.cljs | 23 +- .../components/messages/author/style.cljs | 3 +- src/quo2/components/messages/author/view.cljs | 1 + src/quo2/components/navigation/page_nav.cljs | 5 +- src/quo2/components/reactions/reaction.cljs | 67 +- src/quo2/components/reactions/style.cljs | 31 + src/quo2/core.cljs | 3 + src/status_im/browser/core.cljs | 10 +- src/status_im/chat/models.cljs | 4 +- src/status_im/chat/models/mentions.cljs | 2 +- src/status_im/chat/models/message.cljs | 2 +- src/status_im/chat/models/reactions.cljs | 4 +- .../ui/screens/chat/message/message.cljs | 5 +- .../screens/chat/message/reactions_row.cljs | 30 - .../screens/chat/styles/message/message.cljs | 14 +- src/status_im/ui/screens/status/views.cljs | 5 +- .../chat/components/reaction_drawer.cljs | 80 -- .../ui2/screens/chat/composer/view.cljs | 12 +- .../ui2/screens/chat/messages/message.cljs | 762 ++++-------------- src/status_im/utils/config.cljs | 3 +- src/status_im2/common/constants.cljs | 3 + src/status_im2/common/not_implemented.cljs | 6 + .../chat/messages/content/deleted/view.cljs | 15 + .../chat/messages/content/pin/style.cljs | 14 + .../chat/messages/content/pin/view.cljs | 52 ++ .../chat/messages/content/reactions/view.cljs | 28 + .../chat/messages/content/status/view.cljs | 5 + .../contexts/chat/messages/content/style.cljs | 10 + .../messages/content/system/text/view.cljs | 7 + .../chat/messages/content/text/style.cljs | 1 + .../chat/messages/content/text/view.cljs | 5 + .../chat/messages/content/unknown/view.cljs | 9 + .../contexts/chat/messages/content/view.cljs | 107 +++ .../{message => }/delete_message/events.cljs | 2 +- .../delete_message/events_test.cljs | 4 +- .../delete_message_for_me/events.cljs | 2 +- .../delete_message_for_me/events_test.cljs | 5 +- .../contexts/chat/messages/drawers/view.cljs | 166 ++++ .../contexts/chat/messages/list/view.cljs | 59 +- .../contexts/chat/messages/pin/list/view.cljs | 34 +- .../contexts/chat/messages/view.cljs | 30 +- .../contexts/quo_preview/reactions/react.cljs | 2 +- test/appium/views/chat_view.py | 2 +- 43 files changed, 767 insertions(+), 867 deletions(-) create mode 100644 src/quo2/components/reactions/style.cljs create mode 100644 src/status_im2/common/not_implemented.cljs create mode 100644 src/status_im2/contexts/chat/messages/content/deleted/view.cljs create mode 100644 src/status_im2/contexts/chat/messages/content/pin/style.cljs create mode 100644 src/status_im2/contexts/chat/messages/content/pin/view.cljs create mode 100644 src/status_im2/contexts/chat/messages/content/reactions/view.cljs create mode 100644 src/status_im2/contexts/chat/messages/content/status/view.cljs create mode 100644 src/status_im2/contexts/chat/messages/content/style.cljs create mode 100644 src/status_im2/contexts/chat/messages/content/system/text/view.cljs create mode 100644 src/status_im2/contexts/chat/messages/content/text/style.cljs create mode 100644 src/status_im2/contexts/chat/messages/content/text/view.cljs create mode 100644 src/status_im2/contexts/chat/messages/content/unknown/view.cljs create mode 100644 src/status_im2/contexts/chat/messages/content/view.cljs rename src/status_im2/contexts/chat/messages/{message => }/delete_message/events.cljs (98%) rename src/status_im2/contexts/chat/messages/{message => }/delete_message/events_test.cljs (96%) rename src/status_im2/contexts/chat/messages/{message => }/delete_message_for_me/events.cljs (98%) rename src/status_im2/contexts/chat/messages/{message => }/delete_message_for_me/events_test.cljs (95%) create mode 100644 src/status_im2/contexts/chat/messages/drawers/view.cljs diff --git a/src/quo2/components/dividers/date.cljs b/src/quo2/components/dividers/date.cljs index 8e40d45f18..5e25e31c68 100644 --- a/src/quo2/components/dividers/date.cljs +++ b/src/quo2/components/dividers/date.cljs @@ -4,18 +4,15 @@ [quo2.foundations.colors :as colors] [react-native.core :as rn])) -(defn date - [value] - [rn/view - {:margin-vertical 16 - :padding-right 8 - :padding-left 62} - [text/text - {:weight :medium - :accessibility-label :divider-date-text - :size :label - :style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40) - :text-transform :capitalize - :margin-bottom 4}} +(defn date [value] + [rn/view {:margin-vertical 8 + :padding-right 20 + :padding-left 60} + [text/text {:weight :medium + :accessibility-label :divider-date-text + :size :label + :style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40) + :text-transform :capitalize + :margin-bottom 4}} value] [separator/separator]]) diff --git a/src/quo2/components/messages/author/style.cljs b/src/quo2/components/messages/author/style.cljs index 266e5553f9..992952f15e 100644 --- a/src/quo2/components/messages/author/style.cljs +++ b/src/quo2/components/messages/author/style.cljs @@ -4,7 +4,6 @@ (def container {:flex 1 :flex-wrap :wrap - :height 18 :flex-direction :row :align-items :center}) @@ -40,4 +39,4 @@ (defn time-text [ens?] {:color colors/neutral-50 - :margin-left (if ens? 8 4)}) \ No newline at end of file + :margin-left (if ens? 8 4)}) diff --git a/src/quo2/components/messages/author/view.cljs b/src/quo2/components/messages/author/view.cljs index 499eb14fd4..2aa2209d67 100644 --- a/src/quo2/components/messages/author/view.cljs +++ b/src/quo2/components/messages/author/view.cljs @@ -95,5 +95,6 @@ [text/text {:monospace true :size :paragraph-2 + :accessibility-label :message-timestamp :style (style/time-text ens?)} time-str]]))]) diff --git a/src/quo2/components/navigation/page_nav.cljs b/src/quo2/components/navigation/page_nav.cljs index dcb7083930..6ffd8f9d76 100644 --- a/src/quo2/components/navigation/page_nav.cljs +++ b/src/quo2/components/navigation/page_nav.cljs @@ -146,12 +146,11 @@ [rn/view {:style (assoc centrify-style :flex-direction :row - :flex 1 :justify-content :flex-end)} (let [last-icon-index (-> right-section-buttons count dec)] - (map-indexed (fn [index {:keys [icon on-press type] :or {type :grey}}] + (map-indexed (fn [index {:keys [icon on-press type style] :or {type :grey}}] ^{:key index} - [rn/view {:style {:margin-right (if (not= index last-icon-index) 8 0)}} + [rn/view {:style (merge {:margin-right (if (not= index last-icon-index) 8 0)} style)} [button/button {:on-press on-press :icon true :type type :size 32} icon]]) right-section-buttons))]) diff --git a/src/quo2/components/reactions/reaction.cljs b/src/quo2/components/reactions/reaction.cljs index 890492631a..642a9a0b0a 100644 --- a/src/quo2/components/reactions/reaction.cljs +++ b/src/quo2/components/reactions/reaction.cljs @@ -1,31 +1,18 @@ (ns quo2.components.reactions.reaction (:require [quo2.components.icon :as icons] - [quo2.components.markdown.text :as quo2.text] + [quo2.components.markdown.text :as text] + [quo2.components.reactions.style :as style] [quo2.foundations.colors :as colors] [quo2.theme :as theme] [react-native.core :as rn])) -(def reaction-styling - {:flex-direction :row - :justify-content :center - :align-items :center - :padding-horizontal 8 - :border-radius 8 - :height 24}) - -(defn open-reactions-menu +(defn add-reaction [{:keys [on-press]}] (let [dark? (theme/dark?)] [rn/touchable-opacity {:on-press on-press :accessibility-label :emoji-reaction-add - :style (merge reaction-styling - {:padding-horizontal 9 - :border-width 1 - :margin-top 5 - :border-color (if dark? - colors/neutral-70 - colors/neutral-30)})} + :style (style/add-reaction)} [icons/icon :i/add {:size 20 :color (if dark? @@ -35,41 +22,17 @@ (defn reaction "Add your emoji as a param here" [{:keys [emoji clicks neutral? on-press accessibility-label]}] - (let [dark? (theme/dark?) - text-color (if dark? - colors/white - colors/neutral-100) - numeric-value (int clicks) - clicks-positive? (pos? numeric-value)] + (let [numeric-value (int clicks)] [rn/touchable-opacity {:on-press on-press :accessibility-label accessibility-label - :style (merge reaction-styling - (cond-> - {:background-color - (if dark? - (if neutral? - colors/neutral-70 - :transparent) - (if neutral? - colors/neutral-30 - :transparent))} - (and dark? (not neutral?)) - (assoc :border-color colors/neutral-70 - :border-width 1) - (and (not dark?) (not neutral?)) - (assoc :border-color colors/neutral-30 - :border-width 1)))} - [icons/icon emoji - {:no-color true - :size 16}] - [quo2.text/text - {:size :paragraph-2 - :weight :semi-bold - :color text-color - :flex-direction :row - :align-items :center - :justify-content :center} - (if clicks-positive? - (str " " numeric-value) - "")]])) + :style (style/reaction neutral?)} + [icons/icon emoji {:no-color true + :size 16}] + [text/text {:size :paragraph-2 + :weight :semi-bold + :flex-direction :row + :align-items :center + :justify-content :center} + (when (pos? numeric-value) + (str " " numeric-value))]])) diff --git a/src/quo2/components/reactions/style.cljs b/src/quo2/components/reactions/style.cljs new file mode 100644 index 0000000000..97017b4a26 --- /dev/null +++ b/src/quo2/components/reactions/style.cljs @@ -0,0 +1,31 @@ +(ns quo2.components.reactions.style + (:require [quo2.foundations.colors :as colors])) + +(def reaction-styling + {:flex-direction :row + :justify-content :center + :align-items :center + :padding-horizontal 8 + :border-radius 8 + :height 24}) + +(defn add-reaction [] + (merge reaction-styling + {:padding-horizontal 9 + :border-width 1 + :border-color (colors/theme-colors colors/neutral-30 colors/neutral-70)})) + +(defn reaction [neutral?] + (merge reaction-styling + (cond-> {:background-color (colors/theme-colors (if neutral? + colors/neutral-30 + :transparent) + (if neutral? + colors/neutral-70 + :transparent))} + (and (colors/dark?) (not neutral?)) + (assoc :border-color colors/neutral-70 + :border-width 1) + (and (not (colors/dark?)) (not neutral?)) + (assoc :border-color colors/neutral-30 + :border-width 1)))) diff --git a/src/quo2/core.cljs b/src/quo2/core.cljs index 366e613160..df41cb19c3 100644 --- a/src/quo2/core.cljs +++ b/src/quo2/core.cljs @@ -29,6 +29,7 @@ quo2.components.list-items.preview-list quo2.components.loaders.skeleton quo2.components.markdown.text + quo2.components.messages.author.view quo2.components.messages.gap quo2.components.messages.system-message quo2.components.navigation.floating-shell-button @@ -64,6 +65,7 @@ (def gap quo2.components.messages.gap/gap) (def system-message quo2.components.messages.system-message/system-message) (def reaction quo2.components.reactions.reaction/reaction) +(def add-reaction quo2.components.reactions.reaction/add-reaction) (def tags quo2.components.tags.tags/tags) (def user-avatar-tag quo2.components.tags.context-tags/user-avatar-tag) (def context-tag quo2.components.tags.context-tags/context-tag) @@ -77,6 +79,7 @@ (def disclaimer quo2.components.selectors.disclaimer/disclaimer) (def checkbox quo2.components.selectors.selectors/checkbox) (def skeleton quo2.components.loaders.skeleton/skeleton) +(def author quo2.components.messages.author.view/author) ;;;; AVATAR (def account-avatar quo2.components.avatars.account-avatar/account-avatar) diff --git a/src/status_im/browser/core.cljs b/src/status_im/browser/core.cljs index 8aa0dcaf78..08f922f0a8 100644 --- a/src/status_im/browser/core.cljs +++ b/src/status_im/browser/core.cljs @@ -23,7 +23,8 @@ [status-im.utils.universal-links.utils :as links] [status-im2.navigation.events :as navigation] [taoensso.timbre :as log] - [utils.debounce :as debounce])) + [utils.debounce :as debounce] + [utils.security.core :as security])) (fx/defn update-browser-option [{:keys [db]} option-key option-value] @@ -226,9 +227,10 @@ (fx/defn handle-message-link {:events [:browser.ui/message-link-pressed]} [_ link] - (if (links/universal-link? link) - {:dispatch [:universal-links/handle-url link]} - {:browser/show-browser-selection link})) + (when (security/safe-link? link) + (if (links/universal-link? link) + {:dispatch [:universal-links/handle-url link]} + {:browser/show-browser-selection link}))) (fx/defn update-browser-on-nav-change [cofx url error?] diff --git a/src/status_im/chat/models.cljs b/src/status_im/chat/models.cljs index 047e07b5a2..9c105a0148 100644 --- a/src/status_im/chat/models.cljs +++ b/src/status_im/chat/models.cljs @@ -16,8 +16,8 @@ [status-im.utils.fx :as fx] [status-im.utils.types :as types] [status-im.utils.utils :as utils] - [status-im2.contexts.chat.messages.message.delete-message-for-me.events :as delete-for-me] - [status-im2.contexts.chat.messages.message.delete-message.events :as delete-message] + [status-im2.contexts.chat.messages.delete-message-for-me.events :as delete-for-me] + [status-im2.contexts.chat.messages.delete-message.events :as delete-message] [status-im2.navigation.events :as navigation] [taoensso.timbre :as log])) diff --git a/src/status_im/chat/models/mentions.cljs b/src/status_im/chat/models/mentions.cljs index 72f9113296..e4b2ffe208 100644 --- a/src/status_im/chat/models/mentions.cljs +++ b/src/status_im/chat/models/mentions.cljs @@ -600,7 +600,7 @@ {:keys [new-text at-idxs start end] :as state} (get-in db [:chats/mentions chat-id :mentions]) new-text (or new-text text)] - (log/info "[mentions] calculate suggestions" + (log/debug "[mentions] calculate suggestions" "state" state) (if-not (seq at-idxs) diff --git a/src/status_im/chat/models/message.cljs b/src/status_im/chat/models/message.cljs index 343ca595be..32e030e2ba 100644 --- a/src/status_im/chat/models/message.cljs +++ b/src/status_im/chat/models/message.cljs @@ -13,7 +13,7 @@ [status-im.utils.gfycat.core :as gfycat] [status-im.utils.platform :as platform] [status-im.utils.types :as types] - [status-im2.contexts.chat.messages.message.delete-message.events :as delete-message] + [status-im2.contexts.chat.messages.delete-message.events :as delete-message] [taoensso.timbre :as log])) (defn- message-loaded? diff --git a/src/status_im/chat/models/reactions.cljs b/src/status_im/chat/models/reactions.cljs index 6776cb02f5..6823a9b5f4 100644 --- a/src/status_im/chat/models/reactions.cljs +++ b/src/status_im/chat/models/reactions.cljs @@ -73,13 +73,13 @@ (fx/defn send-emoji-reaction - {:events [::send-emoji-reaction]} + {:events [:models.reactions/send-emoji-reaction]} [{{:keys [current-chat-id]} :db :as cofx} reaction] (message.protocol/send-reaction cofx (update reaction :chat-id #(or % current-chat-id)))) (fx/defn send-retract-emoji-reaction - {:events [::send-emoji-reaction-retraction]} + {:events [:models.reactions/send-emoji-reaction-retraction]} [{{:keys [current-chat-id]} :db :as cofx} reaction] (message.protocol/send-retract-reaction cofx (update reaction :chat-id #(or % current-chat-id)))) diff --git a/src/status_im/ui/screens/chat/message/message.cljs b/src/status_im/ui/screens/chat/message/message.cljs index cb44a7a899..66527c8d70 100644 --- a/src/status_im/ui/screens/chat/message/message.cljs +++ b/src/status_im/ui/screens/chat/message/message.cljs @@ -4,7 +4,6 @@ [re-frame.core :as re-frame] [reagent.core :as reagent] [status-im.chat.models.images :as images] - [status-im.chat.models.reactions :as models.reactions] [status-im.constants :as constants] [status-im.i18n.i18n :as i18n] [status-im.react-native.resources :as resources] @@ -827,11 +826,11 @@ :picker-on-close (fn [] (space-keeper false)) :send-emoji (fn [{:keys [emoji-id]}] - (re-frame/dispatch [::models.reactions/send-emoji-reaction + (re-frame/dispatch [:models.reactions/send-emoji-reaction {:message-id (:message-id message) :emoji-id emoji-id}])) :retract-emoji (fn [{:keys [emoji-id emoji-reaction-id]}] - (re-frame/dispatch [::models.reactions/send-emoji-reaction-retraction + (re-frame/dispatch [:models.reactions/send-emoji-reaction-retraction {:message-id (:message-id message) :emoji-id emoji-id :emoji-reaction-id emoji-reaction-id}])) diff --git a/src/status_im/ui/screens/chat/message/reactions_row.cljs b/src/status_im/ui/screens/chat/message/reactions_row.cljs index 287f33f0ca..e69de29bb2 100644 --- a/src/status_im/ui/screens/chat/message/reactions_row.cljs +++ b/src/status_im/ui/screens/chat/message/reactions_row.cljs @@ -1,30 +0,0 @@ -(ns status-im.ui.screens.chat.message.reactions-row - (:require [quo.react-native :as rn] - [quo2.components.reactions.reaction :as quo2.reaction] - [status-im.constants :as constants] - [status-im.ui.screens.chat.message.styles :as styles])) - -(def default-reaction-margin-top 5) - -(def text-reaction-margin-top -3) - -(defn message-reactions - [{:keys [content-type]} reactions timeline on-emoji-press on-open] - (when (seq reactions) - [rn/view - {:style (styles/reactions-row - timeline - (if (= content-type constants/content-type-text) - text-reaction-margin-top - default-reaction-margin-top))} - (for [{:keys [own emoji-id quantity] :as emoji-reaction} reactions] - ^{:key (str emoji-reaction)} - [rn/view {:style {:margin-right 6 :margin-top 5}} - [quo2.reaction/reaction - {:emoji (get constants/reactions emoji-id) - :neutral? own - :clicks quantity - :on-press #(on-emoji-press emoji-id) - :accessibility-label (str "emoji-reaction-" emoji-id)}]]) - ;; on-press won't work until we integrate Message Context Drawer - [quo2.reaction/open-reactions-menu (when @on-open {:on-press @on-open})]])) diff --git a/src/status_im/ui/screens/chat/styles/message/message.cljs b/src/status_im/ui/screens/chat/styles/message/message.cljs index 388496d053..4886ecb62c 100644 --- a/src/status_im/ui/screens/chat/styles/message/message.cljs +++ b/src/status_im/ui/screens/chat/styles/message/message.cljs @@ -1,7 +1,6 @@ (ns status-im.ui.screens.chat.styles.message.message (:require [quo.design-system.colors :as colors] [quo2.foundations.colors :as quo2.colors] - [quo2.foundations.typography :as typography] [status-im.constants :as constants] [status-im.ui.components.react :as react] [status-im.ui.screens.chat.styles.photos :as photos])) @@ -51,7 +50,9 @@ [] {:font-size 10 :line-height 10 - :color colors/gray}) + :border-color :red + :border-width 1 + :color :red}) (defn audio-message-timestamp-text [] @@ -104,14 +105,7 @@ {:flex-direction :row :margin-top 1}) -(defn pin-author-text - [] - (merge typography/font-medium - {:color quo2.colors/primary-50 - :bottom 2})) - -(defn pinned-by-text - [] +(defn pinned-by-text [] {:margin-left 5}) (def message-author-touchable diff --git a/src/status_im/ui/screens/status/views.cljs b/src/status_im/ui/screens/status/views.cljs index 9b4e13c12e..ccb621257b 100644 --- a/src/status_im/ui/screens/status/views.cljs +++ b/src/status_im/ui/screens/status/views.cljs @@ -3,7 +3,6 @@ [re-frame.core :as re-frame] [reagent.core :as reagent] [status-im.chat.models :as chat] - [status-im.chat.models.reactions :as models.reactions] [status-im.constants :as constants] [status-im.i18n.i18n :as i18n] [status-im.ui.components.fast-image :as fast-image] @@ -174,12 +173,12 @@ :picker-on-open (fn []) :picker-on-close (fn []) :send-emoji (fn [{:keys [emoji-id]}] - (re-frame/dispatch [::models.reactions/send-emoji-reaction + (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 + (re-frame/dispatch [:models.reactions/send-emoji-reaction-retraction {:message-id (:message-id message) :chat-id chat-id :emoji-id emoji-id diff --git a/src/status_im/ui2/screens/chat/components/reaction_drawer.cljs b/src/status_im/ui2/screens/chat/components/reaction_drawer.cljs index 82f69e85da..e69de29bb2 100644 --- a/src/status_im/ui2/screens/chat/components/reaction_drawer.cljs +++ b/src/status_im/ui2/screens/chat/components/reaction_drawer.cljs @@ -1,80 +0,0 @@ -(ns status-im.ui2.screens.chat.components.reaction-drawer - (:require [quo.react-native :as rn] - [quo2.components.buttons.button :as quo2.button] - [quo2.components.list-items.menu-item :as quo2.menu-item] - [quo2.components.separator :as quo2.separator] - [quo2.foundations.colors :as colors] - [re-frame.core :as re-frame] - [status-im.constants :as constants])) - -(defn message-options - [actions own-reactions send-emoji] - (fn [] - (let [main-actions (filter #(= (:type %) :main) actions) - danger-actions (filter #(= (:type %) :danger) actions) - admin-actions (filter #(= (:type %) :admin) actions)] - [rn/view - [rn/view - {:style {:width "100%" - :flex-direction :row - :justify-content :space-between - :padding-horizontal 30 - :padding-top 5 - :padding-bottom 15}} - (doall - (for [[id icon] constants/reactions - :let [active (own-reactions id)]] - ;;TODO reactions selector should be used - ;;https://www.figma.com/file/WQZcp6S0EnzxdTL4taoKDv/Design-System?node-id=9961%3A166549 - ;; not implemented yet - ^{:key id} - [quo2.button/button - (merge - {:size 40 - :type :grey - :icon true - :icon-no-color true - :accessibility-label (str "emoji-picker-" id) - :on-press #(do - (send-emoji id) - (re-frame/dispatch [:bottom-sheet/hide]))} - (when active {:style {:background-color colors/neutral-10}})) - icon]))] - [rn/view {:style {:padding-horizontal 8}} - (for [action main-actions] - (let [on-press (:on-press action)] - ^{:key (:id action)} - [quo2.menu-item/menu-item - {:type :main - :title (:label action) - :accessibility-label (:label action) - :icon (:icon action) - :on-press #(do - (when on-press (on-press)) - (re-frame/dispatch [:bottom-sheet/hide]))}])) - (when-not (empty? danger-actions) - [quo2.separator/separator]) - (for [action danger-actions] - (let [on-press (:on-press action)] - ^{:key (:id action)} - [quo2.menu-item/menu-item - {:type :danger - :title (:label action) - :accessibility-label (:label action) - :icon (:icon action) - :on-press #(do - (when on-press (on-press)) - (re-frame/dispatch [:bottom-sheet/hide]))}])) - (when-not (empty? admin-actions) - [quo2.separator/separator]) - (for [action admin-actions] - (let [on-press (:on-press action)] - ^{:key (:id action)} - [quo2.menu-item/menu-item - {:type :danger - :title (:label action) - :accessibility-label (:label action) - :icon (:icon action) - :on-press #(do - (when on-press (on-press)) - (re-frame/dispatch [:bottom-sheet/hide]))}]))]]))) diff --git a/src/status_im/ui2/screens/chat/composer/view.cljs b/src/status_im/ui2/screens/chat/composer/view.cljs index f23184e460..cf9f70d81d 100644 --- a/src/status_im/ui2/screens/chat/composer/view.cljs +++ b/src/status_im/ui2/screens/chat/composer/view.cljs @@ -20,7 +20,8 @@ [status-im.utils.utils :as utils] [utils.re-frame :as rf] [status-im2.contexts.chat.messages.list.view :refer [scroll-to-bottom]] - [status-im.utils.platform :as platform])) + [status-im.utils.platform :as platform] + [status-im2.common.not-implemented :as not-implemented])) (defn calculate-y [context min-y max-y added-value chat-id set-bg-opacity] @@ -301,10 +302,11 @@ :type :outline :size 32} :i/image] [rn/view {:width 12}] - [quo2.button/button - {:icon true - :type :outline - :size 32} :i/reaction] + [not-implemented/not-implemented + [quo2.button/button + {:icon true + :type :outline + :size 32} :i/reaction]] [rn/view {:flex 1}] ;;SEND button [rn/view {:ref send-ref diff --git a/src/status_im/ui2/screens/chat/messages/message.cljs b/src/status_im/ui2/screens/chat/messages/message.cljs index ad8ffbba09..be8a6ff513 100644 --- a/src/status_im/ui2/screens/chat/messages/message.cljs +++ b/src/status_im/ui2/screens/chat/messages/message.cljs @@ -1,21 +1,16 @@ (ns status-im.ui2.screens.chat.messages.message - (:require [quo.core :as quo] - [quo.design-system.colors :as quo.colors] + (:require [quo.design-system.colors :as quo.colors] [quo.react-native :as rn] [quo2.components.avatars.user-avatar :as user-avatar] [quo2.components.icon :as icons] [quo2.components.markdown.text :as text] - [quo2.components.messages.system-message :as system-message] [quo2.foundations.colors :as colors] [quo2.foundations.typography :as typography] [re-frame.core :as re-frame] [reagent.core :as reagent] - [status-im.chat.models.images :as images] - [status-im.chat.models.reactions :as models.reactions] [status-im.constants :as constants] [status-im.i18n.i18n :as i18n] [status-im.react-native.resources :as resources] - [status-im.ui.components.animation :as animation] [status-im.ui.components.fast-image :as fast-image] [status-im.ui.components.react :as react] [status-im.ui.screens.chat.image.preview.views :as preview] @@ -23,93 +18,39 @@ [status-im.ui.screens.chat.message.command :as message.command] [status-im.ui.screens.chat.message.gap :as message.gap] [status-im.ui.screens.chat.message.link-preview :as link-preview] - [status-im.ui.screens.chat.message.reactions :as reactions] - [status-im.ui.screens.chat.message.reactions-row :as reaction-row] [status-im.ui.screens.chat.sheets :as sheets] [status-im.ui.screens.chat.styles.message.message :as style] - [status-im.ui.screens.chat.styles.photos :as photos.style] [status-im.ui.screens.chat.utils :as chat.utils] [status-im.ui.screens.communities.icon :as communities.icon] - [status-im.ui2.screens.chat.components.reaction-drawer :as reaction-drawer] [status-im.ui2.screens.chat.components.reply :as components.reply] [status-im.utils.config :as config] [status-im.utils.datetime :as time] [status-im.utils.utils :as utils] [status-im2.contexts.chat.home.chat-list-item.view :as home.chat-list-item] - [status-im2.contexts.chat.messages.message.delete-message-for-me.events] - [status-im2.contexts.chat.messages.message.delete-message.events] + [status-im2.contexts.chat.messages.delete-message-for-me.events] + [status-im2.contexts.chat.messages.delete-message.events] [utils.re-frame :as rf] - [utils.security.core :as security]) + [quo2.core :as quo]) (:require-macros [status-im.utils.views :refer [defview letsubs]])) -(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 - [rn/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 quo.colors/gray quo.colors/white) - :accessibility-label (name outgoing-status)}]) - (when edited-at [rn/text {:style (style/message-status-text)} edited-at-text])])) - -(defn message-timestamp - [{:keys [timestamp-str in-popover?]}] - (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)] - [rn/animated-view {:style (style/message-timestamp-wrapper) :opacity anim-opacity} - [rn/text - {:style (style/message-timestamp-text) - :accessibility-label :message-timestamp} - timestamp-str]]))) - -(defn quoted-message - [{:keys [message-id chat-id]} reply pin?] - (let [{:keys [deleted? deleted-for-me?]} (get @(re-frame/subscribe [:chats/chat-messages chat-id]) - message-id) - reply (assoc reply - :deleted? deleted? - :deleted-for-me? deleted-for-me?)] - [rn/view {:style (when-not pin? (style/quoted-message-container))} - [components.reply/reply-message reply false pin?]])) - (defn system-text? [content-type] (= content-type constants/content-type-system-text)) +(defn mention-element + [from] + (rf/sub [:contacts/contact-name-by-identity from])) + (defn render-inline - [message-text content-type acc {:keys [type literal destination]}] + [_message-text content-type acc {:keys [type literal destination]}] (case type "" (conj acc literal) "code" - (conj acc - [quo/text - {:max-font-size-multiplier react/max-font-size-multiplier - :style (style/inline-code-style) - :monospace true} - literal]) + (conj acc [rn/text literal]) "emph" (conj acc [rn/text (style/emph-style) literal]) @@ -126,14 +67,9 @@ "link" (conj acc [rn/text - {:style - {:color quo.colors/blue - :text-decoration-line :underline} - :on-press - #(when (and (security/safe-link? destination) - (security/safe-link-text? message-text)) - (re-frame/dispatch - [:browser.ui/message-link-pressed destination]))} + {:style {:color :blue + :text-decoration-line :underline} + :on-press #(rf/dispatch [:browser.ui/message-link-pressed destination])} destination]) "mention" @@ -150,16 +86,15 @@ "status-tag" (conj acc [rn/text - {:style {:color quo.colors/blue + {:style {:color :blue :text-decoration-line :underline} - :on-press - #(re-frame/dispatch - [:chat.ui/start-public-chat literal])} + :on-press #(rf/dispatch [:chat.ui/start-public-chat literal])} "#" literal]) (conj acc literal))) +;; TEXT (defn render-block [{:keys [content content-type in-popover?]} acc {:keys [type ^js literal children]}] @@ -181,42 +116,37 @@ "codeblock" (conj acc [rn/view {:style style/codeblock-style} - [quo/text - {:max-font-size-multiplier react/max-font-size-multiplier - :style style/codeblock-text-style - :monospace true} - (.substring literal 0 (dec (.-length literal)))]]) + [rn/text (.substring literal 0 (dec (.-length literal)))]]) acc)) (defn render-parsed-text - [message tree] - (reduce (fn [acc e] (render-block message acc e)) [:<>] tree)) + [{:keys [content] :as message-data}] + (reduce (fn [acc e] + (render-block message-data acc e)) + [:<>] + (:parsed-text content))) -(defn render-parsed-text-with-message-status - [{:keys [edited-at in-popover?] :as message} tree] - (let [elements (render-parsed-text message tree) - message-status [rn/text {:style (style/message-status-placeholder)} - (str (if (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 (= rn/text (nth last-element 0)) - ;; 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 message-status + [{:keys [outgoing-status edited-at]}] + (when (or edited-at outgoing-status) + [rn/view {:flex-direction :row} + [rn/text {:style (style/message-status-text)} + (str "[" + (if edited-at + "edited" + (or outgoing-status "")) + " DEBUG]")]])) -(defn unknown-content-type - [{:keys [content-type content] :as message}] - [rn/view (style/message-view message) - [rn/text - {:style {:color quo.colors/white-persist}} - (if (seq (:text content)) - (:text content) - (str "Unhandled content-type " content-type))]]) +(defn quoted-message + [{:keys [message-id chat-id]} reply pin?] + (let [{:keys [deleted? deleted-for-me?]} (get @(re-frame/subscribe [:chats/chat-messages chat-id]) + message-id) + reply (assoc reply + :deleted? deleted? + :deleted-for-me? deleted-for-me?)] + [rn/view {:style (when-not pin? (style/quoted-message-container))} + [components.reply/reply-message reply false pin?]])) (defn message-not-sent-text [chat-id message-id] @@ -234,33 +164,6 @@ [rn/view style/not-sent-icon [icons/icon :i/warning {:color quo.colors/red}]]]]) -(defn pin-author-name - [pinned-by] - (let [user-contact @(re-frame/subscribe [:multiaccount/contact]) - contact-names @(re-frame/subscribe [:contacts/contact-two-names-by-identity pinned-by])] - ;; We append empty spaces to the name as a workaround to make one-line and multi-line label - ;; components show correctly - (str " " (if (= pinned-by (user-contact :public-key)) (i18n/label :t/You) (first contact-names))))) - -(defn pin-icon - [color size] - [icons/icon :i/pin - {:color color - :height size - :width size}]) - -(defn pinned-by-indicator - [pinned-by] - [rn/view - {:style (style/pin-indicator) - :accessibility-label :pinned-by} - [pin-icon colors/primary-50 16] - [quo/text - {:size :small - :color :main - :style (style/pin-author-text)} - (pin-author-name pinned-by)]]) - (defn message-delivery-status [{:keys [chat-id message-id outgoing-status message-type]}] (when (and (not= constants/message-type-private-group-system-message message-type) @@ -278,44 +181,6 @@ (letsubs [contact-with-names [:multiaccount/contact]] (chat.utils/format-author contact-with-names opts nil))) -(defview community-content - [{:keys [community-id] :as message}] - (letsubs [{:keys [name description verified] :as community} [:communities/community community-id] - communities-enabled? [:communities/enabled?]] - (when (and communities-enabled? community) - [rn/view - {:style (assoc (style/message-wrapper message) - :margin-vertical 10 - :margin-left 8 - :width 271)} - (when verified - [rn/view (style/community-verified) - [rn/text - {:style {:font-size 13 - :color quo.colors/blue}} (i18n/label :t/communities-verified)]]) - [rn/view (style/community-message verified) - [rn/view - {:width 62 - :padding-left 14} - (if (= community-id constants/status-community-id) - [rn/image - {:source (resources/get-image :status-logo) - :style {:width 40 - :height 40}}] - [communities.icon/community-icon community])] - [rn/view {:padding-right 14 :flex 1} - [rn/text {:style {:font-weight "700" :font-size 17}} - name] - [rn/text description]]] - [rn/view (style/community-view-button) - [rn/touchable-opacity - {:on-press #(re-frame/dispatch - [:communities/navigate-to-community - {:community-id (:id community)}])} - [rn/text - {:style {:text-align :center - :color quo.colors/blue}} (i18n/label :t/view)]]]]))) - (defn display-name-view [display-name contact timestamp show-key?] [rn/view {:style {:flex-direction :row}} @@ -343,55 +208,43 @@ (defn message-content-wrapper "Author, userpic and delivery wrapper" - [{:keys [last-in-group? timestamp-str timestamp deleted? deleted-undoable-till - deleted-for-me? deleted-for-me-undoable-till pinned from chat-id] + [{:keys [last-in-group? timestamp pinned from chat-id] :as message} content] (let [response-to (:response-to (:content message)) display-name (first (rf/sub [:contacts/contact-two-names-by-identity from])) contact (rf/sub [:contacts/contact-by-address from]) photo-path (when-not (empty? (:images contact)) (rf/sub [:chats/photo-path from])) online? (rf/sub [:visibility-status-updates/online? from])] - (if (or deleted? deleted-for-me?) - [system-message/system-message - {:type :deleted - :label (if deleted? :message-deleted :message-deleted-for-you) - :labels {:pinned-a-message (i18n/label :pinned-a-message) - :message-deleted (i18n/label :message-deleted-for-everyone) - :message-deleted-for-you (i18n/label :message-deleted-for-you) - :added (i18n/label :added)} - :timestamp-str timestamp-str - :non-pressable? true - :animate-landing? (if (or deleted-undoable-till deleted-for-me-undoable-till) - true - false)}] - [rn/view - {:style (style/message-wrapper message) - :pointer-events :box-none - :accessibility-label :chat-item} - (when (and (seq response-to) (:quoted-message message)) - [quoted-message {:message-id response-to :chat-id chat-id} (:quoted-message message)]) - [rn/view - {:style (style/message-body) - :pointer-events :box-none} - [rn/view {:style {:width 40}} - (when (or (and (seq response-to) (:quoted-message message)) last-in-group? pinned) - [react/touchable-highlight {:on-press #(re-frame/dispatch [:chat.ui/show-profile from])} - [user-avatar/user-avatar - {:full-name display-name - :profile-picture photo-path - :status-indicator? true - :online? online? - :size :small - :ring? false}]])] - [rn/view {:style (style/message-author-wrapper)} - (when (or (and (seq response-to) (:quoted-message message)) last-in-group? pinned) - [display-name-view display-name contact timestamp true]) - ;; MESSAGE CONTENT - content - [link-preview/link-preview-wrapper (:links (:content message)) false false]]] - ;; delivery status - [rn/view (style/delivery-status) - [message-delivery-status message]]]))) + [rn/view + {:style (style/message-wrapper message) + :pointer-events :box-none + :accessibility-label :chat-item} + (when (and (seq response-to) (:quoted-message message)) + [quoted-message {:message-id response-to :chat-id chat-id} (:quoted-message message)]) + [rn/view + {:style (style/message-body) + :pointer-events :box-none} + ;; AVATAR + [rn/view {:style {:width 40}} + (when (or (and (seq response-to) (:quoted-message message)) last-in-group? pinned) + [react/touchable-highlight {:on-press #(re-frame/dispatch [:chat.ui/show-profile from])} + [user-avatar/user-avatar + {:full-name display-name + :profile-picture photo-path + :status-indicator? true + :online? online? + :size :small + :ring? false}]])] + [rn/view {:style (style/message-author-wrapper)} + ;; AUTHOR NAME + (when (or (and (seq response-to) (:quoted-message message)) last-in-group? pinned) + [display-name-view display-name contact timestamp true]) + ;; MESSAGE CONTENT + content + [link-preview/link-preview-wrapper (:links (:content message)) false false]]] + ;; delivery status + [rn/view (style/delivery-status) + [message-delivery-status message]]])) (def image-max-width 260) (def image-max-height 192) @@ -408,46 +261,6 @@ (reset! dimensions {:width (/ width k) :height image-max-height :loaded true}))) (swap! dimensions assoc :loaded true))))) -(defn message-content-image - [{:keys [content]} _] - (let [dimensions (reagent/atom {:width image-max-width :height image-max-height :loaded false}) - visible (reagent/atom false) - uri (:image content)] - (fn [{:keys [in-popover?] :as message} - {:keys [on-long-press]}] - (let [style-opts {:outgoing false - :opacity (if (:loaded @dimensions) 1 0) - :width (:width @dimensions) - :height (:height @dimensions)}] - [:<> - [preview/preview-image - {:message message - :visible @visible - :on-close #(do (reset! visible false) - (reagent/flush))}] - [rn/touchable-opacity - {:on-press (fn [] - (reset! visible true) - (rn/dismiss-keyboard!)) - :on-long-press @on-long-press - :disabled in-popover?} - [rn/view - {:style (style/image-message style-opts) - :accessibility-label :image-message} - (when (or (:error @dimensions) (not (:loaded @dimensions))) - [rn/view - (merge (dissoc style-opts :opacity) - {:flex 1 :align-items :center :justify-content :center :position :absolute}) - (if (:error @dimensions) - [icons/icon :i/cancel] - [rn/activity-indicator {:animating true}])]) - [fast-image/fast-image - {:style (dissoc style-opts :outgoing) - :on-load (image-set-size dimensions) - :on-error #(swap! dimensions assoc :error true) - :source {:uri uri}}] - [rn/view {:style (style/image-message-border style-opts)}]]]])))) - (defmulti ->message :content-type) (defmethod ->message constants/content-type-command @@ -519,220 +332,73 @@ :icon :i/delete :id :delete-for-all}])))) -(defn collapsible-text-message - [_ _] - (let [collapsed? (reagent/atom false) - show-timestamp? (reagent/atom false)] - (fn [{:keys [content in-popover?] :as message} on-long-press modal ref] - (let [on-long-press (fn [] - (if @collapsed? - (do (reset! collapsed? false) - (js/setTimeout #(on-long-press-fn on-long-press message content) 200)) - (on-long-press-fn on-long-press message content)))] - (reset! ref on-long-press) - [rn/touchable-opacity - (when-not modal - {:delay-long-press 100 - :on-long-press on-long-press - :disabled in-popover?}) - [rn/view style/message-view-wrapper - [message-timestamp message show-timestamp?] - [rn/view {:style (style/message-view message)} - [rn/view {:style (style/message-view-content)} - [rn/view - [render-parsed-text-with-message-status message (:parsed-text content)]]]]]])))) - -(defmethod ->message constants/content-type-text - [message {:keys [on-long-press modal ref] :as reaction-picker}] - [message-content-wrapper message - [collapsible-text-message message on-long-press modal ref] - reaction-picker]) - -(defmethod ->message constants/content-type-community - [message] - [community-content message]) - +;; STATUS ? whats that ? (defmethod ->message constants/content-type-status - [{:keys [content content-type] :as message}] - [message-content-wrapper message - [rn/view style/status-container - [rn/text {:style (style/status-text)} - (reduce - (fn [acc e] (render-inline (:text content) content-type acc e)) - [rn/text {:style (style/status-text)}] - (-> content :parsed-text peek :children))]]]) + [{:keys [content content-type]}] + [rn/view style/status-container + [rn/text {:style (style/status-text)} + (reduce + (fn [acc e] (render-inline (:text content) content-type acc e)) + [rn/text {:style (style/status-text)}] + (-> content :parsed-text peek :children))]]) -(defmethod ->message constants/content-type-emoji +;; EMOJI +(defn emoji [] - (let [show-timestamp? (reagent/atom false)] - (fn [{:keys [content pinned in-popover? message-pin-enabled] :as message} - {:keys [on-long-press modal ref] - :as reaction-picker}] - (let [on-long-press (fn [] - (on-long-press - (concat - [{:type :main - :on-press #(re-frame/dispatch [:chat.ui/reply-to-message message]) - :id :reply - :icon :i/reply - :label (i18n/label :t/message-reply)} - {:type :main - :on-press #(react/copy-to-clipboard (get content :text)) - :id :copy - :icon :i/copy - :label (i18n/label :t/copy-text)}] - (when message-pin-enabled - [{:type :main - :on-press #(pin-message message) - :id :pin - :icon :i/pin - :label (if pinned (i18n/label :t/unpin) (i18n/label :t/pin))}]))))] - (reset! ref on-long-press) - [message-content-wrapper message - [rn/touchable-opacity - (when-not modal - {:disabled in-popover? - :on-press (fn [] - (rn/dismiss-keyboard!) - (reset! show-timestamp? true)) - :delay-long-press 100 - :on-long-press on-long-press}) - [rn/view style/message-view-wrapper - [message-timestamp message show-timestamp?] - [rn/view (style/message-view message) - [rn/view {:style (style/message-view-content)} - [rn/view {:style (style/style-message-text)} - [rn/text {:style (style/emoji-message message)} - (:text content)]] - [message-status message]]]]] - reaction-picker])))) + (fn [{:keys [content] :as message}] + [rn/view (style/message-view message) + [rn/view {:style (style/message-view-content)} + [rn/view {:style (style/style-message-text)} + [rn/text {:style (style/emoji-message message)} + (:text content)]]]])) -(defmethod ->message constants/content-type-sticker - [{:keys [content from outgoing in-popover?] - :as message} - {:keys [on-long-press modal ref] - :as reaction-picker}] - (let [pack (get-in content [:sticker :pack]) - on-long-press (fn [] - (on-long-press - (when-not outgoing - [{:type :main - :icon :i/stickers - :on-press #(when pack - (re-frame/dispatch [:chat.ui/show-profile from])) - :label (i18n/label :t/see-sticker-set)}])))] - (reset! ref on-long-press) - [message-content-wrapper message - [rn/touchable-opacity - (when-not modal - {:disabled in-popover? - :accessibility-label :sticker-message - :on-press (fn [_] - (when pack - (re-frame/dispatch [:stickers/open-sticker-pack (str pack)])) - (rn/dismiss-keyboard!)) - :delay-long-press 100 - :on-long-press on-long-press}) - [fast-image/fast-image - {:style {:margin 10 :width 140 :height 140} - :source {:uri (str (-> content :sticker :url) "&download=true")}}]] - reaction-picker])) +;; STICKER +(defn sticker + [{:keys [content]}] + [fast-image/fast-image + {:style {:margin 10 :width 140 :height 140} + :source {:uri (str (-> content :sticker :url) "&download=true")}}]) -(defmethod ->message constants/content-type-image - [{:keys [content in-popover? outgoing] :as message} - {:keys [on-long-press modal ref] - :as reaction-picker}] - (let [on-long-press - (fn [] - (on-long-press - (concat [{:type :main - :on-press #(re-frame/dispatch [:chat.ui/reply-to-message message]) - :id :reply - :icon :i/reply - :label (i18n/label :t/message-reply)} - {:type :main - :on-press #(re-frame/dispatch [:chat.ui/save-image-to-gallery (:image content)]) - :id :save - :icon :i/save - :label (i18n/label :t/save-image-library)} - {:type :main - :on-press #(images/download-image-http - (get-in message [:content :image]) - preview/share) - :id :share - :icon :i/share - :label (i18n/label :t/share-image)}] - [{:type :danger - :on-press #(re-frame/dispatch - [:chat.ui/delete-message-for-me message - config/delete-message-undo-time-limit-ms]) - :label (i18n/label :t/delete-for-me) - :icon :i/delete - :id :delete-for-me}] - (when (and outgoing config/delete-message-enabled?) - [{:type :danger - :on-press #(re-frame/dispatch [:chat.ui/delete-message - message - config/delete-message-undo-time-limit-ms]) - :label (i18n/label :t/delete-for-everyone) - :icon :i/delete - :id :delete}]))))] - (reset! ref on-long-press) - [message-content-wrapper message - [message-content-image message - {:modal modal - :disabled in-popover? - :delay-long-press 100 - :on-long-press ref}] - reaction-picker])) +;;IMAGE +(defn message-content-image + [{:keys [content]}] + (let [dimensions (reagent/atom {:width image-max-width :height image-max-height :loaded false}) + visible (reagent/atom false) + uri (:image content)] + (fn [message] + (let [style-opts {:outgoing false + :opacity (if (:loaded @dimensions) 1 0) + :width (:width @dimensions) + :height (:height @dimensions)}] + [:<> + [preview/preview-image + {:message message + :visible @visible + :on-close #(do (reset! visible false) + (reagent/flush))}] + [rn/view + {:style (style/image-message style-opts) + :accessibility-label :image-message} + (when (or (:error @dimensions) (not (:loaded @dimensions))) + [rn/view + (merge (dissoc style-opts :opacity) + {:flex 1 :align-items :center :justify-content :center :position :absolute}) + (if (:error @dimensions) + [icons/icon :i/cancel] + [rn/activity-indicator {:animating true}])]) + [fast-image/fast-image + {:style (dissoc style-opts :outgoing) + :on-load (image-set-size dimensions) + :on-error #(swap! dimensions assoc :error true) + :source {:uri uri}}] + [rn/view {:style (style/image-message-border style-opts)}]]])))) -(defmethod ->message constants/content-type-audio - [] - (let [show-timestamp? (reagent/atom false)] - (fn [{:keys [outgoing pinned] :as message} - {:keys [on-long-press modal ref] - :as reaction-picker}] - (let [on-long-press - (fn [] - (on-long-press - [{:type :main - :on-press #(re-frame/dispatch [:chat.ui/reply-to-message message]) - :label (i18n/label :t/message-reply) - :icon :i/reply - :id :reply} - {:type :main - :on-press #(pin-message message) - :label (i18n/label (if pinned :t/unpin-from-chat :t/pin-to-chat)) - :icon :i/pin - :id (if pinned :unpin :pin)} - {:type :danger - :on-press #(re-frame/dispatch - [:chat.ui/delete-message-for-me message - config/delete-message-for-me-undo-time-limit-ms]) - :label (i18n/label :t/delete-for-me) - :icon :i/delete - :id :delete-for-me} - (when (and outgoing config/delete-message-enabled?) - {:type :danger - :on-press #(re-frame/dispatch [:chat.ui/delete-message - message - config/delete-message-undo-time-limit-ms]) - :label (i18n/label :t/delete-for-everyone) - :icon :i/delete - :id :delete})]))] - (reset! ref on-long-press) - [message-content-wrapper message - [rn/touchable-opacity - (when-not modal - {:on-long-press on-long-press - :on-press (fn [] - (reset! show-timestamp? true))}) - [rn/view style/message-view-wrapper - [message-timestamp message show-timestamp?] - [rn/view {:style (style/message-view message) :accessibility-label :audio-message} - [rn/view {:style (style/message-view-content)} - [message.audio/message-content message] [message-status message]]]]] - reaction-picker])))) +;; AUDIO +(defn audio + [message] + [rn/view {:style (style/message-view message) :accessibility-label :audio-message} + [rn/view {:style (style/message-view-content)} + [message.audio/message-content message]]]) (defn contact-request-status-pending [] @@ -769,7 +435,10 @@ constants/contact-request-message-state-accepted [contact-request-status-accepted] constants/contact-request-message-state-declined [contact-request-status-declined])]) -(defmethod ->message constants/content-type-contact-request +;;;; SYSTEM + +;; CONTACT REQUEST (like system message ? ) no wrapper +(defn system-contact-request [message _] [rn/view {:style (style/content-type-contact-request)} [rn/image @@ -788,120 +457,45 @@ (get-in message [:content :text])]] [contact-request-status-label (:contact-request-state message)]]) -(defmethod ->message :default +(defview community-content + [{:keys [community-id] :as message}] + (letsubs [{:keys [name description verified] :as community} [:communities/community community-id] + communities-enabled? [:communities/enabled?]] + (when (and communities-enabled? community) + [rn/view + {:style (assoc (style/message-wrapper message) + :margin-vertical 10 + :margin-left 8 + :width 271)} + (when verified + [rn/view (style/community-verified) + [rn/text + {:style {:font-size 13 + :color quo.colors/blue}} (i18n/label :t/communities-verified)]]) + [rn/view (style/community-message verified) + [rn/view + {:width 62 + :padding-left 14} + (if (= community-id constants/status-community-id) + [rn/image + {:source (resources/get-image :status-logo) + :style {:width 40 + :height 40}}] + [communities.icon/community-icon community])] + [rn/view {:padding-right 14 :flex 1} + [rn/text {:style {:font-weight "700" :font-size 17}} + name] + [rn/text description]]] + [rn/view (style/community-view-button) + [rn/touchable-opacity + {:on-press #(re-frame/dispatch + [:communities/navigate-to-community + {:community-id (:id community)}])} + [rn/text + {:style {:text-align :center + :color quo.colors/blue}} (i18n/label :t/view)]]]]))) + +;; COMMUNITY (like system ? ) no wrapper +(defn community [message] - [message-content-wrapper message - [unknown-content-type message]]) - -(defn chat-message - [{:keys [pinned pinned-by mentioned in-pinned-view? last-in-group? deleted? deleted-for-me?] - :as message}] - (let [reactions @(re-frame/subscribe [:chats/message-reactions (:message-id message) - (:chat-id message)]) - own-reactions (reduce (fn [acc {:keys [emoji-id own]}] - (if own (conj acc emoji-id) acc)) - [] - reactions) - send-emoji (fn [{:keys [emoji-id]}] - (re-frame/dispatch [::models.reactions/send-emoji-reaction - {:message-id (:message-id message) - :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) - :emoji-id emoji-id - :emoji-reaction-id emoji-reaction-id}])) - on-emoji-press (fn [emoji-id] - (let [active ((set own-reactions) emoji-id)] - (if active - (retract-emoji {:emoji-id emoji-id - :emoji-reaction-id (reactions/extract-id reactions - emoji-id)}) - (send-emoji {:emoji-id emoji-id})))) - on-open-drawer (fn [actions] - (re-frame/dispatch [:bottom-sheet/show-sheet - {:content (reaction-drawer/message-options - actions - (into #{} (js->clj own-reactions)) - #(on-emoji-press %))}])) - on-long-press (atom nil)] - [rn/view - {:style (merge (when (and (not in-pinned-view?) (or mentioned pinned)) - {:background-color colors/primary-50-opa-5 - :border-radius 16 - :margin-bottom 4}) - (when (or mentioned pinned last-in-group?) {:margin-top 8}) - {:margin-horizontal 8})} - - (when (and pinned (not (or deleted? deleted-for-me?))) - [rn/view {:style (style/pin-indicator-container)} - [pinned-by-indicator pinned-by]]) - [->message message - {:ref on-long-press - :modal false - :on-long-press on-open-drawer}] - (when-not (or deleted? deleted-for-me?) - [reaction-row/message-reactions message reactions nil on-emoji-press on-long-press])])) ;; TODO: pass on-open-drawer function - -(defn message-render-fn - [{:keys [outgoing whisper-timestamp] :as message} - _ - {:keys [group-chat public? community? current-public-key show-input? edit-enabled]}] - [chat-message - (assoc message - :incoming-group (and group-chat (not outgoing)) - :group-chat group-chat - :public? public? - :community? community? - :current-public-key current-public-key - :show-input? show-input? - :message-pin-enabled true - :in-pinned-view? true - :pinned true - :timestamp-str (time/timestamp->time whisper-timestamp) - :edit-enabled edit-enabled)]) - -(defn pin-system-message - [{:keys [from in-popover? timestamp-str chat-id] :as message} {:keys [modal close-modal]}] - (let [response-to (:response-to (:content message))] - [rn/touchable-opacity - {:on-press #(rf/dispatch [:bottom-sheet/show-sheet :pinned-messages-list chat-id]) - :active-opacity 1 - :style (merge {:flex-direction :row :margin-vertical 8} (style/message-wrapper message))} - [rn/view - {:style {:width photos.style/default-size - :height photos.style/default-size - :margin-right 16 - :border-radius photos.style/default-size - :justify-content :center - :align-items :center - :background-color colors/primary-50-opa-10} - :accessibility-label :content-type-pin-icon} - [pin-icon colors/primary-50 16]] - [rn/view - [rn/view {:style {:flex-direction :row :align-items :center}} - [rn/touchable-opacity - {:style style/message-author-touchable - :disabled in-popover? - :on-press #(do (when modal (close-modal)) - (re-frame/dispatch [:chat.ui/show-profile from]))} - [message-author-name from {:modal modal} 20]] - [rn/text {:style {:font-size 13}} (str " " (i18n/label :t/pinned-a-message))] - [rn/text - {:style (merge - {:padding-left 5 - :margin-top 2} - (style/message-timestamp-text)) - :accessibility-label :message-timestamp} - timestamp-str]] - [quoted-message {:message-id response-to :chat-id chat-id} (:quoted-message message) true]]])) - -(defmethod ->message constants/content-type-system-text - [{:keys [content quoted-message] :as message}] - (if quoted-message - [pin-system-message message] - [rn/view {:accessibility-label :chat-item} - [rn/view (style/system-message-body message) - [rn/view (style/message-view message) - [rn/view (style/message-view-content) - [render-parsed-text message (:parsed-text content)]]]]])) + [community-content message]) diff --git a/src/status_im/utils/config.cljs b/src/status_im/utils/config.cljs index cd4abc9166..230cd3a7ed 100644 --- a/src/status_im/utils/config.cljs +++ b/src/status_im/utils/config.cljs @@ -152,8 +152,9 @@ (def wallet-connect-project-id "87815d72a81d739d2a7ce15c2cfdefb3") +;; NOTE moved to status-im2.common.constants (def delete-message-undo-time-limit-ms 4000) (def delete-message-for-me-undo-time-limit-ms 4000) -;;TODO for development only should be removed in status 2.0 +;; NOTE for development only should be removed in status 2.0 (def new-ui-enabled? true) diff --git a/src/status_im2/common/constants.cljs b/src/status_im2/common/constants.cljs index 33c9da7988..c95d33b5b8 100644 --- a/src/status_im2/common/constants.cljs +++ b/src/status_im2/common/constants.cljs @@ -195,3 +195,6 @@ (def ^:const community-member-role-all 1) (def ^:const community-member-role-manage-users 2) (def ^:const community-member-role-moderator 3) + +(def ^:const delete-message-undo-time-limit-ms 4000) +(def ^:const delete-message-for-me-undo-time-limit-ms 4000) diff --git a/src/status_im2/common/not_implemented.cljs b/src/status_im2/common/not_implemented.cljs new file mode 100644 index 0000000000..bbbd47e508 --- /dev/null +++ b/src/status_im2/common/not_implemented.cljs @@ -0,0 +1,6 @@ +(ns status-im2.common.not-implemented + (:require [react-native.core :as rn])) + +(defn not-implemented [content] + [rn/view {:border-color :red :border-width 1} + content]) diff --git a/src/status_im2/contexts/chat/messages/content/deleted/view.cljs b/src/status_im2/contexts/chat/messages/content/deleted/view.cljs new file mode 100644 index 0000000000..48c2003a6a --- /dev/null +++ b/src/status_im2/contexts/chat/messages/content/deleted/view.cljs @@ -0,0 +1,15 @@ +(ns status-im2.contexts.chat.messages.content.deleted.view + (:require [quo2.core :as quo] + [i18n.i18n :as i18n])) + +(defn deleted-message [{:keys [deleted? deleted-undoable-till timestamp-str deleted-for-me-undoable-till]}] + [quo/system-message + {:type :deleted + :label (if deleted? :message-deleted :message-deleted-for-you) + :labels {:pinned-a-message (i18n/label :t/pinned-a-message) + :message-deleted (i18n/label :t/message-deleted-for-everyone) + :message-deleted-for-you (i18n/label :t/message-deleted-for-you) + :added (i18n/label :t/added)} + :timestamp-str timestamp-str + :non-pressable? true + :animate-landing? (or deleted-undoable-till deleted-for-me-undoable-till)}]) diff --git a/src/status_im2/contexts/chat/messages/content/pin/style.cljs b/src/status_im2/contexts/chat/messages/content/pin/style.cljs new file mode 100644 index 0000000000..a17d868bb7 --- /dev/null +++ b/src/status_im2/contexts/chat/messages/content/pin/style.cljs @@ -0,0 +1,14 @@ +(ns status-im2.contexts.chat.messages.content.pin.style + (:require [quo2.foundations.colors :as colors])) + +(def pin-indicator-container + {:margin-top 4 + :margin-left 54 + :justify-content :center + :align-self :flex-start + :align-items :flex-start + :flex-direction :row}) + +(def pin-author-text + {:color colors/primary-50 + :bottom 2}) diff --git a/src/status_im2/contexts/chat/messages/content/pin/view.cljs b/src/status_im2/contexts/chat/messages/content/pin/view.cljs new file mode 100644 index 0000000000..b46d437b15 --- /dev/null +++ b/src/status_im2/contexts/chat/messages/content/pin/view.cljs @@ -0,0 +1,52 @@ +(ns status-im2.contexts.chat.messages.content.pin.view + (:require [react-native.core :as rn] + [status-im2.contexts.chat.messages.content.pin.style :as style] + [quo2.core :as quo] + [quo2.foundations.colors :as colors] + [utils.re-frame :as rf] + [i18n.i18n :as i18n] + [status-im.ui2.screens.chat.messages.message :as old-message] + [status-im.ui.screens.chat.styles.message.message :as old-style])) + +(defn pinned-by-view [pinned-by] + (let [{:keys [public-key]} (rf/sub [:multiaccount/contact]) + contact-names (rf/sub [:contacts/contact-two-names-by-identity pinned-by]) + author-name (if (= pinned-by public-key) (i18n/label :t/You) (first contact-names))] + [rn/view {:style style/pin-indicator-container + :accessibility-label :pinned-by} + [quo/icon :i/pin {:color colors/primary-50 :size 16}] + [quo/text {:size :label + :weight :medium + :style style/pin-author-text} + author-name]])) + +(defn system-message [{:keys [from in-popover? timestamp-str chat-id] :as message}] + (let [response-to (:response-to (:content message)) + default-size 36] + [rn/touchable-opacity {:on-press #(rf/dispatch [:bottom-sheet/show-sheet :pinned-messages-list chat-id]) + :active-opacity 1 + :style (merge {:flex-direction :row :margin-vertical 8} (old-style/message-wrapper message))} + [rn/view {:style {:width default-size + :height default-size + :margin-right 16 + :border-radius default-size + :justify-content :center + :align-items :center + :background-color colors/primary-50-opa-10} + :accessibility-label :content-type-pin-icon} + [quo/icon :i/pin {:color colors/primary-50 :size 16}]] + [rn/view + [rn/view {:style {:flex-direction :row :align-items :center}} + [rn/touchable-opacity {:style old-style/message-author-touchable + :disabled in-popover? + :on-press #(rf/dispatch [:chat.ui/show-profile from])} + [old-message/message-author-name from {} 20]] + [rn/text {:style {:font-size 13}} (str " " (i18n/label :t/pinned-a-message))] + [rn/text + {:style (merge + {:padding-left 5 + :margin-top 2} + (old-style/message-timestamp-text)) + :accessibility-label :message-timestamp} + timestamp-str]] + [old-message/quoted-message {:message-id response-to :chat-id chat-id} (:quoted-message message) true]]])) diff --git a/src/status_im2/contexts/chat/messages/content/reactions/view.cljs b/src/status_im2/contexts/chat/messages/content/reactions/view.cljs new file mode 100644 index 0000000000..c0d618269f --- /dev/null +++ b/src/status_im2/contexts/chat/messages/content/reactions/view.cljs @@ -0,0 +1,28 @@ +(ns status-im2.contexts.chat.messages.content.reactions.view + (:require [status-im2.common.constants :as constants] + [quo2.core :as quo] + [react-native.core :as rn] + [utils.re-frame :as rf] + [status-im2.contexts.chat.messages.drawers.view :as drawers])) + +(defn message-reactions-row [chat-id message-id] + (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 #(rf/dispatch [:bottom-sheet/show-sheet + {:content (fn [] [drawers/reactions chat-id message-id])}])}]]))) diff --git a/src/status_im2/contexts/chat/messages/content/status/view.cljs b/src/status_im2/contexts/chat/messages/content/status/view.cljs new file mode 100644 index 0000000000..0f06db0fc3 --- /dev/null +++ b/src/status_im2/contexts/chat/messages/content/status/view.cljs @@ -0,0 +1,5 @@ +(ns status-im2.contexts.chat.messages.content.status.view + (:require [status-im.ui2.screens.chat.messages.message :as old-message])) + +(defn status [message-data] + [old-message/message-status message-data]) diff --git a/src/status_im2/contexts/chat/messages/content/style.cljs b/src/status_im2/contexts/chat/messages/content/style.cljs new file mode 100644 index 0000000000..942dbb5119 --- /dev/null +++ b/src/status_im2/contexts/chat/messages/content/style.cljs @@ -0,0 +1,10 @@ +(ns status-im2.contexts.chat.messages.content.style + (:require [quo2.foundations.colors :as colors])) + +(defn message-container [in-pinned-view? pinned mentioned last-in-group?] + (merge (when (and (not in-pinned-view?) (or mentioned pinned)) + {:background-color colors/primary-50-opa-5 + :margin-bottom 4}) + (when (or mentioned pinned last-in-group?) + {:margin-top 8}) + {:border-radius 16})) diff --git a/src/status_im2/contexts/chat/messages/content/system/text/view.cljs b/src/status_im2/contexts/chat/messages/content/system/text/view.cljs new file mode 100644 index 0000000000..1737232bbb --- /dev/null +++ b/src/status_im2/contexts/chat/messages/content/system/text/view.cljs @@ -0,0 +1,7 @@ +(ns status-im2.contexts.chat.messages.content.system.text.view + (:require [react-native.core :as rn] + [status-im.ui2.screens.chat.messages.message :as old-message])) + +(defn text-content [message-data] + [rn/view {:accessibility-label :chat-item} + [old-message/render-parsed-text message-data]]) diff --git a/src/status_im2/contexts/chat/messages/content/text/style.cljs b/src/status_im2/contexts/chat/messages/content/text/style.cljs new file mode 100644 index 0000000000..d0e5a66a21 --- /dev/null +++ b/src/status_im2/contexts/chat/messages/content/text/style.cljs @@ -0,0 +1 @@ +(ns status-im2.contexts.chat.messages.content.text.style) diff --git a/src/status_im2/contexts/chat/messages/content/text/view.cljs b/src/status_im2/contexts/chat/messages/content/text/view.cljs new file mode 100644 index 0000000000..9737d4ef98 --- /dev/null +++ b/src/status_im2/contexts/chat/messages/content/text/view.cljs @@ -0,0 +1,5 @@ +(ns status-im2.contexts.chat.messages.content.text.view + (:require [status-im.ui2.screens.chat.messages.message :as old-message])) + +(defn text-content [message-data] + [old-message/render-parsed-text message-data]) diff --git a/src/status_im2/contexts/chat/messages/content/unknown/view.cljs b/src/status_im2/contexts/chat/messages/content/unknown/view.cljs new file mode 100644 index 0000000000..4ed5eed73a --- /dev/null +++ b/src/status_im2/contexts/chat/messages/content/unknown/view.cljs @@ -0,0 +1,9 @@ +(ns status-im2.contexts.chat.messages.content.unknown.view + (:require [react-native.core :as rn])) + +(defn unknown-content + [{:keys [content-type content]}] + [rn/text + (if (seq (:text content)) + (:text content) + (str "Unhandled content-type " content-type))]) diff --git a/src/status_im2/contexts/chat/messages/content/view.cljs b/src/status_im2/contexts/chat/messages/content/view.cljs new file mode 100644 index 0000000000..67785cd1fc --- /dev/null +++ b/src/status_im2/contexts/chat/messages/content/view.cljs @@ -0,0 +1,107 @@ +(ns status-im2.contexts.chat.messages.content.view + (:require [react-native.core :as rn] + [quo2.foundations.colors :as colors] + [status-im2.contexts.chat.messages.content.style :as style] + [status-im2.contexts.chat.messages.content.pin.view :as pin] + [status-im2.common.constants :as constants] + [status-im2.contexts.chat.messages.content.unknown.view :as content.unknown] + [status-im2.contexts.chat.messages.content.text.view :as content.text] + [status-im2.contexts.chat.messages.drawers.view :as drawers] + [status-im2.contexts.chat.messages.content.reactions.view :as reactions] + [status-im2.contexts.chat.messages.content.status.view :as status] + [status-im2.contexts.chat.messages.content.system.text.view :as system.text] + [quo2.core :as quo] + [utils.re-frame :as rf] + [status-im.utils.datetime :as time] + [status-im.ui2.screens.chat.messages.message :as old-message] + [status-im2.common.not-implemented :as not-implemented])) + +(defn avatar + [{:keys [response-to last-in-group? pinned quoted-message from]}] + [rn/touchable-without-feedback {:on-press #(rf/dispatch [:chat.ui/show-profile from])} + [rn/view {:padding-top 2 :width 32} + (when (or (and (seq response-to) quoted-message) last-in-group? pinned) + (let [display-name (first (rf/sub [:contacts/contact-two-names-by-identity from])) + contact (rf/sub [:contacts/contact-by-address from]) + photo-path (when-not (empty? (:images contact)) (rf/sub [:chats/photo-path from])) + online? (rf/sub [:visibility-status-updates/online? from])] + [quo/user-avatar + {:full-name display-name + :profile-picture photo-path + :status-indicator? true + :online? online? + :size :small + :ring? false}]))]]) + +(defn author + [{:keys [response-to last-in-group? pinned quoted-message from timestamp]}] + (when (or (and (seq response-to) quoted-message) last-in-group? pinned) + (let [display-name (first (rf/sub [:contacts/contact-two-names-by-identity from])) + {:keys [ens-verified added?]} (rf/sub [:contacts/contact-by-address from])] + [quo/author + {:profile-name display-name + :chat-key from + :time-str (time/timestamp->time timestamp) + :contact? added? + :verified? ens-verified}]))) + +(defn system-message-content + [{:keys [content-type quoted-message] :as message-data}] + (if quoted-message + [not-implemented/not-implemented [pin/system-message message-data]] + (case content-type + + constants/content-type-system-text [not-implemented/not-implemented [system.text/text-content message-data]] + + constants/content-type-community [not-implemented/not-implemented [old-message/community message-data]] + + constants/content-type-contact-request [not-implemented/not-implemented [old-message/system-contact-request message-data]]))) + +(defn user-message-content + [{:keys [content-type quoted-message content] :as message-data} + {:keys [chat-id] :as context}] + (let [response-to (:response-to content)] + [rn/touchable-highlight + {:underlay-color (colors/theme-colors colors/neutral-5 colors/neutral-90) + :style {:border-radius 16} + :on-press #() + :on-long-press #(rf/dispatch [:bottom-sheet/show-sheet + {:content (drawers/reactions-and-actions message-data context)}])} + [rn/view {:padding-vertical 8} + (when (and (seq response-to) quoted-message) + [old-message/quoted-message {:message-id response-to :chat-id chat-id} quoted-message]) + [rn/view {:padding-horizontal 12 :flex-direction :row} + [avatar message-data] + [rn/view {:margin-left 8} + [author message-data] + (case content-type + + constants/content-type-text [not-implemented/not-implemented [content.text/text-content 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-image [not-implemented/not-implemented [old-message/message-content-image message-data]] + + constants/content-type-audio [not-implemented/not-implemented [old-message/audio message-data]] + + [not-implemented/not-implemented [content.unknown/unknown-content message-data]]) + [status/status message-data]]]]])) + +(defn message-with-reactions + [{:keys [pinned pinned-by mentioned in-pinned-view? content-type + last-in-group? message-id] + :as message-data} + {:keys [chat-id] :as context}] + [rn/view + {:style (style/message-container in-pinned-view? pinned mentioned last-in-group?) + :accessibility-label :chat-item} + (when pinned + [pin/pinned-by-view pinned-by]) + (if (#{constants/content-type-system-text constants/content-type-community + constants/content-type-contact-request} + content-type) + [system-message-content message-data] + [user-message-content message-data context]) + [reactions/message-reactions-row chat-id message-id]]) diff --git a/src/status_im2/contexts/chat/messages/message/delete_message/events.cljs b/src/status_im2/contexts/chat/messages/delete_message/events.cljs similarity index 98% rename from src/status_im2/contexts/chat/messages/message/delete_message/events.cljs rename to src/status_im2/contexts/chat/messages/delete_message/events.cljs index 3328610df3..4e7eb7ad37 100644 --- a/src/status_im2/contexts/chat/messages/message/delete_message/events.cljs +++ b/src/status_im2/contexts/chat/messages/delete_message/events.cljs @@ -1,4 +1,4 @@ -(ns status-im2.contexts.chat.messages.message.delete-message.events +(ns status-im2.contexts.chat.messages.delete-message.events (:require [status-im.chat.models.message-list :as message-list] [status-im.utils.datetime :as datetime] [taoensso.timbre :as log] diff --git a/src/status_im2/contexts/chat/messages/message/delete_message/events_test.cljs b/src/status_im2/contexts/chat/messages/delete_message/events_test.cljs similarity index 96% rename from src/status_im2/contexts/chat/messages/message/delete_message/events_test.cljs rename to src/status_im2/contexts/chat/messages/delete_message/events_test.cljs index 2b46bb745f..999c84825e 100644 --- a/src/status_im2/contexts/chat/messages/message/delete_message/events_test.cljs +++ b/src/status_im2/contexts/chat/messages/delete_message/events_test.cljs @@ -1,7 +1,7 @@ -(ns status-im2.contexts.chat.messages.message.delete-message.events-test +(ns status-im2.contexts.chat.messages.delete-message.events-test (:require [cljs.test :refer-macros [deftest is testing]] [status-im.utils.datetime :as datetime] - [status-im2.contexts.chat.messages.message.delete-message.events :as delete-message])) + [status-im2.contexts.chat.messages.delete-message.events :as delete-message])) (def mid "message-id") (def cid "chat-id") diff --git a/src/status_im2/contexts/chat/messages/message/delete_message_for_me/events.cljs b/src/status_im2/contexts/chat/messages/delete_message_for_me/events.cljs similarity index 98% rename from src/status_im2/contexts/chat/messages/message/delete_message_for_me/events.cljs rename to src/status_im2/contexts/chat/messages/delete_message_for_me/events.cljs index 3ae3338a7f..ad86970f8a 100644 --- a/src/status_im2/contexts/chat/messages/message/delete_message_for_me/events.cljs +++ b/src/status_im2/contexts/chat/messages/delete_message_for_me/events.cljs @@ -1,4 +1,4 @@ -(ns status-im2.contexts.chat.messages.message.delete-message-for-me.events +(ns status-im2.contexts.chat.messages.delete-message-for-me.events (:require [status-im.chat.models.message-list :as message-list] [status-im.utils.datetime :as datetime] [taoensso.timbre :as log] diff --git a/src/status_im2/contexts/chat/messages/message/delete_message_for_me/events_test.cljs b/src/status_im2/contexts/chat/messages/delete_message_for_me/events_test.cljs similarity index 95% rename from src/status_im2/contexts/chat/messages/message/delete_message_for_me/events_test.cljs rename to src/status_im2/contexts/chat/messages/delete_message_for_me/events_test.cljs index 5f0e4fe934..b78800903d 100644 --- a/src/status_im2/contexts/chat/messages/message/delete_message_for_me/events_test.cljs +++ b/src/status_im2/contexts/chat/messages/delete_message_for_me/events_test.cljs @@ -1,8 +1,7 @@ -(ns status-im2.contexts.chat.messages.message.delete-message-for-me.events-test +(ns status-im2.contexts.chat.messages.delete-message-for-me.events-test (:require [cljs.test :refer-macros [deftest is testing]] [status-im.utils.datetime :as datetime] - [status-im2.contexts.chat.messages.message.delete-message-for-me.events :as - delete-message-for-me])) + [status-im2.contexts.chat.messages.delete-message-for-me.events :as delete-message-for-me])) (def mid "message-id") (def cid "chat-id") diff --git a/src/status_im2/contexts/chat/messages/drawers/view.cljs b/src/status_im2/contexts/chat/messages/drawers/view.cljs new file mode 100644 index 0000000000..690f9eb075 --- /dev/null +++ b/src/status_im2/contexts/chat/messages/drawers/view.cljs @@ -0,0 +1,166 @@ +(ns status-im2.contexts.chat.messages.drawers.view + (:require [react-native.core :as rn] + [status-im2.common.constants :as constants] + [utils.re-frame :as rf] + [quo2.core :as quo] + [i18n.i18n :as i18n] + [status-im2.setup.config :as config] + [status-im.ui.components.react :as react] + [status-im.ui2.screens.chat.components.reply :as components.reply] + [status-im2.common.not-implemented :as not-implemented])) + +(defn pin-message + [{:keys [chat-id pinned] :as message-data}] + (let [pinned-messages (rf/sub [:chats/pinned chat-id])] + (if (and (not pinned) (> (count pinned-messages) 2)) + (do + (js/setTimeout (fn [] (rf/dispatch [:dismiss-keyboard])) 500) + (rf/dispatch [:pin-message/show-pin-limit-modal chat-id])) + (rf/dispatch [:pin-message/send-pin-message (assoc message-data :pinned (not pinned))])))) + +(defn get-actions + [{:keys [outgoing content pinned] :as message-data} + {:keys [edit-enabled show-input? can-delete-message-for-everyone? community? message-pin-enabled]}] + (concat + (when (and outgoing edit-enabled) + [{:type :main + :on-press #(rf/dispatch [:chat.ui/edit-message message-data]) + :label (i18n/label :t/edit-message) + :icon :i/edit + :id :edit}]) + (when show-input? + [{:type :main + :on-press #(rf/dispatch [:chat.ui/reply-to-message message-data]) + :label (i18n/label :t/message-reply) + :icon :i/reply + :id :reply}]) + [{:type :main + :on-press #(react/copy-to-clipboard + (components.reply/get-quoted-text-with-mentions + (get content :parsed-text))) + :label (i18n/label :t/copy-text) + :icon :i/copy + :id :copy}] + (when message-pin-enabled + [{:type :main + :on-press #(pin-message message-data) + :label (i18n/label (if pinned + (if community? :t/unpin-from-channel :t/unpin-from-chat) + (if community? :t/pin-to-channel :t/pin-to-chat))) + :icon :i/pin + :id (if pinned :unpin :pin)}]) + (when-not pinned + [{:type :danger + :on-press #(rf/dispatch [:chat.ui/delete-message-for-me message-data + constants/delete-message-for-me-undo-time-limit-ms]) + :label (i18n/label :t/delete-for-me) + :icon :i/delete + :id :delete-for-me}]) + (when (and (or outgoing can-delete-message-for-everyone?) config/delete-message-enabled?) + [{:type :danger + :on-press #(rf/dispatch [:chat.ui/delete-message message-data + constants/delete-message-undo-time-limit-ms]) + :label (i18n/label :t/delete-for-everyone) + :icon :i/delete + :id :delete-for-all}]))) + +(defn extract-id + [reactions id] + (->> reactions + (filter (fn [{:keys [emoji-id]}] (= emoji-id id))) + first + :emoji-reaction-id)) + +(defn reactions + [chat-id message-id] + (let [reactions (rf/sub [:chats/message-reactions message-id chat-id]) + own-reactions (reduce (fn [acc {:keys [emoji-id own emoji-reaction-id]}] + (if own + (assoc acc emoji-id emoji-reaction-id) + acc)) + {} + reactions)] + [rn/view + {:style {:flex-direction :row + :justify-content :space-between + :padding-horizontal 30 + :padding-top 5 + :padding-bottom 15}} + (doall + (for [[id icon] constants/reactions] + (let [emoji-reaction-id (get own-reactions id)] + ^{:key id} + [not-implemented/not-implemented + [quo/button + (merge + {:size 40 + :type (if emoji-reaction-id :grey :ghost) + :icon true + :icon-no-color true + :accessibility-label (str "emoji-picker-" id) + :on-press (fn [] + (if emoji-reaction-id + (rf/dispatch [:models.reactions/send-emoji-reaction-retraction + {:message-id message-id + :emoji-id id + :emoji-reaction-id emoji-reaction-id}]) + (rf/dispatch [:models.reactions/send-emoji-reaction + {:message-id message-id + :emoji-id id}])) + (rf/dispatch [:bottom-sheet/hide]))}) + icon]])))])) + +(defn reactions-and-actions + [{:keys [message-id] :as message-data} {:keys [chat-id] :as context}] + (fn [] + (let [actions (get-actions message-data context) + main-actions (filter #(= (:type %) :main) actions) + danger-actions (filter #(= (:type %) :danger) actions) + admin-actions (filter #(= (:type %) :admin) actions)] + [:<> + ;; REACTIONS + [reactions chat-id message-id] + + ;; MAIN ACTIONS + [rn/view {:style {:padding-horizontal 8}} + (for [action main-actions] + (let [on-press (:on-press action)] + ^{:key (:id action)} + [quo/menu-item + {:type :main + :title (:label action) + :accessibility-label (:label action) + :icon (:icon action) + :on-press #(do + (when on-press (on-press)) + (rf/dispatch [:bottom-sheet/hide]))}])) + (when-not (empty? danger-actions) + [quo/separator]) + + ;; DANGER ACTIONS + (for [action danger-actions] + (let [on-press (:on-press action)] + ^{:key (:id action)} + [quo/menu-item + {:type :danger + :title (:label action) + :accessibility-label (:label action) + :icon (:icon action) + :on-press #(do + (when on-press (on-press)) + (rf/dispatch [:bottom-sheet/hide]))}])) + (when-not (empty? admin-actions) + [quo/separator]) + + ;; ADMIN ACTIONS + (for [action admin-actions] + (let [on-press (:on-press action)] + ^{:key (:id action)} + [quo/menu-item + {:type :danger + :title (:label action) + :accessibility-label (:label action) + :icon (:icon action) + :on-press #(do + (when on-press (on-press)) + (rf/dispatch [:bottom-sheet/hide]))}]))]]))) diff --git a/src/status_im2/contexts/chat/messages/list/view.cljs b/src/status_im2/contexts/chat/messages/list/view.cljs index 6a531a71b9..dfa4aaebd9 100644 --- a/src/status_im2/contexts/chat/messages/list/view.cljs +++ b/src/status_im2/contexts/chat/messages/list/view.cljs @@ -8,11 +8,13 @@ [react-native.platform :as platform] [reagent.core :as reagent] [status-im.ui.screens.chat.group :as chat.group] - [status-im.ui.screens.chat.message.gap :as gap] [status-im.ui.screens.chat.state :as state] - [status-im.ui2.screens.chat.messages.message :as message] - [status-im2.common.constants :as constants] ;;TODO move to status-im2 - [utils.re-frame :as rf])) + [status-im2.contexts.chat.messages.content.view :as message] + [status-im2.common.constants :as constants] + [utils.re-frame :as rf] + [status-im2.contexts.chat.messages.content.deleted.view :as content.deleted] + [status-im.ui.screens.chat.message.gap :as message.gap] + [status-im2.common.not-implemented :as not-implemented])) (defonce messages-list-ref (atom nil)) @@ -112,39 +114,23 @@ [rn/view {:style (when platform/android? {:scaleY -1})} [chat.group/group-chat-footer chat-id invitation-admin]])) -(defn render-fn - [{:keys [outgoing type] :as message} - idx - _ - {:keys [group-chat public? community? current-public-key - chat-id show-input? message-pin-enabled edit-enabled in-pinned-view? - can-delete-message-for-everyone?]}] - [rn/view {:style (when (and platform/android? (not in-pinned-view?)) {:scaleY -1})} +(defn render-fn [{:keys [type value deleted? deleted-for-me? content-type] :as message-data} _ _ context] + [rn/view {:style (when platform/android? {:scaleY -1})} (if (= type :datemark) - [quo/divider-date (:value message)] - (if (= type :gap) - ;; TODO (flexsurfer) new gap functionality is not implemented yet - [gap/gap message idx messages-list-ref false chat-id] - ; message content - [message/chat-message - (assoc message - :incoming-group (and group-chat (not outgoing)) - :group-chat group-chat - :public? public? - :community? community? - :current-public-key current-public-key - :show-input? show-input? - :message-pin-enabled message-pin-enabled - :edit-enabled edit-enabled - :can-delete-message-for-everyone? can-delete-message-for-everyone?)]))]) + [quo/divider-date value] + (if (= content-type constants/content-type-gap) + [not-implemented/not-implemented + [message.gap/gap message-data]] + [rn/view {:padding-horizontal 8} + (if (or deleted? deleted-for-me?) + [content.deleted/deleted-message message-data] + [message/message-with-reactions message-data context])]))]) -(defn messages-list - [{:keys [chat - bottom-space - pan-responder - mutual-contact-requests-enabled? - show-input?]}] +(defn messages-list [{:keys [chat + pan-responder + show-input?]}] (let [{:keys [group-chat chat-type chat-id public? community-id admins]} chat + mutual-contact-requests-enabled? (rf/sub [:mutual-contact-requests/enabled?]) messages (rf/sub [:chats/raw-chat-messages-stream chat-id]) one-to-one? (= chat-type constants/one-to-one-chat-type) contact-added? (when one-to-one? (rf/sub [:contacts/contact-added? chat-id])) @@ -152,8 +138,9 @@ (and mutual-contact-requests-enabled? one-to-one? - (not contact-added?))] - [rn/view {:style {:flex 1}} + (not contact-added?)) + bottom-space 15] + [:<> ;;DO NOT use anonymous functions for handlers [rn/flat-list (merge diff --git a/src/status_im2/contexts/chat/messages/pin/list/view.cljs b/src/status_im2/contexts/chat/messages/pin/list/view.cljs index c2848a988e..c290e0b9e3 100644 --- a/src/status_im2/contexts/chat/messages/pin/list/view.cljs +++ b/src/status_im2/contexts/chat/messages/pin/list/view.cljs @@ -1,18 +1,36 @@ (ns status-im2.contexts.chat.messages.pin.list.view (:require [i18n.i18n :as i18n] [quo2.core :as quo] - [quo2.foundations.colors :as colors] ;; TODO move to status-im2 + [quo2.foundations.colors :as colors] [react-native.core :as rn] - [status-im.ui2.screens.chat.messages.message :as old-message] - [utils.re-frame :as rf])) + [status-im2.contexts.chat.messages.content.view :as message] + [utils.re-frame :as rf] + [status-im.utils.datetime :as time])) (def list-key-fn #(or (:message-id %) (:value %))) -(defn pinned-messages-list - [chat-id] +(defn message-render-fn + [{:keys [whisper-timestamp] :as message} + _ + {:keys [group-chat public? community? current-public-key show-input? edit-enabled]}] + ;; TODO (flexsurfer) probably we don't want reactions here + [message/message-with-reactions + message + {:group-chat group-chat + :public? public? + :community? community? + :current-public-key current-public-key + :show-input? show-input? + :message-pin-enabled true + :in-pinned-view? true + :pinned true + :timestamp-str (time/timestamp->time whisper-timestamp) + :edit-enabled edit-enabled}]) + +(defn pinned-messages-list [chat-id] (let [pinned-messages (vec (vals (rf/sub [:chats/pinned chat-id]))) - current-chat (rf/sub [:chats/current-chat]) - community (rf/sub [:communities/community (:community-id current-chat)])] + current-chat (rf/sub [:chats/current-chat]) + community (rf/sub [:communities/community (:community-id current-chat)])] [rn/view {:accessibility-label :pinned-messages-list} ;; TODO (flexsurfer) this should be a component in quo2 ;; https://github.com/status-im/status-mobile/issues/14529 @@ -44,7 +62,7 @@ (if (> (count pinned-messages) 0) [rn/flat-list {:data pinned-messages - :render-fn old-message/message-render-fn + :render-fn message-render-fn :key-fn list-key-fn :separator quo/separator}] [rn/view diff --git a/src/status_im2/contexts/chat/messages/view.cljs b/src/status_im2/contexts/chat/messages/view.cljs index 9b36500240..57222eb0ee 100644 --- a/src/status_im2/contexts/chat/messages/view.cljs +++ b/src/status_im2/contexts/chat/messages/view.cljs @@ -10,7 +10,8 @@ [status-im2.contexts.chat.messages.pin.banner.view :as pin.banner] ;;TODO move to status-im2 [status-im2.navigation.state :as navigation.state] [utils.debounce :as debounce] - [utils.re-frame :as rf])) + [utils.re-frame :as rf] + [status-im2.common.not-implemented :as not-implemented])) (defn navigate-back-handler [] @@ -51,28 +52,21 @@ :accessibility-label :back-button} :right-section-buttons - [{:on-press #() ;; TODO not implemented + [{:on-press #() + :style {:border-width 1 :border-color :red} :icon :i/options :accessibility-label :options-button}]}])) -(defn chat-render - [] - (let [;;we want to react only on these fields, do not use full chat map here - {:keys [chat-id show-input?] :as chat} (rf/sub [:chats/current-chat-chat-view]) - mutual-contact-requests-enabled? (rf/sub [:mutual-contact-requests/enabled?])] +(defn chat-render [] + (let [;;NOTE: we want to react only on these fields, do not use full chat map here + {:keys [chat-id show-input?] :as chat} (rf/sub [:chats/current-chat-chat-view])] [rn/keyboard-avoiding-view {:style {:flex 1}} [page-nav] - ;; TODO (flexsurfer) this should be in-app notification component in quo2 - ;; https://github.com/status-im/status-mobile/issues/14527 - [pin-limit-popover/pin-limit-popover chat-id] - [pin.banner/banner chat-id] - ;;MESSAGES LIST - [messages.list/messages-list - {:chat chat - :mutual-contact-requests-enabled? mutual-contact-requests-enabled? - :show-input? show-input? - :bottom-space 15}] - ;;INPUT COMPOSER + [not-implemented/not-implemented + [pin-limit-popover/pin-limit-popover chat-id]] + [not-implemented/not-implemented + [pin.banner/banner chat-id]] + [messages.list/messages-list {:chat chat :show-input? show-input?}] (when show-input? [composer/composer chat-id])])) diff --git a/src/status_im2/contexts/quo_preview/reactions/react.cljs b/src/status_im2/contexts/quo_preview/reactions/react.cljs index a33d50f922..10d2500f56 100644 --- a/src/status_im2/contexts/quo_preview/reactions/react.cljs +++ b/src/status_im2/contexts/quo_preview/reactions/react.cljs @@ -37,7 +37,7 @@ {:padding-vertical 60 :align-items :center} [quo2.reaction/reaction @state] - [quo2.reaction/open-reactions-menu @state]]]]))) + [quo2.reaction/add-reaction @state]]]]))) (defn preview-react [] diff --git a/test/appium/views/chat_view.py b/test/appium/views/chat_view.py index ebcd43ee00..f162a4dbc4 100644 --- a/test/appium/views/chat_view.py +++ b/test/appium/views/chat_view.py @@ -171,7 +171,7 @@ class ChatElementByText(Text): def username(self): class Username(Text): def __init__(self, driver, parent_locator: str): - super().__init__(driver, prefix=parent_locator, xpath="/android.widget.TextView[1]") + super().__init__(driver, prefix=parent_locator, xpath="/android.view.ViewGroup/android.widget.TextView[1]") return Username(self.driver, self.locator)