authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-02-14 23:55:46-08:00
committergravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-02-14 23:55:46-08:00
log170c3a7162525df90164e118023e3bc8bb97dda0
treece3a05b1211933b24b9158d1bc3f2653d705f265
parent1fe3cf53a7ea192bc1b440dfeb5c6a3b3a75b12d

Fix push-only progress stages in Git action toasts

- Move `buildGitActionProgressStages` into shared logic for reuse and tests - Add `forcePushOnly` path so push actions show only `Pushing...` - Cover push-only and commit+push stage behavior with new logic tests

3 files changed, 62 insertions(+), 23 deletions(-)

apps/web/src/components/GitActionsControl.logic.test.ts+22
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1import type { GitStatusResult } from "@t3tools/contracts";1import type { GitStatusResult } from "@t3tools/contracts";
2import { assert, describe, it } from "vitest";2import { assert, describe, it } from "vitest";
3import {3import {
4 buildGitActionProgressStages,
4 buildMenuItems,5 buildMenuItems,
5 requiresDefaultBranchConfirmation,6 requiresDefaultBranchConfirmation,
6 resolveQuickAction,7 resolveQuickAction,
...@@ -596,6 +597,27 @@ describe("requiresDefaultBranchConfirmation", () => {...@@ -596,6 +597,27 @@ describe("requiresDefaultBranchConfirmation", () => {
596 });597 });
597});598});
598599
600describe("buildGitActionProgressStages", () => {
601 it("shows only push progress when push-only is forced", () => {
602 const stages = buildGitActionProgressStages({
603 action: "commit_push",
604 hasCustomCommitMessage: false,
605 hasWorkingTreeChanges: true,
606 forcePushOnly: true,
607 });
608 assert.deepEqual(stages, ["Pushing..."]);
609 });
610
611 it("includes commit stages for commit+push when working tree is dirty", () => {
612 const stages = buildGitActionProgressStages({
613 action: "commit_push",
614 hasCustomCommitMessage: false,
615 hasWorkingTreeChanges: true,
616 });
617 assert.deepEqual(stages, ["Generating commit message...", "Committing...", "Pushing..."]);
618 });
619});
620
599describe("summarizeGitResult", () => {621describe("summarizeGitResult", () => {
600 it("returns commit-focused toast for commit action", () => {622 it("returns commit-focused toast for commit action", () => {
601 const result = summarizeGitResult({623 const result = summarizeGitResult({
apps/web/src/components/GitActionsControl.logic.ts+22
...@@ -43,6 +43,28 @@ function truncateText(...@@ -43,6 +43,28 @@ function truncateText(
43 return `${value.slice(0, Math.max(0, maxLength - 3)).trimEnd()}...`;43 return `${value.slice(0, Math.max(0, maxLength - 3)).trimEnd()}...`;
44}44}
4545
46export function buildGitActionProgressStages(input: {
47 action: GitStackedAction;
48 hasCustomCommitMessage: boolean;
49 hasWorkingTreeChanges: boolean;
50 forcePushOnly?: boolean;
51}): string[] {
52 const shouldIncludeCommitStages =
53 !input.forcePushOnly && (input.action === "commit" || input.hasWorkingTreeChanges);
54 const commitStages = !shouldIncludeCommitStages
55 ? []
56 : input.hasCustomCommitMessage
57 ? ["Committing..."]
58 : ["Generating commit message...", "Committing..."];
59 if (input.action === "commit") {
60 return commitStages;
61 }
62 if (input.action === "commit_push") {
63 return [...commitStages, "Pushing..."];
64 }
65 return [...commitStages, "Pushing...", "Creating PR..."];
66}
67
46export function summarizeGitResult(result: GitRunStackedActionResult): {68export function summarizeGitResult(result: GitRunStackedActionResult): {
47 title: string;69 title: string;
48 description?: string;70 description?: string;
apps/web/src/components/GitActionsControl.tsx+18-23
...@@ -4,6 +4,7 @@ import { useCallback, useMemo, useState } from "react";...@@ -4,6 +4,7 @@ import { useCallback, useMemo, useState } from "react";
4import { ChevronDownIcon, CloudUploadIcon, GitCommitIcon, InfoIcon } from "lucide-react";4import { ChevronDownIcon, CloudUploadIcon, GitCommitIcon, InfoIcon } from "lucide-react";
5import { GitHubIcon } from "./Icons";5import { GitHubIcon } from "./Icons";
6import {6import {
7 buildGitActionProgressStages,
7 buildMenuItems,8 buildMenuItems,
8 type GitActionIconName,9 type GitActionIconName,
9 type GitActionMenuItem,10 type GitActionMenuItem,
...@@ -134,26 +135,6 @@ function GitQuickActionIcon({ quickAction }: { quickAction: GitQuickAction }) {...@@ -134,26 +135,6 @@ function GitQuickActionIcon({ quickAction }: { quickAction: GitQuickAction }) {
134 return <InfoIcon className={iconClassName} />;135 return <InfoIcon className={iconClassName} />;
135}136}
136137
137function buildGitActionProgressStages(input: {
138 action: GitStackedAction;
139 hasCustomCommitMessage: boolean;
140 hasWorkingTreeChanges: boolean;
141}): string[] {
142 const shouldIncludeCommitStages = input.action === "commit" || input.hasWorkingTreeChanges;
143 const commitStages = !shouldIncludeCommitStages
144 ? []
145 : input.hasCustomCommitMessage
146 ? ["Committing..."]
147 : ["Generating commit message...", "Committing..."];
148 if (input.action === "commit") {
149 return commitStages;
150 }
151 if (input.action === "commit_push") {
152 return [...commitStages, "Pushing..."];
153 }
154 return [...commitStages, "Pushing...", "Creating PR..."];
155}
156
157export default function GitActionsControl({ api, gitCwd }: GitActionsControlProps) {138export default function GitActionsControl({ api, gitCwd }: GitActionsControlProps) {
158 const queryClient = useQueryClient();139 const queryClient = useQueryClient();
159 const [activeDialogAction, setActiveDialogAction] = useState<GitDialogAction | null>(null);140 const [activeDialogAction, setActiveDialogAction] = useState<GitDialogAction | null>(null);
...@@ -230,7 +211,15 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp...@@ -230,7 +211,15 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp
230 }, [api, gitStatus?.openPr?.url]);211 }, [api, gitStatus?.openPr?.url]);
231212
232 const runGitActionWithToast = useCallback(213 const runGitActionWithToast = useCallback(
233 async ({ action, commitMessage }: { action: GitStackedAction; commitMessage?: string }) => {214 async ({
215 action,
216 commitMessage,
217 forcePushOnlyProgress = false,
218 }: {
219 action: GitStackedAction;
220 commitMessage?: string;
221 forcePushOnlyProgress?: boolean;
222 }) => {
234 const confirmed = await maybeConfirmPushToDefaultBranch(action);223 const confirmed = await maybeConfirmPushToDefaultBranch(action);
235 if (!confirmed) return;224 if (!confirmed) return;
236225
...@@ -238,6 +227,7 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp...@@ -238,6 +227,7 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp
238 action,227 action,
239 hasCustomCommitMessage: !!commitMessage?.trim(),228 hasCustomCommitMessage: !!commitMessage?.trim(),
240 hasWorkingTreeChanges: !!gitStatus?.hasWorkingTreeChanges,229 hasWorkingTreeChanges: !!gitStatus?.hasWorkingTreeChanges,
230 forcePushOnly: forcePushOnlyProgress,
241 });231 });
242 const progressToastId = toastManager.add({232 const progressToastId = toastManager.add({
243 type: "loading",233 type: "loading",
...@@ -290,7 +280,10 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp...@@ -290,7 +280,10 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp
290 children: "Push",280 children: "Push",
291 onClick: () => {281 onClick: () => {
292 closeResultToast();282 closeResultToast();
293 void runGitActionWithToast({ action: "commit_push" });283 void runGitActionWithToast({
284 action: "commit_push",
285 forcePushOnlyProgress: true,
286 });
294 },287 },
295 },288 },
296 }289 }
...@@ -381,7 +374,7 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp...@@ -381,7 +374,7 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp
381 return;374 return;
382 }375 }
383 if (item.dialogAction === "push") {376 if (item.dialogAction === "push") {
384 void runGitActionWithToast({ action: "commit_push" });377 void runGitActionWithToast({ action: "commit_push", forcePushOnlyProgress: true });
385 return;378 return;
386 }379 }
387 if (item.dialogAction) {380 if (item.dialogAction) {
...@@ -400,10 +393,12 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp...@@ -400,10 +393,12 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp
400 ? "commit_push"393 ? "commit_push"
401 : "commit_push_pr";394 : "commit_push_pr";
402 const commitMessage = activeDialogAction === "commit" ? dialogCommitMessage.trim() : "";395 const commitMessage = activeDialogAction === "commit" ? dialogCommitMessage.trim() : "";
396 const forcePushOnlyProgress = activeDialogAction === "push";
403 setActiveDialogAction(null);397 setActiveDialogAction(null);
404 void runGitActionWithToast({398 void runGitActionWithToast({
405 action,399 action,
406 ...(commitMessage ? { commitMessage } : {}),400 ...(commitMessage ? { commitMessage } : {}),
401 ...(forcePushOnlyProgress ? { forcePushOnlyProgress } : {}),
407 });402 });
408 }, [activeDialogAction, dialogCommitMessage, runGitActionWithToast]);403 }, [activeDialogAction, dialogCommitMessage, runGitActionWithToast]);
409404