fix(app): wrap daemon settings section in React Query with loading state
Prevents the daemon section from flashing "not running" / "PID —" while IPC data loads. The whole card now shows a spinner until the first fetch completes, and mutations (restart/stop/start) optimistically update the query cache with the returned status.
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import { ActivityIndicator, Alert, Text, View } from "react-native";
|
import { ActivityIndicator, Alert, Text, View } from "react-native";
|
||||||
import * as Clipboard from "expo-clipboard";
|
import * as Clipboard from "expo-clipboard";
|
||||||
import { useFocusEffect } from "@react-navigation/native";
|
|
||||||
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
||||||
import { settingsStyles } from "@/styles/settings";
|
import { settingsStyles } from "@/styles/settings";
|
||||||
import {
|
import {
|
||||||
@@ -21,15 +20,12 @@ import { openExternalUrl } from "@/utils/open-external-url";
|
|||||||
import { isVersionMismatch } from "@/desktop/updates/desktop-updates";
|
import { isVersionMismatch } from "@/desktop/updates/desktop-updates";
|
||||||
import {
|
import {
|
||||||
getCliDaemonStatus,
|
getCliDaemonStatus,
|
||||||
getDesktopDaemonLogs,
|
|
||||||
getDesktopDaemonStatus,
|
|
||||||
restartDesktopDaemon,
|
restartDesktopDaemon,
|
||||||
shouldUseDesktopDaemon,
|
shouldUseDesktopDaemon,
|
||||||
startDesktopDaemon,
|
startDesktopDaemon,
|
||||||
stopDesktopDaemon,
|
stopDesktopDaemon,
|
||||||
type DesktopDaemonLogs,
|
|
||||||
type DesktopDaemonStatus,
|
|
||||||
} from "@/desktop/daemon/desktop-daemon";
|
} from "@/desktop/daemon/desktop-daemon";
|
||||||
|
import { useDaemonStatus } from "@/desktop/hooks/use-daemon-status";
|
||||||
|
|
||||||
export interface LocalDaemonSectionProps {
|
export interface LocalDaemonSectionProps {
|
||||||
appVersion: string | null;
|
appVersion: string | null;
|
||||||
@@ -40,44 +36,18 @@ export function LocalDaemonSection({ appVersion, showLifecycleControls }: LocalD
|
|||||||
const { theme } = useUnistyles();
|
const { theme } = useUnistyles();
|
||||||
const showSection = shouldUseDesktopDaemon();
|
const showSection = shouldUseDesktopDaemon();
|
||||||
const { settings, updateSettings } = useAppSettings();
|
const { settings, updateSettings } = useAppSettings();
|
||||||
const [daemonStatus, setDaemonStatus] = useState<DesktopDaemonStatus | null>(null);
|
const { data, isLoading, error: statusError, setStatus, refetch } = useDaemonStatus();
|
||||||
const [daemonVersion, setDaemonVersion] = useState<string | null>(null);
|
|
||||||
const [statusError, setStatusError] = useState<string | null>(null);
|
|
||||||
const [isRestartingDaemon, setIsRestartingDaemon] = useState(false);
|
const [isRestartingDaemon, setIsRestartingDaemon] = useState(false);
|
||||||
const [isUpdatingDaemonManagement, setIsUpdatingDaemonManagement] = useState(false);
|
const [isUpdatingDaemonManagement, setIsUpdatingDaemonManagement] = useState(false);
|
||||||
const [statusMessage, setStatusMessage] = useState<string | null>(null);
|
const [statusMessage, setStatusMessage] = useState<string | null>(null);
|
||||||
const [daemonLogs, setDaemonLogs] = useState<DesktopDaemonLogs | null>(null);
|
|
||||||
const [isLogsModalOpen, setIsLogsModalOpen] = useState(false);
|
const [isLogsModalOpen, setIsLogsModalOpen] = useState(false);
|
||||||
const [cliStatusOutput, setCliStatusOutput] = useState<string | null>(null);
|
const [cliStatusOutput, setCliStatusOutput] = useState<string | null>(null);
|
||||||
const [isCliStatusModalOpen, setIsCliStatusModalOpen] = useState(false);
|
const [isCliStatusModalOpen, setIsCliStatusModalOpen] = useState(false);
|
||||||
const [isLoadingCliStatus, setIsLoadingCliStatus] = useState(false);
|
const [isLoadingCliStatus, setIsLoadingCliStatus] = useState(false);
|
||||||
|
|
||||||
const loadDaemonData = useCallback(() => {
|
const daemonStatus = data?.status ?? null;
|
||||||
if (!showSection) {
|
const daemonLogs = data?.logs ?? null;
|
||||||
return Promise.resolve();
|
const daemonVersion = daemonStatus?.version ?? null;
|
||||||
}
|
|
||||||
return Promise.all([getDesktopDaemonStatus(), getDesktopDaemonLogs()])
|
|
||||||
.then(([status, logs]) => {
|
|
||||||
setDaemonStatus(status);
|
|
||||||
setDaemonLogs(logs);
|
|
||||||
setDaemonVersion(status.version);
|
|
||||||
setStatusError(null);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
const message = error instanceof Error ? error.message : String(error);
|
|
||||||
setStatusError(message);
|
|
||||||
});
|
|
||||||
}, [showSection]);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
if (!showSection) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
void loadDaemonData();
|
|
||||||
return undefined;
|
|
||||||
}, [loadDaemonData, showSection]),
|
|
||||||
);
|
|
||||||
|
|
||||||
const daemonVersionMismatch = isVersionMismatch(appVersion, daemonVersion);
|
const daemonVersionMismatch = isVersionMismatch(appVersion, daemonVersion);
|
||||||
const daemonStatusStateText =
|
const daemonStatusStateText =
|
||||||
@@ -116,12 +86,12 @@ export function LocalDaemonSection({ appVersion, showLifecycleControls }: LocalD
|
|||||||
daemonStatus?.status === "running" ? restartDesktopDaemon : startDesktopDaemon;
|
daemonStatus?.status === "running" ? restartDesktopDaemon : startDesktopDaemon;
|
||||||
|
|
||||||
void action()
|
void action()
|
||||||
.then((status) => {
|
.then((newStatus) => {
|
||||||
setDaemonStatus(status);
|
setStatus(newStatus);
|
||||||
setStatusMessage(
|
setStatusMessage(
|
||||||
daemonStatus?.status === "running" ? "Daemon restarted." : "Daemon started.",
|
daemonStatus?.status === "running" ? "Daemon restarted." : "Daemon started.",
|
||||||
);
|
);
|
||||||
return loadDaemonData();
|
refetch();
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error("[Settings] Failed to change desktop daemon state", error);
|
console.error("[Settings] Failed to change desktop daemon state", error);
|
||||||
@@ -136,7 +106,7 @@ export function LocalDaemonSection({ appVersion, showLifecycleControls }: LocalD
|
|||||||
console.error("[Settings] Failed to open desktop daemon action confirmation", error);
|
console.error("[Settings] Failed to open desktop daemon action confirmation", error);
|
||||||
Alert.alert("Error", "Unable to open the daemon confirmation dialog.");
|
Alert.alert("Error", "Unable to open the daemon confirmation dialog.");
|
||||||
});
|
});
|
||||||
}, [daemonActionLabel, daemonStatus?.status, isRestartingDaemon, loadDaemonData, showSection]);
|
}, [daemonActionLabel, daemonStatus?.status, isRestartingDaemon, refetch, setStatus, showSection]);
|
||||||
|
|
||||||
const handleToggleDaemonManagement = useCallback(() => {
|
const handleToggleDaemonManagement = useCallback(() => {
|
||||||
if (isUpdatingDaemonManagement) {
|
if (isUpdatingDaemonManagement) {
|
||||||
@@ -182,9 +152,14 @@ export function LocalDaemonSection({ appVersion, showLifecycleControls }: LocalD
|
|||||||
: Promise.resolve(daemonStatus ?? null);
|
: Promise.resolve(daemonStatus ?? null);
|
||||||
|
|
||||||
void stopPromise
|
void stopPromise
|
||||||
.then(() => updateSettings({ manageBuiltInDaemon: false }))
|
.then((newStatus) => {
|
||||||
.then(() => loadDaemonData())
|
if (newStatus) {
|
||||||
|
setStatus(newStatus);
|
||||||
|
}
|
||||||
|
return updateSettings({ manageBuiltInDaemon: false });
|
||||||
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
refetch();
|
||||||
setStatusMessage("Built-in daemon paused and stopped.");
|
setStatusMessage("Built-in daemon paused and stopped.");
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
@@ -202,7 +177,8 @@ export function LocalDaemonSection({ appVersion, showLifecycleControls }: LocalD
|
|||||||
}, [
|
}, [
|
||||||
daemonStatus,
|
daemonStatus,
|
||||||
isUpdatingDaemonManagement,
|
isUpdatingDaemonManagement,
|
||||||
loadDaemonData,
|
refetch,
|
||||||
|
setStatus,
|
||||||
settings.manageBuiltInDaemon,
|
settings.manageBuiltInDaemon,
|
||||||
updateSettings,
|
updateSettings,
|
||||||
]);
|
]);
|
||||||
@@ -277,126 +253,138 @@ export function LocalDaemonSection({ appVersion, showLifecycleControls }: LocalD
|
|||||||
Advanced settings
|
Advanced settings
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
<View style={settingsStyles.card}>
|
{isLoading ? (
|
||||||
<View style={settingsStyles.row}>
|
<View style={[settingsStyles.card, styles.loadingCard]}>
|
||||||
<View style={settingsStyles.rowContent}>
|
<ActivityIndicator size="small" color={theme.colors.foregroundMuted} />
|
||||||
<Text style={settingsStyles.rowTitle}>Status</Text>
|
|
||||||
<Text style={settingsStyles.rowHint}>Only the built-in desktop daemon is shown here.</Text>
|
|
||||||
</View>
|
|
||||||
<View style={styles.statusValueGroup}>
|
|
||||||
<Text style={styles.valueText}>{daemonStatusStateText}</Text>
|
|
||||||
<Text style={styles.valueSubtext}>{daemonStatusDetailText}</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
{showLifecycleControls ? (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
<View style={settingsStyles.card}>
|
||||||
|
<View style={settingsStyles.row}>
|
||||||
|
<View style={settingsStyles.rowContent}>
|
||||||
|
<Text style={settingsStyles.rowTitle}>Status</Text>
|
||||||
|
<Text style={settingsStyles.rowHint}>
|
||||||
|
Only the built-in desktop daemon is shown here.
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<View style={styles.statusValueGroup}>
|
||||||
|
<Text style={styles.valueText}>{daemonStatusStateText}</Text>
|
||||||
|
<Text style={styles.valueSubtext}>{daemonStatusDetailText}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
{showLifecycleControls ? (
|
||||||
|
<>
|
||||||
|
<View style={[settingsStyles.row, settingsStyles.rowBorder]}>
|
||||||
|
<View style={settingsStyles.rowContent}>
|
||||||
|
<Text style={settingsStyles.rowTitle}>Daemon management</Text>
|
||||||
|
<Text style={settingsStyles.rowHint}>
|
||||||
|
{isDaemonManagementPaused
|
||||||
|
? "Paused. The built-in daemon stays stopped until you start it again."
|
||||||
|
: "Enabled. Paseo can manage the built-in daemon from the desktop app."}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
leftIcon={
|
||||||
|
isDaemonManagementPaused ? (
|
||||||
|
<Play size={theme.iconSize.sm} color={theme.colors.foreground} />
|
||||||
|
) : (
|
||||||
|
<Pause size={theme.iconSize.sm} color={theme.colors.foreground} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onPress={handleToggleDaemonManagement}
|
||||||
|
disabled={isUpdatingDaemonManagement}
|
||||||
|
>
|
||||||
|
{isUpdatingDaemonManagement
|
||||||
|
? isDaemonManagementPaused
|
||||||
|
? "Resuming..."
|
||||||
|
: "Pausing..."
|
||||||
|
: isDaemonManagementPaused
|
||||||
|
? "Resume"
|
||||||
|
: "Pause"}
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
<View style={[settingsStyles.row, settingsStyles.rowBorder]}>
|
||||||
|
<View style={settingsStyles.rowContent}>
|
||||||
|
<Text style={settingsStyles.rowTitle}>{daemonActionLabel}</Text>
|
||||||
|
<Text style={settingsStyles.rowHint}>{daemonActionMessage}</Text>
|
||||||
|
{statusMessage ? <Text style={styles.statusText}>{statusMessage}</Text> : null}
|
||||||
|
</View>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
leftIcon={<RotateCw size={theme.iconSize.sm} color={theme.colors.foreground} />}
|
||||||
|
onPress={handleUpdateLocalDaemon}
|
||||||
|
disabled={isRestartingDaemon}
|
||||||
|
>
|
||||||
|
{isRestartingDaemon
|
||||||
|
? daemonStatus?.status === "running"
|
||||||
|
? "Restarting..."
|
||||||
|
: "Starting..."
|
||||||
|
: daemonActionLabel}
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
<View style={[settingsStyles.row, settingsStyles.rowBorder]}>
|
<View style={[settingsStyles.row, settingsStyles.rowBorder]}>
|
||||||
<View style={settingsStyles.rowContent}>
|
<View style={settingsStyles.rowContent}>
|
||||||
<Text style={settingsStyles.rowTitle}>Daemon management</Text>
|
<Text style={settingsStyles.rowTitle}>Log file</Text>
|
||||||
<Text style={settingsStyles.rowHint}>
|
<Text style={settingsStyles.rowHint}>
|
||||||
{isDaemonManagementPaused
|
{daemonLogs?.logPath ?? "Log path unavailable."}
|
||||||
? "Paused. The built-in daemon stays stopped until you start it again."
|
</Text>
|
||||||
: "Enabled. Paseo can manage the built-in daemon from the desktop app."}
|
</View>
|
||||||
|
<View style={styles.actionGroup}>
|
||||||
|
{daemonLogs?.logPath ? (
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
leftIcon={<Copy size={theme.iconSize.sm} color={theme.colors.foreground} />}
|
||||||
|
onPress={handleCopyLogPath}
|
||||||
|
>
|
||||||
|
Copy path
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
leftIcon={<FileText size={theme.iconSize.sm} color={theme.colors.foreground} />}
|
||||||
|
onPress={handleOpenLogs}
|
||||||
|
disabled={!daemonLogs}
|
||||||
|
>
|
||||||
|
Open logs
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View style={[settingsStyles.row, settingsStyles.rowBorder]}>
|
||||||
|
<View style={settingsStyles.rowContent}>
|
||||||
|
<Text style={settingsStyles.rowTitle}>Full status</Text>
|
||||||
|
<Text style={settingsStyles.rowHint}>
|
||||||
|
Runs `paseo daemon status` and shows the output.
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
leftIcon={
|
leftIcon={<Activity size={theme.iconSize.sm} color={theme.colors.foreground} />}
|
||||||
isDaemonManagementPaused ? (
|
onPress={() => void handleOpenCliStatus()}
|
||||||
<Play size={theme.iconSize.sm} color={theme.colors.foreground} />
|
disabled={isLoadingCliStatus}
|
||||||
) : (
|
|
||||||
<Pause size={theme.iconSize.sm} color={theme.colors.foreground} />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
onPress={handleToggleDaemonManagement}
|
|
||||||
disabled={isUpdatingDaemonManagement}
|
|
||||||
>
|
>
|
||||||
{isUpdatingDaemonManagement
|
{isLoadingCliStatus ? "Loading..." : "View status"}
|
||||||
? isDaemonManagementPaused
|
|
||||||
? "Resuming..."
|
|
||||||
: "Pausing..."
|
|
||||||
: isDaemonManagementPaused
|
|
||||||
? "Resume"
|
|
||||||
: "Pause"}
|
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
<View style={[settingsStyles.row, settingsStyles.rowBorder]}>
|
|
||||||
<View style={settingsStyles.rowContent}>
|
|
||||||
<Text style={settingsStyles.rowTitle}>{daemonActionLabel}</Text>
|
|
||||||
<Text style={settingsStyles.rowHint}>{daemonActionMessage}</Text>
|
|
||||||
{statusMessage ? <Text style={styles.statusText}>{statusMessage}</Text> : null}
|
|
||||||
</View>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
size="sm"
|
|
||||||
leftIcon={<RotateCw size={theme.iconSize.sm} color={theme.colors.foreground} />}
|
|
||||||
onPress={handleUpdateLocalDaemon}
|
|
||||||
disabled={isRestartingDaemon}
|
|
||||||
>
|
|
||||||
{isRestartingDaemon
|
|
||||||
? daemonStatus?.status === "running"
|
|
||||||
? "Restarting..."
|
|
||||||
: "Starting..."
|
|
||||||
: daemonActionLabel}
|
|
||||||
</Button>
|
|
||||||
</View>
|
|
||||||
</>
|
|
||||||
) : null}
|
|
||||||
<View style={[settingsStyles.row, settingsStyles.rowBorder]}>
|
|
||||||
<View style={settingsStyles.rowContent}>
|
|
||||||
<Text style={settingsStyles.rowTitle}>Log file</Text>
|
|
||||||
<Text style={settingsStyles.rowHint}>{daemonLogs?.logPath ?? "Log path unavailable."}</Text>
|
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.actionGroup}>
|
|
||||||
{daemonLogs?.logPath ? (
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
size="sm"
|
|
||||||
leftIcon={<Copy size={theme.iconSize.sm} color={theme.colors.foreground} />}
|
|
||||||
onPress={handleCopyLogPath}
|
|
||||||
>
|
|
||||||
Copy path
|
|
||||||
</Button>
|
|
||||||
) : null}
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
size="sm"
|
|
||||||
leftIcon={<FileText size={theme.iconSize.sm} color={theme.colors.foreground} />}
|
|
||||||
onPress={handleOpenLogs}
|
|
||||||
disabled={!daemonLogs}
|
|
||||||
>
|
|
||||||
Open logs
|
|
||||||
</Button>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
<View style={[settingsStyles.row, settingsStyles.rowBorder]}>
|
|
||||||
<View style={settingsStyles.rowContent}>
|
|
||||||
<Text style={settingsStyles.rowTitle}>Full status</Text>
|
|
||||||
<Text style={settingsStyles.rowHint}>
|
|
||||||
Runs `paseo daemon status` and shows the output.
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
size="sm"
|
|
||||||
leftIcon={<Activity size={theme.iconSize.sm} color={theme.colors.foreground} />}
|
|
||||||
onPress={() => void handleOpenCliStatus()}
|
|
||||||
disabled={isLoadingCliStatus}
|
|
||||||
>
|
|
||||||
{isLoadingCliStatus ? "Loading..." : "View status"}
|
|
||||||
</Button>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{daemonVersionMismatch ? (
|
{daemonVersionMismatch ? (
|
||||||
<View style={styles.warningCard}>
|
<View style={styles.warningCard}>
|
||||||
<Text style={styles.warningText}>
|
<Text style={styles.warningText}>
|
||||||
App and daemon versions don't match. Update both to the same version for the best
|
App and daemon versions don't match. Update both to the same version for the best
|
||||||
experience.
|
experience.
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
) : null}
|
) : null}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
<AdaptiveModalSheet
|
<AdaptiveModalSheet
|
||||||
visible={isLogsModalOpen}
|
visible={isLogsModalOpen}
|
||||||
@@ -447,6 +435,11 @@ const styles = StyleSheet.create((theme) => ({
|
|||||||
flexWrap: "wrap",
|
flexWrap: "wrap",
|
||||||
justifyContent: "flex-end",
|
justifyContent: "flex-end",
|
||||||
},
|
},
|
||||||
|
loadingCard: {
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
paddingVertical: theme.spacing[6],
|
||||||
|
},
|
||||||
statusValueGroup: {
|
statusValueGroup: {
|
||||||
alignItems: "flex-end",
|
alignItems: "flex-end",
|
||||||
gap: 2,
|
gap: 2,
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { useCallback } from "react";
|
||||||
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
|
import {
|
||||||
|
getDesktopDaemonLogs,
|
||||||
|
getDesktopDaemonStatus,
|
||||||
|
shouldUseDesktopDaemon,
|
||||||
|
type DesktopDaemonLogs,
|
||||||
|
type DesktopDaemonStatus,
|
||||||
|
} from "@/desktop/daemon/desktop-daemon";
|
||||||
|
|
||||||
|
const DAEMON_STATUS_QUERY_KEY = ["desktopDaemonStatus"] as const;
|
||||||
|
|
||||||
|
interface DaemonStatusData {
|
||||||
|
status: DesktopDaemonStatus;
|
||||||
|
logs: DesktopDaemonLogs;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useDaemonStatus() {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const enabled = shouldUseDesktopDaemon();
|
||||||
|
|
||||||
|
const query = useQuery<DaemonStatusData>({
|
||||||
|
queryKey: DAEMON_STATUS_QUERY_KEY,
|
||||||
|
enabled,
|
||||||
|
staleTime: 30_000,
|
||||||
|
refetchOnMount: "always",
|
||||||
|
queryFn: async () => {
|
||||||
|
const [status, logs] = await Promise.all([getDesktopDaemonStatus(), getDesktopDaemonLogs()]);
|
||||||
|
return { status, logs };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const setStatus = useCallback(
|
||||||
|
(status: DesktopDaemonStatus) => {
|
||||||
|
queryClient.setQueryData<DaemonStatusData>(DAEMON_STATUS_QUERY_KEY, (prev) =>
|
||||||
|
prev ? { ...prev, status } : undefined,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[queryClient],
|
||||||
|
);
|
||||||
|
|
||||||
|
const refetch = useCallback(() => {
|
||||||
|
void queryClient.invalidateQueries({ queryKey: DAEMON_STATUS_QUERY_KEY });
|
||||||
|
}, [queryClient]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: query.data ?? null,
|
||||||
|
isLoading: query.isLoading,
|
||||||
|
error: query.error instanceof Error ? query.error.message : null,
|
||||||
|
setStatus,
|
||||||
|
refetch,
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user