Skip to content
Open
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
20 changes: 15 additions & 5 deletions src/pages/MinerDetailsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
SEO,
} from '../components';
import { WatchlistButton } from '../components/common';
import { useMinerStats } from '../api';

type ViewMode = 'prs' | 'issues';

Expand Down Expand Up @@ -84,6 +85,13 @@ const MinerDetailsPage: React.FC = () => {
setSearchParams(p, { replace: true });
};

const { data: minerStats, isLoading: isLoadingMinerStats } = useMinerStats(
githubId ?? '',
);
// Only show the star once we've confirmed the miner exists — otherwise
// users can pin phantom entries via /miners/details?githubId=<anything>.
const minerExists = !isLoadingMinerStats && !!minerStats;

if (!githubId) {
return <Navigate to="/top-miners" replace />;
}
Expand Down Expand Up @@ -131,11 +139,13 @@ const MinerDetailsPage: React.FC = () => {
}}
>
<BackButton to="/top-miners" mb={0} />
<WatchlistButton
category="miners"
itemKey={githubId}
size="medium"
/>
{minerExists && (
<WatchlistButton
category="miners"
itemKey={githubId}
size="medium"
/>
)}
</Box>
<Box
sx={{
Expand Down
45 changes: 44 additions & 1 deletion src/pages/WatchlistPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,7 @@ const WatchlistOptionsButton: React.FC<WatchlistOptionsButtonProps> = ({

const MinersList: React.FC<{ itemKeys: string[] }> = ({ itemKeys }) => {
const { data: allMinersStats, isLoading } = useAllMiners();
const { remove } = useWatchlist('miners');
const watchedSet = useMemo(() => new Set(itemKeys), [itemKeys]);

const minerStats = useMemo(() => {
Expand All @@ -933,8 +934,50 @@ const MinersList: React.FC<{ itemKeys: string[] }> = ({ itemKeys }) => {
}));
}, [allMinersStats, watchedSet]);

const unresolvedIds = useMemo(() => {
if (isLoading || !allMinersStats) return [];
const known = new Set(allMinersStats.map((m) => m.githubId));
return itemKeys.filter((id) => !known.has(id));
}, [allMinersStats, isLoading, itemKeys]);

const handleRemoveUnresolved = useCallback(() => {
unresolvedIds.forEach((id) => remove(id));
}, [unresolvedIds, remove]);

return (
<Box sx={{ width: '100%' }}>
<Box
sx={{ width: '100%', display: 'flex', flexDirection: 'column', gap: 2 }}
>
{unresolvedIds.length > 0 && (
<Card
sx={(theme) => ({
p: 1.5,
display: 'flex',
alignItems: { xs: 'flex-start', sm: 'center' },
justifyContent: 'space-between',
gap: 1.5,
flexDirection: { xs: 'column', sm: 'row' },
backgroundColor: alpha(theme.palette.status.warningOrange, 0.08),
border: `1px solid ${alpha(theme.palette.status.warningOrange, 0.3)}`,
})}
elevation={0}
>
<Typography sx={{ fontSize: '0.85rem', color: 'text.secondary' }}>
{unresolvedIds.length} watched{' '}
{unresolvedIds.length === 1 ? 'miner' : 'miners'} could not be
loaded (the account may not be tracked by Gittensor).
</Typography>
<Button
size="small"
variant="outlined"
color="warning"
onClick={handleRemoveUnresolved}
sx={{ textTransform: 'none', flexShrink: 0 }}
>
Remove unresolved
</Button>
</Card>
)}
<TopMinersTable
miners={minerStats}
isLoading={isLoading}
Expand Down
Loading