fix: spotlight actions on graphql (#3299)

* fix: spotlight actions for graphql

* fix: environment actions

* fix: gql rename request

* fix: graphql spotlight actions

* fix: tab shortcuts not working properly

* fix: only show download and copy response when there is a response

---------

Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
This commit is contained in:
Anwarul Islam
2023-08-28 21:10:01 +06:00
committed by GitHub
parent 0eacd6763b
commit b953b32ff4
12 changed files with 240 additions and 108 deletions

View File

@@ -670,7 +670,13 @@
"tab_headers": "Headers tab", "tab_headers": "Headers tab",
"tab_authorization": "Authorization tab", "tab_authorization": "Authorization tab",
"tab_pre_request_script": "Pre-request script tab", "tab_pre_request_script": "Pre-request script tab",
"tab_tests": "Tests tab" "tab_tests": "Tests tab",
"tab_query": "Query tab",
"tab_variables": "Variables tab"
},
"graphql": {
"connect": "Connect to server",
"disconnect": "Disconnect from server"
}, },
"response": { "response": {
"copy": "Copy response", "copy": "Copy response",

View File

@@ -71,6 +71,7 @@ import { connect } from "~/helpers/graphql/connection"
import { disconnect } from "~/helpers/graphql/connection" import { disconnect } from "~/helpers/graphql/connection"
import { InterceptorService } from "~/services/interceptor.service" import { InterceptorService } from "~/services/interceptor.service"
import { useService } from "dioc/vue" import { useService } from "dioc/vue"
import { defineActionHandler } from "~/helpers/actions"
const t = useI18n() const t = useI18n()
@@ -140,4 +141,12 @@ const cancelSwitch = () => {
if (connected.value) disconnect() if (connected.value) disconnect()
connectionSwitchModal.value = false connectionSwitchModal.value = false
} }
defineActionHandler(
"gql.connect",
gqlConnect,
computed(() => !connected.value)
)
defineActionHandler("gql.disconnect", disconnect, connected)
</script> </script>

View File

@@ -69,8 +69,8 @@ import { useService } from "dioc/vue"
import { InterceptorService } from "~/services/interceptor.service" import { InterceptorService } from "~/services/interceptor.service"
import { editGraphqlRequest } from "~/newstore/collections" import { editGraphqlRequest } from "~/newstore/collections"
type OptionTabs = "query" | "headers" | "variables" | "authorization" export type GQLOptionTabs = "query" | "headers" | "variables" | "authorization"
const selectedOptionTab = ref<OptionTabs>("query") const selectedOptionTab = ref<GQLOptionTabs>("query")
const interceptorService = useService(InterceptorService) const interceptorService = useService(InterceptorService)
const t = useI18n() const t = useI18n()
@@ -206,4 +206,8 @@ defineActionHandler("request.save-as", () => {
showSaveRequestModal.value = true showSaveRequestModal.value = true
}) })
defineActionHandler("request.reset", clearGQLQuery) defineActionHandler("request.reset", clearGQLQuery)
defineActionHandler("request.open-tab", ({ tab }) => {
selectedOptionTab.value = tab as GQLOptionTabs
})
</script> </script>

View File

@@ -128,8 +128,14 @@ const downloadResponse = (str: string) => {
}, 1000) }, 1000)
} }
defineActionHandler("response.file.download", () => defineActionHandler(
downloadResponse(responseString.value) "response.file.download",
() => downloadResponse(responseString.value),
computed(() => !!props.response && props.response.length > 0)
)
defineActionHandler(
"response.copy",
() => copyResponse(responseString.value),
computed(() => !!props.response && props.response.length > 0)
) )
defineActionHandler("response.copy", () => copyResponse(responseString.value))
</script> </script>

View File

@@ -101,6 +101,6 @@ const newActiveHeadersCount$ = computed(() => {
}) })
defineActionHandler("request.open-tab", ({ tab }) => { defineActionHandler("request.open-tab", ({ tab }) => {
selectedOptionsTab.value = tab selectedOptionsTab.value = tab as RequestOptionTabs
}) })
</script> </script>

View File

