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

feat: add guard for import ledger page #495

Merged
merged 2 commits into from
Jul 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/app/components/guards/onboarding/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useMemo, useState } from 'react';
import { Navigate } from 'react-router-dom';

import { useSingleTabGuard } from '@components/guards/singleTab';
import useHasStateRehydrated from '@hooks/stores/useHasRehydrated';
import useWalletSelector from '@hooks/useWalletSelector';

Expand All @@ -9,18 +10,17 @@ import {
WalletExistsContextProps,
useWalletExistsContext,
} from './WalletExistsContext';
import useOnboardingSingleton from './useOnboardingSingleton';

interface WalletExistsGuardProps {
children: React.ReactElement;
}

/**
* This guard is used to redirect the user to the wallet exists page if they have a wallet and ensures
* that only 1 onboarding workflow tab exists at a time (via the useOnboardingSingleton hook).
* that only 1 onboarding workflow tab exists at a time (via the useSingleTabGuard hook).
*/
function OnboardingGuard({ children }: WalletExistsGuardProps): React.ReactElement {
useOnboardingSingleton();
useSingleTabGuard('onboarding');

const [walletExistsGuardEnabled, setWalletExistsGuardEnabled] = useState(true);

Expand Down
30 changes: 0 additions & 30 deletions src/app/components/guards/onboarding/useOnboardingSingleton.ts

This file was deleted.

66 changes: 66 additions & 0 deletions src/app/components/guards/singleTab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { useEffect } from 'react';

type GuardType = 'onboarding' | 'importLedger' | 'closeWallet';

const getChannelAndPingNames = (guardName: GuardType) => {
const channelName = `${guardName}Channel`;
const pingName = `${guardName}Ping`;
return { channelName, pingName };
};

// NOTE: This is for calling the guard from a non-component context
// The window that is sending it will also receive it if the channel it is broadcasting to is open
// This is predominantly made for the reset wallet guard to close all tabs
// Use with care.
export const PostGuardPing = (guardName: GuardType): void => {
const { channelName, pingName } = getChannelAndPingNames(guardName);
const broadcastChannel = new BroadcastChannel(channelName);
broadcastChannel.postMessage(pingName);
broadcastChannel.close();
};

/**
* This hook is used to ensure that only one window with the guard name is open at a time.
* It fires off an event only once on its first render and will close the window if it receives
* the event.
*/
export const useSingleTabGuard = (guardName: GuardType, broadcastOnLoad = true): void => {
const { channelName, pingName } = getChannelAndPingNames(guardName);

useEffect(() => {
const broadcastChannel = new BroadcastChannel(channelName);

broadcastChannel.onmessage = (message) => {
if (message.data !== pingName) {
return;
}

broadcastChannel.close();
window.close();
};

if (broadcastOnLoad) {
broadcastChannel.postMessage(pingName);
}

return () => {
broadcastChannel.close();
};
}, []);
};

type SingleTabProps = {
guardName: GuardType;
children?: React.ReactElement | React.ReactElement[];
};

/**
* This guard is used to ensure that only one window with the guard name is open at a time.
* It fires off an event only once on its first render and will close the window if it receives
* the event.
*/
export function SingleTabGuard({ guardName, children }: SingleTabProps): React.ReactNode {
useSingleTabGuard(guardName);

return children;
}
17 changes: 17 additions & 0 deletions src/app/components/guards/walletCloseGuard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useSingleTabGuard } from '@components/guards/singleTab';

type WalletCloseGuardProps = {
children?: React.ReactElement | React.ReactElement[];
};

/**
* This guard is used to close any open tabs when the wallet is locked or reset.
* It should only be rendered at the root of the options page.
*/
function WalletCloseGuard({ children }: WalletCloseGuardProps): React.ReactNode {
useSingleTabGuard('closeWallet', false);

return children;
}

export default WalletCloseGuard;
9 changes: 8 additions & 1 deletion src/app/routes/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import ExtendedScreenContainer from '@components/extendedScreenContainer';
import AuthGuard from '@components/guards/auth';
import OnboardingGuard from '@components/guards/onboarding';
import { SingleTabGuard } from '@components/guards/singleTab';
import ScreenContainer from '@components/screenContainer';
import AccountList from '@screens/accountList';
import AuthenticationRequest from '@screens/authenticationRequest';
Expand Down Expand Up @@ -79,7 +80,13 @@ const router = createHashRouter([
},
{
path: 'import-ledger',
element: <ImportLedger />,
element: (
<AuthGuard>
<SingleTabGuard guardName='importLedger'>
<ImportLedger />
</SingleTabGuard>
</AuthGuard>
)
},
{
index: true,
Expand Down
7 changes: 6 additions & 1 deletion src/app/stores/wallet/actions/actionCreators.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { PostGuardPing } from '@components/guards/singleTab';
import { AccountType } from '@secretkeylabs/xverse-core';
import {
Account,
BaseWallet,
Expand All @@ -9,7 +11,6 @@ import {
TransactionData,
} from '@secretkeylabs/xverse-core/types';
import BigNumber from 'bignumber.js';
import { AccountType } from '@secretkeylabs/xverse-core';
import * as actions from './types';

export function setWalletAction(wallet: BaseWallet): actions.SetWallet {
Expand All @@ -27,6 +28,8 @@ export function unlockWalletAction(seed: string) {
}

export function lockWalletAction() {
// We post the closeWallet action to the guard so that any open tabs will close
PostGuardPing('closeWallet');
return {
type: actions.LockWalletKey,
};
Expand All @@ -47,6 +50,8 @@ export function setWalletSeedPhraseAction(seedPhrase: string): actions.SetWallet
}

export function resetWalletAction(): actions.ResetWallet {
// We post the closeWallet action to the guard so that any open tabs will close
PostGuardPing('closeWallet');
return {
type: actions.ResetWalletKey,
};
Expand Down
7 changes: 6 additions & 1 deletion src/pages/Options/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import WalletCloseGuard from '@components/guards/walletCloseGuard';
import { decryptMnemonic } from '@stacks/encryption';
import rootStore from '@stores/index';
import { setWalletSeedPhraseAction } from '@stores/wallet/actions/actionCreators';
Expand Down Expand Up @@ -27,7 +28,11 @@ const renderApp = async () => {
});
const container = document.getElementById('app');
const root = createRoot(container!);
return root.render(<App />);
return root.render(
<WalletCloseGuard>
<App />
</WalletCloseGuard>,
);
};

renderApp();