feat: tab service added (#3367)
This commit is contained in:
@@ -7,23 +7,24 @@
|
||||
<HoppSmartWindows
|
||||
v-if="currentTabID"
|
||||
:id="'gql_windows'"
|
||||
v-model="currentTabID"
|
||||
:model-value="currentTabID"
|
||||
@update:model-value="(tabID) => tabs.setActiveTab(tabID)"
|
||||
@remove-tab="removeTab"
|
||||
@add-tab="addNewTab"
|
||||
@sort="sortTabs"
|
||||
>
|
||||
<HoppSmartWindow
|
||||
v-for="tab in tabs"
|
||||
v-for="tab in activeTabs"
|
||||
:id="tab.id"
|
||||
:key="'removable_tab_' + tab.id"
|
||||
:label="tab.document.request.name"
|
||||
:is-removable="tabs.length > 1"
|
||||
:is-removable="activeTabs.length > 1"
|
||||
:close-visibility="'hover'"
|
||||
>
|
||||
<template #tabhead>
|
||||
<GraphqlTabHead
|
||||
:tab="tab"
|
||||
:is-removable="tabs.length > 1"
|
||||
:is-removable="activeTabs.length > 1"
|
||||
@open-rename-modal="openReqRenameModal(tab)"
|
||||
@close-tab="removeTab(tab.id)"
|
||||
@close-other-tabs="closeOtherTabsAction(tab.id)"
|
||||
@@ -89,21 +90,15 @@ import { computed, onBeforeUnmount, ref } from "vue"
|
||||
import { defineActionHandler } from "~/helpers/actions"
|
||||
import { connection, disconnect } from "~/helpers/graphql/connection"
|
||||
import { getDefaultGQLRequest } from "~/helpers/graphql/default"
|
||||
import {
|
||||
HoppGQLTab,
|
||||
closeOtherTabs,
|
||||
closeTab,
|
||||
createNewTab,
|
||||
currentTabID,
|
||||
getActiveTabs,
|
||||
getDirtyTabsCount,
|
||||
getTabRef,
|
||||
updateTab,
|
||||
updateTabOrdering,
|
||||
} from "~/helpers/graphql/tab"
|
||||
import { HoppGQLDocument } from "~/helpers/graphql/document"
|
||||
import { InspectionService } from "~/services/inspection"
|
||||
import { HoppTab } from "~/services/tab"
|
||||
import { GQLTabService } from "~/services/tab/graphql"
|
||||
|
||||
const t = useI18n()
|
||||
const tabs = useService(GQLTabService)
|
||||
|
||||
const currentTabID = computed(() => tabs.currentTabID.value)
|
||||
|
||||
const inspectionService = useService(InspectionService)
|
||||
|
||||
@@ -113,27 +108,27 @@ usePageHead({
|
||||
title: computed(() => t("navigation.graphql")),
|
||||
})
|
||||
|
||||
const tabs = getActiveTabs()
|
||||
const activeTabs = tabs.getActiveTabs()
|
||||
|
||||
const addNewTab = () => {
|
||||
const tab = createNewTab({
|
||||
const tab = tabs.createNewTab({
|
||||
request: getDefaultGQLRequest(),
|
||||
isDirty: false,
|
||||
})
|
||||
|
||||
currentTabID.value = tab.id
|
||||
tabs.setActiveTab(tab.id)
|
||||
}
|
||||
const sortTabs = (e: { oldIndex: number; newIndex: number }) => {
|
||||
updateTabOrdering(e.oldIndex, e.newIndex)
|
||||
tabs.updateTabOrdering(e.oldIndex, e.newIndex)
|
||||
}
|
||||
|
||||
const removeTab = (tabID: string) => {
|
||||
const tabState = getTabRef(tabID).value
|
||||
const tabState = tabs.getTabRef(tabID).value
|
||||
|
||||
if (tabState.document.isDirty) {
|
||||
confirmingCloseForTabID.value = tabID
|
||||
} else {
|
||||
closeTab(tabState.id)
|
||||
tabs.closeTab(tabState.id)
|
||||
inspectionService.deleteTabInspectorResult(tabState.id)
|
||||
}
|
||||
}
|
||||
@@ -150,7 +145,7 @@ const onCloseConfirm = () => {
|
||||
*/
|
||||
const onResolveConfirm = () => {
|
||||
if (confirmingCloseForTabID.value) {
|
||||
closeTab(confirmingCloseForTabID.value)
|
||||
tabs.closeTab(confirmingCloseForTabID.value)
|
||||
confirmingCloseForTabID.value = null
|
||||
}
|
||||
}
|
||||
@@ -160,24 +155,24 @@ const unsavedTabsCount = ref(0)
|
||||
const exceptedTabID = ref<string | null>(null)
|
||||
|
||||
const closeOtherTabsAction = (tabID: string) => {
|
||||
const dirtyTabCount = getDirtyTabsCount()
|
||||
const dirtyTabCount = tabs.getDirtyTabsCount()
|
||||
// If there are dirty tabs, show the confirm modal
|
||||
if (dirtyTabCount > 0) {
|
||||
confirmingCloseAllTabs.value = true
|
||||
unsavedTabsCount.value = dirtyTabCount
|
||||
exceptedTabID.value = tabID
|
||||
} else {
|
||||
closeOtherTabs(tabID)
|
||||
tabs.closeOtherTabs(tabID)
|
||||
}
|
||||
}
|
||||
|
||||
const onResolveConfirmCloseAllTabs = () => {
|
||||
if (exceptedTabID.value) closeOtherTabs(exceptedTabID.value)
|
||||
if (exceptedTabID.value) tabs.closeOtherTabs(exceptedTabID.value)
|
||||
confirmingCloseAllTabs.value = false
|
||||
}
|
||||
|
||||
const onTabUpdate = (tab: HoppGQLTab) => {
|
||||
updateTab(tab)
|
||||
const onTabUpdate = (tab: HoppTab<HoppGQLDocument>) => {
|
||||
tabs.updateTab(tab)
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
@@ -189,33 +184,33 @@ onBeforeUnmount(() => {
|
||||
const editReqModalReqName = ref("")
|
||||
const showRenamingReqNameModalForTabID = ref<string>()
|
||||
|
||||
const openReqRenameModal = (tab: HoppGQLTab) => {
|
||||
const openReqRenameModal = (tab: HoppTab<HoppGQLDocument>) => {
|
||||
editReqModalReqName.value = tab.document.request.name
|
||||
showRenamingReqNameModalForTabID.value = tab.id
|
||||
}
|
||||
|
||||
const renameReqName = () => {
|
||||
const tab = getTabRef(showRenamingReqNameModalForTabID.value!)
|
||||
const tab = tabs.getTabRef(showRenamingReqNameModalForTabID.value!)
|
||||
if (tab.value) {
|
||||
tab.value.document.request.name = editReqModalReqName.value
|
||||
updateTab(tab.value)
|
||||
tabs.updateTab(tab.value)
|
||||
}
|
||||
showRenamingReqNameModalForTabID.value = undefined
|
||||
}
|
||||
|
||||
const duplicateTab = (tabID: string) => {
|
||||
const tab = getTabRef(tabID)
|
||||
const tab = tabs.getTabRef(tabID)
|
||||
if (tab.value) {
|
||||
const newTab = createNewTab({
|
||||
const newTab = tabs.createNewTab({
|
||||
request: tab.value.document.request,
|
||||
isDirty: true,
|
||||
})
|
||||
currentTabID.value = newTab.id
|
||||
tabs.setActiveTab(newTab.id)
|
||||
}
|
||||
}
|
||||
|
||||
defineActionHandler("gql.request.open", ({ request, saveContext }) => {
|
||||
createNewTab({
|
||||
tabs.createNewTab({
|
||||
saveContext,
|
||||
request: request,
|
||||
isDirty: false,
|
||||
@@ -223,7 +218,7 @@ defineActionHandler("gql.request.open", ({ request, saveContext }) => {
|
||||
})
|
||||
|
||||
defineActionHandler("request.rename", () => {
|
||||
openReqRenameModal(getTabRef(currentTabID.value).value!)
|
||||
openReqRenameModal(tabs.getTabRef(currentTabID.value).value!)
|
||||
})
|
||||
|
||||
defineActionHandler("tab.duplicate-tab", ({ tabID }) => {
|
||||
@@ -233,7 +228,7 @@ defineActionHandler("tab.close-current", () => {
|
||||
removeTab(currentTabID.value)
|
||||
})
|
||||
defineActionHandler("tab.close-other", () => {
|
||||
closeOtherTabs(currentTabID.value)
|
||||
tabs.closeOtherTabs(currentTabID.value)
|
||||
})
|
||||
defineActionHandler("tab.open-new", addNewTab)
|
||||
</script>
|
||||
|
||||
@@ -11,17 +11,17 @@
|
||||
@sort="sortTabs"
|
||||
>
|
||||
<HoppSmartWindow
|
||||
v-for="tab in tabs"
|
||||
v-for="tab in activeTabs"
|
||||
:id="tab.id"
|
||||
:key="tab.id"
|
||||
:label="tab.document.request.name"
|
||||
:is-removable="tabs.length > 1"
|
||||
:is-removable="activeTabs.length > 1"
|
||||
:close-visibility="'hover'"
|
||||
>
|
||||
<template #tabhead>
|
||||
<HttpTabHead
|
||||
:tab="tab"
|
||||
:is-removable="tabs.length > 1"
|
||||
:is-removable="activeTabs.length > 1"
|
||||
@open-rename-modal="openReqRenameModal(tab.id)"
|
||||
@close-tab="removeTab(tab.id)"
|
||||
@close-other-tabs="closeOtherTabsAction(tab.id)"
|
||||
@@ -99,21 +99,6 @@ import { safelyExtractRESTRequest } from "@hoppscotch/data"
|
||||
import { translateExtURLParams } from "~/helpers/RESTExtURLParams"
|
||||
import { useRoute } from "vue-router"
|
||||
import { useI18n } from "@composables/i18n"
|
||||
import {
|
||||
closeTab,
|
||||
closeOtherTabs,
|
||||
createNewTab,
|
||||
currentActiveTab,
|
||||
currentTabID,
|
||||
getActiveTabs,
|
||||
getTabRef,
|
||||
HoppRESTTab,
|
||||
loadTabsFromPersistedState,
|
||||
persistableTabState,
|
||||
updateTab,
|
||||
updateTabOrdering,
|
||||
getDirtyTabsCount,
|
||||
} from "~/helpers/rest/tab"
|
||||
import { getDefaultRESTRequest } from "~/helpers/rest/default"
|
||||
import { defineActionHandler, invokeAction } from "~/helpers/actions"
|
||||
import { onLoggedIn } from "~/composables/auth"
|
||||
@@ -128,7 +113,6 @@ import {
|
||||
Subscription,
|
||||
} from "rxjs"
|
||||
import { useToast } from "~/composables/toast"
|
||||
import { PersistableRESTTabState } from "~/helpers/rest/tab"
|
||||
import { watchDebounced } from "@vueuse/core"
|
||||
import { oauthRedirect } from "~/helpers/oauth"
|
||||
import { useReadonlyStream } from "~/composables/stream"
|
||||
@@ -142,6 +126,9 @@ import { HeaderInspectorService } from "~/services/inspection/inspectors/header.
|
||||
import { EnvironmentInspectorService } from "~/services/inspection/inspectors/environment.inspector"
|
||||
import { ResponseInspectorService } from "~/services/inspection/inspectors/response.inspector"
|
||||
import { cloneDeep } from "lodash-es"
|
||||
import { RESTTabService } from "~/services/tab/rest"
|
||||
import { HoppTab, PersistableTabState } from "~/services/tab"
|
||||
import { HoppRESTDocument } from "~/helpers/rest/document"
|
||||
|
||||
const savingRequest = ref(false)
|
||||
const confirmingCloseForTabID = ref<string | null>(null)
|
||||
@@ -155,6 +142,10 @@ const renameTabID = ref<string | null>(null)
|
||||
const t = useI18n()
|
||||
const toast = useToast()
|
||||
|
||||
const tabs = useService(RESTTabService)
|
||||
|
||||
const currentTabID = tabs.currentTabID
|
||||
|
||||
type PopupDetails = {
|
||||
show: boolean
|
||||
position: {
|
||||
@@ -173,13 +164,13 @@ const contextMenu = ref<PopupDetails>({
|
||||
text: null,
|
||||
})
|
||||
|
||||
const tabs = getActiveTabs()
|
||||
const activeTabs = tabs.getActiveTabs()
|
||||
|
||||
const confirmSync = useReadonlyStream(currentSyncingStatus$, {
|
||||
isInitialSync: false,
|
||||
shouldSync: true,
|
||||
})
|
||||
const tabStateForSync = ref<PersistableRESTTabState | null>(null)
|
||||
const tabStateForSync = ref<PersistableTabState<HoppRESTDocument> | null>(null)
|
||||
|
||||
function bindRequestToURLParams() {
|
||||
const route = useRoute()
|
||||
@@ -190,91 +181,92 @@ function bindRequestToURLParams() {
|
||||
// We skip URL params parsing
|
||||
if (Object.keys(query).length === 0 || query.code || query.error) return
|
||||
|
||||
const request = currentActiveTab.value.document.request
|
||||
const request = tabs.currentActiveTab.value.document.request
|
||||
|
||||
currentActiveTab.value.document.request = safelyExtractRESTRequest(
|
||||
tabs.currentActiveTab.value.document.request = safelyExtractRESTRequest(
|
||||
translateExtURLParams(query, request),
|
||||
getDefaultRESTRequest()
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
const onTabUpdate = (tab: HoppRESTTab) => {
|
||||
updateTab(tab)
|
||||
const onTabUpdate = (tab: HoppTab<HoppRESTDocument>) => {
|
||||
tabs.updateTab(tab)
|
||||
}
|
||||
|
||||
const addNewTab = () => {
|
||||
const tab = createNewTab({
|
||||
const tab = tabs.createNewTab({
|
||||
request: getDefaultRESTRequest(),
|
||||
isDirty: false,
|
||||
})
|
||||
|
||||
currentTabID.value = tab.id
|
||||
tabs.setActiveTab(tab.id)
|
||||
}
|
||||
const sortTabs = (e: { oldIndex: number; newIndex: number }) => {
|
||||
updateTabOrdering(e.oldIndex, e.newIndex)
|
||||
tabs.updateTabOrdering(e.oldIndex, e.newIndex)
|
||||
}
|
||||
|
||||
const inspectionService = useService(InspectionService)
|
||||
|
||||
const removeTab = (tabID: string) => {
|
||||
const tabState = getTabRef(tabID).value
|
||||
const tabState = tabs.getTabRef(tabID).value
|
||||
|
||||
if (tabState.document.isDirty) {
|
||||
confirmingCloseForTabID.value = tabID
|
||||
} else {
|
||||
closeTab(tabState.id)
|
||||
tabs.closeTab(tabState.id)
|
||||
inspectionService.deleteTabInspectorResult(tabState.id)
|
||||
}
|
||||
}
|
||||
|
||||
const closeOtherTabsAction = (tabID: string) => {
|
||||
const isTabDirty = getTabRef(tabID).value?.document.isDirty
|
||||
const dirtyTabCount = getDirtyTabsCount()
|
||||
const isTabDirty = tabs.getTabRef(tabID).value?.document.isDirty
|
||||
const dirtyTabCount = tabs.getDirtyTabsCount()
|
||||
// If current tab is dirty, so we need to subtract 1 from the dirty tab count
|
||||
const balanceDirtyTabCount = isTabDirty ? dirtyTabCount - 1 : dirtyTabCount
|
||||
|
||||
// If there are dirty tabs, show the confirm modal
|
||||
if (balanceDirtyTabCount > 0) {
|
||||
confirmingCloseAllTabs.value = true
|
||||
unsavedTabsCount.value = balanceDirtyTabCount
|
||||
exceptedTabID.value = tabID
|
||||
} else {
|
||||
closeOtherTabs(tabID)
|
||||
tabs.closeOtherTabs(tabID)
|
||||
}
|
||||
}
|
||||
|
||||
const duplicateTab = (tabID: string) => {
|
||||
const tab = getTabRef(tabID)
|
||||
const tab = tabs.getTabRef(tabID)
|
||||
if (tab.value) {
|
||||
const newTab = createNewTab({
|
||||
const newTab = tabs.createNewTab({
|
||||
request: cloneDeep(tab.value.document.request),
|
||||
isDirty: true,
|
||||
})
|
||||
currentTabID.value = newTab.id
|
||||
tabs.setActiveTab(newTab.id)
|
||||
}
|
||||
}
|
||||
|
||||
const onResolveConfirmCloseAllTabs = () => {
|
||||
if (exceptedTabID.value) closeOtherTabs(exceptedTabID.value)
|
||||
if (exceptedTabID.value) tabs.closeOtherTabs(exceptedTabID.value)
|
||||
confirmingCloseAllTabs.value = false
|
||||
}
|
||||
|
||||
const openReqRenameModal = (tabID?: string) => {
|
||||
if (tabID) {
|
||||
const tab = getTabRef(tabID)
|
||||
const tab = tabs.getTabRef(tabID)
|
||||
reqName.value = tab.value.document.request.name
|
||||
renameTabID.value = tabID
|
||||
} else {
|
||||
reqName.value = currentActiveTab.value.document.request.name
|
||||
reqName.value = tabs.currentActiveTab.value.document.request.name
|
||||
}
|
||||
showRenamingReqNameModal.value = true
|
||||
}
|
||||
|
||||
const renameReqName = () => {
|
||||
const tab = getTabRef(renameTabID.value ?? currentTabID.value)
|
||||
const tab = tabs.getTabRef(renameTabID.value ?? currentTabID.value)
|
||||
if (tab.value) {
|
||||
tab.value.document.request.name = reqName.value
|
||||
updateTab(tab.value)
|
||||
tabs.updateTab(tab.value)
|
||||
}
|
||||
showRenamingReqNameModal.value = false
|
||||
}
|
||||
@@ -284,7 +276,7 @@ const renameReqName = () => {
|
||||
*/
|
||||
const onCloseConfirmSaveTab = () => {
|
||||
if (!savingRequest.value && confirmingCloseForTabID.value) {
|
||||
closeTab(confirmingCloseForTabID.value)
|
||||
tabs.closeTab(confirmingCloseForTabID.value)
|
||||
inspectionService.deleteTabInspectorResult(confirmingCloseForTabID.value)
|
||||
confirmingCloseForTabID.value = null
|
||||
}
|
||||
@@ -294,11 +286,11 @@ const onCloseConfirmSaveTab = () => {
|
||||
* Called when the user confirms they want to save the tab
|
||||
*/
|
||||
const onResolveConfirmSaveTab = () => {
|
||||
if (currentActiveTab.value.document.saveContext) {
|
||||
if (tabs.currentActiveTab.value.document.saveContext) {
|
||||
invokeAction("request.save")
|
||||
|
||||
if (confirmingCloseForTabID.value) {
|
||||
closeTab(confirmingCloseForTabID.value)
|
||||
tabs.closeTab(confirmingCloseForTabID.value)
|
||||
confirmingCloseForTabID.value = null
|
||||
}
|
||||
} else {
|
||||
@@ -312,13 +304,14 @@ const onResolveConfirmSaveTab = () => {
|
||||
const onSaveModalClose = () => {
|
||||
savingRequest.value = false
|
||||
if (confirmingCloseForTabID.value) {
|
||||
closeTab(confirmingCloseForTabID.value)
|
||||
tabs.closeTab(confirmingCloseForTabID.value)
|
||||
confirmingCloseForTabID.value = null
|
||||
}
|
||||
}
|
||||
|
||||
const syncTabState = () => {
|
||||
if (tabStateForSync.value) loadTabsFromPersistedState(tabStateForSync.value)
|
||||
if (tabStateForSync.value)
|
||||
tabs.loadTabsFromPersistedState(tabStateForSync.value)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -329,10 +322,11 @@ const syncTabState = () => {
|
||||
*/
|
||||
function startTabStateSync(): Subscription {
|
||||
const currentUser$ = platform.auth.getCurrentUserStream()
|
||||
const tabState$ = new BehaviorSubject<PersistableRESTTabState | null>(null)
|
||||
const tabState$ =
|
||||
new BehaviorSubject<PersistableTabState<HoppRESTDocument> | null>(null)
|
||||
|
||||
watchDebounced(
|
||||
persistableTabState,
|
||||
tabs.persistableTabState,
|
||||
(state) => {
|
||||
tabState$.next(state)
|
||||
},
|
||||
@@ -429,9 +423,10 @@ function oAuthURL() {
|
||||
tokenInfo.hasOwnProperty("access_token")
|
||||
) {
|
||||
if (
|
||||
currentActiveTab.value.document.request.auth.authType === "oauth-2"
|
||||
tabs.currentActiveTab.value.document.request.auth.authType ===
|
||||
"oauth-2"
|
||||
) {
|
||||
currentActiveTab.value.document.request.auth.token =
|
||||
tabs.currentActiveTab.value.document.request.auth.token =
|
||||
tokenInfo.access_token
|
||||
}
|
||||
}
|
||||
@@ -462,7 +457,7 @@ bindRequestToURLParams()
|
||||
oAuthURL()
|
||||
|
||||
defineActionHandler("rest.request.open", ({ doc }) => {
|
||||
createNewTab(doc)
|
||||
tabs.createNewTab(doc)
|
||||
})
|
||||
|
||||
defineActionHandler("request.rename", openReqRenameModal)
|
||||
@@ -473,7 +468,7 @@ defineActionHandler("tab.close-current", () => {
|
||||
removeTab(currentTabID.value)
|
||||
})
|
||||
defineActionHandler("tab.close-other", () => {
|
||||
closeOtherTabs(currentTabID.value)
|
||||
tabs.closeOtherTabs(currentTabID.value)
|
||||
})
|
||||
defineActionHandler("tab.open-new", addNewTab)
|
||||
|
||||
|
||||
@@ -82,15 +82,18 @@ import {
|
||||
|
||||
import IconHome from "~icons/lucide/home"
|
||||
import IconRefreshCW from "~icons/lucide/refresh-cw"
|
||||
import { createNewTab } from "~/helpers/rest/tab"
|
||||
import { getDefaultRESTRequest } from "~/helpers/rest/default"
|
||||
import { platform } from "~/platform"
|
||||
import { RESTTabService } from "~/services/tab/rest"
|
||||
import { useService } from "dioc/vue"
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const t = useI18n()
|
||||
|
||||
const tabs = useService(RESTTabService)
|
||||
|
||||
const invalidLink = ref(false)
|
||||
const shortcodeID = ref("")
|
||||
|
||||
@@ -127,7 +130,7 @@ const addRequestToTab = () => {
|
||||
|
||||
const request: unknown = JSON.parse(data.right.shortcode?.request as string)
|
||||
|
||||
createNewTab({
|
||||
tabs.createNewTab({
|
||||
request: safelyExtractRESTRequest(request, getDefaultRESTRequest()),
|
||||
isDirty: false,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user