mirror of
https://github.com/stablyai/orca.git
synced 2026-10-16 16:03:26 +00:00
Use branch PR cache for sidebar status
Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
@@ -2,7 +2,13 @@ import { renderToStaticMarkup } from 'react-dom/server'
|
||||
import type { ReactNode } from 'react'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { HostedReviewInfo } from '../../../../shared/hosted-review'
|
||||
import type { GlobalSettings, Repo, Worktree, WorktreeCardProperty } from '../../../../shared/types'
|
||||
import type {
|
||||
GlobalSettings,
|
||||
PRInfo,
|
||||
Repo,
|
||||
Worktree,
|
||||
WorktreeCardProperty
|
||||
} from '../../../../shared/types'
|
||||
import { COMPACT_WORKTREE_CARD_PROPERTIES } from '../../../../shared/worktree-card-properties'
|
||||
import type { WorkspacePortScanResult } from '../../../../shared/workspace-ports'
|
||||
|
||||
@@ -14,6 +20,7 @@ const updateWorktreeMeta = vi.fn()
|
||||
|
||||
let worktreeCardProperties: WorktreeCardProperty[] = ['status']
|
||||
let hostedReviewCache: Record<string, unknown> = {}
|
||||
let prCache: Record<string, unknown> = {}
|
||||
let workspacePortScan: WorkspacePortScanResult | null = null
|
||||
let settings: Partial<GlobalSettings> | null = null
|
||||
|
||||
@@ -29,6 +36,7 @@ vi.mock('@/store', () => ({
|
||||
issueCache: {},
|
||||
linearIssueCache: {},
|
||||
openModal,
|
||||
prCache,
|
||||
projectGroups: [],
|
||||
remoteBranchConflictByWorktreeId: {},
|
||||
settings,
|
||||
@@ -121,6 +129,19 @@ function makeHostedReview(overrides: Partial<HostedReviewInfo> = {}): HostedRevi
|
||||
}
|
||||
}
|
||||
|
||||
function makePRInfo(overrides: Partial<PRInfo> = {}): PRInfo {
|
||||
return {
|
||||
number: 456,
|
||||
title: 'Fix stale GH PR',
|
||||
state: 'open',
|
||||
url: 'https://github.com/acme/orca/pull/456',
|
||||
checksStatus: 'success',
|
||||
updatedAt: '2026-05-17T00:00:00.000Z',
|
||||
mergeable: 'MERGEABLE',
|
||||
...overrides
|
||||
}
|
||||
}
|
||||
|
||||
function renderWorktreeCardMarkup(element: ReactNode): string {
|
||||
return renderToStaticMarkup(<>{element}</>)
|
||||
}
|
||||
@@ -140,6 +161,7 @@ describe('WorktreeCard linked PR display', () => {
|
||||
vi.clearAllMocks()
|
||||
worktreeCardProperties = ['status']
|
||||
hostedReviewCache = {}
|
||||
prCache = {}
|
||||
workspacePortScan = null
|
||||
settings = null
|
||||
})
|
||||
@@ -616,4 +638,34 @@ describe('WorktreeCard linked PR display', () => {
|
||||
expect(markup).not.toContain('Linked PR #456')
|
||||
expect(markup).not.toContain('CI checks')
|
||||
})
|
||||
|
||||
it('uses branch PR cache for the status slot before hosted-review metadata warms', async () => {
|
||||
settings = { experimentalNewWorktreeCardStyle: true }
|
||||
worktreeCardProperties = ['status']
|
||||
prCache = {
|
||||
'repo-1::feature/local-branch': {
|
||||
data: makePRInfo({
|
||||
number: 6340,
|
||||
title: 'Remove split terminal from onboarding checklist',
|
||||
state: 'merged',
|
||||
checksStatus: 'success'
|
||||
}),
|
||||
fetchedAt: Date.now()
|
||||
}
|
||||
}
|
||||
const { default: WorktreeCard } = await import('./WorktreeCard')
|
||||
|
||||
const markup = renderWorktreeCardMarkup(
|
||||
<WorktreeCard
|
||||
worktree={makeWorktree({ linkedPR: null })}
|
||||
repo={makeRepo()}
|
||||
isActive={false}
|
||||
/>
|
||||
)
|
||||
|
||||
expect(markup).toContain('PR: Merged')
|
||||
expect(markup).toContain('text-purple-600/70')
|
||||
expect(markup).not.toContain('Branch')
|
||||
expect(markup).not.toContain('lucide-git-branch')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3,6 +3,7 @@ import React, { useEffect, useCallback, useState } from 'react'
|
||||
import { useAppStore } from '@/store'
|
||||
import { getHostedReviewCacheKey } from '@/store/slices/hosted-review'
|
||||
import { issueCacheKey as getIssueCacheKey } from '@/store/slices/github'
|
||||
import { getGitHubPRCacheKey } from '@/store/slices/github-cache-key'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Tooltip, TooltipTrigger, TooltipContent } from '@/components/ui/tooltip'
|
||||
@@ -29,6 +30,7 @@ import { cn } from '@/lib/utils'
|
||||
import { activateWorktreeFromSidebar } from '@/lib/sidebar-worktree-activation'
|
||||
import { isFolderRepo } from '../../../../shared/repo-kind'
|
||||
import type { HostedReviewInfo } from '../../../../shared/hosted-review'
|
||||
import { hostedReviewInfoFromGitHubPRInfo } from '../../../../shared/hosted-review-github'
|
||||
import type {
|
||||
GitHubWorkItem,
|
||||
Worktree,
|
||||
@@ -398,6 +400,17 @@ const WorktreeCard = React.memo(function WorktreeCard({
|
||||
repo.executionHostId
|
||||
)
|
||||
: ''
|
||||
const prCacheKey =
|
||||
repo && branch
|
||||
? getGitHubPRCacheKey(
|
||||
repo.path,
|
||||
repo.id,
|
||||
branch,
|
||||
settings,
|
||||
repo.connectionId,
|
||||
repo.executionHostId
|
||||
)
|
||||
: ''
|
||||
const issueCacheKey =
|
||||
repo && worktree.linkedIssue
|
||||
? getIssueCacheKey(
|
||||
@@ -417,6 +430,7 @@ const WorktreeCard = React.memo(function WorktreeCard({
|
||||
const hostedReviewEntry = useAppStore((s) =>
|
||||
hostedReviewCacheKey ? s.hostedReviewCache[hostedReviewCacheKey] : undefined
|
||||
)
|
||||
const prCacheEntry = useAppStore((s) => (prCacheKey ? s.prCache[prCacheKey] : undefined))
|
||||
const issueEntry = useAppStore((s) => (issueCacheKey ? s.issueCache[issueCacheKey] : undefined))
|
||||
const linearIssueEntry = useAppStore((s) =>
|
||||
linearIssueCacheKey ? s.linearIssueCache[linearIssueCacheKey] : undefined
|
||||
@@ -427,19 +441,26 @@ const WorktreeCard = React.memo(function WorktreeCard({
|
||||
|
||||
const hostedReview: HostedReviewInfo | null | undefined =
|
||||
hostedReviewEntry !== undefined ? hostedReviewEntry.data : undefined
|
||||
// Why: ChecksPanel can discover a branch PR before hosted-review metadata
|
||||
// warms. The sidebar status slot should not fall back to "branch" while
|
||||
// the branch PR cache already knows the review state.
|
||||
const cachedBranchReview =
|
||||
hostedReview === undefined && prCacheEntry?.data
|
||||
? hostedReviewInfoFromGitHubPRInfo(prCacheEntry.data)
|
||||
: hostedReview
|
||||
const linkedGitLabMR = worktree.linkedGitLabMR ?? null
|
||||
const linkedBitbucketPR = worktree.linkedBitbucketPR ?? null
|
||||
const linkedAzureDevOpsPR = worktree.linkedAzureDevOpsPR ?? null
|
||||
const linkedGiteaPR = worktree.linkedGiteaPR ?? null
|
||||
const prDisplay = getWorktreeCardPrDisplay(
|
||||
hostedReview,
|
||||
cachedBranchReview,
|
||||
worktree.linkedPR,
|
||||
linkedGitLabMR,
|
||||
linkedBitbucketPR,
|
||||
linkedAzureDevOpsPR,
|
||||
linkedGiteaPR,
|
||||
{
|
||||
reviewHintKey: hostedReviewEntry?.linkedReviewHintKey
|
||||
reviewHintKey: hostedReviewEntry?.linkedReviewHintKey ?? (prCacheEntry?.data ? '' : undefined)
|
||||
}
|
||||
)
|
||||
const issue: IssueInfo | null | undefined = worktree.linkedIssue
|
||||
|
||||
@@ -65,15 +65,6 @@ function overlayNewCardUnreadStatus(
|
||||
|
||||
function getReviewStatusTooltip(review: WorktreeCardPrDisplay): string {
|
||||
const label = getReviewLabel(review)
|
||||
if (review.status === 'failure') {
|
||||
return `${label} checks: Failed`
|
||||
}
|
||||
if (review.status === 'pending') {
|
||||
return `${label} checks: Pending`
|
||||
}
|
||||
if (review.status === 'success') {
|
||||
return `${label} checks: Passing`
|
||||
}
|
||||
if (review.state === 'merged') {
|
||||
return `${label}: Merged`
|
||||
}
|
||||
@@ -83,6 +74,15 @@ function getReviewStatusTooltip(review: WorktreeCardPrDisplay): string {
|
||||
if (review.state === 'draft') {
|
||||
return `${label}: Draft`
|
||||
}
|
||||
if (review.status === 'failure') {
|
||||
return `${label} checks: Failed`
|
||||
}
|
||||
if (review.status === 'pending') {
|
||||
return `${label} checks: Pending`
|
||||
}
|
||||
if (review.status === 'success') {
|
||||
return `${label} checks: Passing`
|
||||
}
|
||||
return `${label}: Open`
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user