refactor: keep tab dirty status logic at the page level
This commit is contained in:
@@ -772,27 +772,6 @@ const onRemoveRootCollection = async () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const activeTabs = tabs.getActiveTabs()
|
|
||||||
|
|
||||||
for (const tab of activeTabs.value) {
|
|
||||||
if (
|
|
||||||
tab.document.saveContext?.originLocation === "workspace-user-collection"
|
|
||||||
) {
|
|
||||||
const requestHandle = tab.document.saveContext?.requestHandle as
|
|
||||||
| HandleRef<WorkspaceRequest>["value"]
|
|
||||||
| undefined
|
|
||||||
|
|
||||||
if (requestHandle?.type === "invalid") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (requestHandle!.data.requestID.startsWith(collectionIndexPath)) {
|
|
||||||
tab.document.saveContext = null
|
|
||||||
tab.document.isDirty = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toast.success(t("state.deleted"))
|
toast.success(t("state.deleted"))
|
||||||
displayConfirmModal(false)
|
displayConfirmModal(false)
|
||||||
}
|
}
|
||||||
@@ -1062,28 +1041,6 @@ const onRemoveChildCollection = async () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Tab holding a request under the collection should be aware of the parent collection invalidation and toggle the dirty state
|
|
||||||
const activeTabs = tabs.getActiveTabs()
|
|
||||||
|
|
||||||
for (const tab of activeTabs.value) {
|
|
||||||
if (
|
|
||||||
tab.document.saveContext?.originLocation === "workspace-user-collection"
|
|
||||||
) {
|
|
||||||
const requestHandle = tab.document.saveContext?.requestHandle as
|
|
||||||
| HandleRef<WorkspaceRequest>["value"]
|
|
||||||
| undefined
|
|
||||||
|
|
||||||
if (requestHandle?.type === "invalid") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (requestHandle!.data.requestID.startsWith(parentCollectionIndexPath)) {
|
|
||||||
tab.document.saveContext = null
|
|
||||||
tab.document.isDirty = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toast.success(t("state.deleted"))
|
toast.success(t("state.deleted"))
|
||||||
displayConfirmModal(false)
|
displayConfirmModal(false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<HoppSmartWindow
|
<HoppSmartWindow
|
||||||
v-for="tab in activeTabs"
|
v-for="tab in activeTabs"
|
||||||
:id="tab.id"
|
:id="tab.id"
|
||||||
:key="`${tab.id}-${tab.document.isDirty}`"
|
:key="tab.id"
|
||||||
:label="tab.document.request.name"
|
:label="tab.document.request.name"
|
||||||
:is-removable="activeTabs.length > 1"
|
:is-removable="activeTabs.length > 1"
|
||||||
:close-visibility="'hover'"
|
:close-visibility="'hover'"
|
||||||
@@ -26,12 +26,11 @@
|
|||||||
@close-tab="removeTab(tab.id)"
|
@close-tab="removeTab(tab.id)"
|
||||||
@close-other-tabs="closeOtherTabsAction(tab.id)"
|
@close-other-tabs="closeOtherTabsAction(tab.id)"
|
||||||
@duplicate-tab="duplicateTab(tab.id)"
|
@duplicate-tab="duplicateTab(tab.id)"
|
||||||
@share-tab-request="shareTabRequest(tab.id)"
|
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #suffix>
|
<template #suffix>
|
||||||
<span
|
<span
|
||||||
v-if="tab.document.isDirty"
|
v-if="getTabDirtyStatus(tab)"
|
||||||
class="flex w-4 items-center justify-center text-secondary group-hover:hidden"
|
class="flex w-4 items-center justify-center text-secondary group-hover:hidden"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
@@ -64,6 +63,13 @@
|
|||||||
@submit="renameReqName"
|
@submit="renameReqName"
|
||||||
@hide-modal="showRenamingReqNameModal = false"
|
@hide-modal="showRenamingReqNameModal = false"
|
||||||
/>
|
/>
|
||||||
|
<HoppSmartConfirmModal
|
||||||
|
:show="confirmingCloseForTabID !== null"
|
||||||
|
:confirm="t('modal.close_unsaved_tab')"
|
||||||
|
:title="t('confirm.save_unsaved_tab')"
|
||||||
|
@hide-modal="onCloseConfirmSaveTab"
|
||||||
|
@resolve="onResolveConfirmSaveTab"
|
||||||
|
/>
|
||||||
<HoppSmartConfirmModal
|
<HoppSmartConfirmModal
|
||||||
:show="confirmingCloseAllTabs"
|
:show="confirmingCloseAllTabs"
|
||||||
:confirm="t('modal.close_unsaved_tab')"
|
:confirm="t('modal.close_unsaved_tab')"
|
||||||
@@ -71,36 +77,6 @@
|
|||||||
@hide-modal="confirmingCloseAllTabs = false"
|
@hide-modal="confirmingCloseAllTabs = false"
|
||||||
@resolve="onResolveConfirmCloseAllTabs"
|
@resolve="onResolveConfirmCloseAllTabs"
|
||||||
/>
|
/>
|
||||||
<HoppSmartModal
|
|
||||||
v-if="confirmingCloseForTabID !== null"
|
|
||||||
dialog
|
|
||||||
role="dialog"
|
|
||||||
aria-modal="true"
|
|
||||||
:title="t('modal.close_unsaved_tab')"
|
|
||||||
@close="confirmingCloseForTabID = null"
|
|
||||||
>
|
|
||||||
<template #body>
|
|
||||||
<div class="text-center">
|
|
||||||
{{ t("confirm.save_unsaved_tab") }}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #footer>
|
|
||||||
<span class="flex space-x-2">
|
|
||||||
<HoppButtonPrimary
|
|
||||||
v-focus
|
|
||||||
:label="t?.('action.yes')"
|
|
||||||
outline
|
|
||||||
@click="onResolveConfirmSaveTab"
|
|
||||||
/>
|
|
||||||
<HoppButtonSecondary
|
|
||||||
:label="t?.('action.no')"
|
|
||||||
filled
|
|
||||||
outline
|
|
||||||
@click="onCloseConfirmSaveTab"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
</HoppSmartModal>
|
|
||||||
<CollectionsSaveRequest
|
<CollectionsSaveRequest
|
||||||
v-if="savingRequest"
|
v-if="savingRequest"
|
||||||
mode="rest"
|
mode="rest"
|
||||||
@@ -120,23 +96,37 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useI18n } from "@composables/i18n"
|
import { useI18n } from "@composables/i18n"
|
||||||
import { safelyExtractRESTRequest } from "@hoppscotch/data"
|
import { safelyExtractRESTRequest } from "@hoppscotch/data"
|
||||||
|
import { watchDebounced } from "@vueuse/core"
|
||||||
import { useService } from "dioc/vue"
|
import { useService } from "dioc/vue"
|
||||||
import { cloneDeep } from "lodash-es"
|
import { cloneDeep } from "lodash-es"
|
||||||
import { onMounted, ref } from "vue"
|
import {
|
||||||
|
BehaviorSubject,
|
||||||
|
EMPTY,
|
||||||
|
Subscription,
|
||||||
|
audit,
|
||||||
|
combineLatest,
|
||||||
|
from,
|
||||||
|
map,
|
||||||
|
} from "rxjs"
|
||||||
|
import { onBeforeUnmount, onMounted, ref } from "vue"
|
||||||
import { useRoute } from "vue-router"
|
import { useRoute } from "vue-router"
|
||||||
|
import { onLoggedIn } from "~/composables/auth"
|
||||||
import { useReadonlyStream } from "~/composables/stream"
|
import { useReadonlyStream } from "~/composables/stream"
|
||||||
|
import { useToast } from "~/composables/toast"
|
||||||
import { translateExtURLParams } from "~/helpers/RESTExtURLParams"
|
import { translateExtURLParams } from "~/helpers/RESTExtURLParams"
|
||||||
import { defineActionHandler, invokeAction } from "~/helpers/actions"
|
import { defineActionHandler, invokeAction } from "~/helpers/actions"
|
||||||
import { getDefaultRESTRequest } from "~/helpers/rest/default"
|
import { getDefaultRESTRequest } from "~/helpers/rest/default"
|
||||||
import { HoppRESTDocument } from "~/helpers/rest/document"
|
import { HoppRESTDocument } from "~/helpers/rest/document"
|
||||||
|
import {
|
||||||
|
changeCurrentSyncStatus,
|
||||||
|
currentSyncingStatus$,
|
||||||
|
} from "~/newstore/syncing"
|
||||||
import { platform } from "~/platform"
|
import { platform } from "~/platform"
|
||||||
import { InspectionService } from "~/services/inspection"
|
import { InspectionService } from "~/services/inspection"
|
||||||
import { EnvironmentInspectorService } from "~/services/inspection/inspectors/environment.inspector"
|
import { EnvironmentInspectorService } from "~/services/inspection/inspectors/environment.inspector"
|
||||||
import { HeaderInspectorService } from "~/services/inspection/inspectors/header.inspector"
|
import { HeaderInspectorService } from "~/services/inspection/inspectors/header.inspector"
|
||||||
import { ResponseInspectorService } from "~/services/inspection/inspectors/response.inspector"
|
import { ResponseInspectorService } from "~/services/inspection/inspectors/response.inspector"
|
||||||
import { HandleRef } from "~/services/new-workspace/handle"
|
import { HoppTab, PersistableTabState } from "~/services/tab"
|
||||||
import { WorkspaceRequest } from "~/services/new-workspace/workspace"
|
|
||||||
import { HoppTab } from "~/services/tab"
|
|
||||||
import { RESTTabService } from "~/services/tab/rest"
|
import { RESTTabService } from "~/services/tab/rest"
|
||||||
|
|
||||||
const savingRequest = ref(false)
|
const savingRequest = ref(false)
|
||||||
@@ -149,16 +139,12 @@ const exceptedTabID = ref<string | null>(null)
|
|||||||
const renameTabID = ref<string | null>(null)
|
const renameTabID = ref<string | null>(null)
|
||||||
|
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
const tabs = useService(RESTTabService)
|
const tabs = useService(RESTTabService)
|
||||||
|
|
||||||
const currentTabID = tabs.currentTabID
|
const currentTabID = tabs.currentTabID
|
||||||
|
|
||||||
const currentUser = useReadonlyStream(
|
|
||||||
platform.auth.getCurrentUserStream(),
|
|
||||||
platform.auth.getCurrentUser()
|
|
||||||
)
|
|
||||||
|
|
||||||
type PopupDetails = {
|
type PopupDetails = {
|
||||||
show: boolean
|
show: boolean
|
||||||
position: {
|
position: {
|
||||||
@@ -179,6 +165,12 @@ const contextMenu = ref<PopupDetails>({
|
|||||||
|
|
||||||
const activeTabs = tabs.getActiveTabs()
|
const activeTabs = tabs.getActiveTabs()
|
||||||
|
|
||||||
|
const confirmSync = useReadonlyStream(currentSyncingStatus$, {
|
||||||
|
isInitialSync: false,
|
||||||
|
shouldSync: true,
|
||||||
|
})
|
||||||
|
const tabStateForSync = ref<PersistableTabState<HoppRESTDocument> | null>(null)
|
||||||
|
|
||||||
function bindRequestToURLParams() {
|
function bindRequestToURLParams() {
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
// Get URL parameters and set that as the request
|
// Get URL parameters and set that as the request
|
||||||
@@ -218,7 +210,7 @@ const inspectionService = useService(InspectionService)
|
|||||||
const removeTab = (tabID: string) => {
|
const removeTab = (tabID: string) => {
|
||||||
const tabState = tabs.getTabRef(tabID).value
|
const tabState = tabs.getTabRef(tabID).value
|
||||||
|
|
||||||
if (tabState.document.isDirty) {
|
if (getTabDirtyStatus(tabState)) {
|
||||||
confirmingCloseForTabID.value = tabID
|
confirmingCloseForTabID.value = tabID
|
||||||
} else {
|
} else {
|
||||||
tabs.closeTab(tabState.id)
|
tabs.closeTab(tabState.id)
|
||||||
@@ -227,8 +219,10 @@ const removeTab = (tabID: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const closeOtherTabsAction = (tabID: string) => {
|
const closeOtherTabsAction = (tabID: string) => {
|
||||||
const isTabDirty = tabs.getTabRef(tabID).value?.document.isDirty
|
|
||||||
const dirtyTabCount = tabs.getDirtyTabsCount()
|
const dirtyTabCount = tabs.getDirtyTabsCount()
|
||||||
|
|
||||||
|
const isTabDirty = getTabDirtyStatus(tabs.getTabRef(tabID).value)
|
||||||
|
|
||||||
// If current tab is dirty, so we need to subtract 1 from the dirty tab count
|
// If current tab is dirty, so we need to subtract 1 from the dirty tab count
|
||||||
const balanceDirtyTabCount = isTabDirty ? dirtyTabCount - 1 : dirtyTabCount
|
const balanceDirtyTabCount = isTabDirty ? dirtyTabCount - 1 : dirtyTabCount
|
||||||
|
|
||||||
@@ -301,12 +295,7 @@ const onResolveConfirmSaveTab = () => {
|
|||||||
if (
|
if (
|
||||||
!saveContext ||
|
!saveContext ||
|
||||||
(saveContext.originLocation === "workspace-user-collection" &&
|
(saveContext.originLocation === "workspace-user-collection" &&
|
||||||
// `requestHandle` gets unwrapped here
|
saveContext.requestHandle?.value.type === "invalid")
|
||||||
(
|
|
||||||
saveContext.requestHandle as
|
|
||||||
| HandleRef<WorkspaceRequest>["value"]
|
|
||||||
| undefined
|
|
||||||
)?.type === "invalid")
|
|
||||||
) {
|
) {
|
||||||
return (savingRequest.value = true)
|
return (savingRequest.value = true)
|
||||||
}
|
}
|
||||||
@@ -330,17 +319,120 @@ const onSaveModalClose = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const shareTabRequest = (tabID: string) => {
|
const syncTabState = () => {
|
||||||
const tab = tabs.getTabRef(tabID)
|
if (tabStateForSync.value)
|
||||||
if (tab.value) {
|
tabs.loadTabsFromPersistedState(tabStateForSync.value)
|
||||||
if (currentUser.value) {
|
}
|
||||||
invokeAction("share.request", {
|
|
||||||
request: tab.value.document.request,
|
const getTabDirtyStatus = (tab: HoppTab<HoppRESTDocument>) => {
|
||||||
})
|
if (tab.document.isDirty) {
|
||||||
} else {
|
return true
|
||||||
invokeAction("modals.login.toggle")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
tab.document.saveContext?.originLocation === "workspace-user-collection" &&
|
||||||
|
tab.document.saveContext.requestHandle?.value.type === "invalid"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs sync of the REST Tab session with Firestore.
|
||||||
|
*
|
||||||
|
* @returns A subscription to the sync observable stream.
|
||||||
|
* Unsubscribe to stop syncing.
|
||||||
|
*/
|
||||||
|
function startTabStateSync(): Subscription {
|
||||||
|
const currentUser$ = platform.auth.getCurrentUserStream()
|
||||||
|
const tabState$ =
|
||||||
|
new BehaviorSubject<PersistableTabState<HoppRESTDocument> | null>(null)
|
||||||
|
|
||||||
|
watchDebounced(
|
||||||
|
tabs.persistableTabState,
|
||||||
|
(state) => {
|
||||||
|
tabState$.next(state)
|
||||||
|
},
|
||||||
|
{ debounce: 500, deep: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
const sub = combineLatest([currentUser$, tabState$])
|
||||||
|
.pipe(
|
||||||
|
map(([user, tabState]) =>
|
||||||
|
user && tabState
|
||||||
|
? from(platform.sync.tabState.writeCurrentTabState(user, tabState))
|
||||||
|
: EMPTY
|
||||||
|
),
|
||||||
|
audit((x) => x)
|
||||||
|
)
|
||||||
|
.subscribe(() => {
|
||||||
|
// NOTE: This subscription should be kept
|
||||||
|
})
|
||||||
|
|
||||||
|
return sub
|
||||||
|
}
|
||||||
|
|
||||||
|
const showSyncToast = () => {
|
||||||
|
toast.show(t("confirm.sync"), {
|
||||||
|
duration: 0,
|
||||||
|
action: [
|
||||||
|
{
|
||||||
|
text: `${t("action.yes")}`,
|
||||||
|
onClick: (_, toastObject) => {
|
||||||
|
syncTabState()
|
||||||
|
changeCurrentSyncStatus({
|
||||||
|
isInitialSync: true,
|
||||||
|
shouldSync: true,
|
||||||
|
})
|
||||||
|
toastObject.goAway(0)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: `${t("action.no")}`,
|
||||||
|
onClick: (_, toastObject) => {
|
||||||
|
changeCurrentSyncStatus({
|
||||||
|
isInitialSync: true,
|
||||||
|
shouldSync: false,
|
||||||
|
})
|
||||||
|
toastObject.goAway(0)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupTabStateSync() {
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
// Subscription to request sync
|
||||||
|
let sub: Subscription | null = null
|
||||||
|
|
||||||
|
// Load request on login resolve and start sync
|
||||||
|
onLoggedIn(async () => {
|
||||||
|
if (
|
||||||
|
Object.keys(route.query).length === 0 &&
|
||||||
|
!(route.query.code || route.query.error)
|
||||||
|
) {
|
||||||
|
const tabStateFromSync =
|
||||||
|
await platform.sync.tabState.loadTabStateFromSync()
|
||||||
|
|
||||||
|
if (tabStateFromSync && !confirmSync.value.isInitialSync) {
|
||||||
|
tabStateForSync.value = tabStateFromSync
|
||||||
|
showSyncToast()
|
||||||
|
// Have to set isInitialSync to true here because the toast is shown
|
||||||
|
// and the user does not click on any of the actions
|
||||||
|
changeCurrentSyncStatus({
|
||||||
|
isInitialSync: true,
|
||||||
|
shouldSync: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub = startTabStateSync()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Stop subscription to stop syncing
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
sub?.unsubscribe()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
defineActionHandler("contextmenu.open", ({ position, text }) => {
|
defineActionHandler("contextmenu.open", ({ position, text }) => {
|
||||||
@@ -359,6 +451,7 @@ defineActionHandler("contextmenu.open", ({ position, text }) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
setupTabStateSync()
|
||||||
bindRequestToURLParams()
|
bindRequestToURLParams()
|
||||||
|
|
||||||
defineActionHandler("rest.request.open", ({ doc }) => {
|
defineActionHandler("rest.request.open", ({ doc }) => {
|
||||||
@@ -384,5 +477,3 @@ for (const inspectorDef of platform.additionalInspectors ?? []) {
|
|||||||
useService(inspectorDef.service)
|
useService(inspectorDef.service)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
import { HandleRef } from "~/services/new-workspace/handle" import {
|
|
||||||
WorkspaceRequest } from "~/services/new-workspace/workspace"
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
computed,
|
computed,
|
||||||
effectScope,
|
effectScope,
|
||||||
markRaw,
|
markRaw,
|
||||||
|
nextTick,
|
||||||
ref,
|
ref,
|
||||||
shallowRef,
|
shallowRef,
|
||||||
watch,
|
watch,
|
||||||
@@ -302,15 +303,18 @@ export class PersonalWorkspaceProviderService
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const handle of this.issuedHandles) {
|
for (const [idx, handle] of this.issuedHandles.entries()) {
|
||||||
if (handle.value.type === "invalid") continue
|
if (handle.value.type === "invalid") continue
|
||||||
|
|
||||||
if ("requestID" in handle.value.data) {
|
if ("requestID" in handle.value.data) {
|
||||||
if (handle.value.data.requestID.startsWith(collectionID)) {
|
if (handle.value.data.requestID.startsWith(collectionID)) {
|
||||||
handle.value = {
|
// @ts-expect-error - We're deleting the data to invalidate the handle
|
||||||
type: "invalid",
|
delete this.issuedHandles[idx].value.data
|
||||||
reason: "REQUEST_INVALIDATED",
|
|
||||||
}
|
this.issuedHandles[idx].value.type = "invalid"
|
||||||
|
|
||||||
|
// @ts-expect-error - Setting the handle invalidation reason
|
||||||
|
this.issuedHandles[idx].value.reason = "REQUEST_INVALIDATED"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -403,15 +407,18 @@ export class PersonalWorkspaceProviderService
|
|||||||
|
|
||||||
removeRESTRequest(collectionID, requestIndex, requestToRemove?.id)
|
removeRESTRequest(collectionID, requestIndex, requestToRemove?.id)
|
||||||
|
|
||||||
for (const handle of this.issuedHandles) {
|
for (const [idx, handle] of this.issuedHandles.entries()) {
|
||||||
if (handle.value.type === "invalid") continue
|
if (handle.value.type === "invalid") continue
|
||||||
|
|
||||||
if ("requestID" in handle.value.data) {
|
if ("requestID" in handle.value.data) {
|
||||||
if (handle.value.data.requestID === requestID) {
|
if (handle.value.data.requestID === requestID) {
|
||||||
handle.value = {
|
// @ts-expect-error - We're deleting the data to invalidate the handle
|
||||||
type: "invalid",
|
delete this.issuedHandles[idx].value.data
|
||||||
reason: "REQUEST_INVALIDATED",
|
|
||||||
}
|
this.issuedHandles[idx].value.type = "invalid"
|
||||||
|
|
||||||
|
// @ts-expect-error - Setting the handle invalidation reason
|
||||||
|
this.issuedHandles[idx].value.reason = "REQUEST_INVALIDATED"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,12 +35,11 @@ export class RESTTabService extends TabService<HoppRESTDocument> {
|
|||||||
lastActiveTabID: this.currentTabID.value,
|
lastActiveTabID: this.currentTabID.value,
|
||||||
orderedDocs: this.tabOrdering.value.map((tabID) => {
|
orderedDocs: this.tabOrdering.value.map((tabID) => {
|
||||||
const tab = this.tabMap.get(tabID)! // tab ordering is guaranteed to have value for this key
|
const tab = this.tabMap.get(tabID)! // tab ordering is guaranteed to have value for this key
|
||||||
const resolvedTabData = this.getResolvedTabData(tab)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tabID: tab.id,
|
tabID: tab.id,
|
||||||
doc: {
|
doc: {
|
||||||
...this.getPersistedDocument(resolvedTabData.document),
|
...this.getPersistedDocument(tab.document),
|
||||||
response: null,
|
response: null,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,10 +46,7 @@ export abstract class TabService<Doc>
|
|||||||
})
|
})
|
||||||
|
|
||||||
public currentActiveTab = computed(() => {
|
public currentActiveTab = computed(() => {
|
||||||
const tab = this.tabMap.get(this.currentTabID.value)!
|
return this.tabMap.get(this.currentTabID.value)!
|
||||||
return this.getResolvedTabData(
|
|
||||||
tab as HoppTab<HoppRESTDocument | HoppGQLDocument>
|
|
||||||
)
|
|
||||||
}) // Guaranteed to not be undefined
|
}) // Guaranteed to not be undefined
|
||||||
|
|
||||||
protected watchCurrentTabID() {
|
protected watchCurrentTabID() {
|
||||||
@@ -87,15 +84,7 @@ export abstract class TabService<Doc>
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getActiveTab(): HoppTab<Doc> | null {
|
public getActiveTab(): HoppTab<Doc> | null {
|
||||||
const tab = this.tabMap.get(this.currentTabID.value)
|
return this.tabMap.get(this.currentTabID.value) ?? null
|
||||||
|
|
||||||
if (!tab) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.getResolvedTabData(
|
|
||||||
tab as HoppTab<HoppRESTDocument | HoppGQLDocument>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public setActiveTab(tabID: string): void {
|
public setActiveTab(tabID: string): void {
|
||||||
@@ -172,13 +161,7 @@ export abstract class TabService<Doc>
|
|||||||
public getActiveTabs(): Readonly<ComputedRef<HoppTab<Doc>[]>> {
|
public getActiveTabs(): Readonly<ComputedRef<HoppTab<Doc>[]>> {
|
||||||
return shallowReadonly(
|
return shallowReadonly(
|
||||||
computed(() =>
|
computed(() =>
|
||||||
this.tabOrdering.value.map((x) => {
|
this.tabOrdering.value.map((x) => this.tabMap.get(x) as HoppTab<Doc>)
|
||||||
const tab = this.tabMap.get(x) as HoppTab<
|
|
||||||
HoppRESTDocument | HoppGQLDocument
|
|
||||||
>
|
|
||||||
|
|
||||||
return this.getResolvedTabData(tab)
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -186,13 +169,11 @@ export abstract class TabService<Doc>
|
|||||||
public getTabRef(tabID: string) {
|
public getTabRef(tabID: string) {
|
||||||
return computed({
|
return computed({
|
||||||
get: () => {
|
get: () => {
|
||||||
const result = this.tabMap.get(tabID) as HoppTab<
|
const result = this.tabMap.get(tabID)
|
||||||
HoppRESTDocument | HoppGQLDocument
|
|
||||||
>
|
|
||||||
|
|
||||||
if (result === undefined) throw new Error(`Invalid tab id: ${tabID}`)
|
if (result === undefined) throw new Error(`Invalid tab id: ${tabID}`)
|
||||||
|
|
||||||
return this.getResolvedTabData(result)
|
return result
|
||||||
},
|
},
|
||||||
set: (value) => {
|
set: (value) => {
|
||||||
return this.tabMap.set(tabID, value)
|
return this.tabMap.set(tabID, value)
|
||||||
@@ -304,13 +285,10 @@ export abstract class TabService<Doc>
|
|||||||
lastActiveTabID: this.currentTabID.value,
|
lastActiveTabID: this.currentTabID.value,
|
||||||
orderedDocs: this.tabOrdering.value.map((tabID) => {
|
orderedDocs: this.tabOrdering.value.map((tabID) => {
|
||||||
const tab = this.tabMap.get(tabID)! // tab ordering is guaranteed to have value for this key
|
const tab = this.tabMap.get(tabID)! // tab ordering is guaranteed to have value for this key
|
||||||
const resolvedTabData = this.getResolvedTabData(
|
|
||||||
tab as HoppTab<HoppRESTDocument | HoppGQLDocument>
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tabID: tab.id,
|
tabID: tab.id,
|
||||||
doc: this.getPersistedDocument(resolvedTabData.document),
|
doc: this.getPersistedDocument(tab.document),
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
@@ -328,32 +306,4 @@ export abstract class TabService<Doc>
|
|||||||
if (!this.tabMap.has(id)) return id
|
if (!this.tabMap.has(id)) return id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getResolvedTabData(
|
|
||||||
tab: HoppTab<HoppRESTDocument | HoppGQLDocument>
|
|
||||||
): HoppTab<Doc> {
|
|
||||||
if (
|
|
||||||
tab.document.isDirty ||
|
|
||||||
!tab.document.saveContext ||
|
|
||||||
tab.document.saveContext.originLocation !== "workspace-user-collection"
|
|
||||||
) {
|
|
||||||
return tab as HoppTab<Doc>
|
|
||||||
}
|
|
||||||
|
|
||||||
const requestHandle = tab.document.saveContext.requestHandle as
|
|
||||||
| HandleRef<WorkspaceRequest>["value"]
|
|
||||||
| undefined
|
|
||||||
|
|
||||||
if (!requestHandle) {
|
|
||||||
return tab as HoppTab<Doc>
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...tab,
|
|
||||||
document: {
|
|
||||||
...tab.document,
|
|
||||||
isDirty: requestHandle.type === "invalid",
|
|
||||||
},
|
|
||||||
} as HoppTab<Doc>
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user