fix(app): use shared provider snapshot hook in settings and add refresh button
Settings providers section now uses useProvidersSnapshot instead of a one-shot fetch, so it shares the same real-time data source as the status bar and draft form. Adds a Refresh button to re-detect installed provider CLIs without restarting the daemon.
This commit is contained in:
@@ -62,12 +62,12 @@ import { settingsStyles } from "@/styles/settings";
|
|||||||
import { THINKING_TONE_NATIVE_PCM_BASE64 } from "@/utils/thinking-tone.native-pcm";
|
import { THINKING_TONE_NATIVE_PCM_BASE64 } from "@/utils/thinking-tone.native-pcm";
|
||||||
import { useVoiceAudioEngineOptional } from "@/contexts/voice-context";
|
import { useVoiceAudioEngineOptional } from "@/contexts/voice-context";
|
||||||
import { useIsLocalDaemon } from "@/hooks/use-is-local-daemon";
|
import { useIsLocalDaemon } from "@/hooks/use-is-local-daemon";
|
||||||
|
import { useProvidersSnapshot } from "@/hooks/use-providers-snapshot";
|
||||||
import { isCompactFormFactor } from "@/constants/layout";
|
import { isCompactFormFactor } from "@/constants/layout";
|
||||||
import { AGENT_PROVIDER_DEFINITIONS } from "@server/server/agent/provider-manifest";
|
import { AGENT_PROVIDER_DEFINITIONS } from "@server/server/agent/provider-manifest";
|
||||||
import { getProviderIcon } from "@/components/provider-icons";
|
import { getProviderIcon } from "@/components/provider-icons";
|
||||||
import { ProviderDiagnosticSheet } from "@/components/provider-diagnostic-sheet";
|
import { ProviderDiagnosticSheet } from "@/components/provider-diagnostic-sheet";
|
||||||
import { StatusBadge } from "@/components/ui/status-badge";
|
import { StatusBadge } from "@/components/ui/status-badge";
|
||||||
import type { ProviderSnapshotEntry } from "@server/server/agent/agent-sdk-types";
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Section definitions
|
// Section definitions
|
||||||
@@ -432,55 +432,49 @@ interface ProvidersSectionProps {
|
|||||||
|
|
||||||
function ProvidersSection({ routeServerId }: ProvidersSectionProps) {
|
function ProvidersSection({ routeServerId }: ProvidersSectionProps) {
|
||||||
const { theme } = useUnistyles();
|
const { theme } = useUnistyles();
|
||||||
const client = useHostRuntimeClient(routeServerId);
|
|
||||||
const isConnected = useHostRuntimeIsConnected(routeServerId);
|
const isConnected = useHostRuntimeIsConnected(routeServerId);
|
||||||
const [entries, setEntries] = useState<ProviderSnapshotEntry[]>([]);
|
const { entries, isLoading, isFetching, refresh } = useProvidersSnapshot(routeServerId);
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
const [diagnosticProvider, setDiagnosticProvider] = useState<string | null>(null);
|
const [diagnosticProvider, setDiagnosticProvider] = useState<string | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!client || !isConnected) {
|
|
||||||
setEntries([]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let cancelled = false;
|
|
||||||
setLoading(true);
|
|
||||||
client
|
|
||||||
.getProvidersSnapshot()
|
|
||||||
.then((result) => {
|
|
||||||
if (!cancelled) setEntries(result.entries);
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
if (!cancelled) setEntries([]);
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
if (!cancelled) setLoading(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
cancelled = true;
|
|
||||||
};
|
|
||||||
}, [client, isConnected]);
|
|
||||||
|
|
||||||
const hasServer = routeServerId.length > 0;
|
const hasServer = routeServerId.length > 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<View style={settingsStyles.section}>
|
<View style={settingsStyles.section}>
|
||||||
<Text style={settingsStyles.sectionTitle}>Providers</Text>
|
<View style={settingsStyles.sectionHeader}>
|
||||||
|
<Text style={settingsStyles.sectionHeaderTitle}>Providers</Text>
|
||||||
|
{hasServer && isConnected ? (
|
||||||
|
<Pressable
|
||||||
|
onPress={refresh}
|
||||||
|
disabled={isFetching}
|
||||||
|
style={[
|
||||||
|
settingsStyles.sectionHeaderLink,
|
||||||
|
isFetching ? { opacity: 0.5 } : null,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
color: theme.colors.primary,
|
||||||
|
fontSize: theme.fontSize.xs,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Refresh
|
||||||
|
</Text>
|
||||||
|
</Pressable>
|
||||||
|
) : null}
|
||||||
|
</View>
|
||||||
{!hasServer || !isConnected ? (
|
{!hasServer || !isConnected ? (
|
||||||
<View style={[settingsStyles.card, styles.emptyCard]}>
|
<View style={[settingsStyles.card, styles.emptyCard]}>
|
||||||
<Text style={styles.emptyText}>Connect to a host to see providers</Text>
|
<Text style={styles.emptyText}>Connect to a host to see providers</Text>
|
||||||
</View>
|
</View>
|
||||||
) : loading ? (
|
) : isLoading ? (
|
||||||
<View style={[settingsStyles.card, styles.emptyCard]}>
|
<View style={[settingsStyles.card, styles.emptyCard]}>
|
||||||
<Text style={styles.emptyText}>Loading...</Text>
|
<Text style={styles.emptyText}>Loading...</Text>
|
||||||
</View>
|
</View>
|
||||||
) : (
|
) : (
|
||||||
<View style={[settingsStyles.card, styles.audioCard]}>
|
<View style={[settingsStyles.card, styles.audioCard]}>
|
||||||
{AGENT_PROVIDER_DEFINITIONS.map((def) => {
|
{AGENT_PROVIDER_DEFINITIONS.map((def) => {
|
||||||
const entry = entries.find((e) => e.provider === def.id);
|
const entry = entries?.find((e) => e.provider === def.id);
|
||||||
const status = entry?.status ?? "unavailable";
|
const status = entry?.status ?? "unavailable";
|
||||||
const ProviderIcon = getProviderIcon(def.id);
|
const ProviderIcon = getProviderIcon(def.id);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user