diff --git a/src/app/components/account/account-title.tsx b/src/app/components/account/account-title.tsx new file mode 100644 index 00000000000..ff094eb066c --- /dev/null +++ b/src/app/components/account/account-title.tsx @@ -0,0 +1,28 @@ +import { BoxProps } from '@stacks/ui'; + +import { SoftwareWalletAccountWithAddress } from '@app/store/accounts/account.models'; +import { Title } from '../typography'; + +interface AccountTitlePlaceholderProps extends BoxProps { + account: SoftwareWalletAccountWithAddress; +} +export function AccountTitlePlaceholder({ account, ...rest }: AccountTitlePlaceholderProps) { + const name = `Account ${account?.index + 1}`; + return ( + + {name} + + ); +} + +interface AccountTitleProps extends BoxProps { + account: SoftwareWalletAccountWithAddress; + name: string; +} +export function AccountTitle({ account, name, ...rest }: AccountTitleProps) { + return ( + + {name} + + ); +} diff --git a/src/app/features/account-picker/account-picker.layout.tsx b/src/app/features/account-picker/account-picker.layout.tsx new file mode 100644 index 00000000000..f5b5d9b903a --- /dev/null +++ b/src/app/features/account-picker/account-picker.layout.tsx @@ -0,0 +1,24 @@ +import { Flex, Stack, Text } from '@stacks/ui'; + +import { AppIcon } from '@app/components/app-icon'; +import { Title } from '@app/components/typography'; + +interface AccountPickerLayoutProps { + appName?: string; + children: React.ReactNode; +} +export function AccountPickerLayout(props: AccountPickerLayoutProps) { + const { appName, children } = props; + return ( + + + + + Choose an account + to connect to {appName} + + + {children} + + ); +} diff --git a/src/app/pages/choose-account/components/accounts.tsx b/src/app/features/account-picker/accounts.tsx similarity index 77% rename from src/app/pages/choose-account/components/accounts.tsx rename to src/app/features/account-picker/accounts.tsx index ee0f6e864dd..ee1445d270a 100644 --- a/src/app/pages/choose-account/components/accounts.tsx +++ b/src/app/features/account-picker/accounts.tsx @@ -1,10 +1,10 @@ -import { useCallback, Suspense, memo, useState, useMemo } from 'react'; +import { Suspense, memo, useMemo } from 'react'; import { FiPlusCircle } from 'react-icons/fi'; import { Virtuoso } from 'react-virtuoso'; import { Box, BoxProps, color, FlexProps, Spinner, Stack } from '@stacks/ui'; import { truncateMiddle } from '@stacks/ui-utils'; -import { Caption, Text, Title } from '@app/components/typography'; +import { Caption, Text } from '@app/components/typography'; import { useAccountDisplayName } from '@app/common/hooks/account/use-account-names'; import { useWallet } from '@app/common/hooks/use-wallet'; import { useOnboardingState } from '@app/common/hooks/auth/use-onboarding-state'; @@ -20,34 +20,11 @@ import { import { slugify } from '@app/common/utils'; import { useAccounts, useHasCreatedAccount } from '@app/store/accounts/account.hooks'; import { useAddressBalances } from '@app/query/balance/balance.hooks'; +import { AccountTitle, AccountTitlePlaceholder } from '@app/components/account/account-title'; const loadingProps = { color: '#A1A7B3' }; const getLoadingProps = (loading: boolean) => (loading ? loadingProps : {}); -interface AccountTitlePlaceholderProps extends BoxProps { - account: SoftwareWalletAccountWithAddress; -} -const AccountTitlePlaceholder = ({ account, ...rest }: AccountTitlePlaceholderProps) => { - const name = `Account ${account?.index + 1}`; - return ( - - {name} - - ); -}; - -interface AccountTitleProps extends BoxProps { - account: SoftwareWalletAccountWithAddress; - name: string; -} -const AccountTitle = ({ account, name, ...rest }: AccountTitleProps) => { - return ( - - {name} - - ); -}; - interface AccountItemProps extends FlexProps { selectedAddress?: string | null; isLoading: boolean; @@ -131,21 +108,14 @@ const AddAccountAction = memo(() => { ); }); -export const Accounts = memo(() => { - const { wallet, finishSignIn } = useWallet(); - const accounts = useAccounts(); - const { decodedAuthRequest } = useOnboardingState(); - const [selectedAccount, setSelectedAccount] = useState(null); - - const signIntoAccount = useCallback( - async (index: number) => { - setSelectedAccount(index); - await finishSignIn(index); - }, - [finishSignIn] - ); +interface AccountPickerProps { + selectedAccountIndex: number | null; + onAccountSelected(index: number): void; +} +export function AccountPicker(props: AccountPickerProps) { + const { onAccountSelected, selectedAccountIndex } = props; - if (!wallet || !accounts || !decodedAuthRequest) return null; + const accounts = useAccounts(); return ( <> @@ -158,12 +128,12 @@ export const Accounts = memo(() => { itemContent={(index, account) => ( onAccountSelected(index)} /> )} /> ); -}); +} diff --git a/src/app/pages/account-authentication/account-authentication.tsx b/src/app/pages/account-authentication/account-authentication.tsx new file mode 100644 index 00000000000..237d6e09a30 --- /dev/null +++ b/src/app/pages/account-authentication/account-authentication.tsx @@ -0,0 +1,45 @@ +import { memo, useCallback, useEffect, useState } from 'react'; + +import { useRouteHeader } from '@app/common/hooks/use-route-header'; +import { useWallet } from '@app/common/hooks/use-wallet'; +import { useAppDetails } from '@app/common/hooks/auth/use-app-details'; +import { Header } from '@app/components/header'; +import { AccountPicker } from '@app/features/account-picker/accounts'; +import { useOnboardingState } from '@app/common/hooks/auth/use-onboarding-state'; +import { useAccounts } from '@app/store/accounts/account.hooks'; +import { AccountPickerLayout } from '@app/features/account-picker/account-picker.layout'; + +export const AuthenticateAccount = memo(() => { + const accounts = useAccounts(); + const { name: appName } = useAppDetails(); + const { decodedAuthRequest } = useOnboardingState(); + const { cancelAuthentication, wallet, finishSignIn } = useWallet(); + const [selectedAccountIndex, setSelectedAccountIndex] = useState(null); + + useRouteHeader(
); + + const signIntoAccount = async (index: number) => { + setSelectedAccountIndex(index); + await finishSignIn(index); + }; + + const handleUnmount = useCallback(async () => { + cancelAuthentication(); + }, [cancelAuthentication]); + + useEffect(() => { + window.addEventListener('beforeunload', handleUnmount); + return () => window.removeEventListener('beforeunload', handleUnmount); + }, [handleUnmount]); + + if (!wallet || !accounts || !decodedAuthRequest) return null; + + return ( + + signIntoAccount(index)} + selectedAccountIndex={selectedAccountIndex} + /> + + ); +}); diff --git a/src/app/pages/choose-account-request/account-request.tsx b/src/app/pages/choose-account-request/account-request.tsx new file mode 100644 index 00000000000..a9c13d53f90 --- /dev/null +++ b/src/app/pages/choose-account-request/account-request.tsx @@ -0,0 +1,39 @@ +import { useRouteHeader } from '@app/common/hooks/use-route-header'; +import { useAppDetails } from '@app/common/hooks/auth/use-app-details'; +import { Header } from '@app/components/header'; +import { AccountPicker } from '@app/features/account-picker/accounts'; +import { useOnboardingState } from '@app/common/hooks/auth/use-onboarding-state'; +import { useAccounts } from '@app/store/accounts/account.hooks'; +import { AccountPickerLayout } from '@app/features/account-picker/account-picker.layout'; + +export function AccountRequest() { + const accounts = useAccounts(); + const { name: appName } = useAppDetails(); + const { decodedAuthRequest } = useOnboardingState(); + // const [selectedAccountIndex, setSelectedAccountIndex] = useState(null); + + useRouteHeader(
); + + const returnAccountDetailsToApp = async (index: number) => { + // setSelectedAccountIndex(index); + // await finishSignIn(index); + }; + + // const handleUnmount = useCallback(async () => { + // cancelAuthentication(); + // }, [cancelAuthentication]); + + // useEffect(() => { + // window.addEventListener('beforeunload', handleUnmount); + // return () => window.removeEventListener('beforeunload', handleUnmount); + // }, [handleUnmount]); + + return ( + + returnAccountDetailsToApp(index)} + selectedAccountIndex={0} + /> + + ); +} diff --git a/src/app/pages/choose-account/choose-account.tsx b/src/app/pages/choose-account/choose-account.tsx deleted file mode 100644 index eb2ef917cb1..00000000000 --- a/src/app/pages/choose-account/choose-account.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { memo, useCallback, useEffect } from 'react'; -import { Flex, Stack, Text } from '@stacks/ui'; - -import { useRouteHeader } from '@app/common/hooks/use-route-header'; -import { Title } from '@app/components/typography'; -import { AppIcon } from '@app/components/app-icon'; -import { useWallet } from '@app/common/hooks/use-wallet'; -import { useAppDetails } from '@app/common/hooks/auth/use-app-details'; -import { Header } from '@app/components/header'; -import { Accounts } from '@app/pages/choose-account/components/accounts'; - -export const ChooseAccount = memo(() => { - const { name: appName } = useAppDetails(); - const { cancelAuthentication } = useWallet(); - - useRouteHeader(
); - - const handleUnmount = useCallback(async () => { - cancelAuthentication(); - }, [cancelAuthentication]); - - useEffect(() => { - window.addEventListener('beforeunload', handleUnmount); - return () => window.removeEventListener('beforeunload', handleUnmount); - }, [handleUnmount]); - - return ( - - - - - Choose an account - to connect to {appName} - - - - - ); -}); diff --git a/src/app/routes/app-routes.tsx b/src/app/routes/app-routes.tsx index cc9e90159d7..1e33e4061b9 100644 --- a/src/app/routes/app-routes.tsx +++ b/src/app/routes/app-routes.tsx @@ -5,7 +5,7 @@ import { useAnalytics } from '@app/common/hooks/analytics/use-analytics'; import { Container } from '@app/components/container/container'; import { LoadingSpinner } from '@app/components/loading-spinner'; import { MagicRecoveryCode } from '@app/pages/onboarding/magic-recovery-code/magic-recovery-code'; -import { ChooseAccount } from '@app/pages/choose-account/choose-account'; +import { AuthenticateAccount } from '@app/pages/account-authentication/account-authentication'; import { TransactionRequest } from '@app/pages/transaction-request/transaction-request'; import { SignIn } from '@app/pages/onboarding/sign-in/sign-in'; import { ReceiveTokens } from '@app/pages/receive-tokens/receive-tokens'; @@ -112,7 +112,7 @@ export function AppRoutes(): JSX.Element | null { element={ }> - + } diff --git a/src/background/legacy-external-message-handler.ts b/src/background/legacy-external-message-handler.ts index bbf468bd6e4..dfbcfa1c962 100644 --- a/src/background/legacy-external-message-handler.ts +++ b/src/background/legacy-external-message-handler.ts @@ -17,6 +17,8 @@ async function openRequestInFullPage(path: string, urlParams: URLSearchParams) { export function inferLegacyMessage(message: any): message is LegacyMessageFromContentScript { // Now that we use a RPC communication style, we can infer // legacy message types by presence of an id + // Types incorrectly state this is `undefined` + // @ts-ignore return !Object.hasOwn(message, 'id'); } diff --git a/src/inpage/inpage.ts b/src/inpage/inpage.ts index 0253411f553..f9e1937249d 100644 --- a/src/inpage/inpage.ts +++ b/src/inpage/inpage.ts @@ -155,3 +155,9 @@ declare global { randomUUID: () => string; } } + +// declare global { +// interface Window { +// StacksProvider?: Provider; +// } +// }