Skip to content

Commit

Permalink
Merge pull request #334 from tonkeeper/feature/choose-ton-connect-acc…
Browse files Browse the repository at this point in the history
…ount

Select ton connect account for connection
  • Loading branch information
KuznetsovNikita authored Dec 16, 2024
2 parents 11f3b15 + 6ff233f commit 4394df7
Show file tree
Hide file tree
Showing 9 changed files with 242 additions and 77 deletions.
13 changes: 11 additions & 2 deletions apps/desktop/src/app/components/DeepLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
import { useEffect, useState } from 'react';
import { sendBackground } from '../../libs/backgroudService';
import { TonConnectMessage } from '../../libs/message';
import { Account } from '@tonkeeper/core/dist/entries/account';
import { WalletId } from '@tonkeeper/core/dist/entries/wallet';

export const DeepLinkSubscription = () => {
const [params, setParams] = useState<TonConnectParams | null>(null);
Expand All @@ -16,11 +18,18 @@ export const DeepLinkSubscription = () => {
const { mutateAsync: responseConnectionAsync, reset: responseReset } =
useResponseConnectionMutation();

const handlerClose = async (replyItems?: ConnectItemReply[], manifest?: DAppManifest) => {
const handlerClose = async (
result: {
replyItems: ConnectItemReply[];
manifest: DAppManifest;
account: Account;
walletId: WalletId;
} | null
) => {
if (!params) return;
responseReset();
try {
await responseConnectionAsync({ params, replyItems, manifest });
await responseConnectionAsync({ params, result });
} finally {
setParams(null);
sendBackground({ king: 'reconnect' } as TonConnectMessage);
Expand Down
15 changes: 11 additions & 4 deletions apps/extension/src/components/Notifications.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { ConnectItemReply } from '@tonkeeper/core/dist/entries/tonConnect';
import { ConnectItemReply, DAppManifest } from "@tonkeeper/core/dist/entries/tonConnect";
import { delay } from '@tonkeeper/core/dist/utils/common';
import { TonConnectNotification } from '@tonkeeper/uikit/dist/components/connect/TonConnectNotification';
import { TonTransactionNotification } from '@tonkeeper/uikit/dist/components/connect/TonTransactionNotification';
import { useNotificationAnalytics } from '@tonkeeper/uikit/dist/hooks/amplitude';
import { useCallback, useEffect, useState } from 'react';
import { askBackground, sendBackground } from '../event';
import { NotificationData } from '../libs/event';
import { Account } from "@tonkeeper/core/dist/entries/account";
import { WalletId } from "@tonkeeper/core/dist/entries/wallet";

export const Notifications = () => {
const [data, setData] = useState<NotificationData | undefined>(undefined);
Expand Down Expand Up @@ -42,10 +44,15 @@ export const Notifications = () => {
<TonConnectNotification
origin={data?.origin}
params={data?.kind === 'tonConnectRequest' ? data.data : null}
handleClose={(payload?: ConnectItemReply[]) => {
handleClose={(result: {
replyItems: ConnectItemReply[];
manifest: DAppManifest;
account: Account;
walletId: WalletId;
} | null) => {
if (!data) return;
if (payload) {
sendBackground.message('approveRequest', { id: data.id, payload });
if (result) {
sendBackground.message('approveRequest', { id: data.id, payload: result });
} else {
sendBackground.message('rejectRequest', data.id);
}
Expand Down
13 changes: 11 additions & 2 deletions apps/tablet/src/app/components/DeepLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
} from '@tonkeeper/uikit/dist/components/connect/connectHook';
import { useEffect, useState } from 'react';
import { subscribeToTonOrTonConnectUrlOpened, tonConnectSSE } from "../../libs/tonConnect";
import { Account } from "@tonkeeper/core/dist/entries/account";
import { WalletId } from "@tonkeeper/core/dist/entries/wallet";

export const DeepLinkSubscription = () => {
const [params, setParams] = useState<TonConnectParams | null>(null);
Expand All @@ -15,11 +17,18 @@ export const DeepLinkSubscription = () => {
const { mutateAsync: responseConnectionAsync, reset: responseReset } =
useResponseConnectionMutation();

const handlerClose = async (replyItems?: ConnectItemReply[], manifest?: DAppManifest) => {
const handlerClose = async (
result: {
replyItems: ConnectItemReply[];
manifest: DAppManifest;
account: Account;
walletId: WalletId;
} | null
) => {
if (!params) return;
responseReset();
try {
await responseConnectionAsync({ params, replyItems, manifest });
await responseConnectionAsync({ params, result });
} finally {
setParams(null);
await tonConnectSSE.reconnect();
Expand Down
13 changes: 11 additions & 2 deletions apps/web/src/components/UrlTonConnectSubscription.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
import { useEffect, useState } from 'react';
import { useLocation, useNavigate } from "react-router-dom";
import { AppRoute } from "@tonkeeper/uikit/dist/libs/routes";
import { Account } from "@tonkeeper/core/dist/entries/account";
import { WalletId } from "@tonkeeper/core/dist/entries/wallet";

const TON_CONNECT_TRIGGER_PATH = '/ton-connect';

Expand All @@ -19,11 +21,18 @@ export const UrlTonConnectSubscription = () => {
useResponseConnectionMutation();


const handlerClose = async (replyItems?: ConnectItemReply[], manifest?: DAppManifest) => {
const handlerClose = async (
result: {
replyItems: ConnectItemReply[];
manifest: DAppManifest;
account: Account;
walletId: WalletId;
} | null
) => {
if (!params) return;
responseReset();
try {
await responseConnectionAsync({ params, replyItems, manifest });
await responseConnectionAsync({ params, result });
} finally {
setParams(null);
}
Expand Down
14 changes: 11 additions & 3 deletions packages/core/src/service/tonConnect/connectService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,9 @@ import {
TonConnectAccount,
TonProofItemReplySuccess
} from '../../entries/tonConnect';
import { isStandardTonWallet, TonContract } from '../../entries/wallet';
import { isStandardTonWallet, TonContract, WalletId } from '../../entries/wallet';
import { TonWalletStandard, WalletVersion } from '../../entries/wallet';
import { accountsStorage } from '../accountsStorage';
import { getDevSettings } from '../devStorage';
import { walletContractFromState } from '../wallet/contractService';
import {
AccountConnection,
Expand Down Expand Up @@ -403,8 +402,17 @@ export const saveWalletTonConnect = async (options: {
replyItems: ConnectItemReply[];
appVersion: string;
webViewUrl?: string;
walletId?: WalletId;
}): Promise<ConnectEvent> => {
const wallet = options.account.activeTonWallet;
const wallet =
options.walletId !== undefined
? options.account.getTonWallet(options.walletId)
: options.account.activeTonWallet;

if (!wallet) {
throw new Error('Missing wallet');
}

await saveAccountConnection({
storage: options.storage,
wallet,
Expand Down
13 changes: 11 additions & 2 deletions packages/uikit/src/components/connect/ScanButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { useAppSdk } from '../../hooks/appSdk';
import { ScanIcon } from '../Icon';
import { TonConnectNotification } from './TonConnectNotification';
import { useResponseConnectionMutation, useGetConnectInfo } from './connectHook';
import { Account } from '@tonkeeper/core/dist/entries/account';
import { WalletId } from '@tonkeeper/core/dist/entries/wallet';

const ScanBlock = styled.div`
position: absolute;
Expand All @@ -32,11 +34,18 @@ export const ScanButton = () => {
[setParams, mutateAsync]
);

const handlerClose = async (replyItems?: ConnectItemReply[], manifest?: DAppManifest) => {
const handlerClose = async (
result: {
replyItems: ConnectItemReply[];
manifest: DAppManifest;
account: Account;
walletId: WalletId;
} | null
) => {
if (!params) return;
responseReset();
try {
await responseConnectionAsync({ params, replyItems, manifest });
await responseConnectionAsync({ params, result });
} finally {
setParams(null);
}
Expand Down
128 changes: 115 additions & 13 deletions packages/uikit/src/components/connect/TonConnectNotification.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,24 @@ import {
DAppManifest
} from '@tonkeeper/core/dist/entries/tonConnect';
import { getManifest } from '@tonkeeper/core/dist/service/tonConnect/connectService';
import React, { FC, useCallback, useEffect, useState } from 'react';
import React, { FC, useCallback, useEffect, useMemo, useState } from 'react';
import styled from 'styled-components';
import { useAppSdk } from '../../hooks/appSdk';
import { useTranslation } from '../../hooks/translation';
import { TxConfirmationCustomError } from '../../libs/errors/TxConfirmationCustomError';
import { QueryKey } from '../../libs/queryKey';
import { useIsActiveWalletLedger } from '../../state/ledger';
import { useConnectTonConnectAppMutation } from '../../state/tonConnect';
import { useIsActiveWalletWatchOnly } from '../../state/wallet';
import { CheckmarkCircleIcon, ExclamationMarkCircleIcon } from '../Icon';
import { useAccountsState, useActiveAccount } from '../../state/wallet';
import { CheckmarkCircleIcon, ExclamationMarkCircleIcon, SwitchIcon } from '../Icon';
import { Notification, NotificationBlock } from '../Notification';
import { Body2, Body3, H2, Label2 } from '../Text';
import { AccountAndWalletInfo } from '../account/AccountAndWalletInfo';
import { Button } from '../fields/Button';
import { ResultButton } from '../transfer/common';
import { SelectDropDown, SelectDropDownHost, SelectField } from '../fields/Select';
import { DropDownContent, DropDownItem, DropDownItemsDivider } from '../DropDown';
import { Account } from '@tonkeeper/core/dist/entries/account';
import { WalletId } from '@tonkeeper/core/dist/entries/wallet';

const Title = styled(H2)`
text-align: center;
Expand Down Expand Up @@ -75,10 +78,22 @@ const ConnectContent: FC<{
origin?: string;
params: ConnectRequest;
manifest: DAppManifest;
handleClose: (result?: ConnectItemReply[], manifest?: DAppManifest) => void;
handleClose: (
result: {
replyItems: ConnectItemReply[];
manifest: DAppManifest;
account: Account;
walletId: WalletId;
} | null
) => void;
}> = ({ params, manifest, origin, handleClose }) => {
const activeIsLedger = useIsActiveWalletLedger();
const isReadOnly = useIsActiveWalletWatchOnly();
const activeAccount = useActiveAccount();
const [selectedAccountAndWallet, setSelectedAccountAndWallet] = useState<{
account: Account;
walletId: WalletId;
}>({ account: activeAccount, walletId: activeAccount.activeTonWallet.id });

const isReadOnly = selectedAccountAndWallet.account.type === 'watch-only';

const sdk = useAppSdk();
const [done, setDone] = useState(false);
Expand All @@ -97,9 +112,22 @@ const ConnectContent: FC<{
const onSubmit: React.FormEventHandler<HTMLFormElement> = async e => {
e.preventDefault();
try {
const result = await mutateAsync({ request: params, manifest, webViewUrl: origin });
const replyItems = await mutateAsync({
request: params,
manifest,
webViewUrl: origin,
...selectedAccountAndWallet
});
setDone(true);
setTimeout(() => handleClose(result, manifest), 300);
setTimeout(
() =>
handleClose({
replyItems,
manifest,
...selectedAccountAndWallet
}),
300
);
} catch (err) {
setDone(true);
setError(err as Error);
Expand All @@ -114,7 +142,8 @@ const ConnectContent: FC<{
}

const tonProofRequested = params.items.some(item => item.name === 'ton_proof');
const cantConnectLedger = activeIsLedger && tonProofRequested;
const cantConnectLedger =
selectedAccountAndWallet.account.type === 'ledger' && tonProofRequested;

return (
<NotificationBlock onSubmit={onSubmit}>
Expand All @@ -127,10 +156,14 @@ const ConnectContent: FC<{
<Title>{t('ton_login_title_web').replace('%{name}', shortUrl)}</Title>
<SubTitle>
{t('ton_login_caption').replace('%{name}', getDomain(manifest.name))}{' '}
<AccountAndWalletInfo />
</SubTitle>
</div>

<SelectAccountDropDown
selectedAccountAndWallet={selectedAccountAndWallet}
onSelect={setSelectedAccountAndWallet}
/>

<>
{done && !error && (
<ResultButton done>
Expand Down Expand Up @@ -170,6 +203,68 @@ const ConnectContent: FC<{
);
};

const SelectAccountDropDown: FC<{
className?: string;
selectedAccountAndWallet: { account: Account; walletId: WalletId };
onSelect: (accountAndWallet: { account: Account; walletId: WalletId }) => void;
}> = ({ selectedAccountAndWallet, className, onSelect }) => {
const accounts = useAccountsState();
const accountsAndWallets = useMemo(() => {
return accounts.flatMap(account =>
account.allTonWallets.map(w => ({
account,
walletId: w.id
}))
);
}, [accounts]);

return (
<SelectDropDown
right="0"
top="-64px"
width="min(calc(var(--app-width) - 2rem), 400px)"
payload={onClose => (
<DropDownContent>
{accountsAndWallets.map(accountAndWallet => (
<>
<DropDownItem
key={`${accountAndWallet.account.id}-${accountAndWallet.walletId}`}
isSelected={
accountAndWallet.account.id ===
selectedAccountAndWallet.account.id &&
accountAndWallet.walletId === selectedAccountAndWallet.walletId
}
onClick={() => {
onClose();
onSelect?.(accountAndWallet);
}}
>
<AccountAndWalletInfo
account={accountAndWallet.account}
walletId={accountAndWallet.walletId}
noPrefix
/>
</DropDownItem>
<DropDownItemsDivider />
</>
))}
</DropDownContent>
)}
>
<SelectField className={className}>
<SelectDropDownHost>
<AccountAndWalletInfo
account={selectedAccountAndWallet.account}
walletId={selectedAccountAndWallet.walletId}
noPrefix
/>
<SwitchIcon />
</SelectDropDownHost>
</SelectField>
</SelectDropDown>
);
};

const useManifest = (params: ConnectRequest | null) => {
const sdk = useAppSdk();
const { t } = useTranslation();
Expand Down Expand Up @@ -203,7 +298,14 @@ const useManifest = (params: ConnectRequest | null) => {
export const TonConnectNotification: FC<{
origin?: string;
params: ConnectRequest | null;
handleClose: (result?: ConnectItemReply[], manifest?: DAppManifest) => void;
handleClose: (
result: {
replyItems: ConnectItemReply[];
manifest: DAppManifest;
account: Account;
walletId: WalletId;
} | null
) => void;
}> = ({ params, origin, handleClose }) => {
const { data: manifest } = useManifest(params);

Expand All @@ -220,7 +322,7 @@ export const TonConnectNotification: FC<{
}, [origin, params, manifest, handleClose]);

return (
<Notification isOpen={manifest != null} handleClose={() => handleClose()}>
<Notification isOpen={manifest != null} handleClose={() => handleClose(null)}>
{Content}
</Notification>
);
Expand Down
Loading

0 comments on commit 4394df7

Please sign in to comment.