Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions ui/desktop/src/components/ConfigContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import type {
ExtensionConfig,
} from '../api';
import { removeShims } from './settings/extensions/utils';
import { ensureClientInitialized } from '../utils';

export type { ExtensionConfig } from '../api/types.gen';

Expand Down Expand Up @@ -183,7 +182,6 @@ export const ConfigProvider: React.FC<ConfigProviderProps> = ({ children }) => {
useEffect(() => {
// Load all configuration data and providers on mount
(async () => {
await ensureClientInitialized();
// Load config
const configResponse = await readAllConfig();
setConfig(configResponse.data?.config || {});
Expand Down
2 changes: 0 additions & 2 deletions ui/desktop/src/components/ModelAndProviderContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
getModelDisplayName,
getProviderDisplayName,
} from './settings/models/predefinedModelsUtils';
import { ensureClientInitialized } from '../utils';

// titles
export const UNKNOWN_PROVIDER_TITLE = 'Provider name lookup';
Expand Down Expand Up @@ -171,7 +170,6 @@ export const ModelAndProviderProvider: React.FC<ModelAndProviderProviderProps> =

const refreshCurrentModelAndProvider = useCallback(async () => {
try {
await ensureClientInitialized();
const { model, provider } = await getCurrentModelAndProvider();
setCurrentModel(model);
setCurrentProvider(provider);
Expand Down
3 changes: 0 additions & 3 deletions ui/desktop/src/components/ProviderGuard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { startOpenRouterSetup } from '../utils/openRouterSetup';
import WelcomeGooseLogo from './WelcomeGooseLogo';
import { initializeSystem } from '../utils/providerUtils';
import { toastService } from '../toasts';
import { ensureClientInitialized } from '../utils';

interface ProviderGuardProps {
children: React.ReactNode;
Expand Down Expand Up @@ -96,8 +95,6 @@ export default function ProviderGuard({ children }: ProviderGuardProps) {
useEffect(() => {
const checkProvider = async () => {
try {
await ensureClientInitialized();

const config = window.electron.getConfig();
console.log('ProviderGuard - Full config:', config);

Expand Down
84 changes: 84 additions & 0 deletions ui/desktop/src/contexts/ClientInitializationContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import React, { createContext, useContext, useEffect, useState, ReactNode } from 'react';
import { client } from '../api/client.gen';

interface ClientInitializationContextType {
isInitialized: boolean;
initializationError: Error | null;
}

// Track if client has been initialized to avoid duplicate initialization
let clientInitialized = false;

async function ensureClientInitialized() {
if (clientInitialized) return;
client.setConfig({
baseUrl: window.appConfig.get('GOOSE_API_HOST') + ':' + window.appConfig.get('GOOSE_PORT'),
headers: {
'Content-Type': 'application/json',
'X-Secret-Key': await window.electron.getSecretKey(),
},
});
clientInitialized = true;
}

const ClientInitializationContext = createContext<ClientInitializationContextType | undefined>(
undefined
);

interface ClientInitializationProviderProps {
children: ReactNode;
}

export const ClientInitializationProvider: React.FC<ClientInitializationProviderProps> = ({
children,
}) => {
const [isInitialized, setIsInitialized] = useState(false);
const [initializationError, setInitializationError] = useState<Error | null>(null);

useEffect(() => {
const initializeClient = async () => {
try {
await ensureClientInitialized();
setIsInitialized(true);
} catch (error) {
console.error('Failed to initialize API client:', error);
setInitializationError(error instanceof Error ? error : new Error('Unknown error'));
}
};

initializeClient();
}, []);

return (
<ClientInitializationContext.Provider value={{ isInitialized, initializationError }}>
{children}
</ClientInitializationContext.Provider>
);
};

export const useClientInitialization = () => {
const context = useContext(ClientInitializationContext);
if (context === undefined) {
throw new Error('useClientInitialization must be used within a ClientInitializationProvider');
}
return context;
};

// Helper component to ensure initialization before rendering children
export const RequireClientInitialization: React.FC<{ children: ReactNode }> = ({ children }) => {
const { isInitialized, initializationError } = useClientInitialization();

if (initializationError) {
throw initializationError;
}

if (!isInitialized) {
return (
<div className="flex justify-center items-center py-12">
<div className="animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-textStandard"></div>
</div>
);
}

return <>{children}</>;
};
2 changes: 0 additions & 2 deletions ui/desktop/src/hooks/useChat.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { useEffect, useState } from 'react';
import { ChatType } from '../types/chat';
import { fetchSessionDetails, generateSessionId } from '../sessions';
import { ensureClientInitialized } from '../utils';
import { View, ViewOptions } from '../App';

type UseChatArgs = {
Expand Down Expand Up @@ -30,7 +29,6 @@ export const useChat = ({ setIsLoadingSession, setView, setPairChat }: UseChatAr

setIsLoadingSession(true);
try {
await ensureClientInitialized();
const sessionDetails = await fetchSessionDetails(resumeSessionId);

// Only set view if we have valid session details
Expand Down
26 changes: 17 additions & 9 deletions ui/desktop/src/renderer.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import React, { Suspense, lazy } from 'react';
import ReactDOM from 'react-dom/client';
import { ConfigProvider } from './components/ConfigContext';
import {
ClientInitializationProvider,
RequireClientInitialization,
} from './contexts/ClientInitializationContext';
import { ErrorBoundary } from './components/ErrorBoundary';
import { patchConsoleLogging } from './utils';
import SuspenseLoader from './suspense-loader';
Expand All @@ -10,13 +14,17 @@ patchConsoleLogging();
const App = lazy(() => import('./App'));

ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<Suspense fallback={SuspenseLoader()}>
<ConfigProvider>
<ErrorBoundary>
<App />
</ErrorBoundary>
</ConfigProvider>
</Suspense>
</React.StrictMode>
<ClientInitializationProvider>
<React.StrictMode>
<Suspense fallback={SuspenseLoader()}>
<RequireClientInitialization>
<ConfigProvider>
<ErrorBoundary>
<App />
</ErrorBoundary>
</ConfigProvider>
</RequireClientInitialization>
</Suspense>
</React.StrictMode>
</ClientInitializationProvider>
);
17 changes: 0 additions & 17 deletions ui/desktop/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
import { client } from './api/client.gen';

export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
Expand All @@ -17,19 +16,3 @@ export function patchConsoleLogging() {
// Intercept console methods
return;
}

// This needs to be called before any API calls are made, but since we're using the client
// in multiple useEffect locations, we can't be sure who goes first.
let clientInitialized = false;

export async function ensureClientInitialized() {
if (clientInitialized) return;
client.setConfig({
baseUrl: window.appConfig.get('GOOSE_API_HOST') + ':' + window.appConfig.get('GOOSE_PORT'),
headers: {
'Content-Type': 'application/json',
'X-Secret-Key': await window.electron.getSecretKey(),
},
});
clientInitialized = true;
}
Loading