From 0115e51c36d2ade9bd33716346465a9f80a80e48 Mon Sep 17 00:00:00 2001 From: mjac0bs Date: Mon, 19 Aug 2024 15:24:34 -0700 Subject: [PATCH 01/46] Swap out gravatar for colored avatar on Profile > Display --- packages/manager/src/components/Avatar.tsx | 39 +++++++++++ .../manager/src/components/ColorPicker.tsx | 42 ++++++++++++ .../AvatarColorPickerDialog.tsx | 68 +++++++++++++++++++ .../DisplaySettings/DisplaySettings.tsx | 52 +++++++++++--- .../manager/src/types/ManagerPreferences.ts | 1 + 5 files changed, 192 insertions(+), 10 deletions(-) create mode 100644 packages/manager/src/components/Avatar.tsx create mode 100644 packages/manager/src/components/ColorPicker.tsx create mode 100644 packages/manager/src/features/Profile/DisplaySettings/AvatarColorPickerDialog.tsx diff --git a/packages/manager/src/components/Avatar.tsx b/packages/manager/src/components/Avatar.tsx new file mode 100644 index 00000000000..9d6635555e3 --- /dev/null +++ b/packages/manager/src/components/Avatar.tsx @@ -0,0 +1,39 @@ +import { Typography, useTheme } from '@mui/material'; +import { default as _Avatar } from '@mui/material/Avatar'; +import * as React from 'react'; + +import { usePreferences } from 'src/queries/profile/preferences'; +import { useProfile } from 'src/queries/profile/profile'; + +export const DEFAULT_AVATAR_SIZE = 28; + +interface Props { + className?: string; + height?: number; + width?: number; +} + +export const Avatar = (props: Props) => { + const { + className, + height = DEFAULT_AVATAR_SIZE, + width = DEFAULT_AVATAR_SIZE, + } = props; + + const theme = useTheme(); + const { data: preferences } = usePreferences(); + const { data: profile } = useProfile(); + + const avatarLetter = profile?.username[0].toUpperCase(); + const avatarColor = preferences?.avatarColor ?? theme.color.blue; + + return ( + <_Avatar + alt={`Avatar for user ${profile?.email ?? profile?.username ?? ''}`} + className={className} + sx={{ bgcolor: avatarColor, height, width }} + > + {avatarLetter} + + ); +}; diff --git a/packages/manager/src/components/ColorPicker.tsx b/packages/manager/src/components/ColorPicker.tsx new file mode 100644 index 00000000000..71b16aeda2c --- /dev/null +++ b/packages/manager/src/components/ColorPicker.tsx @@ -0,0 +1,42 @@ +import { useTheme } from '@mui/material'; +import React, { useState } from 'react'; + +import { usePreferences } from 'src/queries/profile/preferences'; + +import type { SxProps } from '@mui/material'; + +interface Props { + handleColorChange: (color: string) => void; + hideLabel?: boolean; // TODO: visually hidden + label: string; + sx?: SxProps; +} + +export const ColorPicker = (props: Props) => { + const { handleColorChange, label } = props; + + const { data: preferences } = usePreferences(); + const theme = useTheme(); + + // TODO: figure out why default isn't working + const [color, setColor] = useState( + preferences?.avatarColor ?? theme.color.blue + ); + + return ( + <> + + { + setColor(e.target.value); + handleColorChange(e.target.value); + }} + color={color} + id="color-picker" + type="color" + /> + + ); +}; diff --git a/packages/manager/src/features/Profile/DisplaySettings/AvatarColorPickerDialog.tsx b/packages/manager/src/features/Profile/DisplaySettings/AvatarColorPickerDialog.tsx new file mode 100644 index 00000000000..60b474ac4ec --- /dev/null +++ b/packages/manager/src/features/Profile/DisplaySettings/AvatarColorPickerDialog.tsx @@ -0,0 +1,68 @@ +import { Typography } from '@mui/material'; +import React from 'react'; +import { useState } from 'react'; +import { debounce } from 'throttle-debounce'; + +import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel'; +import { ColorPicker } from 'src/components/ColorPicker'; +import { Dialog } from 'src/components/Dialog/Dialog'; +import { useMutatePreferences } from 'src/queries/profile/preferences'; + +interface Props { + handleClose: () => void; + open: boolean; +} + +export const AvatarColorPickerDialog = (props: Props) => { + const { handleClose, open } = props; + + const [avatarColor, setAvatarColor] = useState(); + + const { mutateAsync: updatePreferences } = useMutatePreferences(); + + const debouncedSetAvatarColor = React.useCallback( + debounce(250, false, (color) => setAvatarColor(color)), + [setAvatarColor] + ); + + return ( + + Select a custom color for your avatar. + debouncedSetAvatarColor(color)} + // sxProps={{ marginTop: '12px' }} //TODO: get working + label="" // TODO: visually hidden + /> + + { + if (avatarColor) { + updatePreferences({ + avatarColor, + }).catch( + () => + /** swallow the error */ + null + ); + } + handleClose(); + }, + }} + secondaryButtonProps={{ + compactX: true, + 'data-testid': 'close-button', + label: 'Close', + onClick: handleClose, + }} + sx={{ + display: 'flex', + marginTop: '18px !important', + paddingBottom: 0, + paddingTop: 0, + }} + /> + + ); +}; diff --git a/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.tsx b/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.tsx index 1a1ba9ae96b..d4e05288b95 100644 --- a/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.tsx +++ b/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.tsx @@ -5,7 +5,9 @@ import { useSelector } from 'react-redux'; import { useLocation } from 'react-router-dom'; import { v4 } from 'uuid'; +import { Avatar } from 'src/components/Avatar'; import { Box } from 'src/components/Box'; +import { Button } from 'src/components/Button/Button'; import { Divider } from 'src/components/Divider'; import { GravatarByEmail } from 'src/components/GravatarByEmail'; import { Link } from 'src/components/Link'; @@ -16,7 +18,9 @@ import { Typography } from 'src/components/Typography'; import { RESTRICTED_FIELD_TOOLTIP } from 'src/features/Account/constants'; import { useNotificationsQuery } from 'src/queries/account/notifications'; import { useMutateProfile, useProfile } from 'src/queries/profile/profile'; +import { getGravatarUrl } from 'src/utilities/gravatar'; +import { AvatarColorPickerDialog } from './AvatarColorPickerDialog'; import { TimezoneForm } from './TimezoneForm'; import type { ApplicationState } from 'src/store'; @@ -34,6 +38,15 @@ export const DisplaySettings = () => { const isProxyUser = profile?.user_type === 'proxy'; + const hasGravatar = getGravatarUrl(profile?.email ?? '').includes('?d=404') + ? false + : true; + + const [ + isColorPickerDialogOpen, + setAvatarColorPickerDialogOpen, + ] = React.useState(false); + React.useEffect(() => { if (location.state?.focusEmail && emailRef.current) { emailRef.current.focus(); @@ -89,11 +102,15 @@ export const DisplaySettings = () => { }} display="flex" > - + {hasGravatar ? ( + + ) : ( + + )}
Profile photo @@ -108,12 +125,23 @@ export const DisplaySettings = () => { /> - Create, upload, and manage your globally recognized avatar from - a single place with Gravatar. + {hasGravatar + ? 'Create, upload, and manage your globally recognized avatar from a single place with Gravatar.' + : 'Your profile photo is automatically generated using the first character of your username.'} - - Manage photo - + {!hasGravatar && ( + + )} + {hasGravatar && ( + + Manage photo + + )}
@@ -155,6 +183,10 @@ export const DisplaySettings = () => { /> + setAvatarColorPickerDialogOpen(false)} + open={isColorPickerDialogOpen} + /> ); }; diff --git a/packages/manager/src/types/ManagerPreferences.ts b/packages/manager/src/types/ManagerPreferences.ts index 1db40f73035..3b97d993996 100644 --- a/packages/manager/src/types/ManagerPreferences.ts +++ b/packages/manager/src/types/ManagerPreferences.ts @@ -15,6 +15,7 @@ export interface DismissedNotification { } export interface ManagerPreferences extends UserPreferences { + avatarColor?: string; backups_cta_dismissed?: boolean; desktop_sidebar_open?: boolean; dismissed_notifications?: Record; From 36cc621613ca7f49f71d62090cd7170306b0529b Mon Sep 17 00:00:00 2001 From: mjac0bs Date: Mon, 19 Aug 2024 16:15:18 -0700 Subject: [PATCH 02/46] Add utils to determine letter color --- .../src/components/{ => Avatar}/Avatar.tsx | 16 +++-- .../manager/src/components/Avatar/utils.ts | 64 +++++++++++++++++++ .../DisplaySettings/DisplaySettings.tsx | 2 +- 3 files changed, 77 insertions(+), 5 deletions(-) rename packages/manager/src/components/{ => Avatar}/Avatar.tsx (60%) create mode 100644 packages/manager/src/components/Avatar/utils.ts diff --git a/packages/manager/src/components/Avatar.tsx b/packages/manager/src/components/Avatar/Avatar.tsx similarity index 60% rename from packages/manager/src/components/Avatar.tsx rename to packages/manager/src/components/Avatar/Avatar.tsx index 9d6635555e3..d8baea9180f 100644 --- a/packages/manager/src/components/Avatar.tsx +++ b/packages/manager/src/components/Avatar/Avatar.tsx @@ -5,6 +5,8 @@ import * as React from 'react'; import { usePreferences } from 'src/queries/profile/preferences'; import { useProfile } from 'src/queries/profile/profile'; +import { getFontColor, hexToHSL } from './utils'; + export const DEFAULT_AVATAR_SIZE = 28; interface Props { @@ -24,16 +26,22 @@ export const Avatar = (props: Props) => { const { data: preferences } = usePreferences(); const { data: profile } = useProfile(); - const avatarLetter = profile?.username[0].toUpperCase(); - const avatarColor = preferences?.avatarColor ?? theme.color.blue; + const avatarLetter = profile?.username[0].toUpperCase() ?? ''; + const avatarHexColor = preferences?.avatarColor ?? theme.color.blue; + const avatarHslColor = hexToHSL(avatarHexColor); + const avatarLetterColor = avatarHslColor + ? `theme.color.${getFontColor(avatarHslColor)}` + : theme.color.white; return ( <_Avatar alt={`Avatar for user ${profile?.email ?? profile?.username ?? ''}`} className={className} - sx={{ bgcolor: avatarColor, height, width }} + sx={{ bgcolor: avatarHexColor, height, width }} > - {avatarLetter} + + {avatarLetter} + ); }; diff --git a/packages/manager/src/components/Avatar/utils.ts b/packages/manager/src/components/Avatar/utils.ts new file mode 100644 index 00000000000..ce70e04fb5c --- /dev/null +++ b/packages/manager/src/components/Avatar/utils.ts @@ -0,0 +1,64 @@ +export type HSL = { h: number; l: number; s: number }; +export type AvatarFontColor = 'black' | 'white'; + +export function getFontColor(hslColor: HSL): AvatarFontColor { + if (hslColor.l <= 25 || hslColor.s <= 50) { + return 'white'; + } else { + return 'black'; + } +} + +// Credit to https://www.jameslmilner.com/posts/converting-rgb-hex-hsl-colors/. +export function hexToHSL( + hex: string +): { h: number; l: number; s: number } | undefined { + const _hex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + + if (!_hex) { + return; + } + + const rHex = parseInt(_hex[1], 16); + const gHex = parseInt(_hex[2], 16); + const bHex = parseInt(_hex[3], 16); + + const r = rHex / 255; + const g = gHex / 255; + const b = bHex / 255; + + const max = Math.max(r, g, b); + const min = Math.min(r, g, b); + + let h = (max + min) / 2; + let s = h; + let l = h; + + if (max === min) { + // Achromatic + return { h: 0, l, s: 0 }; + } + + const d = max - min; + s = l > 0.5 ? d / (2 - max - min) : d / (max + min); + switch (max) { + case r: + h = (g - b) / d + (g < b ? 6 : 0); + break; + case g: + h = (b - r) / d + 2; + break; + case b: + h = (r - g) / d + 4; + break; + } + h /= 6; + + s = s * 100; + s = Math.round(s); + l = l * 100; + l = Math.round(l); + h = Math.round(360 * h); + + return { h, l, s }; +} diff --git a/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.tsx b/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.tsx index d4e05288b95..c4afe0389f3 100644 --- a/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.tsx +++ b/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.tsx @@ -5,7 +5,7 @@ import { useSelector } from 'react-redux'; import { useLocation } from 'react-router-dom'; import { v4 } from 'uuid'; -import { Avatar } from 'src/components/Avatar'; +import { Avatar } from 'src/components/Avatar/Avatar'; import { Box } from 'src/components/Box'; import { Button } from 'src/components/Button/Button'; import { Divider } from 'src/components/Divider'; From 24296cac8fbe569da86b4301aa73570acaef1e92 Mon Sep 17 00:00:00 2001 From: mjac0bs Date: Wed, 28 Aug 2024 13:17:33 -0700 Subject: [PATCH 03/46] Bring back checkForGravatar event and fix hasGravatar boolean --- .../DisplaySettings/DisplaySettings.tsx | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.tsx b/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.tsx index c4afe0389f3..7b9ee57f061 100644 --- a/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.tsx +++ b/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.tsx @@ -1,5 +1,6 @@ import { updateUser } from '@linode/api-v4/lib/account'; import { styled, useTheme } from '@mui/material/styles'; +import { useState } from 'react'; import * as React from 'react'; import { useSelector } from 'react-redux'; import { useLocation } from 'react-router-dom'; @@ -38,9 +39,21 @@ export const DisplaySettings = () => { const isProxyUser = profile?.user_type === 'proxy'; - const hasGravatar = getGravatarUrl(profile?.email ?? '').includes('?d=404') - ? false - : true; + const [hasGravatar, setHasGravatar] = useState(false); + const checkForGravatar = async (url: string) => { + try { + const response = await fetch(url); + + if (response.status === 200) { + setHasGravatar(true); + } + if (response.status === 404) { + setHasGravatar(false); + } + } catch (error) { + // The fetch to Gravatar failed. Event won't be logged. + } + }; const [ isColorPickerDialogOpen, @@ -54,6 +67,11 @@ export const DisplaySettings = () => { } }, [emailRef, location.state]); + React.useEffect(() => { + const url = getGravatarUrl(profile?.email ?? ''); + checkForGravatar(url); + }, [profile]); + // Used as React keys to force-rerender forms. const [emailResetToken, setEmailResetToken] = React.useState(v4()); const [usernameResetToken, setUsernameResetToken] = React.useState(v4()); From c2bd8c74ffdc880bb02fb8161e8cadd25b4d8e11 Mon Sep 17 00:00:00 2001 From: mjac0bs Date: Wed, 28 Aug 2024 13:20:35 -0700 Subject: [PATCH 04/46] Hide tooltip if user unless user hasGravatar --- .../DisplaySettings/DisplaySettings.tsx | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.tsx b/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.tsx index 7b9ee57f061..e0ce92782de 100644 --- a/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.tsx +++ b/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.tsx @@ -132,15 +132,17 @@ export const DisplaySettings = () => {
Profile photo - + {hasGravatar && ( + + )} {hasGravatar From e99f755d7023c76a66659bf8fc30435185f5e5ff Mon Sep 17 00:00:00 2001 From: mjac0bs Date: Wed, 28 Aug 2024 14:37:43 -0700 Subject: [PATCH 05/46] Add GravatarSunsetBanner.tsx --- .../GlobalNotifications/GlobalNotifications.tsx | 2 ++ .../GlobalNotifications/GravatarSunsetBanner.tsx | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 packages/manager/src/features/GlobalNotifications/GravatarSunsetBanner.tsx diff --git a/packages/manager/src/features/GlobalNotifications/GlobalNotifications.tsx b/packages/manager/src/features/GlobalNotifications/GlobalNotifications.tsx index 2d600740b1b..6b89efc6b72 100644 --- a/packages/manager/src/features/GlobalNotifications/GlobalNotifications.tsx +++ b/packages/manager/src/features/GlobalNotifications/GlobalNotifications.tsx @@ -15,6 +15,7 @@ import { APIMaintenanceBanner } from './APIMaintenanceBanner'; import { ComplianceBanner } from './ComplianceBanner'; import { ComplianceUpdateModal } from './ComplianceUpdateModal'; import { EmailBounceNotificationSection } from './EmailBounce'; +import { GravatarSunsetBanner } from './GravatarSunsetBanner'; import { RegionStatusBanner } from './RegionStatusBanner'; import { TaxCollectionBanner } from './TaxCollectionBanner'; import { DesignUpdateBanner } from './TokensUpdateBanner'; @@ -86,6 +87,7 @@ export const GlobalNotifications = () => { Object.keys(flags.taxCollectionBanner).length > 0 ? ( ) : null} + ); }; diff --git a/packages/manager/src/features/GlobalNotifications/GravatarSunsetBanner.tsx b/packages/manager/src/features/GlobalNotifications/GravatarSunsetBanner.tsx new file mode 100644 index 00000000000..69d6d8bd5fe --- /dev/null +++ b/packages/manager/src/features/GlobalNotifications/GravatarSunsetBanner.tsx @@ -0,0 +1,16 @@ +import React from 'react'; + +import { DismissibleBanner } from 'src/components/DismissibleBanner/DismissibleBanner'; +import { Typography } from 'src/components/Typography'; + +export const GravatarSunsetBanner = () => { + const GRAVATAR_DEPRECATION_DATE = 'September 16th, 2024'; + + return ( + + + {`Support for using Gravatar as your profile photo will be deprecated on ${GRAVATAR_DEPRECATION_DATE}. Your profile photo will automatically be changed to your username initial.`} + + + ); +}; From c9044968d68219b1196e40bece9f037a89b79ad2 Mon Sep 17 00:00:00 2001 From: mjac0bs Date: Wed, 28 Aug 2024 15:56:03 -0700 Subject: [PATCH 06/46] Use util for banner and profile page --- .../GlobalNotifications.tsx | 2 +- .../GravatarSunsetBanner.tsx | 15 +++++++++++- .../DisplaySettings/DisplaySettings.tsx | 24 ++++--------------- packages/manager/src/utilities/gravatar.ts | 16 +++++++++++++ 4 files changed, 35 insertions(+), 22 deletions(-) diff --git a/packages/manager/src/features/GlobalNotifications/GlobalNotifications.tsx b/packages/manager/src/features/GlobalNotifications/GlobalNotifications.tsx index 6b89efc6b72..8654d0858ad 100644 --- a/packages/manager/src/features/GlobalNotifications/GlobalNotifications.tsx +++ b/packages/manager/src/features/GlobalNotifications/GlobalNotifications.tsx @@ -87,7 +87,7 @@ export const GlobalNotifications = () => { Object.keys(flags.taxCollectionBanner).length > 0 ? ( ) : null} - + ); }; diff --git a/packages/manager/src/features/GlobalNotifications/GravatarSunsetBanner.tsx b/packages/manager/src/features/GlobalNotifications/GravatarSunsetBanner.tsx index 69d6d8bd5fe..0f1a7b468f7 100644 --- a/packages/manager/src/features/GlobalNotifications/GravatarSunsetBanner.tsx +++ b/packages/manager/src/features/GlobalNotifications/GravatarSunsetBanner.tsx @@ -1,11 +1,24 @@ import React from 'react'; +import { useState } from 'react'; import { DismissibleBanner } from 'src/components/DismissibleBanner/DismissibleBanner'; import { Typography } from 'src/components/Typography'; +import { checkForGravatar, getGravatarUrl } from 'src/utilities/gravatar'; -export const GravatarSunsetBanner = () => { +interface Props { + email: string; +} + +export const GravatarSunsetBanner = (props: Props) => { + const { email } = props; const GRAVATAR_DEPRECATION_DATE = 'September 16th, 2024'; + const [hasGravatar, setHasGravatar] = useState(false); + + checkForGravatar(getGravatarUrl(email)).then((res) => setHasGravatar(res)); + if (!hasGravatar) { + return; + } return ( diff --git a/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.tsx b/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.tsx index e0ce92782de..580ba25ee09 100644 --- a/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.tsx +++ b/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.tsx @@ -19,7 +19,7 @@ import { Typography } from 'src/components/Typography'; import { RESTRICTED_FIELD_TOOLTIP } from 'src/features/Account/constants'; import { useNotificationsQuery } from 'src/queries/account/notifications'; import { useMutateProfile, useProfile } from 'src/queries/profile/profile'; -import { getGravatarUrl } from 'src/utilities/gravatar'; +import { checkForGravatar, getGravatarUrl } from 'src/utilities/gravatar'; import { AvatarColorPickerDialog } from './AvatarColorPickerDialog'; import { TimezoneForm } from './TimezoneForm'; @@ -40,20 +40,9 @@ export const DisplaySettings = () => { const isProxyUser = profile?.user_type === 'proxy'; const [hasGravatar, setHasGravatar] = useState(false); - const checkForGravatar = async (url: string) => { - try { - const response = await fetch(url); - - if (response.status === 200) { - setHasGravatar(true); - } - if (response.status === 404) { - setHasGravatar(false); - } - } catch (error) { - // The fetch to Gravatar failed. Event won't be logged. - } - }; + checkForGravatar(getGravatarUrl(profile?.email ?? '')).then((res: boolean) => + setHasGravatar(res) + ); const [ isColorPickerDialogOpen, @@ -67,11 +56,6 @@ export const DisplaySettings = () => { } }, [emailRef, location.state]); - React.useEffect(() => { - const url = getGravatarUrl(profile?.email ?? ''); - checkForGravatar(url); - }, [profile]); - // Used as React keys to force-rerender forms. const [emailResetToken, setEmailResetToken] = React.useState(v4()); const [usernameResetToken, setUsernameResetToken] = React.useState(v4()); diff --git a/packages/manager/src/utilities/gravatar.ts b/packages/manager/src/utilities/gravatar.ts index 1740e2c4372..989631cc7fd 100644 --- a/packages/manager/src/utilities/gravatar.ts +++ b/packages/manager/src/utilities/gravatar.ts @@ -11,3 +11,19 @@ export const getGravatarUrlFromHash = (hash: string): string => { export const getGravatarUrl = (email: string): string => { return getGravatarUrlFromHash(getEmailHash(email)); }; + +export const checkForGravatar = async (url: string) => { + try { + const response = await fetch(url); + + if (response.status === 200) { + return true; + } + if (response.status === 404) { + return false; + } + } catch (error) { + // The fetch to Gravatar failed. Event won't be logged. + } + return false; +}; From c76b89bca989eed95ea8283a082b770a912dc2af Mon Sep 17 00:00:00 2001 From: mjac0bs Date: Wed, 28 Aug 2024 22:41:41 -0700 Subject: [PATCH 07/46] Add Akamai wave icon for Akamai-generated user events --- packages/manager/src/assets/logo/akamai-wave.svg | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 packages/manager/src/assets/logo/akamai-wave.svg diff --git a/packages/manager/src/assets/logo/akamai-wave.svg b/packages/manager/src/assets/logo/akamai-wave.svg new file mode 100644 index 00000000000..88cd5af9759 --- /dev/null +++ b/packages/manager/src/assets/logo/akamai-wave.svg @@ -0,0 +1,3 @@ + + + From 86bf9fcd7b3bce0ce0c06429d46232a639c74d06 Mon Sep 17 00:00:00 2001 From: mjac0bs Date: Wed, 28 Aug 2024 22:42:38 -0700 Subject: [PATCH 08/46] Clean up of theme colors and conditional rendering --- packages/manager/src/components/Avatar/Avatar.tsx | 7 ++++--- .../Profile/DisplaySettings/DisplaySettings.tsx | 11 +++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/manager/src/components/Avatar/Avatar.tsx b/packages/manager/src/components/Avatar/Avatar.tsx index d8baea9180f..995f4de45be 100644 --- a/packages/manager/src/components/Avatar/Avatar.tsx +++ b/packages/manager/src/components/Avatar/Avatar.tsx @@ -23,15 +23,16 @@ export const Avatar = (props: Props) => { } = props; const theme = useTheme(); + const { data: preferences } = usePreferences(); const { data: profile } = useProfile(); const avatarLetter = profile?.username[0].toUpperCase() ?? ''; - const avatarHexColor = preferences?.avatarColor ?? theme.color.blue; + const avatarHexColor = preferences?.avatarColor ?? theme.palette.primary.dark; const avatarHslColor = hexToHSL(avatarHexColor); const avatarLetterColor = avatarHslColor - ? `theme.color.${getFontColor(avatarHslColor)}` - : theme.color.white; + ? `theme.palette.common.${getFontColor(avatarHslColor)}` + : theme.palette.common.white; return ( <_Avatar diff --git a/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.tsx b/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.tsx index 580ba25ee09..d8b90800a12 100644 --- a/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.tsx +++ b/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.tsx @@ -133,7 +133,11 @@ export const DisplaySettings = () => { ? 'Create, upload, and manage your globally recognized avatar from a single place with Gravatar.' : 'Your profile photo is automatically generated using the first character of your username.'} - {!hasGravatar && ( + {hasGravatar ? ( + + Manage photo + + ) : ( )} - {hasGravatar && ( - - Manage photo - - )}
From 242f570ca6593205f844c41020d6acd8f789435f Mon Sep 17 00:00:00 2001 From: mjac0bs Date: Wed, 28 Aug 2024 23:53:02 -0700 Subject: [PATCH 09/46] Style ColorPicker --- .../manager/src/components/ColorPicker.tsx | 31 ++++++++++++------- .../AvatarColorPickerDialog.tsx | 24 +++++++------- 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/packages/manager/src/components/ColorPicker.tsx b/packages/manager/src/components/ColorPicker.tsx index 71b16aeda2c..b65bd5d0c76 100644 --- a/packages/manager/src/components/ColorPicker.tsx +++ b/packages/manager/src/components/ColorPicker.tsx @@ -1,31 +1,38 @@ import { useTheme } from '@mui/material'; import React, { useState } from 'react'; -import { usePreferences } from 'src/queries/profile/preferences'; - -import type { SxProps } from '@mui/material'; +import type { CSSProperties } from 'react'; interface Props { + defaultColor?: string; handleColorChange: (color: string) => void; hideLabel?: boolean; // TODO: visually hidden + inputStyles?: CSSProperties; label: string; - sx?: SxProps; + labelStyles?: CSSProperties; } export const ColorPicker = (props: Props) => { - const { handleColorChange, label } = props; + const { + defaultColor, + handleColorChange, + inputStyles, + label, + labelStyles, + } = props; - const { data: preferences } = usePreferences(); const theme = useTheme(); - - // TODO: figure out why default isn't working - const [color, setColor] = useState( - preferences?.avatarColor ?? theme.color.blue + const [color, setColor] = useState( + defaultColor ?? theme.palette.primary.dark ); return ( <> -