refactor: persist request handles under tab saveContext
Only the IDs (workspace, provider & request IDs) to restore the handle are stored under `localStorage` and the handle is restored back at runtime.
This commit is contained in:
@@ -277,6 +277,7 @@ const saveRequestAs = async () => {
|
|||||||
workspaceID,
|
workspaceID,
|
||||||
providerID,
|
providerID,
|
||||||
requestID,
|
requestID,
|
||||||
|
requestHandle,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -517,7 +517,7 @@ import { platform } from "~/platform"
|
|||||||
import { NewWorkspaceService } from "~/services/new-workspace"
|
import { NewWorkspaceService } from "~/services/new-workspace"
|
||||||
import { HandleRef } from "~/services/new-workspace/handle"
|
import { HandleRef } from "~/services/new-workspace/handle"
|
||||||
import { RESTCollectionViewRequest } from "~/services/new-workspace/view"
|
import { RESTCollectionViewRequest } from "~/services/new-workspace/view"
|
||||||
import { Workspace } from "~/services/new-workspace/workspace"
|
import { Workspace, WorkspaceRequest } from "~/services/new-workspace/workspace"
|
||||||
import { RESTTabService } from "~/services/tab/rest"
|
import { RESTTabService } from "~/services/tab/rest"
|
||||||
import IconImport from "~icons/lucide/folder-down"
|
import IconImport from "~icons/lucide/folder-down"
|
||||||
import IconHelpCircle from "~icons/lucide/help-circle"
|
import IconHelpCircle from "~icons/lucide/help-circle"
|
||||||
@@ -778,9 +778,15 @@ const onRemoveRootCollection = async () => {
|
|||||||
if (
|
if (
|
||||||
tab.document.saveContext?.originLocation === "workspace-user-collection"
|
tab.document.saveContext?.originLocation === "workspace-user-collection"
|
||||||
) {
|
) {
|
||||||
const { requestID } = tab.document.saveContext
|
const requestHandle = tab.document.saveContext?.requestHandle as
|
||||||
|
| HandleRef<WorkspaceRequest>["value"]
|
||||||
|
| undefined
|
||||||
|
|
||||||
if (requestID.startsWith(collectionIndexPath)) {
|
if (requestHandle?.type === "invalid") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requestHandle!.data.requestID.startsWith(collectionIndexPath)) {
|
||||||
tab.document.saveContext = null
|
tab.document.saveContext = null
|
||||||
tab.document.isDirty = true
|
tab.document.isDirty = true
|
||||||
}
|
}
|
||||||
@@ -867,6 +873,7 @@ const onAddRequest = async (requestName: string) => {
|
|||||||
workspaceID,
|
workspaceID,
|
||||||
providerID,
|
providerID,
|
||||||
requestID,
|
requestID,
|
||||||
|
requestHandle,
|
||||||
},
|
},
|
||||||
inheritedProperties: {
|
inheritedProperties: {
|
||||||
auth,
|
auth,
|
||||||
@@ -1055,15 +1062,22 @@ 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()
|
const activeTabs = tabs.getActiveTabs()
|
||||||
|
|
||||||
for (const tab of activeTabs.value) {
|
for (const tab of activeTabs.value) {
|
||||||
if (
|
if (
|
||||||
tab.document.saveContext?.originLocation === "workspace-user-collection"
|
tab.document.saveContext?.originLocation === "workspace-user-collection"
|
||||||
) {
|
) {
|
||||||
const { requestID } = tab.document.saveContext
|
const requestHandle = tab.document.saveContext?.requestHandle as
|
||||||
|
| HandleRef<WorkspaceRequest>["value"]
|
||||||
|
| undefined
|
||||||
|
|
||||||
if (requestID.startsWith(parentCollectionIndexPath)) {
|
if (requestHandle?.type === "invalid") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requestHandle!.data.requestID.startsWith(parentCollectionIndexPath)) {
|
||||||
tab.document.saveContext = null
|
tab.document.saveContext = null
|
||||||
tab.document.isDirty = true
|
tab.document.isDirty = true
|
||||||
}
|
}
|
||||||
@@ -1209,9 +1223,7 @@ const selectRequest = async (requestIndexPath: string) => {
|
|||||||
// If there is a request with this save context, switch into it
|
// If there is a request with this save context, switch into it
|
||||||
const possibleTab = tabs.getTabRefWithSaveContext({
|
const possibleTab = tabs.getTabRefWithSaveContext({
|
||||||
originLocation: "workspace-user-collection",
|
originLocation: "workspace-user-collection",
|
||||||
workspaceID,
|
requestHandle,
|
||||||
providerID,
|
|
||||||
requestID,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if (possibleTab) {
|
if (possibleTab) {
|
||||||
@@ -1226,6 +1238,7 @@ const selectRequest = async (requestIndexPath: string) => {
|
|||||||
workspaceID,
|
workspaceID,
|
||||||
providerID,
|
providerID,
|
||||||
requestID,
|
requestID,
|
||||||
|
requestHandle,
|
||||||
},
|
},
|
||||||
inheritedProperties: {
|
inheritedProperties: {
|
||||||
auth,
|
auth,
|
||||||
@@ -2177,9 +2190,18 @@ const isActiveRequest = (requestView: RESTCollectionViewRequest) => {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const { requestID } = tabs.currentActiveTab.value.document.saveContext
|
// TODO: Investigate why requestHandle is available unwrapped here
|
||||||
|
const requestHandle = tabs.currentActiveTab.value.document.saveContext
|
||||||
|
.requestHandle as HandleRef<WorkspaceRequest>["value"] | undefined
|
||||||
|
if (!requestHandle) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
return requestID === requestView.requestID
|
if (requestHandle.type === "invalid") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return requestHandle.data.requestID === requestView.requestID
|
||||||
}
|
}
|
||||||
|
|
||||||
const onSelectPick = (payload: Picked | null) => {
|
const onSelectPick = (payload: Picked | null) => {
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import { HoppRESTResponse } from "../types/HoppRESTResponse"
|
|||||||
import { HoppTestResult } from "../types/HoppTestResult"
|
import { HoppTestResult } from "../types/HoppTestResult"
|
||||||
import { RESTOptionTabs } from "~/components/http/RequestOptions.vue"
|
import { RESTOptionTabs } from "~/components/http/RequestOptions.vue"
|
||||||
import { HoppInheritedProperty } from "../types/HoppInheritedProperties"
|
import { HoppInheritedProperty } from "../types/HoppInheritedProperties"
|
||||||
|
import { HandleRef } from "~/services/new-workspace/handle"
|
||||||
|
import { WorkspaceRequest } from "~/services/new-workspace/workspace"
|
||||||
|
|
||||||
export type HoppRESTSaveContext =
|
export type HoppRESTSaveContext =
|
||||||
| {
|
| {
|
||||||
@@ -25,6 +27,11 @@ export type HoppRESTSaveContext =
|
|||||||
* Path to the request in the collection tree
|
* Path to the request in the collection tree
|
||||||
*/
|
*/
|
||||||
requestID: string
|
requestID: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle to the request open in the tab
|
||||||
|
*/
|
||||||
|
requestHandle?: HandleRef<WorkspaceRequest>
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -498,6 +498,7 @@ const HoppRESTSaveContextSchema = z.nullable(
|
|||||||
workspaceID: z.string(),
|
workspaceID: z.string(),
|
||||||
providerID: z.string(),
|
providerID: z.string(),
|
||||||
requestID: z.string(),
|
requestID: z.string(),
|
||||||
|
requestHandle: z.optional(z.record(z.unknown())),
|
||||||
})
|
})
|
||||||
.strict(),
|
.strict(),
|
||||||
z
|
z
|
||||||
|
|||||||
@@ -3,7 +3,12 @@ import { computed } from "vue"
|
|||||||
import { getDefaultRESTRequest } from "~/helpers/rest/default"
|
import { getDefaultRESTRequest } from "~/helpers/rest/default"
|
||||||
import { HoppRESTDocument, HoppRESTSaveContext } from "~/helpers/rest/document"
|
import { HoppRESTDocument, HoppRESTSaveContext } from "~/helpers/rest/document"
|
||||||
import { TabService } from "./tab"
|
import { TabService } from "./tab"
|
||||||
|
<<<<<<< HEAD
|
||||||
import { Container } from "dioc"
|
import { Container } from "dioc"
|
||||||
|
=======
|
||||||
|
import { HandleRef } from "../new-workspace/handle"
|
||||||
|
import { WorkspaceRequest } from "../new-workspace/workspace"
|
||||||
|
>>>>>>> 854ffa28 (refactor: persist request handles under tab `saveContext`)
|
||||||
|
|
||||||
export class RESTTabService extends TabService<HoppRESTDocument> {
|
export class RESTTabService extends TabService<HoppRESTDocument> {
|
||||||
public static readonly ID = "REST_TAB_SERVICE"
|
public static readonly ID = "REST_TAB_SERVICE"
|
||||||
@@ -58,7 +63,16 @@ export class RESTTabService extends TabService<HoppRESTDocument> {
|
|||||||
ctx?.originLocation === "workspace-user-collection" &&
|
ctx?.originLocation === "workspace-user-collection" &&
|
||||||
tab.document.saveContext?.originLocation === "workspace-user-collection"
|
tab.document.saveContext?.originLocation === "workspace-user-collection"
|
||||||
) {
|
) {
|
||||||
if (isEqual(ctx, tab.document.saveContext)) {
|
if (
|
||||||
|
isEqual(
|
||||||
|
ctx.requestHandle?.value,
|
||||||
|
|
||||||
|
// TODO: Investigate why requestHandle gets unwrapped
|
||||||
|
tab.document.saveContext.requestHandle as
|
||||||
|
| HandleRef<WorkspaceRequest>["value"]
|
||||||
|
| undefined
|
||||||
|
)
|
||||||
|
) {
|
||||||
return this.getTabRef(tab.id)
|
return this.getTabRef(tab.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { refWithControl } from "@vueuse/core"
|
import { refWithControl } from "@vueuse/core"
|
||||||
import { Service } from "dioc"
|
import { Service } from "dioc"
|
||||||
|
import * as E from "fp-ts/Either"
|
||||||
import { v4 as uuidV4 } from "uuid"
|
import { v4 as uuidV4 } from "uuid"
|
||||||
import {
|
import {
|
||||||
ComputedRef,
|
ComputedRef,
|
||||||
@@ -10,16 +11,23 @@ import {
|
|||||||
shallowReadonly,
|
shallowReadonly,
|
||||||
watch,
|
watch,
|
||||||
} from "vue"
|
} from "vue"
|
||||||
|
import { HoppRESTDocument } from "~/helpers/rest/document"
|
||||||
import {
|
import {
|
||||||
HoppTab,
|
HoppTab,
|
||||||
PersistableTabState,
|
PersistableTabState,
|
||||||
TabService as TabServiceInterface,
|
TabService as TabServiceInterface,
|
||||||
} from "."
|
} from "."
|
||||||
|
|
||||||
|
import { NewWorkspaceService } from "../new-workspace"
|
||||||
|
import { HandleRef } from "../new-workspace/handle"
|
||||||
|
import { WorkspaceRequest } from "../new-workspace/workspace"
|
||||||
|
|
||||||
export abstract class TabService<Doc>
|
export abstract class TabService<Doc>
|
||||||
extends Service
|
extends Service
|
||||||
implements TabServiceInterface<Doc>
|
implements TabServiceInterface<Doc>
|
||||||
{
|
{
|
||||||
|
private workspaceService = this.bind(NewWorkspaceService)
|
||||||
|
|
||||||
protected tabMap = reactive(new Map<string, HoppTab<Doc>>())
|
protected tabMap = reactive(new Map<string, HoppTab<Doc>>())
|
||||||
protected tabOrdering = ref<string[]>(["test"])
|
protected tabOrdering = ref<string[]>(["test"])
|
||||||
|
|
||||||
@@ -82,15 +90,65 @@ export abstract class TabService<Doc>
|
|||||||
this.currentTabID.value = tabID
|
this.currentTabID.value = tabID
|
||||||
}
|
}
|
||||||
|
|
||||||
public loadTabsFromPersistedState(data: PersistableTabState<Doc>): void {
|
public async loadTabsFromPersistedState(
|
||||||
|
data: PersistableTabState<Doc>
|
||||||
|
): Promise<void> {
|
||||||
if (data) {
|
if (data) {
|
||||||
this.tabMap.clear()
|
this.tabMap.clear()
|
||||||
this.tabOrdering.value = []
|
this.tabOrdering.value = []
|
||||||
|
|
||||||
for (const doc of data.orderedDocs) {
|
for (const doc of data.orderedDocs) {
|
||||||
|
let requestHandle: HandleRef<WorkspaceRequest> | null = null
|
||||||
|
let resolvedTabDoc = doc.doc
|
||||||
|
|
||||||
|
// TODO: Account for GQL
|
||||||
|
const { saveContext } = doc.doc as HoppRESTDocument
|
||||||
|
|
||||||
|
if (saveContext?.originLocation === "workspace-user-collection") {
|
||||||
|
const { providerID, requestID, workspaceID } = saveContext
|
||||||
|
|
||||||
|
if (!providerID || !workspaceID || !requestID) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const workspaceHandleResult =
|
||||||
|
await this.workspaceService.getWorkspaceHandle(
|
||||||
|
providerID!,
|
||||||
|
workspaceID!
|
||||||
|
)
|
||||||
|
|
||||||
|
if (E.isLeft(workspaceHandleResult)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const workspaceHandle = workspaceHandleResult.right
|
||||||
|
|
||||||
|
if (workspaceHandle.value.type === "invalid") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const requestHandleResult =
|
||||||
|
await this.workspaceService.getRequestHandle(
|
||||||
|
workspaceHandle,
|
||||||
|
requestID!
|
||||||
|
)
|
||||||
|
|
||||||
|
if (E.isRight(requestHandleResult)) {
|
||||||
|
requestHandle = requestHandleResult.right
|
||||||
|
|
||||||
|
resolvedTabDoc = {
|
||||||
|
...resolvedTabDoc,
|
||||||
|
saveContext: {
|
||||||
|
...saveContext,
|
||||||
|
requestHandle,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.tabMap.set(doc.tabID, {
|
this.tabMap.set(doc.tabID, {
|
||||||
id: doc.tabID,
|
id: doc.tabID,
|
||||||
document: doc.doc,
|
document: resolvedTabDoc,
|
||||||
})
|
})
|
||||||
|
|
||||||
this.tabOrdering.value.push(doc.tabID)
|
this.tabOrdering.value.push(doc.tabID)
|
||||||
@@ -99,7 +157,6 @@ export abstract class TabService<Doc>
|
|||||||
this.setActiveTab(data.lastActiveTabID)
|
this.setActiveTab(data.lastActiveTabID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public getActiveTabs(): Readonly<ComputedRef<HoppTab<Doc>[]>> {
|
public getActiveTabs(): Readonly<ComputedRef<HoppTab<Doc>[]>> {
|
||||||
return shallowReadonly(
|
return shallowReadonly(
|
||||||
computed(() => this.tabOrdering.value.map((x) => this.tabMap.get(x)!))
|
computed(() => this.tabOrdering.value.map((x) => this.tabMap.get(x)!))
|
||||||
@@ -180,13 +237,51 @@ export abstract class TabService<Doc>
|
|||||||
this.currentTabID.value = tabID
|
this.currentTabID.value = tabID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getPersistedDocument(tabDoc: Doc): Doc {
|
||||||
|
const { saveContext } = tabDoc as HoppRESTDocument
|
||||||
|
|
||||||
|
if (saveContext?.originLocation !== "workspace-user-collection") {
|
||||||
|
return tabDoc
|
||||||
|
}
|
||||||
|
|
||||||
|
const { requestHandle } = saveContext
|
||||||
|
|
||||||
|
if (!requestHandle) {
|
||||||
|
return tabDoc
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requestHandle.value.type === "invalid") {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const { requestHandle, ...rest } = saveContext
|
||||||
|
|
||||||
|
// Return the document without the handle
|
||||||
|
return {
|
||||||
|
...tabDoc,
|
||||||
|
saveContext: rest,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const { providerID, workspaceID, requestID } = requestHandle.value.data
|
||||||
|
|
||||||
|
// Return the document without the handle
|
||||||
|
return {
|
||||||
|
...tabDoc,
|
||||||
|
saveContext: {
|
||||||
|
originLocation: "workspace-user-collection",
|
||||||
|
requestID,
|
||||||
|
providerID,
|
||||||
|
workspaceID,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public persistableTabState = computed<PersistableTabState<Doc>>(() => ({
|
public persistableTabState = computed<PersistableTabState<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
|
||||||
return {
|
return {
|
||||||
tabID: tab.id,
|
tabID: tab.id,
|
||||||
doc: tab.document,
|
doc: this.getPersistedDocument(tab.document),
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|||||||
Reference in New Issue
Block a user