Browse Source

refactor: integrate provider API methods for collection move/reorder

jamesgeorge007 1 year ago
parent
commit
16bdf48d5d

+ 1 - 0
packages/hoppscotch-common/src/components/http/Request.vue

@@ -534,6 +534,7 @@ const saveRequest = async () => {
 
     if (E.isLeft(requestHandleResult)) {
       // INVALID_COLLECTION_HANDLE | INVALID_REQUEST_ID | REQUEST_NOT_FOUND
+      showSaveRequestModal.value = true
       return
     }
 

+ 8 - 6
packages/hoppscotch-common/src/components/new-collections/rest/Collection.vue

@@ -22,8 +22,8 @@
       ></div>
       <div
         class="z-[3] group pointer-events-auto relative flex cursor-pointer items-stretch"
+        :draggable="true"
         @dragstart="dragStart"
-        @drop="handleDrop($event)"
         @dragover="handleDragOver($event)"
         @dragleave="resetDragState"
         @dragend="
@@ -32,7 +32,8 @@
             dropItemID = ''
           }
         "
-        @contextmenu.prevent="options?.tippy.show()"
+        @drop="handleDrop($event)"
+        @contextmenu.prevent="options?.tippy?.show()"
       >
         <div
           class="flex min-w-0 flex-1 items-center justify-center"
@@ -179,12 +180,12 @@
       </div>
     </div>
     <div
-      v-if="isLastItem"
+      v-if="collectionView.isLastItem"
       class="w-full transition"
       :class="[
         {
           'bg-accentDark': isLastItemReorderable,
-          'h-1 ': isLastItem,
+          'h-1 ': collectionView.isLastItem,
         },
       ]"
       @drop="updateLastItemOrder"
