diff --git a/src-tauri/src/commands/folders.rs b/src-tauri/src/commands/folders.rs index 76240c6..939f1c1 100644 --- a/src-tauri/src/commands/folders.rs +++ b/src-tauri/src/commands/folders.rs @@ -194,6 +194,13 @@ struct GitCommitSucceededEvent { committed_files: usize, } +#[derive(Debug, Clone, Serialize)] +struct GitPushSucceededEvent { + folder_id: i32, + pushed_commits: usize, + upstream_set: bool, +} + struct FileWatchEntry { root_canonical: PathBuf, root_display: String, @@ -841,10 +848,11 @@ pub async fn git_fetch( #[tauri::command] pub async fn git_push( + app: tauri::AppHandle, + window: tauri::WebviewWindow, path: String, credentials: Option, db: tauri::State<'_, AppDatabase>, - app_handle: tauri::AppHandle, ) -> Result { let pushed_commits = estimate_push_commit_count(&path).await; @@ -873,12 +881,12 @@ pub async fn git_push( let mut cmd = crate::process::tokio_command("git"); cmd.args(["push", "--set-upstream", "origin", &branch]) .current_dir(&path); - prepare_remote_git_cmd(&mut cmd, &path, credentials.as_ref(), &db, &app_handle).await; + prepare_remote_git_cmd(&mut cmd, &path, credentials.as_ref(), &db, &app).await; cmd.output().await.map_err(AppCommandError::io)? } else { let mut cmd = crate::process::tokio_command("git"); cmd.args(["push"]).current_dir(&path); - prepare_remote_git_cmd(&mut cmd, &path, credentials.as_ref(), &db, &app_handle).await; + prepare_remote_git_cmd(&mut cmd, &path, credentials.as_ref(), &db, &app).await; cmd.output().await.map_err(AppCommandError::io)? }; @@ -886,9 +894,26 @@ pub async fn git_push( return Err(classify_remote_git_error("push", &output.stderr)); } + let upstream_set = !has_upstream; + + if let Some(folder_id) = window + .label() + .strip_prefix("push-") + .and_then(|value| value.parse::().ok()) + { + let _ = app.emit( + "folder://git-push-succeeded", + GitPushSucceededEvent { + folder_id, + pushed_commits, + upstream_set, + }, + ); + } + Ok(GitPushResult { pushed_commits, - upstream_set: !has_upstream, + upstream_set, }) } diff --git a/src/app/push/page.tsx b/src/app/push/page.tsx index 8d432bd..a47183a 100644 --- a/src/app/push/page.tsx +++ b/src/app/push/page.tsx @@ -1,8 +1,9 @@ "use client" -import { Suspense, useEffect, useState } from "react" +import { Suspense, useCallback, useEffect, useState } from "react" import { useSearchParams } from "next/navigation" import { useTranslations } from "next-intl" +import { getCurrentWindow } from "@tauri-apps/api/window" import { Loader2 } from "lucide-react" import { PushWorkspace } from "@/components/layout/push-workspace" import { AppTitleBar } from "@/components/layout/app-title-bar" @@ -27,6 +28,14 @@ function PushPageInner() { error: null, }) + const closeWindow = useCallback(() => { + getCurrentWindow() + .close() + .catch((err) => { + console.error("[PushPage] failed to close window:", err) + }) + }, []) + const folderId = Number(searchParams.get("folderId") ?? "0") const normalizedFolderId = Number.isFinite(folderId) ? folderId : 0 const hasValidFolderId = normalizedFolderId > 0 @@ -89,7 +98,11 @@ function PushPageInner() { {error} ) : folder ? ( - + ) : null} diff --git a/src/components/layout/branch-dropdown.tsx b/src/components/layout/branch-dropdown.tsx index 8218f4d..f27a868 100644 --- a/src/components/layout/branch-dropdown.tsx +++ b/src/components/layout/branch-dropdown.tsx @@ -108,6 +108,12 @@ interface GitCommitSucceededEventPayload { committed_files: number } +interface GitPushSucceededEventPayload { + folder_id: number + pushed_commits: number + upstream_set: boolean +} + export function BranchDropdown({ branch, parentBranch, @@ -187,6 +193,43 @@ export function BranchDropdown({ } }, [folder, onBranchChange, t]) + useEffect(() => { + if (!folder) return + + let unlisten: UnlistenFn | null = null + + listen( + "folder://git-push-succeeded", + (event) => { + if (event.payload.folder_id !== folder.id) return + const { pushed_commits, upstream_set } = event.payload + let description: string + if (upstream_set) { + description = + pushed_commits === 0 + ? t("toasts.upstreamSet") + : t("toasts.upstreamSetAndPushed", { count: pushed_commits }) + } else if (pushed_commits === 0) { + description = t("toasts.noCommitsToPush") + } else { + description = t("toasts.pushedCommits", { count: pushed_commits }) + } + toast.success(t("toasts.pushCodeCompleted"), { description }) + onBranchChange() + } + ) + .then((fn) => { + unlisten = fn + }) + .catch((err) => { + console.error("[BranchDropdown] failed to listen push event:", err) + }) + + return () => { + disposeTauriListener(unlisten, "BranchDropdown.gitPushSucceeded") + } + }, [folder, onBranchChange, t]) + async function runGitTask( label: string, action: () => Promise, diff --git a/src/components/layout/push-workspace.tsx b/src/components/layout/push-workspace.tsx index ebf620e..cf8f7c2 100644 --- a/src/components/layout/push-workspace.tsx +++ b/src/components/layout/push-workspace.tsx @@ -266,9 +266,14 @@ function parseDate(dateStr: string): Date | null { interface PushWorkspaceProps { folderPath: string folderName: string + onPushed?: () => void } -export function PushWorkspace({ folderPath, folderName }: PushWorkspaceProps) { +export function PushWorkspace({ + folderPath, + folderName, + onPushed, +}: PushWorkspaceProps) { const t = useTranslations("Folder.pushWindow") const tLog = useTranslations("Folder.gitLogTab") const { withCredentialRetry } = useGitCredential() @@ -327,30 +332,10 @@ export function PushWorkspace({ folderPath, folderName }: PushWorkspaceProps) { async function handlePush() { setPushing(true) try { - const result = await withCredentialRetry( - (creds) => gitPush(folderPath, creds), - { folderPath } - ) - let description: string | undefined - if (result.upstream_set) { - description = - result.pushed_commits === 0 - ? t("toasts.upstreamSet") - : t("toasts.upstreamSetAndPushed", { - count: result.pushed_commits, - }) - } else if (result.pushed_commits === 0) { - description = t("toasts.noCommitsToPush") - } else { - description = t("toasts.pushedCommits", { - count: result.pushed_commits, - }) - } - toast.success(t("toasts.pushSuccess"), { description }) - await loadCommits() - setSelectedFile(null) - setSelectedCommit(null) - setOpenByCommit({}) + await withCredentialRetry((creds) => gitPush(folderPath, creds), { + folderPath, + }) + onPushed?.() } catch (err) { toast.error(t("toasts.pushFailed"), { description: toErrorMessage(err), diff --git a/src/i18n/messages/ar.json b/src/i18n/messages/ar.json index 9238b95..8043db4 100644 --- a/src/i18n/messages/ar.json +++ b/src/i18n/messages/ar.json @@ -875,6 +875,7 @@ "branchDropdown": { "toasts": { "commitCodeCompleted": "اكتمل التزام الكود", + "pushCodeCompleted": "اكتمل دفع الكود", "committedFiles": "{count, plural, one {# ملف تم الالتزام به} other {# ملفات تم الالتزام بها}}", "taskCompleted": "اكتمل {label}", "taskFailed": "فشل {label}", diff --git a/src/i18n/messages/de.json b/src/i18n/messages/de.json index c5360ce..8c05c1a 100644 --- a/src/i18n/messages/de.json +++ b/src/i18n/messages/de.json @@ -875,6 +875,7 @@ "branchDropdown": { "toasts": { "commitCodeCompleted": "Code-Commit abgeschlossen", + "pushCodeCompleted": "Code-Push abgeschlossen", "committedFiles": "{count, plural, one {# Datei committet} other {# Dateien committet}}", "taskCompleted": "{label} abgeschlossen", "taskFailed": "{label} fehlgeschlagen", diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index dc52258..ba48ae7 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -875,6 +875,7 @@ "branchDropdown": { "toasts": { "commitCodeCompleted": "Code commit completed", + "pushCodeCompleted": "Code push completed", "committedFiles": "Committed {count, plural, one {# file} other {# files}}", "taskCompleted": "{label} completed", "taskFailed": "{label} failed", diff --git a/src/i18n/messages/es.json b/src/i18n/messages/es.json index f012064..6386d2d 100644 --- a/src/i18n/messages/es.json +++ b/src/i18n/messages/es.json @@ -875,6 +875,7 @@ "branchDropdown": { "toasts": { "commitCodeCompleted": "Commit de código completado", + "pushCodeCompleted": "Push de código completado", "committedFiles": "{count, plural, one {# archivo confirmado} other {# archivos confirmados}}", "taskCompleted": "{label} completado", "taskFailed": "{label} falló", diff --git a/src/i18n/messages/fr.json b/src/i18n/messages/fr.json index 5711b75..f6c2f41 100644 --- a/src/i18n/messages/fr.json +++ b/src/i18n/messages/fr.json @@ -875,6 +875,7 @@ "branchDropdown": { "toasts": { "commitCodeCompleted": "Commit de code terminé", + "pushCodeCompleted": "Push de code terminé", "committedFiles": "{count, plural, one {# fichier commit} other {# fichiers commit}}", "taskCompleted": "{label} terminé", "taskFailed": "{label} échoué", diff --git a/src/i18n/messages/ja.json b/src/i18n/messages/ja.json index 0095656..6571fbd 100644 --- a/src/i18n/messages/ja.json +++ b/src/i18n/messages/ja.json @@ -875,6 +875,7 @@ "branchDropdown": { "toasts": { "commitCodeCompleted": "コードコミットが完了しました", + "pushCodeCompleted": "コードプッシュが完了しました", "committedFiles": "{count, plural, one {# 個のファイルをコミット} other {# 個のファイルをコミット}}", "taskCompleted": "{label} が完了しました", "taskFailed": "{label} が失敗しました", diff --git a/src/i18n/messages/ko.json b/src/i18n/messages/ko.json index a3303a7..d50f03f 100644 --- a/src/i18n/messages/ko.json +++ b/src/i18n/messages/ko.json @@ -875,6 +875,7 @@ "branchDropdown": { "toasts": { "commitCodeCompleted": "코드 커밋이 완료되었습니다", + "pushCodeCompleted": "코드 푸시가 완료되었습니다", "committedFiles": "{count, plural, one {#개 파일 커밋됨} other {#개 파일 커밋됨}}", "taskCompleted": "{label} 완료", "taskFailed": "{label} 실패", diff --git a/src/i18n/messages/pt.json b/src/i18n/messages/pt.json index 16165cf..a3ecaa8 100644 --- a/src/i18n/messages/pt.json +++ b/src/i18n/messages/pt.json @@ -875,6 +875,7 @@ "branchDropdown": { "toasts": { "commitCodeCompleted": "Commit de código concluído", + "pushCodeCompleted": "Push de código concluído", "committedFiles": "{count, plural, one {# arquivo commitado} other {# arquivos commitados}}", "taskCompleted": "{label} concluído", "taskFailed": "{label} falhou", diff --git a/src/i18n/messages/zh-CN.json b/src/i18n/messages/zh-CN.json index 8ae1554..592a518 100644 --- a/src/i18n/messages/zh-CN.json +++ b/src/i18n/messages/zh-CN.json @@ -875,6 +875,7 @@ "branchDropdown": { "toasts": { "commitCodeCompleted": "提交代码完成", + "pushCodeCompleted": "推送代码完成", "committedFiles": "已提交 {count} 个文件", "taskCompleted": "{label} 完成", "taskFailed": "{label} 失败", diff --git a/src/i18n/messages/zh-TW.json b/src/i18n/messages/zh-TW.json index 10665aa..4992bc4 100644 --- a/src/i18n/messages/zh-TW.json +++ b/src/i18n/messages/zh-TW.json @@ -875,6 +875,7 @@ "branchDropdown": { "toasts": { "commitCodeCompleted": "提交程式碼完成", + "pushCodeCompleted": "推送程式碼完成", "committedFiles": "已提交 {count} 個檔案", "taskCompleted": "{label} 完成", "taskFailed": "{label} 失敗",