Files
tkmind_go/ui/desktop/src/components/settings/providers/ProviderSettingsPage.tsx
T

105 lines
3.8 KiB
TypeScript

import { useEffect, useState, useCallback, useRef, useMemo } from 'react';
import { useNavigate } from 'react-router-dom';
import { ScrollArea } from '../../ui/scroll-area';
import BackButton from '../../ui/BackButton';
import ProviderGrid from './ProviderGrid';
import { useConfig } from '../../ConfigContext';
import { ProviderDetails } from '../../../api';
import { createNavigationHandler } from '../../../utils/navigationUtils';
interface ProviderSettingsProps {
onClose: () => void;
isOnboarding: boolean;
onProviderLaunched?: (model?: string) => void;
}
export default function ProviderSettings({
onClose,
isOnboarding,
onProviderLaunched,
}: ProviderSettingsProps) {
const { getProviders } = useConfig();
const navigate = useNavigate();
const [loading, setLoading] = useState(true);
const [providers, setProviders] = useState<ProviderDetails[]>([]);
const initialLoadDone = useRef(false);
const setView = useMemo(() => createNavigationHandler(navigate), [navigate]);
// Create a function to load providers that can be called multiple times
const loadProviders = useCallback(async () => {
setLoading(true);
try {
// Only force refresh when explicitly requested, not on initial load
const result = await getProviders(!initialLoadDone.current);
if (result) {
setProviders(result);
initialLoadDone.current = true;
}
} catch (error) {
console.error('Failed to load providers:', error);
} finally {
setLoading(false);
}
}, [getProviders]);
// Load providers only once when component mounts
useEffect(() => {
loadProviders();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []); // Intentionally not including loadProviders in deps to prevent reloading
// This function will be passed to ProviderGrid for manual refreshes after config changes
const refreshProviders = useCallback(() => {
if (initialLoadDone.current) {
getProviders(true).then((result) => {
if (result) setProviders(result);
});
}
}, [getProviders]);
return (
<div className="h-screen w-full flex flex-col bg-background-default text-text-default">
<ScrollArea className="flex-1 w-full">
<div className="w-full max-w-6xl mx-auto px-4 sm:px-6 md:px-8 pt-12 pb-4">
{/* Consistent header pattern with back button */}
<div className="flex flex-col pb-8 border-b border-border-default">
<div className="flex items-center pt-2 mb-1 no-drag">
<BackButton onClick={onClose} />
</div>
<h1 className="text-4xl font-light mb-4 pt-6" data-testid="provider-selection-heading">
{isOnboarding ? 'Other providers' : 'Provider Configuration Settings'}
</h1>
{isOnboarding && (
<p className="text-sm sm:text-base text-text-muted max-w-full sm:max-w-2xl">
Select an AI model provider to get started with goose. You'll need to use API keys
generated by each provider which will be encrypted and stored locally. You can
change your provider at any time in settings.
</p>
)}
</div>
</div>
<div className="py-8 pt-[20px]">
{/* Content Area */}
<div className="w-full max-w-6xl mx-auto pt-4 px-4 sm:px-6 md:px-8">
<div className="relative z-10">
{loading ? (
<div>Loading providers...</div>
) : (
<ProviderGrid
providers={providers}
isOnboarding={isOnboarding}
refreshProviders={refreshProviders}
setView={setView}
onModelSelected={onProviderLaunched}
/>
)}
</div>
</div>
</div>
</ScrollArea>
</div>
);
}