Skip to content

Commit

Permalink
♻️ refactor: FileItem changed to ImageFileItem
Browse files Browse the repository at this point in the history
  • Loading branch information
mushan0x0 committed Dec 15, 2023
1 parent 7239089 commit d62bafa
Show file tree
Hide file tree
Showing 7 changed files with 230 additions and 15 deletions.
4 changes: 2 additions & 2 deletions src/components/FileList/EditableFileList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useResponsive } from 'antd-style';
import { memo } from 'react';
import { Flexbox } from 'react-layout-kit';

import FileItem from '@/components/FileList/FileItem';
import ImageFileItem from '@/components/FileList/ImageFileItem';

interface EditableFileListProps {
alwaysShowClose?: boolean;
Expand All @@ -24,7 +24,7 @@ const EditableFileList = memo<EditableFileListProps>(
>
<ImageGallery>
{items.map((i) => (
<FileItem alwaysShowClose={alwaysShowClose} editable={editable} id={i} key={i} />
<ImageFileItem alwaysShowClose={alwaysShowClose} editable={editable} id={i} key={i} />
))}
</ImageGallery>
</Flexbox>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ interface FileItemProps {
onClick?: () => void;
style?: CSSProperties;
}
const FileItem = memo<FileItemProps>(({ editable, id, alwaysShowClose }) => {
const [useFetchFile, removeFile] = useFileStore((s) => [s.useFetchFile, s.removeFile]);
const ImageFileItem = memo<FileItemProps>(({ editable, id, alwaysShowClose }) => {
const [useFetchImageFile, removeFile] = useFileStore((s) => [s.useFetchImageFile, s.removeFile]);
const IMAGE_SIZE = editable ? MIN_IMAGE_SIZE : '100%';
const { data, isLoading } = useFetchFile(id);
const { data, isLoading } = useFetchImageFile(id);
const { styles, cx } = useStyles();

const handleRemoveFile = useCallback(
Expand Down Expand Up @@ -72,4 +72,4 @@ const FileItem = memo<FileItemProps>(({ editable, id, alwaysShowClose }) => {
);
});

export default FileItem;
export default ImageFileItem;
6 changes: 3 additions & 3 deletions src/components/FileList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Flexbox } from 'react-layout-kit';
import { MAX_SIZE_DESKTOP, MAX_SIZE_MOBILE } from '@/components/FileList/style';

import FileGrid from './FileGrid';
import FileItem from './FileItem';
import ImageFileItem from './ImageFileItem';

interface FileListProps {
items: string[];
Expand Down Expand Up @@ -44,13 +44,13 @@ const FileList = memo<FileListProps>(({ items }) => {
<Flexbox gap={gap}>
<FileGrid col={firstRow.length} gap={gap} max={max}>
{firstRow.map((i) => (
<FileItem id={i} key={i} />
<ImageFileItem id={i} key={i} />
))}
</FileGrid>
{lastRow.length > 0 && (
<FileGrid col={lastRow.length > 2 ? 3 : lastRow.length} gap={gap} max={max}>
{lastRow.map((i) => (
<FileItem id={i} key={i} />
<ImageFileItem id={i} key={i} />
))}
</FileGrid>
)}
Expand Down
24 changes: 18 additions & 6 deletions src/services/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,26 +26,38 @@ class FileService {

// arrayBuffer to url
const url = URL.createObjectURL(new Blob([item.data]));
const img = new Image();
const base64 = Buffer.from(item.data).toString('base64');

return {
base64Url: `data:${item.fileType};base64,${base64}`,
fileType: item.fileType,
name: item.name,
saveMode: 'local',
url,
};
}

async getImageFile(id: string): Promise<FilePreview> {
const file = await this.getFile(id);

// 加载图片
img.src = url;
const img = new Image();
img.src = file.url;
await (() =>
new Promise((resolve) => {
img.addEventListener('load', resolve);
}))();

// 压缩图片
const fileType = 'image/webp';
const base64Url = compressImage({
img,
type: fileType,
});

return {
...file,
base64Url,
fileType,
name: item.name,
saveMode: 'local',
url,
};
}

Check warning on line 62 in src/services/file.ts

View check run for this annotation

Codecov / codecov/patch

src/services/file.ts#L41-L62

Added lines #L41 - L62 were not covered by tests
}
Expand Down
35 changes: 35 additions & 0 deletions src/store/file/slices/images/action.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ vi.mock('@/services/file', () => ({
removeFile: vi.fn(),
uploadFile: vi.fn(),
getFile: vi.fn(),
getImageFile: vi.fn(),
},
}));

Expand Down Expand Up @@ -117,6 +118,40 @@ describe('useFileStore:images', () => {
// Instead, we would need to use a test renderer that can work with hooks, like @testing-library/react
});

// Test for useFetchImageFile
it('useFetchImageFile should call useSWR and update the store', async () => {
const fileId = 'test-id';
const fileData = {
id: fileId,
name: 'test',
url: 'blob:test',
fileType: 'image/png',
base64Url: '',
saveMode: 'local',
};

// Mock the fileService.getFile to resolve with fileData
(fileService.getImageFile as Mock).mockResolvedValue(fileData);

// Mock useSWR to call the fetcher function immediately
const useSWRMock = vi.mocked(useSWR);
useSWRMock.mockImplementation(((key: string, fetcher: any) => {
const data = fetcher(key);
return { data, error: undefined, isValidating: false, mutate: vi.fn() };
}) as any);

const { result } = renderHook(() => useStore().useFetchImageFile(fileId));

await act(async () => {
await result.current.data;
});

expect(fileService.getImageFile).toHaveBeenCalledWith(fileId);

// Since we are not rendering a component with the hook, we cannot test the state update here
// Instead, we would need to use a test renderer that can work with hooks, like @testing-library/react
});

describe('uploadFile', () => {
it('uploadFile should handle errors', async () => {
const { result } = renderHook(() => useStore());
Expand Down
10 changes: 10 additions & 0 deletions src/store/file/slices/images/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export interface FileAction {
uploadFile: (file: File) => Promise<void>;

useFetchFile: (id: string) => SWRResponse<FilePreview>;
useFetchImageFile: (id: string) => SWRResponse<FilePreview>;
}

export const createFileSlice: StateCreator<
Expand Down Expand Up @@ -78,6 +79,15 @@ export const createFileSlice: StateCreator<
useSWR(id, async (id) => {
const item = await fileService.getFile(id);

// TODO: 需要改为 get().setFileMapItem
get().setImageMapItem(id, item);

return item;
}),
useFetchImageFile: (id) =>
useSWR(id, async (id) => {
const item = await fileService.getImageFile(id);

get().setImageMapItem(id, item);

return item;
Expand Down
158 changes: 158 additions & 0 deletions src/store/global/selectors/__snapshots__/settings.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,161 @@ exports[`settingsSelectors > currentSettings > should merge DEFAULT_SETTINGS and
},
}
`;

exports[`settingsSelectors CUSTOM_MODELS custom deletion, addition, and renaming of models 1`] = `
[
{
"displayName": "llama",
"name": "llama",
},
{
"displayName": "claude-2",
"name": "claude-2",
},
{
"displayName": "gpt-4-32k",
"name": "gpt-4-1106-preview",
},
]
`;

exports[`settingsSelectors CUSTOM_MODELS duplicate naming model 1`] = `
[
{
"displayName": "gpt-3.5-turbo",
"name": "gpt-3.5-turbo",
},
{
"displayName": "gpt-3.5-turbo-1106",
"name": "gpt-3.5-turbo-1106",
},
{
"displayName": "gpt-3.5-turbo-16k",
"name": "gpt-3.5-turbo-16k",
},
{
"displayName": "gpt-4",
"name": "gpt-4",
},
{
"displayName": "gpt-4-32k",
"name": "gpt-4-32k",
},
{
"displayName": "gpt-4-vision-preview",
"name": "gpt-4-vision-preview",
},
{
"displayName": "gpt-4-32k",
"name": "gpt-4-1106-preview",
},
]
`;

exports[`settingsSelectors CUSTOM_MODELS only add the model 1`] = `
[
{
"displayName": "gpt-3.5-turbo",
"name": "gpt-3.5-turbo",
},
{
"displayName": "gpt-3.5-turbo-1106",
"name": "gpt-3.5-turbo-1106",
},
{
"displayName": "gpt-3.5-turbo-16k",
"name": "gpt-3.5-turbo-16k",
},
{
"displayName": "gpt-4",
"name": "gpt-4",
},
{
"displayName": "gpt-4-32k",
"name": "gpt-4-32k",
},
{
"displayName": "gpt-4-1106-preview",
"name": "gpt-4-1106-preview",
},
{
"displayName": "gpt-4-vision-preview",
"name": "gpt-4-vision-preview",
},
{
"displayName": "model1",
"name": "model1",
},
{
"displayName": "model2",
"name": "model2",
},
{
"displayName": "model3",
"name": "model3",
},
{
"displayName": "model4",
"name": "model4",
},
]
`;

exports[`settingsSelectors currentSettings should merge DEFAULT_SETTINGS and s.settings correctly 1`] = `
{
"avatar": "avatar.jpg",
"defaultAgent": {
"config": {
"autoCreateTopicThreshold": 2,
"displayMode": "chat",
"enableAutoCreateTopic": true,
"historyCount": 1,
"model": "gpt-3.5-turbo",
"params": {
"frequency_penalty": 0,
"presence_penalty": 0,
"temperature": 0.6,
"top_p": 1,
},
"plugins": [],
"systemRole": "",
"tts": {
"showAllLocaleVoice": false,
"sttLocale": "auto",
"ttsService": "openai",
"voice": {
"openai": "alloy",
},
},
},
"meta": {
"avatar": "Default Agent",
"description": "Default agent for testing",
},
},
"fontSize": 14,
"language": "en-US",
"languageModel": {
"openAI": {
"OPENAI_API_KEY": "openai-api-key",
"customModelName": undefined,
"endpoint": "https://openai-endpoint.com",
"models": [
"gpt-3.5-turbo",
],
},
},
"neutralColor": "sand",
"password": "password123",
"primaryColor": "blue",
"themeMode": "light",
"tts": {
"openAI": {
"sttModel": "whisper-1",
"ttsModel": "tts-1",
},
"sttAutoStop": true,
"sttServer": "openai",
},
}
`;

0 comments on commit d62bafa

Please sign in to comment.