Skip to content

Commit

Permalink
Merge pull request #386 from valory-xyz/fix/response-ok-charset
Browse files Browse the repository at this point in the history
fix: fetch response parsing, create safe retry
  • Loading branch information
truemiller authored Oct 4, 2024
2 parents 06224d2 + fea5d57 commit a744c2e
Show file tree
Hide file tree
Showing 13 changed files with 242 additions and 76 deletions.
2 changes: 1 addition & 1 deletion electron/install.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const homedir = os.homedir();
* - use "" (nothing as a suffix) for latest release candidate, for example "0.1.0rc26"
* - use "alpha" for alpha release, for example "0.1.0rc26-alpha"
*/
const OlasMiddlewareVersion = '0.1.0rc158';
const OlasMiddlewareVersion = '0.1.0rc162';

const path = require('path');
const { app } = require('electron');
Expand Down
104 changes: 84 additions & 20 deletions frontend/components/SetupPage/Create/SetupCreateSafe.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,100 @@
import { message, Typography } from 'antd';
import Image from 'next/image';
import { useEffect, useState } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';

import { Chain } from '@/client';
import { CardSection } from '@/components/styled/CardSection';
import { UNICODE_SYMBOLS } from '@/constants/symbols';
import { SUPPORT_URL } from '@/constants/urls';
import { Pages } from '@/enums/PageState';
import { useMasterSafe } from '@/hooks/useMasterSafe';
import { usePageState } from '@/hooks/usePageState';
import { useSetup } from '@/hooks/useSetup';
import { useWallet } from '@/hooks/useWallet';
import { WalletService } from '@/service/Wallet';
import { delayInSeconds } from '@/utils/delay';

export const SetupCreateSafe = () => {
const { masterSafeAddress } = useWallet();
const { backupSigner } = useSetup();
const { goto } = usePageState();
const { updateWallets } = useWallet();
const { updateMasterSafeOwners, masterSafeAddress, backupSafeAddress } =
useMasterSafe();
const { backupSigner } = useSetup();

const [isCreatingSafe, setIsCreatingSafe] = useState(false);
const [isError, setIsError] = useState(false);
const [isCreateSafeSuccessful, setIsCreateSafeSuccessful] = useState(false);
const [failed, setFailed] = useState(false);

const createSafeWithRetries = useCallback(
async (retries: number) => {
setIsCreatingSafe(true);

// If we have retried too many times, set failed
if (retries <= 0) {
setFailed(true);
setIsCreatingSafe(false);
setIsCreateSafeSuccessful(false);
return;
}

// Try to create the safe
WalletService.createSafe(Chain.GNOSIS, backupSigner)
.then(async () => {
// Backend returned success
message.success('Account created');

// Attempt wallet and master safe updates before proceeding
try {
await updateWallets();
await updateMasterSafeOwners();
} catch (e) {
console.error(e);
}

// Set states for successful creation
setIsCreatingSafe(false);
setIsCreateSafeSuccessful(true);
setFailed(false);
})
.catch(async (e) => {
console.error(e);
// Wait for 5 seconds before retrying
await delayInSeconds(5);
// Retry
const newRetries = retries - 1;
if (newRetries <= 0) {
message.error('Failed to create account');
} else {
message.error('Failed to create account, retrying in 5 seconds');
}
createSafeWithRetries(newRetries);
});
},
[backupSigner, updateMasterSafeOwners, updateWallets],
);

const creationStatusText = useMemo(() => {
if (isCreatingSafe) return 'Creating account';
if (masterSafeAddress && !backupSafeAddress) return 'Checking backup';
if (masterSafeAddress && backupSafeAddress) return 'Account created';
return 'Account creation in progress';
}, [backupSafeAddress, isCreatingSafe, masterSafeAddress]);

useEffect(() => {
if (isCreatingSafe) return;
setIsCreatingSafe(true);
// TODO: add backup signer
WalletService.createSafe(Chain.GNOSIS, backupSigner).catch((e) => {
console.error(e);
setIsError(true);
message.error('Failed to create an account. Please try again later.');
});
}, [backupSigner, isCreatingSafe]);
if (failed || isCreatingSafe || isCreateSafeSuccessful) return;
createSafeWithRetries(3);
}, [
backupSigner,
createSafeWithRetries,
failed,
isCreateSafeSuccessful,
isCreatingSafe,
]);

useEffect(() => {
// Only progress is the safe is created and accessible via context (updates on interval)
if (masterSafeAddress) goto(Pages.Main);
}, [goto, masterSafeAddress]);
if (masterSafeAddress && backupSafeAddress) goto(Pages.Main);
}, [backupSafeAddress, goto, masterSafeAddress]);

