mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-12 21:03:05 -07:00
feat(ui): move user identity display to header (#18216)
This commit is contained in:
@@ -21,7 +21,6 @@ import { act, useContext, type ReactElement } from 'react';
|
|||||||
import { AppContainer } from './AppContainer.js';
|
import { AppContainer } from './AppContainer.js';
|
||||||
import { SettingsContext } from './contexts/SettingsContext.js';
|
import { SettingsContext } from './contexts/SettingsContext.js';
|
||||||
import { type TrackedToolCall } from './hooks/useReactToolScheduler.js';
|
import { type TrackedToolCall } from './hooks/useReactToolScheduler.js';
|
||||||
import { MessageType } from './types.js';
|
|
||||||
import {
|
import {
|
||||||
type Config,
|
type Config,
|
||||||
makeFakeConfig,
|
makeFakeConfig,
|
||||||
@@ -29,8 +28,6 @@ import {
|
|||||||
type UserFeedbackPayload,
|
type UserFeedbackPayload,
|
||||||
type ResumedSessionData,
|
type ResumedSessionData,
|
||||||
AuthType,
|
AuthType,
|
||||||
UserAccountManager,
|
|
||||||
type ContentGeneratorConfig,
|
|
||||||
type AgentDefinition,
|
type AgentDefinition,
|
||||||
} from '@google/gemini-cli-core';
|
} from '@google/gemini-cli-core';
|
||||||
|
|
||||||
@@ -47,11 +44,6 @@ const mockIdeClient = vi.hoisted(() => ({
|
|||||||
getInstance: vi.fn().mockReturnValue(new Promise(() => {})),
|
getInstance: vi.fn().mockReturnValue(new Promise(() => {})),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Mock UserAccountManager
|
|
||||||
const mockUserAccountManager = vi.hoisted(() => ({
|
|
||||||
getCachedGoogleAccount: vi.fn().mockReturnValue(null),
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Mock stdout
|
// Mock stdout
|
||||||
const mocks = vi.hoisted(() => ({
|
const mocks = vi.hoisted(() => ({
|
||||||
mockStdout: { write: vi.fn() },
|
mockStdout: { write: vi.fn() },
|
||||||
@@ -81,9 +73,6 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => {
|
|||||||
})),
|
})),
|
||||||
enableMouseEvents: vi.fn(),
|
enableMouseEvents: vi.fn(),
|
||||||
disableMouseEvents: vi.fn(),
|
disableMouseEvents: vi.fn(),
|
||||||
UserAccountManager: vi
|
|
||||||
.fn()
|
|
||||||
.mockImplementation(() => mockUserAccountManager),
|
|
||||||
FileDiscoveryService: vi.fn().mockImplementation(() => ({
|
FileDiscoveryService: vi.fn().mockImplementation(() => ({
|
||||||
initialize: vi.fn(),
|
initialize: vi.fn(),
|
||||||
})),
|
})),
|
||||||
@@ -428,7 +417,6 @@ describe('AppContainer State Management', () => {
|
|||||||
...defaultMergedSettings.ui,
|
...defaultMergedSettings.ui,
|
||||||
showStatusInTitle: false,
|
showStatusInTitle: false,
|
||||||
hideWindowTitle: false,
|
hideWindowTitle: false,
|
||||||
showUserIdentity: true,
|
|
||||||
},
|
},
|
||||||
useAlternateBuffer: false,
|
useAlternateBuffer: false,
|
||||||
},
|
},
|
||||||
@@ -500,162 +488,6 @@ describe('AppContainer State Management', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Authentication Check', () => {
|
|
||||||
it('displays correct message for LOGIN_WITH_GOOGLE auth type', async () => {
|
|
||||||
// Explicitly mock implementation to ensure we control the instance
|
|
||||||
(UserAccountManager as unknown as Mock).mockImplementation(
|
|
||||||
() => mockUserAccountManager,
|
|
||||||
);
|
|
||||||
|
|
||||||
mockUserAccountManager.getCachedGoogleAccount.mockReturnValue(
|
|
||||||
'test@example.com',
|
|
||||||
);
|
|
||||||
const mockAddItem = vi.fn();
|
|
||||||
mockedUseHistory.mockReturnValue({
|
|
||||||
history: [],
|
|
||||||
addItem: mockAddItem,
|
|
||||||
updateItem: vi.fn(),
|
|
||||||
clearItems: vi.fn(),
|
|
||||||
loadHistory: vi.fn(),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Explicitly enable showUserIdentity
|
|
||||||
mockSettings.merged.ui = {
|
|
||||||
...mockSettings.merged.ui,
|
|
||||||
showUserIdentity: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Need to ensure config.getContentGeneratorConfig() returns appropriate authType
|
|
||||||
const authConfig = makeFakeConfig();
|
|
||||||
// Mock getTargetDir as well since makeFakeConfig might not set it up fully for the component
|
|
||||||
vi.spyOn(authConfig, 'getTargetDir').mockReturnValue('/test/workspace');
|
|
||||||
vi.spyOn(authConfig, 'initialize').mockResolvedValue(undefined);
|
|
||||||
vi.spyOn(authConfig, 'getExtensionLoader').mockReturnValue(
|
|
||||||
mockExtensionManager,
|
|
||||||
);
|
|
||||||
|
|
||||||
vi.spyOn(authConfig, 'getContentGeneratorConfig').mockReturnValue({
|
|
||||||
authType: AuthType.LOGIN_WITH_GOOGLE,
|
|
||||||
} as unknown as ContentGeneratorConfig);
|
|
||||||
vi.spyOn(authConfig, 'getUserTierName').mockReturnValue('Standard Tier');
|
|
||||||
|
|
||||||
let unmount: () => void;
|
|
||||||
await act(async () => {
|
|
||||||
const result = renderAppContainer({ config: authConfig });
|
|
||||||
unmount = result.unmount;
|
|
||||||
});
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(UserAccountManager).toHaveBeenCalled();
|
|
||||||
expect(
|
|
||||||
mockUserAccountManager.getCachedGoogleAccount,
|
|
||||||
).toHaveBeenCalled();
|
|
||||||
expect(mockAddItem).toHaveBeenCalledWith(
|
|
||||||
expect.objectContaining({
|
|
||||||
text: 'Logged in with Google: test@example.com (Plan: Standard Tier)',
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
await act(async () => {
|
|
||||||
unmount!();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it('displays correct message for USE_GEMINI auth type', async () => {
|
|
||||||
// Explicitly mock implementation to ensure we control the instance
|
|
||||||
(UserAccountManager as unknown as Mock).mockImplementation(
|
|
||||||
() => mockUserAccountManager,
|
|
||||||
);
|
|
||||||
|
|
||||||
mockUserAccountManager.getCachedGoogleAccount.mockReturnValue(null);
|
|
||||||
const mockAddItem = vi.fn();
|
|
||||||
mockedUseHistory.mockReturnValue({
|
|
||||||
history: [],
|
|
||||||
addItem: mockAddItem,
|
|
||||||
updateItem: vi.fn(),
|
|
||||||
clearItems: vi.fn(),
|
|
||||||
loadHistory: vi.fn(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const authConfig = makeFakeConfig();
|
|
||||||
vi.spyOn(authConfig, 'getTargetDir').mockReturnValue('/test/workspace');
|
|
||||||
vi.spyOn(authConfig, 'initialize').mockResolvedValue(undefined);
|
|
||||||
vi.spyOn(authConfig, 'getExtensionLoader').mockReturnValue(
|
|
||||||
mockExtensionManager,
|
|
||||||
);
|
|
||||||
|
|
||||||
vi.spyOn(authConfig, 'getContentGeneratorConfig').mockReturnValue({
|
|
||||||
authType: AuthType.USE_GEMINI,
|
|
||||||
} as unknown as ContentGeneratorConfig);
|
|
||||||
vi.spyOn(authConfig, 'getUserTierName').mockReturnValue('Standard Tier');
|
|
||||||
|
|
||||||
let unmount: () => void;
|
|
||||||
await act(async () => {
|
|
||||||
const result = renderAppContainer({ config: authConfig });
|
|
||||||
unmount = result.unmount;
|
|
||||||
});
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(mockAddItem).toHaveBeenCalledWith(
|
|
||||||
expect.objectContaining({
|
|
||||||
text: expect.stringContaining('Authenticated with gemini-api-key'),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
await act(async () => {
|
|
||||||
unmount!();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not display authentication message if showUserIdentity is false', async () => {
|
|
||||||
mockUserAccountManager.getCachedGoogleAccount.mockReturnValue(
|
|
||||||
'test@example.com',
|
|
||||||
);
|
|
||||||
const mockAddItem = vi.fn();
|
|
||||||
mockedUseHistory.mockReturnValue({
|
|
||||||
history: [],
|
|
||||||
addItem: mockAddItem,
|
|
||||||
updateItem: vi.fn(),
|
|
||||||
clearItems: vi.fn(),
|
|
||||||
loadHistory: vi.fn(),
|
|
||||||
});
|
|
||||||
|
|
||||||
mockSettings.merged.ui = {
|
|
||||||
...mockSettings.merged.ui,
|
|
||||||
showUserIdentity: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
const authConfig = makeFakeConfig();
|
|
||||||
vi.spyOn(authConfig, 'getTargetDir').mockReturnValue('/test/workspace');
|
|
||||||
vi.spyOn(authConfig, 'initialize').mockResolvedValue(undefined);
|
|
||||||
vi.spyOn(authConfig, 'getExtensionLoader').mockReturnValue(
|
|
||||||
mockExtensionManager,
|
|
||||||
);
|
|
||||||
|
|
||||||
vi.spyOn(authConfig, 'getContentGeneratorConfig').mockReturnValue({
|
|
||||||
authType: AuthType.LOGIN_WITH_GOOGLE,
|
|
||||||
} as unknown as ContentGeneratorConfig);
|
|
||||||
|
|
||||||
let unmount: () => void;
|
|
||||||
await act(async () => {
|
|
||||||
const result = renderAppContainer({ config: authConfig });
|
|
||||||
unmount = result.unmount;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Give it some time to potentially call addItem
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
||||||
|
|
||||||
expect(mockAddItem).not.toHaveBeenCalledWith(
|
|
||||||
expect.objectContaining({
|
|
||||||
type: MessageType.INFO,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
await act(async () => {
|
|
||||||
unmount!();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Context Providers', () => {
|
describe('Context Providers', () => {
|
||||||
it('provides AppContext with correct values', async () => {
|
it('provides AppContext with correct values', async () => {
|
||||||
let unmount: () => void;
|
let unmount: () => void;
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ import {
|
|||||||
getErrorMessage,
|
getErrorMessage,
|
||||||
getAllGeminiMdFilenames,
|
getAllGeminiMdFilenames,
|
||||||
AuthType,
|
AuthType,
|
||||||
UserAccountManager,
|
|
||||||
clearCachedCredentialFile,
|
clearCachedCredentialFile,
|
||||||
type ResumedSessionData,
|
type ResumedSessionData,
|
||||||
recordExitFail,
|
recordExitFail,
|
||||||
@@ -191,51 +190,6 @@ export const AppContainer = (props: AppContainerProps) => {
|
|||||||
const historyManager = useHistory({
|
const historyManager = useHistory({
|
||||||
chatRecordingService: config.getGeminiClient()?.getChatRecordingService(),
|
chatRecordingService: config.getGeminiClient()?.getChatRecordingService(),
|
||||||
});
|
});
|
||||||
const { addItem } = historyManager;
|
|
||||||
|
|
||||||
const authCheckPerformed = useRef(false);
|
|
||||||
useEffect(() => {
|
|
||||||
if (authCheckPerformed.current) return;
|
|
||||||
authCheckPerformed.current = true;
|
|
||||||
|
|
||||||
if (resumedSessionData || settings.merged.ui.showUserIdentity === false) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const authType = config.getContentGeneratorConfig()?.authType;
|
|
||||||
|
|
||||||
// Run this asynchronously to avoid blocking the event loop.
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
(async () => {
|
|
||||||
try {
|
|
||||||
const userAccountManager = new UserAccountManager();
|
|
||||||
const email = userAccountManager.getCachedGoogleAccount();
|
|
||||||
const tierName = config.getUserTierName();
|
|
||||||
|
|
||||||
if (authType) {
|
|
||||||
let message =
|
|
||||||
authType === AuthType.LOGIN_WITH_GOOGLE
|
|
||||||
? email
|
|
||||||
? `Logged in with Google: ${email}`
|
|
||||||
: 'Logged in with Google'
|
|
||||||
: `Authenticated with ${authType}`;
|
|
||||||
if (tierName) {
|
|
||||||
message += ` (Plan: ${tierName})`;
|
|
||||||
}
|
|
||||||
addItem({
|
|
||||||
type: MessageType.INFO,
|
|
||||||
text: message,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (_e) {
|
|
||||||
// Ignore errors during initial auth check
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
}, [
|
|
||||||
config,
|
|
||||||
resumedSessionData,
|
|
||||||
settings.merged.ui.showUserIdentity,
|
|
||||||
addItem,
|
|
||||||
]);
|
|
||||||
|
|
||||||
useMemoryMonitor(historyManager);
|
useMemoryMonitor(historyManager);
|
||||||
const isAlternateBuffer = useAlternateBuffer();
|
const isAlternateBuffer = useAlternateBuffer();
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
import { Box } from 'ink';
|
import { Box } from 'ink';
|
||||||
import { Header } from './Header.js';
|
import { Header } from './Header.js';
|
||||||
import { Tips } from './Tips.js';
|
import { Tips } from './Tips.js';
|
||||||
|
import { UserIdentity } from './UserIdentity.js';
|
||||||
import { useSettings } from '../contexts/SettingsContext.js';
|
import { useSettings } from '../contexts/SettingsContext.js';
|
||||||
import { useConfig } from '../contexts/ConfigContext.js';
|
import { useConfig } from '../contexts/ConfigContext.js';
|
||||||
import { useUIState } from '../contexts/UIStateContext.js';
|
import { useUIState } from '../contexts/UIStateContext.js';
|
||||||
@@ -40,6 +41,9 @@ export const AppHeader = ({ version }: AppHeaderProps) => {
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
{settings.merged.ui.showUserIdentity !== false && (
|
||||||
|
<UserIdentity config={config} />
|
||||||
|
)}
|
||||||
{!(settings.merged.ui.hideTips || config.getScreenReader()) &&
|
{!(settings.merged.ui.hideTips || config.getScreenReader()) &&
|
||||||
showTips && <Tips config={config} />}
|
showTips && <Tips config={config} />}
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -0,0 +1,139 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright 2025 Google LLC
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { renderWithProviders } from '../../test-utils/render.js';
|
||||||
|
import { UserIdentity } from './UserIdentity.js';
|
||||||
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||||
|
import {
|
||||||
|
makeFakeConfig,
|
||||||
|
AuthType,
|
||||||
|
UserAccountManager,
|
||||||
|
type ContentGeneratorConfig,
|
||||||
|
} from '@google/gemini-cli-core';
|
||||||
|
|
||||||
|
// Mock UserAccountManager to control cached account
|
||||||
|
vi.mock('@google/gemini-cli-core', async (importOriginal) => {
|
||||||
|
const original =
|
||||||
|
await importOriginal<typeof import('@google/gemini-cli-core')>();
|
||||||
|
return {
|
||||||
|
...original,
|
||||||
|
UserAccountManager: vi.fn().mockImplementation(() => ({
|
||||||
|
getCachedGoogleAccount: () => 'test@example.com',
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('<UserIdentity />', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render login message and auth indicator', () => {
|
||||||
|
const mockConfig = makeFakeConfig();
|
||||||
|
vi.spyOn(mockConfig, 'getContentGeneratorConfig').mockReturnValue({
|
||||||
|
authType: AuthType.LOGIN_WITH_GOOGLE,
|
||||||
|
model: 'gemini-pro',
|
||||||
|
} as unknown as ContentGeneratorConfig);
|
||||||
|
vi.spyOn(mockConfig, 'getUserTierName').mockReturnValue(undefined);
|
||||||
|
|
||||||
|
const { lastFrame, unmount } = renderWithProviders(
|
||||||
|
<UserIdentity config={mockConfig} />,
|
||||||
|
);
|
||||||
|
|
||||||
|
const output = lastFrame();
|
||||||
|
expect(output).toContain('Logged in with Google: test@example.com');
|
||||||
|
expect(output).toContain('/auth');
|
||||||
|
unmount();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render login message without colon if email is missing', () => {
|
||||||
|
// Modify the mock for this specific test
|
||||||
|
vi.mocked(UserAccountManager).mockImplementationOnce(
|
||||||
|
() =>
|
||||||
|
({
|
||||||
|
getCachedGoogleAccount: () => undefined,
|
||||||
|
}) as unknown as UserAccountManager,
|
||||||
|
);
|
||||||
|
|
||||||
|
const mockConfig = makeFakeConfig();
|
||||||
|
vi.spyOn(mockConfig, 'getContentGeneratorConfig').mockReturnValue({
|
||||||
|
authType: AuthType.LOGIN_WITH_GOOGLE,
|
||||||
|
model: 'gemini-pro',
|
||||||
|
} as unknown as ContentGeneratorConfig);
|
||||||
|
vi.spyOn(mockConfig, 'getUserTierName').mockReturnValue(undefined);
|
||||||
|
|
||||||
|
const { lastFrame, unmount } = renderWithProviders(
|
||||||
|
<UserIdentity config={mockConfig} />,
|
||||||
|
);
|
||||||
|
|
||||||
|
const output = lastFrame();
|
||||||
|
expect(output).toContain('Logged in with Google');
|
||||||
|
expect(output).not.toContain('Logged in with Google:');
|
||||||
|
expect(output).toContain('/auth');
|
||||||
|
unmount();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render plan name on a separate line if provided', () => {
|
||||||
|
const mockConfig = makeFakeConfig();
|
||||||
|
vi.spyOn(mockConfig, 'getContentGeneratorConfig').mockReturnValue({
|
||||||
|
authType: AuthType.LOGIN_WITH_GOOGLE,
|
||||||
|
model: 'gemini-pro',
|
||||||
|
} as unknown as ContentGeneratorConfig);
|
||||||
|
vi.spyOn(mockConfig, 'getUserTierName').mockReturnValue('Premium Plan');
|
||||||
|
|
||||||
|
const { lastFrame, unmount } = renderWithProviders(
|
||||||
|
<UserIdentity config={mockConfig} />,
|
||||||
|
);
|
||||||
|
|
||||||
|
const output = lastFrame();
|
||||||
|
expect(output).toContain('Logged in with Google: test@example.com');
|
||||||
|
expect(output).toContain('/auth');
|
||||||
|
expect(output).toContain('Plan: Premium Plan');
|
||||||
|
|
||||||
|
// Check for two lines (or more if wrapped, but here it should be separate)
|
||||||
|
const lines = output?.split('\n').filter((line) => line.trim().length > 0);
|
||||||
|
expect(lines?.some((line) => line.includes('Logged in with Google'))).toBe(
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
expect(lines?.some((line) => line.includes('Plan: Premium Plan'))).toBe(
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
unmount();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not render if authType is missing', () => {
|
||||||
|
const mockConfig = makeFakeConfig();
|
||||||
|
vi.spyOn(mockConfig, 'getContentGeneratorConfig').mockReturnValue(
|
||||||
|
{} as unknown as ContentGeneratorConfig,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { lastFrame, unmount } = renderWithProviders(
|
||||||
|
<UserIdentity config={mockConfig} />,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(lastFrame()).toBe('');
|
||||||
|
unmount();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render non-Google auth message', () => {
|
||||||
|
const mockConfig = makeFakeConfig();
|
||||||
|
vi.spyOn(mockConfig, 'getContentGeneratorConfig').mockReturnValue({
|
||||||
|
authType: AuthType.USE_GEMINI,
|
||||||
|
model: 'gemini-pro',
|
||||||
|
} as unknown as ContentGeneratorConfig);
|
||||||
|
vi.spyOn(mockConfig, 'getUserTierName').mockReturnValue(undefined);
|
||||||
|
|
||||||
|
const { lastFrame, unmount } = renderWithProviders(
|
||||||
|
<UserIdentity config={mockConfig} />,
|
||||||
|
);
|
||||||
|
|
||||||
|
const output = lastFrame();
|
||||||
|
expect(output).toContain(`Authenticated with ${AuthType.USE_GEMINI}`);
|
||||||
|
expect(output).toContain('/auth');
|
||||||
|
unmount();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright 2025 Google LLC
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type React from 'react';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import { Box, Text } from 'ink';
|
||||||
|
import { theme } from '../semantic-colors.js';
|
||||||
|
import {
|
||||||
|
type Config,
|
||||||
|
UserAccountManager,
|
||||||
|
AuthType,
|
||||||
|
} from '@google/gemini-cli-core';
|
||||||
|
|
||||||
|
interface UserIdentityProps {
|
||||||
|
config: Config;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const UserIdentity: React.FC<UserIdentityProps> = ({ config }) => {
|
||||||
|
const authType = config.getContentGeneratorConfig()?.authType;
|
||||||
|
|
||||||
|
const { email, tierName } = useMemo(() => {
|
||||||
|
if (!authType) {
|
||||||
|
return { email: undefined, tierName: undefined };
|
||||||
|
}
|
||||||
|
const userAccountManager = new UserAccountManager();
|
||||||
|
return {
|
||||||
|
email: userAccountManager.getCachedGoogleAccount(),
|
||||||
|
tierName: config.getUserTierName(),
|
||||||
|
};
|
||||||
|
}, [config, authType]);
|
||||||
|
|
||||||
|
if (!authType) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box marginY={1} flexDirection="column">
|
||||||
|
<Box>
|
||||||
|
<Text color={theme.text.primary}>
|
||||||
|
{authType === AuthType.LOGIN_WITH_GOOGLE ? (
|
||||||
|
<Text>
|
||||||
|
<Text bold>Logged in with Google{email ? ':' : ''}</Text>
|
||||||
|
{email ? ` ${email}` : ''}
|
||||||
|
</Text>
|
||||||
|
) : (
|
||||||
|
`Authenticated with ${authType}`
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
|
<Text color={theme.text.secondary}> /auth</Text>
|
||||||
|
</Box>
|
||||||
|
{tierName && (
|
||||||
|
<Text color={theme.text.primary}>
|
||||||
|
<Text bold>Plan:</Text> {tierName}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -132,8 +132,8 @@ export async function setupUser(
|
|||||||
if (projectId) {
|
if (projectId) {
|
||||||
return {
|
return {
|
||||||
projectId,
|
projectId,
|
||||||
userTier: loadRes.currentTier.id,
|
userTier: loadRes.paidTier?.id ?? loadRes.currentTier.id,
|
||||||
userTierName: loadRes.currentTier.name,
|
userTierName: loadRes.paidTier?.name ?? loadRes.currentTier.name,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,8 +142,8 @@ export async function setupUser(
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
projectId: loadRes.cloudaicompanionProject,
|
projectId: loadRes.cloudaicompanionProject,
|
||||||
userTier: loadRes.currentTier.id,
|
userTier: loadRes.paidTier?.id ?? loadRes.currentTier.id,
|
||||||
userTierName: loadRes.currentTier.name,
|
userTierName: loadRes.paidTier?.name ?? loadRes.currentTier.name,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ export interface LoadCodeAssistResponse {
|
|||||||
allowedTiers?: GeminiUserTier[] | null;
|
allowedTiers?: GeminiUserTier[] | null;
|
||||||
ineligibleTiers?: IneligibleTier[] | null;
|
ineligibleTiers?: IneligibleTier[] | null;
|
||||||
cloudaicompanionProject?: string | null;
|
cloudaicompanionProject?: string | null;
|
||||||
|
paidTier?: GeminiUserTier | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -109,13 +110,17 @@ export enum IneligibleTierReasonCode {
|
|||||||
/**
|
/**
|
||||||
* UserTierId represents IDs returned from the Cloud Code Private API representing a user's tier
|
* UserTierId represents IDs returned from the Cloud Code Private API representing a user's tier
|
||||||
*
|
*
|
||||||
* //depot/google3/cloud/developer_experience/cloudcode/pa/service/usertier.go;l=16
|
* http://google3/cloud/developer_experience/codeassist/shared/usertier/tiers.go
|
||||||
|
* This is a subset of all available tiers. Since the source list is frequently updated,
|
||||||
|
* only add a tierId here if specific client-side handling is required.
|
||||||
*/
|
*/
|
||||||
export enum UserTierId {
|
export const UserTierId = {
|
||||||
FREE = 'free-tier',
|
FREE: 'free-tier',
|
||||||
LEGACY = 'legacy-tier',
|
LEGACY: 'legacy-tier',
|
||||||
STANDARD = 'standard-tier',
|
STANDARD: 'standard-tier',
|
||||||
}
|
} as const;
|
||||||
|
|
||||||
|
export type UserTierId = (typeof UserTierId)[keyof typeof UserTierId] | string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PrivacyNotice reflects the structure received from the CodeAssist in regards to a tier
|
* PrivacyNotice reflects the structure received from the CodeAssist in regards to a tier
|
||||||
|
|||||||
Reference in New Issue
Block a user