TidGi-Desktop/src/components/TokenForm/index.tsx
lin onetwo b76fc17794
Chore/upgrade (#646)
* docs: deps

* Update dependencies and type usage for AI features

Upgraded multiple dependencies in package.json and pnpm-lock.yaml, including @ai-sdk, @mui, react, and others for improved compatibility and performance. Changed type usage from CoreMessage to ModelMessage in mockOpenAI.test.ts to align with updated ai package. No functional changes to application logic.

* feat: i18n

* feat: test oauth login and use PKCE

* fix: use ollama-ai-provider-v2

* test: github and mock oauth2 login

* test: gitea login

* Refactor context menu cleanup and error message

Moved context menu cleanup for OAuth window to a single closed event handler in Authentication service. Simplified error message formatting in ContextService for missing keys.

* lint: AI fix

* Add tsx as a dev dependency and update scripts

Replaced usage of 'pnpm dlx tsx' with direct 'tsx' command in development and test scripts for improved reliability. Added 'tsx' to devDependencies in package.json.
2025-10-23 23:42:06 +08:00

116 lines
4.5 KiB
TypeScript

import { Box, Tab as TabRaw, Tabs as TabsRaw } from '@mui/material';
import { keyframes, styled, Theme } from '@mui/material/styles';
import { SupportedStorageServices } from '@services/types';
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { ListItemText } from '../ListItem';
import { CustomServerTokenForm } from './CustomServerTokenForm';
import { GitTokenForm } from './GitTokenForm';
const Container = styled('div')`
width: 100%;
display: flex;
flex-direction: column;
background-color: ${({ theme }) => theme.palette.background.paper};
`;
const TabPanel = styled(Box)`
padding: 5px 0;
padding-left: 16px;
background-color: ${({ theme }) => theme.palette.background.paper};
`;
const Tabs = styled(TabsRaw)`
background-color: ${({ theme }) => theme.palette.background.paper};
& button {
background-color: ${({ theme }) => theme.palette.background.paper} !important;
}
`;
const TabsContainer = styled('div')`
background-color: ${({ theme }) => theme.palette.background.paper};
color: ${({ theme }) => theme.palette.text.primary};
display: flex;
padding: 15px 0;
flex-direction: row;
& .MuiTabs-root {
min-width: 160px;
}
`;
const backgroundColorShift = ({ theme }: { theme: Theme }) =>
keyframes`
from {background-color: ${theme.palette.background.default};}
to {background-color: ${theme.palette.background.default};}
`;
const Tab = styled(TabRaw)`
background-color: ${({ theme }) => theme.palette.action.active};
color: ${({ theme }) => theme.palette.text.secondary};
animation: ${backgroundColorShift} 5s infinite;
animation-direction: alternate;
animation-timing-function: cubic-bezier(0.4, 0, 1, 1);
`;
interface Props {
storageProvider?: SupportedStorageServices;
storageProviderSetter?: (next: SupportedStorageServices) => void;
}
/**
* Create storage provider's token.
* @returns
*/
export function TokenForm({ storageProvider, storageProviderSetter }: Props): React.JSX.Element {
const { t } = useTranslation();
let [currentTab, currentTabSetter] = useState<SupportedStorageServices>(SupportedStorageServices.github);
// use external controls if provided
if (storageProvider !== undefined && typeof storageProviderSetter === 'function') {
currentTab = storageProvider;
currentTabSetter = storageProviderSetter as unknown as React.Dispatch<React.SetStateAction<SupportedStorageServices>>;
}
// update storageProvider to be an online service, if this Component is opened
useEffect(() => {
if (storageProvider === SupportedStorageServices.local && typeof storageProviderSetter === 'function') {
storageProviderSetter(SupportedStorageServices.github);
}
}, [storageProvider, storageProviderSetter]);
return (
<Container>
<ListItemText primary={t('Preference.Token')} secondary={t('Preference.TokenDescription')} />
<Box sx={{ display: 'flex', width: '100%' }}>
<TabsContainer>
<Tabs
onChange={(_event: React.SyntheticEvent, newValue: SupportedStorageServices) => {
currentTabSetter(newValue);
}}
orientation='vertical'
variant='scrollable'
value={currentTab}
aria-label='Vertical tabs example'
>
<Tab label='GitHub' value={SupportedStorageServices.github} data-testid='github-tab' />
<Tab label='Codeberg' value={SupportedStorageServices.codeberg} data-testid='codeberg-tab' />
<Tab label='Gitea.com' value={SupportedStorageServices.gitea} data-testid='gitea-tab' />
<Tab label='Custom Server' value={SupportedStorageServices.testOAuth} data-testid='custom-server-tab' />
</Tabs>
{currentTab === SupportedStorageServices.github && (
<TabPanel>
<GitTokenForm storageService={SupportedStorageServices.github} />
</TabPanel>
)}
{currentTab === SupportedStorageServices.codeberg && (
<TabPanel>
<GitTokenForm storageService={SupportedStorageServices.codeberg} />
</TabPanel>
)}
{currentTab === SupportedStorageServices.gitea && (
<TabPanel>
<GitTokenForm storageService={SupportedStorageServices.gitea} />
</TabPanel>
)}
{currentTab === SupportedStorageServices.testOAuth && (
<TabPanel>
<CustomServerTokenForm />
</TabPanel>
)}
</TabsContainer>
</Box>
</Container>
);
}