refactor: iterations

This commit is contained in:
jamesgeorge007
2024-02-04 20:34:23 +05:30
parent 29e25b0ead
commit ab7df212c2
11 changed files with 1235 additions and 187 deletions

View File

@@ -22,20 +22,28 @@
</span>
</span>
</div>
<div v-if="!collectionReadonly" class="flex">
<div class="flex">
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:icon="IconFilePlus"
:title="t('request.new')"
class="hidden group-hover:inline-flex"
@click="emit('add-request')"
@click="
emit('add-request', {
path: collection.collectionID,
})
"
/>
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:icon="IconFolderPlus"
:title="t('folder.new')"
class="hidden group-hover:inline-flex"
@click="emit('add-folder')"
@click="
emit('add-folder', {
path: collection.collectionID,
})
"
/>
<span>
<tippy
@@ -69,7 +77,9 @@
:shortcut="['R']"
@click="
() => {
emit('add-request')
emit('add-request', {
path: collection.collectionID,
})
hide()
}
"
@@ -81,7 +91,9 @@
:shortcut="['N']"
@click="
() => {
emit('add-folder')
emit('add-folder', {
path: collection.collectionID,
})
hide()
}
"
@@ -105,8 +117,7 @@
:shortcut="['X']"
@click="
() => {
emit('export-data'),
collectionsType === 'my-collections' ? hide() : null
emit('export-data'), hide()
}
"
/>
@@ -117,7 +128,9 @@
:shortcut="['⌫']"
@click="
() => {
emit('remove-collection')
emit('remove-collection', {
path: collection.collectionID,
})
hide()
}
"
@@ -150,17 +163,31 @@ const t = useI18n()
const props = defineProps<{
collection: RESTCollectionViewCollection
collectionReadonly: boolean
isOpen: boolean
}>()
const emit = defineEmits<{
(event: "toggle-children"): void
(event: "add-request"): void
(event: "add-folder"): void
(
event: "add-request",
payload: {
path: string
}
): void
(
event: "add-folder",
payload: {
path: string
}
): void
(event: "edit-collection"): void
(event: "export-data"): void
(event: "remove-collection"): void
(
event: "remove-collection",
payload: {
path: string
}
): void
}>()
const tippyActions = ref<TippyComponent | null>(null)

View File

@@ -6,6 +6,7 @@
>
<div
class="pointer-events-auto flex min-w-0 flex-1 cursor-pointer items-center justify-center"
@click="selectRequest(request.requestID, request.request)"
>
<span
class="pointer-events-none flex w-16 items-center justify-center truncate px-2"
@@ -38,12 +39,13 @@
</span>
</span>
</div>
<div v-if="!collectionReadonly" class="flex">
<div class="flex">
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:icon="IconRotateCCW"
:title="t('action.restore')"
class="hidden group-hover:inline-flex"
@click="selectRequest(request.requestID, request.request)"
/>
<span>
<tippy
@@ -75,7 +77,10 @@
:shortcut="['E']"
@click="
() => {
emit('edit-request')
emit('edit-request', {
requestPath: request.requestID,
request: request.request,
})
hide()
}
"
@@ -87,7 +92,10 @@
:shortcut="['D']"
@click="
() => {
emit('duplicate-request')
emit('duplicate-request', {
requestPath: request.requestID,
request: request.request,
})
hide()
}
"
@@ -124,12 +132,37 @@ import { RESTCollectionViewRequest } from "~/services/new-workspace/view"
import { computed, ref } from "vue"
import { TippyComponent } from "vue-tippy"
import { getMethodLabelColorClassOf } from "~/helpers/rest/labelColoring"
import { HoppRESTRequest } from "@hoppscotch/data"
const t = useI18n()
const props = defineProps<{
request: RESTCollectionViewRequest
collectionReadonly: boolean
}>()
const emit = defineEmits<{
(
event: "duplicate-request",
payload: {
requestPath: string
request: HoppRESTRequest
}
): void
(
event: "edit-request",
payload: {
requestPath: string
request: HoppRESTRequest
}
): void
(event: "remove-request"): void
(
event: "select-request",
payload: {
requestPath: string
request: HoppRESTRequest
}
): void
}>()
const tippyActions = ref<TippyComponent | null>(null)
@@ -141,4 +174,10 @@ const isActive = ref(true)
const requestLabelColor = computed(() =>
getMethodLabelColorClassOf(props.request)
)
const selectRequest = (requestPath: string, request: HoppRESTRequest) =>
emit("select-request", {
requestPath,
request,
})
</script>

View File

