Commit b11e70b2 authored by tom's avatar tom

Merge branch 'main' of github.com:blockscout/frontend into block-sockets

parents 08adf970 4d8d344d
......@@ -65,7 +65,6 @@ jobs:
needs: push_to_registry
uses: blockscout/blockscout-ci-cd/.github/workflows/e2e_new.yaml@master
with:
env_vars: VALUES_DIR=deploy/values/e2e,APP_NAME=e2e-front
appNamespace: e2e-front-$GITHUB_SHA_SHORT
frontendImage: ghcr.io/blockscout/frontend:prerelease-${{ needs.push_to_registry.outputs.shortSha }}
blockscoutIngressHost: e2e-blockscout-$GITHUB_SHA_SHORT
......
---
creation_rules:
- path_regex: ^(.+/)?secrets\.yaml$
pgp: >-
99E83B7490B1A9F51781E6055317CE0D5CE1230B
This diff is collapsed.
This diff is collapsed.
import {
ChakraProvider,
cookieStorageManagerSSR,
localStorageManager,
} from '@chakra-ui/react';
import type { ChakraProviderProps } from '@chakra-ui/react';
import React from 'react';
interface Props extends ChakraProviderProps {
cookies?: string;
}
export function Chakra({ cookies, theme, children }: Props) {
const colorModeManager =
typeof cookies === 'string' ?
cookieStorageManagerSSR(cookies) :
localStorageManager;
return (
<ChakraProvider colorModeManager={ colorModeManager } theme={ theme }>
{ children }
</ChakraProvider>
);
}
import React, { createContext, useContext } from 'react';
import type { Props as PageProps } from 'lib/next/getServerSideProps';
type Props = {
children: React.ReactNode;
pageProps: PageProps;
}
const AppContext = createContext<PageProps>({ cookies: '' });
export function AppWrapper({ children, pageProps }: Props) {
const appProps = { cookies: pageProps.cookies };
return (
<AppContext.Provider value={ appProps }>
{ children }
</AppContext.Provider>
);
}
export function useAppContext() {
return useContext(AppContext);
}
......@@ -9,9 +9,9 @@ export enum NAMES {
TXS_SORT='txs_sort',
}
export function get(name?: string | undefined | null) {
export function get(name?: NAMES | undefined | null, serverCookie?: string) {
if (!isBrowser()) {
return undefined;
return serverCookie ? getFromCookieString(serverCookie, name) : undefined;
}
return Cookies.get(name);
}
......@@ -21,3 +21,7 @@ export function set(name: string, value: string, attributes: Types.CookieAttribu
return Cookies.set(name, value, attributes);
}
export function getFromCookieString(cookieString: string, name?: NAMES | undefined | null) {
return cookieString.split(`${ name }=`)[1]?.split(';')[0];
}
import type { GetServerSideProps, GetServerSidePropsResult } from 'next';
export type Props = {
cookies: string;
}
export const getServerSideProps: GetServerSideProps = async({ req }): Promise<GetServerSidePropsResult<Props>> => {
return {
props: {
cookies: req.headers.cookie || '',
},
};
};
import type { GetServerSideProps } from 'next';
export const getServerSideProps: GetServerSideProps = async({ res }) => {
res.setHeader(
'Cache-Control',
`public, s-maxage=${ 60 * 60 }, stale-while-revalidate=${ 2 * 60 * 60 }`,
);
return { props: {} };
};
import type { GetStaticPaths } from 'next';
export const getStaticPaths: GetStaticPaths = async() => {
return { paths: [], fallback: 'blocking' };
};
import type { GetStaticProps, GetStaticPropsResult } from 'next';
export const getStaticProps: GetStaticProps = async(): Promise<GetStaticPropsResult<{ [key: string]: unknown }>> => {
return {
props: {},
};
};
import { ChakraProvider } from '@chakra-ui/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import type { AppProps } from 'next/app';
import React, { useState } from 'react';
import { AppWrapper } from 'lib/appContext';
import { Chakra } from 'lib/Chakra';
import useConfigSentry from 'lib/hooks/useConfigSentry';
import type { ErrorType } from 'lib/hooks/useFetch';
import theme from 'theme';
......@@ -30,12 +31,14 @@ function MyApp({ Component, pageProps }: AppProps) {
}));
return (
<QueryClientProvider client={ queryClient }>
<ChakraProvider theme={ theme }>
<Component { ...pageProps }/>
</ChakraProvider>
<ReactQueryDevtools/>
</QueryClientProvider>
<AppWrapper pageProps={ pageProps }>
<QueryClientProvider client={ queryClient }>
<Chakra theme={ theme } cookies={ pageProps.cookies }>
<Component { ...pageProps }/>
</Chakra>
<ReactQueryDevtools/>
</QueryClientProvider>
</AppWrapper>
);
}
......
......@@ -17,4 +17,4 @@ const ApiKeysPage: NextPage = () => {
export default ApiKeysPage;
export { getServerSideProps } from 'lib/next/getServerSidePropsDummy';
export { getServerSideProps } from 'lib/next/getServerSideProps';
......@@ -17,4 +17,4 @@ const CustomAbiPage: NextPage = () => {
export default CustomAbiPage;
export { getServerSideProps } from 'lib/next/getServerSidePropsDummy';
export { getServerSideProps } from 'lib/next/getServerSideProps';
......@@ -17,4 +17,4 @@ const PublicTagsPage: NextPage = () => {
export default PublicTagsPage;
export { getServerSideProps } from 'lib/next/getServerSidePropsDummy';
export { getServerSideProps } from 'lib/next/getServerSideProps';
......@@ -17,4 +17,4 @@ const AddressTagsPage: NextPage = () => {
export default AddressTagsPage;
export { getServerSideProps } from 'lib/next/getServerSidePropsDummy';
export { getServerSideProps } from 'lib/next/getServerSideProps';
......@@ -19,4 +19,4 @@ const WatchListPage: NextPage = () => {
export default WatchListPage;
export { getServerSideProps } from 'lib/next/getServerSidePropsDummy';
export { getServerSideProps } from 'lib/next/getServerSideProps';
......@@ -19,4 +19,4 @@ const AppsPage: NextPage = () => {
export default AppsPage;
export { getServerSideProps } from 'lib/next/getServerSidePropsDummy';
export { getServerSideProps } from 'lib/next/getServerSideProps';
......@@ -47,5 +47,4 @@ const AppPage: NextPage = () => {
export default AppPage;
export { getStaticPaths } from 'lib/next/getStaticPaths';
export { getStaticProps } from 'lib/next/getStaticProps';
export { getServerSideProps } from 'lib/next/getServerSideProps';
......@@ -15,4 +15,4 @@ const MyProfilePage: NextPage = () => {
export default MyProfilePage;
export { getServerSideProps } from 'lib/next/getServerSidePropsDummy';
export { getServerSideProps } from 'lib/next/getServerSideProps';
......@@ -17,5 +17,4 @@ const BlockPage: NextPage<Props> = ({ pageParams }: Props) => {
export default BlockPage;
export { getStaticPaths } from 'lib/next/getStaticPaths';
export { getStaticProps } from 'lib/next/getStaticProps';
export { getServerSideProps } from 'lib/next/getServerSideProps';
......@@ -11,4 +11,4 @@ const BlockPage: NextPage = () => {
export default BlockPage;
export { getServerSideProps } from 'lib/next/getServerSidePropsDummy';
export { getServerSideProps } from 'lib/next/getServerSideProps';
......@@ -15,4 +15,4 @@ const HomePage: NextPage = () => {
export default HomePage;
export { getServerSideProps } from 'lib/next/getServerSidePropsDummy';
export { getServerSideProps } from 'lib/next/getServerSideProps';
......@@ -17,5 +17,4 @@ const TransactionPage: NextPage<Props> = ({ pageParams }: Props) => {
export default TransactionPage;
export { getStaticPaths } from 'lib/next/getStaticPaths';
export { getStaticProps } from 'lib/next/getStaticProps';
export { getServerSideProps } from 'lib/next/getServerSideProps';
......@@ -17,4 +17,4 @@ const TxsPage: NextPage = () => {
export default TxsPage;
export { getServerSideProps } from 'lib/next/getServerSidePropsDummy';
export { getServerSideProps } from 'lib/next/getServerSideProps';
......@@ -9,7 +9,7 @@ import PageTitle from 'ui/shared/Page/PageTitle';
import UserAvatar from 'ui/shared/UserAvatar';
const MyProfile = () => {
const { data, isLoading, isError } = useFetchProfileInfo();
const { data, isLoading, isError, isFetched } = useFetchProfileInfo();
const content = (() => {
if (isLoading) {
......@@ -22,7 +22,7 @@ const MyProfile = () => {
return (
<VStack maxW="412px" mt={ 12 } gap={ 5 } alignItems="stretch">
<UserAvatar size={ 64 } data={ data }/>
<UserAvatar size={ 64 } data={ data } isFetched={ isFetched }/>
<FormControl variant="floating" id="name" isRequired size="lg">
<Input
required
......
......@@ -20,7 +20,11 @@ interface Props {
hideMobileHeaderOnScrollDown?: boolean;
}
const Page = ({ children, wrapChildren = true, hideMobileHeaderOnScrollDown }: Props) => {
const Page = ({
children,
wrapChildren = true,
hideMobileHeaderOnScrollDown,
}: Props) => {
const fetch = useFetch();
useQuery<unknown, unknown, unknown>([ QueryKeys.csrf ], async() => await fetch('/node-api/account/csrf'), {
......
import { useColorModeValue, chakra, Image } from '@chakra-ui/react';
import { useColorModeValue, chakra, SkeletonCircle, Image } from '@chakra-ui/react';
import React from 'react';
import Identicon from 'react-identicons';
import type { UserInfo } from 'types/api/account';
import { useAppContext } from 'lib/appContext';
import * as cookies from 'lib/cookies';
const ProfileIcon = chakra(Identicon);
interface Props {
size: number;
data?: UserInfo;
isFetched: boolean;
}
const UserAvatar = ({ size, data }: Props) => {
const UserAvatar = ({ size, data, isFetched }: Props) => {
const appProps = useAppContext();
const hasAuth = Boolean(cookies.get(cookies.NAMES.API_TOKEN, appProps.cookies));
const sizeString = `${ size }px`;
const bgColor = useColorModeValue('blackAlpha.100', 'white');
if (hasAuth && !isFetched) {
return <SkeletonCircle h={ sizeString } w={ sizeString }/>;
}
if (data?.avatar) {
return (
<Image
flexShrink={ 0 }
src={ data.avatar }
alt={ `Profile picture of ${ data.name || data.nickname || '' }` }
alt={ `Profile picture of ${ data?.name || data?.nickname || '' }` }
w={ sizeString }
minW={ sizeString }
h={ sizeString }
......
......@@ -3,9 +3,9 @@ import React from 'react';
import appConfig from 'configs/app/config';
import chevronIcon from 'icons/arrows/east-mini.svg';
import { useAppContext } from 'lib/appContext';
import * as cookies from 'lib/cookies';
import useNavItems from 'lib/hooks/useNavItems';
import isBrowser from 'lib/isBrowser';
import getDefaultTransitionProps from 'theme/utils/getDefaultTransitionProps';
import NetworkLogo from 'ui/snippets/networkMenu/NetworkLogo';
import NetworkMenu from 'ui/snippets/networkMenu/NetworkMenu';
......@@ -14,25 +14,24 @@ import NavFooter from './NavFooter';
import NavLink from './NavLink';
const NavigationDesktop = () => {
const { mainNavItems, accountNavItems } = useNavItems();
const appProps = useAppContext();
const cookiesString = appProps.cookies;
const isNavBarCollapsedCookie = cookies.get(cookies.NAMES.NAV_BAR_COLLAPSED, cookiesString);
let isNavBarCollapsed;
if (isNavBarCollapsedCookie === 'true') {
isNavBarCollapsed = true;
}
if (isNavBarCollapsedCookie === 'false') {
isNavBarCollapsed = false;
}
const isInBrowser = isBrowser();
const [ hasAccount, setHasAccount ] = React.useState(false);
const [ isCollapsed, setCollapsedState ] = React.useState<boolean | undefined>();
const hasAuth = Boolean(cookies.get(cookies.NAMES.API_TOKEN, cookiesString));
const { mainNavItems, accountNavItems } = useNavItems();
React.useEffect(() => {
const navBarCollapsedCookie = cookies.get(cookies.NAMES.NAV_BAR_COLLAPSED);
const isAuth = Boolean(cookies.get(cookies.NAMES.API_TOKEN));
if (isInBrowser) {
if (navBarCollapsedCookie === 'true') {
setCollapsedState(true);
}
if (navBarCollapsedCookie === 'false') {
setCollapsedState(false);
}
setHasAccount(Boolean(appConfig.isAccountSupported && isAuth && isInBrowser));
}
}, [ isInBrowser ]);
const hasAccount = hasAuth && appConfig.isAccountSupported;
const [ isCollapsed, setCollapsedState ] = React.useState<boolean | undefined>(isNavBarCollapsed);
const handleTogglerClick = React.useCallback(() => {
setCollapsedState((flag) => !flag);
......
......@@ -7,7 +7,7 @@ import UserAvatar from 'ui/shared/UserAvatar';
import ProfileMenuContent from 'ui/snippets/profileMenu/ProfileMenuContent';
const ProfileMenuDesktop = () => {
const { data } = useFetchProfileInfo();
const { data, isFetched } = useFetchProfileInfo();
const loginUrl = link('auth');
return (
......@@ -21,7 +21,7 @@ const ProfileMenuDesktop = () => {
as={ data ? undefined : 'a' }
href={ data ? undefined : loginUrl }
>
<UserAvatar size={ 50 } data={ data }/>
<UserAvatar size={ 50 } data={ data } isFetched={ isFetched }/>
</Button>
</PopoverTrigger>
{ data && (
......
......@@ -10,13 +10,13 @@ import ProfileMenuContent from 'ui/snippets/profileMenu/ProfileMenuContent';
const ProfileMenuMobile = () => {
const { isOpen, onOpen, onClose } = useDisclosure();
const { data } = useFetchProfileInfo();
const { data, isFetched } = useFetchProfileInfo();
const loginUrl = link('auth');
return (
<>
<Box padding={ 2 } onClick={ onOpen }>
<UserAvatar size={ 24 } data={ data }/>
<UserAvatar size={ 24 } data={ data } isFetched={ isFetched }/>
</Box>
<Drawer
isOpen={ isOpen }
......@@ -34,7 +34,7 @@ const ProfileMenuMobile = () => {
>
<ColorModeToggler/>
<Box onClick={ onClose }>
<UserAvatar size={ 24 } data={ data }/>
<UserAvatar size={ 24 } data={ data } isFetched={ isFetched }/>
</Box>
</Flex>
{ data ? <ProfileMenuContent { ...data }/> : (
......
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