refactor: move modelsToEntries and entriesToModels to modelInputListUtils for better organization

This commit is contained in:
Supra4E8C
2026-01-31 16:43:46 +08:00
parent b4d08dd0d7
commit 1053e91fe4
14 changed files with 54 additions and 50 deletions

View File

@@ -4,7 +4,8 @@ import { Button } from '@/components/ui/Button';
import { HeaderInputList } from '@/components/ui/HeaderInputList'; import { HeaderInputList } from '@/components/ui/HeaderInputList';
import { Input } from '@/components/ui/Input'; import { Input } from '@/components/ui/Input';
import { Modal } from '@/components/ui/Modal'; import { Modal } from '@/components/ui/Modal';
import { ModelInputList, modelsToEntries } from '@/components/ui/ModelInputList'; import { ModelInputList } from '@/components/ui/ModelInputList';
import { modelsToEntries } from '@/components/ui/modelInputListUtils';
import type { ProviderKeyConfig } from '@/types'; import type { ProviderKeyConfig } from '@/types';
import { headersToEntries } from '@/utils/headers'; import { headersToEntries } from '@/utils/headers';
import { excludedModelsToText } from '../utils'; import { excludedModelsToText } from '../utils';

View File

@@ -6,7 +6,7 @@ import { Input } from '@/components/ui/Input';
import { Modal } from '@/components/ui/Modal'; import { Modal } from '@/components/ui/Modal';
import type { ProviderKeyConfig } from '@/types'; import type { ProviderKeyConfig } from '@/types';
import { headersToEntries } from '@/utils/headers'; import { headersToEntries } from '@/utils/headers';
import { modelsToEntries } from '@/components/ui/ModelInputList'; import { modelsToEntries } from '@/components/ui/modelInputListUtils';
import { excludedModelsToText } from '../utils'; import { excludedModelsToText } from '../utils';
import type { ProviderFormState, ProviderModalProps } from '../types'; import type { ProviderFormState, ProviderModalProps } from '../types';

View File

@@ -4,7 +4,8 @@ import { Button } from '@/components/ui/Button';
import { HeaderInputList } from '@/components/ui/HeaderInputList'; import { HeaderInputList } from '@/components/ui/HeaderInputList';
import { Input } from '@/components/ui/Input'; import { Input } from '@/components/ui/Input';
import { Modal } from '@/components/ui/Modal'; import { Modal } from '@/components/ui/Modal';
import { ModelInputList, modelsToEntries } from '@/components/ui/ModelInputList'; import { ModelInputList } from '@/components/ui/ModelInputList';
import { modelsToEntries } from '@/components/ui/modelInputListUtils';
import { useNotificationStore } from '@/stores'; import { useNotificationStore } from '@/stores';
import { apiCallApi, getApiCallErrorMessage } from '@/services/api'; import { apiCallApi, getApiCallErrorMessage } from '@/services/api';
import type { OpenAIProviderConfig, ApiKeyEntry } from '@/types'; import type { OpenAIProviderConfig, ApiKeyEntry } from '@/types';

View File

