feat: added drag and drop sorting for topics list
This commit is contained in:
parent
cf3ba3c440
commit
c9b57536c4
@ -8,6 +8,7 @@ import {
|
|||||||
updateAssistants as _updateAssistants,
|
updateAssistants as _updateAssistants,
|
||||||
updateDefaultAssistant as _updateDefaultAssistant,
|
updateDefaultAssistant as _updateDefaultAssistant,
|
||||||
updateTopic as _updateTopic,
|
updateTopic as _updateTopic,
|
||||||
|
updateTopics as _updateTopics,
|
||||||
addAssistant,
|
addAssistant,
|
||||||
removeAssistant,
|
removeAssistant,
|
||||||
updateAssistant
|
updateAssistant
|
||||||
@ -46,6 +47,7 @@ export function useAssistant(id: string) {
|
|||||||
addTopic: (topic: Topic) => dispatch(_addTopic({ assistantId: assistant.id, topic })),
|
addTopic: (topic: Topic) => dispatch(_addTopic({ assistantId: assistant.id, topic })),
|
||||||
removeTopic: (topic: Topic) => dispatch(_removeTopic({ assistantId: assistant.id, topic })),
|
removeTopic: (topic: Topic) => dispatch(_removeTopic({ assistantId: assistant.id, topic })),
|
||||||
updateTopic: (topic: Topic) => dispatch(_updateTopic({ assistantId: assistant.id, topic })),
|
updateTopic: (topic: Topic) => dispatch(_updateTopic({ assistantId: assistant.id, topic })),
|
||||||
|
updateTopics: (topics: Topic[]) => dispatch(_updateTopics({ assistantId: assistant.id, topics })),
|
||||||
removeAllTopics: () => dispatch(_removeAllTopics({ assistantId: assistant.id })),
|
removeAllTopics: () => dispatch(_removeAllTopics({ assistantId: assistant.id })),
|
||||||
setModel: (model: Model) => dispatch(_setModel({ assistantId: assistant.id, model }))
|
setModel: (model: Model) => dispatch(_setModel({ assistantId: assistant.id, model }))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,16 @@
|
|||||||
import { Assistant } from '@renderer/types'
|
import { Assistant } from '@renderer/types'
|
||||||
|
import { find } from 'lodash'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
|
|
||||||
export function useActiveTopic(assistant: Assistant) {
|
export function useActiveTopic(assistant: Assistant) {
|
||||||
const [activeTopic, setActiveTopic] = useState(assistant?.topics[0])
|
const [activeTopic, setActiveTopic] = useState(assistant?.topics[0])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
assistant?.topics && setActiveTopic(assistant?.topics[0])
|
// activeTopic not in assistant.topics
|
||||||
}, [assistant])
|
if (!find(assistant.topics, { id: activeTopic?.id })) {
|
||||||
|
setActiveTopic(assistant.topics[0])
|
||||||
|
}
|
||||||
|
}, [activeTopic?.id, assistant])
|
||||||
|
|
||||||
return { activeTopic, setActiveTopic }
|
return { activeTopic, setActiveTopic }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import AssistantSettingPopup from '@renderer/components/Popups/AssistantSettingP
|
|||||||
import { useAssistants } from '@renderer/hooks/useAssistant'
|
import { useAssistants } from '@renderer/hooks/useAssistant'
|
||||||
import { getDefaultTopic } from '@renderer/services/assistant'
|
import { getDefaultTopic } from '@renderer/services/assistant'
|
||||||
import { Assistant } from '@renderer/types'
|
import { Assistant } from '@renderer/types'
|
||||||
import { uuid } from '@renderer/utils'
|
import { droppableReorder, uuid } from '@renderer/utils'
|
||||||
import { Dropdown, MenuProps } from 'antd'
|
import { Dropdown, MenuProps } from 'antd'
|
||||||
import { last } from 'lodash'
|
import { last } from 'lodash'
|
||||||
import { FC, useRef } from 'react'
|
import { FC, useRef } from 'react'
|
||||||
@ -16,13 +16,6 @@ interface Props {
|
|||||||
onCreateAssistant: () => void
|
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<Props> = ({ activeAssistant, setActiveAssistant, onCreateAssistant }) => {
|
const Assistants: FC<Props> = ({ activeAssistant, setActiveAssistant, onCreateAssistant }) => {
|
||||||
const { assistants, removeAssistant, updateAssistant, addAssistant, updateAssistants } = useAssistants()
|
const { assistants, removeAssistant, updateAssistant, addAssistant, updateAssistants } = useAssistants()
|
||||||
const targetAssistant = useRef<Assistant | null>(null)
|
const targetAssistant = useRef<Assistant | null>(null)
|
||||||
@ -73,7 +66,7 @@ const Assistants: FC<Props> = ({ activeAssistant, setActiveAssistant, onCreateAs
|
|||||||
if (result.destination) {
|
if (result.destination) {
|
||||||
const sourceIndex = result.source.index
|
const sourceIndex = result.source.index
|
||||||
const destIndex = result.destination.index
|
const destIndex = result.destination.index
|
||||||
const reorderAssistants = reorder(assistants, sourceIndex, destIndex)
|
const reorderAssistants = droppableReorder<Assistant>(assistants, sourceIndex, destIndex)
|
||||||
updateAssistants(reorderAssistants)
|
updateAssistants(reorderAssistants)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,8 @@ import { FC, useRef } from 'react'
|
|||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import { DeleteOutlined, EditOutlined, SignatureOutlined } from '@ant-design/icons'
|
import { DeleteOutlined, EditOutlined, SignatureOutlined } from '@ant-design/icons'
|
||||||
import LocalStorage from '@renderer/services/storage'
|
import LocalStorage from '@renderer/services/storage'
|
||||||
|
import { DragDropContext, Draggable, Droppable, DropResult } from '@hello-pangea/dnd'
|
||||||
|
import { droppableReorder } from '@renderer/utils'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
assistant: Assistant
|
assistant: Assistant
|
||||||
@ -17,7 +19,7 @@ interface Props {
|
|||||||
|
|
||||||
const Topics: FC<Props> = ({ assistant, activeTopic, setActiveTopic }) => {
|
const Topics: FC<Props> = ({ assistant, activeTopic, setActiveTopic }) => {
|
||||||
const { showRightSidebar } = useShowRightSidebar()
|
const { showRightSidebar } = useShowRightSidebar()
|
||||||
const { removeTopic, updateTopic, removeAllTopics } = useAssistant(assistant.id)
|
const { removeTopic, updateTopic, removeAllTopics, updateTopics } = useAssistant(assistant.id)
|
||||||
const currentTopic = useRef<Topic | null>(null)
|
const currentTopic = useRef<Topic | null>(null)
|
||||||
|
|
||||||
const topicMenuItems: MenuProps['items'] = [
|
const topicMenuItems: MenuProps['items'] = [
|
||||||
@ -70,6 +72,14 @@ const Topics: FC<Props> = ({ assistant, activeTopic, setActiveTopic }) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onDragEnd = (result: DropResult) => {
|
||||||
|
if (result.destination) {
|
||||||
|
const sourceIndex = result.source.index
|
||||||
|
const destIndex = result.destination.index
|
||||||
|
updateTopics(droppableReorder(assistant.topics, sourceIndex, destIndex))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!showRightSidebar) {
|
if (!showRightSidebar) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@ -92,17 +102,33 @@ const Topics: FC<Props> = ({ assistant, activeTopic, setActiveTopic }) => {
|
|||||||
</DeleteButton>
|
</DeleteButton>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</TopicTitle>
|
</TopicTitle>
|
||||||
{assistant.topics.map((topic) => (
|
<DragDropContext onDragEnd={onDragEnd}>
|
||||||
|
<Droppable droppableId="droppable">
|
||||||
|
{(provided) => (
|
||||||
|
<div {...provided.droppableProps} ref={provided.innerRef}>
|
||||||
|
{assistant.topics.map((topic, index) => (
|
||||||
|
<Draggable key={`draggable_${topic.id}_${index}`} draggableId={topic.id} index={index}>
|
||||||
|
{(provided) => (
|
||||||
|
<div ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
menu={{ items: topicMenuItems }}
|
menu={{ items: topicMenuItems }}
|
||||||
trigger={['contextMenu']}
|
trigger={['contextMenu']}
|
||||||
key={topic.id}
|
key={topic.id}
|
||||||
onOpenChange={(open) => open && (currentTopic.current = topic)}>
|
onOpenChange={(open) => open && (currentTopic.current = topic)}>
|
||||||
<TopicListItem className={topic.id === activeTopic?.id ? 'active' : ''} onClick={() => setActiveTopic(topic)}>
|
<TopicListItem
|
||||||
|
className={topic.id === activeTopic?.id ? 'active' : ''}
|
||||||
|
onClick={() => setActiveTopic(topic)}>
|
||||||
{topic.name}
|
{topic.name}
|
||||||
</TopicListItem>
|
</TopicListItem>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Draggable>
|
||||||
))}
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Droppable>
|
||||||
|
</DragDropContext>
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -65,6 +65,16 @@ const assistantsSlice = createSlice({
|
|||||||
: assistant
|
: assistant
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
updateTopics: (state, action: PayloadAction<{ assistantId: string; topics: Topic[] }>) => {
|
||||||
|
state.assistants = state.assistants.map((assistant) =>
|
||||||
|
assistant.id === action.payload.assistantId
|
||||||
|
? {
|
||||||
|
...assistant,
|
||||||
|
topics: action.payload.topics
|
||||||
|
}
|
||||||
|
: assistant
|
||||||
|
)
|
||||||
|
},
|
||||||
removeAllTopics: (state, action: PayloadAction<{ assistantId: string }>) => {
|
removeAllTopics: (state, action: PayloadAction<{ assistantId: string }>) => {
|
||||||
state.assistants = state.assistants.map((assistant) => {
|
state.assistants = state.assistants.map((assistant) => {
|
||||||
if (assistant.id === action.payload.assistantId) {
|
if (assistant.id === action.payload.assistantId) {
|
||||||
@ -99,6 +109,7 @@ export const {
|
|||||||
addTopic,
|
addTopic,
|
||||||
removeTopic,
|
removeTopic,
|
||||||
updateTopic,
|
updateTopic,
|
||||||
|
updateTopics,
|
||||||
removeAllTopics,
|
removeAllTopics,
|
||||||
setModel
|
setModel
|
||||||
} = assistantsSlice.actions
|
} = assistantsSlice.actions
|
||||||
|
|||||||
@ -81,3 +81,10 @@ export const getDefaultGroupName = (id: string) => {
|
|||||||
|
|
||||||
return id.toUpperCase()
|
return id.toUpperCase()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function droppableReorder<T>(list: T[], startIndex: number, endIndex: number, len = 1) {
|
||||||
|
const result = Array.from(list)
|
||||||
|
const removed = result.splice(startIndex, len)
|
||||||
|
result.splice(endIndex, 0, ...removed)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user