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 { Input } from '@/components/ui/Input';
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 { headersToEntries } from '@/utils/headers';
import { excludedModelsToText } from '../utils';

View File

@@ -6,7 +6,7 @@ import { Input } from '@/components/ui/Input';
import { Modal } from '@/components/ui/Modal';
import type { ProviderKeyConfig } from '@/types';
import { headersToEntries } from '@/utils/headers';
import { modelsToEntries } from '@/components/ui/ModelInputList';
import { modelsToEntries } from '@/components/ui/modelInputListUtils';
import { excludedModelsToText } from '../utils';
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 { Input } from '@/components/ui/Input';
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 { apiCallApi, getApiCallErrorMessage } from '@/services/api';
import type { OpenAIProviderConfig, ApiKeyEntry } from '@/types';

View File

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

View File

@@ -4,7 +4,8 @@ import { Button } from '@/components/ui/Button';
import { HeaderInputList } from '@/components/ui/HeaderInputList';
import { Input } from '@/components/ui/Input';
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 { headersToEntries } from '@/utils/headers';
import type { ProviderModalProps, VertexFormState } from '../types';

View File

@@ -1,12 +1,7 @@
import { Fragment } from 'react';
import { Button } from './Button';
import { IconX } from './icons';
import type { ModelAlias } from '@/types';
interface ModelEntry {
name: string;
alias: string;
}
import type { ModelEntry } from './modelInputListUtils';
interface ModelInputListProps {
entries: ModelEntry[];
@@ -17,29 +12,6 @@ interface ModelInputListProps {
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({
entries,
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 { Input } from '@/components/ui/Input';
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 { SecondaryScreenShell } from '@/components/common/SecondaryScreenShell';
import { providersApi } from '@/services/api';

View File

@@ -11,7 +11,7 @@ import { providersApi } from '@/services/api';
import { useAuthStore, useConfigStore, useNotificationStore } from '@/stores';
import type { ProviderKeyConfig } from '@/types';
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 type { ProviderFormState } from '@/components/providers';
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 { providersApi } from '@/services/api';
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 { ModelInfo } from '@/utils/models';
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 { Input } from '@/components/ui/Input';
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 { SecondaryScreenShell } from '@/components/common/SecondaryScreenShell';
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 { Card } from '@/components/ui/Card';
import { Button } from '@/components/ui/Button';
@@ -85,11 +85,16 @@ export function OAuthPage() {
const timers = useRef<Record<string, number>>({});
const vertexFileInputRef = useRef<HTMLInputElement | null>(null);
const clearTimers = useCallback(() => {
Object.values(timers.current).forEach((timer) => window.clearInterval(timer));
timers.current = {};
}, []);
useEffect(() => {
return () => {
Object.values(timers.current).forEach((timer) => window.clearInterval(timer));
clearTimers();
};
}, []);
}, [clearTimers]);
const updateProviderState = (provider: OAuthProvider, next: Partial<ProviderState>) => {
setStates((prev) => ({

View File

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

View File

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