refactor: update file management to use filetype instead of filemetadata
This commit is contained in:
parent
9ae9fdf392
commit
aa427c9911
@ -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 }
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
6
src/preload/index.d.ts
vendored
6
src/preload/index.d.ts
vendored
@ -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: {
|
||||
|
||||
@ -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({
|
||||
|
||||
@ -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(() => {
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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 }) => {
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user