Skip to content

Commit

Permalink
feat(tooling-explorer): Rebrand Homepage feeds (#2005)
Browse files Browse the repository at this point in the history
* feat(tooling-explorer): Rebrand Homepage feeds

* refactor(tooling-ui-kit): Clean up of `Table`

* refactor: The Holy refactor

* refactor: use segmented button

* chore: revert unnecessary changes

* chore: pass row indexes

* refactor: Apply suggestions

* chore: Use new textToCopy property

* chore: fmt

* fix: Show `--` when tx sender is not present

* feat: Add link cells

* chore: fmt

* feat: add link to sender

* chore: More links

* chore: '--'

---------

Co-authored-by: evavirseda <[email protected]>
  • Loading branch information
marc2332 and evavirseda authored Sep 3, 2024
1 parent 1c2d885 commit 747ccb1
Show file tree
Hide file tree
Showing 8 changed files with 239 additions and 243 deletions.
150 changes: 85 additions & 65 deletions apps/explorer/src/components/activity/Activity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,39 @@
// SPDX-License-Identifier: Apache-2.0

import { useFeatureIsOn } from '@growthbook/growthbook-react';
import { Heading } from '@iota/ui';
import { useState } from 'react';
import toast from 'react-hot-toast';

import { CheckpointsTable } from '../checkpoints/CheckpointsTable';
import { EpochsActivityTable } from './EpochsActivityTable';
import { TransactionsActivityTable } from './TransactionsActivityTable';
import { PlayPause, Tabs, TabsContent, TabsList, TabsTrigger } from '~/components/ui';
import { PlayPause } from '~/components/ui';
import {
ButtonSegment,
ButtonSegmentType,
SegmentedButton,
SegmentedButtonType,
} from '@iota/apps-ui-kit';

const VALID_TABS = ['transactions', 'epochs', 'checkpoints'];
enum ActivityCategory {
Transactions = 'transactions',
Epochs = 'epochs',
Checkpoints = 'checkpoints',
}
const ACTIVITY_CATEGORIES = [
{
label: 'Transactions',
value: ActivityCategory.Transactions,
},
{
label: 'Epochs',
value: ActivityCategory.Epochs,
},
{
label: 'Checkpoints',
value: ActivityCategory.Checkpoints,
},
];

type ActivityProps = {
initialTab?: string | null;
Expand All @@ -24,16 +47,12 @@ const AUTO_REFRESH_ID = 'auto-refresh';
const REFETCH_INTERVAL_SECONDS = 10;
const REFETCH_INTERVAL = REFETCH_INTERVAL_SECONDS * 1000;

export function Activity({
initialTab,
initialLimit,
disablePagination,
}: ActivityProps): JSX.Element {
export function Activity({ initialLimit, disablePagination }: ActivityProps): JSX.Element {
const pollingTxnTableEnabled = useFeatureIsOn('polling-txn-table');

const [paused, setPaused] = useState(false);
const [activeTab, setActiveTab] = useState(() =>
initialTab && VALID_TABS.includes(initialTab) ? initialTab : 'transactions',
const [selectedCategory, setSelectedCategory] = useState<ActivityCategory>(
ActivityCategory.Transactions,
);

const handlePauseChange = () => {
Expand All @@ -51,81 +70,82 @@ export function Activity({
const refetchInterval = paused || !pollingTxnTableEnabled ? undefined : REFETCH_INTERVAL;
// TODO remove network check when querying transactions with TransactionKind filter is fixed on devnet and testnet
/*const [network] = useNetwork();
const isTransactionKindFilterEnabled = Network.MAINNET === network || Network.LOCAL === network;
const [showSystemTransactions, setShowSystemTransaction] = useState(
!isTransactionKindFilterEnabled,
);
useEffect(() => {
if (!isTransactionKindFilterEnabled) {
setShowSystemTransaction(true);
}
}, [isTransactionKindFilterEnabled]);*/
const isTransactionKindFilterEnabled = Network.MAINNET === network || Network.LOCAL === network;
const [showSystemTransactions, setShowSystemTransaction] = useState(
!isTransactionKindFilterEnabled,
);
useEffect(() => {
if (!isTransactionKindFilterEnabled) {
setShowSystemTransaction(true);
}
}, [isTransactionKindFilterEnabled]);*/

return (
<div>
<Tabs size="lg" value={activeTab} onValueChange={setActiveTab}>
<div className="relative">
<TabsList>
<TabsTrigger value="transactions">
<Heading variant="heading4/semibold">Transaction Blocks</Heading>
</TabsTrigger>
<TabsTrigger value="epochs">
<Heading variant="heading4/semibold">Epochs</Heading>
</TabsTrigger>
<TabsTrigger value="checkpoints">
<Heading variant="heading4/semibold">Checkpoints</Heading>
</TabsTrigger>
</TabsList>
<div className="absolute inset-y-0 -top-1 right-0 flex items-center gap-3 text-2xl">
{/* TODO re-enable this when index is stable */}
{/*activeTab === 'transactions' && isTransactionKindFilterEnabled ? (
<DropdownMenu
trigger={<Filter16 className="p-1" />}
content={
<DropdownMenuCheckboxItem
checked={showSystemTransactions}
label="Show System Transactions"
onSelect={(e) => {
e.preventDefault();
}}
onCheckedChange={() => {
setShowSystemTransaction((value) => !value);
}}
/>
}
modal={false}
align="end"
/>
) : null */}
{/* todo: re-enable this when rpc is stable */}
{pollingTxnTableEnabled && activeTab === 'transactions' && (
<>
<div className="relative">
<SegmentedButton type={SegmentedButtonType.Transparent}>
{ACTIVITY_CATEGORIES.map(({ label, value }) => (
<ButtonSegment
key={value}
onClick={() => setSelectedCategory(value)}
label={label}
selected={selectedCategory === value}
type={ButtonSegmentType.Underlined}
/>
))}
</SegmentedButton>
<div className="absolute inset-y-0 -top-1 right-0 flex items-center gap-3 text-2xl">
{/* TODO re-enable this when index is stable */}
{/*activeTab === 'transactions' && isTransactionKindFilterEnabled ? (
<DropdownMenu
trigger={<Filter16 className="p-1" />}
content={
<DropdownMenuCheckboxItem
checked={showSystemTransactions}
label="Show System Transactions"
onSelect={(e) => {
e.preventDefault();
}}
onCheckedChange={() => {
setShowSystemTransaction((value) => !value);
}}
/>
}
modal={false}
align="end"
/>
) : null */}
{/* todo: re-enable this when rpc is stable */}
{pollingTxnTableEnabled &&
selectedCategory === ActivityCategory.Transactions && (
<PlayPause paused={paused} onChange={handlePauseChange} />
)}
</div>
</div>
<TabsContent value="transactions">
</div>
<div className="p-md">
{selectedCategory === ActivityCategory.Transactions && (
<TransactionsActivityTable
refetchInterval={refetchInterval}
initialLimit={initialLimit}
disablePagination={disablePagination}
transactionKindFilter={undefined}
/>
</TabsContent>
<TabsContent value="epochs">
)}
{selectedCategory === ActivityCategory.Epochs && (
<EpochsActivityTable
refetchInterval={refetchInterval}
initialLimit={initialLimit}
disablePagination={disablePagination}
/>
</TabsContent>
<TabsContent value="checkpoints">
)}
{selectedCategory === ActivityCategory.Checkpoints && (
<CheckpointsTable
refetchInterval={refetchInterval}
initialLimit={initialLimit}
disablePagination={disablePagination}
/>
</TabsContent>
</Tabs>
</div>
)}
</div>
</>
);
}
85 changes: 36 additions & 49 deletions apps/explorer/src/components/transactions/TxCardUtils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,17 @@
// SPDX-License-Identifier: Apache-2.0

import { getTotalGasUsed } from '@iota/core';
import { X12, Dot12 } from '@iota/icons';
import { type IotaClient, type IotaTransactionBlockResponse } from '@iota/iota-sdk/client';

import { IotaAmount } from '../table/IotaAmount';
import { TxTimeType } from '../tx-time/TxTimeType';
import { HighlightedTableCol } from '~/components';
import { AddressLink, TransactionLink } from '~/components/ui';
import { type ReactNode } from 'react';
import { type TableCellProps, TableCellType } from '@iota/apps-ui-kit';
import { addressToLink, transactionToLink } from '../ui';

interface TransactionData {
date: ReactNode;
digest: ReactNode;
txns: ReactNode;
gas: ReactNode;
sender: ReactNode;
date: TableCellProps;
digest: TableCellProps;
txns: TableCellProps;
gas: TableCellProps;
sender: TableCellProps;
}

interface TableColumn {
Expand All @@ -33,47 +29,38 @@ export function genTableDataFromTxData(results: IotaTransactionBlockResponse[]):
} {
return {
data: results.map((transaction) => {
const status = transaction.effects?.status.status;
const sender = transaction.transaction?.data.sender;

return {
date: (
<HighlightedTableCol>
<TxTimeType timestamp={Number(transaction.timestampMs || 0)} />
</HighlightedTableCol>
),
digest: (
<HighlightedTableCol first>
<TransactionLink
digest={transaction.digest}
before={
status === 'success' ? (
<Dot12 className="text-success" />
) : (
<X12 className="text-issue-dark" />
)
}
/>
</HighlightedTableCol>
),
txns: (
<div>
{transaction.transaction?.data.transaction.kind ===
'ProgrammableTransaction'
? transaction.transaction.data.transaction.transactions.length
: '--'}
</div>
),
gas: (
<IotaAmount
amount={transaction.effects ? getTotalGasUsed(transaction.effects) : 0}
/>
),
sender: (
<HighlightedTableCol>
{sender ? <AddressLink address={sender} /> : '-'}
</HighlightedTableCol>
),
date: { type: TableCellType.Text, label: transaction.timestampMs?.toString() },
digest: {
type: TableCellType.Link,
label: transaction.digest,
to: transactionToLink({ digest: transaction.digest }),
},
txns: {
type: TableCellType.Text,
label:
transaction.transaction?.data.transaction.kind === 'ProgrammableTransaction'
? transaction.transaction.data.transaction.transactions.length.toString()
: '--',
},
gas: {
type: TableCellType.Text,
label: transaction.effects
? getTotalGasUsed(transaction.effects)?.toString()
: '0',
},
sender: sender
? {
type: TableCellType.Link,
label: sender,
to: addressToLink({ address: sender }),
}
: {
type: TableCellType.Text,
label: '--',
},
};
}),
columns: [
Expand Down
19 changes: 19 additions & 0 deletions apps/explorer/src/components/ui/InternalLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,22 @@ export const AddressLink = createInternalLink('address', 'address', (addressOrNs
export const ObjectLink = createInternalLink('object', 'objectId', formatAddress);
export const TransactionLink = createInternalLink('txblock', 'digest', formatDigest);
export const ValidatorLink = createInternalLink('validator', 'address', formatAddress);

// This will ultimately replace createInternalLink.
export function createLinkTo<T extends string>(
base: string,
propName: T,
): (args: { queryStrings?: Record<string, string> } & Record<T, string>) => string {
return ({ [propName]: id, queryStrings = {} }) => {
const queryString = new URLSearchParams(queryStrings).toString();
const queryStringPrefix = queryString ? `?${queryString}` : '';

return `/${base}/${encodeURI(id)}${queryStringPrefix}`;
};
}

export const transactionToLink = createLinkTo('txblock', 'digest');
export const checkpointToLink = createLinkTo('checkpoint', 'digest');
export const epochToLink = createLinkTo('epoch', 'epoch');
export const addressToLink = createLinkTo('address', 'address');
export const checkpointSequenceToLink = createLinkTo('checkpoint', 'sequence');
2 changes: 1 addition & 1 deletion apps/explorer/src/components/ui/Link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const linkStyles = cva([], {
variants: {
variant: {
text: 'text-body font-semibold text-steel-dark hover:text-steel-darker active:text-steel disabled:text-gray-60',
mono: 'font-mono text-body font-medium text-hero-dark hover:text-hero-darkest break-all',
mono: 'font-mono text-body text-primary-30 hover:text-primary-20 break-all',
textHeroDark: 'text-pBody font-medium text-hero-dark hover:text-hero-darkest',
},
uppercase: {
Expand Down
Loading

0 comments on commit 747ccb1

Please sign in to comment.