@@ -29,14 +29,8 @@ const PROVIDERS: ProviderNavItem[] = [
export function ProviderNav() { export function ProviderNav() {
const resolvedTheme = useThemeStore((state) => state.resolvedTheme); const resolvedTheme = useThemeStore((state) => state.resolvedTheme);
const [activeProvider, setActiveProvider] = useState<ProviderId | null>(null); const [activeProvider, setActiveProvider] = useState<ProviderId | null>(null);
const [mounted, setMounted] = useState(false);
const scrollContainerRef = useRef<HTMLElement | null>(null); const scrollContainerRef = useRef<HTMLElement | null>(null);
useEffect(() => {
setMounted(true);
return () => setMounted(false);
}, []);
const getScrollContainer = useCallback(() => { const getScrollContainer = useCallback(() => {
if (scrollContainerRef.current) return scrollContainerRef.current; if (scrollContainerRef.current) return scrollContainerRef.current;
const container = document.querySelector('.content') as HTMLElement | null; const container = document.querySelector('.content') as HTMLElement | null;
@@ -74,7 +68,6 @@ export function ProviderNav() {
const container = getScrollContainer(); const container = getScrollContainer();
if (!container) return; if (!container) return;
handleScroll();
container.addEventListener('scroll', handleScroll, { passive: true }); container.addEventListener('scroll', handleScroll, { passive: true });
return () => container.removeEventListener('scroll', handleScroll); return () => container.removeEventListener('scroll', handleScroll);
}, [handleScroll, getScrollContainer]); }, [handleScroll, getScrollContainer]);
@@ -120,7 +113,7 @@ export function ProviderNav() {
</div> </div>
); );
if (!mounted) return null; if (typeof document === 'undefined') return null;
return createPortal(navContent, document.body); return createPortal(navContent, document.body);
} }

View File

@@ -4,7 +4,8 @@ import { Button } from '@/components/ui/Button';
import { HeaderInputList } from '@/components/ui/HeaderInputList'; import { HeaderInputList } from '@/components/ui/HeaderInputList';
import { Input } from '@/components/ui/Input'; import { Input } from '@/components/ui/Input';
import { Modal } from '@/components/ui/Modal'; import { Modal } from '@/components/ui/Modal';
import { ModelInputList, modelsToEntries } from '@/components/ui/ModelInputList'; import { ModelInputList } from '@/components/ui/ModelInputList';
import { modelsToEntries } from '@/components/ui/modelInputListUtils';
import type { ProviderKeyConfig } from '@/types'; import type { ProviderKeyConfig } from '@/types';
import { headersToEntries } from '@/utils/headers'; import { headersToEntries } from '@/utils/headers';
import type { ProviderModalProps, VertexFormState } from '../types'; import type { ProviderModalProps, VertexFormState } from '../types';

View File

@@ -1,12 +1,7 @@
import { Fragment } from 'react'; import { Fragment } from 'react';
import { Button } from './Button'; import { Button } from './Button';
import { IconX } from './icons'; import { IconX } from './icons';
import type { ModelAlias } from '@/types'; import type { ModelEntry } from './modelInputListUtils';
interface ModelEntry {
name: string;
alias: string;
}
interface ModelInputListProps { interface ModelInputListProps {
entries: ModelEntry[]; entries: ModelEntry[];
@@ -17,29 +12,6 @@ interface ModelInputListProps {
aliasPlaceholder?: string; aliasPlaceholder?: string;
} }
export const modelsToEntries = (models?: ModelAlias[]): ModelEntry[] => {
if (!Array.isArray(models) || models.length === 0) {
return [{ name: '', alias: '' }];
}
return models.map((m) => ({
name: m.name || '',
alias: m.alias || ''
}));
};
export const entriesToModels = (entries: ModelEntry[]): ModelAlias[] => {
return entries
.filter((entry) => entry.name.trim())
.map((entry) => {
const model: ModelAlias = { name: entry.name.trim() };
const alias = entry.alias.trim();
if (alias && alias !== model.name) {
model.alias = alias;
}
return model;
});
};
export function ModelInputList({ export function ModelInputList({
entries, entries,
onChange, onChange,

View File

@@ -0,0 +1,29 @@
import type { ModelAlias } from '@/types';
export interface ModelEntry {
name: string;
alias: string;
}
export const modelsToEntries = (models?: ModelAlias[]): ModelEntry[] => {
if (!Array.isArray(models) || models.length === 0) {
return [{ name: '', alias: '' }];
}
return models.map((model) => ({
name: model.name || '',
alias: model.alias || ''
}));
};
export const entriesToModels = (entries: ModelEntry[]): ModelAlias[] => {
return entries
.filter((entry) => entry.name.trim())
.map((entry) => {
const model: ModelAlias = { name: entry.name.trim() };
const alias = entry.alias.trim();
if (alias && alias !== model.name) {
model.alias = alias;
}
return model;
});
};

View File

@@ -5,7 +5,8 @@ import { Card } from '@/components/ui/Card';
import { Button } from '@/components/ui/Button'; import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input'; import { Input } from '@/components/ui/Input';
import { HeaderInputList } from '@/components/ui/HeaderInputList'; import { HeaderInputList } from '@/components/ui/HeaderInputList';
import { ModelInputList, modelsToEntries } from '@/components/ui/ModelInputList'; import { ModelInputList } from '@/components/ui/ModelInputList';
import { modelsToEntries } from '@/components/ui/modelInputListUtils';
import { useEdgeSwipeBack } from '@/hooks/useEdgeSwipeBack'; import { useEdgeSwipeBack } from '@/hooks/useEdgeSwipeBack';
import { SecondaryScreenShell } from '@/components/common/SecondaryScreenShell'; import { SecondaryScreenShell } from '@/components/common/SecondaryScreenShell';
import { providersApi } from '@/services/api'; import { providersApi } from '@/services/api';

View File

@@ -11,7 +11,7 @@ import { providersApi } from '@/services/api';
import { useAuthStore, useConfigStore, useNotificationStore } from '@/stores'; import { useAuthStore, useConfigStore, useNotificationStore } from '@/stores';
import type { ProviderKeyConfig } from '@/types'; import type { ProviderKeyConfig } from '@/types';
import { buildHeaderObject, headersToEntries } from '@/utils/headers'; import { buildHeaderObject, headersToEntries } from '@/utils/headers';
import { entriesToModels } from '@/components/ui/ModelInputList'; import { entriesToModels } from '@/components/ui/modelInputListUtils';
import { excludedModelsToText, parseExcludedModels } from '@/components/providers/utils'; import { excludedModelsToText, parseExcludedModels } from '@/components/providers/utils';
import type { ProviderFormState } from '@/components/providers'; import type { ProviderFormState } from '@/components/providers';
import layoutStyles from './AiProvidersEditLayout.module.scss'; import layoutStyles from './AiProvidersEditLayout.module.scss';

View File

@@ -4,7 +4,7 @@ import { Outlet, useLocation, useNavigate, useParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { providersApi } from '@/services/api'; import { providersApi } from '@/services/api';
import { useAuthStore, useConfigStore, useNotificationStore } from '@/stores'; import { useAuthStore, useConfigStore, useNotificationStore } from '@/stores';
import { entriesToModels, modelsToEntries } from '@/components/ui/ModelInputList'; import { entriesToModels, modelsToEntries } from '@/components/ui/modelInputListUtils';
import type { ApiKeyEntry, OpenAIProviderConfig } from '@/types'; import type { ApiKeyEntry, OpenAIProviderConfig } from '@/types';
import type { ModelInfo } from '@/utils/models'; import type { ModelInfo } from '@/utils/models';
import { buildHeaderObject, headersToEntries } from '@/utils/headers'; import { buildHeaderObject, headersToEntries } from '@/utils/headers';

View File

@@ -5,7 +5,8 @@ import { Card } from '@/components/ui/Card';
import { Button } from '@/components/ui/Button'; import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input'; import { Input } from '@/components/ui/Input';
import { HeaderInputList } from '@/components/ui/HeaderInputList'; import { HeaderInputList } from '@/components/ui/HeaderInputList';
import { ModelInputList, modelsToEntries } from '@/components/ui/ModelInputList'; import { ModelInputList } from '@/components/ui/ModelInputList';
import { modelsToEntries } from '@/components/ui/modelInputListUtils';
import { useEdgeSwipeBack } from '@/hooks/useEdgeSwipeBack'; import { useEdgeSwipeBack } from '@/hooks/useEdgeSwipeBack';
import { SecondaryScreenShell } from '@/components/common/SecondaryScreenShell'; import { SecondaryScreenShell } from '@/components/common/SecondaryScreenShell';
import { providersApi } from '@/services/api'; import { providersApi } from '@/services/api';

View File

@@ -1,4 +1,4 @@
import { useEffect, useRef, useState, type ChangeEvent } from 'react'; import { useCallback, useEffect, useRef, useState, type ChangeEvent } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Card } from '@/components/ui/Card'; import { Card } from '@/components/ui/Card';
import { Button } from '@/components/ui/Button'; import { Button } from '@/components/ui/Button';
@@ -85,11 +85,16 @@ export function OAuthPage() {
const timers = useRef<Record<string, number>>({}); const timers = useRef<Record<string, number>>({});
const vertexFileInputRef = useRef<HTMLInputElement | null>(null); const vertexFileInputRef = useRef<HTMLInputElement | null>(null);
const clearTimers = useCallback(() => {
Object.values(timers.current).forEach((timer) => window.clearInterval(timer));
timers.current = {};
}, []);
useEffect(() => { useEffect(() => {
return () => { return () => {
Object.values(timers.current).forEach((timer) => window.clearInterval(timer)); clearTimers();
}; };
}, []); }, [clearTimers]);
const updateProviderState = (provider: OAuthProvider, next: Partial<ProviderState>) => { const updateProviderState = (provider: OAuthProvider, next: Partial<ProviderState>) => {
setStates((prev) => ({ setStates((prev) => ({

View File

@@ -97,7 +97,7 @@ export function SettingsPage() {
setRoutingStrategy(config.routingStrategy); setRoutingStrategy(config.routingStrategy);
} }
} }
}, [config?.proxyUrl, config?.requestRetry, config?.logsMaxTotalSizeMb, config?.routingStrategy]); }, [config]);
const setPendingFlag = (key: PendingKey, value: boolean) => { const setPendingFlag = (key: PendingKey, value: boolean) => {
setPending((prev) => ({ ...prev, [key]: value })); setPending((prev) => ({ ...prev, [key]: value }));

View File

@@ -163,7 +163,7 @@ export const useAuthStore = create<AuthStoreState>()(
}); });
return true; return true;
} catch (error) { } catch {
set({ set({
isAuthenticated: false, isAuthenticated: false,
connectionStatus: 'error' connectionStatus: 'error'