Skip to content

Commit

Permalink
feat: pay ui
Browse files Browse the repository at this point in the history
  • Loading branch information
samsiegart committed May 22, 2024
1 parent 8d801d9 commit fd97f3b
Show file tree
Hide file tree
Showing 14 changed files with 148 additions and 20 deletions.
1 change: 1 addition & 0 deletions contract/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
start-sell-concert-tickets-permit.json
start-sell-concert-tickets.js
bundles/
,tx.json
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type { PurseJSONState } from '@agoric/react-components';
import type { DisplayInfoForBrand } from '../../store/displayInfo';
import type { DisplayInfoForBrand } from '../store/displayInfo';
import { stringifyValue, type AssetKind } from '@agoric/web-components';
import type { Amount } from '@agoric/ertp/src/types';
import { isCopyBagValue } from '@agoric/ertp';
import { useEffect, useRef, useState } from 'react';
import { stringifyData } from '../../utils/stringify';
import { stringifyData } from '../utils/stringify';

export const PurseValue = ({
purse,
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { AmountInput, type PurseJSONState } from '@agoric/react-components';
import type { Amount, AssetKind } from '@agoric/web-components';
import { useState } from 'react';
import { CopyBagEntry, PurseValue, SetEntry } from './DisplayAmount';
import { stringifyData } from '../../utils/stringify';
import { stringifyData } from '../utils/stringify';
import { makeCopyBag } from '@endo/patterns';

type Props = {
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Amount } from '@agoric/web-components';
import { useDisplayInfo } from '../../store/displayInfo';
import { useDisplayInfo } from '../store/displayInfo';
import { AmountValue } from './DisplayAmount';

type Props = {
Expand Down
3 changes: 2 additions & 1 deletion ui/src/components/Tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { TabWrapper } from './TabWrapper';
import { Notifications } from './Notifications';
import { NotificationContext } from '../context/NotificationContext';
import Swap from './swap/Swap';
import Pay from './pay/Pay';

// notification related types
const dynamicToastChildStatuses = [
Expand Down Expand Up @@ -65,7 +66,7 @@ const Tabs = () => {
activeTab={activeTab}
handleTabClick={handleTabClick}
>
<div>TBD</div>
<Pay />
</TabWrapper>
<TabWrapper
tab="Vote"
Expand Down
137 changes: 137 additions & 0 deletions ui/src/components/pay/Pay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import { useAgoric } from '@agoric/react-components';
import ProposalAmountsBox from '../ProposalAmountsBox';
import RecipientInput from '../RecipientInput';
import { queryPurses } from '../../utils/queryPurses';
import { useContext, useEffect, useState } from 'react';
import type { Amount } from '@agoric/web-components';
import { useDisplayInfo } from '../../store/displayInfo';
import { NotificationContext } from '../../context/NotificationContext';

const Pay = () => {
const { addNotification } = useContext(NotificationContext);
const { purses, chainStorageWatcher, makeOffer } = useAgoric();
const [recipientAddr, setRecipientAddr] = useState('');
const [recipientError, setRecipientError] = useState('');
const [myAmounts, setMyAmounts] = useState<Amount[]>([]);
const { brandToDisplayInfo } = useDisplayInfo(({ brandToDisplayInfo }) => ({
brandToDisplayInfo,
}));

useEffect(() => {
let isCancelled = false;
const checkRecipientSmartWallet = async () => {
if (chainStorageWatcher && recipientAddr) {
try {
await queryPurses(chainStorageWatcher, recipientAddr);
} catch (e) {
if (!isCancelled) {
setRecipientError('Failed to fetch recipient wallet.');
}
}
}
};

if (!recipientAddr.length) {
setRecipientError('');
} else if (
recipientAddr.startsWith('agoric') &&
recipientAddr.length === 45
) {
setRecipientError('');
checkRecipientSmartWallet();
} else {
setRecipientError('Invalid address format');
}

return () => {
isCancelled = true;
};
}, [chainStorageWatcher, recipientAddr]);

const sendOffer = async () => {
assert(chainStorageWatcher && makeOffer);
try {
const invitationSpec = {
source: 'agoricContract',
instancePath: ['postalService'],
callPipe: [['makeSendInvitation', [recipientAddr]]],
};

const gives = myAmounts.map(amount => {
const { petname } = brandToDisplayInfo.get(amount.brand)!;
return [petname, amount];
});
const proposal = {
give: { ...Object.fromEntries(gives) },
want: {},
};

makeOffer(
invitationSpec,
proposal,
undefined,
(update: { status: string; data?: unknown }) => {
if (update.status === 'error') {
addNotification!({
text: `Payment Error: ${update.data}`,
status: 'error',
});
}
if (update.status === 'accepted') {
addNotification!({
text: 'Payment Sent',
status: 'success',
});
}
if (update.status === 'refunded') {
addNotification!({
text: 'Payment Refunded',
status: 'warning',
});
}
},
);
} catch (e) {
addNotification!({
text: `Offer error: ${e}`,
status: 'error',
});
}
};

const isButtonDisabled = !makeOffer || !recipientAddr || !myAmounts.length;

return (
<div className="items-top flex w-full flex-col justify-around lg:flex-row">
<div>
<h2 className="daisyui-card-title mb-2 w-full">Send Payment</h2>
<div className="daisyui-card h-fit w-96 bg-base-300 px-4 py-4 shadow-xl">
<RecipientInput
address={recipientAddr}
onChange={addr => setRecipientAddr(addr)}
error={recipientError}
/>
<div className="my-1">
<h2 className="mb-2 text-lg font-medium">Give</h2>
<ProposalAmountsBox
actionLabel="Add from Your Purse"
amounts={myAmounts}
purses={purses}
onChange={setMyAmounts}
warning={purses ? undefined : 'Wallet Not Connected'}
/>
</div>
<button
onClick={sendOffer}
disabled={isButtonDisabled}
className="daisyui-btn daisyui-btn-primary mt-4 w-full self-center text-lg"
>
Send Payment
</button>
</div>
</div>
</div>
);
};

export default Pay;
2 changes: 1 addition & 1 deletion ui/src/components/swap/FeeInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Amount } from '@agoric/web-components';
import { AmountValue } from './DisplayAmount';
import { AmountValue } from '../DisplayAmount';

type Props = {
fee: Amount;
Expand Down
2 changes: 1 addition & 1 deletion ui/src/components/swap/IncomingOffer.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { type Amount, AssetKind, type Brand } from '@agoric/web-components';
import { AmountValue } from './DisplayAmount';
import { AmountValue } from '../DisplayAmount';
import { useAgoric } from '@agoric/react-components';
import { useContext, useEffect, useState } from 'react';
import { useDisplayInfo } from '../../store/displayInfo';
Expand Down
4 changes: 2 additions & 2 deletions ui/src/components/swap/Swap.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { type PurseJSONState, useAgoric } from '@agoric/react-components';
import ProposalAmountsBox from './ProposalAmountsBox';
import RecipientInput from './RecipientInput';
import ProposalAmountsBox from '../ProposalAmountsBox';
import RecipientInput from '../RecipientInput';
import { queryPurses } from '../../utils/queryPurses';
import { useContext, useEffect, useState } from 'react';
import type { Amount, AssetKind } from '@agoric/web-components';
Expand Down
10 changes: 0 additions & 10 deletions ui/src/providers/Contract.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,6 @@ const watchContract = (watcher: ChainStorageWatcher) => {
});
},
);

watcher.watchLatest<Array<[string, unknown]>>(
[Kind.Data, 'published.agoricNames.vbankAsset'],
vbank => {
console.log('Got vbank', vbank);
useContractStore.setState({
vbank: fromEntries(vbank),
});
},
);
};

export const ContractProvider = ({ children }: PropsWithChildren) => {
Expand Down
1 change: 0 additions & 1 deletion ui/src/store/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { create } from 'zustand';
interface ContractState {
instances?: Record<string, unknown>;
brands?: Record<string, unknown>;
vbank?: Record<string, unknown>;
}

export const useContractStore = create<ContractState>(() => ({}));

0 comments on commit fd97f3b

Please sign in to comment.