fix: corrections for spotlight searchers (#3275)

This commit is contained in:
Anwarul Islam
2023-08-25 02:14:29 +06:00
committed by GitHub
parent a268cab11e
commit 886847ab7b
20 changed files with 443 additions and 319 deletions

View File

@@ -606,7 +606,7 @@
"delete_method": "Select DELETE method", "delete_method": "Select DELETE method",
"get_method": "Select GET method", "get_method": "Select GET method",
"head_method": "Select HEAD method", "head_method": "Select HEAD method",
"rename": "Rename Current Request", "rename": "Rename Request",
"import_curl": "Import cURL", "import_curl": "Import cURL",
"show_code": "Generate code snippet", "show_code": "Generate code snippet",
"method": "Method", "method": "Method",
@@ -649,11 +649,11 @@
}, },
"spotlight": { "spotlight": {
"general": { "general": {
"help_menu": "Open help and support menu", "help_menu": "Help and support",
"chat": "Chat with support", "chat": "Chat with support",
"open_docs": "Read Documentation", "open_docs": "Read Documentation",
"open_keybindings": "Open keyboard shortcuts", "open_keybindings": "Keyboard shortcuts",
"social": "Social links and GitHub", "social": "Social",
"title": "General" "title": "General"
}, },
"miscellaneous": { "miscellaneous": {
@@ -661,15 +661,18 @@
"title": "Miscellaneous" "title": "Miscellaneous"
}, },
"request": { "request": {
"tab_parameters": "Open parameters tab", "switch_to": "Switch to",
"tab_body": "Open body tab", "select_method": "Select method",
"tab_headers": "Open headers tab", "save_as_new": "Save as new request",
"tab_authorization": "Open authorization tab", "tab_parameters": "Parameters tab",
"tab_pre_request_script": "Open pre-request script tab", "tab_body": "Body tab",
"tab_tests": "Open tests tab" "tab_headers": "Headers tab",
"tab_authorization": "Authorization tab",
"tab_pre_request_script": "Pre-request script tab",
"tab_tests": "Tests tab"
}, },
"response": { "response": {
"copy": "Copy response as JSON", "copy": "Copy response",
"download": "Download response as file", "download": "Download response as file",
"title": "Response" "title": "Response"
}, },
@@ -692,6 +695,7 @@
"title": "Teams" "title": "Teams"
}, },
"tab": { "tab": {
"duplicate": "Duplicate tab",
"close_current": "Close current tab", "close_current": "Close current tab",
"close_others": "Close other tabs", "close_others": "Close other tabs",
"new_tab": "Open a new tab", "new_tab": "Open a new tab",
@@ -703,9 +707,7 @@
"interface": "Interface", "interface": "Interface",
"interceptor": "Interceptor" "interceptor": "Interceptor"
}, },
"change_interceptor": "Change Interceptor",
"change_language": "Change Language", "change_language": "Change Language",
"install_extension": "Install Browser Extension",
"settings": { "settings": {
"theme": { "theme": {
"black": "Black Mode", "black": "Black Mode",
@@ -719,8 +721,7 @@
"size_lg": "Change to Large" "size_lg": "Change to Large"
}, },
"change_interceptor": "Change Interceptor", "change_interceptor": "Change Interceptor",
"change_language": "Change Language", "change_language": "Change Language"
"install_extension": "Install Browser Extension"
} }
}, },
"sse": { "sse": {

View File

@@ -29,6 +29,7 @@ declare module 'vue' {
AppSpotlightEntry: typeof import('./components/app/spotlight/Entry.vue')['default'] AppSpotlightEntry: typeof import('./components/app/spotlight/Entry.vue')['default']
AppSpotlightEntryGQLHistory: typeof import('./components/app/spotlight/entry/GQLHistory.vue')['default'] AppSpotlightEntryGQLHistory: typeof import('./components/app/spotlight/entry/GQLHistory.vue')['default']
AppSpotlightEntryGQLRequest: typeof import('./components/app/spotlight/entry/GQLRequest.vue')['default'] AppSpotlightEntryGQLRequest: typeof import('./components/app/spotlight/entry/GQLRequest.vue')['default']
AppSpotlightEntryIconSelected: typeof import('./components/app/spotlight/entry/IconSelected.vue')['default']
AppSpotlightEntryRESTHistory: typeof import('./components/app/spotlight/entry/RESTHistory.vue')['default'] AppSpotlightEntryRESTHistory: typeof import('./components/app/spotlight/entry/RESTHistory.vue')['default']
AppSpotlightEntryRESTRequest: typeof import('./components/app/spotlight/entry/RESTRequest.vue')['default'] AppSpotlightEntryRESTRequest: typeof import('./components/app/spotlight/entry/RESTRequest.vue')['default']
AppSupport: typeof import('./components/app/Support.vue')['default'] AppSupport: typeof import('./components/app/Support.vue')['default']

View File

@@ -1,7 +1,6 @@
<template> <template>
<AppShortcuts :show="showShortcuts" @close="showShortcuts = false" /> <AppShortcuts :show="showShortcuts" @close="showShortcuts = false" />
<AppShare :show="showShare" @hide-modal="showShare = false" /> <AppShare :show="showShare" @hide-modal="showShare = false" />
<AppSocial :show="showSocial" @hide-modal="showSocial = false" />
<FirebaseLogin :show="showLogin" @hide-modal="showLogin = false" /> <FirebaseLogin :show="showLogin" @hide-modal="showLogin = false" />
<HoppSmartConfirmModal <HoppSmartConfirmModal
@@ -27,7 +26,6 @@ const t = useI18n()
const showShortcuts = ref(false) const showShortcuts = ref(false)
const showShare = ref(false) const showShare = ref(false)
const showSocial = ref(false)
const showLogin = ref(false) const showLogin = ref(false)
const confirmRemove = ref(false) const confirmRemove = ref(false)
@@ -60,10 +58,6 @@ defineActionHandler("modals.share.toggle", () => {
showShare.value = !showShare.value showShare.value = !showShare.value
}) })
defineActionHandler("modals.social.toggle", () => {
showSocial.value = !showSocial.value
})
defineActionHandler("modals.login.toggle", () => { defineActionHandler("modals.login.toggle", () => {
showLogin.value = !showLogin.value showLogin.value = !showLogin.value
}) })

View File

@@ -1,135 +0,0 @@
<template>
<HoppSmartModal
v-if="show"
dialog
:title="t('app.social_links')"
@close="hideModal"
>
<template #body>
<div class="flex flex-col space-y-2">
<div class="grid grid-cols-3 gap-4">
<a
v-for="(platform, index) in platforms"
:key="`platform-${index}`"
:href="platform.link"
target="_blank"
class="social-link"
tabindex="0"
>
<component :is="platform.icon" class="w-6 h-6" />
<span class="mt-3">
{{ platform.name }}
</span>
</a>
<button class="social-link" @click="copyAppLink">
<component :is="copyIcon" class="w-6 h-6 text-xl" />
<span class="mt-3">
{{ t("app.copy") }}
</span>
</button>
</div>
</div>
</template>
<template #footer>
<p class="text-secondaryLight">
{{ t("app.social_description") }}
</p>
</template>
</HoppSmartModal>
</template>
<script setup lang="ts">
import { useI18n } from "@composables/i18n"
import { useToast } from "@composables/toast"
import { refAutoReset } from "@vueuse/core"
import { copyToClipboard } from "~/helpers/utils/clipboard"
import IconFacebook from "~icons/brands/facebook"
import IconLinkedIn from "~icons/brands/linkedin"
import IconReddit from "~icons/brands/reddit"
import IconTwitter from "~icons/brands/twitter"
import IconCheck from "~icons/lucide/check"
import IconCopy from "~icons/lucide/copy"
import IconGitHub from "~icons/lucide/github"
const t = useI18n()
const toast = useToast()
defineProps<{
show: boolean
}>()
const emit = defineEmits<{
(e: "hide-modal"): void
}>()
const url = "https://hoppscotch.io"
const copyIcon = refAutoReset<typeof IconCopy | typeof IconCheck>(
IconCopy,
1000
)
const platforms = [
{
name: "GitHub",
icon: IconGitHub,
link: `https://hoppscotch.io/github`,
},
{
name: "Twitter",
icon: IconTwitter,
link: `https://twitter.com/hoppscotch_io`,
},
{
name: "Facebook",
icon: IconFacebook,
link: `https://www.facebook.com/hoppscotch.io`,
},
{
name: "Reddit",
icon: IconReddit,
link: `https://www.reddit.com/r/hoppscotch`,
},
{
name: "LinkedIn",
icon: IconLinkedIn,
link: `https://www.linkedin.com/company/hoppscotch/`,
},
]
const copyAppLink = () => {
copyToClipboard(url)
copyIcon.value = IconCheck
toast.success(`${t("state.copied_to_clipboard")}`)
}
const hideModal = () => {
emit("hide-modal")
}
</script>
<style lang="scss" scoped>
.social-link {
@apply border border-dividerLight;
@apply rounded;
@apply flex-col flex;
@apply p-4;
@apply items-center;
@apply justify-center;
@apply font-semibold;
@apply hover: (bg-primaryLight text-secondaryDark);
@apply focus: outline-none;
@apply focus-visible: border-divider;
svg {
@apply opacity-80;
}
&:hover {
svg {
@apply opacity-100;
}
}
}
</style>

View File

@@ -0,0 +1,3 @@
<template>
<IconLucideCheckCircle class="text-accent" />
</template>

View File

@@ -111,6 +111,7 @@ import {
SwitchWorkspaceSpotlightSearcherService, SwitchWorkspaceSpotlightSearcherService,
WorkspaceSpotlightSearcherService, WorkspaceSpotlightSearcherService,
} from "~/services/spotlight/searchers/workspace.searcher" } from "~/services/spotlight/searchers/workspace.searcher"
import { InterceptorSpotlightSearcherService } from "~/services/spotlight/searchers/interceptor.searcher"
const t = useI18n() const t = useI18n()
@@ -138,6 +139,7 @@ useService(EnvironmentsSpotlightSearcherService)
useService(SwitchEnvSpotlightSearcherService) useService(SwitchEnvSpotlightSearcherService)
useService(WorkspaceSpotlightSearcherService) useService(WorkspaceSpotlightSearcherService)
useService(SwitchWorkspaceSpotlightSearcherService) useService(SwitchWorkspaceSpotlightSearcherService)
useService(InterceptorSpotlightSearcherService)
const search = ref("") const search = ref("")
@@ -264,4 +266,3 @@ function newUseArrowKeysForNavigation() {
return { selectedEntry } return { selectedEntry }
} }
</script> </script>
~/services/spotlight/searchers/workspace.searcher

