Wrap desktop IPC in shared mutation/query hooks (fix #761) (#765)

* Wrap desktop IPC calls in shared hooks

* Unslop desktop IPC hook callsites

* Redesign desktop IPC hooks around domains
This commit is contained in:
Mohamed Boudra
2026-05-06 19:58:17 +08:00
committed by GitHub
parent 24bdd3c8ad
commit ac375f152d
10 changed files with 546 additions and 151 deletions
@@ -1,7 +1,9 @@
/**
* @vitest-environment jsdom
*/
import React from "react";
import { act, renderHook, waitFor } from "@testing-library/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { useDesktopAppUpdater } from "./use-desktop-app-updater";
@@ -37,6 +39,27 @@ vi.mock("@/desktop/settings/desktop-settings", () => ({
}),
}));
vi.mock("@/contexts/toast-context", () => ({
useToast: () => ({ error: vi.fn(), show: vi.fn(), copied: vi.fn() }),
}));
function createQueryClient(): QueryClient {
return new QueryClient({
defaultOptions: {
queries: { retry: false },
mutations: { retry: false },
},
});
}
function renderUpdaterHook() {
const queryClient = createQueryClient();
const wrapper = ({ children }: { children: React.ReactNode }) =>
React.createElement(QueryClientProvider, { client: queryClient }, children);
return renderHook(() => useDesktopAppUpdater(), { wrapper });
}
describe("useDesktopAppUpdater", () => {
beforeEach(() => {
settingsState.releaseChannel = "stable";
@@ -47,7 +70,7 @@ describe("useDesktopAppUpdater", () => {
it("uses the effective desktop release channel when checking for updates", async () => {
settingsState.releaseChannel = "beta";
renderHook(() => useDesktopAppUpdater());
renderUpdaterHook();
await waitFor(() => {
expect(desktopUpdatesMock.checkDesktopAppUpdate).toHaveBeenCalledWith({
@@ -58,7 +81,7 @@ describe("useDesktopAppUpdater", () => {
it("uses the effective desktop release channel when installing updates", async () => {
settingsState.releaseChannel = "beta";
const { result } = renderHook(() => useDesktopAppUpdater());
const { result } = renderUpdaterHook();
await act(async () => {
await result.current.installUpdate();
@@ -1,4 +1,5 @@
import { useCallback, useEffect, useRef, useState } from "react";
import { useMutation } from "@tanstack/react-query";
import {
checkDesktopAppUpdate,
formatVersionWithPrefix,
@@ -8,6 +9,7 @@ import {
type DesktopAppUpdateInstallResult,
} from "@/desktop/updates/desktop-updates";
import { useDesktopSettings } from "@/desktop/settings/desktop-settings";
import { useDesktopIpcErrorReporter } from "@/desktop/hooks/desktop-ipc-error";
export type DesktopAppUpdateStatus =
| "idle"
@@ -86,12 +88,26 @@ export function useDesktopAppUpdater(): UseDesktopAppUpdaterReturn {
const isDesktopApp = shouldShowDesktopUpdateSection();
const { settings: desktopSettings } = useDesktopSettings();
const releaseChannel = desktopSettings.releaseChannel;
const reportError = useDesktopIpcErrorReporter();
const requestVersionRef = useRef(0);
const [status, setStatus] = useState<DesktopAppUpdateStatus>("idle");
const [availableUpdate, setAvailableUpdate] = useState<DesktopAppUpdateCheckResult | null>(null);
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const [installMessage, setInstallMessage] = useState<string | null>(null);
const [lastCheckedAt, setLastCheckedAt] = useState<number | null>(null);
const { mutateAsync: installAppUpdate, isPending: isInstallingAppUpdate } = useMutation<
DesktopAppUpdateInstallResult,
Error
>({
mutationFn: () => installDesktopAppUpdate({ releaseChannel }),
onError: (error) => {
reportError({
error,
message: "Unable to install the desktop app update.",
logLabel: "[DesktopUpdater] Failed to install app update",
});
},
});
const checkForUpdates = useCallback(
async (options: { silent?: boolean } = {}) => {
@@ -177,7 +193,7 @@ export function useDesktopAppUpdater(): UseDesktopAppUpdaterReturn {
setErrorMessage(null);
try {
const result = await installDesktopAppUpdate({ releaseChannel });
const result = await installAppUpdate();
setLastCheckedAt(Date.now());
if (result.installed) {
@@ -197,7 +213,7 @@ export function useDesktopAppUpdater(): UseDesktopAppUpdaterReturn {
setErrorMessage(message);
return null;
}
}, [isDesktopApp, releaseChannel]);
}, [installAppUpdate, isDesktopApp]);
return {
isDesktopApp,
@@ -211,7 +227,7 @@ export function useDesktopAppUpdater(): UseDesktopAppUpdaterReturn {
errorMessage,
lastCheckedAt,
isChecking: status === "checking",
isInstalling: status === "installing",
isInstalling: status === "installing" || isInstallingAppUpdate,
checkForUpdates,
installUpdate,
};