Commit 8fa2e6ea authored by tom's avatar tom

fix rewards modal

parent 729a74c1
......@@ -43,7 +43,6 @@ export const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
{ endElement }
{ closable && (
<CloseButton
size="sm"
pos="relative"
top="-2"
insetEnd="-2"
......
......@@ -3,14 +3,16 @@ import * as React from 'react';
import { space } from 'lib/html-entities';
import getComponentDisplayName from '../utils/getComponentDisplayName';
import type { InputProps } from './input';
import type { InputGroupProps } from './input-group';
export interface FieldProps extends Omit<ChakraField.RootProps, 'label' | 'children'> {
label?: React.ReactNode;
helperText?: React.ReactNode;
errorText?: React.ReactNode;
optionalText?: React.ReactNode;
children: React.ReactElement<InputProps>;
children: React.ReactElement<InputProps> | React.ReactElement<InputGroupProps>;
}
export const Field = React.forwardRef<HTMLDivElement, FieldProps>(
......@@ -19,28 +21,58 @@ export const Field = React.forwardRef<HTMLDivElement, FieldProps>(
// A floating field cannot be without a label.
if (props.floating && label) {
const child = React.Children.only<React.ReactElement<InputProps>>(children);
const clonedChild = React.cloneElement(child, {
const injectedProps = {
className: 'peer',
placeholder: ' ',
size: props.size,
floating: props.floating,
bgColor: rest.bgColor,
});
};
const labelElement = (
<ChakraField.Label bgColor={ rest.bgColor }>
{ label }
<ChakraField.RequiredIndicator fallback={ optionalText }/>
{ errorText && (
<ChakraField.ErrorText ml="2px">-{ space }{ errorText }</ChakraField.ErrorText>
) }
</ChakraField.Label>
);
const helperTextElement = helperText && (
<ChakraField.HelperText>{ helperText }</ChakraField.HelperText>
);
const child = React.Children.only<React.ReactElement<InputProps | InputGroupProps>>(children);
const isInputGroup = getComponentDisplayName(child.type) === 'InputGroup';
if (isInputGroup) {
const inputElement = React.cloneElement(
React.Children.only<React.ReactElement<InputProps>>(child.props.children as React.ReactElement<InputProps>),
injectedProps,
);
const groupInputElement = React.cloneElement(child,
{},
inputElement,
labelElement,
);
return (
<ChakraField.Root pos="relative" w="full" ref={ ref } { ...rest }>
{ groupInputElement }
{ helperTextElement }
</ChakraField.Root>
);
}
const inputElement = React.cloneElement(child, injectedProps);
return (
<ChakraField.Root pos="relative" w="full" ref={ ref } { ...rest }>
{ clonedChild }
<ChakraField.Label bgColor={ rest.bgColor }>
{ label }
<ChakraField.RequiredIndicator fallback={ optionalText }/>
{ errorText && (
<ChakraField.ErrorText ml="2px">-{ space }{ errorText }</ChakraField.ErrorText>
) }
</ChakraField.Label>
{ helperText && (
<ChakraField.HelperText>{ helperText }</ChakraField.HelperText>
) }
{ inputElement }
{ labelElement }
{ helperTextElement }
</ChakraField.Root>
);
}
......
......@@ -2,6 +2,9 @@ import type { BoxProps, InputElementProps } from '@chakra-ui/react';
import { Group, InputElement } from '@chakra-ui/react';
import * as React from 'react';
import getComponentDisplayName from '../utils/getComponentDisplayName';
import type { InputProps } from './input';
export interface InputGroupProps extends BoxProps {
startElementProps?: InputElementProps;
endElementProps?: InputElementProps;
......@@ -20,27 +23,25 @@ export const InputGroup = React.forwardRef<HTMLDivElement, InputGroupProps>(
endElement,
endElementProps,
children,
startOffset = '6px',
endOffset = '6px',
startOffset,
endOffset,
...rest
} = props;
const child =
React.Children.only<React.ReactElement<InputElementProps>>(children);
return (
<Group ref={ ref } { ...rest }>
<Group ref={ ref } w="100%" { ...rest }>
{ startElement && (
<InputElement pointerEvents="none" { ...startElementProps }>
{ startElement }
</InputElement>
) }
{ React.cloneElement(child, {
...(startElement && {
ps: `calc(var(--input-height) - ${ startOffset })`,
}),
...(endElement && { pe: `calc(var(--input-height) - ${ endOffset })` }),
...children.props,
{ React.Children.map(children, (child: React.ReactElement<InputProps>) => {
if (getComponentDisplayName(child.type) !== 'FieldInput') {
return child;
}
return React.cloneElement(child, {
...(startElement && { ps: startOffset ?? `calc(var(--input-height) - 6px)` }),
...(endElement && { pe: endOffset ?? `calc(var(--input-height) - 6px)` }),
});
}) }
{ endElement && (
<InputElement placement="end" { ...endElementProps }>
......@@ -51,3 +52,5 @@ export const InputGroup = React.forwardRef<HTMLDivElement, InputGroupProps>(
);
},
);
InputGroup.displayName = 'InputGroup';
......@@ -56,7 +56,7 @@ export const recipe = defineSlotRecipe({
inline: {
'true': {
root: {
alignItems: 'center',
alignItems: 'flex-start',
},
content: {
display: 'inline-flex',
......@@ -82,6 +82,7 @@ export const recipe = defineSlotRecipe({
},
indicator: {
boxSize: '5',
mt: '2px',
},
},
},
......
......@@ -22,6 +22,7 @@ export const recipe = defineSlotRecipe({
fontWeight: '500',
gap: '0',
userSelect: 'none',
zIndex: '1',
_disabled: {
opacity: '0.5',
},
......
......@@ -6,7 +6,7 @@ export const recipe = defineRecipe({
variants: {
loading: {
'true': {
borderRadius: 'sm',
borderRadius: 'base',
boxShadow: 'none',
backgroundClip: 'padding-box',
cursor: 'default',
......
import type React from 'react';
export default function getComponentDisplayName(type: string | React.JSXElementConstructor<unknown>) {
if (typeof type === 'string') {
return;
}
if ('displayName' in type) {
return type.displayName as string;
}
}
......@@ -8,6 +8,7 @@ import { Button } from 'toolkit/chakra/button';
import { useColorMode } from 'toolkit/chakra/color-mode';
import { Field } from 'toolkit/chakra/field';
import { Input } from 'toolkit/chakra/input';
import { InputGroup } from 'toolkit/chakra/input-group';
import { PinInput } from 'toolkit/chakra/pin-input';
import { ProgressCircleRing, ProgressCircleRoot } from 'toolkit/chakra/progress-circle';
import { Skeleton } from 'toolkit/chakra/skeleton';
......@@ -16,6 +17,7 @@ import { Textarea } from 'toolkit/chakra/textarea';
import { toaster } from 'toolkit/chakra/toaster';
import { Tooltip } from 'toolkit/chakra/tooltip';
import ContentLoader from 'ui/shared/ContentLoader';
import IconSvg from 'ui/shared/IconSvg';
import PageTitle from 'ui/shared/Page/PageTitle';
const TEXT = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.';
......@@ -44,6 +46,7 @@ const ChakraShowcases = () => {
<Button visual="link">Link</Button>
<Button loading loadingText="Solid">Solid</Button>
<Button loading loadingText="Outline" visual="outline">Outline</Button>
<Button loading>Loading</Button>
</HStack>
</section>
......@@ -83,7 +86,7 @@ const ChakraShowcases = () => {
</HStack>
<Heading textStyle="heading.sm" mb={ 2 } mt={ 6 }>Floating (only XL size)</Heading>
<HStack gap={ 4 } mt={ 4 } alignItems="flex-start">
<Field label="Email" required floating size="xl" w="300px">
<Field label="Email" required floating size="xl" helperText="Helper text" w="300px">
<Input type="email"/>
</Field>
<Field label="Email" required floating invalid errorText="Something went wrong" size="xl" w="300px">
......@@ -101,6 +104,18 @@ const ChakraShowcases = () => {
<Input type="email" value="me@example.com"/>
</Field>
</HStack>
<Heading textStyle="heading.sm" mb={ 2 } mt={ 6 }>Input group</Heading>
<HStack gap={ 4 } mt={ 4 } alignItems="flex-start" w="fit-content">
<Field label="Referral code" required floating size="xl" w="300px" flexShrink={ 0 } helperText="Helper text">
<InputGroup endElement={ <IconSvg name="copy" boxSize={ 5 }/> }>
<Input/>
</InputGroup>
</Field>
<InputGroup startElement={ <IconSvg name="search" boxSize={ 5 }/> }>
<Input placeholder="Search"/>
</InputGroup>
</HStack>
<Heading textStyle="heading.sm" mb={ 2 } mt={ 6 }>Pin input</Heading>
<HStack mt={ 4 }>
<PinInput otp count={ 3 }/>
<PinInput otp count={ 3 } value={ [ '1', '2', '3' ] } disabled bgColor="dialog.bg"/>
......
import { Icon, useColorModeValue, chakra } from '@chakra-ui/react';
import { Icon, chakra } from '@chakra-ui/react';
import React from 'react';
// This icon doesn't work properly when it is in the sprite
// Probably because of the gradient
// eslint-disable-next-line no-restricted-imports
import meritsIcon from 'icons/merits_colored.svg';
import MeritsIconColored from 'icons/merits_colored.svg';
type Props = {
className?: string;
};
const MeritsIcon = ({ className }: Props) => {
const shadow = useColorModeValue('drop-shadow(0px 4px 2px rgba(141, 179, 204, 0.25))', 'none');
return (
<Icon as={ meritsIcon } className={ className } filter={ shadow }/>
<Icon className={ className } filter={{ _light: 'drop-shadow(0px 4px 2px rgba(141, 179, 204, 0.25))', _dark: 'none' }}>
<MeritsIconColored/>
</Icon>
);
};
......
......@@ -16,8 +16,6 @@ type Props = {
visual?: ButtonProps['visual'];
};
// TODO @tom2drum check this component
const RewardsButton = ({ visual = 'header', size }: Props) => {
const { isInitialized, apiToken, openLoginModal, dailyRewardQuery, balancesQuery } = useRewardsContext();
const isMobile = useIsMobile();
......@@ -32,7 +30,6 @@ const RewardsButton = ({ visual = 'header', size }: Props) => {
content="Earn Merits for using Blockscout"
openDelay={ 500 }
disabled={ isMobile || isLoading || Boolean(apiToken) }
// width="150px"
>
<Button
visual={ visual }
......
import { chakra } from '@chakra-ui/react';
import React from 'react';
import { Field } from 'toolkit/chakra/field';
import { Input } from 'toolkit/chakra/input';
import { InputGroup } from 'toolkit/chakra/input-group';
import Input from 'theme/components/Input';
import { Skeleton } from 'toolkit/chakra/skeleton';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import FormInputPlaceholder from 'ui/shared/forms/inputs/FormInputPlaceholder';
import { Field } from 'toolkit/chakra/field';
type Props = {
label: string;
......@@ -15,29 +14,16 @@ type Props = {
isLoading?: boolean;
};
const RewardsReadOnlyInputWithCopy = ({ label, value, className, isLoading }: Props) => (
<Field floating id={ label } className={ className }>
<Skeleton loading={ isLoading }>
<InputGroup>
<Input
readOnly
fontWeight="500"
value={ value }
overflow="hidden"
textOverflow="ellipsis"
sx={{
'&:not(:placeholder-shown)': {
pr: '40px',
},
}}
/>
<FormInputPlaceholder text={ label }/>
<InputRightElement w="40px" display="flex" justifyContent="flex-end" pr={ 2 }>
<CopyToClipboard text={ value }/>
</InputRightElement>
</InputGroup>
const RewardsReadOnlyInputWithCopy = ({ label, value, className, isLoading }: Props) => {
return (
<Skeleton loading={ isLoading } className={ className }>
<Field label={ label } floating size="xl" readOnly>
<InputGroup endElement={ <CopyToClipboard text={ value }/> } endOffset="40px">
<Input value={ value } fontWeight="500" overflow="hidden" textOverflow="ellipsis"/>
</InputGroup>
</Field>
</Skeleton>
</FormControl>
);
);
};
export default chakra(RewardsReadOnlyInputWithCopy);
import { DialogHeader } from '@chakra-ui/react';
import React, { useCallback, useEffect } from 'react';
import { useRewardsContext } from 'lib/contexts/rewards';
import useWallet from 'lib/web3/useWallet';
import { DialogBody, DialogContent, DialogRoot } from 'toolkit/chakra/dialog';
import { DialogBody, DialogContent, DialogRoot, DialogHeader } from 'toolkit/chakra/dialog';
import { useDisclosure } from 'toolkit/hooks/useDisclosure';
import AuthModal from 'ui/snippets/auth/AuthModal';
......@@ -65,7 +64,7 @@ const RewardsLoginModal = () => {
<DialogRoot
open={ isLoginModalOpen && !isWalletModalOpen && !authModal.open }
onOpenChange={ handleOpenChange }
size={{ base: 'full', md: isLoginStep ? 'sm' : 'md' }}
size={{ base: 'full', lg: isLoginStep ? 'sm' : 'md' }}
>
<DialogContent>
<DialogHeader>
......
import { Text, Box, Flex } from '@chakra-ui/react';
import { Text, Box, Flex, Center } from '@chakra-ui/react';
import React from 'react';
import { route } from 'nextjs-routes';
......@@ -6,7 +6,6 @@ import { route } from 'nextjs-routes';
import { useRewardsContext } from 'lib/contexts/rewards';
import { Button } from 'toolkit/chakra/button';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { Tag } from 'toolkit/chakra/tag';
import IconSvg from 'ui/shared/IconSvg';
import MeritsIcon from '../../MeritsIcon';
......@@ -16,7 +15,6 @@ type Props = {
isReferral: boolean;
};
// TODO @tom2drum fix this component
const CongratsStepContent = ({ isReferral }: Props) => {
const { referralsQuery, rewardsConfigQuery } = useRewardsContext();
......@@ -78,9 +76,14 @@ const CongratsStepContent = ({ isReferral }: Props) => {
</Flex>
<Flex flexDirection="column" alignItems="flex-start" px={ 3 } mb={ 8 }>
<Flex alignItems="center" gap={ 2 }>
<Tag colorScheme="blue" w={ 8 } h={ 8 } display="flex" alignItems="center" justifyContent="center" borderRadius="8px">
<Center
boxSize={ 8 }
borderRadius="8px"
color={{ _light: 'blue.500', _dark: 'blue.100' }}
bgColor={{ _light: 'blue.50', _dark: 'blue.800' }}
>
<IconSvg name="profile" boxSize={ 5 }/>
</Tag>
</Center>
<Text fontSize="lg" fontWeight="500">
Referral program
</Text>
......@@ -95,27 +98,31 @@ const CongratsStepContent = ({ isReferral }: Props) => {
</Skeleton>
{ ' ' }bonus on all Merits earned by your referrals
</Text>
{ /* <RewardsReadOnlyInputWithCopy
<RewardsReadOnlyInputWithCopy
label="Referral link"
value={ refLink }
isLoading={ referralsQuery.isLoading }
mt={ 3 }
/> */ }
<Button
as="a"
_target="_blank"
mt={ 6 }
loading={ referralsQuery.isLoading }
href={ `https://x.com/intent/tweet?text=${ encodeURIComponent(shareText) }` }
>
Share on <IconSvg name="social/twitter" boxSize={ 6 } ml={ 1 }/>
</Button>
w="100%"
/>
<Skeleton loading={ referralsQuery.isLoading } mt={ 6 }>
<Button asChild>
<a href={ `https://x.com/intent/tweet?text=${ encodeURIComponent(shareText) }` } target="_blank">
Share on <IconSvg name="social/twitter" boxSize={ 6 } ml={ 1 }/>
</a>
</Button>
</Skeleton>
</Flex>
<Flex flexDirection="column" alignItems="flex-start" px={ 3 }>
<Flex alignItems="center" gap={ 2 }>
<Tag colorScheme="blue" w={ 8 } h={ 8 } display="flex" alignItems="center" justifyContent="center" borderRadius="8px">
<Center
boxSize={ 8 }
borderRadius="8px"
color={{ _light: 'blue.500', _dark: 'blue.100' }}
bgColor={{ _light: 'blue.50', _dark: 'blue.800' }}
>
<IconSvg name="stats" boxSize={ 6 }/>
</Tag>
</Center>
<Text fontSize="lg" fontWeight="500">
Dashboard
</Text>
......@@ -124,8 +131,8 @@ const CongratsStepContent = ({ isReferral }: Props) => {
Explore your current Merits balance, find activities to boost your Merits,
and view your capybara NFT badge collection on the dashboard
</Text>
<Button mt={ 3 } as="a" href={ route({ pathname: '/account/rewards' }) }>
Open
<Button asChild mt={ 3 }>
<a href={ route({ pathname: '/account/rewards' }) }>Open</a>
</Button>
</Flex>
</>
......
......@@ -136,7 +136,7 @@ const LoginStepContent = ({ goNext, closeModal, openAuthModal }: Props) => {
size="xl"
mt={ 3 }
invalid={ refCodeError }
helperText={ !refCodeError ? 'The code should be in format XXXXXX' : undefined }
helperText="The code should be in format XXXXXX"
errorText={ refCodeError ? 'Incorrect code or format' : undefined }
>
<Input
......@@ -164,7 +164,7 @@ const LoginStepContent = ({ goNext, closeModal, openAuthModal }: Props) => {
>
{ buttonText }
</Button>
<Text fontSize="sm" color={{ _light: 'blackAlpha.500', _dark: 'whiteAlpha.500' }} textAlign="center">
<Text textStyle="sm" color="text_secondary" textAlign="center">
Already registered for Blockscout Merits on another network or chain? Connect the same wallet here.
</Text>
</>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment