diff --git a/src/components/OfferList.tsx b/src/components/OfferList.tsx
new file mode 100644
index 0000000..1ebe9e0
--- /dev/null
+++ b/src/components/OfferList.tsx
@@ -0,0 +1,153 @@
+import React from 'react';
+
+import dayjs from 'dayjs';
+import advancedFormat from 'dayjs/plugin/advancedFormat';
+
+import { shortenAddress } from '@usedapp/core'
+
+import { Theme } from '@mui/material/styles';
+import makeStyles from '@mui/styles/makeStyles';
+import Typography from '@mui/material/Typography';
+import createStyles from '@mui/styles/createStyles';
+
+import OfferIcon from '@mui/icons-material/ConnectWithoutContact';
+
+import { utils } from "ethers";
+
+import { PropsFromRedux } from '../containers/EventHistoryContainer';
+
+import LinkWrapper from './LinkWrapper';
+
+import {
+ IOfferRecord,
+ NetworkName,
+} from '../interfaces';
+
+import {
+ PROPY_LIGHT_GREY,
+} from '../utils/constants';
+
+import {
+ getEtherscanLinkByNetworkName,
+ priceFormat,
+} from '../utils';
+
+dayjs.extend(advancedFormat);
+
+const useStyles = makeStyles((theme: Theme) =>
+ createStyles({
+ root: {
+ position: 'relative',
+ },
+ content: {
+ width: '100%',
+ display: 'flex',
+ justifyContent: 'center',
+ flexDirection: 'column',
+ overflowX: 'scroll',
+ },
+ offerRecord: {
+ width: '100%',
+ display: 'flex',
+ justifyContent: 'flex-start',
+ marginBottom: theme.spacing(2),
+ },
+ eventIconOuterContainer: {
+ marginRight: theme.spacing(2),
+ },
+ eventIconInnerContainer: {
+ backgroundColor: PROPY_LIGHT_GREY,
+ width: 40,
+ height: 40,
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
+ borderRadius: '50%',
+ color: '#38a5fc',
+ },
+ eventRecordSummaryContainer: {
+ whiteSpace: 'nowrap',
+ display: 'flex',
+ flexDirection: 'column',
+ justifyContent: 'center',
+ },
+ eventRecordSummaryLineOne: {
+ marginBottom: theme.spacing(0.5),
+ },
+ }),
+);
+
+interface ITokenOfferList {
+ offerRecords: IOfferRecord[] | null
+}
+
+const getOfferIcon = () => {
+ return ;
+}
+
+const renderAddress = (value: string) => {
+ let innerElement = {shortenAddress(value)};
+ let link = getEtherscanLinkByNetworkName(NetworkName["ethereum"], value, 'address');
+ if(link) {
+ return (
+
+ {innerElement}
+
+ )
+ }
+ return innerElement
+}
+
+const getEventSummaryLineEntryOne = (event: IOfferRecord) => {
+ // mint
+ return (
+
+ {renderAddress(event.user_address)}
+ offered {priceFormat(Number(utils.formatUnits(event.offer_token_amount, event.offer_token.decimals)), 2, event.offer_token.symbol, false)}
+
+ );
+}
+
+const getEventSummaryLineEntryTwo = (event: IOfferRecord) => {
+ return (
+
+ {event.timestamp_unix ? dayjs.unix(Number(event.timestamp_unix)).format('MMM-D-YYYY hh:mm A') : 'loading...'}
+
+ );
+}
+
+const OfferList = (props: PropsFromRedux & ITokenOfferList) => {
+ const classes = useStyles();
+
+ const {
+ offerRecords,
+ } = props;
+
+ return (
+ <>
+
+
+ {offerRecords && offerRecords.sort((a, b) => {
+ return Number(b.timestamp_unix) - Number(a.timestamp_unix)
+ }).map((offerRecord, index) =>
+
+
+
+
+ {getEventSummaryLineEntryOne(offerRecord)}
+
+ {getEventSummaryLineEntryTwo(offerRecord)}
+
+
+ )}
+
+
+ >
+ )
+};
+
+export default OfferList;
\ No newline at end of file
diff --git a/src/components/SignalInterest.tsx b/src/components/SignalInterest.tsx
index 7e8e95f..09e73e5 100644
--- a/src/components/SignalInterest.tsx
+++ b/src/components/SignalInterest.tsx
@@ -77,6 +77,7 @@ interface ISignalInterest {
tokenAddress: string,
tokenId: string,
tokenNetwork: string,
+ onSuccess?: () => void,
}
const SignalInterest = (props: PropsFromRedux & ISignalInterest) => {
@@ -95,6 +96,7 @@ const SignalInterest = (props: PropsFromRedux & ISignalInterest) => {
tokenAddress,
tokenId,
tokenNetwork,
+ onSuccess,
} = props;
const signOfferMessage = async (proAmount: string) => {
@@ -129,6 +131,9 @@ const SignalInterest = (props: PropsFromRedux & ISignalInterest) => {
let triggerSignedMessageActionResponse = await SignerService.validateSignedMessageAndPerformAction(messageForSigning, signedMessage, signerAccount);
console.log({triggerSignedMessageActionResponse});
if(triggerSignedMessageActionResponse.status) {
+ if(onSuccess) {
+ onSuccess();
+ }
toast.success("Offer placed successfully!");
setShowDialog(false);
}
diff --git a/src/containers/OfferListContainer.tsx b/src/containers/OfferListContainer.tsx
new file mode 100644
index 0000000..0434c47
--- /dev/null
+++ b/src/containers/OfferListContainer.tsx
@@ -0,0 +1,19 @@
+import { connect, ConnectedProps } from 'react-redux';
+
+import OfferList from '../components/OfferList';
+
+interface RootState {
+ isConsideredMobile: boolean
+ isConsideredMedium: boolean
+}
+
+const mapStateToProps = (state: RootState) => ({
+ isConsideredMobile: state.isConsideredMobile,
+ isConsideredMedium: state.isConsideredMedium,
+})
+
+const connector = connect(mapStateToProps, {})
+
+export type PropsFromRedux = ConnectedProps
+
+export default connector(OfferList)
\ No newline at end of file
diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts
index 07fe27f..4573541 100644
--- a/src/interfaces/index.ts
+++ b/src/interfaces/index.ts
@@ -185,4 +185,14 @@ export interface INonceResponse {
nonce: number;
salt: string;
}
+}
+
+export interface IOfferRecord {
+ asset_address: string;
+ offer_token_address: string;
+ offer_token_amount: string;
+ timestamp_unix: number;
+ token_id: string;
+ user_address: string;
+ offer_token: IAssetRecord;
}
\ No newline at end of file
diff --git a/src/pages/SingleTokenPage.tsx b/src/pages/SingleTokenPage.tsx
index 115adcb..dec4381 100644
--- a/src/pages/SingleTokenPage.tsx
+++ b/src/pages/SingleTokenPage.tsx
@@ -19,6 +19,7 @@ import GenericTitleContainer from '../containers/GenericTitleContainer';
import EventHistoryContainer from '../containers/EventHistoryContainer';
import TokenMetadataTimelineContainer from '../containers/TokenMetadataTimelineContainer';
import SignalInterestContainer from '../containers/SignalInterestContainer';
+import OfferListContainer from '../containers/OfferListContainer';
import LinkWrapper from '../components/LinkWrapper';
@@ -36,6 +37,7 @@ import {
ITokenMetadata,
ITransferEventERC20Record,
ITransferEventERC721Record,
+ IOfferRecord,
TokenStandard,
} from '../interfaces';
@@ -111,6 +113,7 @@ const SingleTokenPage = () => {
const [tokenRecord, setTokenRecord] = useState(null);
const [tokenEventRecord, setTokenEventRecord] = useState(null);
+ const [tokenOfferList, setTokenOfferList] = useState(null);
const [tokenMetadata, setTokenMetadata] = useState(null);
const [fetchIndex, setFetchIndex] = useState(0);
const [isMetadataRefreshing, setIsMetadataRefreshing] = useState(false);
@@ -146,6 +149,9 @@ const SingleTokenPage = () => {
if(tokenRecordQueryResponse?.data?.transfer_events_erc20) {
setTokenEventRecord(tokenRecordQueryResponse?.data?.transfer_events_erc20);
}
+ if(tokenRecordQueryResponse?.data?.offchain_offers) {
+ setTokenOfferList(tokenRecordQueryResponse?.data?.offchain_offers);
+ }
if(tokenRecordQueryResponse?.data?.metadata) {
let metadata = JSON.parse(tokenRecordQueryResponse?.data?.metadata);
// // temp timeline shim for testing design
@@ -261,8 +267,11 @@ const SingleTokenPage = () => {
{tokenEventRecord && }
{allowSignalInterest && tokenId && tokenAddress && network &&
<>
-
-
+
+ setFetchIndex(fetchIndex + 1)} tokenId={tokenId} tokenAddress={tokenAddress} tokenNetwork={network} />
+
+ {tokenEventRecord && }
+
>
}
>