From d513a50e27c0e1a332733c0ea08aae2fe010bd8c Mon Sep 17 00:00:00 2001 From: lencx Date: Wed, 21 Dec 2022 14:00:42 +0800 Subject: [PATCH] chore: sync --- src-tauri/tauri.conf.json | 4 +++- src/hooks/useData.ts | 19 +++++++++++++-- src/hooks/useTable.tsx | 37 ++++++++++++++++++++++++++++ src/main.scss | 5 ++++ src/routes.tsx | 10 ++++++++ src/view/LanguageModel/index.tsx | 10 ++------ src/view/SyncMore/config.tsx | 22 +++++++++++++++++ src/view/SyncMore/index.scss | 28 ++++++++++++++++++++++ src/view/SyncMore/index.tsx | 20 ++++++++++++++++ src/view/SyncPrompts/index.scss | 13 ++++++++++ src/view/SyncPrompts/index.tsx | 41 ++++++++++++++++++++++---------- 11 files changed, 186 insertions(+), 23 deletions(-) create mode 100644 src/hooks/useTable.tsx create mode 100644 src/view/SyncMore/config.tsx create mode 100644 src/view/SyncMore/index.scss create mode 100644 src/view/SyncMore/index.tsx diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 9902093..0d1f35b 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -13,8 +13,10 @@ "allowlist": { "all": true, "http": { + "all": true, "scope": [ - "https://raw.githubusercontent.com/*" + "https://**", + "http://**" ] }, "fs": { diff --git a/src/hooks/useData.ts b/src/hooks/useData.ts index 38c7630..8722399 100644 --- a/src/hooks/useData.ts +++ b/src/hooks/useData.ts @@ -1,7 +1,7 @@ import { useState, useEffect } from 'react'; import { v4 } from 'uuid'; -const safeKey = Symbol('chat-id'); +export const safeKey = Symbol('chat-id'); export default function useData(oData: any[]) { const [opData, setData] = useState([]); @@ -35,5 +35,20 @@ export default function useData(oData: any[]) { return nData; }; - return { opSafeKey: safeKey, opInit, opReplace, opAdd, opRemove, opData }; + const opReplaceItems = (ids: string[], data: any) => { + const nData = [...opData]; + let count = 0; + for (let i = 0; i < nData.length; i++) { + const v = nData[i]; + if (ids.includes(v[safeKey])) { + count++; + nData[i] = { ...v, ...data }; + } + if (count === ids.length) break; + } + setData(nData); + return nData; + }; + + return { opSafeKey: safeKey, opInit, opReplace, opAdd, opRemove, opData, opReplaceItems }; } \ No newline at end of file diff --git a/src/hooks/useTable.tsx b/src/hooks/useTable.tsx new file mode 100644 index 0000000..741ab8b --- /dev/null +++ b/src/hooks/useTable.tsx @@ -0,0 +1,37 @@ +import React, { useState } from 'react'; +import { Table } from 'antd'; +import type { TableRowSelection } from 'antd/es/table/interface'; + +import { safeKey } from '@/hooks/useData'; + +export default function useTableRowSelection() { + const [selectedRowKeys, setSelectedRowKeys] = useState([]); + const [selectedRowIDs, setSelectedRowIDs] = useState([]); + + const onSelectChange = (newSelectedRowKeys: React.Key[], selectedRows: Record) => { + const keys = selectedRows.map((i: any) => i[safeKey]); + setSelectedRowIDs(keys); + setSelectedRowKeys(newSelectedRowKeys); + }; + + const rowSelection: TableRowSelection> = { + selectedRowKeys, + onChange: onSelectChange, + selections: [ + Table.SELECTION_ALL, + Table.SELECTION_INVERT, + Table.SELECTION_NONE, + ], + }; + + return { rowSelection, selectedRowIDs }; +} + +export const TABLE_PAGINATION = { + hideOnSinglePage: true, + showSizeChanger: true, + showQuickJumper: true, + defaultPageSize: 5, + pageSizeOptions: [5, 10, 15, 20], + showTotal: (total: number) => Total {total} items, +}; \ No newline at end of file diff --git a/src/main.scss b/src/main.scss index 96312fb..be0111b 100644 --- a/src/main.scss +++ b/src/main.scss @@ -17,4 +17,9 @@ html, body { padding: 0; margin: 0; +} + +.ant-table-selection-column { + width: 50px !important; + min-width: 50px !important; } \ No newline at end of file diff --git a/src/routes.tsx b/src/routes.tsx index fd5e593..604d5f0 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -3,6 +3,7 @@ import { DesktopOutlined, BulbOutlined, SyncOutlined, + FileSyncOutlined, UserOutlined, } from '@ant-design/icons'; import type { MenuProps } from 'antd'; @@ -10,6 +11,7 @@ import type { MenuProps } from 'antd'; import General from '@view/General'; import LanguageModel from '@/view/LanguageModel'; import SyncPrompts from '@/view/SyncPrompts'; +import SyncMore from '@/view/SyncMore'; export type ChatRouteMetaObject = { label: string; @@ -55,6 +57,14 @@ export const routes: Array = [ icon: , }, }, + { + path: 'sync-more', + element: , + meta: { + label: 'Sync More', + icon: , + }, + }, ] }, ]; diff --git a/src/view/LanguageModel/index.tsx b/src/view/LanguageModel/index.tsx index 7c50279..e25a0d0 100644 --- a/src/view/LanguageModel/index.tsx +++ b/src/view/LanguageModel/index.tsx @@ -6,6 +6,7 @@ import useInit from '@/hooks/useInit'; import useData from '@/hooks/useData'; import useChatModel from '@/hooks/useChatModel'; import useColumns from '@/hooks/useColumns'; +import { TABLE_PAGINATION } from '@/hooks/useTable'; import { chatModelPath } from '@/utils'; import { modelColumns } from './config'; import LanguageModelForm from './Form'; @@ -87,14 +88,7 @@ export default function LanguageModel() { columns={columns} scroll={{ x: 'auto' }} dataSource={opData} - pagination={{ - hideOnSinglePage: true, - showSizeChanger: true, - showQuickJumper: true, - defaultPageSize: 5, - pageSizeOptions: [5, 10, 15, 20], - showTotal: (total) => Total {total} items, - }} + pagination={TABLE_PAGINATION} /> act.replace(/\s+|\/+/g, '_').replace(/[^\d\w]/g, '').toLocaleLowerCase(); + +export const recordColumns = () => [ + { + title: 'URL', + dataIndex: 'url', + // fixed: 'left', + // width: 120, + key: 'url', + }, + { + title: 'File Type', + dataIndex: 'file_type', + key: 'file_type', + // width: 200, + }, + { + title: 'Action', + } +]; diff --git a/src/view/SyncMore/index.scss b/src/view/SyncMore/index.scss new file mode 100644 index 0000000..2a6f666 --- /dev/null +++ b/src/view/SyncMore/index.scss @@ -0,0 +1,28 @@ +.chat-prompts-tags { + .ant-tag { + margin: 2px; + } +} + +.add-btn { + margin-bottom: 10px; +} + +.chat-model-path { + font-size: 12px; + font-weight: bold; + color: #888; + margin-bottom: 5px; + + span { + display: inline-block; + // background-color: #d8d8d8; + color: #4096ff; + padding: 0 8px; + height: 20px; + line-height: 20px; + border-radius: 4px; + cursor: pointer; + text-decoration: underline; + } +} \ No newline at end of file diff --git a/src/view/SyncMore/index.tsx b/src/view/SyncMore/index.tsx new file mode 100644 index 0000000..f9f9d46 --- /dev/null +++ b/src/view/SyncMore/index.tsx @@ -0,0 +1,20 @@ +import { Table, Button } from 'antd'; + +import { TABLE_PAGINATION } from '@/hooks/useTable'; +import './index.scss'; + +export default function SyncMore() { + return ( +
+ + + + ) +} \ No newline at end of file diff --git a/src/view/SyncPrompts/index.scss b/src/view/SyncPrompts/index.scss index 6091c74..e942cdd 100644 --- a/src/view/SyncPrompts/index.scss +++ b/src/view/SyncPrompts/index.scss @@ -8,6 +8,19 @@ margin-bottom: 5px; } +.chat-table-tip, .chat-table-btns { + display: flex; + justify-content: space-between; +} + +.chat-table-btns { + margin-bottom: 5px; + + .num { + margin-left: 10px; + } +} + .chat-model-path { font-size: 12px; font-weight: bold; diff --git a/src/view/SyncPrompts/index.tsx b/src/view/SyncPrompts/index.tsx index 78b21ed..12809ec 100644 --- a/src/view/SyncPrompts/index.tsx +++ b/src/view/SyncPrompts/index.tsx @@ -7,6 +7,7 @@ import { writeTextFile } from '@tauri-apps/api/fs'; import useColumns from '@/hooks/useColumns'; import useData from '@/hooks/useData'; import useChatModel from '@/hooks/useChatModel'; +import useTable, { TABLE_PAGINATION } from '@/hooks/useTable'; import { fmtDate, chatPromptsPath, GITHUB_PROMPTS_CSV_URL } from '@/utils'; import { modelColumns, genCmd } from './config'; import './index.scss'; @@ -14,15 +15,19 @@ import './index.scss'; const promptsURL = 'https://github.com/f/awesome-chatgpt-prompts/blob/main/prompts.csv'; export default function LanguageModel() { + const { rowSelection, selectedRowIDs } = useTable(); const [loading, setLoading] = useState(false); const [lastUpdated, setLastUpdated] = useState(); const { modelJson, modelSet } = useChatModel('sys_sync_prompts'); - const { opData, opInit, opReplace, opSafeKey } = useData([]); + const { opData, opInit, opReplace, opReplaceItems, opSafeKey } = useData([]); const { columns, ...opInfo } = useColumns(modelColumns()); + const selectedItems = rowSelection.selectedRowKeys || []; + useEffect(() => { if (!modelJson?.sys_sync_prompts) return; opInit(modelJson?.sys_sync_prompts); + if (lastUpdated) return; (async () => { const fileData: Record = await invoke('metadata', { path: await chatPromptsPath() }); setLastUpdated(fileData.accessedAtMs); @@ -57,25 +62,37 @@ export default function LanguageModel() { } }, [opInfo.opTime]); + const handleEnable = (isEnable: boolean) => { + const data = opReplaceItems(selectedRowIDs, { enable: isEnable }) + modelSet(data); + }; + return (
- - {lastUpdated && Last updated on {fmtDate(lastUpdated)}} - +
+
+ {selectedItems.length > 0 && ( + <> + + + Selected {selectedItems.length} items + + )} +
+ +
+
+ URL: f/awesome-chatgpt-prompts/prompts.csv + {lastUpdated && Last updated on {fmtDate(lastUpdated)}} +
Total {total} items, - }} + rowSelection={rowSelection} + pagination={TABLE_PAGINATION} /> )