import Fuse from "fuse.js"; import { getLang } from "../locales"; import { StoreKey } from "../constant"; import { nanoid } from "nanoid"; import { createPersistStore } from "../utils/store"; import { getFullApi } from "@/app/config/client"; export interface Prompt { id: string; isUser?: boolean; title: string; content: string; createdAt: number; } export const SearchService = { ready: false, builtinEngine: new Fuse([], { keys: ["title"] }), userEngine: new Fuse([], { keys: ["title"] }), count: { builtin: 0, }, allPrompts: [] as Prompt[], builtinPrompts: [] as Prompt[], init(builtinPrompts: Prompt[], userPrompts: Prompt[]) { if (this.ready) { return; } this.allPrompts = userPrompts.concat(builtinPrompts); this.builtinPrompts = builtinPrompts.slice(); this.builtinEngine.setCollection(builtinPrompts); this.userEngine.setCollection(userPrompts); this.ready = true; }, remove(id: string) { this.userEngine.remove((doc) => doc.id === id); }, add(prompt: Prompt) { this.userEngine.add(prompt); }, search(text: string) { const userResults = this.userEngine.search(text); const builtinResults = this.builtinEngine.search(text); return userResults.concat(builtinResults).map((v) => v.item); }, }; export const usePromptStore = createPersistStore( { counter: 0, prompts: {} as Record, }, (set, get) => ({ add(prompt: Prompt) { const prompts = get().prompts; prompt.id = nanoid(); prompt.isUser = true; prompt.createdAt = Date.now(); prompts[prompt.id] = prompt; set(() => ({ prompts: prompts, })); return prompt.id!; }, get(id: string) { const targetPrompt = get().prompts[id]; if (!targetPrompt) { return SearchService.builtinPrompts.find((v) => v.id === id); } return targetPrompt; }, remove(id: string) { const prompts = get().prompts; delete prompts[id]; Object.entries(prompts).some(([key, prompt]) => { if (prompt.id === id) { delete prompts[key]; return true; } return false; }); SearchService.remove(id); set(() => ({ prompts, counter: get().counter + 1, })); }, getUserPrompts() { const userPrompts = Object.values(get().prompts ?? {}); userPrompts.sort((a, b) => b.id && a.id ? b.createdAt - a.createdAt : 0, ); return userPrompts; }, updatePrompt(id: string, updater: (prompt: Prompt) => void) { const prompt = get().prompts[id] ?? { title: "", content: "", id, }; SearchService.remove(id); updater(prompt); const prompts = get().prompts; prompts[id] = prompt; set(() => ({ prompts })); SearchService.add(prompt); }, search(text: string) { if (text.length === 0) { // return all rompts return this.getUserPrompts().concat(SearchService.builtinPrompts); } return SearchService.search(text) as Prompt[]; }, }), { name: StoreKey.Prompt, version: 3, migrate(state, version) { const newState = JSON.parse(JSON.stringify(state)) as { prompts: Record; }; if (version < 3) { Object.values(newState.prompts).forEach((p) => (p.id = nanoid())); } return newState as any; }, onRehydrateStorage(state) { const PROMPT_URL = getFullApi("./prompts.json"); type PromptList = Array<[string, string]>; fetch(PROMPT_URL) .then((res) => res.json()) .then((res) => { let fetchPrompts = [res.en, res.cn]; if (getLang() === "cn") { fetchPrompts = fetchPrompts.reverse(); } const builtinPrompts = fetchPrompts.map((promptList: PromptList) => { return promptList.map( ([title, content]) => ({ id: nanoid(), title, content, createdAt: Date.now(), }) as Prompt, ); }); const userPrompts = usePromptStore.getState().getUserPrompts() ?? []; const allPromptsForSearch = builtinPrompts .reduce((pre, cur) => pre.concat(cur), []) .filter((v) => !!v.title && !!v.content); SearchService.count.builtin = res.en.length + res.cn.length; SearchService.init(allPromptsForSearch, userPrompts); }); }, }, );