From 8a21f183f998583dde69bc7912ee94c61007f087 Mon Sep 17 00:00:00 2001 From: pierre Date: Mon, 12 Sep 2022 16:20:51 +0200 Subject: [PATCH] feat(wallet): buy page bootstrap --- .../components/Buy/DeclinedModal.stories.tsx | 12 +++ .../src/components/Buy/DeclinedModal.tsx | 15 ++++ .../Buy/SignMessageModal.stories.tsx | 35 +++++++++ .../src/components/Buy/SignMessageModal.tsx | 77 +++++++++++++++++++ .../src/components/Buy/Tutorial.stories.tsx | 10 +++ nym-wallet/src/components/Buy/Tutorial.tsx | 24 ++++++ nym-wallet/src/components/CopyToClipboard.tsx | 2 + .../components/Modals/SimpleModal.stories.tsx | 33 ++++++++ .../src/components/Modals/SimpleModal.tsx | 6 +- nym-wallet/src/components/Nav.tsx | 6 ++ .../src/components/StyledBackButton.tsx | 16 +++- nym-wallet/src/context/buy.tsx | 58 ++++++++++++++ nym-wallet/src/context/index.tsx | 1 + nym-wallet/src/context/mocks/buy.tsx | 35 +++++++++ nym-wallet/src/pages/buy/index.tsx | 9 +++ nym-wallet/src/pages/index.ts | 1 + nym-wallet/src/routes/app.tsx | 3 +- 17 files changed, 338 insertions(+), 5 deletions(-) create mode 100644 nym-wallet/src/components/Buy/DeclinedModal.stories.tsx create mode 100644 nym-wallet/src/components/Buy/DeclinedModal.tsx create mode 100644 nym-wallet/src/components/Buy/SignMessageModal.stories.tsx create mode 100644 nym-wallet/src/components/Buy/SignMessageModal.tsx create mode 100644 nym-wallet/src/components/Buy/Tutorial.stories.tsx create mode 100644 nym-wallet/src/components/Buy/Tutorial.tsx create mode 100644 nym-wallet/src/context/buy.tsx create mode 100644 nym-wallet/src/context/mocks/buy.tsx create mode 100644 nym-wallet/src/pages/buy/index.tsx diff --git a/nym-wallet/src/components/Buy/DeclinedModal.stories.tsx b/nym-wallet/src/components/Buy/DeclinedModal.stories.tsx new file mode 100644 index 0000000000..c1dbf62dbf --- /dev/null +++ b/nym-wallet/src/components/Buy/DeclinedModal.stories.tsx @@ -0,0 +1,12 @@ +import React from 'react'; + +import { DeclinedModal } from './DeclinedModal'; + +export default { + title: 'Buy/DeclinedModal', + component: DeclinedModal, +}; + +export const Terms = () => ( + console.log('user has accepted')} onClose={async () => console.log('closed')} /> +); diff --git a/nym-wallet/src/components/Buy/DeclinedModal.tsx b/nym-wallet/src/components/Buy/DeclinedModal.tsx new file mode 100644 index 0000000000..13080859d5 --- /dev/null +++ b/nym-wallet/src/components/Buy/DeclinedModal.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import { Typography } from '@mui/material'; +import { ConfirmationModal } from '../Modals/ConfirmationModal'; + +export const DeclinedModal = ({ onOk, onClose }: { onOk: () => Promise; onClose: () => void }) => ( + + Can’t procced to buy tokens without acceptance + +); diff --git a/nym-wallet/src/components/Buy/SignMessageModal.stories.tsx b/nym-wallet/src/components/Buy/SignMessageModal.stories.tsx new file mode 100644 index 0000000000..201192ac16 --- /dev/null +++ b/nym-wallet/src/components/Buy/SignMessageModal.stories.tsx @@ -0,0 +1,35 @@ +import React, { useState } from 'react'; +import { Button } from '@mui/material'; +import { MockBuyContextProvider } from 'src/context/mocks/buy'; + +import { SignMessageModal } from './SignMessageModal'; + +export default { + title: 'Buy/SignMessage', + component: SignMessageModal, +}; + +export const SignMessage = () => { + const [open, setOpen] = useState(false); + + return ( + + + {open && ( + { + setOpen(false); + }} + /> + )} + + ); +}; diff --git a/nym-wallet/src/components/Buy/SignMessageModal.tsx b/nym-wallet/src/components/Buy/SignMessageModal.tsx new file mode 100644 index 0000000000..eb47a4b7d1 --- /dev/null +++ b/nym-wallet/src/components/Buy/SignMessageModal.tsx @@ -0,0 +1,77 @@ +import * as React from 'react'; +import { useState } from 'react'; +import { Stack, TextField, Typography } from '@mui/material'; +import { useBuyContext } from 'src/context'; +import { SimpleModal } from '../Modals/SimpleModal'; +import { ErrorModal } from '../Modals/ErrorModal'; +import { CopyToClipboard } from '../CopyToClipboard'; + +export const SignMessageModal = ({ onClose }: { onClose: () => void }) => { + const [message, setMessage] = useState(); + const [signature, setSignature] = useState(); + + const { signMessage, loading, refresh, error } = useBuyContext(); + + const handleSign = async () => { + if (!message) { + return; + } + signMessage(message).then((sig) => { + setSignature(sig); + }); + }; + + const handleChange = (event: React.ChangeEvent) => { + setMessage(event.target.value); + }; + + if (error) { + { + refresh(); + }} + />; + } + + return ( + + + + + + + + Copy signature + + + + + + ); +}; diff --git a/nym-wallet/src/components/Buy/Tutorial.stories.tsx b/nym-wallet/src/components/Buy/Tutorial.stories.tsx new file mode 100644 index 0000000000..db23ca9efc --- /dev/null +++ b/nym-wallet/src/components/Buy/Tutorial.stories.tsx @@ -0,0 +1,10 @@ +import React from 'react'; + +import { Tutorial } from './Tutorial'; + +export default { + title: 'Buy/Page', + component: Tutorial, +}; + +export const BuyPage = () => ; diff --git a/nym-wallet/src/components/Buy/Tutorial.tsx b/nym-wallet/src/components/Buy/Tutorial.tsx new file mode 100644 index 0000000000..710b526630 --- /dev/null +++ b/nym-wallet/src/components/Buy/Tutorial.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import { Box, Button, Stack, Typography } from '@mui/material'; +import { NymCard } from '../NymCard'; + +export const Tutorial = () => ( + + + + + How to buy NYM with Bity? + + Follow these 3 steps below to quickly and easily buy NYM tokens + + + + + Card 1 + Card 2 + Card 3 + + +); diff --git a/nym-wallet/src/components/CopyToClipboard.tsx b/nym-wallet/src/components/CopyToClipboard.tsx index 8df15668a8..d6de5cab9b 100644 --- a/nym-wallet/src/components/CopyToClipboard.tsx +++ b/nym-wallet/src/components/CopyToClipboard.tsx @@ -35,6 +35,7 @@ export const CopyToClipboard = ({ text = '', iconButton }: { text?: string; icon sx={{ color: 'text.primary', }} + disabled={!text} > {!copied ? : } @@ -51,6 +52,7 @@ export const CopyToClipboard = ({ text = '', iconButton }: { text?: string; icon }} onClick={() => handleCopy(text)} endIcon={copied && theme.palette.success.light }} />} + disabled={!text} > {!copied ? 'Copy' : 'Copied'} diff --git a/nym-wallet/src/components/Modals/SimpleModal.stories.tsx b/nym-wallet/src/components/Modals/SimpleModal.stories.tsx index 35c4df516e..994cf7eb48 100644 --- a/nym-wallet/src/components/Modals/SimpleModal.stories.tsx +++ b/nym-wallet/src/components/Modals/SimpleModal.stories.tsx @@ -225,3 +225,36 @@ export const withBackButton = () => { ); }; + +export const withBackButtonAndCustomLabel = () => { + const [open, setOpen] = React.useState(false); + const handleClick = () => setOpen(true); + + const theme = useTheme(); + + return ( + + setOpen(false)} + onOk={async () => setOpen(false)} + header="This is a modal" + okLabel="Primary action" + onBack={() => setOpen(false)} + backLabel="Cancel" + backButtonFullWidth + {...storybookStyles(theme)} + > + + Tempor culpa est magna. Sit tempor cillum culpa sint ipsum nostrud ullamco voluptate exercitation dolore magna + elit ut mollit. + + + + Veniam dolor laborum labore sit reprehenderit enim mollit magna nulla adipisicing fugiat. Est ex irure quis. + + + + ); +}; diff --git a/nym-wallet/src/components/Modals/SimpleModal.tsx b/nym-wallet/src/components/Modals/SimpleModal.tsx index 85c6480619..591354ffe5 100644 --- a/nym-wallet/src/components/Modals/SimpleModal.tsx +++ b/nym-wallet/src/components/Modals/SimpleModal.tsx @@ -20,6 +20,8 @@ export const SimpleModal: React.FC<{ header: string | React.ReactNode; subHeader?: string; okLabel: string; + backLabel?: string; + backButtonFullWidth?: boolean; okDisabled?: boolean; sx?: SxProps; backdropProps?: object; @@ -38,6 +40,8 @@ export const SimpleModal: React.FC<{ header, subHeader, okLabel, + backLabel, + backButtonFullWidth, sx, children, backdropProps, @@ -71,7 +75,7 @@ export const SimpleModal: React.FC<{ {(onOk || onBack) && ( - {onBack && } + {onBack && } {onOk && ( ); diff --git a/nym-wallet/src/context/buy.tsx b/nym-wallet/src/context/buy.tsx new file mode 100644 index 0000000000..855f5e988a --- /dev/null +++ b/nym-wallet/src/context/buy.tsx @@ -0,0 +1,58 @@ +import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'; +import { sign } from 'src/requests'; +import { Console } from 'src/utils/console'; +// import { AppContext } from './main'; + +export type TBuyContext = { + loading: boolean; + error?: string; + signMessage: (message: string) => Promise; + refresh: () => Promise; +}; + +export const BuyContext = createContext({ + loading: false, + signMessage: async () => '', + refresh: async () => undefined, +}); + +export const BuyContextProvider = ({ children }: { children?: React.ReactNode }): JSX.Element => { + const [loading, setLoading] = useState(false); + const [error, setError] = useState(); + + const refresh = useCallback(async () => { + setError(undefined); + }, []); + + useEffect(() => { + refresh(); + }, [refresh]); + + const signMessage = async (message: string) => { + let signature; + setLoading(true); + try { + signature = await sign(message); + } catch (e: any) { + Console.log(`Sign message operation failed: ${e}`); + setError(`Sign message operation failed: ${e}`); + } finally { + setLoading(false); + } + return signature; + }; + + const memoizedValue = useMemo( + () => ({ + loading, + error, + refresh, + signMessage, + }), + [loading, error], + ); + + return {children}; +}; + +export const useBuyContext = () => useContext(BuyContext); diff --git a/nym-wallet/src/context/index.tsx b/nym-wallet/src/context/index.tsx index 6cfbf5ffd4..5007dd7b17 100644 --- a/nym-wallet/src/context/index.tsx +++ b/nym-wallet/src/context/index.tsx @@ -2,3 +2,4 @@ export * from './main'; export * from './auth'; export * from './accounts'; export * from './bonding'; +export * from './buy'; diff --git a/nym-wallet/src/context/mocks/buy.tsx b/nym-wallet/src/context/mocks/buy.tsx new file mode 100644 index 0000000000..0d7bca418d --- /dev/null +++ b/nym-wallet/src/context/mocks/buy.tsx @@ -0,0 +1,35 @@ +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { BuyContext } from '../buy'; +import { mockSleep } from './utils'; + +export const MockBuyContextProvider = ({ children }: { children?: React.ReactNode }): JSX.Element => { + const [loading, setLoading] = useState(false); + const [error, setError] = useState(); + + const refresh = useCallback(async () => { + setError(undefined); + }, []); + + const signMessage = async (message: string) => { + setLoading(true); + await mockSleep(1042); + setLoading(false); + return `imagineareallyrealisticsignaturehash${message}`; + }; + + useEffect(() => { + refresh(); + }, [refresh]); + + const memoizedValue = useMemo( + () => ({ + loading, + error, + refresh, + signMessage, + }), + [loading, error], + ); + + return {children}; +}; diff --git a/nym-wallet/src/pages/buy/index.tsx b/nym-wallet/src/pages/buy/index.tsx new file mode 100644 index 0000000000..894d080727 --- /dev/null +++ b/nym-wallet/src/pages/buy/index.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import { BuyContextProvider } from 'src/context'; +import { Tutorial } from 'src/components/Buy/Tutorial'; + +export const BuyPage = () => ( + + + +); diff --git a/nym-wallet/src/pages/index.ts b/nym-wallet/src/pages/index.ts index fe29cb1dd2..c2944d771c 100644 --- a/nym-wallet/src/pages/index.ts +++ b/nym-wallet/src/pages/index.ts @@ -4,3 +4,4 @@ export * from './balance'; export * from './bonding'; export * from './delegation'; export * from './internal-docs'; +export * from './buy'; diff --git a/nym-wallet/src/routes/app.tsx b/nym-wallet/src/routes/app.tsx index 0fbc9edc6c..689a080fc4 100644 --- a/nym-wallet/src/routes/app.tsx +++ b/nym-wallet/src/routes/app.tsx @@ -4,7 +4,7 @@ import { ApplicationLayout } from 'src/layouts'; import { Terminal } from 'src/pages/terminal'; import { Send } from 'src/components/Send'; import { Receive } from '../components/Receive'; -import { Balance, InternalDocs, DelegationPage, Admin, BondingPage, NodeSettingsPage } from '../pages'; +import { Balance, InternalDocs, DelegationPage, Admin, BondingPage, NodeSettingsPage, BuyPage } from '../pages'; export const AppRoutes = () => ( @@ -18,6 +18,7 @@ export const AppRoutes = () => ( } /> } /> } /> + } /> );