@@ -224,7 +225,6 @@ const props = defineProps<{
   collectionView: RESTCollectionViewCollection
   isOpen: boolean
   isSelected?: boolean | null
-  isLastItem?: boolean
 }>()
 
 const emit = defineEmits<{
@@ -349,8 +349,10 @@ const editCollection = () => {
 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,
@@ -376,7 +378,7 @@ const handleDragOver = (e: DragEvent) => {
     notSameDestination.value &&
     !isRequestDragging.value &&
     isSameParent.value &&
-    props.isLastItem
+    props.collectionView.isLastItem
   ) {
     orderingLastItem.value = true
     dragging.value = false

+ 146 - 2
packages/hoppscotch-common/src/components/new-collections/rest/Request.vue

@@ -1,8 +1,27 @@
 <template>
   <div class="flex flex-col">
+    <div
+      class="h-1 w-full transition"
+      :class="[
+        {
+          'bg-accentDark': isReorderable,
+        },
+      ]"
+      @drop="updateRequestOrder"
+      @dragover.prevent="ordering = true"
+      @dragleave="resetDragState"
+      @dragend="resetDragState"
+    ></div>
+
     <div
       class="group flex items-stretch"
-      @contextmenu.prevent="options?.tippy.show()"
+      :draggable="true"
+      @dragstart="dragStart"
+      @dragover="handleDragOver($event)"
+      @dragleave="resetDragState"
+      @dragend="resetDragState"
+      @drop="handleDrop"
+      @contextmenu.prevent="options?.tippy?.show()"
     >
       <div
         class="pointer-events-auto flex min-w-0 flex-1 cursor-pointer items-center justify-center"
@@ -45,6 +64,7 @@
           </span>
         </span>
       </div>
+
       <div class="flex">
         <HoppButtonSecondary
           v-tippy="{ theme: 'tooltip' }"
@@ -59,7 +79,7 @@
             interactive
             trigger="click"
             theme="popover"
-            :on-shown="() => tippyActions!.focus()"
+            :on-shown="() => tippyActions.focus()"
           >
             <HoppButtonSecondary
               v-tippy="{ theme: 'tooltip' }"
@@ -134,6 +154,20 @@
         </span>
       </div>
     </div>
+
+    <div
+      class="w-full transition"
+      :class="[
+        {
+          'bg-accentDark': isLastItemReorderable,
+          'h-1 ': props.requestView.isLastItem,
+        },
+      ]"
+      @drop="handleDrop"
+      @dragover.prevent="orderingLastItem = true"
+      @dragleave="resetDragState"
+      @dragend="resetDragState"
+    ></div>
   </div>
 </template>
 
@@ -142,8 +176,13 @@ import { useI18n } from "@composables/i18n"
 import { HoppRESTRequest } from "@hoppscotch/data"
 import { computed, ref } from "vue"
 import { TippyComponent } from "vue-tippy"
+import { useReadonlyStream } from "~/composables/stream"
 
 import { getMethodLabelColorClassOf } from "~/helpers/rest/labelColoring"
+import {
+  currentReorderingStatus$,
+  changeCurrentReorderStatus,
+} from "~/newstore/reordering"
 import { RESTCollectionViewRequest } from "~/services/new-workspace/view"
 
 import IconCheckCircle from "~icons/lucide/check-circle"
@@ -156,6 +195,12 @@ import IconTrash2 from "~icons/lucide/trash-2"
 
 const t = useI18n()
 
+const currentReorderingStatus = useReadonlyStream(currentReorderingStatus$, {
+  type: "collection",
+  id: "",
+  parentID: "",
+})
+
 const props = defineProps<{
   isActive: boolean
   requestView: RESTCollectionViewRequest
@@ -174,6 +219,9 @@ const emit = defineEmits<{
   (event: "remove-request", requestIndexPath: string): void
   (event: "select-request", requestIndexPath: string): void
   (event: "share-request", request: HoppRESTRequest): void
+  (event: "drag-request", payload: DataTransfer): void
+  (event: "update-request-order", payload: DataTransfer): void
+  (event: "update-last-request-order", payload: DataTransfer): void
 }>()
 
 const tippyActions = ref<TippyComponent | null>(null)
@@ -183,9 +231,105 @@ const options = ref<TippyComponent | null>(null)
 const duplicate = ref<HTMLButtonElement | null>(null)
 const shareAction = ref<HTMLButtonElement | null>(null)
 
+const dragging = ref(false)
+const ordering = ref(false)
+const orderingLastItem = ref(false)
+
+const isCollectionDragging = computed(() => {
+  return currentReorderingStatus.value.type === "collection"
+})
+
+const isLastItemReorderable = computed(() => {
+  return (
+    orderingLastItem.value && isSameParent.value && !isCollectionDragging.value
+  )
+})
+
+const isReorderable = computed(() => {
+  return (
+    ordering.value &&
+    !isCollectionDragging.value &&
+    isSameParent.value &&
+    !isSameRequest.value
+  )
+})
+
+const isSameParent = computed(() => {
+  return (
+    currentReorderingStatus.value.parentID ===
+    props.requestView.parentCollectionID
+  )
+})
+
+const isSameRequest = computed(() => {
+  return currentReorderingStatus.value.id === props.requestView.requestID
+})
+
 const requestLabelColor = computed(() =>
   getMethodLabelColorClassOf(props.requestView.request)
 )
 
+const dragStart = ({ dataTransfer }: DragEvent) => {
+  if (dataTransfer) {
+    emit("drag-request", dataTransfer)
+    dragging.value = !dragging.value
+
+    changeCurrentReorderStatus({
+      type: "request",
+      id: props.requestView.requestID,
+      parentID: props.requestView.parentCollectionID,
+    })
+  }
+}
+
+const handleDrop = (e: DragEvent) => {
+  if (ordering.value) {
+    updateRequestOrder(e)
+  } else if (orderingLastItem.value) {
+    updateLastItemOrder(e)
+  } else {
+    updateRequestOrder(e)
+  }
+}
+
+// Trigger the re-ordering event when a request is dragged over another request's top section
+const handleDragOver = (e: DragEvent) => {
+  dragging.value = true
+  if (e.offsetY < 10) {
+    ordering.value = true
+    dragging.value = false
+    orderingLastItem.value = false
+  } else if (e.offsetY > 18) {
+    orderingLastItem.value = true
+    dragging.value = false
+    ordering.value = false
+  } else {
+    ordering.value = false
+    orderingLastItem.value = false
+  }
+}
+
+const resetDragState = () => {
+  dragging.value = false
+  ordering.value = false
+  orderingLastItem.value = false
+}
+
 const selectRequest = () => emit("select-request", props.requestView.requestID)
+
+const updateRequestOrder = (e: DragEvent) => {
+  if (e.dataTransfer) {
+    e.stopPropagation()
+    resetDragState()
+    emit("update-request-order", e.dataTransfer)
+  }
+}
+
+const updateLastItemOrder = (e: DragEvent) => {
+  if (e.dataTransfer) {
+    e.stopPropagation()
+    resetDragState()
+    emit("update-last-request-order", e.dataTransfer)
+  }
+}
 </script>

+ 406 - 172
packages/hoppscotch-common/src/components/new-collections/rest/index.vue

@@ -12,9 +12,10 @@
   >
     <div
       class="sticky z-10 flex flex-shrink-0 flex-col overflow-x-auto border-b border-dividerLight bg-primary"
-      :style="{
-        top: 0,
-      }"
+      :class="{ 'rounded-t': saveRequest }"
+      :style="
+        saveRequest ? 'top: calc(-1 * var(--line-height-body))' : 'top: 0'
+      "
     >
       <WorkspaceCurrent :section="t('tab.collections')" />
       <input
@@ -25,6 +26,7 @@
         :placeholder="t('action.search')"
       />
     </div>
+
     <div class="flex flex-1 flex-col">
       <div
         class="sticky z-10 flex flex-1 justify-between border-b border-dividerLight bg-primary"
@@ -66,7 +68,6 @@
             <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(
@@ -126,7 +127,6 @@
             <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))
               "
@@ -168,12 +168,14 @@
         </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',
-      }"
-    > -->
+      <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',
+        }"
+      >
+        <icon-lucide-list-end class="svg-icons !h-8 !w-8" />
+      </div>
 
       <CollectionsAdd
         :show="showModalAdd"
@@ -258,20 +260,22 @@ import { WorkspaceRESTCollectionTreeAdapter } from "~/helpers/adapters/Workspace
 import { TeamCollection } from "~/helpers/backend/graphql"
 import {
   getFoldersByPath,
+  resolveSaveContextOnCollectionReorder,
   updateInheritedPropertiesForAffectedRequests,
+  updateSaveContextForAffectedRequests,
 } from "~/helpers/collection/collection"
