Commit 29aa85f3 authored by tom's avatar tom

Initialize the chakra update and ensure the login page does not crash on load.

parent db6708e1
import { Alert as ChakraAlert } from '@chakra-ui/react';
import * as React from 'react';
import { CloseButton } from './close-button';
export interface AlertProps extends Omit<ChakraAlert.RootProps, 'title'> {
startElement?: React.ReactNode;
endElement?: React.ReactNode;
title?: React.ReactNode;
icon?: React.ReactElement;
closable?: boolean;
onClose?: () => void;
}
export const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
function Alert(props, ref) {
const {
title,
children,
icon,
closable,
onClose,
startElement,
endElement,
...rest
} = props;
return (
<ChakraAlert.Root ref={ ref } { ...rest }>
{ startElement || <ChakraAlert.Indicator>{ icon }</ChakraAlert.Indicator> }
{ children ? (
<ChakraAlert.Content>
<ChakraAlert.Title>{ title }</ChakraAlert.Title>
<ChakraAlert.Description>{ children }</ChakraAlert.Description>
</ChakraAlert.Content>
) : (
<ChakraAlert.Title flex="1">{ title }</ChakraAlert.Title>
) }
{ endElement }
{ closable && (
<CloseButton
size="sm"
pos="relative"
top="-2"
insetEnd="-2"
alignSelf="flex-start"
onClick={ onClose }
/>
) }
</ChakraAlert.Root>
);
},
);
'use client';
import type { GroupProps, SlotRecipeProps } from '@chakra-ui/react';
import { Avatar as ChakraAvatar, Group } from '@chakra-ui/react';
import * as React from 'react';
type ImageProps = React.ImgHTMLAttributes<HTMLImageElement>;
export interface AvatarProps extends ChakraAvatar.RootProps {
name?: string;
src?: string;
srcSet?: string;
loading?: ImageProps['loading'];
icon?: React.ReactElement;
fallback?: React.ReactNode;
}
export const Avatar = React.forwardRef<HTMLDivElement, AvatarProps>(
function Avatar(props, ref) {
const { name, src, srcSet, loading, icon, fallback, children, ...rest } =
props;
return (
<ChakraAvatar.Root ref={ ref } { ...rest }>
<AvatarFallback name={ name } icon={ icon }>
{ fallback }
</AvatarFallback>
<ChakraAvatar.Image src={ src } srcSet={ srcSet } loading={ loading }/>
{ children }
</ChakraAvatar.Root>
);
},
);
interface AvatarFallbackProps extends ChakraAvatar.FallbackProps {
name?: string;
icon?: React.ReactElement;
}
const AvatarFallback = React.forwardRef<HTMLDivElement, AvatarFallbackProps>(
function AvatarFallback(props, ref) {
const { name, icon, children, ...rest } = props;
return (
<ChakraAvatar.Fallback ref={ ref } { ...rest }>
{ children }
{ name != null && children == null && <>{ getInitials(name) }</> }
{ name == null && children == null && (
<ChakraAvatar.Icon asChild={ Boolean(icon) }>{ icon }</ChakraAvatar.Icon>
) }
</ChakraAvatar.Fallback>
);
},
);
function getInitials(name: string) {
const names = name.trim().split(' ');
const firstName = names[0] != null ? names[0] : '';
const lastName = names.length > 1 ? names[names.length - 1] : '';
return firstName && lastName ?
`${ firstName.charAt(0) }${ lastName.charAt(0) }` :
firstName.charAt(0);
}
interface AvatarGroupProps extends GroupProps, SlotRecipeProps<'avatar'> {}
export const AvatarGroup = React.forwardRef<HTMLDivElement, AvatarGroupProps>(
function AvatarGroup(props, ref) {
const { size, variant, borderless, ...rest } = props;
return (
<ChakraAvatar.PropsProvider value={{ size, variant, borderless }}>
<Group gap="0" spaceX="-3" ref={ ref } { ...rest }/>
</ChakraAvatar.PropsProvider>
);
},
);
import type { ButtonProps as ChakraButtonProps } from '@chakra-ui/react';
import {
AbsoluteCenter,
Button as ChakraButton,
Span,
Spinner,
} from '@chakra-ui/react';
import * as React from 'react';
interface ButtonLoadingProps {
loading?: boolean;
loadingText?: React.ReactNode;
}
export interface ButtonProps extends ChakraButtonProps, ButtonLoadingProps {}
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
function Button(props, ref) {
const { loading, disabled, loadingText, children, ...rest } = props;
return (
<ChakraButton disabled={ loading || disabled } ref={ ref } { ...rest }>
{ loading && !loadingText ? (
<>
<AbsoluteCenter display="inline-flex">
<Spinner size="inherit" color="inherit"/>
</AbsoluteCenter>
<Span opacity={ 0 }>{ children }</Span>
</>
) : loading && loadingText ? (
<>
<Spinner size="inherit" color="inherit"/>
{ loadingText }
</>
) : (
children
) }
</ChakraButton>
);
},
);
import { Checkbox as ChakraCheckbox } from '@chakra-ui/react';
import * as React from 'react';
export interface CheckboxProps extends ChakraCheckbox.RootProps {
icon?: React.ReactNode;
inputProps?: React.InputHTMLAttributes<HTMLInputElement>;
rootRef?: React.Ref<HTMLLabelElement>;
}
export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
function Checkbox(props, ref) {
const { icon, children, inputProps, rootRef, ...rest } = props;
return (
<ChakraCheckbox.Root ref={ rootRef } { ...rest }>
<ChakraCheckbox.HiddenInput ref={ ref } { ...inputProps }/>
<ChakraCheckbox.Control>
{ icon || <ChakraCheckbox.Indicator/> }
</ChakraCheckbox.Control>
{ children != null && (
<ChakraCheckbox.Label>{ children }</ChakraCheckbox.Label>
) }
</ChakraCheckbox.Root>
);
},
);
import type { ButtonProps } from '@chakra-ui/react';
import { IconButton as ChakraIconButton } from '@chakra-ui/react';
import * as React from 'react';
import { LuX } from 'react-icons/lu';
export type CloseButtonProps = ButtonProps;
export const CloseButton = React.forwardRef<
HTMLButtonElement,
CloseButtonProps
>(function CloseButton(props, ref) {
return (
<ChakraIconButton variant="ghost" aria-label="Close" ref={ ref } { ...props }>
{ props.children ?? <LuX/> }
</ChakraIconButton>
);
});
'use client';
import { ThemeProvider, useTheme } from 'next-themes';
import type { ThemeProviderProps } from 'next-themes';
import * as React from 'react';
export interface ColorModeProviderProps extends ThemeProviderProps {}
export type ColorMode = 'light' | 'dark';
export function ColorModeProvider(props: ColorModeProviderProps) {
return (
<ThemeProvider attribute="class" scriptProps={{ 'data-cfasync': 'false' }} disableTransitionOnChange { ...props }/>
);
}
export function useColorMode() {
const { resolvedTheme, setTheme } = useTheme();
const toggleColorMode = () => {
setTheme(resolvedTheme === 'light' ? 'dark' : 'light');
};
return {
colorMode: resolvedTheme as ColorMode,
setColorMode: setTheme,
toggleColorMode,
};
}
export function useColorModeValue<T>(light: T, dark: T) {
const { colorMode } = useColorMode();
return colorMode === 'light' ? light : dark;
}
import { Dialog as ChakraDialog, Portal } from '@chakra-ui/react';
import * as React from 'react';
import { CloseButton } from './close-button';
interface DialogContentProps extends ChakraDialog.ContentProps {
portalled?: boolean;
portalRef?: React.RefObject<HTMLElement>;
backdrop?: boolean;
}
export const DialogContent = React.forwardRef<
HTMLDivElement,
DialogContentProps
>(function DialogContent(props, ref) {
const {
children,
portalled = true,
portalRef,
backdrop = true,
...rest
} = props;
return (
<Portal disabled={ !portalled } container={ portalRef }>
{ backdrop && <ChakraDialog.Backdrop/> }
<ChakraDialog.Positioner>
<ChakraDialog.Content ref={ ref } { ...rest } asChild={ false }>
{ children }
</ChakraDialog.Content>
</ChakraDialog.Positioner>
</Portal>
);
});
export const DialogCloseTrigger = React.forwardRef<
HTMLButtonElement,
ChakraDialog.CloseTriggerProps
>(function DialogCloseTrigger(props, ref) {
return (
<ChakraDialog.CloseTrigger
position="absolute"
top="2"
insetEnd="2"
{ ...props }
asChild
>
<CloseButton size="sm" ref={ ref }>
{ props.children }
</CloseButton>
</ChakraDialog.CloseTrigger>
);
});
export const DialogRoot = ChakraDialog.Root;
export const DialogFooter = ChakraDialog.Footer;
export const DialogHeader = ChakraDialog.Header;
export const DialogBody = ChakraDialog.Body;
export const DialogBackdrop = ChakraDialog.Backdrop;
export const DialogTitle = ChakraDialog.Title;
export const DialogDescription = ChakraDialog.Description;
export const DialogTrigger = ChakraDialog.Trigger;
export const DialogActionTrigger = ChakraDialog.ActionTrigger;
import { Drawer as ChakraDrawer, Portal } from '@chakra-ui/react';
import * as React from 'react';
import { CloseButton } from './close-button';
interface DrawerContentProps extends ChakraDrawer.ContentProps {
portalled?: boolean;
portalRef?: React.RefObject<HTMLElement>;
offset?: ChakraDrawer.ContentProps['padding'];
}
export const DrawerContent = React.forwardRef<
HTMLDivElement,
DrawerContentProps
>(function DrawerContent(props, ref) {
const { children, portalled = true, portalRef, offset, ...rest } = props;
return (
<Portal disabled={ !portalled } container={ portalRef }>
<ChakraDrawer.Positioner padding={ offset }>
<ChakraDrawer.Content ref={ ref } { ...rest } asChild={ false }>
{ children }
</ChakraDrawer.Content>
</ChakraDrawer.Positioner>
</Portal>
);
});
export const DrawerCloseTrigger = React.forwardRef<
HTMLButtonElement,
ChakraDrawer.CloseTriggerProps
>(function DrawerCloseTrigger(props, ref) {
return (
<ChakraDrawer.CloseTrigger
position="absolute"
top="2"
insetEnd="2"
{ ...props }
asChild
>
<CloseButton size="sm" ref={ ref }/>
</ChakraDrawer.CloseTrigger>
);
});
export const DrawerTrigger = ChakraDrawer.Trigger;
export const DrawerRoot = ChakraDrawer.Root;
export const DrawerFooter = ChakraDrawer.Footer;
export const DrawerHeader = ChakraDrawer.Header;
export const DrawerBody = ChakraDrawer.Body;
export const DrawerBackdrop = ChakraDrawer.Backdrop;
export const DrawerDescription = ChakraDrawer.Description;
export const DrawerTitle = ChakraDrawer.Title;
export const DrawerActionTrigger = ChakraDrawer.ActionTrigger;
import { Field as ChakraField } from '@chakra-ui/react';
import * as React from 'react';
export interface FieldProps extends Omit<ChakraField.RootProps, 'label'> {
label?: React.ReactNode;
helperText?: React.ReactNode;
errorText?: React.ReactNode;
optionalText?: React.ReactNode;
}
export const Field = React.forwardRef<HTMLDivElement, FieldProps>(
function Field(props, ref) {
const { label, children, helperText, errorText, optionalText, ...rest } =
props;
return (
<ChakraField.Root ref={ ref } { ...rest }>
{ label && (
<ChakraField.Label>
{ label }
<ChakraField.RequiredIndicator fallback={ optionalText }/>
</ChakraField.Label>
) }
{ children }
{ helperText && (
<ChakraField.HelperText>{ helperText }</ChakraField.HelperText>
) }
{ errorText && (
<ChakraField.ErrorText>{ errorText }</ChakraField.ErrorText>
) }
</ChakraField.Root>
);
},
);
import type { BoxProps, InputElementProps } from '@chakra-ui/react';
import { Group, InputElement } from '@chakra-ui/react';
import * as React from 'react';
export interface InputGroupProps extends BoxProps {
startElementProps?: InputElementProps;
endElementProps?: InputElementProps;
startElement?: React.ReactNode;
endElement?: React.ReactNode;
children: React.ReactElement<InputElementProps>;
startOffset?: InputElementProps['paddingStart'];
endOffset?: InputElementProps['paddingEnd'];
}
export const InputGroup = React.forwardRef<HTMLDivElement, InputGroupProps>(
function InputGroup(props, ref) {
const {
startElement,
startElementProps,
endElement,
endElementProps,
children,
startOffset = '6px',
endOffset = '6px',
...rest
} = props;
const child =
React.Children.only<React.ReactElement<InputElementProps>>(children);
return (
<Group ref={ ref } { ...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,
}) }
{ endElement && (
<InputElement placement="end" { ...endElementProps }>
{ endElement }
</InputElement>
) }
</Group>
);
},
);
import { Popover as ChakraPopover, Portal } from '@chakra-ui/react';
import * as React from 'react';
import { CloseButton } from './close-button';
interface PopoverContentProps extends ChakraPopover.ContentProps {
portalled?: boolean;
portalRef?: React.RefObject<HTMLElement>;
}
export const PopoverContent = React.forwardRef<
HTMLDivElement,
PopoverContentProps
>(function PopoverContent(props, ref) {
const { portalled = true, portalRef, ...rest } = props;
return (
<Portal disabled={ !portalled } container={ portalRef }>
<ChakraPopover.Positioner>
<ChakraPopover.Content ref={ ref } { ...rest }/>
</ChakraPopover.Positioner>
</Portal>
);
});
export const PopoverArrow = React.forwardRef<
HTMLDivElement,
ChakraPopover.ArrowProps
>(function PopoverArrow(props, ref) {
return (
<ChakraPopover.Arrow { ...props } ref={ ref }>
<ChakraPopover.ArrowTip/>
</ChakraPopover.Arrow>
);
});
export const PopoverCloseTrigger = React.forwardRef<
HTMLButtonElement,
ChakraPopover.CloseTriggerProps
>(function PopoverCloseTrigger(props, ref) {
return (
<ChakraPopover.CloseTrigger
position="absolute"
top="1"
insetEnd="1"
{ ...props }
asChild
ref={ ref }
>
<CloseButton size="sm"/>
</ChakraPopover.CloseTrigger>
);
});
export const PopoverTitle = ChakraPopover.Title;
export const PopoverDescription = ChakraPopover.Description;
export const PopoverFooter = ChakraPopover.Footer;
export const PopoverHeader = ChakraPopover.Header;
export const PopoverRoot = ChakraPopover.Root;
export const PopoverBody = ChakraPopover.Body;
export const PopoverTrigger = ChakraPopover.Trigger;
'use client';
import { ChakraProvider } from '@chakra-ui/react';
import theme from 'chakra/theme/theme';
import {
ColorModeProvider,
type ColorModeProviderProps,
} from './color-mode';
export function Provider(props: ColorModeProviderProps) {
return (
<ChakraProvider value={ theme }>
<ColorModeProvider { ...props }/>
</ChakraProvider>
);
}
import { RadioGroup as ChakraRadioGroup } from '@chakra-ui/react';
import * as React from 'react';
export interface RadioProps extends ChakraRadioGroup.ItemProps {
rootRef?: React.Ref<HTMLDivElement>;
inputProps?: React.InputHTMLAttributes<HTMLInputElement>;
}
export const Radio = React.forwardRef<HTMLInputElement, RadioProps>(
function Radio(props, ref) {
const { children, inputProps, rootRef, ...rest } = props;
return (
<ChakraRadioGroup.Item ref={ rootRef } { ...rest }>
<ChakraRadioGroup.ItemHiddenInput ref={ ref } { ...inputProps }/>
<ChakraRadioGroup.ItemIndicator/>
{ children && (
<ChakraRadioGroup.ItemText>{ children }</ChakraRadioGroup.ItemText>
) }
</ChakraRadioGroup.Item>
);
},
);
export const RadioGroup = ChakraRadioGroup.Root;
import { Slider as ChakraSlider, For, HStack } from '@chakra-ui/react';
import * as React from 'react';
export interface SliderProps extends ChakraSlider.RootProps {
marks?: Array<number | { value: number; label: React.ReactNode }>;
label?: React.ReactNode;
showValue?: boolean;
}
export const Slider = React.forwardRef<HTMLDivElement, SliderProps>(
function Slider(props, ref) {
const { marks: marksProp, label, showValue, ...rest } = props;
const value = props.defaultValue ?? props.value;
const marks = marksProp?.map((mark) => {
if (typeof mark === 'number') return { value: mark, label: undefined };
return mark;
});
const hasMarkLabel = Boolean(marks?.some((mark) => mark.label));
return (
<ChakraSlider.Root ref={ ref } thumbAlignment="center" { ...rest }>
{ label && !showValue && (
<ChakraSlider.Label>{ label }</ChakraSlider.Label>
) }
{ label && showValue && (
<HStack justify="space-between">
<ChakraSlider.Label>{ label }</ChakraSlider.Label>
<ChakraSlider.ValueText/>
</HStack>
) }
<ChakraSlider.Control data-has-mark-label={ hasMarkLabel || undefined }>
<ChakraSlider.Track>
<ChakraSlider.Range/>
</ChakraSlider.Track>
<SliderThumbs value={ value }/>
<SliderMarks marks={ marks }/>
</ChakraSlider.Control>
</ChakraSlider.Root>
);
},
);
function SliderThumbs(props: { value?: Array<number> }) {
const { value } = props;
return (
<For each={ value }>
{ (_, index) => (
<ChakraSlider.Thumb key={ index } index={ index }>
<ChakraSlider.HiddenInput/>
</ChakraSlider.Thumb>
) }
</For>
);
}
interface SliderMarksProps {
marks?: Array<number | { value: number; label: React.ReactNode }>;
}
const SliderMarks = React.forwardRef<HTMLDivElement, SliderMarksProps>(
function SliderMarks(props, ref) {
const { marks } = props;
if (!marks?.length) return null;
return (
<ChakraSlider.MarkerGroup ref={ ref }>
{ marks.map((mark, index) => {
const value = typeof mark === 'number' ? mark : mark.value;
const label = typeof mark === 'number' ? undefined : mark.label;
return (
<ChakraSlider.Marker key={ index } value={ value }>
<ChakraSlider.MarkerIndicator/>
{ label }
</ChakraSlider.Marker>
);
}) }
</ChakraSlider.MarkerGroup>
);
},
);
'use client';
import {
Toaster as ChakraToaster,
Portal,
Spinner,
Stack,
Toast,
createToaster,
} from '@chakra-ui/react';
import { SECOND } from 'lib/consts';
export const toaster = createToaster({
placement: 'top-end',
pauseOnPageIdle: true,
duration: 5 * SECOND,
});
export const Toaster = () => {
return (
<Portal>
<ChakraToaster toaster={ toaster } insetInline={{ mdDown: '4' }}>
{ (toast) => (
<Toast.Root width={{ md: 'sm' }}>
{ toast.type === 'loading' ? (
<Spinner size="sm" color="blue.solid"/>
) : (
<Toast.Indicator/>
) }
<Stack gap="1" flex="1" maxWidth="100%">
{ toast.title && <Toast.Title>{ toast.title }</Toast.Title> }
{ toast.description && (
<Toast.Description>{ toast.description }</Toast.Description>
) }
</Stack>
{ toast.action && (
<Toast.ActionTrigger>{ toast.action.label }</Toast.ActionTrigger>
) }
{ toast.meta?.closable && <Toast.CloseTrigger/> }
</Toast.Root>
) }
</ChakraToaster>
</Portal>
);
};
import { Tooltip as ChakraTooltip, Portal } from '@chakra-ui/react';
import * as React from 'react';
export interface TooltipProps extends ChakraTooltip.RootProps {
showArrow?: boolean;
portalled?: boolean;
portalRef?: React.RefObject<HTMLElement>;
content: React.ReactNode;
contentProps?: ChakraTooltip.ContentProps;
disabled?: boolean;
}
export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
function Tooltip(props, ref) {
const {
showArrow,
children,
disabled,
portalled,
content,
contentProps,
portalRef,
...rest
} = props;
if (disabled) return children;
return (
<ChakraTooltip.Root { ...rest }>
<ChakraTooltip.Trigger asChild>{ children }</ChakraTooltip.Trigger>
<Portal disabled={ !portalled } container={ portalRef }>
<ChakraTooltip.Positioner>
<ChakraTooltip.Content ref={ ref } { ...contentProps }>
{ showArrow && (
<ChakraTooltip.Arrow>
<ChakraTooltip.ArrowTip/>
</ChakraTooltip.Arrow>
) }
{ content }
</ChakraTooltip.Content>
</ChakraTooltip.Positioner>
</Portal>
</ChakraTooltip.Root>
);
},
);
import type { TokenDefinition } from '@chakra-ui/react/dist/types/styled-system/types';
export const radii: TokenDefinition['radii'] = {
none: { value: '0' },
sm: { value: '4px' },
base: { value: '8px' },
md: { value: '12px' },
lg: { value: '16px' },
xl: { value: '24px' },
full: { value: '9999px' },
};
const breakpoints = {
// maybe we need them in future
sm: '415px',
// md: '768px',
lg: '1000px',
xl: '1440px',
'2xl': '1920px',
// these breakpoint are needed just to make others work
'3xl': '3000px',
};
export default breakpoints;
import type { TokenDefinition } from '@chakra-ui/react/dist/types/styled-system/types';
const colors: TokenDefinition['colors'] = {
green: {
'100': { value: '#C6F6D5' },
'400': { value: '#48BB78' },
'500': { value: '#38A169' },
'600': { value: '#25855A' },
},
blue: {
'50': { value: '#EBF8FF' },
'100': { value: '#BEE3F8' },
'200': { value: '#90CDF4' },
'300': { value: '#63B3ED' },
'400': { value: '#4299E1' },
'500': { value: '#3182CE' },
'600': { value: '#2B6CB0' },
'700': { value: '#2C5282' },
'800': { value: '#2A4365' },
'900': { value: '#1A365D' },
},
red: {
'500': { value: '#E53E3E' },
'100': { value: '#FED7D7' },
},
orange: {
'100': { value: '#FEEBCB' },
},
gray: {
'50': { value: '#F7FAFC' },
'100': { value: '#EDF2F7' },
'200': { value: '#E2E8F0' },
'300': { value: '#CBD5E0' },
'400': { value: '#A0AEC0' },
'500': { value: '#718096' },
'600': { value: '#4A5568' },
'700': { value: '#2D3748' },
'800': { value: '#1A202C' },
'900': { value: '#171923' },
},
black: { value: '#101112' },
white: { value: '#ffffff' },
blackAlpha: {
'50': { value: 'RGBA(16, 17, 18, 0.04)' },
'100': { value: 'RGBA(16, 17, 18, 0.06)' },
'200': { value: 'RGBA(16, 17, 18, 0.08)' },
'300': { value: 'RGBA(16, 17, 18, 0.16)' },
'400': { value: 'RGBA(16, 17, 18, 0.24)' },
'500': { value: 'RGBA(16, 17, 18, 0.36)' },
'600': { value: 'RGBA(16, 17, 18, 0.48)' },
'700': { value: 'RGBA(16, 17, 18, 0.64)' },
'800': { value: 'RGBA(16, 17, 18, 0.80)' },
'900': { value: 'RGBA(16, 17, 18, 0.92)' },
},
github: { value: '#171923' },
telegram: { value: '#2775CA' },
linkedin: { value: '#1564BA' },
discord: { value: '#9747FF' },
slack: { value: '#1BA27A' },
twitter: { value: '#000000' },
opensea: { value: '#2081E2' },
facebook: { value: '#4460A0' },
medium: { value: '#231F20' },
reddit: { value: '#FF4500' },
celo: { value: '#FCFF52' },
};
export default colors;
import type { TokenDefinition } from '@chakra-ui/react/dist/types/styled-system/types';
const durations: TokenDefinition['durations'] = {
'ultra-fast': { value: '50ms' },
faster: { value: '100ms' },
fast: { value: '150ms' },
normal: { value: '200ms' },
slow: { value: '300ms' },
slower: { value: '400ms' },
'ultra-slow': { value: '500ms' },
};
export default durations;
import { getCSSVar } from '@chakra-ui/styled-system';
import { mode } from '@chakra-ui/theme-tools';
import type { StyleFunctionProps } from '@chakra-ui/theme-tools';
const scrollbar = (props: StyleFunctionProps) => {
const bgColor = mode('blackAlpha.300', 'whiteAlpha.300')(props);
const resizerUrl = mode('url(/static/resizer_light.png)', 'url(/static/resizer_dark.png)')(props);
return {
'body *::-webkit-scrollbar': {
width: '20px',
},
'body *::-webkit-scrollbar-track': {
backgroundColor: 'transparent',
},
'body *::-webkit-scrollbar-thumb': {
backgroundColor: bgColor,
borderRadius: '20px',
border: `8px solid rgba(0,0,0,0)`,
backgroundClip: 'content-box',
minHeight: '32px',
},
'body *::-webkit-scrollbar-button': {
display: 'none',
},
'body *::-webkit-scrollbar-corner': {
backgroundColor: 'transparent',
},
'body *::-webkit-resizer': {
backgroundImage: resizerUrl,
backgroundSize: '20px',
},
'body *': {
scrollbarWidth: 'thin',
scrollbarColor: `${ getCSSVar(props.theme, 'colors', bgColor) } transparent`,
},
};
};
export default scrollbar;
import type { SemanticTokenDefinition } from '@chakra-ui/react/dist/types/styled-system/types';
const semanticTokens: SemanticTokenDefinition = {
colors: {
divider: {
DEFAULT: { value: '{colors.blackAlpha.200}' },
_dark: { value: '{colors.whiteAlpha.200}' },
},
text: {
DEFAULT: { value: '{colors.blackAlpha.800}' },
_dark: { value: '{colors.whiteAlpha.800}' },
},
text_secondary: {
DEFAULT: { value: '{colors.gray.500}' },
_dark: { value: '{colors.gray.400}' },
},
link: {
DEFAULT: { value: '{colors.blue.600}' },
_dark: { value: '{colors.blue.300}' },
},
link_hovered: {
DEFAULT: { value: '{colors.blue.400}' },
},
icon_link_external: {
DEFAULT: { value: '{colors.gray.300}' },
_dark: { value: '{colors.gray.500}' },
},
icon_info: {
DEFAULT: { value: '{colors.gray.400}' },
_dark: { value: '{colors.gray.500}' },
},
error: {
DEFAULT: { value: '{colors.red.500}' },
_dark: { value: '{colors.red.500}' },
},
dialog_bg: {
DEFAULT: { value: '{colors.white}' },
_dark: { value: '{colors.gray.900}' },
},
},
};
export default semanticTokens;
import type { TokenDefinition } from '@chakra-ui/react/dist/types/styled-system/types';
const shadows: TokenDefinition['shadows'] = {
action_bar: { value: '0 4px 4px -4px rgb(0 0 0 / 10%), 0 2px 4px -4px rgb(0 0 0 / 6%)' },
};
export default shadows;
import type { ThemingConfig, TokenDefinition } from '@chakra-ui/react/dist/types/styled-system/types';
import config from 'configs/app';
export const BODY_TYPEFACE = config.UI.fonts.body?.name ?? 'Inter';
export const HEADING_TYPEFACE = config.UI.fonts.heading?.name ?? 'Poppins';
export const fonts: TokenDefinition['fonts'] = {
heading: { value: `${ HEADING_TYPEFACE }, sans-serif` },
body: { value: `${ BODY_TYPEFACE }, sans-serif` },
};
export const textStyles: ThemingConfig['textStyles'] = {
h2: {
fontSize: '32px',
fontWeight: '500',
lineHeight: '40px',
fontFamily: 'heading',
},
h3: {
fontSize: '24px',
fontWeight: '500',
lineHeight: '32px',
fontFamily: 'heading',
},
h4: {
fontSize: 'md',
fontWeight: '500',
lineHeight: '24px',
fontFamily: 'heading',
},
};
import type { TokenDefinition } from '@chakra-ui/react/dist/types/styled-system/types';
export const zIndex: TokenDefinition['zIndex'] = {
hide: { value: -1 },
auto: { value: 'auto' },
base: { value: 0 },
docked: { value: 10 },
dropdown: { value: 1000 },
sticky: { value: 1100 },
sticky1: { value: 1101 },
sticky2: { value: 1102 },
banner: { value: 1200 },
overlay: { value: 1300 },
modal: { value: 1400 },
popover: { value: 1500 },
tooltip: { value: 1550 }, // otherwise tooltips will not be visible in modals
skipLink: { value: 1600 },
toast: { value: 1700 },
};
export default zIndex;
import type { StyleFunctionProps, SystemStyleObject } from '@chakra-ui/theme-tools';
import { mode } from '@chakra-ui/theme-tools';
import scrollbar from './foundations/scrollbar';
import addressEntity from './globals/address-entity';
import recaptcha from './globals/recaptcha';
// import getDefaultTransitionProps from './utils/getDefaultTransitionProps';
const globalCss: Record<string, SystemStyleObject> = {
body: {
// bg: mode('white', 'black')(props),
// ...getDefaultTransitionProps(),
'-webkit-tap-highlight-color': 'transparent',
'font-variant-ligatures': 'no-contextual',
},
mark: {
// bgColor: mode('green.100', 'green.800')(props),
color: 'inherit',
},
'svg *::selection': {
color: 'none',
background: 'none',
},
form: {
w: '100%',
},
// ...scrollbar(props),
// ...addressEntity(props),
...recaptcha(),
};
export default globalCss;
import { mode } from '@chakra-ui/theme-tools';
import type { StyleFunctionProps } from '@chakra-ui/theme-tools';
const styles = (props: StyleFunctionProps) => {
return {
'.address-entity': {
'&.address-entity_highlighted': {
_before: {
content: `" "`,
position: 'absolute',
py: 1,
pl: 1,
pr: 0,
top: '-5px',
left: '-5px',
width: `100%`,
height: '100%',
borderRadius: 'base',
borderColor: mode('blue.200', 'blue.600')(props),
borderWidth: '1px',
borderStyle: 'dashed',
bgColor: mode('blue.50', 'blue.900')(props),
zIndex: -1,
},
},
},
'.address-entity_no-copy': {
'&.address-entity_highlighted': {
_before: {
pr: 2,
},
},
},
};
};
export default styles;
const styles = () => {
return {
'.grecaptcha-badge': {
visibility: 'hidden',
},
'div:has(div):has(iframe[title="recaptcha challenge expires in two minutes"])': {
'&::after': {
content: `" "`,
display: 'block',
position: 'fixed',
top: 0,
left: 0,
width: '100vw',
height: '100vh',
zIndex: 100000,
bgColor: 'blackAlpha.300',
},
},
};
};
export default styles;
import { createSystem, defaultConfig, defineConfig } from '@chakra-ui/react';
// import components from './components/index';
// import config from './config';
import * as borders from './foundations/borders';
import breakpoints from './foundations/breakpoints';
import colors from './foundations/colors';
import durations from './foundations/durations';
import semanticTokens from './foundations/semanticTokens';
import shadows from './foundations/shadows';
import { fonts, textStyles } from './foundations/typography';
import zIndex from './foundations/zIndex';
import globalCss from './globalCss';
const customConfig = defineConfig({
globalCss,
theme: {
breakpoints,
semanticTokens,
textStyles,
tokens: {
...borders,
colors,
durations,
fonts,
shadows,
zIndex,
},
},
// components,
// config,
});
export default createSystem(defaultConfig, customConfig);
...@@ -283,6 +283,7 @@ export default tseslint.config( ...@@ -283,6 +283,7 @@ export default tseslint.config(
'/types/', '/types/',
[ '/^nextjs/' ], [ '/^nextjs/' ],
[ [
'/^chakra/',
'/^configs/', '/^configs/',
'/^data/', '/^data/',
'/^deploy/', '/^deploy/',
......
import { useBoolean } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query'; import type { UseQueryResult } from '@tanstack/react-query';
import { useQueryClient } from '@tanstack/react-query'; import { useQueryClient } from '@tanstack/react-query';
import { useToggle } from '@uidotdev/usehooks';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React, { createContext, useContext, useEffect, useMemo, useCallback } from 'react'; import React, { createContext, useContext, useEffect, useMemo, useCallback } from 'react';
import { useSignMessage } from 'wagmi'; import { useSignMessage } from 'wagmi';
...@@ -13,6 +13,7 @@ import type { ...@@ -13,6 +13,7 @@ import type {
RewardsConfigResponse, RewardsConfigResponse,
} from 'types/api/rewards'; } from 'types/api/rewards';
import { toaster } from 'chakra/components/toaster';
import config from 'configs/app'; import config from 'configs/app';
import type { ResourceError } from 'lib/api/resources'; import type { ResourceError } from 'lib/api/resources';
import useApiFetch from 'lib/api/useApiFetch'; import useApiFetch from 'lib/api/useApiFetch';
...@@ -22,7 +23,6 @@ import * as cookies from 'lib/cookies'; ...@@ -22,7 +23,6 @@ import * as cookies from 'lib/cookies';
import decodeJWT from 'lib/decodeJWT'; import decodeJWT from 'lib/decodeJWT';
import getErrorMessage from 'lib/errors/getErrorMessage'; import getErrorMessage from 'lib/errors/getErrorMessage';
import getErrorObjPayload from 'lib/errors/getErrorObjPayload'; import getErrorObjPayload from 'lib/errors/getErrorObjPayload';
import useToast from 'lib/hooks/useToast';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import removeQueryParam from 'lib/router/removeQueryParam'; import removeQueryParam from 'lib/router/removeQueryParam';
import useAccount from 'lib/web3/useAccount'; import useAccount from 'lib/web3/useAccount';
...@@ -114,13 +114,12 @@ export function RewardsContextProvider({ children }: Props) { ...@@ -114,13 +114,12 @@ export function RewardsContextProvider({ children }: Props) {
const router = useRouter(); const router = useRouter();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const apiFetch = useApiFetch(); const apiFetch = useApiFetch();
const toast = useToast();
const { address } = useAccount(); const { address } = useAccount();
const { signMessageAsync } = useSignMessage(); const { signMessageAsync } = useSignMessage();
const profileQuery = useProfileQuery(); const profileQuery = useProfileQuery();
const [ isLoginModalOpen, setIsLoginModalOpen ] = useBoolean(false); const [ isLoginModalOpen, setIsLoginModalOpen ] = useToggle(false);
const [ isInitialized, setIsInitialized ] = useBoolean(false); const [ isInitialized, setIsInitialized ] = useToggle(false);
const [ apiToken, setApiToken ] = React.useState<string | undefined>(); const [ apiToken, setApiToken ] = React.useState<string | undefined>();
// Initialize state with the API token from cookies // Initialize state with the API token from cookies
...@@ -131,7 +130,7 @@ export function RewardsContextProvider({ children }: Props) { ...@@ -131,7 +130,7 @@ export function RewardsContextProvider({ children }: Props) {
if (registeredAddress === profileQuery.data?.address_hash) { if (registeredAddress === profileQuery.data?.address_hash) {
setApiToken(token); setApiToken(token);
} }
setIsInitialized.on(); setIsInitialized(true);
} }
}, [ setIsInitialized, profileQuery ]); }, [ setIsInitialized, profileQuery ]);
...@@ -187,22 +186,18 @@ export function RewardsContextProvider({ children }: Props) { ...@@ -187,22 +186,18 @@ export function RewardsContextProvider({ children }: Props) {
cookies.set(cookies.NAMES.REWARDS_REFERRAL_CODE, refCode); cookies.set(cookies.NAMES.REWARDS_REFERRAL_CODE, refCode);
removeQueryParam(router, 'ref'); removeQueryParam(router, 'ref');
if (!apiToken) { if (!apiToken) {
setIsLoginModalOpen.on(); setIsLoginModalOpen(true);
} }
} }
}, [ router, apiToken, isInitialized, setIsLoginModalOpen ]); }, [ router, apiToken, isInitialized, setIsLoginModalOpen ]);
const errorToast = useCallback((error: unknown) => { const errorToast = useCallback((error: unknown) => {
const apiError = getErrorObjPayload<{ message: string }>(error); const apiError = getErrorObjPayload<{ message: string }>(error);
toast({ toaster.error({
position: 'top-right',
title: 'Error', title: 'Error',
description: apiError?.message || getErrorMessage(error) || 'Something went wrong. Try again later.', description: apiError?.message || getErrorMessage(error) || 'Something went wrong. Try again later.',
status: 'error',
variant: 'subtle',
isClosable: true,
}); });
}, [ toast ]); }, [ ]);
// Login to the rewards program // Login to the rewards program
const login = useCallback(async(refCode: string) => { const login = useCallback(async(refCode: string) => {
...@@ -254,6 +249,14 @@ export function RewardsContextProvider({ children }: Props) { ...@@ -254,6 +249,14 @@ export function RewardsContextProvider({ children }: Props) {
} }
}, [ apiFetch, errorToast, fetchParams ]); }, [ apiFetch, errorToast, fetchParams ]);
const openLoginModal = React.useCallback(() => {
setIsLoginModalOpen(true);
}, [ setIsLoginModalOpen ]);
const closeLoginModal = React.useCallback(() => {
setIsLoginModalOpen(false);
}, [ setIsLoginModalOpen ]);
const value = useMemo(() => { const value = useMemo(() => {
if (!feature.isEnabled) { if (!feature.isEnabled) {
return initialState; return initialState;
...@@ -267,14 +270,15 @@ export function RewardsContextProvider({ children }: Props) { ...@@ -267,14 +270,15 @@ export function RewardsContextProvider({ children }: Props) {
apiToken, apiToken,
isInitialized, isInitialized,
isLoginModalOpen, isLoginModalOpen,
openLoginModal: setIsLoginModalOpen.on, openLoginModal,
closeLoginModal: setIsLoginModalOpen.off, closeLoginModal,
login, login,
claim, claim,
}; };
}, [ }, [
isLoginModalOpen, setIsLoginModalOpen, balancesQuery, dailyRewardQuery, checkUserQuery, balancesQuery, dailyRewardQuery, checkUserQuery,
apiToken, login, claim, referralsQuery, rewardsConfigQuery, isInitialized, apiToken, login, claim, referralsQuery, rewardsConfigQuery, isInitialized,
isLoginModalOpen, openLoginModal, closeLoginModal,
]); ]);
return ( return (
......
import type { ColorMode } from '@chakra-ui/react';
import { useColorMode } from '@chakra-ui/react';
import { usePathname } from 'next/navigation'; import { usePathname } from 'next/navigation';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import type { ColorMode } from 'chakra/components/color-mode';
import { useColorMode } from 'chakra/components/color-mode';
import config from 'configs/app'; import config from 'configs/app';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
......
...@@ -70,8 +70,7 @@ export function app(): CspDev.DirectiveDescriptor { ...@@ -70,8 +70,7 @@ export function app(): CspDev.DirectiveDescriptor {
config.app.isDev ? KEY_WORDS.UNSAFE_EVAL : '', config.app.isDev ? KEY_WORDS.UNSAFE_EVAL : '',
// hash of ColorModeScript: system + dark // hash of ColorModeScript: system + dark
'\'sha256-e7MRMmTzLsLQvIy1iizO1lXf7VWYoQ6ysj5fuUzvRwE=\'', '\'sha256-yYJq8IP5/WhJj6zxyTmujEqBFs/MufRufp2QKJFU76M=\'',
'\'sha256-9A7qFFHmxdWjZMQmfzYD2XWaNHLu1ZmQB0Ds4Go764k=\'',
// CapybaraRunner // CapybaraRunner
'\'sha256-5+YTmTcBwCYdJ8Jetbr6kyjGp0Ry/H7ptpoun6CrSwQ=\'', '\'sha256-5+YTmTcBwCYdJ8Jetbr6kyjGp0Ry/H7ptpoun6CrSwQ=\'',
......
...@@ -40,11 +40,10 @@ ...@@ -40,11 +40,10 @@
"@blockscout/bens-types": "1.4.1", "@blockscout/bens-types": "1.4.1",
"@blockscout/stats-types": "2.0.0", "@blockscout/stats-types": "2.0.0",
"@blockscout/visualizer-types": "0.2.0", "@blockscout/visualizer-types": "0.2.0",
"@chakra-ui/react": "2.7.1", "@chakra-ui/react": "3.2.5",
"@chakra-ui/theme-tools": "^2.0.18", "@chakra-ui/theme-tools": "^2.0.18",
"@cloudnouns/kit": "1.1.6", "@cloudnouns/kit": "1.1.6",
"@emotion/react": "^11.10.4", "@emotion/react": "11.14.0",
"@emotion/styled": "^11.10.4",
"@growthbook/growthbook-react": "0.21.0", "@growthbook/growthbook-react": "0.21.0",
"@helia/verified-fetch": "2.0.1", "@helia/verified-fetch": "2.0.1",
"@hypelab/sdk-react": "^1.0.0", "@hypelab/sdk-react": "^1.0.0",
...@@ -60,15 +59,16 @@ ...@@ -60,15 +59,16 @@
"@opentelemetry/sdk-node": "0.49.1", "@opentelemetry/sdk-node": "0.49.1",
"@opentelemetry/sdk-trace-node": "1.22.0", "@opentelemetry/sdk-trace-node": "1.22.0",
"@opentelemetry/semantic-conventions": "1.22.0", "@opentelemetry/semantic-conventions": "1.22.0",
"@rollbar/react": "0.12.0-beta",
"@scure/base": "1.1.9",
"@reown/appkit": "1.6.0", "@reown/appkit": "1.6.0",
"@reown/appkit-adapter-wagmi": "1.6.0", "@reown/appkit-adapter-wagmi": "1.6.0",
"@rollbar/react": "0.12.0-beta",
"@scure/base": "1.1.9",
"@slise/embed-react": "^2.2.0", "@slise/embed-react": "^2.2.0",
"@tanstack/react-query": "5.55.4", "@tanstack/react-query": "5.55.4",
"@tanstack/react-query-devtools": "5.55.4", "@tanstack/react-query-devtools": "5.55.4",
"@types/papaparse": "^5.3.5", "@types/papaparse": "^5.3.5",
"@types/react-scroll": "^1.8.4", "@types/react-scroll": "^1.8.4",
"@uidotdev/usehooks": "2.4.1",
"airtable": "^0.12.2", "airtable": "^0.12.2",
"bignumber.js": "^9.1.0", "bignumber.js": "^9.1.0",
"blo": "^1.1.1", "blo": "^1.1.1",
...@@ -79,7 +79,6 @@ ...@@ -79,7 +79,6 @@
"dayjs": "^1.11.5", "dayjs": "^1.11.5",
"dom-to-image": "^2.6.0", "dom-to-image": "^2.6.0",
"focus-visible": "^5.2.0", "focus-visible": "^5.2.0",
"framer-motion": "^6.5.1",
"getit-sdk": "^1.0.4", "getit-sdk": "^1.0.4",
"gradient-avatar": "git+https://github.com/blockscout/gradient-avatar.git", "gradient-avatar": "git+https://github.com/blockscout/gradient-avatar.git",
"graphiql": "^2.2.0", "graphiql": "^2.2.0",
...@@ -91,6 +90,7 @@ ...@@ -91,6 +90,7 @@
"mixpanel-browser": "^2.47.0", "mixpanel-browser": "^2.47.0",
"monaco-editor": "^0.34.1", "monaco-editor": "^0.34.1",
"next": "15.0.3", "next": "15.0.3",
"next-themes": "0.4.4",
"nextjs-routes": "^1.0.8", "nextjs-routes": "^1.0.8",
"node-fetch": "^3.2.9", "node-fetch": "^3.2.9",
"papaparse": "^5.3.2", "papaparse": "^5.3.2",
...@@ -105,6 +105,7 @@ ...@@ -105,6 +105,7 @@
"react-dom": "18.3.1", "react-dom": "18.3.1",
"react-google-recaptcha": "3.1.0", "react-google-recaptcha": "3.1.0",
"react-hook-form": "7.52.1", "react-hook-form": "7.52.1",
"react-icons": "5.4.0",
"react-identicons": "^1.2.5", "react-identicons": "^1.2.5",
"react-intersection-observer": "^9.5.2", "react-intersection-observer": "^9.5.2",
"react-jazzicon": "^1.0.4", "react-jazzicon": "^1.0.4",
...@@ -119,6 +120,7 @@ ...@@ -119,6 +120,7 @@
"xss": "^1.0.14" "xss": "^1.0.14"
}, },
"devDependencies": { "devDependencies": {
"@chakra-ui/cli": "3.2.5",
"@eslint/compat": "1.2.2", "@eslint/compat": "1.2.2",
"@eslint/js": "9.14.0", "@eslint/js": "9.14.0",
"@next/eslint-plugin-next": "15.0.3", "@next/eslint-plugin-next": "15.0.3",
......
import { type ChakraProps } from '@chakra-ui/react'; import type { HTMLChakraProps } from '@chakra-ui/react';
import { GrowthBookProvider } from '@growthbook/growthbook-react'; import { GrowthBookProvider } from '@growthbook/growthbook-react';
import { QueryClientProvider } from '@tanstack/react-query'; import { QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
...@@ -7,10 +7,11 @@ import React from 'react'; ...@@ -7,10 +7,11 @@ import React from 'react';
import type { NextPageWithLayout } from 'nextjs/types'; import type { NextPageWithLayout } from 'nextjs/types';
import { Provider as ChakraProvider } from 'chakra/components/provider';
import { Toaster } from 'chakra/components/toaster';
import config from 'configs/app'; import config from 'configs/app';
import useQueryClientConfig from 'lib/api/useQueryClientConfig'; import useQueryClientConfig from 'lib/api/useQueryClientConfig';
import { AppContextProvider } from 'lib/contexts/app'; import { AppContextProvider } from 'lib/contexts/app';
import { ChakraProvider } from 'lib/contexts/chakra';
import { MarketplaceContextProvider } from 'lib/contexts/marketplace'; import { MarketplaceContextProvider } from 'lib/contexts/marketplace';
import { RewardsContextProvider } from 'lib/contexts/rewards'; import { RewardsContextProvider } from 'lib/contexts/rewards';
import { ScrollDirectionProvider } from 'lib/contexts/scrollDirection'; import { ScrollDirectionProvider } from 'lib/contexts/scrollDirection';
...@@ -34,7 +35,7 @@ type AppPropsWithLayout = AppProps & { ...@@ -34,7 +35,7 @@ type AppPropsWithLayout = AppProps & {
Component: NextPageWithLayout; Component: NextPageWithLayout;
}; };
const ERROR_SCREEN_STYLES: ChakraProps = { const ERROR_SCREEN_STYLES: HTMLChakraProps<'div'> = {
h: '100vh', h: '100vh',
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
...@@ -56,7 +57,7 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) { ...@@ -56,7 +57,7 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
const getLayout = Component.getLayout ?? ((page) => <Layout>{ page }</Layout>); const getLayout = Component.getLayout ?? ((page) => <Layout>{ page }</Layout>);
return ( return (
<ChakraProvider cookies={ pageProps.cookies }> <ChakraProvider>
<RollbarProvider config={ rollbarConfig }> <RollbarProvider config={ rollbarConfig }>
<AppErrorBoundary <AppErrorBoundary
{ ...ERROR_SCREEN_STYLES } { ...ERROR_SCREEN_STYLES }
...@@ -72,6 +73,7 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) { ...@@ -72,6 +73,7 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
<MarketplaceContextProvider> <MarketplaceContextProvider>
<SettingsContextProvider> <SettingsContextProvider>
{ getLayout(<Component { ...pageProps }/>) } { getLayout(<Component { ...pageProps }/>) }
<Toaster/>
{ config.features.rewards.isEnabled && <RewardsLoginModal/> } { config.features.rewards.isEnabled && <RewardsLoginModal/> }
</SettingsContextProvider> </SettingsContextProvider>
</MarketplaceContextProvider> </MarketplaceContextProvider>
......
import { ColorModeScript } from '@chakra-ui/react';
import type { DocumentContext } from 'next/document'; import type { DocumentContext } from 'next/document';
import Document, { Html, Head, Main, NextScript } from 'next/document'; import Document, { Html, Head, Main, NextScript } from 'next/document';
import React from 'react'; import React from 'react';
...@@ -7,7 +6,6 @@ import logRequestFromBot from 'nextjs/utils/logRequestFromBot'; ...@@ -7,7 +6,6 @@ import logRequestFromBot from 'nextjs/utils/logRequestFromBot';
import * as serverTiming from 'nextjs/utils/serverTiming'; import * as serverTiming from 'nextjs/utils/serverTiming';
import config from 'configs/app'; import config from 'configs/app';
import theme from 'theme/theme';
import * as svgSprite from 'ui/shared/IconSvg'; import * as svgSprite from 'ui/shared/IconSvg';
class MyDocument extends Document { class MyDocument extends Document {
...@@ -57,7 +55,6 @@ class MyDocument extends Document { ...@@ -57,7 +55,6 @@ class MyDocument extends Document {
<link rel="preload" as="image" href={ svgSprite.href }/> <link rel="preload" as="image" href={ svgSprite.href }/>
</Head> </Head>
<body> <body>
<ColorModeScript initialColorMode={ theme.config.initialColorMode }/>
<Main/> <Main/>
<NextScript/> <NextScript/>
</body> </body>
......
...@@ -8,8 +8,8 @@ ...@@ -8,8 +8,8 @@
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"noEmit": true, "noEmit": true,
"esModuleInterop": true, "esModuleInterop": true,
"module": "esnext", "module": "ESNext",
"moduleResolution": "node", "moduleResolution": "Bundler",
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"jsx": "preserve", "jsx": "preserve",
......
import { VStack, Textarea, Button, Alert, AlertTitle, AlertDescription, Code, Flex, Box } from '@chakra-ui/react'; import { VStack, Textarea, Button, Alert, Code, Flex, Box } from '@chakra-ui/react';
import mixpanel from 'mixpanel-browser'; import mixpanel from 'mixpanel-browser';
import type { ChangeEvent } from 'react'; import type { ChangeEvent } from 'react';
import React from 'react'; import React from 'react';
import { toaster } from 'chakra/components/toaster';
import config from 'configs/app'; import config from 'configs/app';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
import useFeatureValue from 'lib/growthbook/useFeatureValue'; import useFeatureValue from 'lib/growthbook/useFeatureValue';
import useGradualIncrement from 'lib/hooks/useGradualIncrement'; import useGradualIncrement from 'lib/hooks/useGradualIncrement';
import useToast from 'lib/hooks/useToast';
import { useRollbar } from 'lib/rollbar'; import { useRollbar } from 'lib/rollbar';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
const Login = () => { const Login = () => {
const rollbar = useRollbar(); const rollbar = useRollbar();
const toast = useToast();
const [ num, setNum ] = useGradualIncrement(0); const [ num, setNum ] = useGradualIncrement(0);
const testFeature = useFeatureValue('test_value', 'fallback'); const testFeature = useFeatureValue('test_value', 'fallback');
...@@ -41,18 +40,18 @@ const Login = () => { ...@@ -41,18 +40,18 @@ const Login = () => {
const handleSetTokenClick = React.useCallback(() => { const handleSetTokenClick = React.useCallback(() => {
cookies.set(cookies.NAMES.API_TOKEN, token); cookies.set(cookies.NAMES.API_TOKEN, token);
setToken(''); setToken('');
toast({ toaster.create({
position: 'top-right', placement: 'top-end',
title: 'Success 🥳', title: 'Success 🥳',
description: 'Successfully set cookie', description: 'Successfully set cookie',
status: 'success', type: 'success',
variant: 'subtle', onStatusChange: (details) => {
isClosable: true, if (details.status === 'unmounted') {
onCloseComplete: () => {
setFormVisibility(false); setFormVisibility(false);
}
}, },
}); });
}, [ toast, token ]); }, [ token ]);
const handleNumIncrement = React.useCallback(() => { const handleNumIncrement = React.useCallback(() => {
for (let index = 0; index < 5; index++) { for (let index = 0; index < 5; index++) {
...@@ -65,16 +64,16 @@ const Login = () => { ...@@ -65,16 +64,16 @@ const Login = () => {
<PageTitle title="Login page 😂"/> <PageTitle title="Login page 😂"/>
{ isFormVisible && ( { isFormVisible && (
<> <>
<Alert status="error" flexDirection="column" alignItems="flex-start"> <Alert.Root status="error" flexDirection="column" alignItems="flex-start">
<AlertTitle fontSize="md"> <Alert.Title fontSize="md">
!!! Temporary solution for authentication on localhost !!! !!! Temporary solution for authentication on localhost !!!
</AlertTitle> </Alert.Title>
<AlertDescription mt={ 3 }> <Alert.Description mt={ 3 }>
To Sign in go to production instance first, sign in there, copy obtained API token from cookie To Sign in go to production instance first, sign in there, copy obtained API token from cookie
<Code ml={ 1 }>{ cookies.NAMES.API_TOKEN }</Code> and paste it in the form below. After submitting the form you should be successfully <Code ml={ 1 }>{ cookies.NAMES.API_TOKEN }</Code> and paste it in the form below. After submitting the form you should be successfully
authenticated in current environment authenticated in current environment
</AlertDescription> </Alert.Description>
</Alert> </Alert.Root>
<Textarea value={ token } onChange={ handleTokenChange } placeholder="API token"/> <Textarea value={ token } onChange={ handleTokenChange } placeholder="API token"/>
<Button onClick={ handleSetTokenClick }>Set cookie</Button> <Button onClick={ handleSetTokenClick }>Set cookie</Button>
</> </>
......
import { chakra, Flex } from '@chakra-ui/react'; import { chakra, Flex } from '@chakra-ui/react';
import type { GroupBase, SelectComponentsConfig, SingleValueProps } from 'chakra-react-select';
import { chakraComponents } from 'chakra-react-select';
import _capitalize from 'lodash/capitalize'; import _capitalize from 'lodash/capitalize';
import React from 'react'; import React from 'react';
import { useFormContext } from 'react-hook-form'; import { useFormContext } from 'react-hook-form';
...@@ -9,6 +7,8 @@ import type { FormFields } from '../types'; ...@@ -9,6 +7,8 @@ import type { FormFields } from '../types';
import type { PublicTagType } from 'types/api/addressMetadata'; import type { PublicTagType } from 'types/api/addressMetadata';
import type { Option } from 'ui/shared/forms/inputs/select/types'; import type { Option } from 'ui/shared/forms/inputs/select/types';
import { chakraComponents } from 'chakra-react-select';
import type { GroupBase, SelectComponentsConfig, SingleValueProps } from 'chakra-react-select';
import FormFieldFancySelect from 'ui/shared/forms/fields/FormFieldFancySelect'; import FormFieldFancySelect from 'ui/shared/forms/fields/FormFieldFancySelect';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
......
import { Box, useColorModeValue } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { useColorModeValue } from 'chakra/components/color-mode';
interface Props { interface Props {
children: React.ReactNode; children: React.ReactNode;
} }
......
import { Button, Text } from '@chakra-ui/react'; import { Button, Text } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { toaster } from 'chakra/components/toaster';
import config from 'configs/app'; import config from 'configs/app';
import buildUrl from 'lib/api/buildUrl'; import buildUrl from 'lib/api/buildUrl';
import useFetch from 'lib/hooks/useFetch'; import useFetch from 'lib/hooks/useFetch';
import useToast from 'lib/hooks/useToast';
import ReCaptcha from 'ui/shared/reCaptcha/ReCaptcha'; import ReCaptcha from 'ui/shared/reCaptcha/ReCaptcha';
import useReCaptcha from 'ui/shared/reCaptcha/useReCaptcha'; import useReCaptcha from 'ui/shared/reCaptcha/useReCaptcha';
...@@ -12,7 +12,6 @@ import AppErrorIcon from '../AppErrorIcon'; ...@@ -12,7 +12,6 @@ import AppErrorIcon from '../AppErrorIcon';
import AppErrorTitle from '../AppErrorTitle'; import AppErrorTitle from '../AppErrorTitle';
const AppErrorTooManyRequests = () => { const AppErrorTooManyRequests = () => {
const toast = useToast();
const fetch = useFetch(); const fetch = useFetch();
const recaptcha = useReCaptcha(); const recaptcha = useReCaptcha();
...@@ -32,16 +31,14 @@ const AppErrorTooManyRequests = () => { ...@@ -32,16 +31,14 @@ const AppErrorTooManyRequests = () => {
window.location.reload(); window.location.reload();
} catch (error) { } catch (error) {
toast({ toaster.create({
position: 'top-right', placement: 'top-end',
title: 'Error', title: 'Error',
description: 'Unable to get client key.', description: 'Unable to get client key.',
status: 'error', type: 'error',
variant: 'subtle',
isClosable: true,
}); });
} }
}, [ recaptcha, toast, fetch ]); }, [ recaptcha, fetch ]);
if (!config.services.reCaptchaV2.siteKey) { if (!config.services.reCaptchaV2.siteKey) {
throw new Error('reCAPTCHA V2 site key is not set'); throw new Error('reCAPTCHA V2 site key is not set');
......
import { Box, Text, chakra } from '@chakra-ui/react'; import { Box, Text, chakra } from '@chakra-ui/react';
import { keyframes } from '@chakra-ui/system';
import React from 'react'; import React from 'react';
const runnerAnimation = keyframes`
0% { left: 0%; transform: translateX(-1%); }
100% { left: '100%'; transform: translateX(-99%); }
`;
interface Props { interface Props {
className?: string; className?: string;
text?: string; text?: string;
...@@ -24,7 +18,8 @@ const ContentLoader = ({ className, text }: Props) => { ...@@ -24,7 +18,8 @@ const ContentLoader = ({ className, text }: Props) => {
position: 'absolute', position: 'absolute',
width: '60px', width: '60px',
height: '6px', height: '6px',
animation: `${ runnerAnimation } 700ms ease-in-out infinite alternate`, // TODO @tom2drum check this animation
animation: `slide-from-left-full 700ms ease-in-out infinite alternate`,
left: '100%', left: '100%',
top: 0, top: 0,
backgroundColor: 'blue.300', backgroundColor: 'blue.300',
......
...@@ -15,7 +15,7 @@ import React, { useCallback, useEffect, useRef } from 'react'; ...@@ -15,7 +15,7 @@ import React, { useCallback, useEffect, useRef } from 'react';
import type { FontFace } from 'use-font-face-observer'; import type { FontFace } from 'use-font-face-observer';
import useFontFaceObserver from 'use-font-face-observer'; import useFontFaceObserver from 'use-font-face-observer';
import { BODY_TYPEFACE, HEADING_TYPEFACE } from 'theme/foundations/typography'; import { BODY_TYPEFACE, HEADING_TYPEFACE } from 'chakra/theme/foundations/typography';
const TAIL_LENGTH = 4; const TAIL_LENGTH = 4;
const HEAD_MIN_LENGTH = 4; const HEAD_MIN_LENGTH = 4;
......
import { Button } from '@chakra-ui/react'; import { Button } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { toaster } from 'chakra/components/toaster';
import config from 'configs/app'; import config from 'configs/app';
import useToast from 'lib/hooks/useToast';
import * as mixpanel from 'lib/mixpanel/index'; import * as mixpanel from 'lib/mixpanel/index';
import useAddOrSwitchChain from 'lib/web3/useAddOrSwitchChain'; import useAddOrSwitchChain from 'lib/web3/useAddOrSwitchChain';
import useProvider from 'lib/web3/useProvider'; import useProvider from 'lib/web3/useProvider';
...@@ -12,7 +12,6 @@ import IconSvg from 'ui/shared/IconSvg'; ...@@ -12,7 +12,6 @@ import IconSvg from 'ui/shared/IconSvg';
const feature = config.features.web3Wallet; const feature = config.features.web3Wallet;
const NetworkAddToWallet = () => { const NetworkAddToWallet = () => {
const toast = useToast();
const { provider, wallet } = useProvider(); const { provider, wallet } = useProvider();
const addOrSwitchChain = useAddOrSwitchChain(); const addOrSwitchChain = useAddOrSwitchChain();
...@@ -24,13 +23,9 @@ const NetworkAddToWallet = () => { ...@@ -24,13 +23,9 @@ const NetworkAddToWallet = () => {
try { try {
await addOrSwitchChain(); await addOrSwitchChain();
toast({ toaster.success({
position: 'top-right',
title: 'Success', title: 'Success',
description: 'Successfully added network to your wallet', description: 'Successfully added network to your wallet',
status: 'success',
variant: 'subtle',
isClosable: true,
}); });
mixpanel.logEvent(mixpanel.EventTypes.ADD_TO_WALLET, { mixpanel.logEvent(mixpanel.EventTypes.ADD_TO_WALLET, {
...@@ -39,16 +34,12 @@ const NetworkAddToWallet = () => { ...@@ -39,16 +34,12 @@ const NetworkAddToWallet = () => {
}); });
} catch (error) { } catch (error) {
toast({ toaster.error({
position: 'top-right',
title: 'Error', title: 'Error',
description: (error as Error)?.message || 'Something went wrong', description: (error as Error)?.message || 'Something went wrong',
status: 'error',
variant: 'subtle',
isClosable: true,
}); });
} }
}, [ addOrSwitchChain, provider, toast, wallet ]); }, [ addOrSwitchChain, provider, wallet ]);
if (!provider || !wallet || !config.chain.rpcUrl || !feature.isEnabled) { if (!provider || !wallet || !config.chain.rpcUrl || !feature.isEnabled) {
return null; return null;
......
import { Heading, Flex, Tooltip, Link, chakra, Skeleton, useDisclosure } from '@chakra-ui/react'; import { Heading, Flex, Link, chakra, Skeleton, useDisclosure } from '@chakra-ui/react';
import _debounce from 'lodash/debounce'; import _debounce from 'lodash/debounce';
import React from 'react'; import React from 'react';
import { Tooltip } from 'chakra/components/tooltip';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import TextAd from 'ui/shared/ad/TextAd'; import TextAd from 'ui/shared/ad/TextAd';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
...@@ -38,7 +39,7 @@ const BackLink = (props: BackLinkProp & { isLoading?: boolean }) => { ...@@ -38,7 +39,7 @@ const BackLink = (props: BackLinkProp & { isLoading?: boolean }) => {
mr={ 3 } mr={ 3 }
my={ 2 } my={ 2 }
verticalAlign="text-bottom" verticalAlign="text-bottom"
isLoaded={ !props.isLoading } loading={ props.isLoading }
/> />
); );
} }
...@@ -47,7 +48,7 @@ const BackLink = (props: BackLinkProp & { isLoading?: boolean }) => { ...@@ -47,7 +48,7 @@ const BackLink = (props: BackLinkProp & { isLoading?: boolean }) => {
if ('url' in props) { if ('url' in props) {
return ( return (
<Tooltip label={ props.label }> <Tooltip content={ props.label }>
<LinkInternal display="inline-flex" href={ props.url } h="40px" mr={ 3 }> <LinkInternal display="inline-flex" href={ props.url } h="40px" mr={ 3 }>
{ icon } { icon }
</LinkInternal> </LinkInternal>
...@@ -56,7 +57,7 @@ const BackLink = (props: BackLinkProp & { isLoading?: boolean }) => { ...@@ -56,7 +57,7 @@ const BackLink = (props: BackLinkProp & { isLoading?: boolean }) => {
} }
return ( return (
<Tooltip label={ props.label }> <Tooltip content={ props.label }>
<Link display="inline-flex" onClick={ props.onClick } h="40px" mr={ 3 }> <Link display="inline-flex" onClick={ props.onClick } h="40px" mr={ 3 }>
{ icon } { icon }
</Link> </Link>
...@@ -64,7 +65,7 @@ const BackLink = (props: BackLinkProp & { isLoading?: boolean }) => { ...@@ -64,7 +65,7 @@ const BackLink = (props: BackLinkProp & { isLoading?: boolean }) => {
); );
}; };
const PageTitle = ({ title, contentAfter, withTextAd, backLink, className, isLoading, afterTitle, beforeTitle, secondRow }: Props) => { const PageTitle = ({ title, contentAfter, withTextAd, backLink, className, isLoading = false, afterTitle, beforeTitle, secondRow }: Props) => {
const tooltip = useDisclosure(); const tooltip = useDisclosure();
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const [ isTextTruncated, setIsTextTruncated ] = React.useState(false); const [ isTextTruncated, setIsTextTruncated ] = React.useState(false);
...@@ -101,6 +102,14 @@ const PageTitle = ({ title, contentAfter, withTextAd, backLink, className, isLoa ...@@ -101,6 +102,14 @@ const PageTitle = ({ title, contentAfter, withTextAd, backLink, className, isLoa
}; };
}, [ updatedTruncateState ]); }, [ updatedTruncateState ]);
const handleTooltipOpenChange = React.useCallback((details: { open: boolean }) => {
if (details.open) {
tooltip.onOpen();
} else {
tooltip.onClose();
}
}, [ tooltip ]);
return ( return (
<Flex className={ className } flexDir="column" rowGap={ 3 } mb={ 6 }> <Flex className={ className } flexDir="column" rowGap={ 3 } mb={ 6 }>
<Flex <Flex
...@@ -114,16 +123,16 @@ const PageTitle = ({ title, contentAfter, withTextAd, backLink, className, isLoa ...@@ -114,16 +123,16 @@ const PageTitle = ({ title, contentAfter, withTextAd, backLink, className, isLoa
{ backLink && <BackLink { ...backLink } isLoading={ isLoading }/> } { backLink && <BackLink { ...backLink } isLoading={ isLoading }/> }
{ beforeTitle } { beforeTitle }
<Skeleton <Skeleton
isLoaded={ !isLoading } loading={ isLoading }
overflow="hidden" overflow="hidden"
> >
<Tooltip <Tooltip
label={ title } content={ title }
isOpen={ tooltip.isOpen } open={ tooltip.open }
onClose={ tooltip.onClose } onOpenChange={ handleTooltipOpenChange }
maxW={{ base: 'calc(100vw - 32px)', lg: '500px' }} contentProps={{ maxW: { base: 'calc(100vw - 32px)', lg: '500px' } }}
closeOnScroll={ isMobile ? true : false } closeOnScroll={ isMobile ? true : false }
isDisabled={ !isTextTruncated } disabled={ !isTextTruncated }
> >
<Heading <Heading
ref={ headingRef } ref={ headingRef }
...@@ -154,7 +163,7 @@ const PageTitle = ({ title, contentAfter, withTextAd, backLink, className, isLoa ...@@ -154,7 +163,7 @@ const PageTitle = ({ title, contentAfter, withTextAd, backLink, className, isLoa
{ withTextAd && <TextAd order={{ base: -1, lg: 100 }} mb={{ base: 6, lg: 0 }} ml="auto" w={{ base: '100%', lg: 'auto' }}/> } { withTextAd && <TextAd order={{ base: -1, lg: 100 }} mb={{ base: 6, lg: 0 }} ml="auto" w={{ base: '100%', lg: 'auto' }}/> }
</Flex> </Flex>
{ secondRow && ( { secondRow && (
<Skeleton isLoaded={ !isLoading } alignItems="center" minH={ 10 } overflow="hidden" display="flex" _empty={{ display: 'none' }}> <Skeleton loading={ isLoading } alignItems="center" minH={ 10 } overflow="hidden" display="flex" _empty={{ display: 'none' }}>
{ secondRow } { secondRow }
</Skeleton> </Skeleton>
) } ) }
......
...@@ -4,7 +4,7 @@ import debounce from 'lodash/debounce'; ...@@ -4,7 +4,7 @@ import debounce from 'lodash/debounce';
import React from 'react'; import React from 'react';
import useFontFaceObserver from 'use-font-face-observer'; import useFontFaceObserver from 'use-font-face-observer';
import { BODY_TYPEFACE } from 'theme/foundations/typography'; import { BODY_TYPEFACE } from 'chakra/theme/foundations/typography';
interface Props { interface Props {
children: React.ReactNode; children: React.ReactNode;
......
import { useColorMode } from '@chakra-ui/react';
import { createAppKit, useAppKitTheme } from '@reown/appkit/react'; import { createAppKit, useAppKitTheme } from '@reown/appkit/react';
import React from 'react'; import React from 'react';
import { WagmiProvider } from 'wagmi'; import { WagmiProvider } from 'wagmi';
import { useColorMode } from 'chakra/components/color-mode';
import colors from 'chakra/theme/foundations/colors';
import { BODY_TYPEFACE } from 'chakra/theme/foundations/typography';
import zIndex from 'chakra/theme/foundations/zIndex';
import config from 'configs/app'; import config from 'configs/app';
import currentChain from 'lib/web3/currentChain'; import currentChain from 'lib/web3/currentChain';
import wagmiConfig from 'lib/web3/wagmiConfig'; import wagmiConfig from 'lib/web3/wagmiConfig';
import colors from 'theme/foundations/colors';
import { BODY_TYPEFACE } from 'theme/foundations/typography';
import zIndices from 'theme/foundations/zIndices';
const feature = config.features.blockchainInteraction; const feature = config.features.blockchainInteraction;
...@@ -37,9 +37,9 @@ const init = () => { ...@@ -37,9 +37,9 @@ const init = () => {
}, },
themeVariables: { themeVariables: {
'--w3m-font-family': `${ BODY_TYPEFACE }, sans-serif`, '--w3m-font-family': `${ BODY_TYPEFACE }, sans-serif`,
'--w3m-accent': colors.blue[600], '--w3m-accent': colors.blue[600].value,
'--w3m-border-radius-master': '2px', '--w3m-border-radius-master': '2px',
'--w3m-z-index': zIndices.popover, '--w3m-z-index': zIndex.popover.value,
}, },
featuredWalletIds: [], featuredWalletIds: [],
allowUnsupportedChain: true, allowUnsupportedChain: true,
...@@ -66,7 +66,7 @@ const Web3ModalProvider = ({ children }: Props) => { ...@@ -66,7 +66,7 @@ const Web3ModalProvider = ({ children }: Props) => {
const { setThemeMode } = useAppKitTheme(); const { setThemeMode } = useAppKitTheme();
React.useEffect(() => { React.useEffect(() => {
setThemeMode(colorMode); setThemeMode(colorMode ?? 'light');
}, [ colorMode, setThemeMode ]); }, [ colorMode, setThemeMode ]);
return ( return (
......
import { FormControl, useToken, useColorMode } from '@chakra-ui/react'; import { FormControl, useToken, useColorMode } from '@chakra-ui/react';
import type { CSSObjectWithLabel, GroupBase, SingleValue, MultiValue, AsyncProps, Props as SelectProps } from 'chakra-react-select';
import { Select, AsyncSelect } from 'chakra-react-select';
import React from 'react'; import React from 'react';
import type { FieldError, FieldErrorsImpl, Merge } from 'react-hook-form'; import type { FieldError, FieldErrorsImpl, Merge } from 'react-hook-form';
import type { Option } from './types'; import type { Option } from './types';
import { Select, AsyncSelect } from 'chakra-react-select';
import type { CSSObjectWithLabel, GroupBase, SingleValue, MultiValue, AsyncProps, Props as SelectProps } from 'chakra-react-select';
import FormInputPlaceholder from 'ui/shared/forms/inputs/FormInputPlaceholder'; import FormInputPlaceholder from 'ui/shared/forms/inputs/FormInputPlaceholder';
import { getChakraStyles } from 'ui/shared/forms/inputs/select/utils'; import { getChakraStyles } from 'ui/shared/forms/inputs/select/utils';
......
import type { ColorMode } from '@chakra-ui/react'; import type { ColorMode } from '@chakra-ui/react';
import type { Size, ChakraStylesConfig } from 'chakra-react-select';
import type { Option } from './types'; import type { Option } from './types';
import type { Size, ChakraStylesConfig } from 'chakra-react-select';
import theme from 'theme/theme'; import theme from 'theme/theme';
import getFormStyles from 'theme/utils/getFormStyles'; import getFormStyles from 'theme/utils/getFormStyles';
......
...@@ -2,14 +2,12 @@ import type { ...@@ -2,14 +2,12 @@ import type {
PlacementWithLogical } from '@chakra-ui/react'; PlacementWithLogical } from '@chakra-ui/react';
import { import {
Box, Box,
DarkMode,
Flex, Flex,
Grid, Grid,
PopoverBody, PopoverBody,
PopoverContent, PopoverContent,
PopoverTrigger, PopoverTrigger,
Portal, Portal,
useColorModeValue,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
...@@ -17,6 +15,7 @@ import type { HomeStats } from 'types/api/stats'; ...@@ -17,6 +15,7 @@ import type { HomeStats } from 'types/api/stats';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
import { useColorModeValue } from 'chakra/components/color-mode';
import config from 'configs/app'; import config from 'configs/app';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import Popover from 'ui/shared/chakra/Popover'; import Popover from 'ui/shared/chakra/Popover';
...@@ -56,7 +55,7 @@ const GasInfoTooltip = ({ children, data, dataUpdatedAt, isOpen, placement }: Pr ...@@ -56,7 +55,7 @@ const GasInfoTooltip = ({ children, data, dataUpdatedAt, isOpen, placement }: Pr
<Portal> <Portal>
<PopoverContent bgColor={ tooltipBg } w="auto"> <PopoverContent bgColor={ tooltipBg } w="auto">
<PopoverBody color="white"> <PopoverBody color="white">
<DarkMode> { /* TODO @tom2drum add dark mode */ }
<Flex flexDir="column" fontSize="xs" lineHeight={ 4 } rowGap={ 3 }> <Flex flexDir="column" fontSize="xs" lineHeight={ 4 } rowGap={ 3 }>
{ data.gas_price_updated_at && ( { data.gas_price_updated_at && (
<Flex justifyContent="space-between"> <Flex justifyContent="space-between">
...@@ -77,7 +76,6 @@ const GasInfoTooltip = ({ children, data, dataUpdatedAt, isOpen, placement }: Pr ...@@ -77,7 +76,6 @@ const GasInfoTooltip = ({ children, data, dataUpdatedAt, isOpen, placement }: Pr
Gas tracker overview Gas tracker overview
</LinkInternal> </LinkInternal>
</Flex> </Flex>
</DarkMode>
</PopoverBody> </PopoverBody>
</PopoverContent> </PopoverContent>
</Portal> </Portal>
......
...@@ -4,22 +4,23 @@ import type { Props } from './types'; ...@@ -4,22 +4,23 @@ import type { Props } from './types';
import AppErrorBoundary from 'ui/shared/AppError/AppErrorBoundary'; import AppErrorBoundary from 'ui/shared/AppError/AppErrorBoundary';
import HeaderAlert from 'ui/snippets/header/HeaderAlert'; import HeaderAlert from 'ui/snippets/header/HeaderAlert';
import HeaderDesktop from 'ui/snippets/header/HeaderDesktop'; // TODO @tom2drum fix main layout
import HeaderMobile from 'ui/snippets/header/HeaderMobile'; // import HeaderDesktop from 'ui/snippets/header/HeaderDesktop';
// import HeaderMobile from 'ui/snippets/header/HeaderMobile';
import * as Layout from './components'; import * as Layout from './components';
const LayoutDefault = ({ children }: Props) => { const LayoutDefault = ({ children }: Props) => {
return ( return (
<Layout.Container> <Layout.Container>
<Layout.TopRow/> { /* <Layout.TopRow/> */ }
<Layout.NavBar/> { /* <Layout.NavBar/>
<HeaderMobile/> <HeaderMobile/> */ }
<Layout.MainArea> <Layout.MainArea>
<Layout.SideBar/> { /* <Layout.SideBar/> */ }
<Layout.MainColumn> <Layout.MainColumn>
<HeaderAlert/> <HeaderAlert/>
<HeaderDesktop/> { /* <HeaderDesktop/> */ }
<AppErrorBoundary> <AppErrorBoundary>
<Layout.Content> <Layout.Content>
{ children } { children }
...@@ -27,7 +28,7 @@ const LayoutDefault = ({ children }: Props) => { ...@@ -27,7 +28,7 @@ const LayoutDefault = ({ children }: Props) => {
</AppErrorBoundary> </AppErrorBoundary>
</Layout.MainColumn> </Layout.MainColumn>
</Layout.MainArea> </Layout.MainArea>
<Layout.Footer/> { /* <Layout.Footer/> */ }
</Layout.Container> </Layout.Container>
); );
}; };
......
import { Box, chakra, useColorModeValue } from '@chakra-ui/react'; import { Box, chakra } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { useColorModeValue } from 'chakra/components/color-mode';
interface Props { interface Props {
children: React.ReactNode; children: React.ReactNode;
className?: string; className?: string;
......
...@@ -5,10 +5,10 @@ import { FormProvider, useForm } from 'react-hook-form'; ...@@ -5,10 +5,10 @@ import { FormProvider, useForm } from 'react-hook-form';
import type { EmailFormFields, Screen } from '../types'; import type { EmailFormFields, Screen } from '../types';
import { toaster } from 'chakra/components/toaster';
import useApiFetch from 'lib/api/useApiFetch'; import useApiFetch from 'lib/api/useApiFetch';
import getErrorMessage from 'lib/errors/getErrorMessage'; import getErrorMessage from 'lib/errors/getErrorMessage';
import getErrorObjPayload from 'lib/errors/getErrorObjPayload'; import getErrorObjPayload from 'lib/errors/getErrorObjPayload';
import useToast from 'lib/hooks/useToast';
import * as mixpanel from 'lib/mixpanel'; import * as mixpanel from 'lib/mixpanel';
import FormFieldEmail from 'ui/shared/forms/fields/FormFieldEmail'; import FormFieldEmail from 'ui/shared/forms/fields/FormFieldEmail';
import ReCaptcha from 'ui/shared/reCaptcha/ReCaptcha'; import ReCaptcha from 'ui/shared/reCaptcha/ReCaptcha';
...@@ -27,7 +27,6 @@ interface Props { ...@@ -27,7 +27,6 @@ interface Props {
const AuthModalScreenEmail = ({ onSubmit, isAuth, mixpanelConfig }: Props) => { const AuthModalScreenEmail = ({ onSubmit, isAuth, mixpanelConfig }: Props) => {
const apiFetch = useApiFetch(); const apiFetch = useApiFetch();
const toast = useToast();
const recaptcha = useReCaptcha(); const recaptcha = useReCaptcha();
const formApi = useForm<EmailFormFields>({ const formApi = useForm<EmailFormFields>({
...@@ -64,13 +63,12 @@ const AuthModalScreenEmail = ({ onSubmit, isAuth, mixpanelConfig }: Props) => { ...@@ -64,13 +63,12 @@ const AuthModalScreenEmail = ({ onSubmit, isAuth, mixpanelConfig }: Props) => {
} }
onSubmit({ type: 'otp_code', email: formData.email, isAuth }); onSubmit({ type: 'otp_code', email: formData.email, isAuth });
} catch (error) { } catch (error) {
toast({ toaster.error({
status: 'error',
title: 'Error', title: 'Error',
description: getErrorObjPayload<{ message: string }>(error)?.message || getErrorMessage(error) || 'Something went wrong', description: getErrorObjPayload<{ message: string }>(error)?.message || getErrorMessage(error) || 'Something went wrong',
}); });
} }
}, [ recaptcha, apiFetch, isAuth, onSubmit, mixpanelConfig?.account_link_info.source, toast ]); }, [ recaptcha, apiFetch, isAuth, onSubmit, mixpanelConfig?.account_link_info.source ]);
return ( return (
<FormProvider { ...formApi }> <FormProvider { ...formApi }>
...@@ -89,7 +87,7 @@ const AuthModalScreenEmail = ({ onSubmit, isAuth, mixpanelConfig }: Props) => { ...@@ -89,7 +87,7 @@ const AuthModalScreenEmail = ({ onSubmit, isAuth, mixpanelConfig }: Props) => {
<Button <Button
mt={ 6 } mt={ 6 }
type="submit" type="submit"
isDisabled={ formApi.formState.isSubmitting } disabled={ formApi.formState.isSubmitting }
isLoading={ formApi.formState.isSubmitting } isLoading={ formApi.formState.isSubmitting }
loadingText="Send a code" loadingText="Send a code"
> >
......
...@@ -6,10 +6,10 @@ import { FormProvider, useForm } from 'react-hook-form'; ...@@ -6,10 +6,10 @@ import { FormProvider, useForm } from 'react-hook-form';
import type { OtpCodeFormFields, ScreenSuccess } from '../types'; import type { OtpCodeFormFields, ScreenSuccess } from '../types';
import type { UserInfo } from 'types/api/account'; import type { UserInfo } from 'types/api/account';
import { toaster } from 'chakra/components/toaster';
import useApiFetch from 'lib/api/useApiFetch'; import useApiFetch from 'lib/api/useApiFetch';
import getErrorMessage from 'lib/errors/getErrorMessage'; import getErrorMessage from 'lib/errors/getErrorMessage';
import getErrorObjPayload from 'lib/errors/getErrorObjPayload'; import getErrorObjPayload from 'lib/errors/getErrorObjPayload';
import useToast from 'lib/hooks/useToast';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import ReCaptcha from 'ui/shared/reCaptcha/ReCaptcha'; import ReCaptcha from 'ui/shared/reCaptcha/ReCaptcha';
import useReCaptcha from 'ui/shared/reCaptcha/useReCaptcha'; import useReCaptcha from 'ui/shared/reCaptcha/useReCaptcha';
...@@ -25,7 +25,6 @@ interface Props { ...@@ -25,7 +25,6 @@ interface Props {
const AuthModalScreenOtpCode = ({ email, onSuccess, isAuth }: Props) => { const AuthModalScreenOtpCode = ({ email, onSuccess, isAuth }: Props) => {
const apiFetch = useApiFetch(); const apiFetch = useApiFetch();
const toast = useToast();
const recaptcha = useReCaptcha(); const recaptcha = useReCaptcha();
const [ isCodeSending, setIsCodeSending ] = React.useState(false); const [ isCodeSending, setIsCodeSending ] = React.useState(false);
...@@ -61,13 +60,12 @@ const AuthModalScreenOtpCode = ({ email, onSuccess, isAuth }: Props) => { ...@@ -61,13 +60,12 @@ const AuthModalScreenOtpCode = ({ email, onSuccess, isAuth }: Props) => {
return; return;
} }
toast({ toaster.error({
status: 'error',
title: 'Error', title: 'Error',
description: getErrorMessage(error) || 'Something went wrong', description: getErrorMessage(error) || 'Something went wrong',
}); });
}); });
}, [ apiFetch, email, onSuccess, isAuth, toast, formApi ]); }, [ apiFetch, email, onSuccess, isAuth, formApi ]);
const handleResendCodeClick = React.useCallback(async() => { const handleResendCodeClick = React.useCallback(async() => {
try { try {
...@@ -81,23 +79,21 @@ const AuthModalScreenOtpCode = ({ email, onSuccess, isAuth }: Props) => { ...@@ -81,23 +79,21 @@ const AuthModalScreenOtpCode = ({ email, onSuccess, isAuth }: Props) => {
}, },
}); });
toast({ toaster.success({
status: 'success',
title: 'Success', title: 'Success',
description: 'Code has been sent to your email', description: 'Code has been sent to your email',
}); });
} catch (error) { } catch (error) {
const apiError = getErrorObjPayload<{ message: string }>(error); const apiError = getErrorObjPayload<{ message: string }>(error);
toast({ toaster.error({
status: 'error',
title: 'Error', title: 'Error',
description: apiError?.message || getErrorMessage(error) || 'Something went wrong', description: apiError?.message || getErrorMessage(error) || 'Something went wrong',
}); });
} finally { } finally {
setIsCodeSending(false); setIsCodeSending(false);
} }
}, [ apiFetch, email, formApi, toast, recaptcha ]); }, [ apiFetch, email, formApi, recaptcha ]);
return ( return (
<FormProvider { ...formApi }> <FormProvider { ...formApi }>
......
...@@ -4,11 +4,11 @@ import React from 'react'; ...@@ -4,11 +4,11 @@ import React from 'react';
import type { Route } from 'nextjs-routes'; import type { Route } from 'nextjs-routes';
import { toaster } from 'chakra/components/toaster';
import config from 'configs/app'; import config from 'configs/app';
import useApiFetch from 'lib/api/useApiFetch'; import useApiFetch from 'lib/api/useApiFetch';
import { getResourceKey } from 'lib/api/useApiQuery'; import { getResourceKey } from 'lib/api/useApiQuery';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
import useToast from 'lib/hooks/useToast';
import * as mixpanel from 'lib/mixpanel'; import * as mixpanel from 'lib/mixpanel';
const PROTECTED_ROUTES: Array<Route['pathname']> = [ const PROTECTED_ROUTES: Array<Route['pathname']> = [
...@@ -24,7 +24,6 @@ const PROTECTED_ROUTES: Array<Route['pathname']> = [ ...@@ -24,7 +24,6 @@ const PROTECTED_ROUTES: Array<Route['pathname']> = [
export default function useLogout() { export default function useLogout() {
const router = useRouter(); const router = useRouter();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const toast = useToast();
const apiFetch = useApiFetch(); const apiFetch = useApiFetch();
return React.useCallback(async() => { return React.useCallback(async() => {
...@@ -62,11 +61,10 @@ export default function useLogout() { ...@@ -62,11 +61,10 @@ export default function useLogout() {
router.push({ pathname: '/' }, undefined, { shallow: true }); router.push({ pathname: '/' }, undefined, { shallow: true });
} }
} catch (error) { } catch (error) {
toast({ toaster.error({
status: 'error',
title: 'Logout failed', title: 'Logout failed',
description: 'Please try again later', description: 'Please try again later',
}); });
} }
}, [ apiFetch, queryClient, router, toast ]); }, [ apiFetch, queryClient, router ]);
} }
...@@ -3,12 +3,12 @@ import { useSignMessage } from 'wagmi'; ...@@ -3,12 +3,12 @@ import { useSignMessage } from 'wagmi';
import type { UserInfo } from 'types/api/account'; import type { UserInfo } from 'types/api/account';
import { toaster } from 'chakra/components/toaster';
import config from 'configs/app'; import config from 'configs/app';
import useApiFetch from 'lib/api/useApiFetch'; import useApiFetch from 'lib/api/useApiFetch';
import getErrorMessage from 'lib/errors/getErrorMessage'; import getErrorMessage from 'lib/errors/getErrorMessage';
import getErrorObj from 'lib/errors/getErrorObj'; import getErrorObj from 'lib/errors/getErrorObj';
import getErrorObjPayload from 'lib/errors/getErrorObjPayload'; import getErrorObjPayload from 'lib/errors/getErrorObjPayload';
import useToast from 'lib/hooks/useToast';
import type * as mixpanel from 'lib/mixpanel'; import type * as mixpanel from 'lib/mixpanel';
import useWeb3Wallet from 'lib/web3/useWallet'; import useWeb3Wallet from 'lib/web3/useWallet';
...@@ -24,7 +24,6 @@ function useSignInWithWallet({ onSuccess, onError, source = 'Login', isAuth }: P ...@@ -24,7 +24,6 @@ function useSignInWithWallet({ onSuccess, onError, source = 'Login', isAuth }: P
const isConnectingWalletRef = React.useRef(false); const isConnectingWalletRef = React.useRef(false);
const apiFetch = useApiFetch(); const apiFetch = useApiFetch();
const toast = useToast();
const web3Wallet = useWeb3Wallet({ source }); const web3Wallet = useWeb3Wallet({ source });
const { signMessageAsync } = useSignMessage(); const { signMessageAsync } = useSignMessage();
...@@ -48,15 +47,14 @@ function useSignInWithWallet({ onSuccess, onError, source = 'Login', isAuth }: P ...@@ -48,15 +47,14 @@ function useSignInWithWallet({ onSuccess, onError, source = 'Login', isAuth }: P
const apiErrorMessage = getErrorObjPayload<{ message: string }>(error)?.message; const apiErrorMessage = getErrorObjPayload<{ message: string }>(error)?.message;
const shortMessage = errorObj && 'shortMessage' in errorObj && typeof errorObj.shortMessage === 'string' ? errorObj.shortMessage : undefined; const shortMessage = errorObj && 'shortMessage' in errorObj && typeof errorObj.shortMessage === 'string' ? errorObj.shortMessage : undefined;
onError?.(); onError?.();
toast({ toaster.error({
status: 'error',
title: 'Error', title: 'Error',
description: apiErrorMessage || shortMessage || getErrorMessage(error) || 'Something went wrong', description: apiErrorMessage || shortMessage || getErrorMessage(error) || 'Something went wrong',
}); });
} finally { } finally {
setIsPending(false); setIsPending(false);
} }
}, [ apiFetch, isAuth, onError, onSuccess, signMessageAsync, toast ]); }, [ apiFetch, isAuth, onError, onSuccess, signMessageAsync ]);
const start = React.useCallback(() => { const start = React.useCallback(() => {
setIsPending(true); setIsPending(true);
......
import { Box, Flex, Drawer, DrawerOverlay, DrawerContent, DrawerBody, useColorModeValue, useDisclosure } from '@chakra-ui/react'; import { Box, Flex, Drawer, useDisclosure } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { useColorModeValue } from 'chakra/components/color-mode';
import config from 'configs/app'; import config from 'configs/app';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import NavigationMobile from 'ui/snippets/navigation/mobile/NavigationMobile'; import NavigationMobile from 'ui/snippets/navigation/mobile/NavigationMobile';
...@@ -39,15 +40,15 @@ const Burger = ({ isMarketplaceAppPage }: Props) => { ...@@ -39,15 +40,15 @@ const Burger = ({ isMarketplaceAppPage }: Props) => {
aria-label="Menu button" aria-label="Menu button"
/> />
</Box> </Box>
<Drawer <Drawer.Root
isOpen={ isOpen } isOpen={ isOpen }
placement="left" placement="left"
onClose={ onClose } onClose={ onClose }
autoFocus={ false } autoFocus={ false }
> >
<DrawerOverlay/> <Drawer.Backdrop/>
<DrawerContent maxWidth="330px"> <Drawer.Content maxWidth="330px">
<DrawerBody p={ 6 } display="flex" flexDirection="column"> <Drawer.Body p={ 6 } display="flex" flexDirection="column">
<TestnetBadge alignSelf="flex-start"/> <TestnetBadge alignSelf="flex-start"/>
<Flex alignItems="center" justifyContent="space-between"> <Flex alignItems="center" justifyContent="space-between">
<NetworkLogo onClick={ handleNetworkLogoClick }/> <NetworkLogo onClick={ handleNetworkLogoClick }/>
...@@ -63,9 +64,9 @@ const Burger = ({ isMarketplaceAppPage }: Props) => { ...@@ -63,9 +64,9 @@ const Burger = ({ isMarketplaceAppPage }: Props) => {
<NetworkMenuContentMobile tabs={ networkMenu.availableTabs } items={ networkMenu.data }/> : <NetworkMenuContentMobile tabs={ networkMenu.availableTabs } items={ networkMenu.data }/> :
<NavigationMobile onNavLinkClick={ onClose } isMarketplaceAppPage={ isMarketplaceAppPage }/> <NavigationMobile onNavLinkClick={ onClose } isMarketplaceAppPage={ isMarketplaceAppPage }/>
} }
</DrawerBody> </Drawer.Body>
</DrawerContent> </Drawer.Content>
</Drawer> </Drawer.Root>
</> </>
); );
}; };
......
import { Box, Flex, Text, VStack, useColorModeValue } from '@chakra-ui/react'; import { Box, Flex, Text, VStack } from '@chakra-ui/react';
import { animate, motion, useMotionValue } from 'framer-motion'; // import { animate, motion, useMotionValue } from 'framer-motion';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import { useColorModeValue } from 'chakra/components/color-mode';
import useNavItems, { isGroupItem } from 'lib/hooks/useNavItems'; import useNavItems, { isGroupItem } from 'lib/hooks/useNavItems';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import useIsAuth from 'ui/snippets/auth/useIsAuth'; import useIsAuth from 'ui/snippets/auth/useIsAuth';
...@@ -10,7 +11,7 @@ import NavLink from '../vertical/NavLink'; ...@@ -10,7 +11,7 @@ import NavLink from '../vertical/NavLink';
import NavLinkRewards from '../vertical/NavLinkRewards'; import NavLinkRewards from '../vertical/NavLinkRewards';
import NavLinkGroup from './NavLinkGroup'; import NavLinkGroup from './NavLinkGroup';
const DRAWER_WIDTH = 330; // const DRAWER_WIDTH = 330;
interface Props { interface Props {
onNavLinkClick?: () => void; onNavLinkClick?: () => void;
...@@ -22,19 +23,20 @@ const NavigationMobile = ({ onNavLinkClick, isMarketplaceAppPage }: Props) => { ...@@ -22,19 +23,20 @@ const NavigationMobile = ({ onNavLinkClick, isMarketplaceAppPage }: Props) => {
const [ openedGroupIndex, setOpenedGroupIndex ] = React.useState(-1); const [ openedGroupIndex, setOpenedGroupIndex ] = React.useState(-1);
const mainX = useMotionValue(0); // const mainX = useMotionValue(0);
const subX = useMotionValue(DRAWER_WIDTH); // const subX = useMotionValue(DRAWER_WIDTH);
const onGroupItemOpen = (index: number) => () => { const onGroupItemOpen = (index: number) => () => {
setOpenedGroupIndex(index); setOpenedGroupIndex(index);
animate(mainX, -DRAWER_WIDTH, { ease: 'easeInOut' }); // TODO @tom2drum animate navigation group menu
animate(subX, 0, { ease: 'easeInOut' }); // animate(mainX, -DRAWER_WIDTH, { ease: 'easeInOut' });
// animate(subX, 0, { ease: 'easeInOut' });
}; };
const onGroupItemClose = useCallback(() => { const onGroupItemClose = useCallback(() => {
animate(mainX, 0, { ease: 'easeInOut' }); // animate(mainX, 0, { ease: 'easeInOut' });
animate(subX, DRAWER_WIDTH, { ease: 'easeInOut', onComplete: () => setOpenedGroupIndex(-1) }); // animate(subX, DRAWER_WIDTH, { ease: 'easeInOut', onComplete: () => setOpenedGroupIndex(-1) });
}, [ mainX, subX ]); }, []);
const isAuth = useIsAuth(); const isAuth = useIsAuth();
...@@ -50,8 +52,8 @@ const NavigationMobile = ({ onNavLinkClick, isMarketplaceAppPage }: Props) => { ...@@ -50,8 +52,8 @@ const NavigationMobile = ({ onNavLinkClick, isMarketplaceAppPage }: Props) => {
display="flex" display="flex"
flexDirection="column" flexDirection="column"
flexGrow={ 1 } flexGrow={ 1 }
as={ motion.div } // as={ motion.div }
style={{ x: mainX }} // style={{ x: mainX }}
maxHeight={ openedGroupIndex > -1 ? '100vh' : 'unset' } maxHeight={ openedGroupIndex > -1 ? '100vh' : 'unset' }
overflowY={ openedGroupIndex > -1 ? 'hidden' : 'unset' } overflowY={ openedGroupIndex > -1 ? 'hidden' : 'unset' }
> >
...@@ -91,12 +93,12 @@ const NavigationMobile = ({ onNavLinkClick, isMarketplaceAppPage }: Props) => { ...@@ -91,12 +93,12 @@ const NavigationMobile = ({ onNavLinkClick, isMarketplaceAppPage }: Props) => {
</Box> </Box>
{ openedGroupIndex >= 0 && ( { openedGroupIndex >= 0 && (
<Box <Box
as={ motion.nav } // as={ motion.nav }
w="100%" w="100%"
mt={ 6 } mt={ 6 }
position="absolute" position="absolute"
top={ 0 } top={ 0 }
style={{ x: subX }} // style={{ x: subX }}
key="sub" key="sub"
> >
<Flex alignItems="center" px={ 2 } py={ 2.5 } w="100%" h="50px" onClick={ onGroupItemClose } mb={ 1 }> <Flex alignItems="center" px={ 2 } py={ 2.5 } w="100%" h="50px" onClick={ onGroupItemClose } mb={ 1 }>
......
import { useColorModeValue, Button, forwardRef, chakra } from '@chakra-ui/react'; import { Button, chakra } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { useColorModeValue } from 'chakra/components/color-mode';
import getDefaultTransitionProps from 'theme/utils/getDefaultTransitionProps'; import getDefaultTransitionProps from 'theme/utils/getDefaultTransitionProps';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
...@@ -44,4 +45,4 @@ const NetworkMenuButton = ({ isMobile, isActive, onClick, className }: Props, re ...@@ -44,4 +45,4 @@ const NetworkMenuButton = ({ isMobile, isActive, onClick, className }: Props, re
); );
}; };
export default chakra(forwardRef(NetworkMenuButton)); export default chakra(React.forwardRef(NetworkMenuButton));
import { InputGroup, Input, InputLeftElement, chakra, useColorModeValue, forwardRef, InputRightElement, Center } from '@chakra-ui/react'; import { InputGroup, Input, InputLeftElement, chakra, InputRightElement, Center } from '@chakra-ui/react';
import throttle from 'lodash/throttle'; import throttle from 'lodash/throttle';
import React from 'react'; import React from 'react';
import type { ChangeEvent, FormEvent, FocusEvent } from 'react'; import type { ChangeEvent, FormEvent, FocusEvent } from 'react';
import { useColorModeValue } from 'chakra/components/color-mode';
import { useScrollDirection } from 'lib/contexts/scrollDirection'; import { useScrollDirection } from 'lib/contexts/scrollDirection';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import ClearButton from 'ui/shared/ClearButton'; import ClearButton from 'ui/shared/ClearButton';
...@@ -179,4 +180,4 @@ const SearchBarInput = ( ...@@ -179,4 +180,4 @@ const SearchBarInput = (
); );
}; };
export default React.memo(forwardRef(SearchBarInput)); export default React.memo(React.forwardRef(SearchBarInput));
import { Flex, Divider, useColorModeValue, Box } from '@chakra-ui/react'; import { Flex, Divider, Box } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { useColorModeValue } from 'chakra/components/color-mode';
import config from 'configs/app'; import config from 'configs/app';
import { CONTENT_MAX_WIDTH } from 'ui/shared/layout/utils'; import { CONTENT_MAX_WIDTH } from 'ui/shared/layout/utils';
......
This diff is collapsed.
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