feat: add translations and file management features
This commit is contained in:
parent
dba1f76db7
commit
4cbdd563e8
@ -184,7 +184,12 @@
|
||||
"open": "Open",
|
||||
"size": "Size",
|
||||
"text": "Text",
|
||||
"title": "Files"
|
||||
"title": "Files",
|
||||
"edit": "Edit",
|
||||
"delete": "Delete",
|
||||
"delete.title": "Delete File",
|
||||
"delete.content": "Deleting a file will delete its reference from all messages. Are you sure you want to delete this file?",
|
||||
"delete.paintings.warning": "Image contains this file, deletion is not possible"
|
||||
},
|
||||
"history": {
|
||||
"continue_chat": "Continue Chatting",
|
||||
|
||||
@ -184,7 +184,12 @@
|
||||
"open": "開く",
|
||||
"size": "サイズ",
|
||||
"text": "テキスト",
|
||||
"title": "ファイル"
|
||||
"title": "ファイル",
|
||||
"edit": "編集",
|
||||
"delete": "削除",
|
||||
"delete.title": "ファイルを削除",
|
||||
"delete.content": "ファイルを削除すると、ファイルがすべてのメッセージで参照されることを削除します。このファイルを削除してもよろしいですか?",
|
||||
"delete.paintings.warning": "画像に含まれているため、削除できません"
|
||||
},
|
||||
"history": {
|
||||
"continue_chat": "チャットを続ける",
|
||||
|
||||
@ -184,7 +184,12 @@
|
||||
"open": "Открыть",
|
||||
"size": "Размер",
|
||||
"text": "Текст",
|
||||
"title": "Файлы"
|
||||
"title": "Файлы",
|
||||
"edit": "Редактировать",
|
||||
"delete": "Удалить",
|
||||
"delete.title": "Удалить файл",
|
||||
"delete.content": "Удаление файла удалит его из всех сообщений, вы уверены, что хотите удалить этот файл?",
|
||||
"delete.paintings.warning": "В изображениях содержится этот файл, удаление невозможно"
|
||||
},
|
||||
"history": {
|
||||
"continue_chat": "Продолжить чат",
|
||||
|
||||
@ -185,7 +185,12 @@
|
||||
"open": "打开",
|
||||
"size": "大小",
|
||||
"text": "文本",
|
||||
"title": "文件"
|
||||
"title": "文件",
|
||||
"edit": "编辑",
|
||||
"delete": "删除",
|
||||
"delete.title": "删除文件",
|
||||
"delete.content": "删除文件会删除文件在所有消息中的引用,确定要删除此文件吗?",
|
||||
"delete.paintings.warning": "绘图中包含该图片,暂时无法删除"
|
||||
},
|
||||
"history": {
|
||||
"continue_chat": "继续聊天",
|
||||
|
||||
@ -184,7 +184,12 @@
|
||||
"open": "打開",
|
||||
"size": "大小",
|
||||
"text": "文本",
|
||||
"title": "檔案"
|
||||
"title": "檔案",
|
||||
"edit": "編輯",
|
||||
"delete": "刪除",
|
||||
"delete.title": "刪除檔案",
|
||||
"delete.content": "刪除檔案會刪除檔案在所有消息中的引用,確定要刪除此檔案嗎?",
|
||||
"delete.paintings.warning": "繪圖中包含該圖片,暫時無法刪除"
|
||||
},
|
||||
"history": {
|
||||
"continue_chat": "繼續聊天",
|
||||
|
||||
@ -1,11 +1,21 @@
|
||||
import { FileImageOutlined, FilePdfOutlined, FileTextOutlined } from '@ant-design/icons'
|
||||
import {
|
||||
DeleteOutlined,
|
||||
EditOutlined,
|
||||
EllipsisOutlined,
|
||||
FileImageOutlined,
|
||||
FilePdfOutlined,
|
||||
FileTextOutlined
|
||||
} from '@ant-design/icons'
|
||||
import { Navbar, NavbarCenter } from '@renderer/components/app/Navbar'
|
||||
import TextEditPopup from '@renderer/components/Popups/TextEditPopup'
|
||||
import Scrollbar from '@renderer/components/Scrollbar'
|
||||
import db from '@renderer/databases'
|
||||
import FileManager from '@renderer/services/FileManager'
|
||||
import store from '@renderer/store'
|
||||
import { FileType, FileTypes } from '@renderer/types'
|
||||
import { formatFileSize } from '@renderer/utils'
|
||||
import { Col, Image, Menu, Row, Spin, Table } from 'antd'
|
||||
import type { MenuProps } from 'antd'
|
||||
import { Button, Col, Dropdown, Image, Menu, Row, Spin, Table } from 'antd'
|
||||
import dayjs from 'dayjs'
|
||||
import { useLiveQuery } from 'dexie-react-hooks'
|
||||
import { FC, useState } from 'react'
|
||||
@ -23,14 +33,88 @@ const FilesPage: FC = () => {
|
||||
return db.files.where('type').equals(fileType).sortBy('count')
|
||||
}, [fileType])
|
||||
|
||||
const handleDelete = async (fileId: string) => {
|
||||
const file = await FileManager.getFile(fileId)
|
||||
|
||||
const paintings = await store.getState().paintings.paintings
|
||||
const paintingsFiles = paintings.flatMap((p) => p.files)
|
||||
|
||||
if (paintingsFiles.some((p) => p.id === fileId)) {
|
||||
window.modal.warning({ content: t('files.delete.paintings.warning'), centered: true })
|
||||
return
|
||||
}
|
||||
|
||||
if (file) {
|
||||
await FileManager.deleteFile(fileId, true)
|
||||
}
|
||||
|
||||
const topics = await db.topics
|
||||
.filter((topic) => topic.messages.some((message) => message.files?.some((f) => f.id === fileId)))
|
||||
.toArray()
|
||||
|
||||
if (topics.length > 0) {
|
||||
for (const topic of topics) {
|
||||
const updatedMessages = topic.messages.map((message) => ({
|
||||
...message,
|
||||
files: message.files?.filter((f) => f.id !== fileId)
|
||||
}))
|
||||
await db.topics.update(topic.id, { messages: updatedMessages })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleRename = async (fileId: string) => {
|
||||
const file = await FileManager.getFile(fileId)
|
||||
if (file) {
|
||||
const newName = await TextEditPopup.show({ text: file.origin_name })
|
||||
if (newName) {
|
||||
FileManager.updateFile({ ...file, origin_name: newName })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const getActionMenu = (fileId: string): MenuProps['items'] => [
|
||||
{
|
||||
key: 'rename',
|
||||
icon: <EditOutlined />,
|
||||
label: t('files.edit'),
|
||||
onClick: () => handleRename(fileId)
|
||||
},
|
||||
{
|
||||
key: 'delete',
|
||||
icon: <DeleteOutlined />,
|
||||
label: t('files.delete'),
|
||||
danger: true,
|
||||
onClick: () => {
|
||||
window.modal.confirm({
|
||||
title: t('files.delete.title'),
|
||||
content: t('files.delete.content'),
|
||||
centered: true,
|
||||
okButtonProps: { danger: true },
|
||||
onOk: () => handleDelete(fileId)
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const dataSource = files?.map((file) => {
|
||||
return {
|
||||
key: file.id,
|
||||
file: <FileNameText className="text-nowrap">{file.origin_name}</FileNameText>,
|
||||
file: (
|
||||
<FileNameText className="text-nowrap" onClick={() => window.api.file.openPath(file.path)}>
|
||||
{file.origin_name}
|
||||
</FileNameText>
|
||||
),
|
||||
size: formatFileSize(file),
|
||||
size_bytes: file.size,
|
||||
count: file.count,
|
||||
created_at: dayjs(file.created_at).format('MM-DD HH:mm'),
|
||||
actions: <a href={'file://' + FileManager.getSafePath(file)}>{t('files.open')}</a>
|
||||
created_at_unix: dayjs(file.created_at).unix(),
|
||||
actions: (
|
||||
<Dropdown menu={{ items: getActionMenu(file.id) }} trigger={['click']}>
|
||||
<Button type="text" size="small" icon={<EllipsisOutlined />} />
|
||||
</Dropdown>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
@ -45,19 +129,25 @@ const FilesPage: FC = () => {
|
||||
title: t('files.size'),
|
||||
dataIndex: 'size',
|
||||
key: 'size',
|
||||
width: '80px'
|
||||
width: '80px',
|
||||
sorter: (a: { size_bytes: number }, b: { size_bytes: number }) => b.size_bytes - a.size_bytes,
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: t('files.count'),
|
||||
dataIndex: 'count',
|
||||
key: 'count',
|
||||
width: '60px'
|
||||
width: '60px',
|
||||
sorter: (a: { count: number }, b: { count: number }) => b.count - a.count,
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: t('files.created_at'),
|
||||
dataIndex: 'created_at',
|
||||
key: 'created_at',
|
||||
width: '120px'
|
||||
width: '120px',
|
||||
align: 'center',
|
||||
sorter: (a: { created_at_unix: number }, b: { created_at_unix: number }) => b.created_at_unix - a.created_at_unix
|
||||
},
|
||||
{
|
||||
title: t('files.actions'),
|
||||
@ -149,6 +239,7 @@ const TableContainer = styled(Scrollbar)`
|
||||
const FileNameText = styled.div`
|
||||
font-size: 14px;
|
||||
color: var(--color-text);
|
||||
cursor: pointer;
|
||||
`
|
||||
|
||||
const ImageWrapper = styled.div`
|
||||
|
||||
@ -57,20 +57,29 @@ class FileManager {
|
||||
return file
|
||||
}
|
||||
|
||||
static async deleteFile(id: string): Promise<void> {
|
||||
static async deleteFile(id: string, force: boolean = false): Promise<void> {
|
||||
const file = await this.getFile(id)
|
||||
|
||||
console.debug('[FileManager] Deleting file:', file)
|
||||
|
||||
if (!file) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!force) {
|
||||
if (file.count > 1) {
|
||||
await db.files.update(id, { ...file, count: file.count - 1 })
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
await db.files.delete(id)
|
||||
|
||||
try {
|
||||
await window.api.file.delete(id + file.ext)
|
||||
} catch (error) {
|
||||
console.error('[FileManager] Failed to delete file:', error)
|
||||
}
|
||||
}
|
||||
|
||||
static async deleteFiles(files: FileType[]): Promise<void> {
|
||||
@ -93,6 +102,14 @@ class FileManager {
|
||||
const filesPath = store.getState().runtime.filesPath
|
||||
return 'file://' + filesPath + '/' + file.name
|
||||
}
|
||||
|
||||
static async updateFile(file: FileType) {
|
||||
if (!file.origin_name.includes(file.ext)) {
|
||||
file.origin_name = file.origin_name + file.ext
|
||||
}
|
||||
|
||||
await db.files.update(file.id, file)
|
||||
}
|
||||
}
|
||||
|
||||
export default FileManager
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user