feature: search auto complete API

This commit is contained in:
Alikia2x 2024-04-10 00:11:08 +08:00
parent 8e65767ac1
commit 04d72d3ca8
7 changed files with 347 additions and 294 deletions

View File

@ -0,0 +1,13 @@
import { completeGoogle } from "search-engine-autocomplete";
import { NextRequest } from "next/server"
export async function GET(request: NextRequest) {
const query = request.nextUrl.searchParams.get('q')!;
let language = request.nextUrl.searchParams.get('l');
if (language === null) language = 'en-US';
const data = await completeGoogle(query, language);
const completeWord: string | undefined = data.suggestions.filter((s) => {
return s.relativeRelevance > 0.96
})[0]?.suggestion;
return new Response(completeWord ? completeWord : query);
}

View File

@ -0,0 +1,9 @@
import { completeGoogle } from "search-engine-autocomplete";
import { NextRequest } from "next/server"
export async function GET(request: NextRequest) {
const query = request.nextUrl.searchParams.get('q')!;
const language = "ja-JP";
const data = await completeGoogle(query, language);
return new Response(JSON.stringify(data));
}

View File

@ -1,16 +1,24 @@
import { useState } from "react"; import { useEffect, useState } from "react";
import SuggestionBox from "./suggestionBox"; import SuggestionBox from "./suggestionBox";
import Suggestion from "./suggestion"; import Suggestion from "./suggestion";
import { useRecoilValue } from "recoil";
import { queryState } from "@/components/state/query";
export default function(){ export default function () {
const [suggestion, setSuggetsion] = useState([]); const [suggestion, setSuggetsion] = useState([]);
const query = useRecoilValue(queryState);
useEffect(() => {
fetch(`/api/suggestion?q=${query}`)
.then((res) => res.json())
.then((data) => {
setSuggetsion(data);
});
}, [query]);
return ( return (
<SuggestionBox> <SuggestionBox>
{ {suggestion.map((s: string) => {
suggestion.map((s: string) => { return <Suggestion key={s}>{s}</Suggestion>;
return <Suggestion key={s}>{s}</Suggestion> })}
})
}
</SuggestionBox> </SuggestionBox>
) );
} }

View File

@ -1,16 +1,17 @@
"use client"; "use client";
import { useRecoilValue } from "recoil"; import { useRecoilState, useRecoilValue } from "recoil";
import { settingsState } from "../state/settings"; import { settingsState } from "../state/settings";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import { useState } from "react"; import { useState } from "react";
import { normalizeURL } from "@/lib/normalizeURL"; import { normalizeURL } from "@/lib/normalizeURL";
import validLink from "@/lib/url/validLink"; import validLink from "@/lib/url/validLink";
import { queryState } from "../state/query";
export default function Search(props: { onFocus: () => void }) { export default function Search(props: { onFocus: () => void }) {
const settings: settings = useRecoilValue(settingsState); const settings: settings = useRecoilValue(settingsState);
const t = useTranslations("Search"); const t = useTranslations("Search");
const [query, setQuery] = useState(""); const [query, setQuery] = useRecoilState(queryState);
let style = "default"; let style = "default";
@ -41,7 +42,11 @@ export default function Search(props: { onFocus: () => void }) {
placeholder={t("placeholder")} placeholder={t("placeholder")}
onFocus={props.onFocus} onFocus={props.onFocus}
onKeyDown={handleKeydown} onKeyDown={handleKeydown}
onChange={(e) => setQuery(e.target.value)} onChange={(e) =>
setQuery((_) => {
return e.target.value;
})
}
autoComplete="off" autoComplete="off"
autoCorrect="off" autoCorrect="off"
autoCapitalize="off" autoCapitalize="off"

10
components/state/query.ts Normal file
View File

@ -0,0 +1,10 @@
import { atom } from "recoil";
const queryState = atom({
key: "searchQuery",
default: ""
});
export {
queryState,
}

View File

@ -1,6 +1,6 @@
{ {
"name": "sparkhome", "name": "sparkhome",
"version": "4.2.0", "version": "4.10.0",
"private": false, "private": false,
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",
@ -12,14 +12,15 @@
"dependencies": { "dependencies": {
"@nextui-org/react": "^2.2.10", "@nextui-org/react": "^2.2.10",
"clsx": "^2.1.0", "clsx": "^2.1.0",
"framer-motion": "^11.0.24", "framer-motion": "^11.0.25",
"next": "14.1.4", "next": "14.1.4",
"next-intl": "^3.10.0", "next-intl": "^3.11.1",
"next-themes": "^0.3.0", "next-themes": "^0.3.0",
"punycode": "^2.3.1", "punycode": "^2.3.1",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"recoil": "^0.7.7", "recoil": "^0.7.7",
"search-engine-autocomplete": "^0.4.2",
"tailwind-merge": "^2.2.2", "tailwind-merge": "^2.2.2",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"valid-url": "^1.0.9", "valid-url": "^1.0.9",
@ -28,19 +29,19 @@
"devDependencies": { "devDependencies": {
"@jest/globals": "^29.7.0", "@jest/globals": "^29.7.0",
"@testing-library/jest-dom": "^6.4.2", "@testing-library/jest-dom": "^6.4.2",
"@testing-library/react": "^14.2.2", "@testing-library/react": "^14.3.0",
"@types/jest": "^29.5.12", "@types/jest": "^29.5.12",
"@types/node": "^20", "@types/node": "^20.12.6",
"@types/punycode": "^2.1.4", "@types/punycode": "^2.1.4",
"@types/react": "^18", "@types/react": "^18.2.75",
"@types/react-dom": "^18", "@types/react-dom": "^18.2.24",
"@types/valid-url": "^1.0.7", "@types/valid-url": "^1.0.7",
"autoprefixer": "^10.0.1", "autoprefixer": "^10.4.19",
"jest": "^29.7.0", "jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0", "jest-environment-jsdom": "^29.7.0",
"postcss": "^8", "postcss": "^8.4.38",
"tailwindcss": "^3.3.0", "tailwindcss": "^3.4.3",
"ts-jest": "^29.1.2", "ts-jest": "^29.1.2",
"typescript": "^5" "typescript": "^5.4.4"
} }
} }

File diff suppressed because it is too large Load Diff