mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-17 13:13:35 +08:00
Merge pull request #586 from yjjheizhu/feat/hide-official-providers-when-not-logged-in
feat: hide official provider sections when OAuth not logged in
This commit is contained in:
commit
071ee48e2b
@ -1,9 +1,11 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { act, cleanup, fireEvent, render, screen } from '@testing-library/react'
|
||||
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,14 @@ 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)
|
||||
})
|
||||
|
||||
// Prevent real API calls from fetchStatus on mount
|
||||
beforeEach(() => {
|
||||
useHahaOAuthStore.setState({ fetchStatus: async () => {} })
|
||||
useHahaOpenAIOAuthStore.setState({ fetchStatus: async () => {} })
|
||||
})
|
||||
|
||||
describe('ModelSelector', () => {
|
||||
@ -135,6 +145,10 @@ describe('ModelSelector', () => {
|
||||
},
|
||||
]
|
||||
const setSessionRuntime = vi.fn()
|
||||
useHahaOpenAIOAuthStore.setState({
|
||||
status: { loggedIn: true, expiresAt: null, email: null, accountId: null },
|
||||
fetchStatus: async () => {},
|
||||
})
|
||||
useSettingsStore.setState({
|
||||
locale: 'en',
|
||||
availableModels: openAIModels,
|
||||
@ -169,6 +183,45 @@ describe('ModelSelector', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('hides official provider sections when OAuth is not logged in', async () => {
|
||||
useHahaOAuthStore.setState({ status: { loggedIn: false }, fetchStatus: async () => {} })
|
||||
useHahaOpenAIOAuthStore.setState({ status: { loggedIn: false }, fetchStatus: async () => {} })
|
||||
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 = {
|
||||
@ -36,7 +38,8 @@ type Props = {
|
||||
}
|
||||
|
||||
type DropdownPosition = {
|
||||
top: number
|
||||
top: number | undefined
|
||||
bottom: number | undefined
|
||||
left: number
|
||||
width: number
|
||||
maxHeight: number
|
||||
@ -101,6 +104,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 +114,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 +187,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 +218,11 @@ export function ModelSelector({
|
||||
void fetchProviders()
|
||||
}, [fetchProviders, isRuntimeScoped, providersLoading])
|
||||
|
||||
useEffect(() => {
|
||||
void fetchClaudeOAuthStatus()
|
||||
void fetchOpenAIOAuthStatus()
|
||||
}, [fetchClaudeOAuthStatus, fetchOpenAIOAuthStatus])
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return
|
||||
const handleClick = (e: MouseEvent) => {
|
||||
@ -245,9 +268,8 @@ export function ModelSelector({
|
||||
const maxHeight = Math.min(DROPDOWN_MAX_HEIGHT, availableHeight)
|
||||
|
||||
setDropdownPosition({
|
||||
top: placeBelow
|
||||
? rect.bottom + DROPDOWN_GAP
|
||||
: Math.max(VIEWPORT_MARGIN, rect.top - DROPDOWN_GAP - maxHeight),
|
||||
top: placeBelow ? rect.bottom + DROPDOWN_GAP : undefined,
|
||||
bottom: placeBelow ? undefined : (viewportHeight - rect.top + DROPDOWN_GAP),
|
||||
left,
|
||||
width,
|
||||
maxHeight,
|
||||
@ -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
|
||||
@ -511,6 +535,7 @@ export function ModelSelector({
|
||||
className="fixed z-[80] rounded-xl border border-[var(--color-border)] bg-[var(--color-surface-container-lowest)] shadow-[var(--shadow-dropdown)]"
|
||||
style={{
|
||||
top: dropdownPosition.top,
|
||||
bottom: dropdownPosition.bottom,
|
||||
left: dropdownPosition.left,
|
||||
width: dropdownPosition.width,
|
||||
}}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user