Skip to content

Commit

Permalink
Add basic object routes/support
Browse files Browse the repository at this point in the history
  • Loading branch information
CapCap committed Jul 26, 2023
1 parent c1dabcd commit 4969f7e
Show file tree
Hide file tree
Showing 11 changed files with 153 additions and 79 deletions.
10 changes: 10 additions & 0 deletions src/ExplorerRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export default function ExplorerRoutes() {
<Route path=":txnHashOrVersion" element={<TransactionPage />} />
<Route path=":txnHashOrVersion/:tab" element={<TransactionPage />} />
</Route>

<Route path="/account">
<Route
path=":address/modules/:modulesTab/:selectedModuleName"
Expand All @@ -41,6 +42,15 @@ export default function ExplorerRoutes() {
<Route path=":address/:tab" element={<AccountPage />} />
<Route path=":address" element={<AccountPage />} />
</Route>

<Route path="/object">
<Route
path=":address/:tab"
element={<AccountPage isObject={true} />}
/>
<Route path=":address" element={<AccountPage isObject={true} />} />
</Route>

<Route path="/blocks" element={<BlocksPage />} />
<Route path="/block">
<Route path=":height" element={<BlockPage />} />
Expand Down
1 change: 0 additions & 1 deletion src/api/hooks/useGetANS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ export function useGetNameFromAddress(
isValidator = false,
) {
const [state, _] = useGlobalState();

const queryResult = useQuery<string | null, ResponseError>({
queryKey: ["ANSName", address, shouldCache, state.network_name],
queryFn: () => {
Expand Down
143 changes: 80 additions & 63 deletions src/api/hooks/useGetSearchResults.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {useEffect, useState} from "react";
import {
getAccount,
getAccountResources,
getBlockByHeight,
getBlockByVersion,
getTransaction,
Expand Down Expand Up @@ -47,6 +48,8 @@ export default function useGetSearchResults(input: string) {
const isValidTxnHashOrVer = isValidTxnHashOrVersion(searchText);
const isValidBlockHeightOrVer = isNumeric(searchText);

const promises = [];

const namePromise = getAddressFromName(searchText, state.network_name)
.then(({address, primaryName}): SearchResult | null => {
if (address) {
Expand All @@ -64,77 +67,91 @@ export default function useGetSearchResults(input: string) {
return null;
// Do nothing. It's expected that not all search input is a valid transaction
});

const accountPromise = getAccount(
{address: searchText},
state.network_value,
)
.then((): SearchResult => {
return {
label: `Account ${searchText}`,
to: `/account/${searchText}`,
};
})
.catch(() => {
return null;
// Do nothing. It's expected that not all search input is a valid account
});

const txnPromise = getTransaction(
{txnHashOrVersion: searchText},
state.network_value,
)
.then((): SearchResult => {
return {
label: `Transaction ${searchText}`,
to: `/txn/${searchText}`,
};
})
.catch(() => {
return null;
// Do nothing. It's expected that not all search input is a valid transaction
});

const blockByHeightPromise = getBlockByHeight(
{height: parseInt(searchText), withTransactions: false},
state.network_value,
)
.then((): SearchResult => {
return {
label: `Block ${searchText}`,
to: `/block/${searchText}`,
};
})
.catch(() => {
return null;
// Do nothing. It's expected that not all search input is a valid transaction
});

const blockByVersionPromise = getBlockByVersion(
{version: parseInt(searchText), withTransactions: false},
state.network_value,
)
.then((block): SearchResult => {
return {
label: `Block with Txn Version ${searchText}`,
to: `/block/${block.block_height}`,
};
})
.catch(() => {
return null;
// Do nothing. It's expected that not all search input is a valid transaction
});

const promises = [];

promises.push(namePromise);

if (isValidAccountAddr) {
// It's either an account OR an object: we query both at once to save time
const accountPromise = await getAccount(
{address: searchText},
state.network_value,
)
.then((): SearchResult => {
return {
label: `Account ${searchText}`,
to: `/account/${searchText}`,
};
})
.catch(() => {
return null;
// Do nothing. It's expected that not all search input is a valid account
});

const resourcePromise = await getAccountResources(
{address: searchText},
state.network_value,
)
.then((): SearchResult => {
return {
label: `Object ${searchText}`,
to: `/object/${searchText}`,
};
})
.catch(() => {
return null;
// Do nothing. It's expected that not all search input is a valid account
});
promises.push(accountPromise);
promises.push(resourcePromise);
}

if (isValidTxnHashOrVer) {
const txnPromise = getTransaction(
{txnHashOrVersion: searchText},
state.network_value,
)
.then((): SearchResult => {
return {
label: `Transaction ${searchText}`,
to: `/txn/${searchText}`,
};
})
.catch(() => {
return null;
// Do nothing. It's expected that not all search input is a valid transaction
});
promises.push(txnPromise);
}

if (isValidBlockHeightOrVer) {
const blockByHeightPromise = getBlockByHeight(
{height: parseInt(searchText), withTransactions: false},
state.network_value,
)
.then((): SearchResult => {
return {
label: `Block ${searchText}`,
to: `/block/${searchText}`,
};
})
.catch(() => {
return null;
// Do nothing. It's expected that not all search input is a valid transaction
});

const blockByVersionPromise = getBlockByVersion(
{version: parseInt(searchText), withTransactions: false},
state.network_value,
)
.then((block): SearchResult => {
return {
label: `Block with Txn Version ${searchText}`,
to: `/block/${block.block_height}`,
};
})
.catch(() => {
return null;
// Do nothing. It's expected that not all search input is a valid transaction
});
promises.push(blockByHeightPromise);
promises.push(blockByVersionPromise);
}
Expand Down
37 changes: 33 additions & 4 deletions src/pages/Account/Index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Error from "./Error";
import {AptosNamesBanner} from "./Components/AptosNamesBanner";
import {useGlobalState} from "../../global-config/GlobalConfig";
import {Network} from "aptos";
import {useGetAccountResources} from "../../api/hooks/useGetAccountResources";

const TAB_VALUES_FULL: TabValue[] = [
"transactions",
Expand All @@ -24,10 +25,29 @@ const TAB_VALUES_FULL: TabValue[] = [

const TAB_VALUES: TabValue[] = ["transactions", "resources", "modules", "info"];

export default function AccountPage() {
const OBJECT_VALUES_FULL: TabValue[] = [
"transactions",
// TODO: Once indexer supports objects owning coins/tokens (v2?)- uncomment these
// "coins",
// "tokens",
"resources",
];
const OBJECT_TAB_VALUES: TabValue[] = ["transactions", "resources"];

type AccountPageProps = {
isObject?: boolean;
};

export default function AccountPage({isObject = false}: AccountPageProps) {
const isGraphqlClientSupported = useGetIsGraphqlClientSupported();
const address = useParams().address ?? "";
const {data, error, isLoading} = useGetAccount(address);
let loadingFunction;
if (isObject) {
loadingFunction = useGetAccountResources;
} else {
loadingFunction = useGetAccount;
}
const {data, error, isLoading} = loadingFunction(address);
const [state] = useGlobalState();

// TODO: [BE] instead of passing down address as props, use context
Expand All @@ -41,7 +61,7 @@ export default function AccountPage() {
<PageHeader />
</Grid>
<Grid item xs={12} md={8} lg={9} alignSelf="center">
<AccountTitle address={addressHex} />
<AccountTitle address={addressHex} isObject={isObject} />
</Grid>
<Grid item xs={12} md={4} lg={3} marginTop={{md: 0, xs: 2}}>
<BalanceCard address={addressHex} />
Expand All @@ -56,7 +76,16 @@ export default function AccountPage() {
<AccountTabs
address={addressHex}
accountData={data}
tabValues={isGraphqlClientSupported ? TAB_VALUES_FULL : TAB_VALUES}
tabValues={
isObject
? isGraphqlClientSupported
? OBJECT_VALUES_FULL
: OBJECT_TAB_VALUES
: isGraphqlClientSupported
? TAB_VALUES_FULL
: TAB_VALUES
}
isObject={isObject}
/>
)}
</Grid>
Expand Down
12 changes: 9 additions & 3 deletions src/pages/Account/Tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ function getTabIcon(value: TabValue): JSX.Element {
type TabPanelProps = {
value: TabValue;
address: string;
accountData: Types.AccountData | undefined;
accountData: Types.AccountData | Types.MoveResource[] | undefined;
};

function TabPanel({value, address, accountData}: TabPanelProps): JSX.Element {
Expand All @@ -83,14 +83,16 @@ function TabPanel({value, address, accountData}: TabPanelProps): JSX.Element {

type AccountTabsProps = {
address: string;
accountData: Types.AccountData | undefined;
accountData: Types.AccountData | Types.MoveResource[] | undefined;
tabValues?: TabValue[];
isObject?: boolean;
};

// TODO: create reusable Tabs for all pages
export default function AccountTabs({
address,
accountData,
isObject = false,
tabValues = TAB_VALUES,
}: AccountTabsProps): JSX.Element {
const {tab, modulesTab} = useParams();
Expand All @@ -105,7 +107,11 @@ export default function AccountTabs({
}

const handleChange = (event: React.SyntheticEvent, newValue: TabValue) => {
navigate(`/account/${address}/${newValue}`);
if (isObject) {
navigate(`/object/${address}/${newValue}`);
} else {
navigate(`/account/${address}/${newValue}`);
}
};

return (
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Account/Tabs/CoinsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const COINS_QUERY = gql`

type TokenTabsProps = {
address: string;
accountData: Types.AccountData | undefined;
accountData: Types.AccountData | Types.MoveResource[] | undefined;
};

export default function CoinsTab({address}: TokenTabsProps) {
Expand Down
4 changes: 2 additions & 2 deletions src/pages/Account/Tabs/InfoTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import {getLearnMoreTooltip} from "../../Transaction/helpers";

type InfoTabProps = {
address: string;
accountData: Types.AccountData | undefined;
accountData: Types.AccountData | Types.MoveResource[] | undefined;
};

export default function InfoTab({accountData}: InfoTabProps) {
if (!accountData) {
if (!accountData || Array.isArray(accountData)) {
return <EmptyTabContent />;
}

Expand Down
2 changes: 1 addition & 1 deletion src/pages/Account/Tabs/ResourcesTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ function ResourcesContent({

type ResourcesTabProps = {
address: string;
accountData: Types.AccountData | undefined;
accountData: Types.AccountData | Types.MoveResource[] | undefined;
};

export default function ResourcesTab({address}: ResourcesTabProps) {
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Account/Tabs/TokensTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export function AccountTokensWithPagination({

type TokenTabsProps = {
address: string;
accountData: Types.AccountData | undefined;
accountData: Types.AccountData | Types.MoveResource[] | undefined;
};

export default function TokenTabs({address}: TokenTabsProps) {
Expand Down
11 changes: 10 additions & 1 deletion src/pages/Account/Tabs/TransactionsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import {Types} from "aptos";
import AccountTransactions from "../Components/AccountTransactions";
import {useGetIsGraphqlClientSupported} from "../../../api/hooks/useGraphqlClient";
import AccountAllTransactions from "../Components/AccountAllTransactions";
import EmptyTabContent from "../../../components/IndividualPageContent/EmptyTabContent";

type TransactionsTabProps = {
address: string;
accountData: Types.AccountData | undefined;
accountData: Types.AccountData | Types.MoveResource[] | undefined;
};

export default function TransactionsTab({
Expand All @@ -17,6 +18,14 @@ export default function TransactionsTab({

// AccountTransactions: render transactions where the account is the sender
// AccountAllTransactions: render all transactions where the account is involved
if (Array.isArray(accountData)) {
return isGraphqlClientSupported ? (
<AccountAllTransactions address={address} />
) : (
<EmptyTabContent />
);
}

return isGraphqlClientSupported ? (
<AccountAllTransactions address={address} />
) : (
Expand Down
8 changes: 6 additions & 2 deletions src/pages/Account/Title.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ import TitleHashButton, {HashType} from "../../components/TitleHashButton";

type AccountTitleProps = {
address: string;
isObject?: boolean;
};

export default function AccountTitle({address}: AccountTitleProps) {
export default function AccountTitle({
address,
isObject = false,
}: AccountTitleProps) {
return (
<Stack direction="column" spacing={2} marginX={1}>
<Typography variant="h3">Account</Typography>
<Typography variant="h3">{isObject ? "Object" : "Account"}</Typography>
<Stack direction="row" spacing={1}>
<TitleHashButton hash={address} type={HashType.ACCOUNT} />
<TitleHashButton hash={address} type={HashType.NAME} />
Expand Down

0 comments on commit 4969f7e

Please sign in to comment.