49 lines · 1.9 KB · TypeScript
RawDownload
import type { ThreadPullRequestLink } from "@t3tools/contracts";import type { ThreadPullRequestChain } from "@t3tools/shared/threadPullRequests"; /** One line of a thread's pull-request list: a link plus how deep it sits in its stack. */export interface PullRequestListLine {  readonly link: ThreadPullRequestLink;  /** 0 for a pull request on the base branch; each layer above steps in by one. */  readonly depth: number;  /** Which chain the line belongs to, so callers can tell one stack's lines from another's. */  readonly chainKey: string;  /** Set on the bottom layer of a multi-layer stack, so that row can name the whole stack. */  readonly stack: { readonly kind: ThreadPullRequestChain["kind"]; readonly size: number } | null;} function activityAt(link: ThreadPullRequestLink): number {  const ms = Date.parse(link.snapshot?.updatedAt ?? link.linkedAt);  return Number.isNaN(ms) ? 0 : ms;} function chainKeyOf(chain: ThreadPullRequestChain): string {  const bottom = chain.layers[0]!;  return `${bottom.host}/${bottom.repository}#${bottom.number}`;} /** * Flattens chains into indented lines, newest first. A stack sorts by its most recent layer and * then reads bottom to top beneath that slot, so the layer you would review first is at the * bottom of the indent and a fresh push anywhere in the stack floats the whole stack up. */export function pullRequestListLines(  chains: ReadonlyArray<ThreadPullRequestChain>,): ReadonlyArray<PullRequestListLine> {  const ordered = [...chains].sort(    (left, right) =>      Math.max(...right.layers.map(activityAt)) - Math.max(...left.layers.map(activityAt)),  );  return ordered.flatMap((chain) => {    const chainKey = chainKeyOf(chain);    return chain.layers.map((link, depth) => ({      link,      depth,      chainKey,      stack:        depth === 0 && chain.layers.length > 1          ? { kind: chain.kind, size: chain.layers.length }          : null,    }));  });}