diff --git a/resources/images/icons/delete16@2x.png b/resources/images/icons/delete16@2x.png new file mode 100644 index 0000000000..45b9ad9b0b Binary files /dev/null and b/resources/images/icons/delete16@2x.png differ diff --git a/resources/images/icons/delete16@3x.png b/resources/images/icons/delete16@3x.png new file mode 100644 index 0000000000..059a32bf6d Binary files /dev/null and b/resources/images/icons/delete16@3x.png differ diff --git a/resources/images/icons/pin16@2x.png b/resources/images/icons/pin16@2x.png new file mode 100644 index 0000000000..f5a304209e Binary files /dev/null and b/resources/images/icons/pin16@2x.png differ diff --git a/resources/images/icons/pin16@3x.png b/resources/images/icons/pin16@3x.png new file mode 100644 index 0000000000..0c184a1788 Binary files /dev/null and b/resources/images/icons/pin16@3x.png differ diff --git a/resources/images/icons/timeout12@2x.png b/resources/images/icons/timeout12@2x.png new file mode 100644 index 0000000000..5a09af30b0 Binary files /dev/null and b/resources/images/icons/timeout12@2x.png differ diff --git a/resources/images/icons/timeout12@3x.png b/resources/images/icons/timeout12@3x.png new file mode 100644 index 0000000000..1fe2e6d302 Binary files /dev/null and b/resources/images/icons/timeout12@3x.png differ diff --git a/resources/images/mock/user_picture_female2@2x.png b/resources/images/mock/user_picture_female2@2x.png new file mode 100644 index 0000000000..8c92662bc6 Binary files /dev/null and b/resources/images/mock/user_picture_female2@2x.png differ diff --git a/resources/images/mock/user_picture_male4@2x.png b/resources/images/mock/user_picture_male4@2x.png new file mode 100644 index 0000000000..6dd80d64f4 Binary files /dev/null and b/resources/images/mock/user_picture_male4@2x.png differ diff --git a/resources/images/mock/user_picture_male5@2x.png b/resources/images/mock/user_picture_male5@2x.png new file mode 100644 index 0000000000..2eff870140 Binary files /dev/null and b/resources/images/mock/user_picture_male5@2x.png differ diff --git a/src/quo2/components/avatars/icon_avatar.cljs b/src/quo2/components/avatars/icon_avatar.cljs index 722aa63b2d..06ef028360 100644 --- a/src/quo2/components/avatars/icon_avatar.cljs +++ b/src/quo2/components/avatars/icon_avatar.cljs @@ -9,9 +9,10 @@ :small 20}) (defn icon-avatar - [{:keys [size icon color]}] + [{:keys [size icon color opacity] + :or {opacity 20}}] (let [component-size (size sizes) - circle-color (colors/custom-color color 50 20) + circle-color (colors/custom-color color 50 opacity) icon-color (colors/custom-color-by-theme color 50 60) icon-size (case size :big 20 @@ -25,4 +26,4 @@ :align-items :center}} [icons/icon icon {:container-style {:width icon-size :height icon-size} - :color icon-color}]])) + :color icon-color}]])) diff --git a/src/quo2/components/avatars/user_avatar.cljs b/src/quo2/components/avatars/user_avatar.cljs index f67131ea0f..09024e18df 100644 --- a/src/quo2/components/avatars/user_avatar.cljs +++ b/src/quo2/components/avatars/user_avatar.cljs @@ -4,8 +4,7 @@ [quo2.foundations.colors :as colors] [quo2.components.icon :as icons] [clojure.string :refer [upper-case split blank?]] - [quo.theme :refer [dark?]] - [status-im.ui.components.react :as react])) + [quo.theme :refer [dark?]])) (def sizes {:big {:outer 80 :inner 72 @@ -98,47 +97,46 @@ status-indicator? profile-picture full-name] - :or {full-name "empty name" - status-indicator? true - online? true - size :big - ring? true}}] - (let [initials (if full-name - (reduce str (map first (split full-name " "))) - "") + :or {full-name "empty name" + status-indicator? true + online? true + size :big + ring? true}}] + (let [initials (if full-name + (reduce str (map first (split full-name " "))) + "") first-initial-letter (if full-name (or (first full-name) "") "") - identicon? (contains? identicon-sizes size) - small? (contains? small-sizes size) - using-profile-picture? (-> profile-picture - blank? - false?) - outer-dimensions (get-in sizes [size :outer]) - inner-dimensions (get-in sizes [size (if ring? - :inner - :outer)]) - font-size (get-in sizes [size :font-size]) - icon-text (if-not (or (blank? first-initial-letter) - (blank? initials)) - (if small? - first-initial-letter - initials) - "")] - [rn/view {:style {:width outer-dimensions - :height outer-dimensions + identicon? (contains? identicon-sizes size) + small? (contains? small-sizes size) + outer-dimensions (get-in sizes [size :outer]) + inner-dimensions (get-in sizes [size (if ring? + :inner + :outer)]) + font-size (get-in sizes [size :font-size]) + icon-text (if-not (or (blank? first-initial-letter) + (blank? initials)) + (if small? + first-initial-letter + initials) + "")] + [rn/view {:style {:width outer-dimensions + :height outer-dimensions :border-radius outer-dimensions}} (when (and ring? identicon?) - [icons/icon :main-icons/identicon-ring {:width outer-dimensions - :height outer-dimensions - :size outer-dimensions + [icons/icon :main-icons/identicon-ring {:width outer-dimensions + :height outer-dimensions + :size outer-dimensions :no-color true}]) - (if using-profile-picture? - [react/image {:style (container-styling inner-dimensions outer-dimensions) - :source {:uri profile-picture}}] + (if profile-picture + ;; display image + [rn/image {:style (container-styling inner-dimensions outer-dimensions) + :source profile-picture}] + ;; else display initials [container inner-dimensions outer-dimensions [text/text {:weight :semi-bold - :size font-size - :style {:color colors/white-opa-70}} + :size font-size + :style {:color colors/white-opa-70}} (upper-case icon-text)]]) [dot-indicator size status-indicator? online? ring? (dark?)]])) diff --git a/src/quo2/components/messages/system_message.cljs b/src/quo2/components/messages/system_message.cljs new file mode 100644 index 0000000000..ef6dc5db55 --- /dev/null +++ b/src/quo2/components/messages/system_message.cljs @@ -0,0 +1,162 @@ +(ns quo2.components.messages.system-message + (:require [status-im.i18n.i18n :as i18n] + [quo.react-native :as rn] + [status-im.utils.core :as utils] + [quo.theme :as theme] + [quo2.components.buttons.button :as button] + [quo2.components.markdown.text :as text] + [quo2.reanimated :as ra] + [quo2.foundations.colors :as colors] + [quo2.components.avatars.user-avatar :as user-avatar] + [quo2.components.avatars.icon-avatar :as icon-avatar])) + +(def themes-landed {:pinned colors/primary-50-opa-5 + :added colors/primary-50-opa-5 + :deleted colors/danger-50-opa-5}) + +(def themes + {:light {:text colors/neutral-100 + :time colors/neutral-50 + :bg {:default colors/white + :pressed colors/neutral-5 + :landed themes-landed}} + :dark {:text colors/white + :time colors/neutral-40 + :bg {:default colors/neutral-90 + :pressed colors/neutral-80 + :landed themes-landed}}}) + +(defn get-color [& keys] + (reduce (fn [acc k] (get acc k (reduced acc))) + ((theme/get-theme) themes) (vec keys))) + +(defn sm-timestamp [timestamp-str] + [rn/view {:margin-left 6} + [text/text {:size :label + :style {:color (get-color :time) + :text-transform :none}} + timestamp-str]]) + +(defn sm-icon [{:keys [icon color opacity]}] + [rn/view {:align-items :center + :margin-right 8} + [icon-avatar/icon-avatar {:size :medium + :icon icon + :color color + :opacity opacity}]]) + +(defn sm-user-avatar [image] + [rn/view {:margin-right 4} + [user-avatar/user-avatar {:status-indicator? false + :online? false + :size :xxxs + :profile-picture image + :ring? false}]]) + +(defmulti sm-render :type) + +(defmethod sm-render :deleted [{:keys [state action timestamp-str]}] + [rn/view {:align-items :center + :justify-content :space-between + :flex 1 + :flex-direction :row} + [rn/view {:align-items :center + :flex-direction :row} + [sm-icon {:icon :main-icons/delete16 + :color :danger + :opacity (if (= state :landed) 0 5)}] + [text/text {:size :paragraph-2 + :style {:color (get-color :text) + :margin-right 5}} + (i18n/label (if action :message-deleted-for-you :message-deleted))] + (when (nil? action) [sm-timestamp timestamp-str])] + (when action [button/button {:size 24 + :before :main-icons/timeout + :type :grey} (i18n/label :undo)])]) + +(defmethod sm-render :added [{:keys [state mentions timestamp-str]}] + [rn/view {:align-items :center + :flex-direction :row} + [sm-icon {:icon :main-icons/add-user16 + :color :primary + :opacity (if (= state :landed) 0 5)}] + [sm-user-avatar (:image (first mentions))] + [text/text {:weight :semi-bold + :size :paragraph-2} + (:name (first mentions))] + [text/text {:size :paragraph-2 + :style {:color (get-color :text) + :margin-left 3 + :margin-right 3}} + (i18n/label :added)] + [sm-user-avatar (:image (second mentions))] + [text/text {:weight :semi-bold + :size :paragraph-2} + (:name (second mentions))] + [sm-timestamp timestamp-str]]) + +(defmethod sm-render :pinned [{:keys [state pinned-by content timestamp-str]}] + [rn/view {:flex-direction :row + :flex 1 + :align-items :center} + [sm-icon {:icon :main-icons/pin16 + :color :primary + :opacity (if (= state :landed) 0 5)}] + [rn/view {:flex-direction :column + :flex 1} + [rn/view {:align-items :baseline + :flex-direction :row} + [text/text {:size :paragraph-2 + :weight :semi-bold + :style {:color (get-color :text)}} + (utils/truncate-str pinned-by 18)] + [rn/view {:margin-left 4 + :margin-right 2} + [text/text {:size :paragraph-2 + :style {:color (get-color :text)}} + (i18n/label :pinned-a-message)]] + [sm-timestamp timestamp-str]] + [rn/view {:flex-direction :row} + [rn/view {:flex-direction :row + :margin-right 4} + [sm-user-avatar (:image (:mentions content))] + [text/text {:weight :semi-bold + :size :label} + (:name (:mentions content))]] + (when (seq (:text content)) + [rn/view {:margin-right 20 + :flex-direction :row + :flex 1} + [text/text {:size :label + :style {:color (get-color :text)} + :number-of-lines 1 + :ellipsize-mode :tail} + (:text content)]]) + [rn/view {:justify-content :flex-end + :flex-direction :row + :min-width 10} + (when (seq (:info content)) + [text/text {:size :label + :style {:color (get-color :time)}} + (utils/truncate-str (:info content) 24)])]]]]) + +(defn system-message [{:keys [type] :as message}] + [:f> + (fn [] + (let [sv-color (ra/use-shared-value (get-color :bg :landed type))] + (ra/animate-shared-value-with-delay + sv-color (get-color :bg :default type) 0 :linear 1000) + [ra/touchable-opacity + {:on-press #(ra/set-shared-value + sv-color (get-color :bg :pressed type)) + :style (ra/apply-animations-to-style + {:background-color sv-color} + {:flex-direction :row + :flex 1 + :border-radius 16 + :padding-vertical 9 + :padding-horizontal 11 + :width 359 + :height 52 + :background-color sv-color})} + [sm-render message]]))]) diff --git a/src/quo2/foundations/colors.cljs b/src/quo2/foundations/colors.cljs index bc4a28091a..6b5bd0f969 100644 --- a/src/quo2/foundations/colors.cljs +++ b/src/quo2/foundations/colors.cljs @@ -119,6 +119,7 @@ ;;;; Customization +;; Colors for customizing profiles and communities themes (def customization {:primary {50 primary-50 ;; User can also use primary color as customisation color 60 primary-60} @@ -145,6 +146,11 @@ :beige {50 "#CAAE93" 60 "#AA927C"}}) +(def colors-map (merge {:danger {50 danger-50 + 60 danger-60} + :success {50 success-50 + 60 success-60}} customization)) + (def custom-color "(custom-color color suffix opacity) color :primary/:purple/... @@ -155,7 +161,7 @@ ([color suffix] (custom-color color suffix nil)) ([color suffix opacity] - (let [base-color (get-in customization [(keyword color) suffix])] + (let [base-color (get-in colors-map [(keyword color) suffix])] (if opacity (alpha base-color (/ opacity 100)) base-color)))))) (defn custom-color-by-theme diff --git a/src/quo2/screens/avatars/icon_avatar.cljs b/src/quo2/screens/avatars/icon_avatar.cljs index 6dd0ea69aa..5a658cf67b 100644 --- a/src/quo2/screens/avatars/icon_avatar.cljs +++ b/src/quo2/screens/avatars/icon_avatar.cljs @@ -19,7 +19,7 @@ :type :select :options [{:key :main-icons/placeholder20 :value "Placeholder"} - {:key :main-icons/walelt + {:key :main-icons/wallet :value "Wallet"} {:key :main-icons/play :value "Play"}]} diff --git a/src/quo2/screens/avatars/user_avatar.cljs b/src/quo2/screens/avatars/user_avatar.cljs index 411a9643bd..50e7caaf0d 100644 --- a/src/quo2/screens/avatars/user_avatar.cljs +++ b/src/quo2/screens/avatars/user_avatar.cljs @@ -3,7 +3,8 @@ [quo2.foundations.colors :as colors] [quo.previews.preview :as preview] [quo2.components.avatars.user-avatar :as quo2] - [reagent.core :as reagent])) + [reagent.core :as reagent] + [status-im.react-native.resources :as resources])) (def descriptor [{:label "Size:" :key :size @@ -32,9 +33,15 @@ {:label "Full name separated by space" :key :full-name :type :text} - {:label "Profile Picture URL" - :key :profile-picture - :type :text}]) + {:label "Profile Picture" + :key :profile-picture + :type :select + :options [{:value "None" + :key nil} + {:value "Alicia Keys" + :key (resources/get-mock-image :user-picture-female2)} + {:value "pedro.eth" + :key (resources/get-mock-image :user-picture-male4)}]}]) (defn cool-preview [] (let [state (reagent/atom {:full-name "A Y" diff --git a/src/quo2/screens/main.cljs b/src/quo2/screens/main.cljs index 586c6eb318..700567abb2 100644 --- a/src/quo2/screens/main.cljs +++ b/src/quo2/screens/main.cljs @@ -25,6 +25,7 @@ [quo2.screens.list-items.channel :as channel] [quo2.screens.markdown.text :as text] [quo2.screens.messages.gap :as messages-gap] + [quo2.screens.messages.system-message :as system-message] [quo2.screens.notifications.activity-logs :as activity-logs] [quo2.screens.reactions.react :as react] [quo2.screens.selectors.disclaimer :as disclaimer] @@ -96,7 +97,10 @@ :component text/preview-text}] :messages [{:name :gap :insets {:top false} - :component messages-gap/preview-messages-gap}] + :component messages-gap/preview-messages-gap} + {:name :system-messages + :insets {:top false} + :component system-message/preview-system-message}] :navigation [{:name :bottom-nav-tab :insets {:top false} :component bottom-nav-tab/preview-bottom-nav-tab} diff --git a/src/quo2/screens/messages/system_message.cljs b/src/quo2/screens/messages/system_message.cljs new file mode 100644 index 0000000000..987dffc29e --- /dev/null +++ b/src/quo2/screens/messages/system_message.cljs @@ -0,0 +1,68 @@ +(ns quo2.screens.messages.system-message + (:require [reagent.core :as reagent] + [status-im.react-native.resources :as resources] + [quo.react-native :as rn] + [quo.previews.preview :as preview] + [quo2.components.messages.system-message :as system-message] + [quo2.foundations.colors :as colors])) + +(def descriptor [{:label "Message Type" + :key :type + :type :select + :options [{:value "Message pinned" + :key :pinned} + {:value "User added" + :key :added} + {:value "Message deleted" + :key :deleted}]} + {:label "Action" + :key :action + :type :select + :options [{:value "none" + :key nil} + {:value "Undo" + :key :undo}]} + {:label "Pinned By" + :key :pinned-by + :type :text} + {:label "Content Text" + :key :content-text + :type :text} + {:label "Content Info" + :key :content-info + :type :text} + {:label "Timestamp" + :key :timestamp-str + :type :text}]) + +(defn finalize-state [state] + (merge @state + {:mentions [{:name "Alicia Keys" + :image (resources/get-mock-image :user-picture-female2)} + {:name "pedro.eth" + :image (resources/get-mock-image :user-picture-male4)}] + :content {:text (:content-text @state) + :info (:content-info @state) + :mentions {:name "Alisher" + :image (resources/get-mock-image :user-picture-male5)}}})) +(defn preview [] + (let [state (reagent/atom {:type :deleted + :pinned-by "Steve" + :content-text "Hello! This is an example of a pinned message!" + :content-info "3 photos" + :timestamp-str "09:41"})] + (fn [] + [rn/touchable-without-feedback {:on-press rn/dismiss-keyboard!} + [rn/view {:padding-bottom 150} + [preview/customizer state descriptor] + [rn/view {:padding-vertical 60 + :align-items :center} + [system-message/system-message (finalize-state state)]]]]))) + +(defn preview-system-message [] + [rn/view {:background-color (colors/theme-colors colors/white colors/neutral-90) + :flex 1} + [rn/flat-list {:flex 1 + :header [preview] + :key-fn str + :keyboardShouldPersistTaps :always}]]) diff --git a/src/status_im/react_native/resources.cljs b/src/status_im/react_native/resources.cljs index d8b0aebe5c..d81e251b5c 100644 --- a/src/status_im/react_native/resources.cljs +++ b/src/status_im/react_native/resources.cljs @@ -55,13 +55,16 @@ :podcasts (js/require "../resources/images/ui/podcasts.png")}) (def mock-images - {:photo1 (js/require "../resources/images/mock/photo1.png") - :photo2 (js/require "../resources/images/mock/photo2.png") - :photo3 (js/require "../resources/images/mock/photo3.png") - :community-banner (js/require "../resources/images/mock/community-banner.png") - :community-logo (js/require "../resources/images/mock/community-logo.png") - :gif (js/require "../resources/images/mock/gif.png") - :sticker (js/require "../resources/images/mock/sticker.png")}) + {:photo1 (js/require "../resources/images/mock/photo1.png") + :photo2 (js/require "../resources/images/mock/photo2.png") + :photo3 (js/require "../resources/images/mock/photo3.png") + :community-banner (js/require "../resources/images/mock/community-banner.png") + :community-logo (js/require "../resources/images/mock/community-logo.png") + :gif (js/require "../resources/images/mock/gif.png") + :sticker (js/require "../resources/images/mock/sticker.png") + :user-picture-female2 (js/require "../resources/images/mock/user_picture_female2.png") + :user-picture-male4 (js/require "../resources/images/mock/user_picture_male4.png") + :user-picture-male5 (js/require "../resources/images/mock/user_picture_male5.png")}) (defn get-theme-image [k] (get ui (when (colors/dark?) (keyword (str (name k) "-dark"))) (get ui k))) diff --git a/translations/en.json b/translations/en.json index b4dcd37dc3..3d7010e905 100644 --- a/translations/en.json +++ b/translations/en.json @@ -20,6 +20,7 @@ "active-online": "Online", "active-unknown": "Unknown", "add": "Add", + "added": "added", "add-a-watch-account": "Add a watch-only address", "add-account": "Add an account", "add-account-description": "You can import any type of Ethereum account to add it to your Status wallet", @@ -856,6 +857,8 @@ "members-active-none": "no members", "members-title": "Members", "message": "Message", + "message-deleted": "Message deleted", + "message-deleted-for-you": "Message deleted for you", "message-not-sent": "Message not sent", "message-options-cancel": "Cancel", "message-reply": "Reply", @@ -1302,6 +1305,7 @@ "last-backup-performed": "Last backup performed:", "unable-to-read-this-code": "Unable to read this code", "unblock-contact": "Unblock this user", + "undo": "Undo", "unknown-status-go-error": "Unknown status-go error", "unlock": "Unlock", "unpair-card": "Unpair card", @@ -1653,6 +1657,7 @@ "accept-and-continue": "Accept and continue", "empty-activity-center": "Your chat notifications\nwill appear here", "pinned-messages": "Pinned messages", + "pinned-a-message": "pinned a message", "pin": "Pin", "unpin": "Unpin", "no-pinned-messages": "No pinned messages",