From cf3ba3c4406e9789aeed79f297a503cbe17fdba8 Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Wed, 10 Jul 2024 16:53:39 +0800 Subject: [PATCH] feat: added drag and drop sorting for assistants list --- package.json | 1 + src/renderer/src/hooks/useAssistant.ts | 2 + .../src/pages/home/components/Assistants.tsx | 73 ++++++++++++----- .../home/components/DragableAssistants.tsx | 0 src/renderer/src/store/assistants.ts | 4 + yarn.lock | 80 ++++++++++++++++++- 6 files changed, 136 insertions(+), 24 deletions(-) create mode 100644 src/renderer/src/pages/home/components/DragableAssistants.tsx diff --git a/package.json b/package.json index f806581b..2a06b833 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "@electron-toolkit/eslint-config-ts": "^1.0.1", "@electron-toolkit/tsconfig": "^1.0.1", "@fontsource/inter": "^5.0.18", + "@hello-pangea/dnd": "^16.6.0", "@reduxjs/toolkit": "^2.2.5", "@types/lodash": "^4.17.5", "@types/node": "^18.19.9", diff --git a/src/renderer/src/hooks/useAssistant.ts b/src/renderer/src/hooks/useAssistant.ts index 5220e94c..68ef89e5 100644 --- a/src/renderer/src/hooks/useAssistant.ts +++ b/src/renderer/src/hooks/useAssistant.ts @@ -5,6 +5,7 @@ import { removeAllTopics as _removeAllTopics, removeTopic as _removeTopic, setModel as _setModel, + updateAssistants as _updateAssistants, updateDefaultAssistant as _updateDefaultAssistant, updateTopic as _updateTopic, addAssistant, @@ -21,6 +22,7 @@ export function useAssistants() { return { assistants, + updateAssistants: (assistants: Assistant[]) => dispatch(_updateAssistants(assistants)), addAssistant: (assistant: Assistant) => dispatch(addAssistant(assistant)), updateAssistant: (assistant: Assistant) => dispatch(updateAssistant(assistant)), removeAssistant: (id: string) => { diff --git a/src/renderer/src/pages/home/components/Assistants.tsx b/src/renderer/src/pages/home/components/Assistants.tsx index 725e0050..1533fada 100644 --- a/src/renderer/src/pages/home/components/Assistants.tsx +++ b/src/renderer/src/pages/home/components/Assistants.tsx @@ -1,13 +1,14 @@ -import { FC, useRef } from 'react' -import styled from 'styled-components' +import { CopyOutlined, DeleteOutlined, EditOutlined } from '@ant-design/icons' +import { DragDropContext, Draggable, Droppable, DropResult } from '@hello-pangea/dnd' +import AssistantSettingPopup from '@renderer/components/Popups/AssistantSettingPopup' import { useAssistants } from '@renderer/hooks/useAssistant' +import { getDefaultTopic } from '@renderer/services/assistant' import { Assistant } from '@renderer/types' +import { uuid } from '@renderer/utils' import { Dropdown, MenuProps } from 'antd' import { last } from 'lodash' -import AssistantSettingPopup from '@renderer/components/Popups/AssistantSettingPopup' -import { CopyOutlined, DeleteOutlined, EditOutlined } from '@ant-design/icons' -import { uuid } from '@renderer/utils' -import { getDefaultTopic } from '@renderer/services/assistant' +import { FC, useRef } from 'react' +import styled from 'styled-components' interface Props { activeAssistant: Assistant @@ -15,8 +16,15 @@ interface Props { onCreateAssistant: () => void } +const reorder = (list: Assistant[], startIndex: number, endIndex: number, len = 1) => { + const result = Array.from(list) + const removed = result.splice(startIndex, len) + result.splice(endIndex, 0, ...removed) + return result +} + const Assistants: FC = ({ activeAssistant, setActiveAssistant, onCreateAssistant }) => { - const { assistants, removeAssistant, updateAssistant, addAssistant } = useAssistants() + const { assistants, removeAssistant, updateAssistant, addAssistant, updateAssistants } = useAssistants() const targetAssistant = useRef(null) const onDelete = (assistant: Assistant) => { @@ -61,22 +69,45 @@ const Assistants: FC = ({ activeAssistant, setActiveAssistant, onCreateAs } ] + const onDragEnd = (result: DropResult) => { + if (result.destination) { + const sourceIndex = result.source.index + const destIndex = result.destination.index + const reorderAssistants = reorder(assistants, sourceIndex, destIndex) + updateAssistants(reorderAssistants) + } + } + return ( - {assistants.map((assistant) => ( - (targetAssistant.current = assistant)}> - setActiveAssistant(assistant)} - className={assistant.id === activeAssistant?.id ? 'active' : ''}> - {assistant.name} - {assistant.description} - - - ))} + + + {(provided) => ( +
+ {assistants.map((assistant, index) => ( + + {(provided) => ( +
+ (targetAssistant.current = assistant)}> + setActiveAssistant(assistant)} + className={assistant.id === activeAssistant?.id ? 'active' : ''}> + {assistant.name} + {assistant.description} + + +
+ )} +
+ ))} +
+ )} +
+
) } diff --git a/src/renderer/src/pages/home/components/DragableAssistants.tsx b/src/renderer/src/pages/home/components/DragableAssistants.tsx new file mode 100644 index 00000000..e69de29b diff --git a/src/renderer/src/store/assistants.ts b/src/renderer/src/store/assistants.ts index bb423b9f..f745e7f7 100644 --- a/src/renderer/src/store/assistants.ts +++ b/src/renderer/src/store/assistants.ts @@ -21,6 +21,9 @@ const assistantsSlice = createSlice({ updateDefaultAssistant: (state, action: PayloadAction<{ assistant: Assistant }>) => { state.defaultAssistant = action.payload.assistant }, + updateAssistants: (state, action: PayloadAction) => { + state.assistants = action.payload + }, addAssistant: (state, action: PayloadAction) => { state.assistants.push(action.payload) }, @@ -89,6 +92,7 @@ const assistantsSlice = createSlice({ export const { updateDefaultAssistant, + updateAssistants, addAssistant, removeAssistant, updateAssistant, diff --git a/yarn.lock b/yarn.lock index 32423b05..cc208057 100644 --- a/yarn.lock +++ b/yarn.lock @@ -234,7 +234,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.0" -"@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.6", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.7", "@babel/runtime@^7.3.1": +"@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.6", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.1", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.7", "@babel/runtime@^7.3.1", "@babel/runtime@^7.9.2": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.7.tgz#f4f0d5530e8dbdf59b3451b9b3e594b6ba082e12" integrity sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw== @@ -678,6 +678,19 @@ resolved "https://registry.yarnpkg.com/@fontsource/inter/-/inter-5.0.18.tgz#eaddac790ee74b70932030f37ebaa9fc76decbd8" integrity sha512-YCsoYPTcs713sI7tLtxaPrIhXAXvEetGg5Ry02ivA8qUOb3fQHojbK/X9HLD5OOKvFUNR2Ynkwb1kR1hVKQHpw== +"@hello-pangea/dnd@^16.6.0": + version "16.6.0" + resolved "https://registry.yarnpkg.com/@hello-pangea/dnd/-/dnd-16.6.0.tgz#7509639c7bd13f55e537b65a9dcfcd54e7c99ac7" + integrity sha512-vfZ4GydqbtUPXSLfAvKvXQ6xwRzIjUSjVU0Sx+70VOhc2xx6CdmJXJ8YhH70RpbTUGjxctslQTHul9sIOxCfFQ== + dependencies: + "@babel/runtime" "^7.24.1" + css-box-model "^1.2.1" + memoize-one "^6.0.0" + raf-schd "^4.0.3" + react-redux "^8.1.3" + redux "^4.2.1" + use-memo-one "^1.1.3" + "@humanwhocodes/config-array@^0.11.14": version "0.11.14" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" @@ -1057,6 +1070,14 @@ dependencies: "@types/unist" "*" +"@types/hoist-non-react-statics@^3.3.1": + version "3.3.5" + resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz#dab7867ef789d87e2b4b0003c9d65c49cc44a494" + integrity sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg== + dependencies: + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + "@types/http-cache-semantics@*": version "4.0.4" resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4" @@ -2030,6 +2051,13 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +css-box-model@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/css-box-model/-/css-box-model-1.2.1.tgz#59951d3b81fd6b2074a62d49444415b0d2b4d7c1" + integrity sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw== + dependencies: + tiny-invariant "^1.0.6" + css-color-keywords@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05" @@ -3176,6 +3204,13 @@ highlight.js@^10.4.1, highlight.js@~10.7.0: resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== +hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== + dependencies: + react-is "^16.7.0" + hosted-git-info@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz#827b82867e9ff1c8d0c4d9d53880397d2c86d224" @@ -3940,6 +3975,11 @@ mdast-util-to-string@^4.0.0: dependencies: "@types/mdast" "^4.0.0" +memoize-one@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-6.0.0.tgz#b2591b871ed82948aee4727dc6abceeeac8c1045" + integrity sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw== + merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" @@ -4623,6 +4663,11 @@ quick-lru@^5.1.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== +raf-schd@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-4.0.3.tgz#5d6c34ef46f8b2a0e880a8fcdb743efc5bfdbc1a" + integrity sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ== + rc-cascader@~3.26.0: version "3.26.0" resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.26.0.tgz#1bcc9c29451047dc99e28fdd125c94828d7ddf76" @@ -4995,12 +5040,12 @@ react-i18next@^14.1.2: "@babel/runtime" "^7.23.9" html-parse-stringify "^3.0.1" -react-is@^16.13.1: +react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -react-is@^18.2.0: +react-is@^18.0.0, react-is@^18.2.0: version "18.3.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== @@ -5021,6 +5066,18 @@ react-markdown@^9.0.1: unist-util-visit "^5.0.0" vfile "^6.0.0" +react-redux@^8.1.3: + version "8.1.3" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-8.1.3.tgz#4fdc0462d0acb59af29a13c27ffef6f49ab4df46" + integrity sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw== + dependencies: + "@babel/runtime" "^7.12.1" + "@types/hoist-non-react-statics" "^3.3.1" + "@types/use-sync-external-store" "^0.0.3" + hoist-non-react-statics "^3.3.2" + react-is "^18.0.0" + use-sync-external-store "^1.0.0" + react-redux@^9.1.2: version "9.1.2" resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-9.1.2.tgz#deba38c64c3403e9abd0c3fbeab69ffd9d8a7e4b" @@ -5109,6 +5166,13 @@ redux-thunk@^3.1.0: resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-3.1.0.tgz#94aa6e04977c30e14e892eae84978c1af6058ff3" integrity sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw== +redux@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.1.tgz#c08f4306826c49b5e9dc901dee0452ea8fce6197" + integrity sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w== + dependencies: + "@babel/runtime" "^7.9.2" + redux@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/redux/-/redux-5.0.1.tgz#97fa26881ce5746500125585d5642c77b6e9447b" @@ -5698,6 +5762,11 @@ throttle-debounce@^5.0.0: resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-5.0.2.tgz#ec5549d84e053f043c9fd0f2a6dd892ff84456b1" integrity sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A== +tiny-invariant@^1.0.6: + version "1.3.3" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz#46680b7a873a0d5d10005995eb90a70d74d60127" + integrity sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg== + tiny-typed-emitter@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz#b3b027fdd389ff81a152c8e847ee2f5be9fad7b5" @@ -5943,6 +6012,11 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +use-memo-one@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.3.tgz#2fd2e43a2169eabc7496960ace8c79efef975e99" + integrity sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ== + use-sync-external-store@^1.0.0: version "1.2.2" resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz#c3b6390f3a30eba13200d2302dcdf1e7b57b2ef9"