return (
<CardSection
Expand All @@ -44,14 +104,18 @@ export const SetupCreateSafe = () => {
padding="80px 24px"
gap={12}
>
{isError ? (
{failed ? (
<>
<Image src="/broken-robot.svg" alt="logo" width={80} height={80} />
<Typography.Text type="secondary" className="mt-12">
Error, please contact{' '}
<a target="_blank" href={SUPPORT_URL}>
Olas community {UNICODE_SYMBOLS.EXTERNAL_LINK}
Error, please restart the app and try again.
</Typography.Text>
<Typography.Text>
If the issue persists, please{' '}
<a href={SUPPORT_URL} target="_blank" rel="noreferrer">
contact Olas community support {UNICODE_SYMBOLS.EXTERNAL_LINK}
</a>
.
</Typography.Text>
</>
) : (
Expand All @@ -67,7 +131,7 @@ export const SetupCreateSafe = () => {
className="m-0 mt-12 loading-ellipses"
style={{ width: '220px' }}
>
Creating account
{creationStatusText}
</Typography.Title>
<Typography.Text type="secondary">
You will be redirected once the account is created
Expand Down
28 changes: 15 additions & 13 deletions frontend/components/SetupPage/Create/SetupSeedPhrase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,21 @@ export const SetupSeedPhrase = () => {
<Tag key={word}>{word}</Tag>
))}
</Flex>
<Button
size="large"
onClick={() =>
copyToClipboard(mnemonic.join(' ')).then(() =>
message.success('Seed phrase is copied!'),
)
}
>
<CopyOutlined /> Copy to clipboard
</Button>
<Button type="primary" size="large" onClick={handleNext}>
Continue
</Button>
<Flex gap={10}>
<Button
size="large"
onClick={() =>
copyToClipboard(mnemonic.join(' ')).then(() =>
message.success('Seed phrase is copied!'),
)
}
>
<CopyOutlined /> Copy to clipboard
</Button>
<Button type="primary" size="large" onClick={handleNext}>
Continue
</Button>
</Flex>
</CardFlex>
);
};
2 changes: 1 addition & 1 deletion frontend/components/styled/CardFlex.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const CardFlex = styled(Card).withConfig({
const { gap, noBodyPadding } = props;
const gapStyle = gap ? `gap: ${gap}px;` : '';
const paddingStyle = noBodyPadding === 'true' ? 'padding: 0;' : undefined;
const paddingStyle = noBodyPadding === 'true' ? 'padding: 0;' : '';
return `${gapStyle} ${paddingStyle}`;
}}
Expand Down
3 changes: 3 additions & 0 deletions frontend/constants/headers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const CONTENT_TYPE_JSON_UTF8 = {
'Content-Type': 'application/json; charset=utf-8',
} as const;
2 changes: 1 addition & 1 deletion frontend/context/MasterSafeProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const MasterSafeContext = createContext<{
masterSafeAddress?: Address;
masterEoaAddress?: Address;
masterSafeOwners?: Address[];
updateMasterSafeOwners?: () => Promise<void>;
updateMasterSafeOwners: () => Promise<void>;
}>({
backupSafeAddress: undefined,
masterSafeAddress: undefined,
Expand Down
3 changes: 2 additions & 1 deletion frontend/context/ServicesProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ export const ServicesProvider = ({ children }: PropsWithChildren) => {
setHasInitialLoaded(true);
})
.catch((e) => {
message.error(e.message);
console.error(e);
// message.error(e.message); Commented out to avoid showing error message; need to handle "isAuthenticated" in a better way
}),
[],
);
Expand Down
9 changes: 6 additions & 3 deletions frontend/context/WalletProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,12 @@ export const WalletProvider = ({ children }: PropsWithChildren) => {
const masterSafeAddress: Address | undefined = wallets?.[0]?.safe;

const updateWallets = async () => {
const wallets = await WalletService.getWallets();
if (!wallets) return;
setWallets(wallets);
try {
const wallets = await WalletService.getWallets();
setWallets(wallets);
} catch (e) {
console.error(e);
}
};

useInterval(updateWallets, isOnline ? FIVE_SECONDS_INTERVAL : null);
Expand Down
34 changes: 22 additions & 12 deletions frontend/service/Account.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import { CONTENT_TYPE_JSON_UTF8 } from '@/constants/headers';
import { BACKEND_URL } from '@/constants/urls';

/**
* Gets account status "is_setup"
*/
const getAccount = () =>
fetch(`${BACKEND_URL}/account`).then((res) => res.json());
fetch(`${BACKEND_URL}/account`, {
headers: {
...CONTENT_TYPE_JSON_UTF8,
},
}).then((res) => {
if (res.ok) return res.json();
throw new Error('Failed to get account');
});

/**
* Creates a local user account
Expand All @@ -13,41 +21,43 @@ const createAccount = (password: string) =>
fetch(`${BACKEND_URL}/account`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...CONTENT_TYPE_JSON_UTF8,
},
body: JSON.stringify({ password }),
}).then((res) => res.json());
}).then((res) => {
if (res.ok) return res.json();
throw new Error('Failed to create account');
});

/**
* Updates user's password
*/
const updateAccount = (oldPassword: string, newPassword: string) =>
fetch(`${BACKEND_URL}/account`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
headers: { ...CONTENT_TYPE_JSON_UTF8 },
body: JSON.stringify({
old_password: oldPassword,
new_password: newPassword,
}),
}).then((res) => res.json());
}).then((res) => {
if (res.ok) return res.json();
throw new Error('Failed to update account');
});

/**
* Logs in a user
*/
const loginAccount = (password: string) =>
fetch(`${BACKEND_URL}/account/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
headers: { ...CONTENT_TYPE_JSON_UTF8 },
body: JSON.stringify({
password,
}),
}).then((res) => {
if (![200, 201].includes(res.status)) throw new Error('Login failed');
res.json();
if (res.ok) return res.json();
throw new Error('Failed to login');
});

export const AccountService = {
Expand Down
Loading

0 comments on commit a744c2e

Please sign in to comment.