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
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ import {
BackendUnavailableStory,
UnsupportedChainStory,
AppKitConnectWalletStory,
MultiBuyerManageStory,
DeepLinkBuyerStory,
MultiBuyerHistoryStory,
} from '../helpers/aiCreditsWidgetStories'

const meta: Meta<typeof AiCreditsWidget> = {
Expand Down Expand Up @@ -86,6 +89,21 @@ export const UnsupportedChain: Story = {
render: () => <UnsupportedChainStory />,
}

/** Multi-buyer manage tab: buyer selector and private-key reveal. */
export const MultiBuyerManage: Story = {
render: () => <MultiBuyerManageStory />,
}

/** Deep-link partner buyer: consent via pre-signed operatorSignature. */
export const DeepLinkBuyer: Story = {
render: () => <DeepLinkBuyerStory />,
}

/** History tab with buyer filter dropdown. */
export const MultiBuyerHistory: Story = {
render: () => <MultiBuyerHistoryStory />,
}

export const AppKitConnectWallet: Story = {
render: () => <AppKitConnectWalletStory />,
play: async ({ canvasElement }) => {
Expand Down
86 changes: 86 additions & 0 deletions examples/storybook/src/stories/helpers/aiCreditsWidgetStories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ function createMockState(
isGoodIdVerified: false,
buyerPubKey: null,
buyerPrvKey: null,
operatorSignature: null,
operatorConsented: false,
operatorAddress: null,
minDepositUsd: '1.00',
Expand All @@ -45,6 +46,8 @@ function createMockState(
streamBonusPercent: 20,
error: null,
activeTab: 'buy',
buyers: [],
activeBuyerAddress: null,
}
return { ...base, ...overrides }
}
Expand All @@ -59,6 +62,9 @@ function createAdapterFactory(
connect: async () => {},
switchChain: async () => {},
generateBuyerKey: async () => {},
selectBuyer: () => {},
importBuyerFromPrivateKey: async () => {},
applyDeepLinkBuyer: async () => {},
signOperatorConsent: async () => {},
syncOperatorConsentFromChain: async () => {},
buildQuote: async (depositG, streamG) => ({
Expand Down Expand Up @@ -399,3 +405,83 @@ export function InjectedWalletStory() {
</YStack>
)
}

// ---------------------------------------------------------------------------
// Multi-buyer fixture stories
// ---------------------------------------------------------------------------

const BUYER_WALLET = {
address: '0xfc128652c9b397a1f89A9EC84E798B869B0E4c7a' as const,
privateKey: '0x0000000000000000000000000000000000000000000000000000000000000001' as const,
}

const BUYER_IMPORTED = {
address: '0xAbcDef1234567890AbcDef1234567890AbcDef12' as const,
privateKey: '0x0000000000000000000000000000000000000000000000000000000000000002' as const,
}

const BUYER_PARTNER = {
address: '0x1111111111111111111111111111111111111111' as const,
operatorSignature:
'0x1111111111111111111111111111111111111111111111111111111111111111222222222222222222222222222222222222222222222222222222222222222200' as const,
}

/** Multi-buyer manage: backend address list with one selected buyer that has a local key. */
export function MultiBuyerManageStory() {
return (
<MockStoryShell
dataTestId="AiCreditsWidget-multi-buyer-manage"
adapterFactory={createAdapterFactory('quote_ready', {
totalCreditUsd: '110000000',
buyerPubKey: BUYER_WALLET.address,
buyerPrvKey: BUYER_WALLET.privateKey,
operatorConsented: true,
operatorAddress: '0x0000000000000000000000000000000000000004',
totalGdDepositedG: '50.00',
monthlyStreamG: '5.00',
gBalance: '42.50',
activeTab: 'manage',
buyers: [BUYER_WALLET.address, BUYER_IMPORTED.address, BUYER_PARTNER.address],
activeBuyerAddress: BUYER_WALLET.address,
})}
/>
)
}

/** Deep-link partner buyer: consent uses pre-signed operatorSignature (no private key). */
export function DeepLinkBuyerStory() {
return (
<MockStoryShell
dataTestId="AiCreditsWidget-deep-link-buyer"
adapterFactory={createAdapterFactory('purchase_setup', {
buyerPubKey: BUYER_PARTNER.address,
buyerPrvKey: null,
operatorSignature: BUYER_PARTNER.operatorSignature,
operatorConsented: false,
gBalance: '42.50',
activeTab: 'manage',
buyers: [BUYER_PARTNER.address],
activeBuyerAddress: BUYER_PARTNER.address,
})}
/>
)
}

/** History tab with multi-buyer filter options available. */
export function MultiBuyerHistoryStory() {
return (
<MockStoryShell
dataTestId="AiCreditsWidget-multi-buyer-history"
adapterFactory={createAdapterFactory('quote_ready', {
totalCreditUsd: '110000000',
buyerPubKey: BUYER_WALLET.address,
buyerPrvKey: BUYER_WALLET.privateKey,
operatorConsented: true,
gBalance: '42.50',
activeTab: 'history',
buyers: [BUYER_WALLET.address, BUYER_IMPORTED.address],
activeBuyerAddress: BUYER_WALLET.address,
})}
/>
)
}
17 changes: 16 additions & 1 deletion packages/ai-credits-widget/src/AiCreditsWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,15 @@ function BuyCreditsPanel({ state, actions, isPending, onPay }: BuyPanelProps) {
} else {
content = (
<>
{state.error && (
<AiCreditsStatusNotice>
<Text color="$error" fontWeight="700">
Deep link unavailable
</Text>
<Text secondary>{state.error}</Text>
</AiCreditsStatusNotice>
)}

{state.address && (
<AiCreditsHero
gBalance={state.gBalance}
Expand Down Expand Up @@ -314,6 +323,8 @@ function AiCreditsInner({
const history = useAiCreditsHistory({
address: state.address,
backendUrl,
// Default to the active buyer so users see filtered history immediately
defaultBuyerFilter: state.activeBuyerAddress ?? 'all',
environment,
backendClient: adapterOptions?.backendClient,
})
Expand Down Expand Up @@ -387,7 +398,11 @@ function AiCreditsInner({
{state.activeTab === 'manage' ? (
<ManagePanel state={state} actions={actions} />
) : state.activeTab === 'history' ? (
<HistoryTab state={history.state} actions={history.actions} />
<HistoryTab
state={history.state}
actions={history.actions}
knownBuyers={state.buyers.map((address) => ({ address }))}
/>
) : (
buyPanel
)}
Expand Down
Loading
Loading