✨ feat(vscode): webview 移交/接管按钮、选人弹窗与待接管角标

Claude-Session: https://claude.ai/code/session_011cEyL6k351U2BzX1Qmygph
This commit is contained in:
cruldra committed 2026-08-26 16:41:35 +08:00
1 parent 6d3720d424
commit 6770570f09
6 files changed
+226 -3

No files matched your search

+54 -2
View File
@@ -283,6 +283,18 @@ export interface UseIssuesResult {
*/
pendingSelectId: string | null
clearPendingSelect: () => void
/** 当前 Gitea login,来自最近一次 `issues/update`;undefined = 身份未知。 */
me: string | undefined
/** 最近一次 `requestHandoffUsers` 的结果;`null` = 仍在加载或尚未请求。 */
handoffUsers: { issueNumber: number, users: string[] } | null
/** 拉取某工单可移交的候选用户列表,结果通过 `handoffUsers` 返回。 */
requestHandoffUsers: (issueNumber: number) => void
/** 发起移交:删本机 worktree、打包会话记录挂到附件、把工单指派给对方。 */
startHandoff: (issueNumber: number, to: string) => void
/** 接管一个待接管工单:拉取移交附件、恢复会话与 worktree。 */
acceptHandoff: (issueNumber: number) => void
/** 是否有移交/接管流程正在该工单上跑,直到扩展端回 `handoff/done` 才清除。 */
isHandoffRunning: (issueNumber: number) => boolean
}
export function useIssues(): UseIssuesResult {
@@ -308,6 +320,10 @@ export function useIssues(): UseIssuesResult {
const [envLockTitle, setEnvLockTitle] = useState<string>('正在检查 .env 锁定状态…')
const prDiffSummaryRunningRef = useRef<Set<number>>(new Set())
const [prDiffSummaryRunning, setPrDiffSummaryRunning] = useState<ReadonlySet<number>>(() => new Set())
const [me, setMe] = useState<string | undefined>(undefined)
const [handoffUsers, setHandoffUsers] = useState<{ issueNumber: number, users: string[] } | null>(null)
const handoffRunningRef = useRef<Set<number>>(new Set())
const [handoffRunning, setHandoffRunning] = useState<Set<number>>(new Set())
const clearPrDiffSummaryRunning = useCallback((issueNumber: number): void => {
if (!prDiffSummaryRunningRef.current.delete(issueNumber))
@@ -454,6 +470,35 @@ export function useIssues(): UseIssuesResult {
return prDiffSummaryRunning.has(issueNumber)
}, [prDiffSummaryRunning])
const requestHandoffUsers = useCallback((issueNumber: number): void => {
setHandoffUsers(null)
postMessage({ type: 'handoff/users', issueNumber })
}, [])
const markHandoffRunning = useCallback((issueNumber: number, running: boolean): void => {
if (running)
handoffRunningRef.current.add(issueNumber)
else
handoffRunningRef.current.delete(issueNumber)
setHandoffRunning(new Set(handoffRunningRef.current))
}, [])
const startHandoff = useCallback((issueNumber: number, to: string): void => {
if (handoffRunningRef.current.has(issueNumber))
return
markHandoffRunning(issueNumber, true)
postMessage({ type: 'handoff/start', issueNumber, to })
}, [markHandoffRunning])
const acceptHandoff = useCallback((issueNumber: number): void => {
if (handoffRunningRef.current.has(issueNumber))
return
markHandoffRunning(issueNumber, true)
postMessage({ type: 'handoff/accept', issueNumber })
}, [markHandoffRunning])
const isHandoffRunning = useCallback((issueNumber: number): boolean => handoffRunning.has(issueNumber), [handoffRunning])
const openPr = useCallback((pr: string): void => {
postMessage({ type: 'pr/open', pr })
}, [])
@@ -615,6 +660,7 @@ export function useIssues(): UseIssuesResult {
setScope(msg.scope)
setGlobalAutoReview(msg.globalAutoReview)
setYoutrackConfigured(msg.youtrackConfigured)
setMe(msg.me)
break
case 'issues/error':
setState({ status: 'error', message: msg.message })
@@ -636,6 +682,12 @@ export function useIssues(): UseIssuesResult {
case 'issue/pr-diff-summary-done':
clearPrDiffSummaryRunning(msg.issueNumber)
break
case 'handoff/users-result':
setHandoffUsers({ issueNumber: msg.issueNumber, users: msg.users })
break
case 'handoff/done':
markHandoffRunning(msg.issueNumber, false)
break
case 'issue/remove':
// 工单被删除或关闭 — 从 open issues 数组移除。如果它是当前选中的,挑下一张
// 同列的卡片(按该列排序取首张近邻:完成列按合并时间,其余按 number 降序);同列没有则交给 App.tsx 的
@@ -815,7 +867,7 @@ export function useIssues(): UseIssuesResult {
postMessage({ type: 'branch-sync/check' })
postMessage({ type: 'env-lock/check' })
return cleanup
}, [clearPrDiffSummaryRunning])
}, [clearPrDiffSummaryRunning, markHandoffRunning])
return { state, settings, globalAutoReview, youtrackConfigured, importYouTrack, toasts, profiles, setIssues, refresh, scope, toggleScope, saveSettings, listYouTrackProjects, youtrackProjects, dismissSettings, requestEditAuth, createIssue, dismissToast, openUrl, resumeSession, resumeReviewSession, startTestSession, resumeTestSession, focusSession, openFile, implement, generatePrDiffSummary, isPrDiffSummaryRunning, openPr, openWorktree, deleteWorktree, mergeBranch, deleteIssue, closeIssue, closeSessionTab, startBrainstormSession, changeColumn, setDependency, clearDependency, updateIssueAutoReview, updateIssueProfilePath, updateIssueBrainstormProfilePath, updateIssueTestProfilePath, logs, fetchLogs, clearLogs, pendingSelectId, clearPendingSelect, commitRunning, runCommit, hasChanges, branchSyncBehind, branchSyncRunning, branchSyncDisabled, branchSyncTitle, runBranchSync, envLocked, envFileCount, envLockRunning, envLockTitle, toggleEnvLock }
return { state, settings, globalAutoReview, youtrackConfigured, importYouTrack, toasts, profiles, setIssues, refresh, scope, toggleScope, saveSettings, listYouTrackProjects, youtrackProjects, dismissSettings, requestEditAuth, createIssue, dismissToast, openUrl, resumeSession, resumeReviewSession, startTestSession, resumeTestSession, focusSession, openFile, implement, generatePrDiffSummary, isPrDiffSummaryRunning, openPr, openWorktree, deleteWorktree, mergeBranch, deleteIssue, closeIssue, closeSessionTab, startBrainstormSession, changeColumn, setDependency, clearDependency, updateIssueAutoReview, updateIssueProfilePath, updateIssueBrainstormProfilePath, updateIssueTestProfilePath, logs, fetchLogs, clearLogs, pendingSelectId, clearPendingSelect, commitRunning, runCommit, hasChanges, branchSyncBehind, branchSyncRunning, branchSyncDisabled, branchSyncTitle, runBranchSync, envLocked, envFileCount, envLockRunning, envLockTitle, toggleEnvLock, me, handoffUsers, requestHandoffUsers, startHandoff, acceptHandoff, isHandoffRunning }
}