import React from "react";
import { createSearchParams, Link, useSearchParams } from "react-router-dom";
import useSWR from "swr";
import { Loading } from "../ui/atoms/loading";
import { WRITER_MODE, KUMA_HOST } from "../env";
import { useLocale } from "../hooks";
import { appendURL } from "./utils";
import { Button } from "../ui/atoms/button";
import "./search-results.scss";
import NoteCard from "../ui/molecules/notecards";
import LANGUAGES_RAW from "../../../libs/languages";
const LANGUAGES = new Map(
Object.entries(LANGUAGES_RAW).map(([locale, data]) => {
return [locale.toLowerCase(), data];
})
);
const SORT_OPTIONS = [
["best", "Best"],
["relevance", "Relevance"],
["popularity", "Popularity"],
];
type Highlight = {
body?: string[];
title?: string[];
};
interface Document {
mdn_url: string;
locale: string;
title: string;
highlight: Highlight;
summary: string;
score: number;
popularity: number;
}
type Total = {
value: number;
relation: "eq" | "gt";
};
interface Metadata {
// The time it took Elasticsearch query
took_ms: number;
// The time it took Kuma's API to wrap the Elasticsearch query
api_took_ms: number;
total: Total;
}
interface Suggestion {
text: string;
total: Total;
}
interface FormErrorMessage {
message: string;
code: string;
}
type FormErrors = [{ key: string }, FormErrorMessage[]];
class BadRequestError extends Error {
public formErrors: FormErrors;
constructor(formErrors: FormErrors) {
super(`BadRequestError ${JSON.stringify(formErrors)}`);
this.formErrors = formErrors;
}
}
class ServerOperationalError extends Error {
public statusCode: number;
constructor(statusCode: number) {
super(`ServerOperationalError ${statusCode}`);
this.statusCode = statusCode;
}
}
export default function SearchResults() {
const [searchParams] = useSearchParams();
const locale = useLocale();
// A call to `/api/v1/search` will default to mean the same thing as
// a call to `/api/v1/search?locale=en-us`. But if they page you're currently
// on, (e.g. `/ja/search`) we should supply a more explicit default.
const sp = createSearchParams(searchParams);
if (!searchParams.getAll("locale").length) {
// In other words, if it's not explicitly set by the current query string,
// force in the locale based on the current URL (path).
sp.set("locale", locale);
}
const fetchURL = `/api/v1/search?${sp.toString()}`;
const { data, error } = useSWR(
fetchURL,
async (url) => {
const response = await fetch(url);
if (response.status === 400) {
const badRequest = await response.json();
throw new BadRequestError(badRequest.errors);
} else if (response.status >= 500) {
throw new ServerOperationalError(response.status);
} else if (!response.ok) {
throw new Error(`${response.status} on ${url}`);
}
return await response.json();
},
{
revalidateOnFocus: process.env.NODE_ENV === "development",
}
);
const page = searchParams.get("page");
const [initialPage, setInitialPage] = React.useState(page);
React.useEffect(() => {
if (page !== initialPage) {
setInitialPage(page);
const resultsElement = document.querySelector("div.site-search");
if (resultsElement) {
resultsElement.scrollIntoView({ behavior: "smooth" });
}
}
}, [page, initialPage]);
if (error) {
if (error instanceof BadRequestError) {
return (
Something else went horribly wrong with the search
Site-search is proxied to {error.toString()}
Note!
{kumaHost} which means that
some content found doesn't reflect what's in your current branch.
The search didn't work because there were problems with the input.
{key}{" "}
{messages.map((message) => {
return {message.message};
})}
The search failed because the server failed to respond.
If you're curious, it was a {statusCode} error.
Found
Did you mean...
{highlights.length ? ( highlights.map((highlight, i) => { return ( ); }) ) : ( {document.summary} )}
{searchParams.get("debug") !== null && ( score:{document.score},{" "}
popularity: {document.popularity},{" "}
)}