@@ -2,12 +2,14 @@
* For example, sending a request. * For example, sending a request.
*/ */
import { Ref, onBeforeUnmount, onMounted, watch } from "vue" import { Ref, onBeforeUnmount, onMounted, reactive, watch } from "vue"
import { BehaviorSubject } from "rxjs" import { BehaviorSubject } from "rxjs"
import { HoppRESTDocument } from "./rest/document" import { HoppRESTDocument } from "./rest/document"
import { HoppGQLRequest, HoppRESTRequest } from "@hoppscotch/data" import { HoppGQLRequest, HoppRESTRequest } from "@hoppscotch/data"
import { RequestOptionTabs } from "~/components/http/RequestOptions.vue" import { RequestOptionTabs } from "~/components/http/RequestOptions.vue"
import { HoppGQLSaveContext } from "./graphql/document" import { HoppGQLSaveContext } from "./graphql/document"
import { GQLOptionTabs } from "~/components/graphql/RequestOptions.vue"
import { computed } from "vue"
export type HoppAction = export type HoppAction =
| "contextmenu.open" // Send/Cancel a Hoppscotch Request | "contextmenu.open" // Send/Cancel a Hoppscotch Request
@@ -16,7 +18,7 @@ export type HoppAction =
| "request.copy-link" // Copy Request Link | "request.copy-link" // Copy Request Link
| "request.save" // Save to Collections | "request.save" // Save to Collections
| "request.save-as" // Save As | "request.save-as" // Save As
| "rest.request.rename" // Rename | "request.rename" // Rename request on REST or GraphQL
| "request.method.next" // Select Next Method | "request.method.next" // Select Next Method
| "request.method.prev" // Select Previous Method | "request.method.prev" // Select Previous Method
| "request.method.get" // Select GET Method | "request.method.get" // Select GET Method
@@ -26,6 +28,11 @@ export type HoppAction =
| "request.method.delete" // Select DELETE Method | "request.method.delete" // Select DELETE Method
| "request.import-curl" // Import cURL | "request.import-curl" // Import cURL
| "request.show-code" // Show generated code | "request.show-code" // Show generated code
| "gql.connect" // Connect to GraphQL endpoint given
| "gql.disconnect" // Disconnect from GraphQL endpoint given
| "tab.close-current" // Close current tab
| "tab.close-other" // Close other tabs
| "tab.open-new" // Open new tab
| "collection.new" // Create root collection | "collection.new" // Create root collection
| "flyouts.chat.open" // Shows the keybinds flyout | "flyouts.chat.open" // Shows the keybinds flyout
| "flyouts.keybinds.toggle" // Shows the keybinds flyout | "flyouts.keybinds.toggle" // Shows the keybinds flyout
@@ -106,11 +113,11 @@ type HoppActionArgsMap = {
request: HoppGQLRequest request: HoppGQLRequest
} }
"request.open-tab": { "request.open-tab": {
tab: RequestOptionTabs tab: RequestOptionTabs | GQLOptionTabs
} }
"request.duplicate-tab": { "tab.duplicate-tab": {
tabID: string tabID?: string
} }
"gql.request.open": { "gql.request.open": {
@@ -150,7 +157,7 @@ type BoundActionList = {
[A in HoppAction | HoppActionWithArgs]?: Array<ActionFunc<A>> [A in HoppAction | HoppActionWithArgs]?: Array<ActionFunc<A>>
} }
const boundActions: BoundActionList = {} const boundActions: BoundActionList = reactive({})
export const activeActions$ = new BehaviorSubject< export const activeActions$ = new BehaviorSubject<
(HoppAction | HoppActionWithArgs)[] (HoppAction | HoppActionWithArgs)[]
@@ -206,6 +213,15 @@ export function unbindAction<A extends HoppAction | HoppActionWithArgs>(
activeActions$.next(Object.keys(boundActions) as HoppAction[]) activeActions$.next(Object.keys(boundActions) as HoppAction[])
} }
/**
* Returns a ref that indicates whether a given action is bound at a given time
*
* @param action The action to check
*/
export function isActionBound(action: HoppAction): Ref<boolean> {
return computed(() => !!boundActions[action])
}
/** /**
* A composable function that defines a component can handle a given * A composable function that defines a component can handle a given
* HoppAction. The handler will be bound when the component is mounted * HoppAction. The handler will be bound when the component is mounted

View File

@@ -27,7 +27,7 @@
@open-rename-modal="openReqRenameModal(tab)" @open-rename-modal="openReqRenameModal(tab)"
@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)" @duplicate-tab="duplicateTab(tab.id)"
/> />
</template> </template>
@@ -203,12 +203,15 @@ const renameReqName = () => {
showRenamingReqNameModalForTabID.value = undefined showRenamingReqNameModalForTabID.value = undefined
} }
const duplicateTab = (tab: HoppGQLTab) => { const duplicateTab = (tabID: string) => {
const newTab = createNewTab({ const tab = getTabRef(tabID)
request: tab.document.request, if (tab.value) {
isDirty: true, const newTab = createNewTab({
}) request: tab.value.document.request,
currentTabID.value = newTab.id isDirty: true,
})
currentTabID.value = newTab.id
}
} }
defineActionHandler("gql.request.open", ({ request, saveContext }) => { defineActionHandler("gql.request.open", ({ request, saveContext }) => {
@@ -218,4 +221,19 @@ defineActionHandler("gql.request.open", ({ request, saveContext }) => {
isDirty: false, isDirty: false,
}) })
}) })
defineActionHandler("request.rename", () => {
openReqRenameModal(getTabRef(currentTabID.value).value!)
})
defineActionHandler("tab.duplicate-tab", ({ tabID }) => {
duplicateTab(tabID ?? currentTabID.value)
})
defineActionHandler("tab.close-current", () => {
removeTab(currentTabID.value)
})
defineActionHandler("tab.close-other", () => {
closeOtherTabs(currentTabID.value)
})
defineActionHandler("tab.open-new", addNewTab)
</script> </script>

View File

@@ -461,10 +461,17 @@ defineActionHandler("rest.request.open", ({ doc }) => {
createNewTab(doc) createNewTab(doc)
}) })
defineActionHandler("rest.request.rename", openReqRenameModal) defineActionHandler("request.rename", openReqRenameModal)
defineActionHandler("request.duplicate-tab", ({ tabID }) => { defineActionHandler("tab.duplicate-tab", ({ tabID }) => {
duplicateTab(tabID) duplicateTab(tabID ?? currentTabID.value)
}) })
defineActionHandler("tab.close-current", () => {
removeTab(currentTabID.value)
})
defineActionHandler("tab.close-other", () => {
closeOtherTabs(currentTabID.value)
})
defineActionHandler("tab.open-new", addNewTab)
useService(HeaderInspectorService) useService(HeaderInspectorService)
useService(EnvironmentInspectorService) useService(EnvironmentInspectorService)

View File

@@ -45,8 +45,11 @@ import {
setSelectedEnvironmentIndex, setSelectedEnvironmentIndex,
} from "~/newstore/environments" } from "~/newstore/environments"
import IconCheckCircle from "~/components/app/spotlight/entry/IconSelected.vue"
import IconCircle from "~icons/lucide/circle"
type Doc = { type Doc = {
text: string text: string | string[]
alternates: string[] alternates: string[]
icon: object | Component icon: object | Component
excludeFromSearch?: boolean excludeFromSearch?: boolean
@@ -88,40 +91,61 @@ export class EnvironmentsSpotlightSearcherService extends StaticSpotlightSearche
private documents: Record<string, Doc> = reactive({ private documents: Record<string, Doc> = reactive({
new_environment: { new_environment: {
text: this.t("spotlight.environments.new"), text: [
this.t("spotlight.environments.title"),
this.t("spotlight.environments.new"),
],
alternates: ["new", "environment"], alternates: ["new", "environment"],
icon: markRaw(IconLayers), icon: markRaw(IconLayers),
}, },
new_environment_variable: { new_environment_variable: {
text: this.t("spotlight.environments.new_variable"), text: [
this.t("spotlight.environments.title"),
this.t("spotlight.environments.new_variable"),
],
alternates: ["new", "environment", "variable"], alternates: ["new", "environment", "variable"],
icon: markRaw(IconLayers), icon: markRaw(IconLayers),
}, },
edit_selected_env: { edit_selected_env: {
text: this.t("spotlight.environments.edit"), text: [
this.t("spotlight.environments.title"),
this.t("spotlight.environments.edit"),
],
alternates: ["edit", "environment"], alternates: ["edit", "environment"],
icon: markRaw(IconEdit), icon: markRaw(IconEdit),
excludeFromSearch: computed(() => !this.hasSelectedEnv.value), excludeFromSearch: computed(() => !this.hasSelectedEnv.value),
}, },
delete_selected_env: { delete_selected_env: {
text: this.t("spotlight.environments.delete"), text: [
this.t("spotlight.environments.title"),
this.t("spotlight.environments.delete"),
],
alternates: ["delete", "environment"], alternates: ["delete", "environment"],
icon: markRaw(IconTrash2), icon: markRaw(IconTrash2),
excludeFromSearch: computed(() => !this.hasSelectedEnv.value), excludeFromSearch: computed(() => !this.hasSelectedEnv.value),
}, },
duplicate_selected_env: { duplicate_selected_env: {
text: this.t("spotlight.environments.duplicate"), text: [
this.t("spotlight.environments.title"),
this.t("spotlight.environments.duplicate"),
],
alternates: ["duplicate", "environment"], alternates: ["duplicate", "environment"],
icon: markRaw(IconCopy), icon: markRaw(IconCopy),
excludeFromSearch: computed(() => !this.hasSelectedEnv.value), excludeFromSearch: computed(() => !this.hasSelectedEnv.value),
}, },
edit_global_env: { edit_global_env: {
text: this.t("spotlight.environments.edit_global"), text: [
this.t("spotlight.environments.title"),
this.t("spotlight.environments.edit_global"),
],
alternates: ["edit", "global", "environment"], alternates: ["edit", "global", "environment"],
icon: markRaw(IconEdit), icon: markRaw(IconEdit),
}, },
duplicate_global_env: { duplicate_global_env: {
text: this.t("spotlight.environments.duplicate_global"), text: [
this.t("spotlight.environments.title"),
this.t("spotlight.environments.duplicate_global"),
],
alternates: ["duplicate", "global", "environment"], alternates: ["duplicate", "global", "environment"],
icon: markRaw(IconCopy), icon: markRaw(IconCopy),
}, },
@@ -245,6 +269,16 @@ export class SwitchEnvSpotlightSearcherService
this.spotlight.registerSearcher(this) this.spotlight.registerSearcher(this)
} }
private selectedEnvIndex = useStreamStatic(
selectedEnvironmentIndex$,
{
type: "NO_ENV_SELECTED",
},
() => {
/* noop */
}
)[0]
private environmentSearchable = useStreamStatic( private environmentSearchable = useStreamStatic(
activeActions$.pipe( activeActions$.pipe(
map((actions) => actions.includes("modals.environment.add")) map((actions) => actions.includes("modals.environment.add"))
@@ -262,16 +296,25 @@ export class SwitchEnvSpotlightSearcherService
const results = ref<SpotlightSearcherResult[]>([]) const results = ref<SpotlightSearcherResult[]>([])
const minisearch = new MiniSearch({ const minisearch = new MiniSearch({
fields: ["name"], fields: ["name", "alternates"],
storeFields: ["name"], storeFields: ["name"],
}) })
if (this.environmentSearchable.value) { if (this.environmentSearchable.value) {
minisearch.addAll( minisearch.addAll(
environmentsStore.value.environments.map((entry, index) => { environmentsStore.value.environments.map((entry, index) => {
let id = `environment-${index}`
if (
this.selectedEnvIndex.value?.type === "MY_ENV" &&
this.selectedEnvIndex.value.index === index
) {
id += "-selected"
}
return { return {
id: `environment-${index}`, id,
name: entry.name, name: entry.name,
alternates: ["environment", "change", entry.name],
} }
}) })
) )
@@ -298,7 +341,9 @@ export class SwitchEnvSpotlightSearcherService
.map((x) => { .map((x) => {
return { return {
id: x.id, id: x.id,
icon: markRaw(IconLayers), icon: markRaw(
x.id.endsWith("-selected") ? IconCheckCircle : IconCircle
),
score: x.score, score: x.score,
text: { text: {
type: "text", type: "text",

View File

@@ -1,5 +1,5 @@
import { Component, computed, markRaw, reactive } from "vue" import { Component, computed, markRaw, reactive } from "vue"
import { invokeAction } from "~/helpers/actions" import { invokeAction, isActionBound } from "~/helpers/actions"
import { getI18n } from "~/modules/i18n" import { getI18n } from "~/modules/i18n"
import { SpotlightSearcherResult, SpotlightService } from ".." import { SpotlightSearcherResult, SpotlightService } from ".."
import { import {
@@ -19,6 +19,7 @@ import IconRename from "~icons/lucide/file-edit"
import IconPlay from "~icons/lucide/play" import IconPlay from "~icons/lucide/play"
import IconRotateCCW from "~icons/lucide/rotate-ccw" import IconRotateCCW from "~icons/lucide/rotate-ccw"
import IconSave from "~icons/lucide/save" import IconSave from "~icons/lucide/save"
import { GQLOptionTabs } from "~/components/graphql/RequestOptions.vue"
type Doc = { type Doc = {
text: string | string[] text: string | string[]
@@ -46,39 +47,51 @@ export class RequestSpotlightSearcherService extends StaticSpotlightSearcherServ
private route = useRoute() private route = useRoute()
private isRESTPage = computed(() => this.route.name === "index") private isRESTPage = computed(() => this.route.name === "index")
private isGQLPage = computed(() => this.route.name === "graphql") private isGQLPage = computed(() => this.route.name === "graphql")
private isRESTOrGQLPage = computed(
() => this.isRESTPage.value || this.isGQLPage.value
)
private isGQLConnectBound = isActionBound("gql.connect")
private isGQLDisconnectBound = isActionBound("gql.disconnect")
private documents: Record<string, Doc> = reactive({ private documents: Record<string, Doc> = reactive({
send_request: { send_request: {
text: this.t("shortcut.request.send_request"), text: this.t("shortcut.request.send_request"),
alternates: ["request", "send"], alternates: ["request", "send"],
icon: markRaw(IconPlay), icon: markRaw(IconPlay),
excludeFromSearch: computed( excludeFromSearch: computed(() => !this.isRESTOrGQLPage.value),
() => !this.isRESTPage.value ?? !this.isGQLPage.value },
), gql_connect: {
text: [this.t("navigation.graphql"), this.t("spotlight.graphql.connect")],
alternates: ["connect", "server", "graphql"],
icon: markRaw(IconPlay),
excludeFromSearch: computed(() => !this.isGQLConnectBound.value),
},
gql_disconnect: {
text: [
this.t("navigation.graphql"),
this.t("spotlight.graphql.disconnect"),
],
alternates: ["disconnect", "stop", "graphql"],
icon: markRaw(IconPlay),
excludeFromSearch: computed(() => !this.isGQLDisconnectBound.value),
}, },
save_to_collections: { save_to_collections: {
text: this.t("spotlight.request.save_as_new"), text: this.t("spotlight.request.save_as_new"),
alternates: ["save", "collections"], alternates: ["save", "collections"],
icon: markRaw(IconSave), icon: markRaw(IconSave),
excludeFromSearch: computed( excludeFromSearch: computed(() => !this.isRESTOrGQLPage.value),
() => !this.isRESTPage.value ?? !this.isGQLPage.value
),
}, },
save_request: { save_request: {
text: this.t("shortcut.request.save_request"), text: this.t("shortcut.request.save_request"),
alternates: ["save", "request"], alternates: ["save", "request"],
icon: markRaw(IconSave), icon: markRaw(IconSave),
excludeFromSearch: computed( excludeFromSearch: computed(() => !this.isRESTOrGQLPage.value),
() => !this.isRESTPage.value ?? !this.isGQLPage.value
),
}, },
rename_request: { rename_request: {
text: this.t("shortcut.request.rename"), text: this.t("shortcut.request.rename"),
alternates: ["rename", "request"], alternates: ["rename", "request"],
icon: markRaw(IconRename), icon: markRaw(IconRename),
excludeFromSearch: computed( excludeFromSearch: computed(() => !this.isRESTOrGQLPage.value),
() => !this.isRESTPage.value ?? !this.isGQLPage.value
),
}, },
copy_request_link: { copy_request_link: {
text: this.t("shortcut.request.copy_request_link"), text: this.t("shortcut.request.copy_request_link"),
@@ -90,7 +103,7 @@ export class RequestSpotlightSearcherService extends StaticSpotlightSearcherServ
text: this.t("shortcut.request.reset_request"), text: this.t("shortcut.request.reset_request"),
alternates: ["reset", "request"], alternates: ["reset", "request"],
icon: markRaw(IconRotateCCW), icon: markRaw(IconRotateCCW),
excludeFromSearch: computed(() => !this.isRESTPage.value), excludeFromSearch: computed(() => !this.isRESTOrGQLPage.value),
}, },
import_curl: { import_curl: {
text: this.t("shortcut.request.import_curl"), text: this.t("shortcut.request.import_curl"),
@@ -143,9 +156,7 @@ export class RequestSpotlightSearcherService extends StaticSpotlightSearcherServ
], ],
alternates: ["parameters", "tab"], alternates: ["parameters", "tab"],
icon: markRaw(IconWindow), icon: markRaw(IconWindow),
excludeFromSearch: computed( excludeFromSearch: computed(() => !this.isRESTOrGQLPage.value),
() => !this.isRESTPage.value ?? !this.isGQLPage.value
),
}, },
tab_body: { tab_body: {
text: [ text: [
@@ -154,9 +165,7 @@ export class RequestSpotlightSearcherService extends StaticSpotlightSearcherServ
], ],
alternates: ["body", "tab"], alternates: ["body", "tab"],
icon: markRaw(IconWindow), icon: markRaw(IconWindow),
excludeFromSearch: computed( excludeFromSearch: computed(() => !this.isRESTOrGQLPage.value),
() => !this.isRESTPage.value ?? !this.isGQLPage.value
),
}, },
tab_headers: { tab_headers: {
text: [ text: [
@@ -165,9 +174,7 @@ export class RequestSpotlightSearcherService extends StaticSpotlightSearcherServ
], ],
alternates: ["headers", "tab"], alternates: ["headers", "tab"],
icon: markRaw(IconWindow), icon: markRaw(IconWindow),
excludeFromSearch: computed( excludeFromSearch: computed(() => !this.isRESTOrGQLPage.value),
() => !this.isRESTPage.value ?? !this.isGQLPage.value
),
}, },
tab_authorization: { tab_authorization: {
text: [ text: [
@@ -176,9 +183,7 @@ export class RequestSpotlightSearcherService extends StaticSpotlightSearcherServ
], ],
alternates: ["authorization", "tab"], alternates: ["authorization", "tab"],
icon: markRaw(IconWindow), icon: markRaw(IconWindow),
excludeFromSearch: computed( excludeFromSearch: computed(() => !this.isRESTOrGQLPage.value),
() => !this.isRESTPage.value ?? !this.isGQLPage.value
),
}, },
tab_pre_request_script: { tab_pre_request_script: {
text: [ text: [
@@ -198,6 +203,24 @@ export class RequestSpotlightSearcherService extends StaticSpotlightSearcherServ
icon: markRaw(IconWindow), icon: markRaw(IconWindow),
excludeFromSearch: computed(() => !this.isRESTPage.value), excludeFromSearch: computed(() => !this.isRESTPage.value),
}, },
tab_query: {
text: [
this.t("spotlight.request.switch_to"),
this.t("spotlight.request.tab_query"),
],
alternates: ["query", "tab"],
icon: markRaw(IconWindow),
excludeFromSearch: computed(() => !this.isGQLPage.value),
},
tab_variables: {
text: [
this.t("spotlight.request.switch_to"),
this.t("spotlight.request.tab_variables"),
],
alternates: ["variables", "tab"],
icon: markRaw(IconWindow),
excludeFromSearch: computed(() => !this.isGQLPage.value),
},
}) })
constructor() { constructor() {
@@ -224,7 +247,7 @@ export class RequestSpotlightSearcherService extends StaticSpotlightSearcherServ
} }
} }
private openRequestTab(tab: RequestOptionTabs): void { private openRequestTab(tab: RequestOptionTabs | GQLOptionTabs): void {
invokeAction("request.open-tab", { invokeAction("request.open-tab", {
tab, tab,
}) })
@@ -235,6 +258,12 @@ export class RequestSpotlightSearcherService extends StaticSpotlightSearcherServ
case "send_request": case "send_request":
invokeAction("request.send-cancel") invokeAction("request.send-cancel")
break break
case "gql_connect":
invokeAction("gql.connect")
break
case "gql_disconnect":
invokeAction("gql.disconnect")
break
case "save_to_collections": case "save_to_collections":
invokeAction("request.save-as", { invokeAction("request.save-as", {
requestType: "rest", requestType: "rest",
@@ -245,7 +274,7 @@ export class RequestSpotlightSearcherService extends StaticSpotlightSearcherServ
invokeAction("request.save") invokeAction("request.save")
break break
case "rename_request": case "rename_request":
invokeAction("rest.request.rename") invokeAction("request.rename")
break break
case "copy_request_link": case "copy_request_link":
invokeAction("request.copy-link") invokeAction("request.copy-link")
@@ -292,6 +321,12 @@ export class RequestSpotlightSearcherService extends StaticSpotlightSearcherServ
case "tab_tests": case "tab_tests":
this.openRequestTab("tests") this.openRequestTab("tests")
break break
case "tab_query":
this.openRequestTab("query")
break
case "tab_variables":
this.openRequestTab("variables")
break
} }
} }
} }

View File

@@ -1,5 +1,5 @@
import { Component, computed, markRaw, reactive } from "vue" import { Component, computed, markRaw, reactive } from "vue"
import { activeActions$, invokeAction } from "~/helpers/actions" import { invokeAction, isActionBound } from "~/helpers/actions"
import { getI18n } from "~/modules/i18n" import { getI18n } from "~/modules/i18n"
import { SpotlightSearcherResult, SpotlightService } from ".." import { SpotlightSearcherResult, SpotlightService } from ".."
import { import {
@@ -9,8 +9,6 @@ import {
import IconDownload from "~icons/lucide/download" import IconDownload from "~icons/lucide/download"
import IconCopy from "~icons/lucide/copy" import IconCopy from "~icons/lucide/copy"
import { map } from "rxjs"
import { useStreamStatic } from "~/composables/stream"
type Doc = { type Doc = {
text: string text: string
@@ -35,23 +33,11 @@ export class ResponseSpotlightSearcherService extends StaticSpotlightSearcherSer
private readonly spotlight = this.bind(SpotlightService) private readonly spotlight = this.bind(SpotlightService)
private copyResponseActionEnabled = useStreamStatic( private copyResponseActionEnabled = isActionBound("response.copy")
activeActions$.pipe(map((actions) => actions.includes("response.copy"))),
activeActions$.value.includes("response.copy"),
() => {
/* noop */
}
)[0]
private downloadResponseActionEnabled = useStreamStatic( private downloadResponseActionEnabled = isActionBound(
activeActions$.pipe( "response.file.download"
map((actions) => actions.includes("response.file.download")) )
),
activeActions$.value.includes("response.file.download"),
() => {
/* noop */
}
)[0]
private documents: Record<string, Doc> = reactive({ private documents: Record<string, Doc> = reactive({
copy_response: { copy_response: {

View File

@@ -7,22 +7,16 @@ import {
} from "./base/static.searcher" } from "./base/static.searcher"
import { useRoute } from "vue-router" import { useRoute } from "vue-router"
import { getDefaultRESTRequest } from "~/helpers/rest/default"
import {
closeOtherTabs,
closeTab,
createNewTab,
currentTabID,
getActiveTabs,
} from "~/helpers/rest/tab"
import IconCopy from "~icons/lucide/copy" import IconCopy from "~icons/lucide/copy"
import IconCopyPlus from "~icons/lucide/copy-plus" import IconCopyPlus from "~icons/lucide/copy-plus"
import IconXCircle from "~icons/lucide/x-circle" import IconXCircle from "~icons/lucide/x-circle"
import IconXSquare from "~icons/lucide/x-square" import IconXSquare from "~icons/lucide/x-square"
import { invokeAction } from "~/helpers/actions" import { invokeAction } from "~/helpers/actions"
import { getActiveTabs as getRESTActiveTabs } from "~/helpers/rest/tab"
import { getActiveTabs as getGQLActiveTabs } from "~/helpers/graphql/tab"
type Doc = { type Doc = {
text: string text: string | string[]
alternates: string[] alternates: string[]
icon: object | Component icon: object | Component
excludeFromSearch?: boolean excludeFromSearch?: boolean
@@ -46,34 +40,47 @@ export class TabSpotlightSearcherService extends StaticSpotlightSearcherService<
private route = useRoute() private route = useRoute()
private showAction = computed( private showAction = computed(
() => this.route.name === "index" ?? this.route.name === "graphql" () => this.route.name === "index" || this.route.name === "graphql"
)
private gqlActiveTabs = getGQLActiveTabs()
private restActiveTabs = getRESTActiveTabs()
private isOnlyTab = computed(() =>
this.route.name === "graphql"
? this.gqlActiveTabs.value.length === 1
: this.restActiveTabs.value.length === 1
) )
private documents: Record<string, Doc> = reactive({ private documents: Record<string, Doc> = reactive({
duplicate_tab: { duplicate_tab: {
text: this.t("spotlight.tab.duplicate"), text: [this.t("spotlight.tab.title"), this.t("spotlight.tab.duplicate")],
alternates: ["tab", "duplicate", "duplicate tab"], alternates: ["tab", "duplicate", "duplicate tab"],
icon: markRaw(IconCopy), icon: markRaw(IconCopy),
excludeFromSearch: computed(() => !this.showAction.value), excludeFromSearch: computed(() => !this.showAction.value),
}, },
close_current_tab: { close_current_tab: {
text: this.t("spotlight.tab.close_current"), text: [
this.t("spotlight.tab.title"),
this.t("spotlight.tab.close_current"),
],
alternates: ["tab", "close", "close tab"], alternates: ["tab", "close", "close tab"],
icon: markRaw(IconXCircle), icon: markRaw(IconXCircle),
excludeFromSearch: computed( excludeFromSearch: computed(
() => !this.showAction.value || getActiveTabs().value.length === 1 () => !this.showAction.value || this.isOnlyTab.value
), ),
}, },
close_other_tabs: { close_other_tabs: {
text: this.t("spotlight.tab.close_others"), text: [
this.t("spotlight.tab.title"),
this.t("spotlight.tab.close_others"),
],
alternates: ["tab", "close", "close all"], alternates: ["tab", "close", "close all"],
icon: markRaw(IconXSquare), icon: markRaw(IconXSquare),
excludeFromSearch: computed( excludeFromSearch: computed(
() => !this.showAction.value || getActiveTabs().value.length < 2 () => !this.showAction.value || this.isOnlyTab.value
), ),
}, },
open_new_tab: { open_new_tab: {
text: this.t("spotlight.tab.new_tab"), text: [this.t("spotlight.tab.title"), this.t("spotlight.tab.new_tab")],
alternates: ["tab", "new", "open tab"], alternates: ["tab", "new", "open tab"],
icon: markRaw(IconCopyPlus), icon: markRaw(IconCopyPlus),
excludeFromSearch: computed(() => !this.showAction.value), excludeFromSearch: computed(() => !this.showAction.value),
@@ -105,16 +112,9 @@ export class TabSpotlightSearcherService extends StaticSpotlightSearcherService<
} }
public onDocSelected(id: string): void { public onDocSelected(id: string): void {
if (id === "duplicate_tab") if (id === "duplicate_tab") invokeAction("tab.duplicate-tab", {})
invokeAction("request.duplicate-tab", { if (id === "close_current_tab") invokeAction("tab.close-current")
tabID: currentTabID.value, if (id === "close_other_tabs") invokeAction("tab.close-other")
}) if (id === "open_new_tab") invokeAction("tab.open-new")
if (id === "close_current_tab") closeTab(currentTabID.value)
if (id === "close_other_tabs") closeOtherTabs(currentTabID.value)
if (id === "open_new_tab")
createNewTab({
request: getDefaultRESTRequest(),
isDirty: false,
})
} }
} }