fix: simplify existing pull request header
Reflow PR status and checks into a cleaner two-row header Remove the remote selector from existing PRs Keep remote context as read-only tracking info
This commit is contained in:
@@ -28,12 +28,6 @@ import {
|
|||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from '@/components/ui/dialog';
|
} from '@/components/ui/dialog';
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from '@/components/ui/dropdown-menu';
|
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||||
import { Textarea } from '@/components/ui/textarea';
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
@@ -448,8 +442,6 @@ export const PullRequestSection: React.FC<{
|
|||||||
return Array.from(unique).sort((a, b) => a.localeCompare(b));
|
return Array.from(unique).sort((a, b) => a.localeCompare(b));
|
||||||
}, [baseBranch, remoteBranches, selectedRemote?.name, targetBaseBranch, upstreamBranches, useDetectedUpstream]);
|
}, [baseBranch, remoteBranches, selectedRemote?.name, targetBaseBranch, upstreamBranches, useDetectedUpstream]);
|
||||||
|
|
||||||
const hasMultipleRemotes = remotes.length > 1;
|
|
||||||
|
|
||||||
// Update selected remote when remotes change
|
// Update selected remote when remotes change
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (remotes.length === 0) {
|
if (remotes.length === 0) {
|
||||||
@@ -1018,12 +1010,6 @@ export const PullRequestSection: React.FC<{
|
|||||||
}, delayMs));
|
}, delayMs));
|
||||||
}, [refresh]);
|
}, [refresh]);
|
||||||
|
|
||||||
// Refetch PR status when selected remote changes
|
|
||||||
const handleRemoteChange = React.useCallback((remote: GitRemote) => {
|
|
||||||
didUserOverrideRemoteRef.current = true;
|
|
||||||
setSelectedRemote((prev) => (prev?.name === remote.name ? prev : remote));
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!github?.prStatus || !canShow || remotes.length <= 1) {
|
if (!github?.prStatus || !canShow || remotes.length <= 1) {
|
||||||
return;
|
return;
|
||||||
@@ -1397,7 +1383,20 @@ export const PullRequestSection: React.FC<{
|
|||||||
: prVisualState === 'closed'
|
: prVisualState === 'closed'
|
||||||
? RiGitClosePullRequestLine
|
? RiGitClosePullRequestLine
|
||||||
: RiGitPullRequestLine;
|
: RiGitPullRequestLine;
|
||||||
|
const prStatusText = pr
|
||||||
|
? [
|
||||||
|
`${pr.state}${pr.draft ? ' (draft)' : ''}`,
|
||||||
|
pr.mergeable === false ? t('gitView.pr.notMergeable') : null,
|
||||||
|
pr.state === 'open' && typeof pr.mergeableState === 'string' && pr.mergeableState && pr.mergeableState !== 'unknown'
|
||||||
|
? pr.mergeableState
|
||||||
|
: null,
|
||||||
|
].filter(Boolean).join(' · ')
|
||||||
|
: '';
|
||||||
|
const checksText = checks
|
||||||
|
? checks.total > 0
|
||||||
|
? `${checks.success}/${checks.total} ${t('gitView.pr.checks.label')}`
|
||||||
|
: `${checks.state} ${t('gitView.pr.checks.label')}`
|
||||||
|
: '';
|
||||||
const containerClassName = 'border-0 bg-transparent rounded-none';
|
const containerClassName = 'border-0 bg-transparent rounded-none';
|
||||||
const headerClassName = 'px-0 py-3 border-b border-border/40 flex flex-col gap-1';
|
const headerClassName = 'px-0 py-3 border-b border-border/40 flex flex-col gap-1';
|
||||||
const bodyClassName = 'flex flex-col gap-3 py-3';
|
const bodyClassName = 'flex flex-col gap-3 py-3';
|
||||||
@@ -1405,8 +1404,8 @@ export const PullRequestSection: React.FC<{
|
|||||||
return (
|
return (
|
||||||
<section className={containerClassName}>
|
<section className={containerClassName}>
|
||||||
<div className={headerClassName}>
|
<div className={headerClassName}>
|
||||||
<div className="flex items-center justify-between gap-2">
|
<div className="flex items-start justify-between gap-2">
|
||||||
<div className="flex items-center gap-2 min-w-0">
|
<div className="flex min-w-0 items-center gap-2">
|
||||||
{pr ? (
|
{pr ? (
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
@@ -1429,7 +1428,7 @@ export const PullRequestSection: React.FC<{
|
|||||||
<span className="typography-meta text-muted-foreground truncate">#{pr.number}</span>
|
<span className="typography-meta text-muted-foreground truncate">#{pr.number}</span>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex shrink-0 items-center gap-1">
|
||||||
{isLoading ? <RiLoader4Line className="size-4 animate-spin text-muted-foreground" /> : null}
|
{isLoading ? <RiLoader4Line className="size-4 animate-spin text-muted-foreground" /> : null}
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
@@ -1445,83 +1444,23 @@ export const PullRequestSection: React.FC<{
|
|||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent><p>{t('gitView.pr.actions.refresh')}</p></TooltipContent>
|
<TooltipContent><p>{t('gitView.pr.actions.refresh')}</p></TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{pr ? (
|
||||||
|
<div className="flex flex-wrap items-center gap-x-2 gap-y-1 typography-micro text-muted-foreground">
|
||||||
|
<span style={{ color: prColorVar }}>{prStatusText}</span>
|
||||||
{checks ? (
|
{checks ? (
|
||||||
<span className="inline-flex items-center gap-2 typography-micro text-muted-foreground">
|
<span className="inline-flex items-center gap-1.5">
|
||||||
<span className={`h-2 w-2 rounded-full ${statusColor(checks.state)}`} />
|
<span className={`h-2 w-2 rounded-full ${statusColor(checks.state)}`} />
|
||||||
{checks.total > 0 ? `${checks.success}/${checks.total} checks` : `${checks.state} checks`}
|
{checksText}
|
||||||
</span>
|
</span>
|
||||||
) : null}
|
) : null}
|
||||||
{trackingBranch && selectedRemote && trackingBranch.split('/')[0] !== selectedRemote.name ? (
|
{trackingBranch && selectedRemote && trackingBranch.split('/')[0] !== selectedRemote.name ? (
|
||||||
<span className="typography-micro text-muted-foreground">
|
<span className="min-w-0 truncate">
|
||||||
{trackingBranch.split('/')[0]} → {selectedRemote.name}
|
{trackingBranch.split('/')[0]} → {selectedRemote.name}
|
||||||
</span>
|
</span>
|
||||||
) : null}
|
) : null}
|
||||||
{hasMultipleRemotes || detectedUpstream ? (
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<Button variant="ghost" size="xs" className="gap-1">
|
|
||||||
<span className="typography-micro">
|
|
||||||
{useDetectedUpstream && detectedUpstream
|
|
||||||
? `upstream · ${detectedUpstream.owner}/${detectedUpstream.repo}`
|
|
||||||
: selectedRemote?.name ?? 'target'}
|
|
||||||
</span>
|
|
||||||
<RiArrowDownSLine className="size-3" />
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent align="end" className="min-w-[200px]">
|
|
||||||
{remotes.map((remote) => (
|
|
||||||
<DropdownMenuItem
|
|
||||||
key={remote.name}
|
|
||||||
onSelect={() => {
|
|
||||||
setUseDetectedUpstream(false);
|
|
||||||
handleRemoteChange(remote);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<span className="typography-ui-label text-foreground">
|
|
||||||
{remote.name}
|
|
||||||
{!useDetectedUpstream && remote.name === selectedRemote?.name && (
|
|
||||||
<span className="ml-2 text-primary">✓</span>
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
<span className="typography-meta text-muted-foreground truncate">
|
|
||||||
{remote.pushUrl}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
))}
|
|
||||||
{detectedUpstream ? (
|
|
||||||
<DropdownMenuItem
|
|
||||||
key="detected-upstream"
|
|
||||||
onSelect={() => setUseDetectedUpstream(true)}
|
|
||||||
>
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<span className="typography-ui-label text-foreground">
|
|
||||||
upstream · {detectedUpstream.owner}/{detectedUpstream.repo}
|
|
||||||
{useDetectedUpstream && (
|
|
||||||
<span className="ml-2 text-primary">✓</span>
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
<span className="typography-meta text-muted-foreground truncate">
|
|
||||||
{detectedUpstream.url}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
) : null}
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{pr ? (
|
|
||||||
<div className="typography-micro text-muted-foreground">
|
|
||||||
<span style={{ color: prColorVar }}>
|
|
||||||
{pr.state}{pr.draft ? ' (draft)' : ''}
|
|
||||||
</span>
|
|
||||||
{pr.mergeable === false ? ` · ${t('gitView.pr.notMergeable')}` : ''}
|
|
||||||
{pr.state === 'open' && typeof pr.mergeableState === 'string' && pr.mergeableState && pr.mergeableState !== 'unknown'
|
|
||||||
? ` · ${pr.mergeableState}`
|
|
||||||
: ''}
|
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -615,6 +615,7 @@ export const dict = {
|
|||||||
'gitView.pr.checkDetails.empty': 'No check details available',
|
'gitView.pr.checkDetails.empty': 'No check details available',
|
||||||
'gitView.pr.checkDetails.title': 'Failed check details',
|
'gitView.pr.checkDetails.title': 'Failed check details',
|
||||||
'gitView.pr.checkingStatus': 'Checking pull request status...',
|
'gitView.pr.checkingStatus': 'Checking pull request status...',
|
||||||
|
'gitView.pr.checks.label': 'checks',
|
||||||
'gitView.pr.checks.completedLabel': 'Completed',
|
'gitView.pr.checks.completedLabel': 'Completed',
|
||||||
'gitView.pr.checks.conclusionLabel': 'Conclusion',
|
'gitView.pr.checks.conclusionLabel': 'Conclusion',
|
||||||
'gitView.pr.checks.startedLabel': 'Started',
|
'gitView.pr.checks.startedLabel': 'Started',
|
||||||
|
|||||||
@@ -616,6 +616,7 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
"gitView.pr.checkDetails.empty": "No hay detalles del check disponibles",
|
"gitView.pr.checkDetails.empty": "No hay detalles del check disponibles",
|
||||||
"gitView.pr.checkDetails.title": "Detalles del check fallido",
|
"gitView.pr.checkDetails.title": "Detalles del check fallido",
|
||||||
"gitView.pr.checkingStatus": "Comprobando estado de la PR...",
|
"gitView.pr.checkingStatus": "Comprobando estado de la PR...",
|
||||||
|
"gitView.pr.checks.label": "checks",
|
||||||
"gitView.pr.checks.completedLabel": "Completado",
|
"gitView.pr.checks.completedLabel": "Completado",
|
||||||
"gitView.pr.checks.conclusionLabel": "Conclusión",
|
"gitView.pr.checks.conclusionLabel": "Conclusión",
|
||||||
"gitView.pr.checks.startedLabel": "Iniciado",
|
"gitView.pr.checks.startedLabel": "Iniciado",
|
||||||
|
|||||||
@@ -616,6 +616,7 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
'gitView.pr.checkDetails.empty': '체크 상세 정보 없음',
|
'gitView.pr.checkDetails.empty': '체크 상세 정보 없음',
|
||||||
'gitView.pr.checkDetails.title': '실패한 체크 상세',
|
'gitView.pr.checkDetails.title': '실패한 체크 상세',
|
||||||
'gitView.pr.checkingStatus': 'PR 상태 확인 중…',
|
'gitView.pr.checkingStatus': 'PR 상태 확인 중…',
|
||||||
|
'gitView.pr.checks.label': 'checks',
|
||||||
'gitView.pr.checks.completedLabel': '완료됨',
|
'gitView.pr.checks.completedLabel': '완료됨',
|
||||||
'gitView.pr.checks.conclusionLabel': '결론',
|
'gitView.pr.checks.conclusionLabel': '결론',
|
||||||
'gitView.pr.checks.startedLabel': '시작됨',
|
'gitView.pr.checks.startedLabel': '시작됨',
|
||||||
|
|||||||
@@ -616,6 +616,7 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
"gitView.pr.checkDetails.empty": "Não há detalhes do check disponíveis",
|
"gitView.pr.checkDetails.empty": "Não há detalhes do check disponíveis",
|
||||||
"gitView.pr.checkDetails.title": "Detalhes do check falhou",
|
"gitView.pr.checkDetails.title": "Detalhes do check falhou",
|
||||||
"gitView.pr.checkingStatus": "Verificando status da PR...",
|
"gitView.pr.checkingStatus": "Verificando status da PR...",
|
||||||
|
"gitView.pr.checks.label": "checks",
|
||||||
"gitView.pr.checks.completedLabel": "Concluído",
|
"gitView.pr.checks.completedLabel": "Concluído",
|
||||||
"gitView.pr.checks.conclusionLabel": "Conclusión",
|
"gitView.pr.checks.conclusionLabel": "Conclusión",
|
||||||
"gitView.pr.checks.startedLabel": "Iniciado",
|
"gitView.pr.checks.startedLabel": "Iniciado",
|
||||||
|
|||||||
@@ -616,6 +616,7 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
"gitView.pr.checkDetails.empty": "Деталі перевірки відсутні",
|
"gitView.pr.checkDetails.empty": "Деталі перевірки відсутні",
|
||||||
"gitView.pr.checkDetails.title": "Деталі перевірки",
|
"gitView.pr.checkDetails.title": "Деталі перевірки",
|
||||||
"gitView.pr.checkingStatus": "Перевірка статусу PR...",
|
"gitView.pr.checkingStatus": "Перевірка статусу PR...",
|
||||||
|
"gitView.pr.checks.label": "checks",
|
||||||
"gitView.pr.checks.completedLabel": "Виконано",
|
"gitView.pr.checks.completedLabel": "Виконано",
|
||||||
"gitView.pr.checks.conclusionLabel": "Висновок",
|
"gitView.pr.checks.conclusionLabel": "Висновок",
|
||||||
"gitView.pr.checks.startedLabel": "Почато",
|
"gitView.pr.checks.startedLabel": "Почато",
|
||||||
|
|||||||
@@ -616,6 +616,7 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
'gitView.pr.checkDetails.empty': '没有可用的检查详情',
|
'gitView.pr.checkDetails.empty': '没有可用的检查详情',
|
||||||
'gitView.pr.checkDetails.title': '失败检查详情',
|
'gitView.pr.checkDetails.title': '失败检查详情',
|
||||||
'gitView.pr.checkingStatus': '正在检查拉取请求状态...',
|
'gitView.pr.checkingStatus': '正在检查拉取请求状态...',
|
||||||
|
'gitView.pr.checks.label': 'checks',
|
||||||
'gitView.pr.checks.completedLabel': '完成时间',
|
'gitView.pr.checks.completedLabel': '完成时间',
|
||||||
'gitView.pr.checks.conclusionLabel': '结论',
|
'gitView.pr.checks.conclusionLabel': '结论',
|
||||||
'gitView.pr.checks.startedLabel': '开始时间',
|
'gitView.pr.checks.startedLabel': '开始时间',
|
||||||
|
|||||||
Reference in New Issue
Block a user