refactor: update file management to use filetype instead of filemetadata

This commit is contained in:
kangfenmao 2024-09-14 16:08:43 +08:00
parent 9ae9fdf392
commit aa427c9911
12 changed files with 45 additions and 52 deletions

View File

@ -1,4 +1,4 @@
import { FileMetadata } from '@types'
import { FileType } from '@types'
import { BrowserWindow, ipcMain, OpenDialogOptions, session, shell } from 'electron'
import Logger from 'electron-log'
import fs from 'fs'
@ -55,7 +55,7 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) {
})
ipcMain.handle('file:select', async (_, options?: OpenDialogOptions) => await fileManager.selectFile(options))
ipcMain.handle('file:upload', async (_, file: FileMetadata) => await fileManager.uploadFile(file))
ipcMain.handle('file:upload', async (_, file: FileType) => await fileManager.uploadFile(file))
ipcMain.handle('file:delete', async (_, fileId: string) => {
await fileManager.deleteFile(fileId)
return { success: true }

View File

@ -1,5 +1,5 @@
import { getFileType } from '@main/utils/file'
import { FileMetadata } from '@types'
import { FileType } from '@types'
import * as crypto from 'crypto'
import { app, dialog, OpenDialogOptions } from 'electron'
import * as fs from 'fs'
@ -30,7 +30,7 @@ class File {
})
}
async findDuplicateFile(filePath: string): Promise<FileMetadata | null> {
async findDuplicateFile(filePath: string): Promise<FileType | null> {
const stats = fs.statSync(filePath)
const fileSize = stats.size
@ -66,7 +66,7 @@ class File {
return null
}
async selectFile(options?: OpenDialogOptions): Promise<FileMetadata[] | null> {
async selectFile(options?: OpenDialogOptions): Promise<FileType[] | null> {
const defaultOptions: OpenDialogOptions = {
properties: ['openFile']
}
@ -100,7 +100,7 @@ class File {
return Promise.all(fileMetadataPromises)
}
async uploadFile(file: FileMetadata): Promise<FileMetadata> {
async uploadFile(file: FileType): Promise<FileType> {
const duplicateFile = await this.findDuplicateFile(file.path)
if (duplicateFile) {
@ -116,7 +116,7 @@ class File {
const stats = await fs.promises.stat(destPath)
const fileType = getFileType(ext)
const fileMetadata: FileMetadata = {
const fileMetadata: FileType = {
id: uuid,
origin_name,
name: uuid + ext,

View File

@ -3,7 +3,7 @@ import logger from 'electron-log'
import { writeFile } from 'fs'
import { readFile } from 'fs/promises'
import { FileType } from '../../renderer/src/types'
import { FileTypes } from '../../renderer/src/types'
export async function saveFile(
_: Electron.IpcMainInvokeEvent,
@ -56,16 +56,16 @@ export async function openFile(
}
}
export function getFileType(ext: string): FileType {
export function getFileType(ext: string): FileTypes {
const imageExts = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp']
const videoExts = ['.mp4', '.avi', '.mov', '.wmv', '.flv', '.mkv']
const audioExts = ['.mp3', '.wav', '.ogg', '.flac', '.aac']
const documentExts = ['.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.txt']
ext = ext.toLowerCase()
if (imageExts.includes(ext)) return FileType.IMAGE
if (videoExts.includes(ext)) return FileType.VIDEO
if (audioExts.includes(ext)) return FileType.AUDIO
if (documentExts.includes(ext)) return FileType.DOCUMENT
return FileType.OTHER
if (imageExts.includes(ext)) return FileTypes.IMAGE
if (videoExts.includes(ext)) return FileTypes.VIDEO
if (audioExts.includes(ext)) return FileTypes.AUDIO
if (documentExts.includes(ext)) return FileTypes.DOCUMENT
return FileTypes.OTHER
}

View File

@ -1,5 +1,5 @@
import { ElectronAPI } from '@electron-toolkit/preload'
import { FileMetadata } from '@renderer/types'
import { FileType } from '@renderer/types'
import type { OpenDialogOptions } from 'electron'
declare global {
@ -22,8 +22,8 @@ declare global {
compress: (text: string) => Promise<Buffer>
decompress: (text: Buffer) => Promise<string>
file: {
select: (options?: OpenDialogOptions) => Promise<FileMetadata[] | null>
upload: (file: FileMetadata) => Promise<FileMetadata>
select: (options?: OpenDialogOptions) => Promise<FileType[] | null>
upload: (file: FileType) => Promise<FileType>
delete: (fileId: string) => Promise<{ success: boolean }>
}
image: {

View File

@ -1,9 +1,9 @@
import { FileMetadata } from '@renderer/types'
import { FileType } from '@renderer/types'
import { Dexie, type EntityTable } from 'dexie'
// Database declaration (move this to its own module also)
export const db = new Dexie('CherryStudio') as Dexie & {
files: EntityTable<FileMetadata, 'id'>
files: EntityTable<FileType, 'id'>
}
db.version(1).stores({

View File

@ -11,8 +11,7 @@ import { useSettings } from './useSettings'
export function useAppInit() {
const dispatch = useAppDispatch()
const { proxyUrl } = useSettings()
const { language } = useSettings()
const { proxyUrl, language } = useSettings()
const { setDefaultModel, setTopicNamingModel, setTranslateModel } = useDefaultModel()
useEffect(() => {

View File

@ -1,9 +1,8 @@
import { Navbar, NavbarCenter } from '@renderer/components/app/Navbar'
import { VStack } from '@renderer/components/Layout'
import db from '@renderer/databases'
import FileManager from '@renderer/services/file'
import { FileMetadata } from '@renderer/types'
import { Button, Image, Table } from 'antd'
import { FileType } from '@renderer/types'
import { Image, Table } from 'antd'
import dayjs from 'dayjs'
import { useLiveQuery } from 'dexie-react-hooks'
import { FC } from 'react'
@ -12,9 +11,10 @@ import styled from 'styled-components'
const FilesPage: FC = () => {
const { t } = useTranslation()
const files = useLiveQuery<FileMetadata[]>(() => db.files.toArray())
const files = useLiveQuery<FileType[]>(() => db.files.toArray())
const dataSource = files?.map((file) => ({
key: file.id,
file: <Image src={'file://' + file.path} preview={false} style={{ maxHeight: '40px' }} />,
name: <a href={'file://' + file.path}>{file.name}</a>,
size: `${(file.size / 1024 / 1024).toFixed(2)} MB`,
@ -25,7 +25,8 @@ const FilesPage: FC = () => {
{
title: t('files.file'),
dataIndex: 'file',
key: 'file'
key: 'file',
width: '300px'
},
{
title: t('files.name'),
@ -52,13 +53,6 @@ const FilesPage: FC = () => {
<NavbarCenter style={{ borderRight: 'none' }}>{t('files.title')}</NavbarCenter>
</Navbar>
<ContentContainer>
<Button
onClick={async () => {
const files = await FileManager.selectFiles()
files && FileManager.uploadFiles(files)
}}>
Upload
</Button>
<VStack style={{ flex: 1 }}>
<Table dataSource={dataSource} columns={columns} style={{ width: '100%', height: '100%' }} size="small" />
</VStack>

View File

@ -1,14 +1,14 @@
import { PaperClipOutlined } from '@ant-design/icons'
import { isVisionModel } from '@renderer/config/models'
import { FileMetadata, Model } from '@renderer/types'
import { FileType, Model } from '@renderer/types'
import { Tooltip } from 'antd'
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
interface Props {
model: Model
files: FileMetadata[]
setFiles: (files: FileMetadata[]) => void
files: FileType[]
setFiles: (files: FileType[]) => void
ToolbarButton: any
}

View File

@ -1,12 +1,12 @@
import { FileMetadata } from '@renderer/types'
import { FileType } from '@renderer/types'
import { Upload } from 'antd'
import { isEmpty } from 'lodash'
import { FC } from 'react'
import styled from 'styled-components'
interface Props {
files: FileMetadata[]
setFiles: (files: FileMetadata[]) => void
files: FileType[]
setFiles: (files: FileType[]) => void
}
const AttachmentPreview: FC<Props> = ({ files, setFiles }) => {

View File

@ -1,11 +1,11 @@
import {
ClearOutlined,
ControlOutlined,
FormOutlined,
FullscreenExitOutlined,
FullscreenOutlined,
HistoryOutlined,
PauseCircleOutlined,
PlusCircleOutlined,
QuestionCircleOutlined
} from '@ant-design/icons'
import { useAssistant } from '@renderer/hooks/useAssistant'
@ -17,7 +17,7 @@ import FileManager from '@renderer/services/file'
import { estimateInputTokenCount } from '@renderer/services/messages'
import store, { useAppDispatch, useAppSelector } from '@renderer/store'
import { setGenerating, setSearching } from '@renderer/store/runtime'
import { Assistant, FileMetadata, Message, Topic } from '@renderer/types'
import { Assistant, FileType, Message, Topic } from '@renderer/types'
import { delay, uuid } from '@renderer/utils'
import { Button, Popconfirm, Tooltip } from 'antd'
import TextArea, { TextAreaRef } from 'antd/es/input/TextArea'
@ -49,7 +49,7 @@ const Inputbar: FC<Props> = ({ assistant, setActiveTopic }) => {
const [contextCount, setContextCount] = useState(0)
const generating = useAppSelector((state) => state.runtime.generating)
const textareaRef = useRef<TextAreaRef>(null)
const [files, setFiles] = useState<FileMetadata[]>([])
const [files, setFiles] = useState<FileType[]>([])
const { t } = useTranslation()
const containerRef = useRef(null)
const { showTopics, toggleShowTopics } = useShowTopics()
@ -229,7 +229,7 @@ const Inputbar: FC<Props> = ({ assistant, setActiveTopic }) => {
<ToolbarMenu>
<Tooltip placement="top" title={t('chat.input.new_topic')} arrow>
<ToolbarButton type="text" onClick={addNewTopic}>
<PlusCircleOutlined />
<FormOutlined />
</ToolbarButton>
</Tooltip>
<Tooltip placement="top" title={t('chat.input.clear')} arrow>

View File

@ -1,13 +1,13 @@
import db from '@renderer/databases'
import { FileMetadata } from '@renderer/types'
import { FileType } from '@renderer/types'
class FileManager {
static async selectFiles(options?: Electron.OpenDialogOptions): Promise<FileMetadata[] | null> {
static async selectFiles(options?: Electron.OpenDialogOptions): Promise<FileType[] | null> {
const files = await window.api.file.select(options)
return files
}
static async uploadFile(file: FileMetadata): Promise<FileMetadata> {
static async uploadFile(file: FileType): Promise<FileType> {
const uploadFile = await window.api.file.upload(file)
const fileRecord = await db.files.get(uploadFile.id)
@ -21,11 +21,11 @@ class FileManager {
return uploadFile
}
static async uploadFiles(files: FileMetadata[]): Promise<FileMetadata[]> {
static async uploadFiles(files: FileType[]): Promise<FileType[]> {
return Promise.all(files.map((file) => this.uploadFile(file)))
}
static async getFile(id: string): Promise<FileMetadata | undefined> {
static async getFile(id: string): Promise<FileType | undefined> {
return db.files.get(id)
}
@ -49,7 +49,7 @@ class FileManager {
await Promise.all(ids.map((id) => this.deleteFile(id)))
}
static async allFiles(): Promise<FileMetadata[]> {
static async allFiles(): Promise<FileType[]> {
return db.files.toArray()
}
}

View File

@ -27,7 +27,7 @@ export type Message = {
createdAt: string
status: 'sending' | 'pending' | 'success' | 'paused' | 'error'
modelId?: string
files?: FileMetadata[]
files?: FileType[]
images?: string[]
usage?: OpenAI.Completions.CompletionUsage
type?: 'text' | '@' | 'clear'
@ -87,7 +87,7 @@ export type MinAppType = {
url: string
}
export interface FileMetadata {
export interface FileType {
id: string
name: string
origin_name: string
@ -99,7 +99,7 @@ export interface FileMetadata {
count: number
}
export enum FileType {
export enum FileTypes {
IMAGE = 'image',
VIDEO = 'video',
AUDIO = 'audio',