mirror of
https://github.com/valitydev/checkout.git
synced 2024-11-06 02:25:18 +00:00
IMP-198: Move layout loader to index.html (#294)
This commit is contained in:
parent
1efe8e7b37
commit
39d97e6d63
@ -15,8 +15,37 @@
|
||||
/>
|
||||
<title>Checkout</title>
|
||||
</head>
|
||||
<body>
|
||||
<body style="background: #163735">
|
||||
<div id="spinner" style="position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%)">
|
||||
<svg
|
||||
height="64px"
|
||||
viewBox="0 0 57 57"
|
||||
width="64px"
|
||||
style="position: relative; animation: spin 1s linear infinite"
|
||||
>
|
||||
<defs>
|
||||
<linearGradient id="loaderGradient" x1="100%" x2="0%" y1="0%" y2="100%">
|
||||
<stop offset="0%" stop-color="#ff7808" />
|
||||
<stop offset="100%" stop-color="#FFDB57" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g fill="none" fill-rule="evenodd" stroke="none" stroke-width="1">
|
||||
<g stroke="url(#loaderGradient)" stroke-width="4" transform="translate(-655.000000, -383.000000)">
|
||||
<circle cx="683.5" cy="411.5" r="26.5" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div id="app"></div>
|
||||
<script defer type="module" src="/src/main.tsx"></script>
|
||||
|
||||
<style>
|
||||
@keyframes spin {
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
</html>
|
||||
|
43
src/App.tsx
43
src/App.tsx
@ -1,8 +1,9 @@
|
||||
import { ChakraBaseProvider, extendBaseTheme, theme as chakraTheme } from '@chakra-ui/react';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { CompletePaymentContext } from './common/contexts';
|
||||
import { isNil } from './common/utils';
|
||||
import { CompletePaymentContext } from 'checkout/contexts';
|
||||
import { isNil } from 'checkout/utils';
|
||||
|
||||
import { CommunicatorEvents } from './communicator';
|
||||
import { InitializationFailed, AppLayout } from './components';
|
||||
import { useInitialize } from './useInitialize';
|
||||
@ -37,28 +38,26 @@ export function App() {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ChakraBaseProvider theme={theme}>
|
||||
{state.status === 'SUCCESS' && (
|
||||
<ChakraBaseProvider theme={theme}>
|
||||
<CompletePaymentContext.Provider
|
||||
value={{
|
||||
onComplete: () =>
|
||||
setTimeout(() => {
|
||||
const [transport, params] = state.data;
|
||||
transport.emit(CommunicatorEvents.finished);
|
||||
transport.destroy();
|
||||
const redirectUrl = params.initConfig?.redirectUrl;
|
||||
if (!isNil(redirectUrl)) {
|
||||
window.open(redirectUrl, '_self');
|
||||
}
|
||||
}, ON_COMPLETE_TIMEOUT_MS),
|
||||
}}
|
||||
>
|
||||
<AppLayout initParams={state.data[1]} />
|
||||
</CompletePaymentContext.Provider>
|
||||
</ChakraBaseProvider>
|
||||
<CompletePaymentContext.Provider
|
||||
value={{
|
||||
onComplete: () =>
|
||||
setTimeout(() => {
|
||||
const [transport, params] = state.data;
|
||||
transport.emit(CommunicatorEvents.finished);
|
||||
transport.destroy();
|
||||
const redirectUrl = params.initConfig?.redirectUrl;
|
||||
if (!isNil(redirectUrl)) {
|
||||
window.open(redirectUrl, '_self');
|
||||
}
|
||||
}, ON_COMPLETE_TIMEOUT_MS),
|
||||
}}
|
||||
>
|
||||
<AppLayout initParams={state.data[1]} />
|
||||
</CompletePaymentContext.Provider>
|
||||
)}
|
||||
{state.status === 'FAILURE' && <InitializationFailed error={state.error} />}
|
||||
</>
|
||||
</ChakraBaseProvider>
|
||||
);
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import { toCustomizationContext } from './utils';
|
||||
import { CustomizationContext } from '../../common/contexts';
|
||||
import { InitParams } from '../../common/init';
|
||||
import { getTheme } from '../../common/theme';
|
||||
import { LayoutLoader, Overlay, ErrorBoundaryFallback } from '../legacy';
|
||||
import { ErrorBoundaryFallback } from '../legacy';
|
||||
|
||||
type AppLayoutProps = {
|
||||
initParams: InitParams;
|
||||
@ -44,9 +44,7 @@ export function AppLayout({ initParams }: AppLayoutProps) {
|
||||
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
<Overlay />
|
||||
<ModalContainer>
|
||||
{modelsState.status === 'PROCESSING' && <LayoutLoader />}
|
||||
{modelsState.status === 'INITIALIZED' && (
|
||||
<CustomizationContext.Provider value={toCustomizationContext(initParams.initConfig)}>
|
||||
<ErrorBoundary fallback={<ErrorBoundaryFallback />}>
|
||||
|
@ -1,18 +1,9 @@
|
||||
/* eslint-disable react/jsx-max-depth */
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import {
|
||||
Locale,
|
||||
LocaleContext,
|
||||
PaymentConditionsContext,
|
||||
PaymentContext,
|
||||
PaymentModelContext,
|
||||
} from 'checkout/contexts';
|
||||
import { PaymentConditionsContext, PaymentContext, PaymentModelContext } from 'checkout/contexts';
|
||||
import { PaymentCondition } from 'checkout/paymentCondition';
|
||||
import { PaymentModel } from 'checkout/paymentModel';
|
||||
|
||||
import { LocaleSelector } from './LocaleSelector';
|
||||
import { usePaymentCondition } from './usePaymentCondition';
|
||||
import { toContainer } from './utils';
|
||||
import { RedirectContainer } from '../RedirectContainer';
|
||||
@ -26,21 +17,15 @@ export type GlobalContainerProps = {
|
||||
export function GlobalContainer({ paymentModel, initConditions }: GlobalContainerProps) {
|
||||
const { conditions, startPayment, startWaitingPaymentResult } = usePaymentCondition(paymentModel, initConditions);
|
||||
const containerName = toContainer(conditions);
|
||||
const [locale, setLocale] = useState<{ l: Locale; localeCode: string }>({ l: {}, localeCode: 'en' });
|
||||
|
||||
return (
|
||||
<PaymentModelContext.Provider value={{ paymentModel }}>
|
||||
<PaymentConditionsContext.Provider value={{ conditions }}>
|
||||
<PaymentContext.Provider value={{ startPayment, startWaitingPaymentResult }}>
|
||||
<LocaleContext.Provider value={locale}>
|
||||
<Box marginBottom={16} marginTop={16} width={['full', '75%', 'auto']}>
|
||||
{containerName === 'ViewContainer' && <ViewContainer />}
|
||||
{containerName === 'RedirectContainer' && <RedirectContainer />}
|
||||
<Box paddingLeft="5" paddingRight="5" paddingTop="3">
|
||||
<LocaleSelector onLocaleChange={setLocale} />
|
||||
</Box>
|
||||
</Box>
|
||||
</LocaleContext.Provider>
|
||||
<Box marginBottom={16} marginTop={16} width={['full', '75%', 'auto']}>
|
||||
{containerName === 'ViewContainer' && <ViewContainer />}
|
||||
{containerName === 'RedirectContainer' && <RedirectContainer />}
|
||||
</Box>
|
||||
</PaymentContext.Provider>
|
||||
</PaymentConditionsContext.Provider>
|
||||
</PaymentModelContext.Provider>
|
||||
|
@ -1,4 +1,6 @@
|
||||
import { extractError } from '../common/utils';
|
||||
import { Alert, AlertDescription, AlertIcon, AlertTitle } from '@chakra-ui/react';
|
||||
|
||||
import { extractError } from 'checkout/utils';
|
||||
|
||||
export type InitializationFailedProps = {
|
||||
error: unknown;
|
||||
@ -6,5 +8,21 @@ export type InitializationFailedProps = {
|
||||
|
||||
export function InitializationFailed({ error }: InitializationFailedProps) {
|
||||
console.error(`Application Initialization Failed: ${extractError(error)}`, error);
|
||||
return <p>Application Initialization Failed: Please check the console for more details.</p>;
|
||||
return (
|
||||
<Alert
|
||||
alignItems="center"
|
||||
flexDirection="column"
|
||||
height="200px"
|
||||
justifyContent="center"
|
||||
status="error"
|
||||
textAlign="center"
|
||||
variant="subtle"
|
||||
>
|
||||
<AlertIcon boxSize="40px" mr={0} />
|
||||
<AlertTitle fontSize="lg" mb={1} mt={4}>
|
||||
Application Initialization Failed!
|
||||
</AlertTitle>
|
||||
<AlertDescription maxWidth="sm">{extractError(error)}</AlertDescription>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ import styled from 'styled-components';
|
||||
|
||||
import { prepareForm } from './utils';
|
||||
import { BrowserRequest } from '../../common/backend/payments';
|
||||
import { LayoutLoader } from '../legacy';
|
||||
|
||||
const RedirectContainer = styled.div`
|
||||
visibility: hidden;
|
||||
@ -29,10 +28,5 @@ export function SelfRedirectContainer({ origin, request }: SelfRedirectContainer
|
||||
form && form.submit();
|
||||
}, [form]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<LayoutLoader />
|
||||
<RedirectContainer ref={containerRef} />
|
||||
</>
|
||||
);
|
||||
return <RedirectContainer ref={containerRef} />;
|
||||
}
|
||||
|
@ -1,12 +1,21 @@
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import { useContext, useMemo } from 'react';
|
||||
/* eslint-disable react/jsx-max-depth */
|
||||
|
||||
import { LocaleContext, PaymentConditionsContext, PaymentModelContext, ViewModelContext } from 'checkout/contexts';
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import { useContext, useMemo, useState } from 'react';
|
||||
|
||||
import {
|
||||
Locale,
|
||||
LocaleContext,
|
||||
PaymentConditionsContext,
|
||||
PaymentModelContext,
|
||||
ViewModelContext,
|
||||
} from 'checkout/contexts';
|
||||
import { formatAmount } from 'checkout/utils';
|
||||
|
||||
import { ApiExtensionView } from './ApiExtensionView';
|
||||
import { InfoContainer } from './InfoContainer';
|
||||
import { Loader } from './Loader';
|
||||
import { LocaleSelector } from './LocaleSelector';
|
||||
import { NoAvailablePaymentMethodsView } from './NoAvailablePaymentMethodsView';
|
||||
import { PaymentFormView } from './PaymentFormView';
|
||||
import { PaymentMethodSelectorView } from './PaymentMethodSelectorView';
|
||||
@ -17,48 +26,54 @@ import { TerminalSelectorView } from './TerminalSelectorView';
|
||||
import { useViewModel } from './useViewModel';
|
||||
|
||||
export function ViewContainer() {
|
||||
const { localeCode } = useContext(LocaleContext);
|
||||
const { conditions } = useContext(PaymentConditionsContext);
|
||||
const {
|
||||
paymentModel: { paymentAmount, paymentMethods },
|
||||
} = useContext(PaymentModelContext);
|
||||
const { viewModel, goTo, forward, backward } = useViewModel(paymentMethods, conditions);
|
||||
|
||||
const viewAmount = useMemo(() => formatAmount(paymentAmount, localeCode), [localeCode]);
|
||||
const [locale, setLocale] = useState<{ l: Locale; localeCode: string }>({ l: {}, localeCode: 'en' });
|
||||
const viewAmount = useMemo(() => formatAmount(paymentAmount, locale.localeCode), [locale.localeCode]);
|
||||
const { views, activeViewId, isLoading } = viewModel;
|
||||
const activeView = views.get(activeViewId).name;
|
||||
|
||||
return (
|
||||
<Flex
|
||||
alignItems="stretch"
|
||||
background="gray.50"
|
||||
borderRadius="xl"
|
||||
direction={['column', 'column', 'row']}
|
||||
gap={4}
|
||||
p={[4, 4, 8]}
|
||||
>
|
||||
<InfoContainer viewAmount={viewAmount}></InfoContainer>
|
||||
<ViewModelContext.Provider value={{ viewModel, viewAmount, goTo, forward, backward }}>
|
||||
<Box
|
||||
background="white"
|
||||
border="1px solid"
|
||||
borderColor="gray.200"
|
||||
<>
|
||||
<LocaleContext.Provider value={locale}>
|
||||
<Flex
|
||||
alignItems="stretch"
|
||||
background="gray.50"
|
||||
borderRadius="xl"
|
||||
padding={[4, 4, 6]}
|
||||
position="relative"
|
||||
width={['full', 'full', '420px']}
|
||||
direction={['column', 'column', 'row']}
|
||||
gap={4}
|
||||
p={[4, 4, 8]}
|
||||
>
|
||||
{activeView === 'NoAvailablePaymentMethodsView' && <NoAvailablePaymentMethodsView />}
|
||||
{activeView === 'PaymentMethodSelectorView' && <PaymentMethodSelectorView />}
|
||||
{activeView === 'TerminalSelectorView' && <TerminalSelectorView />}
|
||||
{activeView === 'PaymentFormView' && <PaymentFormView />}
|
||||
{activeView === 'PaymentResultView' && <PaymentResultView />}
|
||||
{activeView === 'QrCodeView' && <QrCodeView />}
|
||||
{activeView === 'ApiExtensionView' && <ApiExtensionView />}
|
||||
{activeView === 'PaymentProcessFailedView' && <PaymentProcessFailedView />}
|
||||
{isLoading && <Loader />}
|
||||
</Box>
|
||||
</ViewModelContext.Provider>
|
||||
</Flex>
|
||||
<InfoContainer viewAmount={viewAmount}></InfoContainer>
|
||||
<ViewModelContext.Provider value={{ viewModel, viewAmount, goTo, forward, backward }}>
|
||||
<Box
|
||||
background="white"
|
||||
border="1px solid"
|
||||
borderColor="gray.200"
|
||||
borderRadius="xl"
|
||||
padding={[4, 4, 6]}
|
||||
position="relative"
|
||||
width={['full', 'full', '420px']}
|
||||
>
|
||||
{activeView === 'NoAvailablePaymentMethodsView' && <NoAvailablePaymentMethodsView />}
|
||||
{activeView === 'PaymentMethodSelectorView' && <PaymentMethodSelectorView />}
|
||||
{activeView === 'TerminalSelectorView' && <TerminalSelectorView />}
|
||||
{activeView === 'PaymentFormView' && <PaymentFormView />}
|
||||
{activeView === 'PaymentResultView' && <PaymentResultView />}
|
||||
{activeView === 'QrCodeView' && <QrCodeView />}
|
||||
{activeView === 'ApiExtensionView' && <ApiExtensionView />}
|
||||
{activeView === 'PaymentProcessFailedView' && <PaymentProcessFailedView />}
|
||||
{isLoading && <Loader />}
|
||||
</Box>
|
||||
</ViewModelContext.Provider>
|
||||
</Flex>
|
||||
</LocaleContext.Provider>
|
||||
<Box paddingLeft="5" paddingRight="5" paddingTop="3">
|
||||
<LocaleSelector onLocaleChange={setLocale} />
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,41 +0,0 @@
|
||||
import styled, { keyframes } from 'styled-components';
|
||||
|
||||
import { Loader } from './Loader';
|
||||
import { device } from '../../common/utils';
|
||||
|
||||
const growth = keyframes`
|
||||
from {
|
||||
transform: scale(0);
|
||||
}
|
||||
to {
|
||||
transform: scale(1);
|
||||
}
|
||||
`;
|
||||
|
||||
const LayoutLoaderWrapper = styled.div`
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
@media ${device.desktop} {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transform: translate(0, 0);
|
||||
animation: ${growth} 0.5s;
|
||||
}
|
||||
`;
|
||||
|
||||
export const LayoutLoader = () => (
|
||||
<LayoutLoaderWrapper>
|
||||
<Loader />
|
||||
</LayoutLoaderWrapper>
|
||||
);
|
@ -1,17 +0,0 @@
|
||||
import styled, { css } from 'styled-components';
|
||||
|
||||
const OverlayBg = styled.div`
|
||||
position: fixed;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
|
||||
${({ theme }) => {
|
||||
return css`
|
||||
background: ${theme.background.gradient};
|
||||
`;
|
||||
}}
|
||||
`;
|
||||
|
||||
export const Overlay = () => <OverlayBg key="overlay" />;
|
@ -1,5 +1,3 @@
|
||||
export { LayoutLoader } from './LayoutLoader';
|
||||
export { Overlay } from './Overlay';
|
||||
export { PayButton } from './PayButton';
|
||||
export { HeaderWrapper } from './HeaderWrapper';
|
||||
export { Title } from './Title';
|
||||
|
Loading…
Reference in New Issue
Block a user