refactor: port collection move/reorder

This commit is contained in:
jamesgeorge007
2024-02-20 11:45:07 +05:30
parent 6ed9c09f06
commit 076006c4a6
5 changed files with 760 additions and 36 deletions

View File

@@ -1,6 +1,17 @@
<template>
<div v-if="!activeWorkspaceHandle">No Workspace Selected.</div>
<div v-else class="flex-1">
<div
v-else
:class="{
'rounded border border-divider': saveRequest,
'bg-primaryDark':
draggingToRoot && currentReorderingStatus.type !== 'request',
}"
class="flex-1"
@drop.prevent="dropToRoot"
@dragover.prevent="draggingToRoot = true"
@dragend="draggingToRoot = false"
>
<div
class="sticky z-10 flex flex-shrink-0 flex-col overflow-x-auto border-b border-dividerLight bg-primary"
:style="{
@@ -30,7 +41,12 @@
import { useService } from "dioc/vue"
import { ref } from "vue"
import { useI18n } from "~/composables/i18n"
import { useReadonlyStream } from "~/composables/stream"
import { useToast } from "~/composables/toast"
import { Picked } from "~/helpers/types/HoppPicked"
import toast from "~/modules/toast"
import { moveRESTFolder } from "~/newstore/collections"
import { currentReorderingStatus$ } from "~/newstore/reordering"
import { NewWorkspaceService } from "~/services/new-workspace"
defineProps<{
@@ -44,10 +60,49 @@ const emit = defineEmits<{
}>()
const t = useI18n()
const toast = useToast()
const draggingToRoot = ref(false)
const searchText = ref("")
const workspaceService = useService(NewWorkspaceService)
const activeWorkspaceHandle = workspaceService.activeWorkspaceHandle
const currentReorderingStatus = useReadonlyStream(currentReorderingStatus$, {
type: "collection",
id: "",
parentID: "",
})
/**
* This function is called when the user drops the collection
* to the root
* @param payload - object containing the collection index dragged
*/
const dropToRoot = ({ dataTransfer }: DragEvent) => {
if (dataTransfer) {
const collectionIndexDragged = dataTransfer.getData("collectionIndex")
if (!collectionIndexDragged) return
// check if the collection is already in the root
if (isAlreadyInRoot(collectionIndexDragged)) {
toast.error(`${t("collection.invalid_root_move")}`)
} else {
moveRESTFolder(collectionIndexDragged, null)
toast.success(`${t("collection.moved")}`)
}
draggingToRoot.value = false
}
}
/**
* Checks if the collection is already in the root
* @param id - path of the collection
* @returns boolean - true if the collection is already in the root
*/
const isAlreadyInRoot = (id: string) => {
const indexPath = id.split("/")
return indexPath.length === 1
}
</script>

View File

@@ -1,8 +1,37 @@
<template>
<div class="flex flex-col">
<div
class="h-1 w-full transition"
:class="[
{
'bg-accentDark': isReorderable,
},
]"
@drop="orderUpdateCollectionEvent"
@dragover.prevent="ordering = true"
@dragleave="ordering = false"
@dragend="resetDragState"
></div>
<div class="relative flex flex-col">
<div
class="z-3 group pointer-events-auto relative flex cursor-pointer items-stretch"
class="z-[1] pointer-events-none absolute inset-0 bg-accent opacity-0 transition"
:class="{
'opacity-25':
dragging && notSameDestination && notSameParentDestination,
}"
></div>
<div
class="z-[3] group pointer-events-auto relative flex cursor-pointer items-stretch"
@dragstart="dragStart"
@drop="handelDrop($event)"
@dragover="handleDragOver($event)"
@dragleave="resetDragState"
@dragend="
() => {
resetDragState()
dropItemID = ''
}
"
@contextmenu.prevent="options?.tippy.show()"
>
<div
@@ -149,14 +178,33 @@
</div>
</div>
</div>
<div
v-if="isLastItem"
class="w-full transition"
:class="[
{
'bg-accentDark': isLastItemReorderable,
'h-1 ': isLastItem,
},
]"
@drop="updateLastItemOrder"
@dragover.prevent="orderingLastItem = true"
@dragleave="orderingLastItem = false"
@dragend="resetDragState"
></div>
</div>
</template>
<script setup lang="ts">
import { computed, ref } from "vue"
import { computed, ref, watch } from "vue"
import { TippyComponent } from "vue-tippy"
import { useI18n } from "~/composables/i18n"
import { useReadonlyStream } from "~/composables/stream"
import {
currentReorderingStatus$,
changeCurrentReorderStatus,
} from "~/newstore/reordering"
import { RESTCollectionViewCollection } from "~/services/new-workspace/view"
import IconCheckCircle from "~icons/lucide/check-circle"
@@ -176,11 +224,15 @@ const props = defineProps<{
collectionView: RESTCollectionViewCollection
isOpen: boolean
isSelected?: boolean | null
isLastItem?: boolean
}>()
const emit = defineEmits<{
(event: "add-child-collection", parentCollectionIndexPath: string): void
(event: "add-request", parentCollectionIndexPath: string): void
(event: "dragging", payload: boolean): void
(event: "drop-event", payload: DataTransfer): void
(event: "drag-event", payload: DataTransfer): void
(
event: "edit-child-collection",
payload: { collectionIndexPath: string; collectionName: string }
@@ -194,6 +246,8 @@ const emit = defineEmits<{
(event: "remove-child-collection", collectionIndexPath: string): void
(event: "remove-root-collection", collectionIndexPath: string): void
(event: "toggle-children"): void
(event: "update-collection-order", payload: DataTransfer): void
(event: "update-last-collection-order", payload: DataTransfer): void
}>()
const tippyActions = ref<TippyComponent | null>(null)
@@ -205,11 +259,69 @@ const exportAction = ref<HTMLButtonElement | null>(null)
const propertiesAction = ref<TippyComponent | null>(null)
const options = ref<TippyComponent | null>(null)
const dragging = ref(false)
const ordering = ref(false)
const orderingLastItem = ref(false)
const dropItemID = ref("")
const currentReorderingStatus = useReadonlyStream(currentReorderingStatus$, {
type: "collection",
id: "",
parentID: "",
})
// Used to determine if the collection is being dragged to a different destination
// This is used to make the highlight effect work
watch(
() => dragging.value,
(val) => {
if (val && notSameDestination.value && notSameParentDestination.value) {
emit("dragging", true)
} else {
emit("dragging", false)
}
}
)
const collectionIcon = computed(() => {
if (props.isSelected) {
return IconCheckCircle
}
return !props.isOpen ? IconFolder : IconFolderOpen
return props.isOpen ? IconFolderOpen : IconFolder
})
const notSameParentDestination = computed(() => {
return (
currentReorderingStatus.value.parentID !== props.collectionView.collectionID
)
})
const isRequestDragging = computed(() => {
return currentReorderingStatus.value.type === "request"
})
const isSameParent = computed(() => {
return (
currentReorderingStatus.value.parentID ===
props.collectionView.parentCollectionID
)
})
const isReorderable = computed(() => {
return (
ordering.value &&
notSameDestination.value &&
!isRequestDragging.value &&
isSameParent.value
)
})
const isLastItemReorderable = computed(() => {
return (
orderingLastItem.value &&
notSameDestination.value &&
!isRequestDragging.value &&
isSameParent.value
)
})
const addChildCollection = () => {
@@ -234,6 +346,85 @@ const editCollection = () => {
: emit("edit-root-collection", data)
}
const dragStart = ({ dataTransfer }: DragEvent) => {
if (dataTransfer) {
emit("drag-event", dataTransfer)
dropItemID.value = dataTransfer.getData("collectionIndex")
dragging.value = !dragging.value
changeCurrentReorderStatus({
type: "collection",
id: props.collectionView.collectionID,
parentID: props.collectionView.parentCollectionID,
})
}
}
// Trigger the re-ordering event when a collection is dragged over another collection's top section
const handleDragOver = (e: DragEvent) => {
dragging.value = true
if (
e.offsetY < 10 &&
notSameDestination.value &&
!isRequestDragging.value &&
isSameParent.value
) {
ordering.value = true
dragging.value = false
orderingLastItem.value = false
} else if (
e.offsetY > 18 &&
notSameDestination.value &&
!isRequestDragging.value &&
isSameParent.value &&
props.isLastItem
) {
orderingLastItem.value = true
dragging.value = false
ordering.value = false
} else {
ordering.value = false
orderingLastItem.value = false
}
}
const handelDrop = (e: DragEvent) => {
if (ordering.value) {
orderUpdateCollectionEvent(e)
} else if (orderingLastItem.value) {
updateLastItemOrder(e)
} else {
notSameParentDestination.value ? dropEvent(e) : e.stopPropagation()
}
}
const dropEvent = (e: DragEvent) => {
if (e.dataTransfer) {
e.stopPropagation()
emit("drop-event", e.dataTransfer)
resetDragState()
}
}
const orderUpdateCollectionEvent = (e: DragEvent) => {
if (e.dataTransfer) {
e.stopPropagation()
emit("update-collection-order", e.dataTransfer)
resetDragState()
}
}
const updateLastItemOrder = (e: DragEvent) => {
if (e.dataTransfer) {
e.stopPropagation()
emit("update-last-collection-order", e.dataTransfer)
resetDragState()
}
}
const notSameDestination = computed(() => {
return dropItemID.value !== props.collectionView.collectionID
})
const removeCollection = () => {
const { collectionID } = props.collectionView
@@ -241,4 +432,10 @@ const removeCollection = () => {
? emit("remove-child-collection", collectionID)
: emit("remove-root-collection", collectionID)
}
const resetDragState = () => {
dragging.value = false
ordering.value = false
orderingLastItem.value = false
}
</script>

View File

@@ -34,10 +34,13 @@
<div class="flex flex-1 flex-col">
<HoppSmartTree :adapter="treeAdapter">
<template #content="{ node, toggleChildren, isOpen }">
<template
#content="{ highlightChildren, isOpen, node, toggleChildren }"
>
<NewCollectionsRestCollection
v-if="node.data.type === 'collection'"
:collection-view="node.data.value"
:is-last-item="node.data.value.isLastItem"
:is-open="isOpen"
:is-selected="
isSelected(
@@ -47,6 +50,14 @@
:save-request="saveRequest"
@add-request="addRequest"
@add-child-collection="addChildCollection"
@dragging="
(isDraging) =>
highlightChildren(
isDraging ? node.data.value.collectionID : null
)
"
@drag-event="dragEvent($event, node.data.value.collectionID)"
@drop-event="dropEvent($event, node.data.value.collectionID)"
@edit-child-collection="editChildCollection"
@edit-root-collection="editRootCollection"
@edit-collection-properties="editCollectionProperties"
@@ -68,22 +79,55 @@
})
}
"
@update-collection-order="
updateCollectionOrder($event, {
destinationCollectionIndex: node.data.value.collectionID,
destinationCollectionParentIndex:
node.data.value.parentCollectionID,
})
"
@update-last-collection-order="
updateCollectionOrder($event, {
destinationCollectionIndex: null,
destinationCollectionParentIndex:
node.data.value.parentCollectionID,
})
"
/>
<NewCollectionsRestRequest
v-else-if="node.data.type === 'request'"
:is-active="isActiveRequest(node.data.value)"
:is-last-item="node.data.value.isLastItem"
:is-selected="
isSelected(getRequestIndexPathArgs(node.data.value.requestID))
"
:request-view="node.data.value"
:save-request="saveRequest"
@drag-request="
dragRequest($event, {
parentCollectionIndexPath: node.data.value.parentCollectionID,
requestIndex: node.data.value.requestID,
})
"
@duplicate-request="duplicateRequest"
@edit-request="editRequest"
@remove-request="removeRequest"
@select-pick="onSelectPick"
@select-request="selectRequest"
@share-request="shareRequest"
@update-request-order="
updateRequestOrder($event, {
parentCollectionIndexPath: node.data.value.parentCollectionID,
requestIndex: node.data.value.requestID,
})
"
@update-last-request-order="
updateRequestOrder($event, {
parentCollectionIndexPath: node.data.value.parentCollectionID,
requestIndex: null,
})
"
/>
<div v-else @click="toggleChildren">
{{ node.data.value }}
@@ -96,6 +140,13 @@
</HoppSmartTree>
</div>
<!-- <div
class="py-15 hidden flex-1 flex-col items-center justify-center bg-primaryDark px-4 text-secondaryLight"
:class="{
'!flex': draggingToRoot && currentReorderingStatus.type !== 'request',
}"
> -->
<CollectionsAdd
:show="showModalAdd"
:loading-state="modalLoadingState"
@@ -169,20 +220,29 @@ import { useService } from "dioc/vue"
import { markRaw, nextTick, ref } from "vue"
import { HoppCollection, HoppRESTAuth, HoppRESTRequest } from "@hoppscotch/data"
import { cloneDeep } from "lodash-es"
import { cloneDeep, isEqual } from "lodash-es"
import { useI18n } from "~/composables/i18n"
import { useReadonlyStream } from "~/composables/stream"
import { useToast } from "~/composables/toast"
import { invokeAction } from "~/helpers/actions"
import { WorkspaceRESTCollectionTreeAdapter } from "~/helpers/adapters/WorkspaceRESTCollectionTreeAdapter"
import { TeamCollection } from "~/helpers/backend/graphql"
import { updateInheritedPropertiesForAffectedRequests } from "~/helpers/collection/collection"
import {
getFoldersByPath,
resolveSaveContextOnCollectionReorder,
updateInheritedPropertiesForAffectedRequests,
} from "~/helpers/collection/collection"
import { HoppInheritedProperty } from "~/helpers/types/HoppInheritedProperties"
import { Picked } from "~/helpers/types/HoppPicked"
import {
cascadeParentCollectionForHeaderAuth,
moveRESTFolder,
moveRESTRequest,
navigateToFolderWithIndexPath,
restCollections$,
saveRESTRequestAs,
updateRESTCollectionOrder,
updateRESTRequestOrder,
} from "~/newstore/collections"
import { platform } from "~/platform"
import { NewWorkspaceService } from "~/services/new-workspace"
@@ -205,9 +265,42 @@ const props = defineProps<{
}>()
const emit = defineEmits<{
(event: "select", payload: Picked | null): void
(e: "display-modal-add"): void
(e: "display-modal-import-export"): void
(
event: "drop-collection",
payload: {
collectionIndexDragged: string
destinationCollectionIndex: string
}
): void
(
event: "drop-request",
payload: {
parentCollectionIndexPath: string
requestIndex: string
destinationCollectionIndex: string
}
): void
(event: "select", payload: Picked | null): void
(
event: "update-collection-order",
payload: {
dragedCollectionIndex: string
destinationCollection: {
destinationCollectionIndex: string | null
destinationCollectionParentIndex: string | null
}
}
): void
(
event: "update-request-order",
payload: {
dragedRequestIndex: string
destinationRequestIndex: string | null
destinationCollectionIndex: string
}
): void
}>()
const workspaceService = useService(NewWorkspaceService)
@@ -1151,6 +1244,263 @@ const exportCollection = async (collectionIndexPath: string) => {
}
}
const dragEvent = (dataTransfer: DataTransfer, collectionIndex: string) => {
dataTransfer.setData("collectionIndex", collectionIndex)
}
const dragRequest = (
dataTransfer: DataTransfer,
{
parentCollectionIndexPath,
requestIndex,
}: { parentCollectionIndexPath: string | null; requestIndex: string }
) => {
if (!parentCollectionIndexPath) return
dataTransfer.setData("parentCollectionIndexPath", parentCollectionIndexPath)
dataTransfer.setData("requestIndex", requestIndex)
}
const dropEvent = (
dataTransfer: DataTransfer,
destinationCollectionIndex: string
) => {
const parentCollectionIndexPath = dataTransfer.getData(
"parentCollectionIndexPath"
)
const requestIndex = dataTransfer.getData("requestIndex")
const collectionIndexDragged = dataTransfer.getData("collectionIndex")
if (parentCollectionIndexPath && requestIndex) {
// emit("drop-request", {
// parentCollectionIndexPath,
// requestIndex,
// destinationCollectionIndex,
// })
dropRequest({
parentCollectionIndexPath,
requestIndex,
destinationCollectionIndex,
})
} else {
dropCollection({
collectionIndexDragged,
destinationCollectionIndex,
})
}
}
const dropRequest = (payload: {
parentCollectionIndexPath?: string | undefined
requestIndex: string
destinationCollectionIndex: string
}) => {
const {
parentCollectionIndexPath,
requestIndex,
destinationCollectionIndex,
} = payload
if (
!requestIndex ||
!destinationCollectionIndex ||
!parentCollectionIndexPath
)
return
// const { auth, headers } = cascadeParentCollectionForHeaderAuth(
// destinationCollectionIndex,
// "rest"
// )
// const possibleTab = tabs.getTabRefWithSaveContext({
// originLocation: "user-collection",
// folderPath,
// requestIndex: pathToLastIndex(requestIndex),
// })
// If there is a tab attached to this request, change save its save context
// if (possibleTab) {
// possibleTab.value.document.saveContext = {
// originLocation: "user-collection",
// folderPath: destinationCollectionIndex,
// requestIndex: getRequestsByPath(
// restCollectionState.value,
// destinationCollectionIndex
// ).length,
// }
// possibleTab.value.document.inheritedProperties = {
// auth,
// headers,
// }
// }
// When it's drop it's basically getting deleted from last folder. reordering last folder accordingly
// resolveSaveContextOnRequestReorder({
// lastIndex: pathToLastIndex(requestIndex),
// newIndex: -1, // being deleted from last folder
// folderPath,
// length: getRequestsByPath(myCollections.value, folderPath).length,
// })
moveRESTRequest(
parentCollectionIndexPath,
pathToLastIndex(requestIndex),
destinationCollectionIndex
)
toast.success(`${t("request.moved")}`)
// draggingToRoot.value = false
}
const dropCollection = (payload: {
collectionIndexDragged: string
destinationCollectionIndex: string
}) => {
const { collectionIndexDragged, destinationCollectionIndex } = payload
if (!collectionIndexDragged || !destinationCollectionIndex) return
if (collectionIndexDragged === destinationCollectionIndex) return
if (
checkIfCollectionIsAParentOfTheChildren(
collectionIndexDragged,
destinationCollectionIndex
)
) {
toast.error(`${t("team.parent_coll_move")}`)
return
}
//check if the collection is being moved to its own parent
if (
isMoveToSameLocation(collectionIndexDragged, destinationCollectionIndex)
) {
return
}
const parentFolder = collectionIndexDragged.split("/").slice(0, -1).join("/") // remove last folder to get parent folder
const totalFoldersOfDestinationCollection =
getFoldersByPath(restCollectionState.value, destinationCollectionIndex)
.length - (parentFolder === destinationCollectionIndex ? 1 : 0)
moveRESTFolder(collectionIndexDragged, destinationCollectionIndex)
// resolveSaveContextOnCollectionReorder(
// {
// lastIndex: pathToLastIndex(collectionIndexDragged),
// newIndex: -1,
// folderPath: parentFolder,
// length: getFoldersByPath(restCollectionState.value, parentFolder).length,
// },
// "drop"
// )
// updateSaveContextForAffectedRequests(
// collectionIndexDragged,
// `${destinationCollectionIndex}/${totalFoldersOfDestinationCollection}`
// )
const { auth, headers } = cascadeParentCollectionForHeaderAuth(
`${destinationCollectionIndex}/${totalFoldersOfDestinationCollection}`,
"rest"
)
const inheritedProperty = {
auth,
headers,
}
updateInheritedPropertiesForAffectedRequests(
`${destinationCollectionIndex}/${totalFoldersOfDestinationCollection}`,
inheritedProperty,
"rest"
)
// draggingToRoot.value = false
toast.success(`${t("collection.moved")}`)
}
const updateRequestOrder = (
dataTransfer: DataTransfer,
{
parentCollectionIndexPath,
requestIndex,
}: { parentCollectionIndexPath: string | null; requestIndex: string | null }
) => {
if (!parentCollectionIndexPath) return
const dragedRequestIndex = dataTransfer.getData("requestIndex")
const destinationRequestIndex = requestIndex
const destinationCollectionIndex = parentCollectionIndexPath
if (
!dragedRequestIndex ||
!destinationCollectionIndex ||
dragedRequestIndex === destinationRequestIndex
) {
return
}
if (
!isSameSameParent(
dragedRequestIndex,
destinationRequestIndex,
destinationCollectionIndex
)
) {
toast.error(`${t("collection.different_parent")}`)
} else {
updateRESTRequestOrder(
pathToLastIndex(dragedRequestIndex),
destinationRequestIndex ? pathToLastIndex(destinationRequestIndex) : null,
destinationCollectionIndex
)
toast.success(`${t("request.order_changed")}`)
}
}
const updateCollectionOrder = (
dataTransfer: DataTransfer,
destinationCollection: {
destinationCollectionIndex: string | null
destinationCollectionParentIndex: string | null
}
) => {
const draggedCollectionIndex = dataTransfer.getData("collectionIndex")
const { destinationCollectionIndex, destinationCollectionParentIndex } =
destinationCollection
if (
!draggedCollectionIndex ||
draggedCollectionIndex === destinationCollectionIndex
) {
return
}
if (
!isSameSameParent(
draggedCollectionIndex,
destinationCollectionIndex,
destinationCollectionParentIndex
)
) {
toast.error(`${t("collection.different_parent")}`)
} else {
updateRESTCollectionOrder(
draggedCollectionIndex,
destinationCollectionIndex
)
// resolveSaveContextOnCollectionReorder({
// lastIndex: pathToLastIndex(draggedCollectionIndex),
// newIndex: pathToLastIndex(
// destinationCollectionIndex ? destinationCollectionIndex : ""
// ),
// folderPath: draggedCollectionIndex.split("/").slice(0, -1).join("/"),
// })
toast.success(`${t("collection.order_changed")}`)
}
}
const shareRequest = (request: HoppRESTRequest) => {
if (currentUser.value) {
// Opens the share request modal if the user is logged in
@@ -1161,37 +1511,35 @@ const shareRequest = (request: HoppRESTRequest) => {
invokeAction("modals.login.toggle")
}
const resolveConfirmModal = (title: string | null) => {
if (title === `${t("confirm.remove_collection")}`) {
onRemoveRootCollection()
} else if (title === `${t("confirm.remove_request")}`) {
onRemoveRequest()
} else if (title === `${t("confirm.remove_folder")}`) {
onRemoveChildCollection()
} else {
console.error(
`Confirm modal title ${title} is not handled by the component`
)
toast.error(t("error.something_went_wrong"))
displayConfirmModal(false)
}
}
const resetSelectedData = () => {
editingCollectionIndexPath.value = ""
editingRootCollectionName.value = ""
editingChildCollectionName.value = ""
editingRequestName.value = ""
editingRequestIndexPath.value = ""
}
/**
* Generic helpers
*/
/**
* @param path The path of the collection or request
* @returns The index of the collection or request
* Used to check if the collection exist as the parent of the childrens
* @param collectionIndexDragged The index of the collection dragged
* @param destinationCollectionIndex The index of the destination collection
* @returns True if the collection exist as the parent of the childrens
*/
const pathToIndex = (path: string) => {
const pathArr = path.split("/")
return pathArr
const checkIfCollectionIsAParentOfTheChildren = (
collectionIndexDragged: string,
destinationCollectionIndex: string
) => {
const collectionDraggedPath = pathToIndex(collectionIndexDragged)
const destinationCollectionPath = pathToIndex(destinationCollectionIndex)
if (collectionDraggedPath.length < destinationCollectionPath.length) {
const slicedDestinationCollectionPath = destinationCollectionPath.slice(
0,
collectionDraggedPath.length
)
if (isEqual(slicedDestinationCollectionPath, collectionDraggedPath)) {
return true
}
return false
}
return false
}
/**
@@ -1242,4 +1590,111 @@ const getRequestIndexPathArgs = (requestIndexPath: string) => {
requestIndex,
}
}
const isMoveToSameLocation = (
draggedItemPath: string,
destinationPath: string
) => {
const draggedItemPathArr = pathToIndex(draggedItemPath)
const destinationPathArr = pathToIndex(destinationPath)
if (draggedItemPathArr.length > 0) {
const draggedItemParentPathArr = draggedItemPathArr.slice(
0,
draggedItemPathArr.length - 1
)
if (isEqual(draggedItemParentPathArr, destinationPathArr)) {
return true
}
return false
}
}
/**
* Used to check if the request/collection is being moved to the same parent since reorder is only allowed within the same parent
* @param draggedItem - path index of the dragged request
* @param destinationItem - path index of the destination request
* @param destinationCollectionIndex - index of the destination collection
* @returns boolean - true if the request is being moved to the same parent
*/
const isSameSameParent = (
draggedItemPath: string,
destinationItemPath: string | null,
destinationCollectionIndex: string | null
) => {
const draggedItemIndex = pathToIndex(draggedItemPath)
// if the destinationItemPath and destinationCollectionIndex is null, it means the request is being moved to the root
if (destinationItemPath === null && destinationCollectionIndex === null) {
return draggedItemIndex.length === 1
} else if (
destinationItemPath === null &&
destinationCollectionIndex !== null &&
draggedItemIndex.length === 1
) {
return draggedItemIndex[0] === destinationCollectionIndex
} else if (
destinationItemPath === null &&
draggedItemIndex.length !== 1 &&
destinationCollectionIndex !== null
) {
const dragedItemParent = draggedItemIndex.slice(0, -1)
return dragedItemParent.join("/") === destinationCollectionIndex
}
if (destinationItemPath === null) return false
const destinationItemIndex = pathToIndex(destinationItemPath)
// length of 1 means the request is in the root
if (draggedItemIndex.length === 1 && destinationItemIndex.length === 1) {
return true
} else if (draggedItemIndex.length === destinationItemIndex.length) {
const dragedItemParent = draggedItemIndex.slice(0, -1)
const destinationItemParent = destinationItemIndex.slice(0, -1)
if (isEqual(dragedItemParent, destinationItemParent)) {
return true
}
return false
}
return false
}
/**
* @param path The path of the collection or request
* @returns The index of the collection or request
*/
const pathToIndex = (path: string) => {
const pathArr = path.split("/")
return pathArr
}
const pathToLastIndex = (path: string) => {
const pathArr = pathToIndex(path)
return parseInt(pathArr[pathArr.length - 1])
}
const resolveConfirmModal = (title: string | null) => {
if (title === `${t("confirm.remove_collection")}`) {
onRemoveRootCollection()
} else if (title === `${t("confirm.remove_request")}`) {
onRemoveRequest()
} else if (title === `${t("confirm.remove_folder")}`) {
onRemoveChildCollection()
} else {
console.error(
`Confirm modal title ${title} is not handled by the component`
)
toast.error(t("error.something_went_wrong"))
displayConfirmModal(false)
}
}
const resetSelectedData = () => {
editingCollectionIndexPath.value = ""
editingRootCollectionName.value = ""
editingChildCollectionName.value = ""
editingRequestName.value = ""
editingRequestIndexPath.value = ""
}
</script>

View File

@@ -656,7 +656,12 @@ export class PersonalWorkspaceProviderService
type: "collection",
value: {
collectionID: `${collectionID}/${id}`,
isLastItem:
item.folders?.length > 1
? id === item.folders.length - 1
: false,
name: childColl.name,
parentCollectionID: collectionID,
},
}
})
@@ -665,6 +670,11 @@ export class PersonalWorkspaceProviderService
return <RESTCollectionViewItem>{
type: "request",
value: {
isLastItem:
item.requests?.length > 1
? id === item.requests.length - 1
: false,
parentCollectionID: collectionID,
requestID: `${collectionID}/${id}`,
request: req,
},
@@ -711,7 +721,10 @@ export class PersonalWorkspaceProviderService
return this.restCollectionState.value.state.map((coll, id) => {
return {
collectionID: id.toString(),
isLastItem:
id === this.restCollectionState.value.state.length - 1,
name: coll.name,
parentIndex: null,
}
})
}),

View File

@@ -10,7 +10,9 @@ export type RESTCollectionLevelAuthHeadersView = {
export type RESTCollectionViewCollection = {
collectionID: string
isLastItem: boolean
name: string
parentCollectionID: string | null
}
export type RESTCollectionViewRequest = {
@@ -18,6 +20,8 @@ export type RESTCollectionViewRequest = {
requestID: string
request: HoppRESTRequest
isLastItem: boolean
parentCollectionID: string | null
}
export type RESTCollectionViewItem =