Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ anchor: function ChannelDetails
created: 2026-10-15T02:58:24Z
norm: '1'
sig: b49433e7f0d6d4f3
body_hash: c78ae37103f4096d
raw_hash: 7d353ff7240b0bf1
vouched: 2026-10-22T16:29:20Z
body_hash: 0ae4093a683b3fe7
raw_hash: 92ed025366ce01d8
vouched: 2026-10-22T16:53:07Z
lines: 161-209
---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ anchor: function usePull
created: 2026-10-16T22:11:37Z
norm: '1'
sig: a9da5d18c3817c7c
body_hash: 1d9b3fa757ffe35b
raw_hash: 32a07fd4de2d6171
body_hash: 745f3ff60655c30b
raw_hash: e2e769d80fa9b661
vouched: 2026-10-22T16:53:07Z
lines: 191-218
---

Expand Down
17 changes: 13 additions & 4 deletions apps/app/src/channel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ const dollars = new Intl.NumberFormat("en-US", { style: "currency", currency: "U
const date = new Intl.DateTimeFormat(undefined, { dateStyle: "medium", timeStyle: "short" });
// Open checks change on their own, so the shown channel's pull request is read again this often.
const PULL = 60_000;
// While a merge or close settles, so the pull request's new state shows within seconds.
const SETTLING = 3_000;

/** Links people and agents wrote in the chat, newest mention first; tool output is left out. */
function links(items: Item[], repo?: string): DetailsLink[] {
Expand Down Expand Up @@ -194,6 +196,7 @@ function usePull(
branch: string | undefined,
head: string | undefined,
busy: boolean,
settling: boolean,
) {
const [, render] = useReducer((count: number) => count + 1, 0);
const key = repo && branch ? `${repo}\0${branch}` : undefined;
Expand All @@ -209,22 +212,28 @@ function usePull(
() => {},
);
void read();
const timer = setInterval(read, PULL);
const timer = setInterval(read, settling ? SETTLING : PULL);
return () => {
active = false;
clearInterval(timer);
};
}, [repo, branch, head, busy]);
}, [repo, branch, head, busy, settling]);
return key ? pulls.get(key) : undefined;
}

export function ChannelDetails({ channel, user }: { channel: Listing; user: string }) {
const right = useLayoutRight();
const details = useDetails(channel.id);
const channels = useSyncExternalStore(host.subscribe, () => host.channels);
const { changes, info, items } = details;
const { changes, info, items, asked } = details;
const busy = !!info?.chats.find((value) => value.id === chat)?.busy;
const pull = usePull(channel.repo, changes?.branch, changes?.head, busy);
const pull = usePull(
channel.repo,
changes?.branch,
changes?.head,
busy,
asked === "merge" || asked === "close",
);
useEffect(() => publish(channel.id, { pull }), [channel.id, pull]);
useAsked(channel.id, busy, pull);
const git = useGit(channel, chat, user);
Expand Down
18 changes: 17 additions & 1 deletion apps/app/src/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,8 @@ export function useGit(channel: Listing, chat: number, user: string) {
};
}

const SETTLE = 15_000;

/**
* Ends an action's pending state once the run it started ends, or for Create PR once the pull
* request exists. Called once per shown channel, by its details, which stay mounted while the
Expand All @@ -184,15 +186,29 @@ export function useGit(channel: Listing, chat: number, user: string) {
export function useAsked(channel: string, busy: boolean, pull: GithubPull | null | undefined) {
const { asked } = useDetails(channel);
const ran = useRef(false);
const ended = useRef(0);
useEffect(() => {
if (!asked) {
ran.current = false;
ended.current = 0;
return;
}
// A closed or merged pull request from before isn't the one Create PR asked for.
const live = pull?.state === "open" || pull?.state === "draft";
if (asked === "create" && live) return publish(channel, { asked: null });
if (busy) ran.current = true;
else if (ran.current) publish(channel, { asked: null });
else if (ran.current) {
// Merge and Close stay pending until GitHub reports the pull request settled, so the stale
// Merge PR doesn't come back after the run; a refused merge shows it again after SETTLE.
if ((asked === "merge" || asked === "close") && live) {
ended.current ||= Date.now();
const timer = setTimeout(
() => publish(channel, { asked: null }),
ended.current + SETTLE - Date.now(),
);
return () => clearTimeout(timer);
}
publish(channel, { asked: null });
}
}, [channel, asked, busy, pull]);
}