Skip to content
Merged
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
23 changes: 21 additions & 2 deletions src/components/CopyableAddress.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,33 @@ const CopyableAddress: React.FC<CopyableAddressProps> = ({
}) => {
const [copied, setCopied] = useState(false);

const handleClick = () => {
const handleClick = (e: React.MouseEvent) => {
// Prevent enclosing <a> / RouterLink from navigating when the address is
// nested inside a clickable card.
e.preventDefault();
e.stopPropagation();
navigator.clipboard.writeText(address);
setCopied(true);
setTimeout(() => setCopied(false), 1500);
};

return (
<Tooltip title={copied ? 'Copied!' : address} arrow placement="top">
<Tooltip
title={copied ? 'Copied!' : address}
arrow
placement="top"
slotProps={{
tooltip: {
sx: {
// Address-specific overrides on top of the global tooltip theme:
// never wrap a single address across lines.
fontFamily: FONTS.mono,
whiteSpace: 'nowrap',
maxWidth: 'none',
},
},
}}
>
<Typography
component="span"
onClick={handleClick}
Expand Down
98 changes: 98 additions & 0 deletions src/components/Timeline.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import React from 'react';
import { Stack, Typography, useTheme } from '@mui/material';
import { FONTS } from '../theme';

export type TimelineStepState = 'done' | 'active' | 'pending' | 'failed';

const STATE_GLYPH: Record<TimelineStepState, string> = {
done: '●', // ●
failed: '✗', // ✗
active: '○', // ○
pending: '○', // ○
};

export const TimelineStep: React.FC<{
state: TimelineStepState;
label: string;
detail?: React.ReactNode;
/**
* Override the default glyph (e.g. '⏱' for a Timeout row).
*/
glyph?: string;
/**
* Override the state-derived color. Use sparingly — reserved for
* terminal "finality" rows that need to pop (e.g. green ✓ on a
* completed swap, red ✗ on a timed-out swap).
*/
color?: string;
/**
* Default 80 — bump if labels in your timeline are wordier.
*/
labelMinWidth?: number;
}> = ({ state, label, detail, glyph, color, labelMinWidth = 80 }) => {
const theme = useTheme();
const stepColor =
color ??
(state === 'done'
? theme.palette.status.completed
: state === 'failed'
? theme.palette.status.timedOut
: state === 'active'
? theme.palette.status.active
: theme.palette.text.secondary);
return (
<Stack direction="row" alignItems="center" spacing={1.5}>
<Typography
sx={{
fontSize: '0.9rem',
width: 16,
textAlign: 'center',
color: stepColor,
}}
>
{glyph ?? STATE_GLYPH[state]}
</Typography>
<Typography
sx={{
fontFamily: FONTS.mono,
fontSize: '0.75rem',
color: stepColor,
fontWeight: state === 'done' ? 600 : 400,
minWidth: labelMinWidth,
}}
>
{label}
</Typography>
{detail != null && detail !== '' && (
<Typography
component="div"
sx={{
fontFamily: FONTS.mono,
fontSize: '0.7rem',
color: 'text.secondary',
}}
>
{detail}
</Typography>
)}
</Stack>
);
};

export const SectionTitle: React.FC<{ children: React.ReactNode }> = ({
children,
}) => (
<Typography
sx={{
fontFamily: FONTS.mono,
fontSize: '0.7rem',
fontWeight: 600,
color: 'text.secondary',
textTransform: 'uppercase',
letterSpacing: '0.5px',
mb: 1.5,
}}
>
{children}
</Typography>
);
19 changes: 7 additions & 12 deletions src/components/dashboard/EventFeed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,17 +90,12 @@ const EventFeed: React.FC = () => {
</Typography>
<Tooltip
title={
<Stack spacing={0.5} sx={{ maxWidth: 280 }}>
<Typography variant="subtitle2" sx={{ fontWeight: 'bold' }}>
What is this?
</Typography>
<Typography variant="body2">
A real-time stream of events emitted by the smart contract and
the underlying chain — exchange lifecycle, network collateral
changes, validator votes, and reservations. Newest events appear
at the top.
</Typography>
</Stack>
<Box sx={{ maxWidth: 280 }}>
A real-time stream of events emitted by the smart contract and the
underlying chain — exchange lifecycle, network collateral changes,
validator votes, and reservations. Newest events appear at the
top.
</Box>
}
arrow
placement="right"
Expand Down Expand Up @@ -133,7 +128,7 @@ const EventFeed: React.FC = () => {
sx={{
p: 1.5,
borderRadius: 0,
backgroundColor: 'background.paper',
backgroundColor: 'surface.light',
border: '1px solid',
borderColor: 'divider',
transition: 'border-color 0.2s',
Expand Down
13 changes: 5 additions & 8 deletions src/components/dashboard/MinerRatesTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -366,19 +366,16 @@ const MinerRatesTable: React.FC = () => {
</Typography>
<Tooltip
title={
<Stack spacing={0.5} sx={{ maxWidth: 280 }}>
<Typography variant="subtitle2" sx={{ fontWeight: 'bold' }}>
What is this?
</Typography>
<Typography variant="body2">
<Stack spacing={1} sx={{ maxWidth: 280 }}>
<Box>
Live exchange rates quoted across the Allways network. Each
row represents an active network node; both directions
(BTC→TAO and TAO→BTC) are shown when quoted, with the spread
between them being the network's margin.
</Typography>
<Typography variant="body2">
</Box>
<Box>
Sort by rate or capacity to find the best counterparty.
</Typography>
</Box>
</Stack>
}
arrow
Expand Down
21 changes: 7 additions & 14 deletions src/components/dashboard/ReservationsTracker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
formatAmount,
formatTimeUntilBlock,
} from '../../utils/format';
import { ReservationsTrackerSkeleton } from './Skeletons';

const STATUS_COLORS = (palette: {
status: { active: string; fulfilled: string; timedOut: string };
Expand All @@ -40,11 +41,15 @@ const ReservationsTracker: React.FC = () => {
const { data: miners } = useMiners();
const { data: chainState } = useChainState();
const { data: protocol } = useProtocolConstants();
const [searchAddr, setSearchAddr] = useState('');
const reservations = data ?? [];
const colors = STATUS_COLORS(theme.palette);
const currentBlock = chainState?.currentBlock ?? 0;

const [searchAddr, setSearchAddr] = useState('');
if (isLoading && !data) {
return <ReservationsTrackerSkeleton />;
}

const trimmed = searchAddr.trim().toLowerCase();
const filtered = trimmed
? reservations.filter((r: Reservation) =>
Expand Down Expand Up @@ -126,19 +131,7 @@ const ReservationsTracker: React.FC = () => {
</Box>
</Stack>

{isLoading && (
<Typography
sx={{
fontFamily: FONTS.mono,
fontSize: '0.75rem',
color: 'text.secondary',
}}
>
Loading…
</Typography>
)}

{!isLoading && filtered.length === 0 && (
{filtered.length === 0 && (
<Typography
sx={{
fontFamily: FONTS.mono,
Expand Down
61 changes: 60 additions & 1 deletion src/components/dashboard/Skeletons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { FONTS } from '../../theme';

const cardBorder = {
borderRadius: 0,
backgroundColor: 'background.paper',
backgroundColor: 'surface.light',
border: '1px solid',
borderColor: 'divider',
};
Expand Down Expand Up @@ -211,6 +211,65 @@ export const EventFeedSkeleton: React.FC = () => (
</Stack>
);

export const ReservationsTrackerSkeleton: React.FC = () => (
<Stack spacing={1.5}>
<Stack
direction={{ xs: 'column', sm: 'row' }}
spacing={{ xs: 1, sm: 2 }}
alignItems={{ xs: 'stretch', sm: 'center' }}
justifyContent="space-between"
>
<Skeleton
variant="text"
width={110}
height={14}
sx={{ borderRadius: 0 }}
/>
<Skeleton
variant="rectangular"
width={420}
height={36}
sx={{ borderRadius: 0, maxWidth: '100%' }}
/>
</Stack>
<Stack spacing={0.75}>
{[0, 1, 2].map((i) => (
<Stack
key={i}
spacing={0.5}
sx={{
p: 1.25,
borderRadius: 0,
border: '1px solid',
borderColor: 'divider',
}}
>
<Stack direction="row" justifyContent="space-between">
<Skeleton
variant="text"
width={220}
height={14}
sx={{ borderRadius: 0 }}
/>
<Skeleton
variant="text"
width={60}
height={12}
sx={{ borderRadius: 0 }}
/>
</Stack>
<Skeleton
variant="text"
width={180}
height={12}
sx={{ borderRadius: 0 }}
/>
</Stack>
))}
</Stack>
</Stack>
);

export const SwapTrackerSkeleton: React.FC = () => (
<Stack>
<Skeleton
Expand Down
2 changes: 1 addition & 1 deletion src/components/dashboard/StatsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const StatCard: React.FC<{ label: string; value: string }> = ({
sx={{
p: 2.5,
borderRadius: 0,
backgroundColor: 'background.paper',
backgroundColor: 'surface.light',
border: '1px solid',
borderColor: 'divider',
textAlign: 'center',
Expand Down
31 changes: 15 additions & 16 deletions src/components/dashboard/SwapTracker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,13 @@ const getStatusColor = (
};
},
): string => {
// Terminal states pop with semantic color — completion green / timeout red.
// In-flight states keep their muted blue tints.
const map: Record<string, string> = {
ACTIVE: palette.status.active,
FULFILLED: palette.status.fulfilled,
COMPLETED: palette.status.completed,
TIMED_OUT: palette.status.timedOut,
COMPLETED: 'var(--color-success)',
TIMED_OUT: 'var(--color-danger)',
};
return map[status] ?? palette.status.active;
};
Expand Down Expand Up @@ -102,17 +104,12 @@ const SwapTracker: React.FC = () => {
</Typography>
<Tooltip
title={
<Stack spacing={0.5} sx={{ maxWidth: 280 }}>
<Typography variant="subtitle2" sx={{ fontWeight: 'bold' }}>
What is this?
</Typography>
<Typography variant="body2">
Every transaction on the network in chronological order, with
its current status and progress through the lifecycle: Initiated
→ Fulfilled → Completed (or Timed Out). Click a row to see the
full timeline.
</Typography>
</Stack>
<Box sx={{ maxWidth: 280 }}>
Every transaction on the network in chronological order, with its
current status and progress through the lifecycle: Initiated →
Fulfilled → Completed (or Timed Out). Click a row to see the full
timeline.
</Box>
}
arrow
placement="right"
Expand Down Expand Up @@ -145,7 +142,7 @@ const SwapTracker: React.FC = () => {
p: 4,
textAlign: 'center',
borderRadius: 0,
backgroundColor: 'background.paper',
backgroundColor: 'surface.light',
border: '1px solid',
borderColor: 'divider',
}}
Expand Down Expand Up @@ -187,7 +184,7 @@ const SwapTracker: React.FC = () => {
sx={{
p: 2,
borderRadius: 0,
backgroundColor: 'background.paper',
backgroundColor: 'surface.light',
border: '1px solid',
borderColor: 'divider',
textDecoration: 'none',
Expand Down Expand Up @@ -250,7 +247,9 @@ const SwapTracker: React.FC = () => {
borderRadius: 0,
backgroundColor: theme.palette.border.light,
'& .MuiLinearProgress-bar': {
backgroundColor: color,
// Bar fill stays neutral regardless of status;
// status text carries the green/red signal.
backgroundColor: theme.palette.border.medium,
borderRadius: 0,
},
}}
Expand Down
Loading