Improve chat Avatar (#15036)

This commit is contained in:
Ulises Manuel Cárdenas 2023-03-01 14:54:40 -06:00 committed by GitHub
parent aec1b5fafa
commit 7d9709ee67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 164 additions and 173 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 989 B

View File

@ -1,6 +1,5 @@
(ns quo2.components.avatars.user-avatar (ns quo2.components.avatars.user-avatar
(:require [clojure.string :refer [blank? split upper-case]] (:require [clojure.string :as string]
[quo2.components.icon :as icons]
[quo2.components.markdown.text :as text] [quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors] [quo2.foundations.colors :as colors]
[quo2.theme :refer [dark?]] [quo2.theme :refer [dark?]]
@ -40,115 +39,100 @@
:font-size :label}}) :font-size :label}})
(defn dot-indicator (defn dot-indicator
[size status-indicator? online? ring? dark?] [{:keys [size online? ring? dark?]}]
(when status-indicator? (let [dimensions (get-in sizes [size :status-indicator])
(let [dimensions (get-in sizes [size :status-indicator]) border-width (get-in sizes [size :status-indicator-border])
border-width (get-in sizes [size :status-indicator-border]) right (case size
right (case size :big 2
:big 2 :medium 0
:medium 0 :small -2
:small -2 0)
0) bottom (case size
bottom (case size :big (if ring? -1 2)
:big (if ring? :medium (if ring? 0 -2)
-1 :small -2
2) 0)]
:medium (if ring? [rn/view
0 {:style {:background-color (if online?
-2) colors/success-50
:small (if ring? colors/neutral-40)
-2 :width dimensions
-2) :height dimensions
0)] :border-width border-width
[rn/view :border-radius dimensions
{:style {:background-color (if online? :border-color (if dark?
colors/success-50 colors/neutral-100
colors/neutral-40) colors/white)
:width dimensions :position :absolute
:height dimensions :bottom bottom
:border-width border-width :right right}}]))
:border-radius dimensions
:border-color (if dark?
colors/neutral-100
colors/white)
:position :absolute
:bottom bottom
:right right}}])))
(defn container-styling (defn initials-style
[inner-dimensions outer-dimensions] [inner-dimensions outer-dimensions]
{:width inner-dimensions {:position :absolute
:position :absolute :top (/ (- outer-dimensions inner-dimensions) 2)
:top (/ (- outer-dimensions inner-dimensions) 2) :left (/ (- outer-dimensions inner-dimensions) 2)
:left (/ (- outer-dimensions inner-dimensions) 2) :width inner-dimensions
:height inner-dimensions :height inner-dimensions
:border-radius inner-dimensions}) :border-radius inner-dimensions
:justify-content :center
:align-items :center
:background-color (colors/custom-color-by-theme :turquoise 50 60)})
(defn container (defn outer-styles
[inner-dimensions outer-dimensions & children] [outer-dimensions]
[rn/view {:width outer-dimensions
{:style (merge {:background-color (colors/custom-color-by-theme :turquoise 50 60) :height outer-dimensions
:justify-content :center :border-radius outer-dimensions})
:align-items :center}
(container-styling inner-dimensions outer-dimensions))}
children])
(def small-sizes #{:xs :xxs :xxxs}) (def one-initial-letter-sizes #{:xs :xxs :xxxs})
(def identicon-sizes #{:big :medium :small}) (def valid-ring-sizes #{:big :medium :small})
(defn initials-avatar
[{:keys [full-name size inner-dimensions outer-dimensions]}]
(let [amount-initials (if (one-initial-letter-sizes size) 1 2)
initials (as-> full-name $
(string/split $ " ")
(map (comp string/upper-case first) $)
(take amount-initials $)
(string/join $))
font-size (get-in sizes [size :font-size])]
[rn/view {:style (initials-style inner-dimensions outer-dimensions)}
[text/text
{:style {:color colors/white-opa-70}
:weight :semi-bold
:size font-size}
initials]]))
(defn user-avatar (defn user-avatar
[{:keys [ring? "If no `profile-picture` is given, draws the initials based on the `full-name` and
online? uses `ring-background` to display the ring behind the initials when given. Otherwise,
size shows the profile picture which already comes with the ring drawn over it."
status-indicator? [{:keys [full-name status-indicator? online? size profile-picture ring-background]
profile-picture :or {status-indicator? true
full-name]
:or {full-name "empty name"
status-indicator? true
online? true online? true
size :big size :big}}]
ring? true}}] (let [full-name (or full-name "empty name")
(let [initials (if full-name draw-ring? (and ring-background (valid-ring-sizes size))
(reduce str (map first (split full-name " "))) outer-dimensions (get-in sizes [size :outer])
"") inner-dimensions (get-in sizes [size (if draw-ring? :inner :outer)])]
first-initial-letter (if full-name
(or (first full-name) "")
"")
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 [rn/view
{:accessibility-label :user-avatar {:style (outer-styles outer-dimensions)
:style {:width outer-dimensions :accessibility-label :user-avatar}
:height outer-dimensions ;; The `profile-picture` already has the ring in it
:border-radius outer-dimensions}} (when-let [image (or profile-picture ring-background)]
(when (and false (and ring? identicon?)) ;;TODO not implemented yet
[icons/icon :i/identicon-ring
{:size outer-dimensions
:no-color true}])
(if profile-picture
;; display image
[fast-image/fast-image [fast-image/fast-image
{:source profile-picture {:style (outer-styles outer-dimensions)
:style (container-styling inner-dimensions outer-dimensions)}] :source image}])
;; else display initials (when-not profile-picture
[container inner-dimensions outer-dimensions [initials-avatar
^{:key :icon-text} {:full-name full-name
[text/text :size size
{:weight :semi-bold :inner-dimensions inner-dimensions
:size font-size :outer-dimensions outer-dimensions}])
:style {:color colors/white-opa-70}} (when status-indicator?
(upper-case icon-text)]]) [dot-indicator
[dot-indicator size status-indicator? online? ring? (dark?)]])) {:size size
:online? online?
:ring? draw-ring?
:dark? (dark?)}])]))

View File

@ -49,8 +49,7 @@
:profile-picture photo-path :profile-picture photo-path
:status-indicator? true :status-indicator? true
:online? online? :online? online?
:size :small :size :small}]
:ring? false}]
[rn/view {:style {:margin-left 8}} [rn/view {:style {:margin-left 8}}
[author/author [author/author
{:primary-name primary-name {:primary-name primary-name

View File

@ -25,8 +25,7 @@
:profile-picture profile-picture :profile-picture profile-picture
:override-theme :dark :override-theme :dark
:size :medium :size :medium
:status-indicator? false :status-indicator? false}]
:ring? true}]
[button/button [button/button
{:size 32 {:size 32
:type :blur-bg :type :blur-bg

View File

@ -24,29 +24,31 @@
(let [internal-selected? (reagent/atom (or default-selected? false))] (let [internal-selected? (reagent/atom (or default-selected? false))]
(fn [{:keys [selected? (fn [{:keys [selected?
profile-picture profile-picture
ring-background
name name
customization-color customization-color
on-change] on-change]
:or {customization-color :turquoise}}] :or {customization-color :turquoise}}]
(when (and (not (nil? selected?)) (not= @internal-selected? selected?)) (let [avatar-image-key (if profile-picture :profile-picture :ring-background)
(reset! internal-selected? selected?)) picture (or profile-picture ring-background)]
[rn/touchable-opacity (when (and (not (nil? selected?)) (not= @internal-selected? selected?))
{:style (style/container customization-color @internal-selected?) (reset! internal-selected? selected?))
:on-press #(on-change-handler internal-selected? on-change) [rn/touchable-opacity
:active-opacity 1 {:style (style/container customization-color @internal-selected?)
:accessibility-label :select-profile} :on-press #(on-change-handler internal-selected? on-change)
[rn/view {:style style/header} :active-opacity 1
[user-avatar/user-avatar :accessibility-label :select-profile}
{:full-name name [rn/view {:style style/header}
:profile-picture profile-picture [user-avatar/user-avatar
:status-indicator? false {:full-name name
:ring? true :status-indicator? false
:size :medium}] :size :medium
[rn/view {:style (style/select-radio @internal-selected?)} avatar-image-key picture}]
(when @internal-selected? [rn/view {:style style/select-radio-inner}])]] [rn/view {:style (style/select-radio @internal-selected?)}
[text/text (when @internal-selected? [rn/view {:style style/select-radio-inner}])]]
{:size :heading-2 [text/text
:weight :semi-bold {:size :heading-2
:style style/profile-name :weight :semi-bold
} name]]))) :style style/profile-name}
name]]))))

View File

@ -69,7 +69,8 @@
:user-picture-male5 (js/require "../resources/images/mock/user_picture_male5.png") :user-picture-male5 (js/require "../resources/images/mock/user_picture_male5.png")
:coinbase (js/require "../resources/images/mock/coinbase.png") :coinbase (js/require "../resources/images/mock/coinbase.png")
:small-opt-card-icon (js/require "../resources/images/mock/small_opt_card_icon.png") :small-opt-card-icon (js/require "../resources/images/mock/small_opt_card_icon.png")
:small-opt-card-main (js/require "../resources/images/mock/small_opt_card_main.png")}) :small-opt-card-main (js/require "../resources/images/mock/small_opt_card_main.png")
:ring (js/require "../resources/images/mock/ring.png")})
(defn get-theme-image (defn get-theme-image
[k] [k]

View File

@ -52,10 +52,7 @@
new-notifications? (pos? notif-count) new-notifications? (pos? notif-count)
notification-indicator :unread-dot notification-indicator :unread-dot
counter-label "0"] counter-label "0"]
[rn/view [rn/view {:style (assoc style :height 56)}
{:style (merge
{:height 56}
style)}
;; Left Section ;; Left Section
[rn/touchable-without-feedback {:on-press open-profile} [rn/touchable-without-feedback {:on-press open-profile}
[rn/view [rn/view
@ -64,8 +61,7 @@
:top 12}} :top 12}}
[quo/user-avatar [quo/user-avatar
(merge (merge
{:ring? true {:status-indicator? true
:status-indicator? true
:size :small} :size :small}
avatar)]]] avatar)]]]
;; Right Section ;; Right Section