@@ -1,20 +1,10 @@
<template>
<div v-if="collectionsAreReadonly !== undefined" class="flex flex-1 flex-col">
<div class="flex flex-1 flex-col">
<div
class="sticky z-10 flex flex-1 justify-between border-b border-dividerLight bg-primary"
:style="'top: var(--upper-primary-sticky-fold)'"
>
<HoppButtonSecondary
v-if="collectionsAreReadonly"
v-tippy="{ theme: 'tooltip' }"
disabled
class="!rounded-none"
:icon="IconPlus"
:title="t('team.no_access')"
:label="t('add.new')"
/>
<HoppButtonSecondary
v-else
:icon="IconPlus"
:label="t('add.new')"
class="!rounded-none"
@@ -36,59 +26,114 @@
/>
</span>
</div>
<div class="flex flex-1 flex-col">
<HoppSmartTree :adapter="treeAdapter">
<template
#content="{ node, toggleChildren, isOpen, highlightChildren }"
>
<template #content="{ node, toggleChildren, isOpen }">
<!-- TODO: Implement -->
<NewCollectionsRestCollection
v-if="node.data.type === 'collection'"
:collection="node.data.value"
:collection-readonly="collectionsAreReadonly"
:is-open="isOpen"
@add-request="addRequest"
@add-folder="addFolder"
@remove-collection="removeFolder"
@toggle-children="toggleChildren"
/>
<NewCollectionsRestRequest
v-else-if="node.data.type === 'request'"
:request="node.data.value"
@duplicate-request="duplicateRequest"
@edit-request="editRequest"
@remove-request="removeRequest(node.data.value.requestID)"
@select-request="selectRequest"
/>
<div v-else @click="toggleChildren">
{{ node.data.value }}
</div>
</template>
<template #emptyNode="{ node }">
<template #emptyNode>
<!-- TODO: Implement -->
<div>Empty Node!</div>
</template>
</HoppSmartTree>
</div>
<CollectionsAdd
:show="showModalAdd"
:loading-state="modalLoadingState"
@submit="addNewRootCollection"
@hide-modal="showModalAdd = false"
/>
<CollectionsAddRequest
:show="showModalAddRequest"
:loading-state="modalLoadingState"
@add-request="onAddRequest"
@hide-modal="displayModalAddRequest(false)"
/>
<CollectionsAddFolder
:show="showModalAddFolder"
:loading-state="modalLoadingState"
@add-folder="onAddFolder"
@hide-modal="displayModalAddFolder(false)"
/>
<CollectionsEditRequest
v-model="editingRequestName"
:show="showModalEditRequest"
:loading-state="modalLoadingState"
@submit="onEditRequest"
@hide-modal="displayModalEditRequest(false)"
/>
<HoppSmartConfirmModal
:show="showConfirmModal"
:title="confirmModalTitle"
:loading-state="modalLoadingState"
@hide-modal="showConfirmModal = false"
@resolve="resolveConfirmModal"
/>
</div>
</template>
<script setup lang="ts">
import * as E from "fp-ts/lib/Either"
import { useService } from "dioc/vue"
import { markRaw, ref } from "vue"
import { useI18n } from "~/composables/i18n"
import IconPlus from "~icons/lucide/plus"
import IconHelpCircle from "~icons/lucide/help-circle"
import IconImport from "~icons/lucide/folder-down"
import { HandleRef } from "~/services/new-workspace/handle"
import { Workspace } from "~/services/new-workspace/workspace"
import { computed, markRaw, ref } from "vue"
import { useToast } from "~/composables/toast"
import { WorkspaceRESTCollectionTreeAdapter } from "~/helpers/adapters/WorkspaceRESTCollectionTreeAdapter"
import { NewWorkspaceService } from "~/services/new-workspace"
import { useService } from "dioc/vue"
import { HandleRef } from "~/services/new-workspace/handle"
import { Workspace } from "~/services/new-workspace/workspace"
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 {
resolveSaveContextOnCollectionReorder,
getFoldersByPath,
} from "~/helpers/collection/collection"
import {
navigateToFolderWithIndexPath,
restCollectionStore,
removeRESTFolder,
restCollections$,
} from "~/newstore/collections"
import { useReadonlyStream } from "~/composables/stream"
import { cloneDeep } from "lodash-es"
import { HoppRESTRequest } from "@hoppscotch/data"
const t = useI18n()
const toast = useToast()
const tabs = useService(RESTTabService)
const props = defineProps<{
workspaceHandle: HandleRef<Workspace>
}>()
const emit = defineEmits<{
defineEmits<{
(e: "display-modal-add"): void
(e: "display-modal-import-export"): void
}>()
@@ -101,18 +146,48 @@ const treeAdapter = markRaw(
)
)
const collectionsAreReadonly = computed(() => {
if (props.workspaceHandle.value.type === "ok") {
return props.workspaceHandle.value.data.collectionsAreReadonly
}
return undefined
})
const showModalAdd = ref(false)
const modalLoadingState = ref(false)
async function addNewRootCollection(name: string) {
const showModalAdd = ref(false)
const showModalAddRequest = ref(false)
const showModalAddFolder = ref(false)
const showModalEditRequest = ref(false)
const showConfirmModal = ref(false)
const editingFolderPath = ref<string | null>(null)
const editingRequest = ref<HoppRESTRequest | null>(null)
const editingRequestName = ref("")
const editingRequestIndex = ref<number | null>(null)
const confirmModalTitle = ref<string | null>(null)
const myCollections = useReadonlyStream(restCollections$, [], "deep")
const displayModalAddRequest = (show: boolean) => {
showModalAddRequest.value = show
if (!show) resetSelectedData()
}
const displayModalAddFolder = (show: boolean) => {
showModalAddFolder.value = show
if (!show) resetSelectedData()
}
const displayModalEditRequest = (show: boolean) => {
showModalEditRequest.value = show
if (!show) resetSelectedData()
}
const displayConfirmModal = (show: boolean) => {
showConfirmModal.value = show
if (!show) resetSelectedData()
}
const addNewRootCollection = async (name: string) => {
modalLoadingState.value = true
const result = await workspaceService.createRESTRootCollection(
@@ -135,7 +210,379 @@ async function addNewRootCollection(name: string) {
showModalAdd.value = false
}
const addRequest = (payload: { path: string }) => {
const { path } = payload
editingFolderPath.value = path
displayModalAddRequest(true)
}
const onAddRequest = async (requestName: string) => {
const path = editingFolderPath.value
if (!path) return
const collHandleResult = await workspaceService.getCollectionHandle(
props.workspaceHandle,
path
)
if (E.isLeft(collHandleResult)) {
// INVALID_WORKSPACE_HANDLE
return
}
const collHandle = collHandleResult.right
if (collHandle.value.type === "invalid") {
// WORKSPACE_INVALIDATED
return
}
const result = await workspaceService.createRESTRequest(
collHandle,
requestName,
path
)
if (E.isLeft(result)) {
// INVALID_WORKSPACE_HANDLE
return
}
if (result.right.value.type === "invalid") {
// WORKSPACE_INVALIDATED
return
}
displayModalAddRequest(false)
}
const addFolder = (payload: { path: string }) => {
const { path } = payload
editingFolderPath.value = path
displayModalAddFolder(true)
}
const onAddFolder = async (folderName: string) => {
const path = editingFolderPath.value
if (!path) return
const collHandleResult = await workspaceService.getCollectionHandle(
props.workspaceHandle,
path
)
if (E.isLeft(collHandleResult)) {
// INVALID_WORKSPACE_HANDLE
return
}
const collHandle = collHandleResult.right
if (collHandle.value.type === "invalid") {
// WORKSPACE_INVALIDATED
return
}
const result = await workspaceService.createRESTChildCollection(
collHandle,
folderName,
path
)
if (E.isLeft(result)) {
// INVALID_WORKSPACE_HANDLE
return
}
if (result.right.value.type === "invalid") {
// WORKSPACE_INVALIDATED
return
}
displayModalAddFolder(false)
}
const removeFolder = (payload: { path: string }) => {
const { path } = payload
editingFolderPath.value = path
confirmModalTitle.value = `${t("confirm.remove_folder")}`
displayConfirmModal(true)
}
const onRemoveFolder = () => {
const path = editingFolderPath.value
if (!path) return
const folderToRemove = path
? navigateToFolderWithIndexPath(
restCollectionStore.value.state,
path.split("/").map((i) => parseInt(i))
)
: undefined
removeRESTFolder(path, folderToRemove ? folderToRemove.id : undefined)
const parentFolder = path.split("/").slice(0, -1).join("/") // remove last folder to get parent folder
resolveSaveContextOnCollectionReorder({
lastIndex: pathToLastIndex(path),
newIndex: -1,
folderPath: parentFolder,
length: getFoldersByPath(myCollections.value, parentFolder).length,
})
toast.success(t("state.deleted"))
displayConfirmModal(false)
}
const removeRequest = (requestIndex: string) => {
const folderPath = requestIndex.slice(0, -2)
const requestID = requestIndex[requestIndex.length - 1]
editingFolderPath.value = folderPath
editingRequestIndex.value = parseInt(requestID)
confirmModalTitle.value = `${t("confirm.remove_request")}`
displayConfirmModal(true)
}
const onRemoveRequest = async () => {
const path = editingFolderPath.value
const requestIndex = editingRequestIndex.value
if (path === null || requestIndex === null) return
const collHandleResult = await workspaceService.getCollectionHandle(
props.workspaceHandle,
path
)
if (E.isLeft(collHandleResult)) {
// INVALID_WORKSPACE_HANDLE
return
}
const collHandle = collHandleResult.right
if (collHandle.value.type === "invalid") {
// WORKSPACE_INVALIDATED
return
}
const result = await workspaceService.removeRESTRequest(
collHandle,
path,
requestIndex
)
if (E.isLeft(result)) {
// INVALID_WORKSPACE_HANDLE
return
}
if (result.right.value.type === "invalid") {
// WORKSPACE_INVALIDATED
return
}
toast.success(t("state.deleted"))
displayConfirmModal(false)
}
const selectRequest = async (payload: {
requestPath: string
request: HoppRESTRequest
}) => {
const { requestPath, request } = payload
const collPath = requestPath.slice(0, -2)
const requestIndex = requestPath[requestPath.length - 1]
const collHandleResult = await workspaceService.getCollectionHandle(
props.workspaceHandle,
collPath
)
if (E.isLeft(collHandleResult)) {
// INVALID_WORKSPACE_HANDLE
return
}
const collHandle = collHandleResult.right
if (collHandle.value.type === "invalid") {
// WORKSPACE_INVALIDATED
return
}
const result = await workspaceService.selectRESTRequest(
collHandle,
collPath,
requestIndex,
request
)
if (E.isLeft(result)) {
// INVALID_WORKSPACE_HANDLE
return
}
if (result.right.value.type === "invalid") {
// WORKSPACE_INVALIDATED
return
}
}
const duplicateRequest = async (payload: {
requestPath: string
request: HoppRESTRequest
}) => {
const { requestPath, request } = payload
const collPath = requestPath.slice(0, -2)
const collHandleResult = await workspaceService.getCollectionHandle(
props.workspaceHandle,
collPath
)
if (E.isLeft(collHandleResult)) {
// INVALID_WORKSPACE_HANDLE
return
}
const collHandle = collHandleResult.right
if (collHandle.value.type === "invalid") {
// WORKSPACE_INVALIDATED
return
}
const newRequest = {
...cloneDeep(request),
name: `${request.name} - ${t("action.duplicate")}`,
}
const result = await workspaceService.duplicateRESTRequest(
collHandle,
collPath,
newRequest
)
if (E.isLeft(result)) {
// INVALID_WORKSPACE_HANDLE
return
}
if (result.right.value.type === "invalid") {
// WORKSPACE_INVALIDATED
return
}
toast.success(t("request.duplicated"))
}
const editRequest = (payload: {
requestPath: string
request: HoppRESTRequest
}) => {
const { requestPath, request } = payload
const collPath = requestPath.slice(0, -2)
const requestIndex = requestPath[requestPath.length - 1]
editingRequest.value = request
editingRequestName.value = request.name ?? ""
editingFolderPath.value = collPath
editingRequestIndex.value = parseInt(requestIndex)
displayModalEditRequest(true)
}
const onEditRequest = async (newReqName: string) => {
const collPath = editingFolderPath.value
const requestIndex = editingRequestIndex.value
const request = editingRequest.value
if (collPath === null || requestIndex === null || !request) {
return
}
const collHandleResult = await workspaceService.getCollectionHandle(
props.workspaceHandle,
collPath
)
if (E.isLeft(collHandleResult)) {
// INVALID_WORKSPACE_HANDLE
return
}
const collHandle = collHandleResult.right
if (collHandle.value.type === "invalid") {
// WORKSPACE_INVALIDATED
return
}
const updatedRequest = {
...request,
name: newReqName || request.name,
}
const result = await workspaceService.editRESTRequest(
collHandle,
collPath,
requestIndex,
updatedRequest
)
if (E.isLeft(result)) {
// INVALID_WORKSPACE_HANDLE
return
}
if (result.right.value.type === "invalid") {
// WORKSPACE_INVALIDATED
return
}
displayModalEditRequest(false)
toast.success(t("request.renamed"))
}
function onImportExportClick() {
// TODO: Implement
}
const resolveConfirmModal = (title: string | null) => {
if (title === `${t("confirm.remove_collection")}`) {
// onRemoveCollection()
} else if (title === `${t("confirm.remove_request")}`) {
onRemoveRequest()
} else if (title === `${t("confirm.remove_folder")}`) {
onRemoveFolder()
} else {
console.error(
`Confirm modal title ${title} is not handled by the component`
)
toast.error(t("error.something_went_wrong"))
displayConfirmModal(false)
}
}
const resetSelectedData = () => {
editingFolderPath.value = null
}
/**
* Used to get the index of the request from the path
* @param path The path of the request
* @returns The index of the request
*/
const pathToLastIndex = (path: string) => {
const pathArr = path.split("/")
return parseInt(pathArr[pathArr.length - 1])
}
</script>