feat: first time user spotlight animation (#3977)
This commit is contained in:
@@ -66,6 +66,8 @@ const SettingsDefSchema = z.object({
|
||||
cookie: z.boolean().catch(true),
|
||||
})
|
||||
),
|
||||
|
||||
HAS_OPENED_SPOTLIGHT: z.optional(z.boolean()),
|
||||
})
|
||||
|
||||
// Common properties shared across REST & GQL collections
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
restCollectionStore,
|
||||
} from "~/newstore/collections"
|
||||
import IconFolder from "~icons/lucide/folder"
|
||||
import IconImport from "~icons/lucide/folder-down"
|
||||
import RESTRequestSpotlightEntry from "~/components/app/spotlight/entry/RESTRequest.vue"
|
||||
import GQLRequestSpotlightEntry from "~/components/app/spotlight/entry/GQLRequest.vue"
|
||||
import {
|
||||
@@ -151,6 +152,10 @@ export class CollectionsSpotlightSearcherService
|
||||
id: `create-collection`,
|
||||
name: this.t("collection.new"),
|
||||
})
|
||||
minisearch.add({
|
||||
id: "import-collection",
|
||||
name: this.t("collection.import"),
|
||||
})
|
||||
}
|
||||
|
||||
if (pageCategory === "rest") {
|
||||
@@ -168,6 +173,11 @@ export class CollectionsSpotlightSearcherService
|
||||
text: this.t("collection.new"),
|
||||
}
|
||||
|
||||
const importCollectionText: SpotlightResultTextType<any> = {
|
||||
type: "text",
|
||||
text: this.t("collection.import_collection"),
|
||||
}
|
||||
|
||||
scopeHandle.run(() => {
|
||||
const isPersonalWorkspace = computed(
|
||||
() => this.workspaceService.currentWorkspace.value.type === "personal"
|
||||
@@ -183,44 +193,37 @@ export class CollectionsSpotlightSearcherService
|
||||
results.value = []
|
||||
return
|
||||
}
|
||||
|
||||
if (pageCategory === "rest") {
|
||||
const searchResults = minisearch.search(query).slice(0, 10)
|
||||
|
||||
results.value = searchResults.map((result) => ({
|
||||
id: result.id,
|
||||
text:
|
||||
result.id === "create-collection"
|
||||
? newCollectionText
|
||||
: {
|
||||
type: "custom",
|
||||
component: markRaw(RESTRequestSpotlightEntry),
|
||||
componentProps: {
|
||||
folderPath: result.id.split("rest-")[1],
|
||||
},
|
||||
},
|
||||
icon: markRaw(IconFolder),
|
||||
score: result.score,
|
||||
}))
|
||||
} else if (pageCategory === "graphql") {
|
||||
const searchResults = minisearch.search(query).slice(0, 10)
|
||||
|
||||
results.value = searchResults.map((result) => ({
|
||||
id: result.id,
|
||||
text:
|
||||
result.id === "create-collection"
|
||||
? newCollectionText
|
||||
: {
|
||||
type: "custom",
|
||||
component: markRaw(GQLRequestSpotlightEntry),
|
||||
componentProps: {
|
||||
folderPath: result.id.split("gql-")[1],
|
||||
},
|
||||
},
|
||||
icon: markRaw(IconFolder),
|
||||
score: result.score,
|
||||
}))
|
||||
const getResultText = (id: string): SpotlightResultTextType<any> => {
|
||||
if (id === "create-collection") return newCollectionText
|
||||
else if (id === "import-collection") return importCollectionText
|
||||
return {
|
||||
type: "custom",
|
||||
component: markRaw(
|
||||
pageCategory === "rest"
|
||||
? RESTRequestSpotlightEntry
|
||||
: GQLRequestSpotlightEntry
|
||||
),
|
||||
componentProps: {
|
||||
folderPath: id.split(
|
||||
pageCategory === "rest" ? "rest-" : "gql-"
|
||||
)[1],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const getResultIcon = (id: string) => {
|
||||
if (id === "import-collection") return markRaw(IconImport)
|
||||
return markRaw(IconFolder)
|
||||
}
|
||||
|
||||
const searchResults = minisearch.search(query).slice(0, 10)
|
||||
|
||||
results.value = searchResults.map((result) => ({
|
||||
id: result.id,
|
||||
text: getResultText(result.id),
|
||||
icon: getResultIcon(result.id),
|
||||
score: result.score,
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -288,6 +291,9 @@ export class CollectionsSpotlightSearcherService
|
||||
public onResultSelect(result: SpotlightSearcherResult): void {
|
||||
if (result.id === "create-collection") return invokeAction("collection.new")
|
||||
|
||||
if (result.id === "import-collection")
|
||||
return invokeAction(`modals.collection.import`)
|
||||
|
||||
const [type, path] = result.id.split("-")
|
||||
|
||||
if (type === "rest") {
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
import { Service } from "dioc"
|
||||
import {
|
||||
SpotlightResultTextType,
|
||||
SpotlightSearcher,
|
||||
SpotlightSearcherResult,
|
||||
SpotlightSearcherSessionState,
|
||||
SpotlightService,
|
||||
} from ".."
|
||||
import { getI18n } from "~/modules/i18n"
|
||||
import { Ref, computed, effectScope, markRaw, watch } from "vue"
|
||||
import { Ref, computed, effectScope, markRaw, ref, watch } from "vue"
|
||||
import { TeamSearchService } from "~/helpers/teams/TeamsSearch.service"
|
||||
import { cloneDeep, debounce } from "lodash-es"
|
||||
import IconFolder from "~icons/lucide/folder"
|
||||
import IconImport from "~icons/lucide/folder-down"
|
||||
import { WorkspaceService } from "~/services/workspace.service"
|
||||
import RESTTeamRequestEntry from "~/components/app/spotlight/entry/RESTTeamRequestEntry.vue"
|
||||
import { RESTTabService } from "~/services/tab/rest"
|
||||
import { HoppInheritedProperty } from "~/helpers/types/HoppInheritedProperties"
|
||||
import { HoppRESTRequest } from "@hoppscotch/data"
|
||||
import MiniSearch from "minisearch"
|
||||
import { invokeAction } from "~/helpers/actions"
|
||||
|
||||
export class TeamsSpotlightSearcherService
|
||||
extends Service
|
||||
@@ -41,9 +45,79 @@ export class TeamsSpotlightSearcherService
|
||||
this.spotlight.registerSearcher(this)
|
||||
}
|
||||
|
||||
private getCurrentPageCategory() {
|
||||
// TODO: Better logic for this ?
|
||||
try {
|
||||
const url = new URL(window.location.href)
|
||||
|
||||
if (url.pathname.startsWith("/graphql")) {
|
||||
return "graphql"
|
||||
} else if (url.pathname === "/") {
|
||||
return "rest"
|
||||
}
|
||||
return "other"
|
||||
} catch (e) {
|
||||
return "other"
|
||||
}
|
||||
}
|
||||
|
||||
createSearchSession(
|
||||
query: Readonly<Ref<string>>
|
||||
): [Ref<SpotlightSearcherSessionState>, () => void] {
|
||||
const pageCategory = this.getCurrentPageCategory()
|
||||
|
||||
// Only show the searcher on the REST page
|
||||
if (pageCategory !== "rest") {
|
||||
return [computed(() => ({ loading: false, results: [] })), () => {}]
|
||||
}
|
||||
|
||||
const results = ref<SpotlightSearcherResult[]>([])
|
||||
|
||||
const minisearch = new MiniSearch({
|
||||
fields: ["name"],
|
||||
storeFields: ["name"],
|
||||
searchOptions: {
|
||||
prefix: true,
|
||||
fuzzy: true,
|
||||
boost: {
|
||||
name: 2,
|
||||
},
|
||||
weights: {
|
||||
fuzzy: 0.2,
|
||||
prefix: 0.8,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
minisearch.add({
|
||||
id: `create-collection`,
|
||||
name: this.t("collection.new"),
|
||||
})
|
||||
minisearch.add({
|
||||
id: "import-collection",
|
||||
name: this.t("collection.import"),
|
||||
})
|
||||
|
||||
const newCollectionText: SpotlightResultTextType<any> = {
|
||||
type: "text",
|
||||
text: this.t("collection.new"),
|
||||
}
|
||||
|
||||
const importCollectionText: SpotlightResultTextType<any> = {
|
||||
type: "text",
|
||||
text: this.t("collection.import_collection"),
|
||||
}
|
||||
|
||||
const getResultText = (id: string): SpotlightResultTextType<any> => {
|
||||
if (id === "create-collection") return newCollectionText
|
||||
return importCollectionText
|
||||
}
|
||||
|
||||
const getResultIcon = (id: string) => {
|
||||
if (id === "import-collection") return markRaw(IconImport)
|
||||
return markRaw(IconFolder)
|
||||
}
|
||||
|
||||
const isTeamWorkspace = computed(
|
||||
() => this.workspaceService.currentWorkspace.value.type === "team"
|
||||
)
|
||||
@@ -59,6 +133,13 @@ export class TeamsSpotlightSearcherService
|
||||
if (this.workspaceService.currentWorkspace.value.type === "team") {
|
||||
const teamID = this.workspaceService.currentWorkspace.value.teamID
|
||||
debouncedSearch(query, teamID)?.catch(() => {})
|
||||
const searchResults = minisearch.search(query).slice(0, 10)
|
||||
results.value = searchResults.map((result) => ({
|
||||
id: result.id,
|
||||
text: getResultText(result.id),
|
||||
icon: getResultIcon(result.id),
|
||||
score: result.score,
|
||||
}))
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -91,36 +172,47 @@ export class TeamsSpotlightSearcherService
|
||||
}
|
||||
|
||||
const resultObj = computed<SpotlightSearcherSessionState>(() => {
|
||||
return isTeamWorkspace.value
|
||||
? {
|
||||
loading: this.teamsSearch.teamsSearchResultsLoading.value,
|
||||
results:
|
||||
this.teamsSearch.teamsSearchResultsFormattedForSpotlight.value.map(
|
||||
(result) => ({
|
||||
id: result.request.id,
|
||||
icon: markRaw(IconFolder),
|
||||
score: 1, // make a better scoring system for this
|
||||
text: {
|
||||
type: "custom",
|
||||
component: markRaw(RESTTeamRequestEntry),
|
||||
componentProps: {
|
||||
collectionTitles: result.collectionTitles,
|
||||
request: result.request,
|
||||
},
|
||||
},
|
||||
})
|
||||
),
|
||||
}
|
||||
: {
|
||||
loading: false,
|
||||
results: [],
|
||||
}
|
||||
})
|
||||
if (isTeamWorkspace.value) {
|
||||
const teamsSearchResults =
|
||||
this.teamsSearch.teamsSearchResultsFormattedForSpotlight.value
|
||||
const minisearchResults = results.value
|
||||
|
||||
const mergedResults = [
|
||||
...teamsSearchResults.map((result) => ({
|
||||
id: result.request.id,
|
||||
icon: markRaw(IconFolder),
|
||||
score: 1, // make a better scoring system for this
|
||||
text: {
|
||||
type: "custom",
|
||||
component: markRaw(RESTTeamRequestEntry),
|
||||
componentProps: {
|
||||
collectionTitles: result.collectionTitles,
|
||||
request: result.request,
|
||||
},
|
||||
},
|
||||
})),
|
||||
...minisearchResults,
|
||||
] as SpotlightSearcherResult[]
|
||||
|
||||
return {
|
||||
loading: this.teamsSearch.teamsSearchResultsLoading.value,
|
||||
results: mergedResults,
|
||||
}
|
||||
}
|
||||
return {
|
||||
loading: false,
|
||||
results: [],
|
||||
}
|
||||
})
|
||||
return [resultObj, onSessionEnd]
|
||||
}
|
||||
|
||||
onResultSelect(result: SpotlightSearcherResult): void {
|
||||
if (result.id === "create-collection") return invokeAction("collection.new")
|
||||
|
||||
if (result.id === "import-collection")
|
||||
return invokeAction(`modals.collection.import`)
|
||||
|
||||
let inheritedProperties: HoppInheritedProperty | undefined = undefined
|
||||
|
||||
const selectedRequest = this.teamsSearch.searchResultsRequests[result.id]
|
||||
|
||||
Reference in New Issue
Block a user