diff --git a/packages/adena-extension/public/manifest.json b/packages/adena-extension/public/manifest.json index c8c75f927..44d665c6a 100644 --- a/packages/adena-extension/public/manifest.json +++ b/packages/adena-extension/public/manifest.json @@ -4,7 +4,11 @@ "manifest_version": 3, "version": "1.9.2", "action": { - "default_popup": "popup.html" + "default_icon": { + "16": "icons/icon16.png", + "32": "icons/icon32.png" + }, + "default_title": "Adena" }, "background": { "service_worker": "background.js" @@ -12,7 +16,7 @@ "permissions": ["storage", "tabs"], "content_scripts": [ { - "matches": ["*://*/*"], + "matches": [""], "js": ["content.js"] } ], diff --git a/packages/adena-extension/src/App/app-provider.tsx b/packages/adena-extension/src/App/app-provider.tsx index dbb430eb4..851603c43 100644 --- a/packages/adena-extension/src/App/app-provider.tsx +++ b/packages/adena-extension/src/App/app-provider.tsx @@ -5,26 +5,27 @@ import theme from '@styles/theme'; import { RecoilRoot } from 'recoil'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { AdenaProvider, WalletProvider } from '@common/provider'; +import { AppProviderErrorBoundary, AppReloadFallback } from '@common/error-boundary'; const queryClient = new QueryClient(); const AppProvider = ({ children }: { children: ReactNode }): ReactElement => { return ( - <> - - - - - - Loading...}> - {children} - - - - - - - + + }> + + + + + + Loading...}>{children} + + + + + + + ); }; diff --git a/packages/adena-extension/src/App/popup.tsx b/packages/adena-extension/src/App/popup.tsx index 91073d26a..9468506e2 100644 --- a/packages/adena-extension/src/App/popup.tsx +++ b/packages/adena-extension/src/App/popup.tsx @@ -5,10 +5,22 @@ import AppProvider from './app-provider'; import useApp from './use-app'; import { GlobalPopupStyle } from '@styles/global-style'; import { HashRouter } from 'react-router-dom'; +import { useInitWallet } from '@hooks/use-init-wallet'; +import { useWallet } from '@hooks/use-wallet'; +import useLink from '@hooks/use-link'; const RunApp = (): ReactElement => { useApp(); - return ; + useInitWallet(); + const { existWallet, isLoadingExistWallet } = useWallet(); + const { openRegister } = useLink(); + + if (isLoadingExistWallet === false && existWallet === false) { + openRegister(); + window.close(); + } + + return existWallet ? : <>; }; const App = (): ReactElement => { diff --git a/packages/adena-extension/src/background.ts b/packages/adena-extension/src/background.ts index 8dfdb3012..ff5cfae08 100644 --- a/packages/adena-extension/src/background.ts +++ b/packages/adena-extension/src/background.ts @@ -1,13 +1,44 @@ +import { ChromeLocalStorage } from '@common/storage'; import { MessageHandler } from './inject/message'; +function existsWallet(): Promise { + const storage = new ChromeLocalStorage(); + return storage + .get('SERIALIZED') + .then(async (serialized) => typeof serialized === 'string' && serialized.length !== 0) + .catch(() => false); +} + chrome.runtime.onInstalled.addListener((details) => { if (details.reason === 'install') { chrome.tabs.create({ url: chrome.runtime.getURL('/register.html'), }); } else if (details.reason === 'update') { - console.log('update'); + existsWallet().then(() => { + chrome.action.setPopup({ popup: '/popup.html' }); + }); } }); +chrome.action.onClicked.addListener((tab) => { + existsWallet() + .then(async (exist) => { + if (!exist) { + await chrome.action.setPopup({ tabId: tab.id, popup: '/popup.html' }); + chrome.tabs.create({ + url: chrome.runtime.getURL('/register.html'), + }); + } else { + await chrome.action.setPopup({ tabId: tab.id, popup: '' }); + } + }) + .catch(async () => { + await chrome.action.setPopup({ tabId: tab.id, popup: '' }); + chrome.tabs.create({ + url: chrome.runtime.getURL('/register.html'), + }); + }); +}); + chrome.runtime.onMessage.addListener(MessageHandler.createHandler); diff --git a/packages/adena-extension/src/common/error-boundary/app-provider-error-boundary.tsx b/packages/adena-extension/src/common/error-boundary/app-provider-error-boundary.tsx new file mode 100644 index 000000000..074390b64 --- /dev/null +++ b/packages/adena-extension/src/common/error-boundary/app-provider-error-boundary.tsx @@ -0,0 +1,39 @@ +import React, { ErrorInfo } from 'react'; + +import { CommonError } from '@common/errors/common'; + +interface Props { + fallback: React.ReactNode; + children?: React.ReactNode; +} + +interface State { + hasError: boolean; +} + +export class AppProviderErrorBoundary extends React.Component { + public state: State = { + hasError: false, + }; + + public static getDerivedStateFromError(error: Error): State { + if (error instanceof CommonError) { + if (error.getStatus() === 401) { + return { hasError: true }; + } + } + return { hasError: false }; + } + + public componentDidCatch(error: Error, errorInfo: ErrorInfo): void { + console.error('Uncaught error:', error, errorInfo); + } + + public render(): React.ReactNode { + if (this.state.hasError) { + return this.props.fallback; + } + + return this.props.children; + } +} diff --git a/packages/adena-extension/src/common/error-boundary/fallback/app-reload-fallback.tsx b/packages/adena-extension/src/common/error-boundary/fallback/app-reload-fallback.tsx new file mode 100644 index 000000000..96825f7de --- /dev/null +++ b/packages/adena-extension/src/common/error-boundary/fallback/app-reload-fallback.tsx @@ -0,0 +1,9 @@ +import React, { useEffect } from 'react'; + +export const AppReloadFallback: React.FC = () => { + useEffect(() => { + location.reload(); + }, []); + + return ; +}; diff --git a/packages/adena-extension/src/common/error-boundary/fallback/index.ts b/packages/adena-extension/src/common/error-boundary/fallback/index.ts new file mode 100644 index 000000000..4512a8a04 --- /dev/null +++ b/packages/adena-extension/src/common/error-boundary/fallback/index.ts @@ -0,0 +1 @@ +export * from './app-reload-fallback'; diff --git a/packages/adena-extension/src/common/error-boundary/index.ts b/packages/adena-extension/src/common/error-boundary/index.ts new file mode 100644 index 000000000..9444bc44d --- /dev/null +++ b/packages/adena-extension/src/common/error-boundary/index.ts @@ -0,0 +1,2 @@ +export * from './app-provider-error-boundary'; +export * from './fallback'; diff --git a/packages/adena-extension/src/common/errors/common/common-error.ts b/packages/adena-extension/src/common/errors/common/common-error.ts index 5403b280b..5207bd22b 100644 --- a/packages/adena-extension/src/common/errors/common/common-error.ts +++ b/packages/adena-extension/src/common/errors/common/common-error.ts @@ -13,6 +13,10 @@ const ERROR_VALUE = { status: 400, type: 'FAILED_TO_RUN', }, + FAILED_INITIALIZE_CHROME_API: { + status: 401, + type: 'FAILED_INITIALIZE_CHROME_API', + }, }; type ErrorType = keyof typeof ERROR_VALUE; diff --git a/packages/adena-extension/src/common/storage/chrome-local-storage.ts b/packages/adena-extension/src/common/storage/chrome-local-storage.ts index 1cae7a0ef..27944b117 100644 --- a/packages/adena-extension/src/common/storage/chrome-local-storage.ts +++ b/packages/adena-extension/src/common/storage/chrome-local-storage.ts @@ -1,5 +1,6 @@ import { Storage } from '.'; import { StorageMigrator, StorageModelLatest } from '@migrates/storage-migrator'; +import { CommonError } from '@common/errors/common'; type StorageKeyTypes = | 'NETWORKS' @@ -42,6 +43,9 @@ export class ChromeLocalStorage implements Storage { private current: StorageModelLatest | null = null; constructor() { + if (!chrome.storage) { + throw new CommonError('FAILED_INITIALIZE_CHROME_API'); + } this.storage = chrome.storage.local; this.migrator = new StorageMigrator(StorageMigrator.migrations(), this.storage); } diff --git a/packages/adena-extension/src/common/storage/chrome-session-storage.ts b/packages/adena-extension/src/common/storage/chrome-session-storage.ts index af187d900..935f89353 100644 --- a/packages/adena-extension/src/common/storage/chrome-session-storage.ts +++ b/packages/adena-extension/src/common/storage/chrome-session-storage.ts @@ -1,9 +1,13 @@ +import { CommonError } from '@common/errors/common'; import { Storage } from '.'; export class ChromeSessionStorage implements Storage { private storage: chrome.storage.SessionStorageArea; constructor() { + if (!chrome.storage) { + throw new CommonError('FAILED_INITIALIZE_CHROME_API'); + } this.storage = chrome.storage.session; } diff --git a/packages/adena-extension/src/hooks/use-clear.tsx b/packages/adena-extension/src/hooks/use-clear.tsx index 6260540f4..b8f0a5148 100644 --- a/packages/adena-extension/src/hooks/use-clear.tsx +++ b/packages/adena-extension/src/hooks/use-clear.tsx @@ -1,4 +1,5 @@ import { BalanceState, CommonState, NetworkState, WalletState } from '@states'; +import { useQueryClient } from '@tanstack/react-query'; import { useResetRecoilState, useSetRecoilState } from 'recoil'; import { useAdenaContext } from './use-context'; @@ -15,6 +16,7 @@ export const useClear = (): UseClearReturn => { establishService, tokenService, } = useAdenaContext(); + const queryClient = useQueryClient(); const clearCurrentAccount = useResetRecoilState(WalletState.currentAccount); const setWalletState = useSetRecoilState(WalletState.state); const clearTransactionHistory = useResetRecoilState(WalletState.transactionHistory); @@ -41,6 +43,7 @@ export const useClear = (): UseClearReturn => { await chainService.clear(); await establishService.clear(); await tokenService.clear(); + queryClient.clear(); return true; }; diff --git a/packages/adena-extension/src/hooks/use-init-wallet.tsx b/packages/adena-extension/src/hooks/use-init-wallet.tsx index 661274e81..0ab0cdf79 100644 --- a/packages/adena-extension/src/hooks/use-init-wallet.tsx +++ b/packages/adena-extension/src/hooks/use-init-wallet.tsx @@ -2,14 +2,12 @@ import { useEffect } from 'react'; import { RoutePath } from '@types'; import useAppNavigate from './use-app-navigate'; -import useLink from './use-link'; import { useLoadAccounts } from './use-load-accounts'; import { useMatch } from 'react-router-dom'; import { useAddressBook } from './use-address-book'; export const useInitWallet = (): void => { const { navigate } = useAppNavigate(); - const { openRegister } = useLink(); const isApproveLoginPage = useMatch('/approve/*'); const { state } = useLoadAccounts(); @@ -19,10 +17,8 @@ export const useInitWallet = (): void => { switch (state) { case 'NONE': case 'LOADING': - break; case 'CREATE': case 'FAIL': - openRegister(); break; case 'LOGIN': if (!isApproveLoginPage) { diff --git a/packages/adena-extension/src/hooks/use-wallet.ts b/packages/adena-extension/src/hooks/use-wallet.ts index ec2817a60..c5e8a3e55 100644 --- a/packages/adena-extension/src/hooks/use-wallet.ts +++ b/packages/adena-extension/src/hooks/use-wallet.ts @@ -1,16 +1,16 @@ import { useQuery } from '@tanstack/react-query'; -import { useAdenaContext, useWalletContext } from './use-context'; +import { useAdenaContext } from './use-context'; export interface UseWalletReturn { - existWallet: boolean; + existWallet: boolean | undefined; + isLoadingExistWallet: boolean; } export const useWallet = (): UseWalletReturn => { - const { wallet } = useWalletContext(); const { walletService } = useAdenaContext(); - const { data: existWallet = false } = useQuery( - ['wallet/existWallet', walletService, wallet], + const { data: existWallet, isLoading: isLoadingExistWallet } = useQuery( + ['wallet/existWallet', walletService], async () => { const existWallet = await walletService.existsWallet().catch(() => false); return existWallet; @@ -18,5 +18,5 @@ export const useWallet = (): UseWalletReturn => { {}, ); - return { existWallet }; + return { existWallet, isLoadingExistWallet }; }; diff --git a/packages/adena-extension/src/router/popup/index.tsx b/packages/adena-extension/src/router/popup/index.tsx index 3ed220e70..538e098a5 100644 --- a/packages/adena-extension/src/router/popup/index.tsx +++ b/packages/adena-extension/src/router/popup/index.tsx @@ -55,13 +55,10 @@ import LoadingMain from './loading-main'; import { CreatePassword } from '@pages/popup/certify/create-password'; import { LaunchAdena } from '@pages/popup/certify/launch-adena'; import ApproveSignFailedScreen from '@pages/popup/wallet/approve-sign-failed-screen'; -import { useInitWallet } from '@hooks/use-init-wallet'; export const PopupRouter = (): JSX.Element => { - useInitWallet(); - return ( - <> +
} /> @@ -127,6 +124,6 @@ export const PopupRouter = (): JSX.Element => { - + ); }; diff --git a/packages/adena-extension/src/services/wallet/wallet.ts b/packages/adena-extension/src/services/wallet/wallet.ts index dfa524efc..c7fbd5f1c 100644 --- a/packages/adena-extension/src/services/wallet/wallet.ts +++ b/packages/adena-extension/src/services/wallet/wallet.ts @@ -16,7 +16,7 @@ export class WalletService { public existsWallet = (): Promise => { return this.walletRepository .getSerializedWallet() - .then(() => true) + .then((serializedWallet) => !!serializedWallet) .catch(() => false); }; diff --git a/packages/adena-extension/webpack.config.js b/packages/adena-extension/webpack.config.js index b5d2c66d6..efe16763e 100644 --- a/packages/adena-extension/webpack.config.js +++ b/packages/adena-extension/webpack.config.js @@ -85,27 +85,12 @@ const config = { transform: (content, path) => Buffer.from( JSON.stringify({ - name: packageInfo.name, - version: packageInfo.version, - description: packageInfo.description, icons: { 16: 'icons/icon16.png', 32: 'icons/icon32.png', 48: 'icons/icon48.png', 128: 'icons/icon128.png', }, - background: { - service_worker: 'background.js', - }, - content_scripts: [ - { - matches: [''], - js: ['content.js'], - }, - ], - action: { - default_popup: 'popup.html', - }, ...JSON.parse(content.toString()), }), ),