View File

@@ -239,6 +239,7 @@ import {
resetTeamRequestsContext, resetTeamRequestsContext,
} from "~/helpers/collection/collection" } from "~/helpers/collection/collection"
import { currentReorderingStatus$ } from "~/newstore/reordering" import { currentReorderingStatus$ } from "~/newstore/reordering"
import { defineActionHandler } from "~/helpers/actions"
const t = useI18n() const t = useI18n()
const toast = useToast() const toast = useToast()
@@ -2067,4 +2068,8 @@ const getErrorMessage = (err: GQLError<string>) => {
} }
} }
} }
defineActionHandler("collection.new", () => {
displayModalAdd(true)
})
</script> </script>

View File

@@ -34,6 +34,13 @@
@hide-modal="displayModalNew(false)" @hide-modal="displayModalNew(false)"
/> />
</div> </div>
<HoppSmartConfirmModal
:show="showConfirmRemoveEnvModal"
:title="t('confirm.remove_team')"
@hide-modal="showConfirmRemoveEnvModal = false"
@resolve="removeSelectedEnvironment()"
/>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@@ -44,6 +51,7 @@ import { GetMyTeamsQuery } from "~/helpers/backend/graphql"
import { useReadonlyStream, useStream } from "@composables/stream" import { useReadonlyStream, useStream } from "@composables/stream"
import { useI18n } from "~/composables/i18n" import { useI18n } from "~/composables/i18n"
import { import {
getSelectedEnvironmentIndex,
globalEnv$, globalEnv$,
selectedEnvironmentIndex$, selectedEnvironmentIndex$,
setSelectedEnvironmentIndex, setSelectedEnvironmentIndex,
@@ -54,8 +62,15 @@ import { workspaceStatus$ } from "~/newstore/workspace"
import TeamListAdapter from "~/helpers/teams/TeamListAdapter" import TeamListAdapter from "~/helpers/teams/TeamListAdapter"
import { useLocalState } from "~/newstore/localstate" import { useLocalState } from "~/newstore/localstate"
import { onLoggedIn } from "~/composables/auth" import { onLoggedIn } from "~/composables/auth"
import { pipe } from "fp-ts/function"
import * as TE from "fp-ts/TaskEither"
import { GQLError } from "~/helpers/backend/GQLClient"
import { deleteEnvironment } from "~/newstore/environments"
import { deleteTeamEnvironment } from "~/helpers/backend/mutations/TeamEnvironment"
import { useToast } from "~/composables/toast"
const t = useI18n() const t = useI18n()
const toast = useToast()
type EnvironmentType = "my-environments" | "team-environments" type EnvironmentType = "my-environments" | "team-environments"
@@ -168,6 +183,7 @@ watch(
} }
) )
const showConfirmRemoveEnvModal = ref(false)
const showModalNew = ref(false) const showModalNew = ref(false)
const showModalDetails = ref(false) const showModalDetails = ref(false)
const action = ref<"new" | "edit">("edit") const action = ref<"new" | "edit">("edit")
@@ -194,6 +210,30 @@ const editEnvironment = (environmentIndex: "Global") => {
displayModalEdit(true) displayModalEdit(true)
} }
const removeSelectedEnvironment = () => {
const selectedEnvIndex = getSelectedEnvironmentIndex()
if (selectedEnvIndex?.type === "NO_ENV_SELECTED") return
if (selectedEnvIndex?.type === "MY_ENV") {
deleteEnvironment(selectedEnvIndex.index)
toast.success(`${t("state.deleted")}`)
}
if (selectedEnvIndex?.type === "TEAM_ENV") {
pipe(
deleteTeamEnvironment(selectedEnvIndex.teamEnvID),
TE.match(
(err: GQLError<string>) => {
console.error(err)
},
() => {
toast.success(`${t("team_environment.deleted")}`)
}
)
)()
}
}
const resetSelectedData = () => { const resetSelectedData = () => {
editingEnvironmentIndex.value = null editingEnvironmentIndex.value = null
} }
@@ -203,6 +243,10 @@ defineActionHandler("modals.environment.new", () => {
showModalDetails.value = true showModalDetails.value = true
}) })
defineActionHandler("modals.environment.delete-selected", () => {
showConfirmRemoveEnvModal.value = true
})
defineActionHandler( defineActionHandler(
"modals.my.environment.edit", "modals.my.environment.edit",
({ envName, variableName }) => { ({ envName, variableName }) => {

View File

@@ -129,9 +129,7 @@ const downloadResponse = (str: string) => {
} }
defineActionHandler("response.file.download", () => defineActionHandler("response.file.download", () =>
downloadResponse.bind(responseString.value) downloadResponse(responseString.value)
)
defineActionHandler("response.copy", () =>
copyResponse.bind(responseString.value)
) )
defineActionHandler("response.copy", () => copyResponse(responseString.value))
</script> </script>

View File

@@ -26,14 +26,15 @@ 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
| "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
| "modals.search.toggle" // Shows the search modal | "modals.search.toggle" // Shows the search modal
| "modals.support.toggle" // Shows the support modal | "modals.support.toggle" // Shows the support modal
| "modals.share.toggle" // Shows the share modal | "modals.share.toggle" // Shows the share modal
| "modals.social.toggle" // Shows the social links modal
| "modals.environment.add" // Show add environment modal via context menu | "modals.environment.add" // Show add environment modal via context menu
| "modals.environment.new" // Add new environment | "modals.environment.new" // Add new environment
| "modals.environment.delete-selected" // Delete Selected Environment
| "modals.my.environment.edit" // Edit current personal environment | "modals.my.environment.edit" // Edit current personal environment
| "modals.team.environment.edit" // Edit current team environment | "modals.team.environment.edit" // Edit current team environment
| "modals.team.new" // Add new team | "modals.team.new" // Add new team
@@ -108,6 +109,10 @@ type HoppActionArgsMap = {
tab: RequestOptionTabs tab: RequestOptionTabs
} }
"request.duplicate-tab": {
tabID: string
}
"gql.request.open": { "gql.request.open": {
request: HoppGQLRequest request: HoppGQLRequest
saveContext?: HoppGQLSaveContext saveContext?: HoppGQLSaveContext
@@ -177,7 +182,7 @@ type InvokeActionFunc = {
* @param args The argument passed to the action handler. Optional if action has no args required * @param args The argument passed to the action handler. Optional if action has no args required
*/ */
export const invokeAction: InvokeActionFunc = < export const invokeAction: InvokeActionFunc = <
A extends HoppAction | HoppActionWithArgs, A extends HoppAction | HoppActionWithArgs
>( >(
action: A, action: A,
args: ArgOfHoppAction<A> args: ArgOfHoppAction<A>

View File

@@ -427,6 +427,10 @@ export function getCurrentEnvironment(): Environment {
} }
} }
export function getSelectedEnvironmentIndex() {
return environmentsStore.value.selectedEnvironmentIndex
}
export function getSelectedEnvironmentType() { export function getSelectedEnvironmentType() {
return environmentsStore.value.selectedEnvironmentIndex.type return environmentsStore.value.selectedEnvironmentIndex.type
} }

View File

@@ -461,6 +461,9 @@ defineActionHandler("rest.request.open", ({ doc }) => {
}) })
defineActionHandler("rest.request.rename", openReqRenameModal) defineActionHandler("rest.request.rename", openReqRenameModal)
defineActionHandler("request.duplicate-tab", ({ tabID }) => {
duplicateTab(tabID)
})
const inspectionService = useService(InspectionService) const inspectionService = useService(InspectionService)
useService(HeaderInspectorService) useService(HeaderInspectorService)

View File

@@ -1,5 +1,6 @@
import { Service } from "dioc" import { Service } from "dioc"
import { import {
SpotlightResultTextType,
SpotlightSearcher, SpotlightSearcher,
SpotlightSearcherResult, SpotlightSearcherResult,
SpotlightSearcherSessionState, SpotlightSearcherSessionState,
@@ -26,6 +27,7 @@ import {
} from "@hoppscotch/data" } from "@hoppscotch/data"
import { hoppWorkspaceStore } from "~/newstore/workspace" import { hoppWorkspaceStore } from "~/newstore/workspace"
import { changeWorkspace } from "~/newstore/workspace" import { changeWorkspace } from "~/newstore/workspace"
import { invokeAction } from "~/helpers/actions"
/** /**
* A spotlight searcher that searches through the user's collections * A spotlight searcher that searches through the user's collections
@@ -143,6 +145,13 @@ export class CollectionsSpotlightSearcherService
}, },
}) })
if (pageCategory === "rest" || pageCategory === "graphql") {
minisearch.add({
id: `create-collection`,
name: this.t("collection.new"),
})
}
if (pageCategory === "rest") { if (pageCategory === "rest") {
this.loadRESTDocsIntoMinisearch(minisearch) this.loadRESTDocsIntoMinisearch(minisearch)
} else if (pageCategory === "graphql") { } else if (pageCategory === "graphql") {
@@ -153,6 +162,11 @@ export class CollectionsSpotlightSearcherService
const scopeHandle = effectScope() const scopeHandle = effectScope()
const newCollectionText: SpotlightResultTextType<any> = {
type: "text",
text: this.t("collection.new"),
}
scopeHandle.run(() => { scopeHandle.run(() => {
watch(query, (query) => { watch(query, (query) => {
if (pageCategory === "other") { if (pageCategory === "other") {
@@ -165,28 +179,34 @@ export class CollectionsSpotlightSearcherService
results.value = searchResults.map((result) => ({ results.value = searchResults.map((result) => ({
id: result.id, id: result.id,
text: { text:
type: "custom", result.id === "create-collection"
component: markRaw(RESTRequestSpotlightEntry), ? newCollectionText
componentProps: { : {
folderPath: result.id.split("rest-")[1], type: "custom",
}, component: markRaw(RESTRequestSpotlightEntry),
}, componentProps: {
folderPath: result.id.split("rest-")[1],
},
},
icon: markRaw(IconFolder), icon: markRaw(IconFolder),
score: result.score, score: result.score,
})) }))
} else { } else if (pageCategory === "graphql") {
const searchResults = minisearch.search(query).slice(0, 10) const searchResults = minisearch.search(query).slice(0, 10)
results.value = searchResults.map((result) => ({ results.value = searchResults.map((result) => ({
id: result.id, id: result.id,
text: { text:
type: "custom", result.id === "create-collection"
component: markRaw(GQLRequestSpotlightEntry), ? newCollectionText
componentProps: { : {
folderPath: result.id.split("gql-")[1], type: "custom",
}, component: markRaw(GQLRequestSpotlightEntry),
}, componentProps: {
folderPath: result.id.split("gql-")[1],
},
},
icon: markRaw(IconFolder), icon: markRaw(IconFolder),
score: result.score, score: result.score,
})) }))
@@ -256,6 +276,8 @@ export class CollectionsSpotlightSearcherService
} }
public onResultSelect(result: SpotlightSearcherResult): void { public onResultSelect(result: SpotlightSearcherResult): void {
if (result.id === "create-collection") return invokeAction("collection.new")
const [type, path] = result.id.split("-") const [type, path] = result.id.split("-")
if (type === "rest") { if (type === "rest") {

View File

@@ -21,30 +21,29 @@ import {
StaticSpotlightSearcherService, StaticSpotlightSearcherService,
} from "./base/static.searcher" } from "./base/static.searcher"
import IconEdit from "~icons/lucide/edit"
import IconTrash2 from "~icons/lucide/trash-2"
import IconCopy from "~icons/lucide/copy" import IconCopy from "~icons/lucide/copy"
import IconEdit from "~icons/lucide/edit"
import IconLayers from "~icons/lucide/layers" import IconLayers from "~icons/lucide/layers"
import IconTrash2 from "~icons/lucide/trash-2"
import { Service } from "dioc"
import * as TE from "fp-ts/TaskEither"
import { pipe } from "fp-ts/function"
import { cloneDeep } from "lodash-es"
import MiniSearch from "minisearch"
import { map } from "rxjs"
import { useStreamStatic } from "~/composables/stream" import { useStreamStatic } from "~/composables/stream"
import { GQLError } from "~/helpers/backend/GQLClient"
import { deleteTeamEnvironment } from "~/helpers/backend/mutations/TeamEnvironment"
import { import {
createEnvironment, createEnvironment,
currentEnvironment$, currentEnvironment$,
deleteEnvironment,
duplicateEnvironment, duplicateEnvironment,
environmentsStore, environmentsStore,
getGlobalVariables, getGlobalVariables,
selectedEnvironmentIndex$, selectedEnvironmentIndex$,
setSelectedEnvironmentIndex, setSelectedEnvironmentIndex,
} from "~/newstore/environments" } from "~/newstore/environments"
import { pipe } from "fp-ts/function"
import * as TE from "fp-ts/TaskEither"
import { deleteTeamEnvironment } from "~/helpers/backend/mutations/TeamEnvironment"
import { GQLError } from "~/helpers/backend/GQLClient"
import { cloneDeep } from "lodash-es"
import { Service } from "dioc"
import MiniSearch from "minisearch"
import { map } from "rxjs"
type Doc = { type Doc = {
text: string text: string
@@ -188,29 +187,6 @@ export class EnvironmentsSpotlightSearcherService extends StaticSpotlightSearche
} }
} }
removeSelectedEnvironment = () => {
if (this.selectedEnvIndex.value?.type === "NO_ENV_SELECTED") return
if (this.selectedEnvIndex.value?.type === "MY_ENV") {
deleteEnvironment(this.selectedEnvIndex.value.index)
// this.toast.success(`${t("state.deleted")}`)
}
if (this.selectedEnvIndex.value?.type === "TEAM_ENV") {
pipe(
deleteTeamEnvironment(this.selectedEnvIndex.value.teamEnvID),
TE.match(
(err: GQLError<string>) => {
console.error(err)
},
() => {
// this.toast.success(`${this.t("team_environment.deleted")}`)
}
)
)()
}
}
public onDocSelected(id: string): void { public onDocSelected(id: string): void {
switch (id) { switch (id) {
case "new_environment": case "new_environment":
@@ -229,7 +205,7 @@ export class EnvironmentsSpotlightSearcherService extends StaticSpotlightSearche
}) })
break break
case "delete_selected_env": case "delete_selected_env":
this.removeSelectedEnvironment() invokeAction(`modals.environment.delete-selected`)
break break
case "duplicate_selected_env": case "duplicate_selected_env":
this.duplicateSelectedEnv() this.duplicateSelectedEnv()

View File

@@ -7,14 +7,17 @@ import {
StaticSpotlightSearcherService, StaticSpotlightSearcherService,
} from "./base/static.searcher" } from "./base/static.searcher"
import IconLinkedIn from "~icons/brands/linkedin"
import IconTwitter from "~icons/brands/twitter"
import IconBook from "~icons/lucide/book" import IconBook from "~icons/lucide/book"
import IconGithub from "~icons/lucide/github" import IconDiscord from "~icons/lucide/link"
import IconGitHub from "~icons/lucide/github"
import IconLifeBuoy from "~icons/lucide/life-buoy" import IconLifeBuoy from "~icons/lucide/life-buoy"
import IconMessageCircle from "~icons/lucide/message-circle" import IconMessageCircle from "~icons/lucide/message-circle"
import IconZap from "~icons/lucide/zap" import IconZap from "~icons/lucide/zap"
type Doc = { type Doc = {
text: string text: string | string[]
alternates: string[] alternates: string[]
icon: object | Component icon: object | Component
} }
@@ -56,10 +59,25 @@ export class GeneralSpotlightSearcherService extends StaticSpotlightSearcherServ
alternates: ["key", "shortcuts", "binding"], alternates: ["key", "shortcuts", "binding"],
icon: markRaw(IconZap), icon: markRaw(IconZap),
}, },
social_links: { link_github: {
text: this.t("spotlight.general.social"), text: [this.t("spotlight.general.social"), "GitHub"],
alternates: ["social", "github", "binding"], alternates: ["social", "github", "link"],
icon: markRaw(IconGithub), icon: markRaw(IconGitHub),
},
link_twitter: {
text: [this.t("spotlight.general.social"), "Twitter"],
alternates: ["social", "twitter", "link"],
icon: markRaw(IconTwitter),
},
link_discord: {
text: [this.t("spotlight.general.social"), "Discord"],
alternates: ["social", "discord", "link"],
icon: markRaw(IconDiscord),
},
link_linkedin: {
text: [this.t("spotlight.general.social"), "LinkedIn"],
alternates: ["social", "linkedin", "link"],
icon: markRaw(IconLinkedIn),
}, },
}) })
@@ -87,8 +105,7 @@ export class GeneralSpotlightSearcherService extends StaticSpotlightSearcherServ
} }
} }
private openDocs() { private openURL(url: string) {
const url = "https://docs.hoppscotch.io"
window.open(url, "_blank") window.open(url, "_blank")
} }
@@ -101,13 +118,22 @@ export class GeneralSpotlightSearcherService extends StaticSpotlightSearcherServ
invokeAction("flyouts.chat.open") invokeAction("flyouts.chat.open")
break break
case "open_docs": case "open_docs":
this.openDocs() this.openURL("https://docs.hoppscotch.io")
break break
case "open_keybindings": case "open_keybindings":
invokeAction("flyouts.keybinds.toggle") invokeAction("flyouts.keybinds.toggle")
break break
case "social_links": case "link_github":
invokeAction("modals.social.toggle") this.openURL("https://hoppscotch.io/github")
break
case "link_twitter":
this.openURL("https://twitter.com/hoppscotch_io")
break
case "link_discord":
this.openURL("https://hoppscotch.io/discord")
break
case "link_linkedin":
this.openURL("https://www.linkedin.com/company/hoppscotch/")
break break
} }
} }

View File

@@ -0,0 +1,126 @@
import { Ref, computed, effectScope, markRaw, ref, unref, watch } from "vue"
import { getI18n } from "~/modules/i18n"
import {
SpotlightSearcher,
SpotlightSearcherResult,
SpotlightSearcherSessionState,
SpotlightService,
} from ".."
import { Service } from "dioc"
import { useService } from "dioc/vue"
import MiniSearch from "minisearch"
import IconCheckCircle from "~/components/app/spotlight/entry/IconSelected.vue"
import { InterceptorService } from "~/services/interceptor.service"
import IconCircle from "~icons/lucide/circle"
/**
* This searcher is responsible for searching through the interceptor.
* And switching between them.
*/
export class InterceptorSpotlightSearcherService
extends Service
implements SpotlightSearcher
{
public static readonly ID = "INTERCEPTOR_SPOTLIGHT_SEARCHER_SERVICE"
private t = getI18n()
public searcherID = "interceptor"
public searcherSectionTitle = this.t("settings.interceptor")
private readonly spotlight = this.bind(SpotlightService)
constructor() {
super()
this.spotlight.registerSearcher(this)
}
private interceptorService = useService(InterceptorService)
createSearchSession(
query: Readonly<Ref<string>>
): [Ref<SpotlightSearcherSessionState>, () => void] {
const loading = ref(false)
const results = ref<SpotlightSearcherResult[]>([])
const minisearch = new MiniSearch({
fields: ["name", "alternates"],
storeFields: ["name"],
})
const interceptorSelection = this.interceptorService
.currentInterceptorID as Ref<string>
const interceptors = this.interceptorService.availableInterceptors
minisearch.addAll(
interceptors.value.map((entry) => {
let id = `interceptor-${entry.interceptorID}`
if (entry.interceptorID === interceptorSelection.value) {
id += "-selected"
}
const name = unref(entry.name(this.t))
return {
id,
name,
alternates: ["interceptor", "change", name],
}
})
)
const scopeHandle = effectScope()
scopeHandle.run(() => {
watch(
[query],
([query]) => {
results.value = minisearch
.search(query, {
prefix: true,
fuzzy: true,
boost: {
reltime: 2,
},
weights: {
fuzzy: 0.2,
prefix: 0.8,
},
})
.map((x) => {
return {
id: x.id,
icon: markRaw(
x.id.endsWith("-selected") ? IconCheckCircle : IconCircle
),
score: x.score,
text: {
type: "text",
text: [this.t("spotlight.section.interceptor"), x.name],
},
}
})
},
{ immediate: true }
)
})
const onSessionEnd = () => {
scopeHandle.stop()
minisearch.removeAll()
}
const resultObj = computed<SpotlightSearcherSessionState>(() => ({
loading: loading.value,
results: results.value,
}))
return [resultObj, onSessionEnd]
}
onResultSelect(result: SpotlightSearcherResult): void {
const selectedInterceptor = result.id.split("-")[1]
this.interceptorService.currentInterceptorID.value = selectedInterceptor
}
}

View File

@@ -1,4 +1,4 @@
import { Component, markRaw, reactive } from "vue" import { Component, computed, markRaw, reactive } from "vue"
import { invokeAction } from "~/helpers/actions" import { invokeAction } from "~/helpers/actions"
import { getI18n } from "~/modules/i18n" import { getI18n } from "~/modules/i18n"
import { SpotlightSearcherResult, SpotlightService } from ".." import { SpotlightSearcherResult, SpotlightService } from ".."
@@ -7,12 +7,11 @@ import {
StaticSpotlightSearcherService, StaticSpotlightSearcherService,
} from "./base/static.searcher" } from "./base/static.searcher"
import { useRoute } from "vue-router"
import { RequestOptionTabs } from "~/components/http/RequestOptions.vue" import { RequestOptionTabs } from "~/components/http/RequestOptions.vue"
import { currentActiveTab } from "~/helpers/rest/tab" import { currentActiveTab } from "~/helpers/rest/tab"
import IconWindow from "~icons/lucide/app-window" import IconWindow from "~icons/lucide/app-window"
import IconCheck from "~icons/lucide/check" import IconCheckCircle from "~icons/lucide/check-circle"
import IconChevronLeft from "~icons/lucide/chevron-left"
import IconChevronRight from "~icons/lucide/chevron-right"
import IconCode2 from "~icons/lucide/code-2" import IconCode2 from "~icons/lucide/code-2"
import IconCopy from "~icons/lucide/copy" import IconCopy from "~icons/lucide/copy"
import IconFileCode from "~icons/lucide/file-code" import IconFileCode from "~icons/lucide/file-code"
@@ -25,6 +24,7 @@ type Doc = {
text: string | string[] text: string | string[]
alternates: string[] alternates: string[]
icon: object | Component icon: object | Component
excludeFromSearch?: boolean
} }
/** /**
@@ -43,116 +43,160 @@ export class RequestSpotlightSearcherService extends StaticSpotlightSearcherServ
private readonly spotlight = this.bind(SpotlightService) private readonly spotlight = this.bind(SpotlightService)
private route = useRoute()
private isRESTPage = computed(() => this.route.name === "index")
private isGQLPage = computed(() => this.route.name === "graphql")
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(
() => !this.isRESTPage.value ?? !this.isGQLPage.value
),
}, },
save_to_collections: { save_to_collections: {
text: [ text: this.t("spotlight.request.save_as_new"),
this.t("request.save_as"),
this.t("shortcut.request.save_to_collections"),
],
alternates: ["save", "collections"], alternates: ["save", "collections"],
icon: markRaw(IconSave), icon: markRaw(IconSave),
excludeFromSearch: computed(
() => !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(
() => !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(
() => !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"),
alternates: ["copy", "link"], alternates: ["copy", "link"],
icon: markRaw(IconCopy), icon: markRaw(IconCopy),
excludeFromSearch: computed(() => !this.isRESTPage.value),
}, },
reset_request: { reset_request: {
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),
}, },
import_curl: { import_curl: {
text: this.t("shortcut.request.import_curl"), text: this.t("shortcut.request.import_curl"),
alternates: ["import", "curl"], alternates: ["import", "curl"],
icon: markRaw(IconFileCode), icon: markRaw(IconFileCode),
excludeFromSearch: computed(() => !this.isRESTPage.value),
}, },
show_code: { show_code: {
text: this.t("shortcut.request.show_code"), text: this.t("shortcut.request.show_code"),
alternates: ["show", "code"], alternates: ["show", "code"],
icon: markRaw(IconCode2), icon: markRaw(IconCode2),
excludeFromSearch: computed(() => !this.isRESTPage.value),
}, },
// Change request method // Change request method
next_method: {
text: this.t("shortcut.request.next_method"),
alternates: ["next", "method"],
icon: markRaw(IconChevronRight),
},
previous_method: {
text: this.t("shortcut.request.previous_method"),
alternates: ["previous", "method"],
icon: markRaw(IconChevronLeft),
},
get_method: { get_method: {
text: this.t("shortcut.request.get_method"), text: [this.t("spotlight.request.select_method"), "GET"],
alternates: ["get", "method"], alternates: ["get", "method"],
icon: markRaw(IconCheck), icon: markRaw(IconCheckCircle),
excludeFromSearch: computed(() => !this.isRESTPage.value),
}, },
head_method: { head_method: {
text: this.t("shortcut.request.head_method"), text: [this.t("spotlight.request.select_method"), "HEAD"],
alternates: ["head", "method"], alternates: ["head", "method"],
icon: markRaw(IconCheck), icon: markRaw(IconCheckCircle),
excludeFromSearch: computed(() => !this.isRESTPage.value),
}, },
post_method: { post_method: {
text: this.t("shortcut.request.post_method"), text: [this.t("spotlight.request.select_method"), "POST"],
alternates: ["post", "method"], alternates: ["post", "method"],
icon: markRaw(IconCheck), icon: markRaw(IconCheckCircle),
excludeFromSearch: computed(() => !this.isRESTPage.value),
}, },
put_method: { put_method: {
text: this.t("shortcut.request.put_method"), text: [this.t("spotlight.request.select_method"), "PUT"],
alternates: ["put", "method"], alternates: ["put", "method"],
icon: markRaw(IconCheck), icon: markRaw(IconCheckCircle),
excludeFromSearch: computed(() => !this.isRESTPage.value),
}, },
delete_method: { delete_method: {
text: this.t("shortcut.request.delete_method"), text: [this.t("spotlight.request.select_method"), "DELETE"],
alternates: ["delete", "method"], alternates: ["delete", "method"],
icon: markRaw(IconCheck), icon: markRaw(IconCheckCircle),
excludeFromSearch: computed(() => !this.isRESTPage.value),
}, },
// Change sub tabs // Change sub tabs
tab_parameters: { tab_parameters: {
text: this.t("spotlight.request.tab_parameters"), text: [
this.t("spotlight.request.switch_to"),
this.t("spotlight.request.tab_parameters"),
],
alternates: ["parameters", "tab"], alternates: ["parameters", "tab"],
icon: markRaw(IconWindow), icon: markRaw(IconWindow),
excludeFromSearch: computed(
() => !this.isRESTPage.value ?? !this.isGQLPage.value
),
}, },
tab_body: { tab_body: {
text: this.t("spotlight.request.tab_body"), text: [
this.t("spotlight.request.switch_to"),
this.t("spotlight.request.tab_body"),
],
alternates: ["body", "tab"], alternates: ["body", "tab"],
icon: markRaw(IconWindow), icon: markRaw(IconWindow),
excludeFromSearch: computed(
() => !this.isRESTPage.value ?? !this.isGQLPage.value
),
}, },
tab_headers: { tab_headers: {
text: this.t("spotlight.request.tab_headers"), text: [
this.t("spotlight.request.switch_to"),
this.t("spotlight.request.tab_headers"),
],
alternates: ["headers", "tab"], alternates: ["headers", "tab"],
icon: markRaw(IconWindow), icon: markRaw(IconWindow),
excludeFromSearch: computed(
() => !this.isRESTPage.value ?? !this.isGQLPage.value
),
}, },
tab_authorization: { tab_authorization: {
text: this.t("spotlight.request.tab_authorization"), text: [
this.t("spotlight.request.switch_to"),
this.t("spotlight.request.tab_authorization"),
],
alternates: ["authorization", "tab"], alternates: ["authorization", "tab"],
icon: markRaw(IconWindow), icon: markRaw(IconWindow),
excludeFromSearch: computed(
() => !this.isRESTPage.value ?? !this.isGQLPage.value
),
}, },
tab_pre_request_script: { tab_pre_request_script: {
text: this.t("spotlight.request.tab_pre_request_script"), text: [
this.t("spotlight.request.switch_to"),
this.t("spotlight.request.tab_pre_request_script"),
],
alternates: ["pre-request", "script", "tab"], alternates: ["pre-request", "script", "tab"],
icon: markRaw(IconWindow), icon: markRaw(IconWindow),
excludeFromSearch: computed(() => !this.isRESTPage.value),
}, },
tab_tests: { tab_tests: {
text: this.t("spotlight.request.tab_tests"), text: [
this.t("spotlight.request.switch_to"),
this.t("spotlight.request.tab_tests"),
],
alternates: ["tests", "tab"], alternates: ["tests", "tab"],
icon: markRaw(IconWindow), icon: markRaw(IconWindow),
excludeFromSearch: computed(() => !this.isRESTPage.value),
}, },
}) })
@@ -209,12 +253,6 @@ export class RequestSpotlightSearcherService extends StaticSpotlightSearcherServ
case "reset_request": case "reset_request":
invokeAction("request.reset") invokeAction("request.reset")
break break
case "next_method":
invokeAction("request.method.next")
break
case "previous_method":
invokeAction("request.method.prev")
break
case "get_method": case "get_method":
invokeAction("request.method.get") invokeAction("request.method.get")
break break

View File

@@ -10,11 +10,10 @@ import {
} from "./base/static.searcher" } from "./base/static.searcher"
import IconCloud from "~icons/lucide/cloud" import IconCloud from "~icons/lucide/cloud"
import IconGlobe from "~icons/lucide/globe"
import IconMonitor from "~icons/lucide/monitor" import IconMonitor from "~icons/lucide/monitor"
import IconMoon from "~icons/lucide/moon" import IconMoon from "~icons/lucide/moon"
import IconSun from "~icons/lucide/sun" import IconSun from "~icons/lucide/sun"
import IconGlobe from "~icons/lucide/globe"
import IconShieldCheck from "~icons/lucide/shield-check"
import IconType from "~icons/lucide/type" import IconType from "~icons/lucide/type"
type Doc = { type Doc = {
@@ -128,22 +127,6 @@ export class SettingsSpotlightSearcherService extends StaticSpotlightSearcherSer
alternates: ["language", "change language"], alternates: ["language", "change language"],
icon: markRaw(IconGlobe), icon: markRaw(IconGlobe),
}, },
change_interceptor: {
text: [
this.t("spotlight.section.interceptor"),
this.t("spotlight.settings.change_interceptor"),
],
alternates: ["interceptor", "change interceptor"],
icon: markRaw(IconShieldCheck),
},
install_ext: {
text: [
this.t("spotlight.section.interceptor"),
this.t("spotlight.settings.install_extension"),
],
alternates: ["install extension", "extension", "interceptor"],
icon: markRaw(IconShieldCheck),
},
}) })
constructor() { constructor() {
@@ -174,27 +157,12 @@ export class SettingsSpotlightSearcherService extends StaticSpotlightSearcherSer
applySetting("BG_COLOR", theme) applySetting("BG_COLOR", theme)
} }
installExtension() {
const url = navigator.userAgent.includes("Firefox")
? "https://addons.mozilla.org/en-US/firefox/addon/hoppscotch"
: "https://chrome.google.com/webstore/detail/hoppscotch-browser-extens/amknoiejhlmhancpahfcfcfhllgkpbld"
window.open(url, "_blank")
}
public onDocSelected(id: string): void { public onDocSelected(id: string): void {
switch (id) { switch (id) {
case "change_interceptor":
invokeAction("navigation.jump.settings")
break
case "change_lang": case "change_lang":
invokeAction("navigation.jump.settings") invokeAction("navigation.jump.settings")
break break
case "install_ext":
this.installExtension()
break
// theme actions // theme actions
case "theme_system": case "theme_system":
invokeAction("settings.theme.system") invokeAction("settings.theme.system")

View File

@@ -1,4 +1,4 @@
import { Component, markRaw, reactive } from "vue" import { Component, computed, markRaw, reactive } from "vue"
import { getI18n } from "~/modules/i18n" import { getI18n } from "~/modules/i18n"
import { SpotlightSearcherResult, SpotlightService } from ".." import { SpotlightSearcherResult, SpotlightService } from ".."
import { import {
@@ -6,19 +6,23 @@ import {
StaticSpotlightSearcherService, StaticSpotlightSearcherService,
} from "./base/static.searcher" } from "./base/static.searcher"
import { useRoute } from "vue-router"
import { getDefaultRESTRequest } from "~/helpers/rest/default"
import { import {
closeOtherTabs, closeOtherTabs,
closeTab, closeTab,
createNewTab, createNewTab,
currentTabID, currentTabID,
getActiveTabs,
} from "~/helpers/rest/tab" } from "~/helpers/rest/tab"
import IconWindow from "~icons/lucide/app-window" import IconWindow from "~icons/lucide/app-window"
import { getDefaultRESTRequest } from "~/helpers/rest/default" import { invokeAction } from "~/helpers/actions"
type Doc = { type Doc = {
text: string text: string
alternates: string[] alternates: string[]
icon: object | Component icon: object | Component
excludeFromSearch?: boolean
} }
/** /**
@@ -37,21 +41,39 @@ export class TabSpotlightSearcherService extends StaticSpotlightSearcherService<
private readonly spotlight = this.bind(SpotlightService) private readonly spotlight = this.bind(SpotlightService)
private route = useRoute()
private showAction = computed(
() => this.route.name === "index" ?? this.route.name === "graphql"
)
private documents: Record<string, Doc> = reactive({ private documents: Record<string, Doc> = reactive({
duplicate_tab: {
text: this.t("spotlight.tab.duplicate"),
alternates: ["tab", "duplicate", "duplicate tab"],
icon: markRaw(IconWindow),
excludeFromSearch: computed(() => !this.showAction.value),
},
close_current_tab: { close_current_tab: {
text: this.t("spotlight.tab.close_current"), text: this.t("spotlight.tab.close_current"),
alternates: ["tab", "close", "close tab"], alternates: ["tab", "close", "close tab"],
icon: markRaw(IconWindow), icon: markRaw(IconWindow),
excludeFromSearch: computed(
() => !this.showAction.value ?? getActiveTabs().value.length === 1
),
}, },
close_other_tabs: { close_other_tabs: {
text: this.t("spotlight.tab.close_others"), text: this.t("spotlight.tab.close_others"),
alternates: ["tab", "close", "close all"], alternates: ["tab", "close", "close all"],
icon: markRaw(IconWindow), icon: markRaw(IconWindow),
excludeFromSearch: computed(
() => !this.showAction.value ?? getActiveTabs().value.length < 2
),
}, },
open_new_tab: { open_new_tab: {
text: this.t("spotlight.tab.new_tab"), text: this.t("spotlight.tab.new_tab"),
alternates: ["tab", "new", "open tab"], alternates: ["tab", "new", "open tab"],
icon: markRaw(IconWindow), icon: markRaw(IconWindow),
excludeFromSearch: computed(() => !this.showAction.value),
}, },
}) })
@@ -80,6 +102,10 @@ export class TabSpotlightSearcherService extends StaticSpotlightSearcherService<
} }
public onDocSelected(id: string): void { public onDocSelected(id: string): void {
if (id === "duplicate_tab")
invokeAction("request.duplicate-tab", {
tabID: currentTabID.value,
})
if (id === "close_current_tab") closeTab(currentTabID.value) if (id === "close_current_tab") closeTab(currentTabID.value)
if (id === "close_other_tabs") closeOtherTabs(currentTabID.value) if (id === "close_other_tabs") closeOtherTabs(currentTabID.value)
if (id === "open_new_tab") if (id === "open_new_tab")

View File

@@ -8,7 +8,7 @@ import {
ref, ref,
watch, watch,
} from "vue" } from "vue"
import { activeActions$, invokeAction } from "~/helpers/actions" import { invokeAction } from "~/helpers/actions"
import { getI18n } from "~/modules/i18n" import { getI18n } from "~/modules/i18n"
import { import {
SpotlightSearcher, SpotlightSearcher,
@@ -24,6 +24,7 @@ import {
import { Service } from "dioc" import { Service } from "dioc"
import * as E from "fp-ts/Either" import * as E from "fp-ts/Either"
import MiniSearch from "minisearch" import MiniSearch from "minisearch"
import IconCheckCircle from "~/components/app/spotlight/entry/IconSelected.vue"
import { useStreamStatic } from "~/composables/stream" import { useStreamStatic } from "~/composables/stream"
import { runGQLQuery } from "~/helpers/backend/GQLClient" import { runGQLQuery } from "~/helpers/backend/GQLClient"
import { GetMyTeamsDocument, GetMyTeamsQuery } from "~/helpers/backend/graphql" import { GetMyTeamsDocument, GetMyTeamsQuery } from "~/helpers/backend/graphql"
@@ -36,7 +37,7 @@ import IconUserPlus from "~icons/lucide/user-plus"
import IconUsers from "~icons/lucide/users" import IconUsers from "~icons/lucide/users"
type Doc = { type Doc = {
text: string text: string | string[]
alternates: string[] alternates: string[]
icon: object | Component icon: object | Component
excludeFromSearch?: boolean excludeFromSearch?: boolean
@@ -66,14 +67,6 @@ export class WorkspaceSpotlightSearcherService extends StaticSpotlightSearcherSe
} }
)[0] )[0]
private activeActions = useStreamStatic(activeActions$, [], () => {
/* noop */
})[0]
private isLoggedInUser = computed(() =>
this.activeActions.value.includes("user.logout")
)
private isTeamSelected = computed( private isTeamSelected = computed(
() => () =>
this.workspace.value.type === "team" && this.workspace.value.type === "team" &&
@@ -82,31 +75,33 @@ export class WorkspaceSpotlightSearcherService extends StaticSpotlightSearcherSe
private documents: Record<string, Doc> = reactive({ private documents: Record<string, Doc> = reactive({
new_team: { new_team: {
text: this.t("spotlight.workspace.new"), text: [this.t("team.title"), this.t("spotlight.workspace.new")],
alternates: ["new", "team", "workspace"], alternates: ["new", "team", "workspace"],
icon: markRaw(IconUsers), icon: markRaw(IconUsers),
excludeFromSearch: computed(() => !this.isLoggedInUser.value),
}, },
edit_team: { edit_team: {
text: this.t("spotlight.workspace.edit"), text: [this.t("team.title"), this.t("spotlight.workspace.edit")],
alternates: ["edit", "team", "workspace"], alternates: ["edit", "team", "workspace"],
icon: markRaw(IconEdit), icon: markRaw(IconEdit),
excludeFromSearch: computed(() => !this.isTeamSelected.value), excludeFromSearch: computed(() => !this.isTeamSelected.value),
}, },
invite_members: { invite_members: {
text: this.t("spotlight.workspace.invite"), text: [this.t("team.title"), this.t("spotlight.workspace.invite")],
alternates: ["invite", "members", "workspace"], alternates: ["invite", "members", "workspace"],
icon: markRaw(IconUserPlus), icon: markRaw(IconUserPlus),
excludeFromSearch: computed(() => !this.isTeamSelected.value), excludeFromSearch: computed(() => !this.isTeamSelected.value),
}, },
delete_team: { delete_team: {
text: this.t("spotlight.workspace.delete"), text: [this.t("team.title"), this.t("spotlight.workspace.delete")],
alternates: ["delete", "team", "workspace"], alternates: ["delete", "team", "workspace"],
icon: markRaw(IconTrash2), icon: markRaw(IconTrash2),
excludeFromSearch: computed(() => !this.isTeamSelected.value), excludeFromSearch: computed(() => !this.isTeamSelected.value),
}, },
switch_to_personal: { switch_to_personal: {
text: this.t("spotlight.workspace.switch_to_personal"), text: [
this.t("team.title"),
this.t("spotlight.workspace.switch_to_personal"),
],
alternates: ["switch", "team", "workspace", "personal"], alternates: ["switch", "team", "workspace", "personal"],
icon: markRaw(IconUser), icon: markRaw(IconUser),
excludeFromSearch: computed(() => !this.isTeamSelected.value), excludeFromSearch: computed(() => !this.isTeamSelected.value),
@@ -145,8 +140,13 @@ export class WorkspaceSpotlightSearcherService extends StaticSpotlightSearcherSe
} }
public onDocSelected(id: string): void { public onDocSelected(id: string): void {
if (id === "new_team") invokeAction(`modals.team.new`) if (id === "new_team") {
else if (id === "edit_team") invokeAction(`modals.team.edit`) if (platform.auth.getCurrentUser()) {
invokeAction(`modals.team.new`)
} else {
invokeAction(`modals.login.toggle`)
}
} else if (id === "edit_team") invokeAction(`modals.team.edit`)
else if (id === "invite_members") invokeAction(`modals.team.invite`) else if (id === "invite_members") invokeAction(`modals.team.invite`)
else if (id === "delete_team") this.deleteTeam() else if (id === "delete_team") this.deleteTeam()
else if (id === "switch_to_personal") else if (id === "switch_to_personal")
@@ -197,6 +197,14 @@ export class SwitchWorkspaceSpotlightSearcherService
}) })
} }
private workspace = useStreamStatic(
workspaceStatus$,
{ type: "personal" },
() => {
/* noop */
}
)[0]
createSearchSession( createSearchSession(
query: Readonly<Ref<string>> query: Readonly<Ref<string>>
): [Ref<SpotlightSearcherSessionState>, () => void] { ): [Ref<SpotlightSearcherSessionState>, () => void] {
@@ -211,8 +219,16 @@ export class SwitchWorkspaceSpotlightSearcherService
this.fetchMyTeams().then((teams) => { this.fetchMyTeams().then((teams) => {
minisearch.addAll( minisearch.addAll(
teams.map((entry) => { teams.map((entry) => {
let id = `workspace-${entry.id}`
// if id matches add -selected to it
if (
this.workspace.value.type === "team" &&
this.workspace.value.teamID === entry.id
) {
id += "-selected"
}
return { return {
id: `workspace-${entry.id}`, id,
name: entry.name, name: entry.name,
alternates: ["team", "workspace", "change", "switch"], alternates: ["team", "workspace", "change", "switch"],
} }
@@ -241,7 +257,9 @@ export class SwitchWorkspaceSpotlightSearcherService
.map((x) => { .map((x) => {
return { return {
id: x.id, id: x.id,
icon: markRaw(IconUsers), icon: markRaw(
x.id.endsWith("-selected") ? IconCheckCircle : IconUsers
),
score: x.score, score: x.score,
text: { text: {
type: "text", type: "text",