mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-18 13:23:33 +08:00
feat: hide official provider sections in ModelSelector when OAuth not logged in
Only show Claude Official and ChatGPT Official model groups in the runtime-scoped ModelSelector dropdown when the corresponding OAuth session is active. This prevents users from selecting official models they cannot actually use. - Import useHahaOAuthStore and useHahaOpenAIOAuthStore into ModelSelector - Fetch OAuth status on mount - Pass login flags to buildProviderChoices to conditionally include sections - Add test case verifying hidden sections when not logged in Co-Authored-By: claude-opus-4-6 <noreply@anthropic.com>
This commit is contained in:
parent
91224cbd20
commit
aa1321a02b
@ -4,6 +4,8 @@ import '@testing-library/jest-dom'
|
||||
|
||||
import { ModelSelector } from './ModelSelector'
|
||||
import { useChatStore } from '../../stores/chatStore'
|
||||
import { useHahaOAuthStore } from '../../stores/hahaOAuthStore'
|
||||
import { useHahaOpenAIOAuthStore } from '../../stores/hahaOpenAIOAuthStore'
|
||||
import { useProviderStore } from '../../stores/providerStore'
|
||||
import { useSessionRuntimeStore } from '../../stores/sessionRuntimeStore'
|
||||
import { useSettingsStore } from '../../stores/settingsStore'
|
||||
@ -28,6 +30,8 @@ afterEach(() => {
|
||||
useProviderStore.setState(useProviderStore.getInitialState(), true)
|
||||
useSessionRuntimeStore.setState(useSessionRuntimeStore.getInitialState(), true)
|
||||
useChatStore.setState(useChatStore.getInitialState(), true)
|
||||
useHahaOAuthStore.setState(useHahaOAuthStore.getInitialState(), true)
|
||||
useHahaOpenAIOAuthStore.setState(useHahaOpenAIOAuthStore.getInitialState(), true)
|
||||
})
|
||||
|
||||
describe('ModelSelector', () => {
|
||||
@ -135,6 +139,7 @@ describe('ModelSelector', () => {
|
||||
},
|
||||
]
|
||||
const setSessionRuntime = vi.fn()
|
||||
useHahaOpenAIOAuthStore.setState({ status: { loggedIn: true, expiresAt: null, email: null, accountId: null } })
|
||||
useSettingsStore.setState({
|
||||
locale: 'en',
|
||||
availableModels: openAIModels,
|
||||
@ -169,6 +174,45 @@ describe('ModelSelector', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('hides official provider sections when OAuth is not logged in', async () => {
|
||||
useHahaOAuthStore.setState({ status: { loggedIn: false } })
|
||||
useHahaOpenAIOAuthStore.setState({ status: { loggedIn: false } })
|
||||
useSettingsStore.setState({
|
||||
locale: 'en',
|
||||
availableModels: MODELS,
|
||||
currentModel: MODELS[0],
|
||||
activeProviderName: 'Provider A',
|
||||
})
|
||||
useProviderStore.setState({
|
||||
providers: [{
|
||||
id: 'provider-a',
|
||||
presetId: 'custom',
|
||||
name: 'Provider A',
|
||||
apiKey: '***',
|
||||
baseUrl: 'https://api.example.com',
|
||||
apiFormat: 'anthropic',
|
||||
models: {
|
||||
main: 'provider-main',
|
||||
haiku: '',
|
||||
sonnet: '',
|
||||
opus: '',
|
||||
},
|
||||
}],
|
||||
activeId: 'provider-a',
|
||||
hasLoadedProviders: true,
|
||||
isLoading: true,
|
||||
})
|
||||
|
||||
render(<ModelSelector runtimeKey="session-hide" />)
|
||||
|
||||
await clickByRole(/alpha/i)
|
||||
|
||||
const dropdown = screen.getByTestId('model-selector-dropdown')
|
||||
expect(dropdown.textContent).not.toContain('Claude Official')
|
||||
expect(dropdown.textContent).not.toContain('ChatGPT Official')
|
||||
expect(dropdown.textContent).toContain('Provider A')
|
||||
})
|
||||
|
||||
it('portals the dropdown outside clipping containers and positions it below the trigger', async () => {
|
||||
useSettingsStore.setState({
|
||||
locale: 'en',
|
||||
|
||||
@ -16,6 +16,8 @@ import type { RuntimeSelection } from '../../types/runtime'
|
||||
import type { EffortLevel, ModelInfo } from '../../types/settings'
|
||||
import { useMobileViewport } from '../../hooks/useMobileViewport'
|
||||
import { isTauriRuntime } from '../../lib/desktopRuntime'
|
||||
import { useHahaOAuthStore } from '../../stores/hahaOAuthStore'
|
||||
import { useHahaOpenAIOAuthStore } from '../../stores/hahaOpenAIOAuthStore'
|
||||
import { MobileBottomSheet } from '../shared/MobileBottomSheet'
|
||||
|
||||
type ProviderChoice = {
|
||||
@ -101,6 +103,8 @@ function buildProviderChoices(
|
||||
officialName: string,
|
||||
openAIOfficialName: string,
|
||||
labels: Record<'main' | 'haiku' | 'sonnet' | 'opus', string>,
|
||||
claudeOfficialLoggedIn: boolean,
|
||||
openAIOfficialLoggedIn: boolean,
|
||||
): ProviderChoice[] {
|
||||
const claudeOfficialModels = activeId === null && availableModels.length > 0
|
||||
? availableModels
|
||||
@ -109,21 +113,30 @@ function buildProviderChoices(
|
||||
? availableModels
|
||||
: OPENAI_OFFICIAL_MODELS
|
||||
|
||||
return [
|
||||
officialChoices(null, claudeOfficialModels, activeId === null, officialName),
|
||||
officialChoices(
|
||||
const choices: ProviderChoice[] = []
|
||||
|
||||
if (claudeOfficialLoggedIn) {
|
||||
choices.push(officialChoices(null, claudeOfficialModels, activeId === null, officialName))
|
||||
}
|
||||
if (openAIOfficialLoggedIn) {
|
||||
choices.push(officialChoices(
|
||||
OPENAI_OFFICIAL_PROVIDER_ID,
|
||||
openAIOfficialModels,
|
||||
activeId === OPENAI_OFFICIAL_PROVIDER_ID,
|
||||
openAIOfficialName,
|
||||
),
|
||||
...providers.map((provider) => ({
|
||||
))
|
||||
}
|
||||
|
||||
for (const provider of providers) {
|
||||
choices.push({
|
||||
providerId: provider.id,
|
||||
providerName: provider.name,
|
||||
isDefault: activeId === provider.id,
|
||||
models: buildProviderModels(provider, labels),
|
||||
})),
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
return choices
|
||||
}
|
||||
|
||||
function resolveDefaultRuntimeSelection(
|
||||
@ -173,6 +186,10 @@ export function ModelSelector({
|
||||
isLoading: providersLoading,
|
||||
fetchProviders,
|
||||
} = useProviderStore()
|
||||
const claudeOAuthStatus = useHahaOAuthStore((s) => s.status)
|
||||
const fetchClaudeOAuthStatus = useHahaOAuthStore((s) => s.fetchStatus)
|
||||
const openAIOAuthStatus = useHahaOpenAIOAuthStore((s) => s.status)
|
||||
const fetchOpenAIOAuthStatus = useHahaOpenAIOAuthStore((s) => s.fetchStatus)
|
||||
const runtimeSelection = useSessionRuntimeStore((state) =>
|
||||
runtimeKey ? state.selections[runtimeKey] : undefined,
|
||||
)
|
||||
@ -200,6 +217,11 @@ export function ModelSelector({
|
||||
void fetchProviders()
|
||||
}, [fetchProviders, isRuntimeScoped, providersLoading])
|
||||
|
||||
useEffect(() => {
|
||||
void fetchClaudeOAuthStatus()
|
||||
void fetchOpenAIOAuthStatus()
|
||||
}, [fetchClaudeOAuthStatus, fetchOpenAIOAuthStatus])
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return
|
||||
const handleClick = (e: MouseEvent) => {
|
||||
@ -290,8 +312,10 @@ export function ModelSelector({
|
||||
t('settings.providers.officialName'),
|
||||
t('settings.providers.openaiOfficialName'),
|
||||
roleLabels,
|
||||
claudeOAuthStatus?.loggedIn === true,
|
||||
openAIOAuthStatus?.loggedIn === true,
|
||||
),
|
||||
[activeId, availableModels, providers, roleLabels, t],
|
||||
[activeId, availableModels, providers, roleLabels, t, claudeOAuthStatus, openAIOAuthStatus],
|
||||
)
|
||||
|
||||
const selectedModel = isControlled
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user