123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085 |
- <template>
- <div
- :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 bg-primary border-b border-dividerLight"
- :class="{ 'rounded-t': saveRequest }"
- :style="
- saveRequest ? 'top: calc(-1 * var(--line-height-body))' : 'top: 0'
- "
- >
- <WorkspaceCurrent :section="t('tab.collections')" />
- <input
- v-model="filterTexts"
- type="search"
- autocomplete="off"
- class="flex w-full bg-transparent px-4 py-2 h-8"
- :placeholder="t('action.search')"
- :disabled="collectionsType.type === 'team-collections'"
- />
- </div>
- <CollectionsMyCollections
- v-if="collectionsType.type === 'my-collections'"
- :collections-type="collectionsType"
- :filtered-collections="filteredCollections"
- :filter-text="filterTexts"
- :save-request="saveRequest"
- :picked="picked"
- @add-folder="addFolder"
- @add-request="addRequest"
- @edit-collection="editCollection"
- @edit-folder="editFolder"
- @edit-properties="editProperties"
- @export-data="exportData"
- @remove-collection="removeCollection"
- @remove-folder="removeFolder"
- @share-request="shareRequest"
- @drop-collection="dropCollection"
- @update-request-order="updateRequestOrder"
- @update-collection-order="updateCollectionOrder"
- @edit-request="editRequest"
- @duplicate-request="duplicateRequest"
- @remove-request="removeRequest"
- @select-request="selectRequest"
- @select="selectPicked"
- @drop-request="dropRequest"
- @display-modal-add="displayModalAdd(true)"
- @display-modal-import-export="displayModalImportExport(true)"
- />
- <CollectionsTeamCollections
- v-else
- :collections-type="collectionsType"
- :team-collection-list="teamCollectionList"
- :team-loading-collections="teamLoadingCollections"
- :export-loading="exportLoading"
- :duplicate-loading="duplicateLoading"
- :save-request="saveRequest"
- :picked="picked"
- :collection-move-loading="collectionMoveLoading"
- :request-move-loading="requestMoveLoading"
- @add-request="addRequest"
- @add-folder="addFolder"
- @edit-collection="editCollection"
- @edit-folder="editFolder"
- @edit-properties="editProperties"
- @export-data="exportData"
- @remove-collection="removeCollection"
- @remove-folder="removeFolder"
- @share-request="shareRequest"
- @edit-request="editRequest"
- @duplicate-request="duplicateRequest"
- @remove-request="removeRequest"
- @select-request="selectRequest"
- @select="selectPicked"
- @drop-request="dropRequest"
- @drop-collection="dropCollection"
- @update-request-order="updateRequestOrder"
- @update-collection-order="updateCollectionOrder"
- @expand-team-collection="expandTeamCollection"
- @display-modal-add="displayModalAdd(true)"
- @display-modal-import-export="displayModalImportExport(true)"
- />
- <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"
- :loading-state="modalLoadingState"
- @submit="addNewRootCollection"
- @hide-modal="displayModalAdd(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)"
- />
- <CollectionsEdit
- :show="showModalEditCollection"
- :editing-collection-name="editingCollectionName ?? ''"
- :loading-state="modalLoadingState"
- @hide-modal="displayModalEditCollection(false)"
- @submit="updateEditingCollection"
- />
- <CollectionsEditFolder
- :show="showModalEditFolder"
- :editing-folder-name="editingFolderName ?? ''"
- :loading-state="modalLoadingState"
- @submit="updateEditingFolder"
- @hide-modal="displayModalEditFolder(false)"
- />
- <CollectionsEditRequest
- v-model="editingRequestName"
- :show="showModalEditRequest"
- :loading-state="modalLoadingState"
- @submit="updateEditingRequest"
- @hide-modal="displayModalEditRequest(false)"
- />
- <HoppSmartConfirmModal
- :show="showConfirmModal"
- :title="confirmModalTitle"
- :loading-state="modalLoadingState"
- @hide-modal="showConfirmModal = false"
- @resolve="resolveConfirmModal"
- />
- <CollectionsImportExport
- v-if="showModalImportExport"
- :collections-type="collectionsType"
- @hide-modal="displayModalImportExport(false)"
- />
- <TeamsAdd
- :show="showTeamModalAdd"
- @hide-modal="displayTeamModalAdd(false)"
- />
- <CollectionsProperties
- :show="showModalEditProperties"
- :editing-properties="editingProperties"
- @hide-modal="displayModalEditProperties(false)"
- @set-collection-properties="setCollectionProperties"
- />
- </div>
- </template>
- <script setup lang="ts">
- import { computed, nextTick, PropType, ref, watch } from "vue"
- import { useToast } from "@composables/toast"
- import { useI18n } from "@composables/i18n"
- import { Picked } from "~/helpers/types/HoppPicked"
- import { useReadonlyStream } from "~/composables/stream"
- import { useLocalState } from "~/newstore/localstate"
- import { GetMyTeamsQuery } from "~/helpers/backend/graphql"
- import { pipe } from "fp-ts/function"
- import * as TE from "fp-ts/TaskEither"
- import {
- addRESTCollection,
- addRESTFolder,
- editRESTCollection,
- editRESTFolder,
- editRESTRequest,
- moveRESTRequest,
- removeRESTCollection,
- removeRESTFolder,
- removeRESTRequest,
- restCollections$,
- saveRESTRequestAs,
- updateRESTRequestOrder,
- updateRESTCollectionOrder,
- moveRESTFolder,
- navigateToFolderWithIndexPath,
- restCollectionStore,
- cascaseParentCollectionForHeaderAuth,
- } from "~/newstore/collections"
- import TeamCollectionAdapter from "~/helpers/teams/TeamCollectionAdapter"
- import {
- HoppCollection,
- HoppRESTRequest,
- makeCollection,
- } from "@hoppscotch/data"
- import { cloneDeep, isEqual } from "lodash-es"
- import { GQLError } from "~/helpers/backend/GQLClient"
- import {
- createNewRootCollection,
- createChildCollection,
- renameCollection,
- deleteCollection,
- moveRESTTeamCollection,
- updateOrderRESTTeamCollection,
- } from "~/helpers/backend/mutations/TeamCollection"
- import {
- updateTeamRequest,
- createRequestInCollection,
- deleteTeamRequest,
- moveRESTTeamRequest,
- updateOrderRESTTeamRequest,
- } from "~/helpers/backend/mutations/TeamRequest"
- import { TeamCollection } from "~/helpers/teams/TeamCollection"
- import { Collection as NodeCollection } from "./MyCollections.vue"
- import {
- getCompleteCollectionTree,
- teamCollToHoppRESTColl,
- } from "~/helpers/backend/helpers"
- import { platform } from "~/platform"
- import {
- getRequestsByPath,
- resolveSaveContextOnRequestReorder,
- } from "~/helpers/collection/request"
- import {
- getFoldersByPath,
- resolveSaveContextOnCollectionReorder,
- updateSaveContextForAffectedRequests,
- updateInheritedPropertiesForAffectedRequests,
- resetTeamRequestsContext,
- } from "~/helpers/collection/collection"
- import { currentReorderingStatus$ } from "~/newstore/reordering"
- import { defineActionHandler, invokeAction } from "~/helpers/actions"
- import { WorkspaceService } from "~/services/workspace.service"
- import { useService } from "dioc/vue"
- import { RESTTabService } from "~/services/tab/rest"
- import { HoppInheritedProperty } from "~/helpers/types/HoppInheritedProperties"
- const t = useI18n()
- const toast = useToast()
- const tabs = useService(RESTTabService)
- const props = defineProps({
- saveRequest: {
- type: Boolean,
- default: false,
- required: false,
- },
- picked: {
- type: Object as PropType<Picked | null>,
- default: null,
- required: false,
- },
- })
- const emit = defineEmits<{
- (event: "select", payload: Picked | null): void
- (event: "update-team", team: SelectedTeam): void
- (event: "update-collection-type", type: CollectionType["type"]): void
- }>()
- type SelectedTeam = GetMyTeamsQuery["myTeams"][number] | undefined
- type CollectionType =
- | {
- type: "team-collections"
- selectedTeam: SelectedTeam
- }
- | { type: "my-collections"; selectedTeam: undefined }
- const collectionsType = ref<CollectionType>({
- type: "my-collections",
- selectedTeam: undefined,
- })
- // Collection Data
- const editingCollection = ref<
- HoppCollection<HoppRESTRequest> | TeamCollection | null
- >(null)
- const editingCollectionName = ref<string | null>(null)
- const editingCollectionIndex = ref<number | null>(null)
- const editingCollectionID = ref<string | null>(null)
- const editingFolder = ref<
- HoppCollection<HoppRESTRequest> | TeamCollection | null
- >(null)
- const editingFolderName = ref<string | null>(null)
- const editingFolderPath = ref<string | null>(null)
- const editingRequest = ref<HoppRESTRequest | null>(null)
- const editingRequestName = ref("")
- const editingRequestIndex = ref<number | null>(null)
- const editingRequestID = ref<string | null>(null)
- const editingProperties = ref<{
- collection: HoppCollection<HoppRESTRequest> | TeamCollection | null
- isRootCollection: boolean
- path: string
- inheritedProperties?: HoppInheritedProperty
- }>({
- collection: null,
- isRootCollection: false,
- path: "",
- inheritedProperties: undefined,
- })
- const confirmModalTitle = ref<string | null>(null)
- const filterTexts = ref("")
- const currentUser = useReadonlyStream(
- platform.auth.getCurrentUserStream(),
- platform.auth.getCurrentUser()
- )
- const myCollections = useReadonlyStream(restCollections$, [], "deep")
- // Draging
- const draggingToRoot = ref(false)
- const collectionMoveLoading = ref<string[]>([])
- const requestMoveLoading = ref<string[]>([])
- // TeamList-Adapter
- const workspaceService = useService(WorkspaceService)
- const teamListAdapter = workspaceService.acquireTeamListAdapter(null)
- const myTeams = useReadonlyStream(teamListAdapter.teamList$, null)
- const REMEMBERED_TEAM_ID = useLocalState("REMEMBERED_TEAM_ID")
- const teamListFetched = ref(false)
- // Team Collection Adapter
- const teamCollectionAdapter = new TeamCollectionAdapter(null)
- const teamCollectionList = useReadonlyStream(
- teamCollectionAdapter.collections$,
- []
- )
- const teamLoadingCollections = useReadonlyStream(
- teamCollectionAdapter.loadingCollections$,
- []
- )
- watch(
- () => myTeams.value,
- (newTeams) => {
- if (newTeams && !teamListFetched.value) {
- teamListFetched.value = true
- if (REMEMBERED_TEAM_ID.value && currentUser.value) {
- const team = newTeams.find((t) => t.id === REMEMBERED_TEAM_ID.value)
- if (team) updateSelectedTeam(team)
- }
- }
- }
- )
- watch(
- () => collectionsType.value.selectedTeam,
- (newTeam) => {
- if (newTeam) {
- teamCollectionAdapter.changeTeamID(newTeam.id)
- }
- }
- )
- const switchToMyCollections = () => {
- collectionsType.value.type = "my-collections"
- collectionsType.value.selectedTeam = undefined
- teamCollectionAdapter.changeTeamID(null)
- }
- const expandTeamCollection = (collectionID: string) => {
- teamCollectionAdapter.expandCollection(collectionID)
- }
- const updateSelectedTeam = (team: SelectedTeam) => {
- if (team) {
- collectionsType.value.type = "team-collections"
- collectionsType.value.selectedTeam = team
- REMEMBERED_TEAM_ID.value = team.id
- emit("update-team", team)
- emit("update-collection-type", "team-collections")
- }
- }
- const workspace = workspaceService.currentWorkspace
- // Used to switch collection type and team when user switch workspace in the global workspace switcher
- // Check if there is a teamID in the workspace, if yes, switch to team collections and select the team
- // If there is no teamID, switch to my collections
- watch(
- () => {
- const space = workspace.value
- return space.type === "personal" ? undefined : space.teamID
- },
- (teamID) => {
- if (teamID) {
- const team = myTeams.value?.find((t) => t.id === teamID)
- if (team) {
- updateSelectedTeam(team)
- }
- return
- }
- return switchToMyCollections()
- },
- {
- immediate: true,
- }
- )
- // Switch to my-collections and reset the team collection when user logout
- watch(
- () => currentUser.value,
- (user) => {
- if (!user) {
- switchToMyCollections()
- }
- }
- )
- const currentReorderingStatus = useReadonlyStream(currentReorderingStatus$, {
- type: "collection",
- id: "",
- parentID: "",
- })
- const hasTeamWriteAccess = computed(() => {
- if (collectionsType.value.type !== "team-collections") {
- return false
- }
- const role = collectionsType.value.selectedTeam?.myRole
- return role === "OWNER" || role === "EDITOR"
- })
- const filteredCollections = computed(() => {
- const collections =
- collectionsType.value.type === "my-collections" ? myCollections.value : []
- if (filterTexts.value === "") return collections
- if (collectionsType.value.type === "team-collections") return []
- const filterText = filterTexts.value.toLowerCase()
- const filteredCollections = []
- const isMatch = (text: string) => text.toLowerCase().includes(filterText)
- for (const collection of collections) {
- const filteredRequests = []
- const filteredFolders = []
- for (const request of collection.requests) {
- if (isMatch(request.name)) filteredRequests.push(request)
- }
- for (const folder of collection.folders) {
- if (isMatch(folder.name)) filteredFolders.push(folder)
- const filteredFolderRequests = []
- for (const request of folder.requests) {
- if (isMatch(request.name)) filteredFolderRequests.push(request)
- }
- if (filteredFolderRequests.length > 0) {
- const filteredFolder = Object.assign({}, folder)
- filteredFolder.requests = filteredFolderRequests
- filteredFolders.push(filteredFolder)
- }
- }
- if (
- filteredRequests.length + filteredFolders.length > 0 ||
- isMatch(collection.name)
- ) {
- const filteredCollection = Object.assign({}, collection)
- filteredCollection.requests = filteredRequests
- filteredCollection.folders = filteredFolders
- filteredCollections.push(filteredCollection)
- }
- }
- return filteredCollections
- })
- const isSelected = ({
- collectionIndex,
- folderPath,
- requestIndex,
- collectionID,
- folderID,
- requestID,
- }: {
- collectionIndex?: number | undefined
- folderPath?: string | undefined
- requestIndex?: number | undefined
- collectionID?: string | undefined
- folderID?: string | undefined
- requestID?: string | undefined
- }) => {
- if (collectionIndex !== undefined) {
- return (
- props.picked &&
- props.picked.pickedType === "my-collection" &&
- props.picked.collectionIndex === collectionIndex
- )
- } else if (requestIndex !== undefined && folderPath !== undefined) {
- return (
- props.picked &&
- props.picked.pickedType === "my-request" &&
- props.picked.folderPath === folderPath &&
- props.picked.requestIndex === requestIndex
- )
- } else if (folderPath !== undefined) {
- return (
- props.picked &&
- props.picked.pickedType === "my-folder" &&
- props.picked.folderPath === folderPath
- )
- } else if (collectionID !== undefined) {
- return (
- props.picked &&
- props.picked.pickedType === "teams-collection" &&
- props.picked.collectionID === collectionID
- )
- } else if (requestID !== undefined) {
- return (
- props.picked &&
- props.picked.pickedType === "teams-request" &&
- props.picked.requestID === requestID
- )
- } else if (folderID !== undefined) {
- return (
- props.picked &&
- props.picked.pickedType === "teams-folder" &&
- props.picked.folderID === folderID
- )
- }
- }
- const modalLoadingState = ref(false)
- const exportLoading = ref(false)
- const duplicateLoading = ref(false)
- const showModalAdd = ref(false)
- const showModalAddRequest = ref(false)
- const showModalAddFolder = ref(false)
- const showModalEditCollection = ref(false)
- const showModalEditFolder = ref(false)
- const showModalEditRequest = ref(false)
- const showModalImportExport = ref(false)
- const showModalEditProperties = ref(false)
- const showConfirmModal = ref(false)
- const showTeamModalAdd = ref(false)
- const displayModalAdd = (show: boolean) => {
- showModalAdd.value = show
- if (!show) resetSelectedData()
- }
- const displayModalAddRequest = (show: boolean) => {
- showModalAddRequest.value = show
- if (!show) resetSelectedData()
- }
- const displayModalAddFolder = (show: boolean) => {
- showModalAddFolder.value = show
- if (!show) resetSelectedData()
- }
- const displayModalEditCollection = (show: boolean) => {
- showModalEditCollection.value = show
- if (!show) resetSelectedData()
- }
- const displayModalEditFolder = (show: boolean) => {
- showModalEditFolder.value = show
- if (!show) resetSelectedData()
- }
- const displayModalEditRequest = (show: boolean) => {
- showModalEditRequest.value = show
- if (!show) resetSelectedData()
- }
- const displayModalImportExport = (show: boolean) => {
- showModalImportExport.value = show
- if (!show) resetSelectedData()
- }
- const displayModalEditProperties = (show: boolean) => {
- showModalEditProperties.value = show
- if (!show) resetSelectedData()
- }
- const displayConfirmModal = (show: boolean) => {
- showConfirmModal.value = show
- if (!show) resetSelectedData()
- }
- const displayTeamModalAdd = (show: boolean) => {
- showTeamModalAdd.value = show
- teamListAdapter.fetchList()
- }
- const addNewRootCollection = (name: string) => {
- if (collectionsType.value.type === "my-collections") {
- addRESTCollection(
- makeCollection({
- name,
- folders: [],
- requests: [],
- headers: [],
- auth: {
- authType: "inherit",
- authActive: false,
- },
- })
- )
- platform.analytics?.logEvent({
- type: "HOPP_CREATE_COLLECTION",
- platform: "rest",
- workspaceType: "personal",
- isRootCollection: true,
- })
- displayModalAdd(false)
- } else if (hasTeamWriteAccess.value) {
- if (!collectionsType.value.selectedTeam) return
- modalLoadingState.value = true
- platform.analytics?.logEvent({
- type: "HOPP_CREATE_COLLECTION",
- platform: "rest",
- workspaceType: "team",
- isRootCollection: true,
- })
- pipe(
- createNewRootCollection(name, collectionsType.value.selectedTeam.id),
- TE.match(
- (err: GQLError<string>) => {
- toast.error(`${getErrorMessage(err)}`)
- modalLoadingState.value = false
- },
- () => {
- modalLoadingState.value = false
- toast.success(t("collection.created"))
- displayModalAdd(false)
- }
- )
- )()
- }
- }
- const addRequest = (payload: {
- path: string
- folder: HoppCollection<HoppRESTRequest> | TeamCollection
- }) => {
- const { path, folder } = payload
- editingFolder.value = folder
- editingFolderPath.value = path
- displayModalAddRequest(true)
- }
- const onAddRequest = (requestName: string) => {
- const newRequest = {
- ...cloneDeep(tabs.currentActiveTab.value.document.request),
- name: requestName,
- }
- if (collectionsType.value.type === "my-collections") {
- const path = editingFolderPath.value
- if (!path) return
- const insertionIndex = saveRESTRequestAs(path, newRequest)
- const { auth, headers } = cascaseParentCollectionForHeaderAuth(path)
- tabs.createNewTab({
- request: newRequest,
- isDirty: false,
- saveContext: {
- originLocation: "user-collection",
- folderPath: path,
- requestIndex: insertionIndex,
- },
- inheritedProperties: {
- auth,
- headers,
- },
- })
- platform.analytics?.logEvent({
- type: "HOPP_SAVE_REQUEST",
- workspaceType: "personal",
- createdNow: true,
- platform: "rest",
- })
- displayModalAddRequest(false)
- } else if (hasTeamWriteAccess.value) {
- const folder = editingFolder.value
- if (!folder || !collectionsType.value.selectedTeam) return
- if (!folder.id) return
- modalLoadingState.value = true
- const data = {
- request: JSON.stringify(newRequest),
- teamID: collectionsType.value.selectedTeam.id,
- title: requestName,
- }
- platform.analytics?.logEvent({
- type: "HOPP_SAVE_REQUEST",
- workspaceType: "team",
- platform: "rest",
- createdNow: true,
- })
- pipe(
- createRequestInCollection(folder.id, data),
- TE.match(
- (err: GQLError<string>) => {
- toast.error(`${getErrorMessage(err)}`)
- modalLoadingState.value = false
- },
- (result) => {
- const { createRequestInCollection } = result
- tabs.createNewTab({
- request: newRequest,
- isDirty: false,
- saveContext: {
- originLocation: "team-collection",
- requestID: createRequestInCollection.id,
- collectionID: createRequestInCollection.collection.id,
- teamID: createRequestInCollection.collection.team.id,
- },
- })
- modalLoadingState.value = false
- displayModalAddRequest(false)
- }
- )
- )()
- }
- }
- const addFolder = (payload: {
- path: string
- folder: HoppCollection<HoppRESTRequest> | TeamCollection
- }) => {
- const { path, folder } = payload
- editingFolder.value = folder
- editingFolderPath.value = path
- displayModalAddFolder(true)
- }
- const onAddFolder = (folderName: string) => {
- const path = editingFolderPath.value
- if (collectionsType.value.type === "my-collections") {
- if (!path) return
- addRESTFolder(folderName, path)
- platform.analytics?.logEvent({
- type: "HOPP_CREATE_COLLECTION",
- workspaceType: "personal",
- isRootCollection: false,
- platform: "rest",
- })
- displayModalAddFolder(false)
- } else if (hasTeamWriteAccess.value) {
- const folder = editingFolder.value
- if (!folder || !folder.id) return
- modalLoadingState.value = true
- platform.analytics?.logEvent({
- type: "HOPP_CREATE_COLLECTION",
- workspaceType: "personal",
- isRootCollection: false,
- platform: "rest",
- })
- pipe(
- createChildCollection(folderName, folder.id),
- TE.match(
- (err: GQLError<string>) => {
- if (err.error === "team_coll/short_title") {
- toast.error(t("folder.name_length_insufficient"))
- } else {
- toast.error(`${getErrorMessage(err)}`)
- }
- modalLoadingState.value = false
- },
- () => {
- toast.success(t("folder.created"))
- modalLoadingState.value = false
- displayModalAddFolder(false)
- }
- )
- )()
- }
- }
- const editCollection = (payload: {
- collectionIndex: string
- collection: HoppCollection<HoppRESTRequest> | TeamCollection
- }) => {
- const { collectionIndex, collection } = payload
- editingCollection.value = collection
- if (collectionsType.value.type === "my-collections") {
- editingCollectionIndex.value = parseInt(collectionIndex)
- editingCollectionName.value = (
- collection as HoppCollection<HoppRESTRequest>
- ).name
- } else {
- editingCollectionName.value = (collection as TeamCollection).title
- }
- displayModalEditCollection(true)
- }
- const updateEditingCollection = (newName: string) => {
- if (!editingCollection.value) return
- if (!newName) {
- toast.error(t("collection.invalid_name"))
- return
- }
- if (collectionsType.value.type === "my-collections") {
- const collectionIndex = editingCollectionIndex.value
- if (collectionIndex === null) return
- const collectionUpdated = {
- ...editingCollection.value,
- name: newName,
- }
- editRESTCollection(
- collectionIndex,
- collectionUpdated as NodeCollection["data"]["data"]
- )
- displayModalEditCollection(false)
- } else if (hasTeamWriteAccess.value) {
- if (!editingCollection.value.id) return
- modalLoadingState.value = true
- pipe(
- renameCollection(editingCollection.value.id, newName),
- TE.match(
- (err: GQLError<string>) => {
- toast.error(`${getErrorMessage(err)}`)
- modalLoadingState.value = false
- },
- () => {
- modalLoadingState.value = false
- toast.success(t("collection.renamed"))
- displayModalEditCollection(false)
- }
- )
- )()
- }
- }
- const editFolder = (payload: {
- folderPath: string | undefined
- folder: HoppCollection<HoppRESTRequest> | TeamCollection
- }) => {
- const { folderPath, folder } = payload
- editingFolder.value = folder
- if (collectionsType.value.type === "my-collections" && folderPath) {
- editingFolderPath.value = folderPath
- editingFolderName.value = (folder as HoppCollection<HoppRESTRequest>).name
- } else {
- editingFolderName.value = (folder as TeamCollection).title
- }
- displayModalEditFolder(true)
- }
- const updateEditingFolder = (newName: string) => {
- if (!editingFolder.value) return
- if (collectionsType.value.type === "my-collections") {
- if (!editingFolderPath.value) return
- editRESTFolder(editingFolderPath.value, {
- ...(editingFolder.value as HoppCollection<HoppRESTRequest>),
- name: newName,
- })
- displayModalEditFolder(false)
- } else if (hasTeamWriteAccess.value) {
- if (!editingFolder.value.id) return
- modalLoadingState.value = true
- /* renameCollection can be used to rename both collections and folders
- since folder is treated as collection in the BE. */
- pipe(
- renameCollection(editingFolder.value.id, newName),
- TE.match(
- (err: GQLError<string>) => {
- if (err.error === "team_coll/short_title") {
- toast.error(t("folder.name_length_insufficient"))
- } else {
- toast.error(`${getErrorMessage(err)}`)
- }
- modalLoadingState.value = false
- },
- () => {
- modalLoadingState.value = false
- toast.success(t("folder.renamed"))
- displayModalEditFolder(false)
- }
- )
- )()
- }
- }
- const editRequest = (payload: {
- folderPath: string | undefined
- requestIndex: string
- request: HoppRESTRequest
- }) => {
- const { folderPath, requestIndex, request } = payload
- editingRequest.value = request
- editingRequestName.value = request.name ?? ""
- if (collectionsType.value.type === "my-collections" && folderPath) {
- editingFolderPath.value = folderPath
- editingRequestIndex.value = parseInt(requestIndex)
- } else {
- editingRequestID.value = requestIndex
- }
- displayModalEditRequest(true)
- }
- const updateEditingRequest = (newName: string) => {
- const request = editingRequest.value
- if (!request) return
- const requestUpdated = {
- ...request,
- name: newName || request.name,
- }
- if (collectionsType.value.type === "my-collections") {
- const folderPath = editingFolderPath.value
- const requestIndex = editingRequestIndex.value
- if (folderPath === null || requestIndex === null) return
- const possibleActiveTab = tabs.getTabRefWithSaveContext({
- originLocation: "user-collection",
- requestIndex,
- folderPath,
- })
- editRESTRequest(folderPath, requestIndex, requestUpdated)
- if (possibleActiveTab) {
- possibleActiveTab.value.document.request.name = requestUpdated.name
- nextTick(() => {
- possibleActiveTab.value.document.isDirty = false
- })
- }
- displayModalEditRequest(false)
- } else if (hasTeamWriteAccess.value) {
- modalLoadingState.value = true
- const requestID = editingRequestID.value
- const requestName = newName || request.name
- if (!requestID) return
- const data = {
- request: JSON.stringify(requestUpdated),
- title: requestName,
- }
- pipe(
- updateTeamRequest(requestID, data),
- TE.match(
- (err: GQLError<string>) => {
- toast.error(`${getErrorMessage(err)}`)
- modalLoadingState.value = false
- },
- () => {
- modalLoadingState.value = false
- toast.success(t("request.renamed"))
- displayModalEditRequest(false)
- }
- )
- )()
- const possibleTab = tabs.getTabRefWithSaveContext({
- originLocation: "team-collection",
- requestID,
- })
- if (possibleTab) {
- possibleTab.value.document.request.name = requestName
- nextTick(() => {
- possibleTab.value.document.isDirty = false
- })
- }
- }
- }
- const duplicateRequest = (payload: {
- folderPath: string
- request: HoppRESTRequest
- }) => {
- const { folderPath, request } = payload
- if (!folderPath) return
- const newRequest = {
- ...cloneDeep(request),
- name: `${request.name} - ${t("action.duplicate")}`,
- }
- if (collectionsType.value.type === "my-collections") {
- saveRESTRequestAs(folderPath, newRequest)
- toast.success(t("request.duplicated"))
- } else if (hasTeamWriteAccess.value) {
- duplicateLoading.value = true
- if (!collectionsType.value.selectedTeam) return
- const data = {
- request: JSON.stringify(newRequest),
- teamID: collectionsType.value.selectedTeam.id,
- title: `${request.name} - ${t("action.duplicate")}`,
- }
- pipe(
- createRequestInCollection(folderPath, data),
- TE.match(
- (err: GQLError<string>) => {
- toast.error(`${getErrorMessage(err)}`)
- duplicateLoading.value = false
- },
- () => {
- duplicateLoading.value = false
- toast.success(t("request.duplicated"))
- displayModalAddRequest(false)
- }
- )
- )()
- }
- }
- const removeCollection = (id: string) => {
- if (collectionsType.value.type === "my-collections")
- editingCollectionIndex.value = parseInt(id)
- else editingCollectionID.value = id
- confirmModalTitle.value = `${t("confirm.remove_collection")}`
- displayConfirmModal(true)
- }
- /**
- * Used to delete both collections and folders
- * since folder is treated as collection in the BE.
- * @param collectionID - ID of the collection or folder to be deleted.
- */
- const removeTeamCollectionOrFolder = async (collectionID: string) => {
- modalLoadingState.value = true
- await pipe(
- deleteCollection(collectionID),
- TE.match(
- (err: GQLError<string>) => {
- toast.error(`${getErrorMessage(err)}`)
- modalLoadingState.value = false
- },
- () => {
- modalLoadingState.value = false
- toast.success(t("state.deleted"))
- displayConfirmModal(false)
- }
- )
- )()
- }
- const onRemoveCollection = () => {
- if (collectionsType.value.type === "my-collections") {
- const collectionIndex = editingCollectionIndex.value
- const collectionToRemove =
- collectionIndex || collectionIndex === 0
- ? navigateToFolderWithIndexPath(restCollectionStore.value.state, [
- collectionIndex,
- ])
- : undefined
- if (collectionIndex === null) return
- if (
- isSelected({
- collectionIndex,
- })
- ) {
- emit("select", null)
- }
- removeRESTCollection(
- collectionIndex,
- collectionToRemove ? collectionToRemove.id : undefined
- )
- resolveSaveContextOnCollectionReorder({
- lastIndex: collectionIndex,
- newIndex: -1,
- folderPath: "", // root folder
- length: myCollections.value.length,
- })
- toast.success(t("state.deleted"))
- displayConfirmModal(false)
- } else if (hasTeamWriteAccess.value) {
- const collectionID = editingCollectionID.value
- if (!collectionID) return
- if (
- isSelected({
- collectionID,
- })
- ) {
- emit("select", null)
- }
- removeTeamCollectionOrFolder(collectionID).then(() => {
- resetTeamRequestsContext()
- })
- }
- }
- const removeFolder = (id: string) => {
- if (collectionsType.value.type === "my-collections")
- editingFolderPath.value = id
- else editingCollectionID.value = id
- confirmModalTitle.value = `${t("confirm.remove_folder")}`
- displayConfirmModal(true)
- }
- const onRemoveFolder = () => {
- if (collectionsType.value.type === "my-collections") {
- const folderPath = editingFolderPath.value
- if (!folderPath) return
- if (
- isSelected({
- folderPath,
- })
- ) {
- emit("select", null)
- }
- const folderToRemove = folderPath
- ? navigateToFolderWithIndexPath(
- restCollectionStore.value.state,
- folderPath.split("/").map((i) => parseInt(i))
- )
- : undefined
- removeRESTFolder(folderPath, folderToRemove ? folderToRemove.id : undefined)
- const parentFolder = folderPath.split("/").slice(0, -1).join("/") // remove last folder to get parent folder
- resolveSaveContextOnCollectionReorder({
- lastIndex: pathToLastIndex(folderPath),
- newIndex: -1,
- folderPath: parentFolder,
- length: getFoldersByPath(myCollections.value, parentFolder).length,
- })
- toast.success(t("state.deleted"))
- displayConfirmModal(false)
- } else if (hasTeamWriteAccess.value) {
- const collectionID = editingCollectionID.value
- if (!collectionID) return
- if (
- isSelected({
- collectionID,
- })
- ) {
- emit("select", null)
- }
- removeTeamCollectionOrFolder(collectionID).then(() => {
- resetTeamRequestsContext()
- })
- }
- }
- const removeRequest = (payload: {
- folderPath: string | null
- requestIndex: string
- }) => {
- const { folderPath, requestIndex } = payload
- if (collectionsType.value.type === "my-collections" && folderPath) {
- editingFolderPath.value = folderPath
- editingRequestIndex.value = parseInt(requestIndex)
- } else {
- editingRequestID.value = requestIndex
- }
- confirmModalTitle.value = `${t("confirm.remove_request")}`
- displayConfirmModal(true)
- }
- const onRemoveRequest = () => {
- if (collectionsType.value.type === "my-collections") {
- const folderPath = editingFolderPath.value
- const requestIndex = editingRequestIndex.value
- if (folderPath === null || requestIndex === null) return
- if (
- isSelected({
- folderPath,
- requestIndex,
- })
- ) {
- emit("select", null)
- }
- const possibleTab = tabs.getTabRefWithSaveContext({
- originLocation: "user-collection",
- folderPath,
- requestIndex,
- })
- // If there is a tab attached to this request, dissociate its state and mark it dirty
- if (possibleTab) {
- possibleTab.value.document.saveContext = null
- possibleTab.value.document.isDirty = true
- }
- const requestToRemove = navigateToFolderWithIndexPath(
- restCollectionStore.value.state,
- folderPath.split("/").map((i) => parseInt(i))
- )?.requests[requestIndex]
- removeRESTRequest(folderPath, requestIndex, requestToRemove?.id)
- // the same function is used to reorder requests since after removing, it's basically doing reorder
- resolveSaveContextOnRequestReorder({
- lastIndex: requestIndex,
- newIndex: -1,
- folderPath,
- length: getRequestsByPath(myCollections.value, folderPath).length,
- })
- toast.success(t("state.deleted"))
- displayConfirmModal(false)
- } else if (hasTeamWriteAccess.value) {
- const requestID = editingRequestID.value
- if (!requestID) return
- if (
- isSelected({
- requestID,
- })
- ) {
- emit("select", null)
- }
- modalLoadingState.value = true
- pipe(
- deleteTeamRequest(requestID),
- TE.match(
- (err: GQLError<string>) => {
- toast.error(`${getErrorMessage(err)}`)
- modalLoadingState.value = false
- },
- () => {
- modalLoadingState.value = false
- toast.success(t("state.deleted"))
- displayConfirmModal(false)
- }
- )
- )()
- // If there is a tab attached to this request, dissociate its state and mark it dirty
- const possibleTab = tabs.getTabRefWithSaveContext({
- originLocation: "team-collection",
- requestID,
- })
- if (possibleTab) {
- possibleTab.value.document.saveContext = null
- possibleTab.value.document.isDirty = true
- }
- }
- }
- // The request is picked in the save request as modal
- const selectPicked = (payload: Picked | null) => {
- emit("select", payload)
- }
- /**
- * This function is called when the user clicks on a request
- * @param selectedRequest The request that the user clicked on emited from the collection tree
- */
- const selectRequest = (selectedRequest: {
- request: HoppRESTRequest
- folderPath: string | undefined
- requestIndex: string
- isActive: boolean
- }) => {
- const { request, folderPath, requestIndex } = selectedRequest
- // If there is a request with this save context, switch into it
- let possibleTab = null
- const { auth, headers } = cascaseParentCollectionForHeaderAuth(folderPath)
- if (collectionsType.value.type === "team-collections") {
- possibleTab = tabs.getTabRefWithSaveContext({
- originLocation: "team-collection",
- requestID: requestIndex,
- })
- if (possibleTab) {
- tabs.setActiveTab(possibleTab.value.id)
- } else {
- tabs.createNewTab({
- request: cloneDeep(request),
- isDirty: false,
- saveContext: {
- originLocation: "team-collection",
- requestID: requestIndex,
- },
- })
- }
- } else {
- possibleTab = tabs.getTabRefWithSaveContext({
- originLocation: "user-collection",
- requestIndex: parseInt(requestIndex),
- folderPath: folderPath!,
- })
- if (possibleTab) {
- tabs.setActiveTab(possibleTab.value.id)
- } else {
- // If not, open the request in a new tab
- tabs.createNewTab({
- request: cloneDeep(request),
- isDirty: false,
- saveContext: {
- originLocation: "user-collection",
- folderPath: folderPath!,
- requestIndex: parseInt(requestIndex),
- },
- inheritedProperties: {
- auth,
- headers,
- },
- })
- }
- }
- }
- /**
- * 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])
- }
- /**
- * This function is called when the user drops the request inside a collection
- * @param payload Object that contains the folder path, request index and the destination collection index
- */
- const dropRequest = (payload: {
- folderPath?: string | undefined
- requestIndex: string
- destinationCollectionIndex: string
- }) => {
- const { folderPath, requestIndex, destinationCollectionIndex } = payload
- if (!requestIndex || !destinationCollectionIndex) return
- let possibleTab = null
- if (collectionsType.value.type === "my-collections" && folderPath) {
- const { auth, headers } = cascaseParentCollectionForHeaderAuth(
- destinationCollectionIndex
- )
- 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(
- myCollections.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(
- folderPath,
- pathToLastIndex(requestIndex),
- destinationCollectionIndex
- )
- toast.success(`${t("request.moved")}`)
- draggingToRoot.value = false
- } else if (hasTeamWriteAccess.value) {
- // add the request index to the loading array
- requestMoveLoading.value.push(requestIndex)
- pipe(
- moveRESTTeamRequest(destinationCollectionIndex, requestIndex),
- TE.match(
- (err: GQLError<string>) => {
- toast.error(`${getErrorMessage(err)}`)
- requestMoveLoading.value.splice(
- requestMoveLoading.value.indexOf(requestIndex),
- 1
- )
- },
- () => {
- // remove the request index from the loading array
- requestMoveLoading.value.splice(
- requestMoveLoading.value.indexOf(requestIndex),
- 1
- )
- possibleTab = tabs.getTabRefWithSaveContext({
- originLocation: "team-collection",
- requestID: requestIndex,
- })
- if (possibleTab) {
- possibleTab.value.document.saveContext = {
- originLocation: "team-collection",
- requestID: requestIndex,
- }
- }
- toast.success(`${t("request.moved")}`)
- }
- )
- )()
- }
- }
- /**
- * @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
- }
- /**
- * 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 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
- }
- 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
- }
- }
- /**
- * This function is called when the user moves the collection
- * to a different collection or folder
- * @param payload - object containing the collection index dragged and the destination collection index
- */
- const dropCollection = (payload: {
- collectionIndexDragged: string
- destinationCollectionIndex: string
- }) => {
- const { collectionIndexDragged, destinationCollectionIndex } = payload
- if (!collectionIndexDragged || !destinationCollectionIndex) return
- if (collectionIndexDragged === destinationCollectionIndex) return
- if (collectionsType.value.type === "my-collections") {
- 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(myCollections.value, destinationCollectionIndex).length -
- (parentFolder === destinationCollectionIndex ? 1 : 0)
- moveRESTFolder(collectionIndexDragged, destinationCollectionIndex)
- resolveSaveContextOnCollectionReorder(
- {
- lastIndex: pathToLastIndex(collectionIndexDragged),
- newIndex: -1,
- folderPath: parentFolder,
- length: getFoldersByPath(myCollections.value, parentFolder).length,
- },
- "drop"
- )
- updateSaveContextForAffectedRequests(
- collectionIndexDragged,
- `${destinationCollectionIndex}/${totalFoldersOfDestinationCollection}`
- )
- const { auth, headers } = cascaseParentCollectionForHeaderAuth(
- `${destinationCollectionIndex}/${totalFoldersOfDestinationCollection}`
- )
- const inheritedProperty = {
- auth,
- headers,
- }
- updateInheritedPropertiesForAffectedRequests(
- `${destinationCollectionIndex}/${totalFoldersOfDestinationCollection}`,
- inheritedProperty
- )
- draggingToRoot.value = false
- toast.success(`${t("collection.moved")}`)
- } else if (hasTeamWriteAccess.value) {
- // add the collection index to the loading array
- collectionMoveLoading.value.push(collectionIndexDragged)
- pipe(
- moveRESTTeamCollection(
- collectionIndexDragged,
- destinationCollectionIndex
- ),
- TE.match(
- (err: GQLError<string>) => {
- toast.error(`${getErrorMessage(err)}`)
- collectionMoveLoading.value.splice(
- collectionMoveLoading.value.indexOf(collectionIndexDragged),
- 1
- )
- },
- () => {
- toast.success(`${t("collection.moved")}`)
- // remove the collection index from the loading array
- collectionMoveLoading.value.splice(
- collectionMoveLoading.value.indexOf(collectionIndexDragged),
- 1
- )
- }
- )
- )()
- }
- }
- /**
- * 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 = pathToIndex(id)
- return indexPath.length === 1
- }
- /**
- * 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
- if (collectionsType.value.type === "my-collections") {
- // 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
- } else if (hasTeamWriteAccess.value) {
- // add the collection index to the loading array
- collectionMoveLoading.value.push(collectionIndexDragged)
- // destination collection index is null since we are moving to root
- pipe(
- moveRESTTeamCollection(collectionIndexDragged, null),
- TE.match(
- (err: GQLError<string>) => {
- collectionMoveLoading.value.splice(
- collectionMoveLoading.value.indexOf(collectionIndexDragged),
- 1
- )
- toast.error(`${getErrorMessage(err)}`)
- },
- () => {
- // remove the collection index from the loading array
- collectionMoveLoading.value.splice(
- collectionMoveLoading.value.indexOf(collectionIndexDragged),
- 1
- )
- toast.success(`${t("collection.moved")}`)
- }
- )
- )()
- }
- }
- }
- /**
- * 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
- }
- /**
- * This function is called when the user updates the request order in a collection
- * @param payload - object containing the request index dragged and the destination request index
- * with the destination collection index
- */
- const updateRequestOrder = (payload: {
- dragedRequestIndex: string
- destinationRequestIndex: string | null
- destinationCollectionIndex: string
- }) => {
- const {
- dragedRequestIndex,
- destinationRequestIndex,
- destinationCollectionIndex,
- } = payload
- if (!dragedRequestIndex || !destinationCollectionIndex) return
- if (dragedRequestIndex === destinationRequestIndex) return
- if (collectionsType.value.type === "my-collections") {
- 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")}`)
- }
- } else if (hasTeamWriteAccess.value) {
- // add the request index to the loading array
- requestMoveLoading.value.push(dragedRequestIndex)
- pipe(
- updateOrderRESTTeamRequest(
- dragedRequestIndex,
- destinationRequestIndex,
- destinationCollectionIndex
- ),
- TE.match(
- (err: GQLError<string>) => {
- toast.error(`${getErrorMessage(err)}`)
- requestMoveLoading.value.splice(
- requestMoveLoading.value.indexOf(dragedRequestIndex),
- 1
- )
- },
- () => {
- toast.success(`${t("request.order_changed")}`)
- // remove the request index from the loading array
- requestMoveLoading.value.splice(
- requestMoveLoading.value.indexOf(dragedRequestIndex),
- 1
- )
- }
- )
- )()
- }
- }
- /**
- * This function is called when the user updates the collection or folder order
- * @param payload - object containing the collection index dragged and the destination collection index
- */
- const updateCollectionOrder = (payload: {
- dragedCollectionIndex: string
- destinationCollection: {
- destinationCollectionIndex: string | null
- destinationCollectionParentIndex: string | null
- }
- }) => {
- const { dragedCollectionIndex, destinationCollection } = payload
- const { destinationCollectionIndex, destinationCollectionParentIndex } =
- destinationCollection
- if (!dragedCollectionIndex) return
- if (dragedCollectionIndex === destinationCollectionIndex) return
- if (collectionsType.value.type === "my-collections") {
- if (
- !isSameSameParent(
- dragedCollectionIndex,
- destinationCollectionIndex,
- destinationCollectionParentIndex
- )
- ) {
- toast.error(`${t("collection.different_parent")}`)
- } else {
- updateRESTCollectionOrder(
- dragedCollectionIndex,
- destinationCollectionIndex
- )
- resolveSaveContextOnCollectionReorder({
- lastIndex: pathToLastIndex(dragedCollectionIndex),
- newIndex: pathToLastIndex(
- destinationCollectionIndex ? destinationCollectionIndex : ""
- ),
- folderPath: dragedCollectionIndex.split("/").slice(0, -1).join("/"),
- })
- toast.success(`${t("collection.order_changed")}`)
- }
- } else if (hasTeamWriteAccess.value) {
- collectionMoveLoading.value.push(dragedCollectionIndex)
- pipe(
- updateOrderRESTTeamCollection(
- dragedCollectionIndex,
- destinationCollectionIndex
- ),
- TE.match(
- (err: GQLError<string>) => {
- toast.error(`${getErrorMessage(err)}`)
- collectionMoveLoading.value.splice(
- collectionMoveLoading.value.indexOf(dragedCollectionIndex),
- 1
- )
- },
- () => {
- toast.success(`${t("collection.order_changed")}`)
- collectionMoveLoading.value.splice(
- collectionMoveLoading.value.indexOf(dragedCollectionIndex),
- 1
- )
- }
- )
- )()
- }
- }
- // Import - Export Collection functions
- /**
- * Create a downloadable file from a collection and prompts the user to download it.
- * @param collectionJSON - JSON string of the collection
- * @param name - Name of the collection set as the file name
- */
- const initializeDownloadCollection = async (
- collectionJSON: string,
- name: string | null
- ) => {
- const result = await platform.io.saveFileWithDialog({
- data: collectionJSON,
- contentType: "application/json",
- suggestedFilename: `${name ?? "collection"}.json`,
- filters: [
- {
- name: "Hoppscotch Collection JSON file",
- extensions: ["json"],
- },
- ],
- })
- if (result.type === "unknown" || result.type === "saved") {
- toast.success(t("state.download_started").toString())
- }
- }
- /**
- * Export a specific collection or folder
- * Triggered by the export button in the tippy menu
- * @param collection - Collection or folder to be exported
- */
- const exportData = async (
- collection: HoppCollection<HoppRESTRequest> | TeamCollection
- ) => {
- if (collectionsType.value.type === "my-collections") {
- const collectionJSON = JSON.stringify(collection)
- const name = (collection as HoppCollection<HoppRESTRequest>).name
- initializeDownloadCollection(collectionJSON, name)
- } else {
- if (!collection.id) return
- exportLoading.value = true
- pipe(
- getCompleteCollectionTree(collection.id),
- TE.match(
- (err: GQLError<string>) => {
- toast.error(`${getErrorMessage(err)}`)
- exportLoading.value = false
- return
- },
- async (coll) => {
- const hoppColl = teamCollToHoppRESTColl(coll)
- const collectionJSONString = JSON.stringify(hoppColl)
- await initializeDownloadCollection(
- collectionJSONString,
- hoppColl.name
- )
- exportLoading.value = false
- }
- )
- )()
- }
- }
- const shareRequest = ({ request }: { request: HoppRESTRequest }) => {
- if (currentUser.value) {
- // opens the share request modal
- invokeAction("share.request", {
- request,
- })
- } else {
- invokeAction("modals.login.toggle")
- }
- }
- const editProperties = (payload: {
- collectionIndex: string
- collection: HoppCollection<HoppRESTRequest> | TeamCollection
- }) => {
- const { collection, collectionIndex } = payload
- const parentIndex = collectionIndex.split("/").slice(0, -1).join("/") // remove last folder to get parent folder
- let inheritedProperties = {}
- if (parentIndex) {
- const { auth, headers } = cascaseParentCollectionForHeaderAuth(parentIndex)
- inheritedProperties = {
- auth,
- headers,
- } as HoppInheritedProperty
- }
- editingProperties.value = {
- collection,
- isRootCollection: isAlreadyInRoot(collectionIndex),
- path: collectionIndex,
- inheritedProperties,
- }
- displayModalEditProperties(true)
- }
- const setCollectionProperties = (newCollection: {
- collection: HoppCollection<HoppRESTRequest>
- path: string
- isRootCollection: boolean
- }) => {
- const { collection, path, isRootCollection } = newCollection
- if (isRootCollection) {
- editRESTCollection(parseInt(path), collection)
- } else {
- editRESTFolder(path, collection)
- }
- const { auth, headers } = cascaseParentCollectionForHeaderAuth(path)
- nextTick(() => {
- updateInheritedPropertiesForAffectedRequests(path, {
- auth,
- headers,
- })
- })
- displayModalEditProperties(false)
- }
- 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 = () => {
- editingCollection.value = null
- editingCollectionIndex.value = null
- editingCollectionID.value = null
- editingFolder.value = null
- editingFolderPath.value = null
- editingRequest.value = null
- editingRequestIndex.value = null
- editingRequestID.value = null
- confirmModalTitle.value = null
- }
- const getErrorMessage = (err: GQLError<string>) => {
- console.error(err)
- if (err.type === "network_error") {
- return t("error.network_error")
- }
- switch (err.error) {
- case "team_coll/short_title":
- return t("collection.name_length_insufficient")
- case "team/invalid_coll_id":
- case "bug/team_coll/no_coll_id":
- case "team_req/invalid_target_id":
- return t("team.invalid_coll_id")
- case "team/not_required_role":
- return t("profile.no_permission")
- case "team_req/not_required_role":
- return t("profile.no_permission")
- case "Forbidden resource":
- return t("profile.no_permission")
- case "team_req/not_found":
- return t("team.no_request_found")
- case "bug/team_req/no_req_id":
- return t("team.no_request_found")
- case "team/collection_is_parent_coll":
- return t("team.parent_coll_move")
- case "team/target_and_destination_collection_are_same":
- return t("team.same_target_destination")
- case "team/target_collection_is_already_root_collection":
- return t("collection.invalid_root_move")
- case "team_req/requests_not_from_same_collection":
- return t("request.different_collection")
- case "team/team_collections_have_different_parents":
- return t("collection.different_parent")
- default:
- return t("error.something_went_wrong")
- }
- }
- defineActionHandler("collection.new", () => {
- displayModalAdd(true)
- })
- </script>
|