Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

636 check for existing wallet #719

Closed
12 changes: 12 additions & 0 deletions src/components/App.test.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { render, screen, act } from '../testUtils'
import user from '@testing-library/user-event'
import * as apiMock from '../libs/JmWalletApi'
import * as loadersMock from './loaders/DataLoaders'

import App from './App'

jest.mock('./loaders/DataLoaders', () => ({
...jest.requireActual('./loaders/DataLoaders'),
allWalletsLoader: jest.fn(),
}))

jest.mock('../libs/JmWalletApi', () => ({
...jest.requireActual('../libs/JmWalletApi'),
getGetinfo: jest.fn(),
Expand All @@ -15,6 +21,12 @@ describe('<App />', () => {
const neverResolvingPromise = new Promise(() => {})
;(apiMock.getGetinfo as jest.Mock).mockResolvedValue(neverResolvingPromise)
;(apiMock.getSession as jest.Mock).mockResolvedValue(neverResolvingPromise)
;(loadersMock.allWalletsLoader as jest.Mock).mockReturnValue(
Promise.resolve({
ok: true,
wallets: [],
}),
)
})

it('should display Onboarding screen initially', async () => {
Expand Down
229 changes: 184 additions & 45 deletions src/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,55 +39,144 @@ import Send from './Send'
import RescanChain from './RescanChain'
import Settings from './Settings'
import Wallets from './Wallets'
import { allWalletsLoader } from './loaders/DataLoaders'
const DevSetupPage = lazy(() => import('./DevSetupPage'))

export default function App() {
const { t } = useTranslation()
const settings = useSettings()
const currentWallet = useCurrentWallet()
const setCurrentWallet = useSetCurrentWallet()
const clearCurrentWallet = useClearCurrentWallet()
const reloadCurrentWalletInfo = useReloadCurrentWalletInfo()
const serviceInfo = useServiceInfo()
const sessionConnectionError = useSessionConnectionError()
const [reloadingWalletInfoCounter, setReloadingWalletInfoCounter] = useState(0)
const isReloadingWalletInfo = useMemo(() => reloadingWalletInfoCounter > 0, [reloadingWalletInfoCounter])

const startWallet = useCallback(
(walletFileName: Api.WalletFileName, auth: Api.ApiAuthContext) => {
setSession({ walletFileName, auth })
setCurrentWallet({ walletFileName, token: auth.token })
const router3 = (startWallet: any, sessionConnectionError: any, currentWallet: any, stopWallet: any, t: any) =>
createBrowserRouter([
{
id: 'base',
element: (
<>
<Navbar />
<rb.Container as="main" className="py-4 py-lg-5" fluid="xl">
<Outlet />
</rb.Container>
<Footer />
</>
),
errorElement: <ErrorPage />,
children: [
{
id: 'error-boundary',
element: (
<Layout>
<Outlet />
</Layout>
),
errorElement: (
<Layout variant="wide">
<ErrorPage />
</Layout>
),
children: [
// Routes that are displayed even if the connection to the backend is down
{
id: 'create-wallet',
path: routes.createWallet,
loader: allWalletsLoader,
element: <CreateWallet parentRoute="home" startWallet={startWallet} />,
},
sessionConnectionError && {
id: '404',
path: '*',
element: (
<rb.Alert variant="danger">
{t('app.alert_no_connection', { connectionError: sessionConnectionError.message })}.
</rb.Alert>
),
},
// Routes that are displayed only if the backend is reachable
!sessionConnectionError && [
{
id: 'wallets',
path: routes.home,
loader: allWalletsLoader,
element: <Wallets currentWallet={currentWallet} startWallet={startWallet} stopWallet={stopWallet} />,
},
{
id: 'import-wallet',
path: routes.importWallet,
element: <ImportWallet parentRoute="home" startWallet={startWallet} />,
},
currentWallet && [
{
id: 'wallet',
path: routes.wallet,
element: <MainWalletView wallet={currentWallet} />,
},
{
id: 'jam',
path: routes.jam,
element: <Jam wallet={currentWallet} />,
},
{
id: 'send',
path: routes.send,
element: <Send wallet={currentWallet} />,
},
{
id: 'earn',
path: routes.earn,
element: <Earn wallet={currentWallet} />,
},
{
id: 'receive',
path: routes.receive,
element: <Receive wallet={currentWallet} />,
},
{
id: 'rescan',
path: routes.rescanChain,
element: <RescanChain wallet={currentWallet} />,
},
{
id: 'settings',
path: routes.settings,
element: <Settings wallet={currentWallet} stopWallet={stopWallet} />,
},
],
isDebugFeatureEnabled('errorExamplePage') && {
id: 'error-example',
path: routes.__errorExample,
element: <ErrorThrowingComponent />,
},
isDebugFeatureEnabled('devSetupPage') && {
id: 'dev-env',
path: routes.__devSetup,
element: (
<Suspense fallback={<Loading />}>
<DevSetupPage />
</Suspense>
),
},
{
id: '404',
path: '*',
element: <Navigate to={routes.home} replace />,
},
],
],
},
],
},
[setCurrentWallet],
)
])

const stopWallet = useCallback(() => {
clearCurrentWallet()
clearSession()
}, [clearCurrentWallet])
// passing in anything here causes multiple renders. Why?
// actually it's not the passing in, it's the use of the function
// this works fine in photo voice...
const router2 = createBrowserRouter([
{
id: 'wallets',
path: routes.home,
loader: allWalletsLoader,
element: <h1>Wallets</h1>,
// element: <Wallets currentWallet={currentWallet} startWallet={startWallet} stopWallet={stopWallet} />
},
])

const reloadWalletInfo = useCallback(
(delay: Milliseconds) => {
setReloadingWalletInfoCounter((current) => current + 1)
console.info('Reloading wallet info...')
return new Promise<WalletInfo>((resolve, reject) =>
setTimeout(() => {
const abortCtrl = new AbortController()
reloadCurrentWalletInfo
.reloadAll({ signal: abortCtrl.signal })
.then((result) => resolve(result))
.catch((error) => reject(error))
.finally(() => {
console.info('Finished reloading wallet info.')
setReloadingWalletInfoCounter((current) => current - 1)
})
}, delay),
)
},
[reloadCurrentWalletInfo],
)

const router = createBrowserRouter(
const router = (startWallet: any, sessionConnectionError: any, currentWallet: any, stopWallet: any, t: any) =>
createBrowserRouter(
createRoutesFromElements(
<Route
id="base"
Expand Down Expand Up @@ -123,6 +212,7 @@ export default function App() {
<Route
id="create-wallet"
path={routes.createWallet}
loader={allWalletsLoader}
element={<CreateWallet parentRoute={'home'} startWallet={startWallet} />}
/>

Expand All @@ -144,6 +234,7 @@ export default function App() {
<Route
id="wallets"
path={routes.home}
loader={allWalletsLoader}
element={<Wallets currentWallet={currentWallet} startWallet={startWallet} stopWallet={stopWallet} />}
/>
<Route
Expand Down Expand Up @@ -195,6 +286,52 @@ export default function App() {
},
)

export default function App() {
const { t } = useTranslation()
const settings = useSettings()
const currentWallet = useCurrentWallet()
const setCurrentWallet = useSetCurrentWallet()
const clearCurrentWallet = useClearCurrentWallet()
const reloadCurrentWalletInfo = useReloadCurrentWalletInfo()
const serviceInfo = useServiceInfo()
const sessionConnectionError = useSessionConnectionError()
const [reloadingWalletInfoCounter, setReloadingWalletInfoCounter] = useState(0)
const isReloadingWalletInfo = useMemo(() => reloadingWalletInfoCounter > 0, [reloadingWalletInfoCounter])

const startWallet = useCallback(
(walletFileName: Api.WalletFileName, auth: Api.ApiAuthContext) => {
setSession({ walletFileName, auth })
setCurrentWallet({ walletFileName, token: auth.token })
},
[setCurrentWallet],
)

const stopWallet = useCallback(() => {
clearCurrentWallet()
clearSession()
}, [clearCurrentWallet])

const reloadWalletInfo = useCallback(
(delay: Milliseconds) => {
setReloadingWalletInfoCounter((current) => current + 1)
console.info('Reloading wallet info...')
return new Promise<WalletInfo>((resolve, reject) =>
setTimeout(() => {
const abortCtrl = new AbortController()
reloadCurrentWalletInfo
.reloadAll({ signal: abortCtrl.signal })
.then((result) => resolve(result))
.catch((error) => reject(error))
.finally(() => {
console.info('Finished reloading wallet info.')
setReloadingWalletInfoCounter((current) => current - 1)
})
}, delay),
)
},
[reloadCurrentWalletInfo],
)

if (settings.showOnboarding === true) {
return (
<rb.Container className="onboarding pt-3 pt-md-5">
Expand All @@ -217,7 +354,9 @@ export default function App() {
'jm-maker-running': serviceInfo?.makerRunning === true,
})}
>
<RouterProvider router={router} />
{/* <RouterProvider router={router(startWallet, sessionConnectionError, currentWallet, stopWallet, t)} /> */}
{/* <RouterProvider router={router3(startWallet, sessionConnectionError, currentWallet, stopWallet, t)} /> */}
<RouterProvider router={router2} />
</div>
<WalletInfoAutoReload currentWallet={currentWallet} reloadWalletInfo={reloadWalletInfo} />
</>
Expand Down
10 changes: 10 additions & 0 deletions src/components/CreateWallet.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@ jest.mock('../libs/JmWalletApi', () => ({
postWalletCreate: jest.fn(),
}))

const mockedUseNavigation = jest.fn()
const mockUseLoaderData = jest.fn()
jest.mock('react-router-dom', () => {
return {
...jest.requireActual('react-router-dom'),
useNavigation: () => mockedUseNavigation,
useLoaderData: () => mockUseLoaderData,
}
})

const NOOP = () => {}

describe('<CreateWallet />', () => {
Expand Down
12 changes: 11 additions & 1 deletion src/components/WalletCreationForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import { useCallback } from 'react'
import * as rb from 'react-bootstrap'
import { useTranslation } from 'react-i18next'
import { Formik, FormikErrors } from 'formik'
import { useLoaderData, useNavigation } from 'react-router-dom'
import Sprite from './Sprite'
import { JM_WALLET_FILE_EXTENSION, sanitizeWalletName } from '../utils'
import styles from './WalletCreationForm.module.css'
import { AllWalletsLoaderResponse } from './loaders/DataLoaders'

export interface CreateWalletFormValues {
walletName: string
Expand Down Expand Up @@ -37,13 +39,21 @@ const WalletCreationForm = ({
onSubmit,
}: WalletCreationFormProps) => {
const { t, i18n } = useTranslation()
const getWalletAllResponse = useLoaderData() as AllWalletsLoaderResponse
const navigation = useNavigation()

const validate = useCallback(
(values: CreateWalletFormValues) => {
const errors = {} as FormikErrors<CreateWalletFormValues>
if (!values.walletName || !validateWalletName(values.walletName)) {
errors.walletName = t('create_wallet.feedback_invalid_wallet_name')
}
if (
navigation.state === 'idle' &&
getWalletAllResponse?.existingWallets?.wallets?.includes(`${values.walletName}.jmdat`)
) {
errors.walletName = t('create_wallet.feedback_wallet_name_already_exists')
}
if (!values.password) {
errors.password = t('create_wallet.feedback_invalid_password')
}
Expand All @@ -52,7 +62,7 @@ const WalletCreationForm = ({
}
return errors
},
[t],
[getWalletAllResponse?.existingWallets?.wallets, navigation.state, t],
)

return (
Expand Down
Loading
Loading