View File

@ -102,33 +102,39 @@
(datetime/to-short-str timestamp)]]) (datetime/to-short-str timestamp)]])
(defn avatar-view (defn avatar-view
[group-chat color display-name photo-path chat-id] [{:keys [contact chat-id full-name color]}]
(if group-chat (if contact ; `contact` is passed when it's not a group chat
(let [online? (rf/sub [:visibility-status-updates/online? chat-id])
photo-path (rf/sub [:chats/photo-path chat-id])
image-key (if (seq (:images contact)) :profile-picture :ring-background)]
[quo/user-avatar
{:full-name full-name
:size :small
:online? online?
image-key photo-path}])
[quo/group-avatar [quo/group-avatar
{:color color {:color color
:size :medium}] :size :medium}]))
(let [online? (rf/sub [:visibility-status-updates/online? chat-id])]
[quo/user-avatar
{:full-name display-name
:online? online?
:profile-picture photo-path
:size :small}])))
(defn chat-list-item (defn chat-list-item
[item] [{:keys [chat-id group-chat color name unviewed-messages-count unviewed-mentions-count
(let [{:keys [chat-id color group-chat last-message timestamp name unviewed-mentions-count timestamp last-message]
unviewed-messages-count]} :as item}]
item (let [display-name (if group-chat
display-name name
(if group-chat name (first (rf/sub [:contacts/contact-two-names-by-identity chat-id]))) (first (rf/sub [:contacts/contact-two-names-by-identity chat-id])))
contact (when-not group-chat (rf/sub [:contacts/contact-by-address chat-id])) contact (when-not group-chat
photo-path (when-not (empty? (:images contact)) (rf/sub [:chats/photo-path chat-id]))] (rf/sub [:contacts/contact-by-address chat-id]))]
[rn/touchable-opacity [rn/touchable-opacity
(merge {:style (style/container) {:style (style/container)
:on-press (open-chat chat-id) :on-press (open-chat chat-id)
:on-long-press #(rf/dispatch [:bottom-sheet/show-sheet :on-long-press #(rf/dispatch [:bottom-sheet/show-sheet
{:content (fn [] [actions/chat-actions item false])}])}) {:content (fn [] [actions/chat-actions item false])}])}
[avatar-view group-chat color display-name photo-path chat-id] [avatar-view
{:contact contact
:chat-id chat-id
:full-name display-name
:color color}]
[rn/view {:style {:margin-left 8}} [rn/view {:style {:margin-left 8}}
[name-view display-name contact timestamp] [name-view display-name contact timestamp]
(if (string/blank? (get-in last-message [:content :parsed-text])) (if (string/blank? (get-in last-message [:content :parsed-text]))

View File

@ -17,7 +17,6 @@
{:full-name display-name {:full-name display-name
:profile-picture profile-picture :profile-picture profile-picture
:status-indicator? false :status-indicator? false
:ring? false
:size :xxxs}]] :size :xxxs}]]
[quo/author {:primary-name display-name}] [quo/author {:primary-name display-name}]
[quo/text [quo/text

View File

@ -39,8 +39,7 @@
:profile-picture photo-path :profile-picture photo-path
:status-indicator? true :status-indicator? true
:online? online? :online? online?
:size :small :size :small}]]])
:ring? false}]]])
[rn/view {:padding-top 2 :width 32}])) [rn/view {:padding-top 2 :width 32}]))
(defn author (defn author

View File

@ -32,19 +32,22 @@
(first (rf/sub [:contacts/contact-two-names-by-identity chat-id])) (first (rf/sub [:contacts/contact-two-names-by-identity chat-id]))
(str emoji " " chat-name)) (str emoji " " chat-name))
online? (rf/sub [:visibility-status-updates/online? chat-id]) online? (rf/sub [:visibility-status-updates/online? chat-id])
contact (when-not group-chat (rf/sub [:contacts/contact-by-address chat-id])) contact (when-not group-chat
photo-path (when-not (empty? (:images contact)) (rf/sub [:chats/photo-path chat-id]))] (rf/sub [:contacts/contact-by-address chat-id]))
photo-path (rf/sub [:chats/photo-path chat-id])
avatar-image-key (if (seq (:images contact))
:profile-picture
:ring-background)]
[quo/page-nav [quo/page-nav
{:align-mid? true {:align-mid? true
:mid-section (if group-chat :mid-section (if group-chat
{:type :text-only {:type :text-only
:main-text display-name} :main-text display-name}
{:type :user-avatar {:type :user-avatar
:avatar {:full-name display-name :avatar {:full-name display-name
:online? online? :online? online?
:profile-picture photo-path :size :medium
:size :medium} avatar-image-key photo-path}
:main-text display-name :main-text display-name
:on-press #(debounce/dispatch-and-chill [:chat.ui/show-profile chat-id] :on-press #(debounce/dispatch-and-chill [:chat.ui/show-profile chat-id]
1000)}) 1000)})

View File

@ -28,7 +28,7 @@
{:label "Status Indicator" {:label "Status Indicator"
:key :status-indicator? :key :status-indicator?
:type :boolean} :type :boolean}
{:label "Identicon Ring" {:label "Identicon Ring (applies only when there's no profile picture)"
:key :ring? :key :ring?
:type :boolean} :type :boolean}
{:label "Full name separated by space" {:label "Full name separated by space"
@ -49,8 +49,7 @@
(let [state (reagent/atom {:full-name "A Y" (let [state (reagent/atom {:full-name "A Y"
:status-indicator? true :status-indicator? true
:online? true :online? true
:size :medium :size :medium})]
:ring? true})]
(fn [] (fn []
[rn/touchable-without-feedback {:on-press rn/dismiss-keyboard!} [rn/touchable-without-feedback {:on-press rn/dismiss-keyboard!}
[rn/view {:padding-bottom 150} [rn/view {:padding-bottom 150}
@ -60,7 +59,12 @@
{:padding-vertical 60 {:padding-vertical 60
:flex-direction :row :flex-direction :row
:justify-content :center} :justify-content :center}
[quo2/user-avatar @state]]]]))) (let [{:keys [profile-picture ring?]} @state
ring-bg (resources/get-mock-image :ring)
params (cond-> @state
(and (not profile-picture) ring?)
(assoc :ring-background ring-bg))]
[quo2/user-avatar params])]]])))
(defn preview-user-avatar (defn preview-user-avatar
[] []

View File

@ -109,8 +109,7 @@
(case type (case type
shell.constants/one-to-one-chat-card shell.constants/one-to-one-chat-card
[quo/user-avatar [quo/user-avatar
(merge {:ring? false (merge {:size :medium
:size :medium
:status-indicator? false} :status-indicator? false}
avatar-params)] avatar-params)]