fix(memory): replace swr with native React hooks (swr not installed)

F7 originally implemented useEngineStatus/useMemorySettings via swr, but the
package is not in package.json — would crash at runtime. Replaced with native
useState + useEffect + setInterval polling. Same public API
(status/settings/isLoading/isError/mutate/save) so the existing components
and the 8 UI tests (which mock the hooks directly) keep working unchanged.
This commit is contained in:
diegosouzapw
2026-05-28 12:17:31 -03:00
parent f8bf164180
commit 22d2633773
2 changed files with 99 additions and 49 deletions

View File

@@ -1,28 +1,58 @@
"use client";
import useSWR from "swr";
import { useCallback, useEffect, useRef, useState } from "react";
import type { MemoryEngineStatus } from "@/shared/schemas/memory";
const fetcher = (url: string) => fetch(url).then((res) => res.json());
export interface UseEngineStatusResult {
status: MemoryEngineStatus | null;
isLoading: boolean;
isError: boolean;
mutate: () => void;
mutate: () => Promise<void>;
}
export function useEngineStatus(): UseEngineStatusResult {
const { data, error, isLoading, mutate } = useSWR<MemoryEngineStatus>(
"/api/memory/engine-status",
fetcher,
{ refreshInterval: 5000 },
);
/**
* Lightweight engine-status fetcher with periodic polling.
* Avoids the swr dependency (not installed in this project) while keeping a
* compatible mutate()/loading/error surface for callers.
*/
export function useEngineStatus(refreshIntervalMs = 5000): UseEngineStatusResult {
const [status, setStatus] = useState<MemoryEngineStatus | null>(null);
const [isLoading, setIsLoading] = useState<boolean>(true);
const [isError, setIsError] = useState<boolean>(false);
const mounted = useRef(true);
return {
status: data ?? null,
isLoading,
isError: Boolean(error),
mutate,
};
const fetchOnce = useCallback(async (): Promise<void> => {
try {
const res = await fetch("/api/memory/engine-status");
if (!res.ok) throw new Error(`status_${res.status}`);
const data = (await res.json()) as MemoryEngineStatus;
if (mounted.current) {
setStatus(data);
setIsError(false);
}
} catch {
if (mounted.current) setIsError(true);
} finally {
if (mounted.current) setIsLoading(false);
}
}, []);
useEffect(() => {
mounted.current = true;
void fetchOnce();
if (!refreshIntervalMs || refreshIntervalMs <= 0) {
return () => {
mounted.current = false;
};
}
const id = setInterval(() => {
void fetchOnce();
}, refreshIntervalMs);
return () => {
mounted.current = false;
clearInterval(id);
};
}, [fetchOnce, refreshIntervalMs]);
return { status, isLoading, isError, mutate: fetchOnce };
}

View File

@@ -1,48 +1,68 @@
"use client";
import useSWR from "swr";
import { useCallback, useEffect, useRef, useState } from "react";
import type { MemorySettingsExtended } from "@/shared/schemas/memory";
const fetcher = (url: string) => fetch(url).then((res) => res.json());
export interface UseMemorySettingsResult {
settings: MemorySettingsExtended | null;
isLoading: boolean;
isError: boolean;
mutate: () => void;
mutate: () => Promise<void>;
save: (updates: Partial<MemorySettingsExtended>) => Promise<boolean>;
}
/**
* Lightweight settings fetcher + saver.
* Avoids the swr dependency (not installed in this project) while keeping a
* compatible mutate()/save() surface for callers.
*/
export function useMemorySettings(): UseMemorySettingsResult {
const { data, error, isLoading, mutate } = useSWR<MemorySettingsExtended>(
"/api/settings/memory",
fetcher,
const [settings, setSettings] = useState<MemorySettingsExtended | null>(null);
const [isLoading, setIsLoading] = useState<boolean>(true);
const [isError, setIsError] = useState<boolean>(false);
const mounted = useRef(true);
const fetchOnce = useCallback(async (): Promise<void> => {
try {
const res = await fetch("/api/settings/memory");
if (!res.ok) throw new Error(`status_${res.status}`);
const data = (await res.json()) as MemorySettingsExtended;
if (mounted.current) {
setSettings(data);
setIsError(false);
}
} catch {
if (mounted.current) setIsError(true);
} finally {
if (mounted.current) setIsLoading(false);
}
}, []);
useEffect(() => {
mounted.current = true;
void fetchOnce();
return () => {
mounted.current = false;
};
}, [fetchOnce]);
const save = useCallback(
async (updates: Partial<MemorySettingsExtended>): Promise<boolean> => {
try {
const res = await fetch("/api/settings/memory", {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(updates),
});
if (!res.ok) return false;
await fetchOnce();
return true;
} catch {
return false;
}
},
[fetchOnce]
);
const save = async (updates: Partial<MemorySettingsExtended>): Promise<boolean> => {
try {
const current = data ?? {};
const next = { ...current, ...updates };
const res = await fetch("/api/settings/memory", {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(next),
});
if (res.ok) {
await mutate();
return true;
}
return false;
} catch {
return false;
}
};
return {
settings: data ?? null,
isLoading,
isError: Boolean(error),
mutate,
save,
};
return { settings, isLoading, isError, mutate: fetchOnce, save };
}