diff --git a/web/avatars/emoji-avatar-selection-modal.css b/web/avatars/emoji-avatar-selection-modal.css index d24865221..645728b2d 100644 --- a/web/avatars/emoji-avatar-selection-modal.css +++ b/web/avatars/emoji-avatar-selection-modal.css @@ -1,51 +1,48 @@ div.modalContainer { display: flex; flex-direction: column; overflow: hidden; min-height: 594px; } /* https://github.com/missive/emoji-mart/discussions/663 */ em-emoji-picker { max-height: 300px; } div.modalBody { padding: 20px; } div.tabBody { + height: 300px; padding-top: 20px; display: flex; justify-content: center; align-items: center; } div.avatarContainer { display: flex; justify-content: center; padding-top: 20px; } div.emojiPickerContainer { display: flex; justify-content: center; align-items: center; } -div.colorSelectorContainer { - margin: 20px 60px; -} - div.saveButtonContainer { display: flex; flex-direction: column; } div.saveAvatarButtonContent { display: flex; justify-content: center; align-items: center; min-height: 24px; gap: 4px; } diff --git a/web/avatars/emoji-avatar-selection-modal.react.js b/web/avatars/emoji-avatar-selection-modal.react.js index c92d39817..b9f889a32 100644 --- a/web/avatars/emoji-avatar-selection-modal.react.js +++ b/web/avatars/emoji-avatar-selection-modal.react.js @@ -1,169 +1,170 @@ // @flow import data from '@emoji-mart/data'; import Picker from '@emoji-mart/react'; import invariant from 'invariant'; import * as React from 'react'; import { EditUserAvatarContext } from 'lib/components/base-edit-user-avatar-provider.react.js'; import { useModalContext } from 'lib/components/modal-provider.react.js'; import SWMansionIcon from 'lib/components/SWMansionIcon.react.js'; import { defaultAnonymousUserEmojiAvatar, getAvatarForUser, getDefaultAvatar, } from 'lib/shared/avatar-utils.js'; import type { ClientAvatar, ClientEmojiAvatar, } from 'lib/types/avatar-types.js'; import Avatar from './avatar.react.js'; import css from './emoji-avatar-selection-modal.css'; import Button, { buttonThemes } from '../components/button.react.js'; import Tabs from '../components/tabs.react.js'; import LoadingIndicator from '../loading-indicator.react.js'; import Modal from '../modals/modal.react.js'; import ColorSelector from '../modals/threads/color-selector.react.js'; import { useSelector } from '../redux/redux-utils.js'; type TabType = 'emoji' | 'color'; function EmojiAvatarSelectionModal(): React.Node { const { popModal } = useModalContext(); const editUserAvatarContext = React.useContext(EditUserAvatarContext); invariant(editUserAvatarContext, 'editUserAvatarContext should be set'); const { setUserAvatar, userAvatarSaveInProgress } = editUserAvatarContext; const [updateAvatarStatus, setUpdateAvatarStatus] = React.useState(); const currentUserInfo = useSelector(state => state.currentUserInfo); const currentUserAvatar: ClientAvatar = getAvatarForUser(currentUserInfo); const defaultUserAvatar: ClientEmojiAvatar = currentUserInfo?.username ? getDefaultAvatar(currentUserInfo.username) : defaultAnonymousUserEmojiAvatar; // eslint-disable-next-line no-unused-vars const [pendingAvatarEmoji, setPendingAvatarEmoji] = React.useState( currentUserAvatar.type === 'emoji' ? currentUserAvatar.emoji : defaultUserAvatar.emoji, ); const [pendingAvatarColor, setPendingAvatarColor] = React.useState( currentUserAvatar.type === 'emoji' ? currentUserAvatar.color : defaultUserAvatar.color, ); const pendingEmojiAvatar: ClientEmojiAvatar = React.useMemo( () => ({ type: 'emoji', emoji: pendingAvatarEmoji, color: pendingAvatarColor, }), [pendingAvatarColor, pendingAvatarEmoji], ); const onEmojiSelect = React.useCallback(selection => { setUpdateAvatarStatus(); setPendingAvatarEmoji(selection.native); }, []); const onColorSelection = React.useCallback((hex: string) => { setUpdateAvatarStatus(); setPendingAvatarColor(hex); }, []); const onSaveAvatar = React.useCallback(async () => { try { await setUserAvatar(pendingEmojiAvatar); setUpdateAvatarStatus('success'); } catch { setUpdateAvatarStatus('failure'); } }, [pendingEmojiAvatar, setUserAvatar]); let saveButtonContent; let buttonColor; if (userAvatarSaveInProgress) { buttonColor = buttonThemes.standard; saveButtonContent = ; } else if (updateAvatarStatus === 'success') { buttonColor = buttonThemes.success; saveButtonContent = ( <> {'Avatar update succeeded.'} ); } else if (updateAvatarStatus === 'failure') { buttonColor = buttonThemes.danger; saveButtonContent = ( <> {'Avatar update failed. Please try again.'} ); } else { buttonColor = buttonThemes.standard; saveButtonContent = 'Save Avatar'; } const [currentTabType, setCurrentTabType] = React.useState('emoji'); return (
-
+
); } export default EmojiAvatarSelectionModal; diff --git a/web/modals/threads/color-selector-button.css b/web/modals/threads/color-selector-button.css index dbdeff1fe..be04a8c84 100644 --- a/web/modals/threads/color-selector-button.css +++ b/web/modals/threads/color-selector-button.css @@ -1,17 +1,29 @@ .container { height: 48px; width: 48px; border-radius: 24px; } +.containerLarge { + height: 80px; + width: 80px; + border-radius: 40px; +} + .active, .container:hover { background-color: var(--color-selector-active-bg); } div.colorSplotch { height: 32px; width: 32px; border-radius: 16px; cursor: pointer; } + +div.colorSplotchLarge { + height: 60px; + width: 60px; + border-radius: 30px; +} diff --git a/web/modals/threads/color-selector-button.react.js b/web/modals/threads/color-selector-button.react.js index 6d64e2849..010756aa1 100644 --- a/web/modals/threads/color-selector-button.react.js +++ b/web/modals/threads/color-selector-button.react.js @@ -1,41 +1,49 @@ // @flow import classNames from 'classnames'; import * as React from 'react'; import tinycolor from 'tinycolor2'; import css from './color-selector-button.css'; import Button from '../../components/button.react.js'; type ColorSelectorButtonProps = { +color: string, +currentColor: string, +onColorSelection: (hex: string) => void, + +size?: 'small' | 'large', }; function ColorSelectorButton(props: ColorSelectorButtonProps): React.Node { - const { color, currentColor, onColorSelection } = props; + const { color, currentColor, onColorSelection, size } = props; const active = tinycolor.equals(color, currentColor); - const containerClassName = classNames(css.container, { + const containerClassName = classNames({ + [css.container]: true, + [css.containerLarge]: size === 'large', [css.active]: active, }); + const colorSplotchClassName = classNames({ + [css.colorSplotch]: true, + [css.colorSplotchLarge]: size === 'large', + }); + const colorSplotchStyle = React.useMemo( () => ({ backgroundColor: `#${color}`, }), [color], ); const onColorSplotchClicked = React.useCallback(() => { onColorSelection(color); }, [onColorSelection, color]); return ( ); } export default ColorSelectorButton; diff --git a/web/modals/threads/color-selector.css b/web/modals/threads/color-selector.css index fb4231e8a..8ebec7629 100644 --- a/web/modals/threads/color-selector.css +++ b/web/modals/threads/color-selector.css @@ -1,11 +1,15 @@ div.container { display: flex; flex-direction: row; flex-wrap: wrap; align-items: center; justify-content: center; column-gap: 12px; row-gap: 6px; width: 100%; padding: 6px 0; } + +div.containerLarge { + row-gap: 36px; +} diff --git a/web/modals/threads/color-selector.react.js b/web/modals/threads/color-selector.react.js index a6ff8788a..29237a5ca 100644 --- a/web/modals/threads/color-selector.react.js +++ b/web/modals/threads/color-selector.react.js @@ -1,33 +1,41 @@ // @flow +import classNames from 'classnames'; import * as React from 'react'; import { selectedThreadColors } from 'lib/shared/color-utils.js'; import ColorSelectorButton from './color-selector-button.react.js'; import css from './color-selector.css'; type ColorSelectorProps = { +currentColor: string, +onColorSelection: (hex: string) => void, + +size?: 'small' | 'large', }; function ColorSelector(props: ColorSelectorProps): React.Node { - const { currentColor, onColorSelection } = props; + const { currentColor, onColorSelection, size } = props; const colorSelectorButtons = React.useMemo( () => selectedThreadColors.map(color => ( )), - [currentColor, onColorSelection], + [currentColor, onColorSelection, size], ); - return
{colorSelectorButtons}
; + const containerClassName = classNames({ + [css.container]: true, + [css.containerLarge]: size === 'large', + }); + + return
{colorSelectorButtons}
; } export default ColorSelector;