62 lines
3.2 KiB
TypeScript
62 lines
3.2 KiB
TypeScript
import type { Issue } from '../../gitea/types'
|
|
import { columnLabel, prNumberOf } from './reviewRules'
|
|
|
|
/** PR 改了几个文件:还在读、没读到、读到了。 */
|
|
export type FileCount = 'loading' | 'failed' | { count: number }
|
|
|
|
/** 选工单弹框里一项背后的数据;工单没有关联 PR 时没有 pr。 */
|
|
export interface PickEntry<T extends Pick<Issue, 'number' | 'title'>> {
|
|
issue: T
|
|
pr?: { number: number, files: FileCount, hasWorktree: boolean }
|
|
}
|
|
|
|
/** 弹框里一项的字(QuickPickItem 的 label / description / detail)。 */
|
|
export interface PickText {
|
|
label: string
|
|
description?: string
|
|
detail: string
|
|
}
|
|
|
|
/** 弹框一打开就摆出来的各项:有 PR 的文件数先写「正在读」,读到一个换一个(withFileCount)。 */
|
|
export function pickEntries<T extends Pick<Issue, 'number' | 'title' | 'pr'>>(issues: readonly T[], hasWorktree: (issue: T) => boolean): PickEntry<T>[] {
|
|
return issues.map((issue) => {
|
|
const pr = prNumberOf(issue)
|
|
return pr === undefined ? { issue } : { issue, pr: { number: pr, files: 'loading', hasWorktree: hasWorktree(issue) } }
|
|
})
|
|
}
|
|
|
|
/** 读到(或没读到)一张工单的 PR 改了几个文件:只换掉那一项,别的项原样(同一个对象),次序不变。 */
|
|
export function withFileCount<T extends Pick<Issue, 'number' | 'title'>>(entries: readonly PickEntry<T>[], issueNumber: number, files: FileCount): PickEntry<T>[] {
|
|
return entries.map(entry => entry.issue.number === issueNumber && entry.pr ? { ...entry, pr: { ...entry.pr, files } } : entry)
|
|
}
|
|
|
|
/** 弹框里的一行:分组标题(列名)或一项。 */
|
|
export type PickRow<T extends Pick<Issue, 'number' | 'title' | 'column'>> = { separator: string } | { entry: PickEntry<T> }
|
|
|
|
/** 给按列排好的各项插上分组标题:列一变先插一行列名(没有工单的列不出标题)。 */
|
|
export function pickRows<T extends Pick<Issue, 'number' | 'title' | 'column'>>(entries: readonly PickEntry<T>[]): PickRow<T>[] {
|
|
return entries.flatMap((entry, index) => index > 0 && entries[index - 1].issue.column === entry.issue.column
|
|
? [{ entry }]
|
|
: [{ separator: columnLabel(entry.issue.column) }, { entry }])
|
|
}
|
|
|
|
/** 还有 PR 的文件数没读完:弹框接着转圈。 */
|
|
export function countingFiles(entries: readonly PickEntry<Pick<Issue, 'number' | 'title'>>[]): boolean {
|
|
return entries.some(entry => entry.pr?.files === 'loading')
|
|
}
|
|
|
|
/**
|
|
* 弹框里的一项:第一行工单号 + 标题,后面淡色 PR 号;第二行改了几个文件、本机有没有这张工单的工作副本。
|
|
* 没有关联 PR 的第二行只写「没有关联 PR」。
|
|
*/
|
|
export function pickItemText({ issue, pr }: PickEntry<Pick<Issue, 'number' | 'title'>>): PickText {
|
|
const label = `#${issue.number} ${issue.title}`
|
|
if (!pr)
|
|
return { label, detail: '没有关联 PR' }
|
|
const files = pr.files === 'loading'
|
|
? '正在读改动文件数…'
|
|
: pr.files === 'failed' ? '读不到改动文件数' : `改了 ${pr.files.count} 个文件`
|
|
const worktree = pr.hasWorktree ? '本机有工作副本' : '本机没有工作副本,选中后拉取 PR 分支'
|
|
return { label, description: `PR #${pr.number}`, detail: `${files} · ${worktree}` }
|
|
}
|