+import {
+  getRequestsByPath,
+  resolveSaveContextOnRequestReorder,
+} from "~/helpers/collection/request"
 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 { currentReorderingStatus$ } from "~/newstore/reordering"
 import { platform } from "~/platform"
 import { NewWorkspaceService } from "~/services/new-workspace"
 import { HandleRef } from "~/services/new-workspace/handle"
@@ -281,7 +285,6 @@ import { RESTTabService } from "~/services/tab/rest"
 import IconImport from "~icons/lucide/folder-down"
 import IconHelpCircle from "~icons/lucide/help-circle"
 import IconPlus from "~icons/lucide/plus"
-import { currentReorderingStatus$ } from "~/newstore/reordering"
 
 const t = useI18n()
 const toast = useToast()
@@ -296,40 +299,7 @@ const props = defineProps<{
 const emit = defineEmits<{
   (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)
@@ -1301,7 +1271,7 @@ const dragRequest = (
   dataTransfer.setData("requestIndex", requestIndex)
 }
 
-const dropEvent = (
+const dropEvent = async (
   dataTransfer: DataTransfer,
   destinationCollectionIndex: string
 ) => {
@@ -1309,22 +1279,17 @@ const dropEvent = (
     "parentCollectionIndexPath"
   )
   const requestIndex = dataTransfer.getData("requestIndex")
-  const collectionIndexDragged = dataTransfer.getData("collectionIndex")
+  const draggedCollectionIndex = dataTransfer.getData("collectionIndex")
 
   if (parentCollectionIndexPath && requestIndex) {
-    // emit("drop-request", {
-    //   parentCollectionIndexPath,
-    //   requestIndex,
-    //   destinationCollectionIndex,
-    // })
-    dropRequest({
+    await dropRequest({
       parentCollectionIndexPath,
       requestIndex,
       destinationCollectionIndex,
     })
   } else {
-    dropCollection({
-      collectionIndexDragged,
+    await dropCollection({
+      draggedCollectionIndex,
       destinationCollectionIndex,
     })
   }
@@ -1335,23 +1300,115 @@ const dropEvent = (
  * 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")}`)
-    }
+const dropToRoot = async ({ dataTransfer }: DragEvent) => {
+  if (!dataTransfer) {
+    return
+  }
+
+  const draggedCollectionIndex = dataTransfer.getData("collectionIndex")
+  if (!draggedCollectionIndex) {
+    return
+  }
 
+  // check if the collection is already in the root
+  if (isAlreadyInRoot(draggedCollectionIndex)) {
+    toast.error(`${t("collection.invalid_root_move")}`)
     draggingToRoot.value = false
+    return
+  }
+
+  const draggedCollectionHandleResult =
+    await workspaceService.getCollectionHandle(
+      props.workspaceHandle,
+      draggedCollectionIndex
+    )
+
+  if (E.isLeft(draggedCollectionHandleResult)) {
+    // INVALID_WORKSPACE_HANDLE | INVALID_COLLECTION_ID | INVALID_PATH
+    return
+  }
+
+  const draggedCollectionHandle = draggedCollectionHandleResult.right
+
+  if (draggedCollectionHandle.value.type === "invalid") {
+    // WORKSPACE_INVALIDATED
+    return
+  }
+
+  const result = await workspaceService.moveRESTCollection(
+    draggedCollectionHandle,
+    null
+  )
+
+  if (E.isLeft(result)) {
+    // INVALID_COLLECTION_HANDLE
+    return
+  }
+
+  const destinationRootCollectionIndex = (
+    restCollectionState.value.length - 1
+  ).toString()
+
+  updateSaveContextForAffectedRequests(
+    draggedCollectionIndex,
+    destinationRootCollectionIndex
+  )
+
+  const destinationRootCollectionHandleResult =
+    await workspaceService.getCollectionHandle(
+      props.workspaceHandle,
+      destinationRootCollectionIndex
+    )
+
+  if (E.isLeft(destinationRootCollectionHandleResult)) {
+    // INVALID_WORKSPACE_HANDLE | INVALID_COLLECTION_ID | INVALID_PATH
+    return
+  }
+
+  const destinationRootCollectionHandle =
+    destinationRootCollectionHandleResult.right
+
+  if (destinationRootCollectionHandle.value.type === "invalid") {
+    // WORKSPACE_INVALIDATED
+    return
+  }
+
+  const cascadingAuthHeadersHandleResult =
+    await workspaceService.getRESTCollectionLevelAuthHeadersView(
+      destinationRootCollectionHandle
+    )
+
+  if (E.isLeft(cascadingAuthHeadersHandleResult)) {
+    // INVALID_COLLECTION_HANDLE
+    return
   }
+
+  const cascadingAuthHeadersHandle = cascadingAuthHeadersHandleResult.right
+
+  if (cascadingAuthHeadersHandle.value.type === "invalid") {
+    // COLLECTION_INVALIDATED
+    return
+  }
+
+  const { auth, headers } = cascadingAuthHeadersHandle.value.data
+
+  const inheritedProperty = {
+    auth,
+    headers,
+  }
+
+  updateInheritedPropertiesForAffectedRequests(
+    destinationRootCollectionIndex,
+    inheritedProperty,
+    "rest"
+  )
+
+  toast.success(`${t("collection.moved")}`)
+
+  draggingToRoot.value = false
 }
 
-const dropRequest = (payload: {
+const dropRequest = async (payload: {
   parentCollectionIndexPath?: string | undefined
   requestIndex: string
   destinationCollectionIndex: string
@@ -1366,65 +1423,137 @@ const dropRequest = (payload: {
     !requestIndex ||
     !destinationCollectionIndex ||
     !parentCollectionIndexPath
+  ) {
+    return
+  }
+
+  const requestHandleResult = await workspaceService.getRequestHandle(
+    props.workspaceHandle,
+    requestIndex
   )
+
+  if (E.isLeft(requestHandleResult)) {
+    // INVALID_COLLECTION_HANDLE | INVALID_REQUEST_ID | REQUEST_NOT_FOUND
     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,
-  //   }
-  // }
+  const requestHandle = requestHandleResult.right
 
-  // 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),
+  if (requestHandle.value.type === "invalid") {
+    // COLLECTION_INVALIDATED
+    return
+  }
+
+  const result = await workspaceService.moveRESTRequest(
+    requestHandle,
     destinationCollectionIndex
   )
 
+  if (E.isLeft(result)) {
+    // INVALID_REQUEST_HANDLE
+    return
+  }
+
+  const collectionHandleResult = await workspaceService.getCollectionHandle(
+    props.workspaceHandle,
+    destinationCollectionIndex
+  )
+
+  if (E.isLeft(collectionHandleResult)) {
+    // INVALID_WORKSPACE_HANDLE | INVALID_COLLECTION_ID | INVALID_PATH
+    return
+  }
+
+  const collectionHandle = collectionHandleResult.right
+
+  if (collectionHandle.value.type === "invalid") {
+    // WORKSPACE_INVALIDATED
+    return
+  }
+
+  const cascadingAuthHeadersHandleResult =
+    await workspaceService.getRESTCollectionLevelAuthHeadersView(
+      collectionHandle
+    )
+
+  if (E.isLeft(cascadingAuthHeadersHandleResult)) {
+    // INVALID_COLLECTION_HANDLE
+    return
+  }
+
+  const cascadingAuthHeadersHandle = cascadingAuthHeadersHandleResult.right
+
+  if (cascadingAuthHeadersHandle.value.type === "invalid") {
+    // COLLECTION_INVALIDATED
+    return
+  }
+
+  const { auth, headers } = cascadingAuthHeadersHandle.value.data
+
+  const { collectionID, providerID, requestID, workspaceID } =
+    requestHandle.value.data
+
+  const possibleTab = tabs.getTabRefWithSaveContext({
+    originLocation: "workspace-user-collection",
+    workspaceID,
+    providerID,
+    collectionID,
+    requestID,
+  })
+
+  // If there is a tab attached to this request, update its save context
+  if (possibleTab) {
+    const newCollectionID = destinationCollectionIndex
+    const newRequestID = `${destinationCollectionIndex}/${(
+      getRequestsByPath(restCollectionState.value, destinationCollectionIndex)
+        .length - 1
+    ).toString()}`
+
+    possibleTab.value.document.saveContext = {
+      originLocation: "workspace-user-collection",
+      workspaceID,
+      providerID,
+      collectionID: newCollectionID,
+      requestID: newRequestID,
+    }
+
+    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: parentCollectionIndexPath,
+    length:
+      getRequestsByPath(restCollectionState.value, parentCollectionIndexPath)
+        .length - 1,
+  })
+
   toast.success(`${t("request.moved")}`)
-  // draggingToRoot.value = false
+  draggingToRoot.value = false
 }
 
-const dropCollection = (payload: {
-  collectionIndexDragged: string
+const dropCollection = async (payload: {
+  draggedCollectionIndex: string
   destinationCollectionIndex: string
 }) => {
-  const { collectionIndexDragged, destinationCollectionIndex } = payload
-  if (!collectionIndexDragged || !destinationCollectionIndex) return
-  if (collectionIndexDragged === destinationCollectionIndex) return
+  const { draggedCollectionIndex, destinationCollectionIndex } = payload
+
+  if (
+    !draggedCollectionIndex ||
+    !destinationCollectionIndex ||
+    draggedCollectionIndex === destinationCollectionIndex
+  ) {
+    return
+  }
 
   if (
     checkIfCollectionIsAParentOfTheChildren(
-      collectionIndexDragged,
+      draggedCollectionIndex,
       destinationCollectionIndex
     )
   ) {
@@ -1434,38 +1563,96 @@ const dropCollection = (payload: {
 
   //check if the collection is being moved to its own parent
   if (
-    isMoveToSameLocation(collectionIndexDragged, destinationCollectionIndex)
+    isMoveToSameLocation(draggedCollectionIndex, destinationCollectionIndex)
   ) {
     return
   }
 
-  const parentFolder = collectionIndexDragged.split("/").slice(0, -1).join("/") // remove last folder to get parent folder
+  const parentFolder = draggedCollectionIndex.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)
+  const draggedCollectionHandleResult =
+    await workspaceService.getCollectionHandle(
+      props.workspaceHandle,
+      draggedCollectionIndex
+    )
+
+  if (E.isLeft(draggedCollectionHandleResult)) {
+    // INVALID_WORKSPACE_HANDLE | INVALID_COLLECTION_ID | INVALID_PATH
+    return
+  }
+
+  const draggedCollectionHandle = draggedCollectionHandleResult.right
 
-  // resolveSaveContextOnCollectionReorder(
-  //   {
-  //     lastIndex: pathToLastIndex(collectionIndexDragged),
-  //     newIndex: -1,
-  //     folderPath: parentFolder,
-  //     length: getFoldersByPath(restCollectionState.value, parentFolder).length,
-  //   },
-  //   "drop"
-  // )
+  if (draggedCollectionHandle.value.type === "invalid") {
+    // WORKSPACE_INVALIDATED
+    return
+  }
 
-  // updateSaveContextForAffectedRequests(
-  //   collectionIndexDragged,
-  //   `${destinationCollectionIndex}/${totalFoldersOfDestinationCollection}`
-  // )
+  const result = await workspaceService.moveRESTCollection(
+    draggedCollectionHandle,
+    destinationCollectionIndex
+  )
 
-  const { auth, headers } = cascadeParentCollectionForHeaderAuth(
-    `${destinationCollectionIndex}/${totalFoldersOfDestinationCollection}`,
-    "rest"
+  if (E.isLeft(result)) {
+    // INVALID_COLLECTION_HANDLE
+    return
+  }
+
+  resolveSaveContextOnCollectionReorder(
+    {
+      lastIndex: pathToLastIndex(draggedCollectionIndex),
+      newIndex: -1,
+      folderPath: parentFolder,
+      length: getFoldersByPath(restCollectionState.value, parentFolder).length,
+    },
+    "drop"
+  )
+
+  updateSaveContextForAffectedRequests(
+    draggedCollectionIndex,
+    `${destinationCollectionIndex}/${totalFoldersOfDestinationCollection}`
   )
 
+  const destinationCollectionHandleResult =
+    await workspaceService.getCollectionHandle(
+      props.workspaceHandle,
+      destinationCollectionIndex
+    )
+
+  if (E.isLeft(destinationCollectionHandleResult)) {
+    // INVALID_WORKSPACE_HANDLE | INVALID_COLLECTION_ID | INVALID_PATH
+    return
+  }
+
+  const destinationCollectionHandle = destinationCollectionHandleResult.right
+
+  if (destinationCollectionHandle.value.type === "invalid") {
+    // WORKSPACE_INVALIDATED
+    return
+  }
+
+  const cascadingAuthHeadersHandleResult =
+    await workspaceService.getRESTCollectionLevelAuthHeadersView(
+      destinationCollectionHandle
+    )
+
+  if (E.isLeft(cascadingAuthHeadersHandleResult)) {
+    // INVALID_COLLECTION_HANDLE
+    return
+  }
+
+  const cascadingAuthHeadersHandle = cascadingAuthHeadersHandleResult.right
+
+  if (cascadingAuthHeadersHandle.value.type === "invalid") {
+    // COLLECTION_INVALIDATED
+    return
+  }
+
+  const { auth, headers } = cascadingAuthHeadersHandle.value.data
+
   const inheritedProperty = {
     auth,
     headers,
@@ -1477,50 +1664,75 @@ const dropCollection = (payload: {
     "rest"
   )
 
-  // draggingToRoot.value = false
+  draggingToRoot.value = false
   toast.success(`${t("collection.moved")}`)
 }
 
-const updateRequestOrder = (
+const updateRequestOrder = async (
   dataTransfer: DataTransfer,
   {
     parentCollectionIndexPath,
     requestIndex,
   }: { parentCollectionIndexPath: string | null; requestIndex: string | null }
 ) => {
-  if (!parentCollectionIndexPath) return
-  const dragedRequestIndex = dataTransfer.getData("requestIndex")
+  if (!parentCollectionIndexPath) {
+    return
+  }
+
+  const draggedRequestIndex = dataTransfer.getData("requestIndex")
   const destinationRequestIndex = requestIndex
   const destinationCollectionIndex = parentCollectionIndexPath
 
   if (
-    !dragedRequestIndex ||
+    !draggedRequestIndex ||
     !destinationCollectionIndex ||
-    dragedRequestIndex === destinationRequestIndex
+    draggedRequestIndex === destinationRequestIndex
   ) {
     return
   }
 
   if (
     !isSameSameParent(
-      dragedRequestIndex,
+      draggedRequestIndex,
       destinationRequestIndex,
       destinationCollectionIndex
     )
   ) {
-    toast.error(`${t("collection.different_parent")}`)
-  } else {
-    updateRESTRequestOrder(
-      pathToLastIndex(dragedRequestIndex),
-      destinationRequestIndex ? pathToLastIndex(destinationRequestIndex) : null,
-      destinationCollectionIndex
-    )
+    return toast.error(`${t("collection.different_parent")}`)
+  }
+
+  const requestHandleResult = await workspaceService.getRequestHandle(
+    props.workspaceHandle,
+    draggedRequestIndex
+  )
 
-    toast.success(`${t("request.order_changed")}`)
+  if (E.isLeft(requestHandleResult)) {
+    // INVALID_COLLECTION_HANDLE | INVALID_REQUEST_ID | REQUEST_NOT_FOUND
+    return
   }
+
+  const requestHandle = requestHandleResult.right
+
+  if (requestHandle.value.type === "invalid") {
+    // COLLECTION_INVALIDATED
+    return
+  }
+
+  const result = await workspaceService.reorderRESTRequest(
+    requestHandle,
+    destinationCollectionIndex,
+    destinationRequestIndex
+  )
+
+  if (E.isLeft(result)) {
+    // INVALID_REQUEST_HANDLE
+    return
+  }
+
+  toast.success(`${t("request.order_changed")}`)
 }
 
-const updateCollectionOrder = (
+const updateCollectionOrder = async (
   dataTransfer: DataTransfer,
   destinationCollection: {
     destinationCollectionIndex: string | null
@@ -1546,21 +1758,43 @@ const updateCollectionOrder = (
       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")}`)
+    return toast.error(`${t("collection.different_parent")}`)
   }
+
+  const collectionHandleResult = await workspaceService.getCollectionHandle(
+    props.workspaceHandle,
+    draggedCollectionIndex
+  )
+
+  if (E.isLeft(collectionHandleResult)) {
+    // INVALID_WORKSPACE_HANDLE | INVALID_COLLECTION_ID | INVALID_PATH
+    return
+  }
+
+  const collectionHandle = collectionHandleResult.right
+
+  if (collectionHandle.value.type === "invalid") {
+    // WORKSPACE_INVALIDATED
+    return
+  }
+
+  const result = await workspaceService.reorderRESTCollection(
+    collectionHandle,
+    destinationCollectionIndex
+  )
+
+  if (E.isLeft(result)) {
+    // INVALID_COLLECTION_HANDLE
+    return
+  }
+
+  resolveSaveContextOnCollectionReorder({
+    lastIndex: pathToLastIndex(draggedCollectionIndex),
+    newIndex: pathToLastIndex(destinationCollectionIndex ?? ""),
+    folderPath: draggedCollectionIndex.split("/").slice(0, -1).join("/"),
+  })
+
+  toast.success(`${t("collection.order_changed")}`)
 }
 
 const shareRequest = (request: HoppRESTRequest) => {
@@ -1579,15 +1813,15 @@ const shareRequest = (request: HoppRESTRequest) => {
 
 /**
  * Used to check if the collection exist as the parent of the childrens
- * @param collectionIndexDragged The index of the collection dragged
+ * @param draggedCollectionIndex 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 checkIfCollectionIsAParentOfTheChildren = (
-  collectionIndexDragged: string,
+  draggedCollectionIndex: string,
   destinationCollectionIndex: string
 ) => {
-  const collectionDraggedPath = pathToIndex(collectionIndexDragged)
+  const collectionDraggedPath = pathToIndex(draggedCollectionIndex)
   const destinationCollectionPath = pathToIndex(destinationCollectionIndex)
 
   if (collectionDraggedPath.length < destinationCollectionPath.length) {
@@ -1701,9 +1935,9 @@ const isSameSameParent = (
     draggedItemIndex.length !== 1 &&
     destinationCollectionIndex !== null
   ) {
-    const dragedItemParent = draggedItemIndex.slice(0, -1)
+    const draggedItemParent = draggedItemIndex.slice(0, -1)
 
-    return dragedItemParent.join("/") === destinationCollectionIndex
+    return draggedItemParent.join("/") === destinationCollectionIndex
   }
   if (destinationItemPath === null) return false
   const destinationItemIndex = pathToIndex(destinationItemPath)
@@ -1712,9 +1946,9 @@ const isSameSameParent = (
   if (draggedItemIndex.length === 1 && destinationItemIndex.length === 1) {
     return true
   } else if (draggedItemIndex.length === destinationItemIndex.length) {
-    const dragedItemParent = draggedItemIndex.slice(0, -1)
+    const draggedItemParent = draggedItemIndex.slice(0, -1)
     const destinationItemParent = destinationItemIndex.slice(0, -1)
-    if (isEqual(dragedItemParent, destinationItemParent)) {
+    if (isEqual(draggedItemParent, destinationItemParent)) {
       return true
     }
     return false

+ 18 - 6
packages/hoppscotch-common/src/helpers/collection/collection.ts

@@ -87,10 +87,15 @@ export function resolveSaveContextOnCollectionReorder(
       tab.value.document.saveContext?.originLocation ===
       "workspace-user-collection"
     ) {
-      const newPath = affectedPaths.get(
+      const newCollectionID = affectedPaths.get(
         tab.value.document.saveContext?.collectionID
       )!
-      tab.value.document.saveContext.collectionID = newPath
+      const newRequestID = `${newCollectionID}/${
+        tab.value.document.saveContext.requestID.split("/").slice(-1)[0]
+      }`
+
+      tab.value.document.saveContext.collectionID = newCollectionID
+      tab.value.document.saveContext.requestID = newRequestID
     }
   }
 }
@@ -134,12 +139,19 @@ export function updateSaveContextForAffectedRequests(
       tab.value.document.saveContext?.originLocation ===
       "workspace-user-collection"
     ) {
-      tab.value.document.saveContext = {
-        ...tab.value.document.saveContext,
-        collectionID: tab.value.document.saveContext.collectionID.replace(
+      const newCollectionID =
+        tab.value.document.saveContext.collectionID.replace(
           oldFolderPath,
           newFolderPath
-        ),
+        )
+      const newRequestID = `${newCollectionID}/${
+        tab.value.document.saveContext.requestID.split("/").slice(-1)[0]
+      }`
+
+      tab.value.document.saveContext = {
+        ...tab.value.document.saveContext,
+        collectionID: newCollectionID,
+        requestID: newRequestID,
       }
     }
   }

+ 13 - 0
packages/hoppscotch-common/src/helpers/collection/request.ts

@@ -63,6 +63,19 @@ export function resolveSaveContextOnRequestReorder(payload: {
       )!
       tab.value.document.saveContext.requestIndex = newIndex
     }
+
+    if (
+      tab.value.document.saveContext?.originLocation ===
+      "workspace-user-collection"
+    ) {
+      const requestID = tab.value.document.saveContext.requestID
+      const requestIDArray = requestID.split("/")
+      const requestIndex = affectedIndexes.get(
+        parseInt(requestIDArray[requestIDArray.length - 1])
+      )!
+      requestIDArray[requestIDArray.length - 1] = requestIndex.toString()
+      tab.value.document.saveContext.requestID = requestIDArray.join("/")
+    }
   }
 }
 

+ 6 - 6
packages/hoppscotch-common/src/services/new-workspace/index.ts

@@ -478,7 +478,7 @@ export class NewWorkspaceService extends Service {
 
   public async reorderRESTCollection(
     collectionHandle: HandleRef<WorkspaceCollection>,
-    destinationCollectionIndex: string
+    destinationCollectionIndex: string | null
   ): Promise<
     E.Either<WorkspaceError<"INVALID_HANDLE" | "INVALID_PROVIDER">, void>
   > {
@@ -508,7 +508,7 @@ export class NewWorkspaceService extends Service {
 
   public async moveRESTCollection(
     collectionHandle: HandleRef<WorkspaceCollection>,
-    destinationCollectionIndex: string
+    destinationCollectionIndex: string | null
   ): Promise<
     E.Either<WorkspaceError<"INVALID_HANDLE" | "INVALID_PROVIDER">, void>
   > {
@@ -538,8 +538,8 @@ export class NewWorkspaceService extends Service {
 
   public async reorderRESTRequest(
     requestHandle: HandleRef<WorkspaceRequest>,
-    destinationRequestIndex: string,
-    destinationCollectionIndex: string
+    destinationCollectionIndex: string,
+    destinationRequestIndex: string | null
   ): Promise<
     E.Either<WorkspaceError<"INVALID_HANDLE" | "INVALID_PROVIDER">, void>
   > {
@@ -557,8 +557,8 @@ export class NewWorkspaceService extends Service {
 
     const result = await provider.reorderRESTRequest(
       requestHandle,
-      destinationRequestIndex,
-      destinationCollectionIndex
+      destinationCollectionIndex,
+      destinationRequestIndex
     )
 
     if (E.isLeft(result)) {

+ 3 - 3
packages/hoppscotch-common/src/services/new-workspace/provider.ts

@@ -83,7 +83,7 @@ export interface WorkspaceProvider {
 
   reorderRESTCollection(
     collectionHandle: HandleRef<WorkspaceCollection>,
-    destinationCollectionIndex: string
+    destinationCollectionIndex: string | null
   ): Promise<E.Either<unknown, void>>
   moveRESTCollection(
     collectionHandle: HandleRef<WorkspaceCollection>,
@@ -91,8 +91,8 @@ export interface WorkspaceProvider {
   ): Promise<E.Either<unknown, void>>
   reorderRESTRequest(
     requestHandle: HandleRef<WorkspaceRequest>,
-    destinationRequestIndex: string,
-    destinationCollectionIndex: string
+    destinationCollectionIndex: string,
+    destinationRequestIndex: string | null
   ): Promise<E.Either<unknown, void>>
   moveRESTRequest(
     requestHandle: HandleRef<WorkspaceRequest>,

+ 7 - 108
packages/hoppscotch-common/src/services/new-workspace/providers/personal.workspace.ts

@@ -16,7 +16,6 @@ import {
   addRESTCollection,
   addRESTFolder,
   appendRESTCollections,
-  cascadeParentCollectionForHeaderAuth,
   editRESTCollection,
   editRESTFolder,
   editRESTRequest,
@@ -52,20 +51,10 @@ import { HoppRESTRequest } from "@hoppscotch/data"
 import { merge } from "lodash-es"
 import path from "path"
 import { HoppGQLHeader } from "~/helpers/graphql"
+import { initializeDownloadFile } from "~/helpers/import-export/export"
 import { HoppInheritedProperty } from "~/helpers/types/HoppInheritedProperties"
 import IconUser from "~icons/lucide/user"
 import { NewWorkspaceService } from ".."
-import { initializeDownloadFile } from "~/helpers/import-export/export"
-import {
-  getFoldersByPath,
-  resolveSaveContextOnCollectionReorder,
-  updateInheritedPropertiesForAffectedRequests,
-  updateSaveContextForAffectedRequests,
-} from "~/helpers/collection/collection"
-import {
-  getRequestsByPath,
-  resolveSaveContextOnRequestReorder,
-} from "~/helpers/collection/request"
 
 export class PersonalWorkspaceProviderService
   extends Service
@@ -492,7 +481,7 @@ export class PersonalWorkspaceProviderService
 
   public reorderRESTCollection(
     collectionHandle: HandleRef<WorkspaceCollection>,
-    destinationCollectionIndex: string
+    destinationCollectionIndex: string | null
   ): Promise<E.Either<unknown, void>> {
     if (
       collectionHandle.value.type !== "ok" ||
@@ -508,11 +497,6 @@ export class PersonalWorkspaceProviderService
       draggedCollectionIndex,
       destinationCollectionIndex
     )
-    resolveSaveContextOnCollectionReorder({
-      lastIndex: this.pathToLastIndex(draggedCollectionIndex),
-      newIndex: this.pathToLastIndex(destinationCollectionIndex ?? ""),
-      folderPath: draggedCollectionIndex.split("/").slice(0, -1).join("/"),
-    })
 
     return Promise.resolve(E.right(undefined))
   }
@@ -529,64 +513,18 @@ export class PersonalWorkspaceProviderService
       return Promise.resolve(E.left("INVALID_COLLECTION_HANDLE" as const))
     }
 
-    const draggedCollectionIndex = collectionHandle.value.data.collectionID
-
-    moveRESTFolder(draggedCollectionIndex, destinationCollectionIndex)
-
-    if (destinationCollectionIndex === null) {
-      return Promise.resolve(E.right(undefined))
-    }
-
-    const parentFolder = draggedCollectionIndex
-      .split("/")
-      .slice(0, -1)
-      .join("/") // remove last folder to get parent folder
-
-    const totalFoldersOfDestinationCollection =
-      getFoldersByPath(
-        restCollectionStore.value.state,
-        destinationCollectionIndex
-      ).length - (parentFolder === destinationCollectionIndex ? 1 : 0)
-
-    resolveSaveContextOnCollectionReorder(
-      {
-        lastIndex: this.pathToLastIndex(draggedCollectionIndex),
-        newIndex: -1,
-        folderPath: parentFolder,
-        length: getFoldersByPath(restCollectionStore.value.state, parentFolder)
-          .length,
-      },
-      "drop"
-    )
-
-    updateSaveContextForAffectedRequests(
-      draggedCollectionIndex,
-      `${destinationCollectionIndex}/${totalFoldersOfDestinationCollection}`
+    moveRESTFolder(
+      collectionHandle.value.data.collectionID,
+      destinationCollectionIndex
     )
 
-    // const { auth, headers } = cascadeParentCollectionForHeaderAuth(
-    //   `${destinationCollectionIndex}/${totalFoldersOfDestinationCollection}`,
-    //   "rest"
-    // )
-
-    // const inheritedProperty = {
-    //   auth,
-    //   headers,
-    // }
-
-    // updateInheritedPropertiesForAffectedRequests(
-    //   `${destinationCollectionIndex}/${totalFoldersOfDestinationCollection}`,
-    //   inheritedProperty,
-    //   "rest"
-    // )
-
     return Promise.resolve(E.right(undefined))
   }
 
   public reorderRESTRequest(
     requestHandle: HandleRef<WorkspaceRequest>,
     destinationCollectionIndex: string,
-    destinationRequestIndex: string
+    destinationRequestIndex: string | null
   ): Promise<E.Either<unknown, void>> {
     if (
       requestHandle.value.type !== "ok" ||
@@ -627,45 +565,6 @@ export class PersonalWorkspaceProviderService
       .slice(0, -1)
       .join("/")
 
-    // const { auth, headers } = cascadeParentCollectionForHeaderAuth(
-    //   destinationCollectionIndex,
-    //   "rest"
-    // )
-
-    // const possibleTab = tabs.getTabRefWithSaveContext({
-    //   originLocation: "user-collection",
-    //   folderPath: parentCollectionIndexPath,
-    //   requestIndex: this.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(
-    //       restCollectionStore.value.state,
-    //       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: this.pathToLastIndex(requestIndex),
-    //   newIndex: -1, // being deleted from last folder
-    //   folderPath: parentCollectionIndexPath,
-    //   length: getRequestsByPath(
-    //     restCollectionStore.value.state,
-    //     parentCollectionIndexPath
-    //   ).length,
-    // })
-
     moveRESTRequest(
       parentCollectionIndexPath,
       this.pathToLastIndex(requestIndex),
@@ -924,7 +823,7 @@ export class PersonalWorkspaceProviderService
                     isLastItem:
                       id === this.restCollectionState.value.state.length - 1,
                     name: coll.name,
-                    parentIndex: null,
+                    parentCollectionID: null,
                   }
                 })
               }),