diff --git a/src/components/leaderboard/TopRepositoriesTable.tsx b/src/components/leaderboard/TopRepositoriesTable.tsx index d3e514d1..7ef46533 100644 --- a/src/components/leaderboard/TopRepositoriesTable.tsx +++ b/src/components/leaderboard/TopRepositoriesTable.tsx @@ -1118,6 +1118,7 @@ const TopRepositoriesTable: React.FC = ({ header: '★', width: '52px', align: 'center', + sortKey: 'watch', cellSx: { p: 0 }, renderCell: (repo) => repo.repository ? ( diff --git a/src/components/miners/MinerPRsTable.tsx b/src/components/miners/MinerPRsTable.tsx index bb521faa..6180f39e 100644 --- a/src/components/miners/MinerPRsTable.tsx +++ b/src/components/miners/MinerPRsTable.tsx @@ -30,11 +30,21 @@ import { import FilterButton from '../FilterButton'; import { ClearSearchAdornment } from '../common/ClearSearchAdornment'; import { WatchlistButton } from '../../components/common'; -import { serializePRKey } from '../../hooks/useWatchlist'; +import { + comparePRsByWatchlist, + serializePRKey, + useWatchlist, +} from '../../hooks/useWatchlist'; import TablePagination from '../common/TablePagination'; import { tooltipSlotProps } from '../../theme'; -type PrSortField = 'number' | 'repository' | 'score' | 'lines' | 'date'; +type PrSortField = + | 'number' + | 'repository' + | 'score' + | 'lines' + | 'date' + | 'watch'; type SortDir = 'asc' | 'desc'; const PAGE_SIZE = 20; @@ -54,6 +64,7 @@ const DEFAULT_SORT_DIR: Record = { score: 'desc', lines: 'desc', date: 'desc', + watch: 'desc', }; // Mirrors the Score cell's render logic so clicking the Score header @@ -90,6 +101,7 @@ const MinerPRsTable: React.FC = ({ githubId }) => { const navigate = useNavigate(); const [searchParams, setSearchParams] = useSearchParams(); const { data: prs, isLoading } = useMinerPRs(githubId); + const { isWatched } = useWatchlist('prs'); const [selectedAuthor, setSelectedAuthor] = useState(null); const [searchQuery, setSearchQuery] = useState(''); const [sortField, setSortField] = useState('date'); @@ -187,11 +199,14 @@ const MinerPRsTable: React.FC = ({ githubId }) => { cmp = da.localeCompare(db); break; } + case 'watch': + cmp = comparePRsByWatchlist(a, b, isWatched); + break; } return sortDir === 'asc' ? cmp : -cmp; }); return sorted; - }, [filteredPRs, sortField, sortDir]); + }, [filteredPRs, sortField, sortDir, isWatched]); const pagedPRs = useMemo( () => paginateItems(sortedPRs, page, PAGE_SIZE), @@ -426,6 +441,7 @@ const MinerPRsTable: React.FC = ({ githubId }) => { header: '★', width: '8%', align: 'center', + sortKey: 'watch', renderCell: (pr) => ( = ({ }) => { const navigate = useNavigate(); const [searchParams, setSearchParams] = useSearchParams(); + const { isWatched } = useWatchlist('prs'); const [filter, setFilter] = useSessionStoredState( 'repository:prs:statusFilter', state, @@ -139,12 +145,14 @@ const RepositoryPRsTable: React.FC = ({ a.mergedAt ? new Date(a.mergedAt).getTime() : 0, b.mergedAt ? new Date(b.mergedAt).getTime() : 0, ); + case 'watch': + return comparePRsByWatchlist(a, b, isWatched) * dir; case 'score': default: return cmpNum(parseFloat(a.score || '0'), parseFloat(b.score || '0')); } }); - }, [filteredPRs, sortField, sortOrder]); + }, [filteredPRs, sortField, sortOrder, isWatched]); const handleRowClick = useCallback( (pr: CommitLog) => { @@ -360,6 +368,7 @@ const RepositoryPRsTable: React.FC = ({ key: 'watch', header: '★', align: 'center', + sortKey: 'watch', renderCell: (pr) => ( // should always use this helper to avoid drift in key format. export const serializePRKey = (repo: string, number: number): string => `${repo}#${number}`; + +export const comparePRsByWatchlist = < + T extends { repository: string; pullRequestNumber: number }, +>( + a: T, + b: T, + isWatched: (key: string) => boolean, +): number => + compareByWatchlist( + a, + b, + (pr) => serializePRKey(pr.repository, pr.pullRequestNumber), + isWatched, + ); diff --git a/src/pages/WatchlistPage.tsx b/src/pages/WatchlistPage.tsx index 43590754..8a12a174 100644 --- a/src/pages/WatchlistPage.tsx +++ b/src/pages/WatchlistPage.tsx @@ -86,9 +86,10 @@ import { isOutsideScoringWindow, } from '../utils/ExplorerUtils'; import { + comparePRsByWatchlist, + serializePRKey, useWatchlist, useWatchlistCounts, - serializePRKey, type WatchlistCategory, } from '../hooks/useWatchlist'; import { useWatchedPRs, type WatchedPRSource } from '../hooks/useWatchedPRs'; @@ -100,7 +101,6 @@ import { import { filterPrs, type PrStatusFilter } from '../utils/prTable'; import { getIssueStatusMeta } from '../utils/issueStatus'; import { formatDate, formatTokenAmount } from '../utils/format'; -import { compareByWatchlist } from '../utils/watchlistSort'; import { getRepositoryOwnerAvatarSrc } from '../utils/avatar'; import theme, { CHART_COLORS, @@ -3474,11 +3474,8 @@ const PRsList: React.FC<{ itemKeys: string[] }> = ({ itemKeys }) => { } case 'score': return cmpNum(parseFloat(a.score || '0'), parseFloat(b.score || '0')); - case 'watch': { - const key = (pr: CommitLog) => - serializePRKey(pr.repository, pr.pullRequestNumber); - return compareByWatchlist(a, b, key, isWatched) * dir; - } + case 'watch': + return comparePRsByWatchlist(a, b, isWatched) * dir; default: return 0; }