Skip to content
Open
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
527 changes: 527 additions & 0 deletions apps/demo-wallet/src/components/IntentRequestModal.tsx

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions apps/demo-wallet/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export { SettingsDropdown } from './SettingsDropdown';
export { ProtectedRoute } from './ProtectedRoute';
export { RecentTransactions } from './RecentTransactions';
export { SignDataRequestModal } from './SignDataRequestModal';
export { IntentRequestModal, BatchedIntentRequestModal } from './IntentRequestModal';
export { TraceRow } from './TraceRow';
export { TransactionRequestModal } from './TransactionRequestModal';
export { WalletPreview } from './WalletPreview';
Expand Down
83 changes: 72 additions & 11 deletions apps/demo-wallet/src/pages/WalletDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@

import React, { useState, useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import { useWallet, useWalletKit, useTonConnect, useTransactionRequests, useSignDataRequests } from '@demo/wallet-core';
import {
useWallet,
useWalletKit,
useTonConnect,
useTransactionRequests,
useSignDataRequests,
useIntents,
} from '@demo/wallet-core';

import {
Layout,
Expand All @@ -17,6 +24,8 @@ import {
ConnectRequestModal,
TransactionRequestModal,
SignDataRequestModal,
IntentRequestModal,
BatchedIntentRequestModal,
DisconnectNotifications,
NftsCard,
RecentTransactions,
Expand Down Expand Up @@ -62,10 +71,33 @@ export const WalletDashboard: React.FC = () => {
const { pendingTransactionRequest, isTransactionModalOpen } = useTransactionRequests();
const { pendingSignDataRequest, isSignDataModalOpen, approveSignDataRequest, rejectSignDataRequest } =
useSignDataRequests();
const {
pendingIntentEvent,
pendingBatchedIntentEvent,
isIntentModalOpen,
isBatchedIntentModalOpen,
handleIntentUrl,
isIntentUrl,
approveIntent,
rejectIntent,
approveBatchedIntent,
rejectBatchedIntent,
} = useIntents();
const { error } = useTonWallet();

// Use the paste handler hook
usePasteHandler(handleTonConnectUrl);
// Use the paste handler hook — route intent URLs to handleIntentUrl
const handlePastedUrl = useCallback(
async (url: string) => {
if (isIntentUrl(url)) {
log.info('Detected pasted intent URL, routing to intent handler');
await handleIntentUrl(url);
} else {
await handleTonConnectUrl(url);
}
},
[isIntentUrl, handleIntentUrl, handleTonConnectUrl],
);
usePasteHandler(handlePastedUrl);

const handleRefreshBalance = useCallback(async () => {
setIsRefreshing(true);
Expand Down Expand Up @@ -93,17 +125,22 @@ export const WalletDashboard: React.FC = () => {
const handleConnectDApp = useCallback(async () => {
if (!tonConnectUrl.trim()) return;

const url = tonConnectUrl.trim();
setIsConnecting(true);
try {
await handleTonConnectUrl(tonConnectUrl.trim());
if (isIntentUrl(url)) {
log.info('Detected intent URL, routing to intent handler');
await handleIntentUrl(url);
} else {
await handleTonConnectUrl(url);
}
setTonConnectUrl('');
} catch (err) {
log.error('Failed to connect to dApp:', err);
// TODO: Show error message to user
log.error('Failed to process URL:', err);
} finally {
setIsConnecting(false);
}
}, [tonConnectUrl, handleTonConnectUrl]);
}, [tonConnectUrl, handleTonConnectUrl, isIntentUrl, handleIntentUrl]);

const handleTestDisconnectAll = useCallback(async () => {
if (!walletKit) return;
Expand Down Expand Up @@ -286,18 +323,18 @@ export const WalletDashboard: React.FC = () => {
<NftsCard />

{/* TON Connect URL Input */}
<Card title="Connect to dApp">
<Card title="Connect to dApp / Handle Intent">
<div className="space-y-4">
<div>
<label htmlFor="tonconnect-url" className="block text-sm font-medium text-gray-700 mb-2">
Paste TON Connect Link
Paste TON Connect or Intent Link
</label>
<textarea
data-testid="tonconnect-url"
id="tonconnect-url"
rows={3}
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 resize-none text-black"
placeholder="tc://... or ton://... or https://..."
placeholder="tc://... or ton://... or https://... or intent URL"
value={tonConnectUrl}
onChange={(e) => setTonConnectUrl(e.target.value)}
/>
Expand All @@ -309,7 +346,9 @@ export const WalletDashboard: React.FC = () => {
disabled={!tonConnectUrl.trim() || isConnecting}
className="w-full"
>
Connect to dApp
{tonConnectUrl.trim() && isIntentUrl(tonConnectUrl.trim())
? 'Process Intent'
: 'Connect to dApp'}
</Button>
</div>
</Card>
Expand Down Expand Up @@ -382,6 +421,28 @@ export const WalletDashboard: React.FC = () => {
onReject={rejectSignDataRequest}
/>
)}

{/* Intent Request Modal */}
{pendingIntentEvent && (
<IntentRequestModal
event={pendingIntentEvent}
savedWallets={savedWallets}
isOpen={isIntentModalOpen}
onApprove={approveIntent}
onReject={rejectIntent}
/>
)}

{/* Batched Intent Request Modal */}
{pendingBatchedIntentEvent && (
<BatchedIntentRequestModal
batch={pendingBatchedIntentEvent}
savedWallets={savedWallets}
isOpen={isBatchedIntentModalOpen}
onApprove={approveBatchedIntent}
onReject={rejectBatchedIntent}
/>
)}
</Layout>
);
};
24 changes: 24 additions & 0 deletions demo/wallet-core/src/hooks/useWalletStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,3 +229,27 @@ export const useSwap = () => {
})),
);
};

/**
* Hook for Intent state and actions
*/
export const useIntents = () => {
return useWalletStore(
useShallow((state) => ({
pendingIntentEvent: state.intent.pendingIntentEvent,
pendingBatchedIntentEvent: state.intent.pendingBatchedIntentEvent,
isIntentModalOpen: state.intent.isIntentModalOpen,
isBatchedIntentModalOpen: state.intent.isBatchedIntentModalOpen,
intentResult: state.intent.intentResult,
intentError: state.intent.intentError,
handleIntentUrl: state.handleIntentUrl,
isIntentUrl: state.isIntentUrl,
approveIntent: state.approveIntent,
rejectIntent: state.rejectIntent,
approveBatchedIntent: state.approveBatchedIntent,
rejectBatchedIntent: state.rejectBatchedIntent,
closeIntentModal: state.closeIntentModal,
closeBatchedIntentModal: state.closeBatchedIntentModal,
})),
);
};
2 changes: 2 additions & 0 deletions demo/wallet-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export {
useNfts,
useJettons,
useSwap,
useIntents,
} from './hooks/useWalletStore';
export { useFormattedTonBalance, useFormattedAmount } from './hooks/useFormattedBalance';
export { useWalletInitialization } from './hooks/useWalletInitialization';
Expand All @@ -42,6 +43,7 @@ export type {
WalletCoreSlice,
WalletManagementSlice,
TonConnectSlice,
IntentSlice,
JettonsSlice,
NftsSlice,
SwapSlice,
Expand Down
4 changes: 4 additions & 0 deletions demo/wallet-core/src/store/createWalletStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { createTonConnectSlice } from './slices/tonConnectSlice';
import { createJettonsSlice } from './slices/jettonsSlice';
import { createNftsSlice } from './slices/nftsSlice';
import { createSwapSlice } from './slices/swapSlice';
import { createIntentSlice } from './slices/intentSlice';
import type { AppState } from '../types/store';
import type { StorageAdapter } from '../adapters/storage/types';
import type { WalletKitConfig } from '../types/wallet';
Expand Down Expand Up @@ -141,6 +142,9 @@ export function createWalletStore(options: CreateWalletStoreOptions = {}) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
...createSwapSlice(...a),
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
...createIntentSlice(...a),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
})) as unknown as any,
{
Expand Down
Loading
Loading