Updated meilisearch

This commit is contained in:
DrMint 2023-04-09 09:59:43 +02:00
parent 0f735c62cc
commit 5be25c656f
16 changed files with 282 additions and 274 deletions

View File

@ -46,7 +46,7 @@ module.exports = {
"func-style": ["warn", "expression"],
"grouped-accessor-pairs": "warn",
"guard-for-in": "warn",
"id-denylist": ["error", "data", "err", "e", "cb", "callback", "i"],
"id-denylist": ["error", "err", "e", "cb", "callback", "i"],
// "id-length": "warn",
"id-match": "warn",
"max-classes-per-file": ["error", 1],

28
package-lock.json generated
View File

@ -20,7 +20,7 @@
"markdown-to-jsx": "^7.2.0",
"marked": "^4.3.0",
"material-symbols": "^0.5.5",
"meilisearch": "^0.31.1",
"meilisearch": "^0.32.3",
"next": "^13.3.0",
"nodemailer": "^6.9.1",
"rc-slider": "^10.1.1",
@ -60,7 +60,7 @@
"eslint-plugin-import": "^2.27.5",
"graphql": "^16.6.0",
"graphql-request": "5.1.0",
"next-sitemap": "^4.0.6",
"next-sitemap": "^4.0.7",
"prettier": "^2.8.7",
"prettier-plugin-tailwindcss": "^0.2.7",
"tailwindcss": "^3.3.1",
@ -8021,9 +8021,9 @@
"integrity": "sha512-NFUsjEVBNZvcRRqslY0RWnmlGgjhJkpDQkQs42o52gT2AmIbaP6V7wTRgyTkLAoD5VtpgpIx9eoOAXcH2ynwkg=="
},
"node_modules/meilisearch": {
"version": "0.31.1",
"resolved": "https://registry.npmjs.org/meilisearch/-/meilisearch-0.31.1.tgz",
"integrity": "sha512-ajMieU0e25lLkT+05J0snX0Ycow1UofxIy5sag03flERUbjXq8ouVwkrJkW27JsKftIeDeffRRRr89LasU9+0w==",
"version": "0.32.3",
"resolved": "https://registry.npmjs.org/meilisearch/-/meilisearch-0.32.3.tgz",
"integrity": "sha512-EOgfBuRE5SiIPIpEDYe2HO0D7a4z5bexIgaAdJFma/dH5hx1kwO+u/qb2g3qKyjG+iA3l8MlmTj/Xd72uahaAw==",
"dependencies": {
"cross-fetch": "^3.1.5"
}
@ -8208,9 +8208,9 @@
}
},
"node_modules/next-sitemap": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/next-sitemap/-/next-sitemap-4.0.6.tgz",
"integrity": "sha512-pZ9tynYe6mRR189qZqcOlWVgM1Gxo07BJQW0AjerKmLwQOt+6FQMdaDgifgCt6jDT3Y3EG/+NUDDZRcd0gbPkA==",
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/next-sitemap/-/next-sitemap-4.0.7.tgz",
"integrity": "sha512-S2g5IwJeO0+ecmFq981fb+Mw9YWmntOuN/qTCxclSkUibOJ8qKIOye0vn6NEJ1S4tKhbY+MTYKgJpNdFZYxLoA==",
"dev": true,
"funding": [
{
@ -16972,9 +16972,9 @@
"integrity": "sha512-NFUsjEVBNZvcRRqslY0RWnmlGgjhJkpDQkQs42o52gT2AmIbaP6V7wTRgyTkLAoD5VtpgpIx9eoOAXcH2ynwkg=="
},
"meilisearch": {
"version": "0.31.1",
"resolved": "https://registry.npmjs.org/meilisearch/-/meilisearch-0.31.1.tgz",
"integrity": "sha512-ajMieU0e25lLkT+05J0snX0Ycow1UofxIy5sag03flERUbjXq8ouVwkrJkW27JsKftIeDeffRRRr89LasU9+0w==",
"version": "0.32.3",
"resolved": "https://registry.npmjs.org/meilisearch/-/meilisearch-0.32.3.tgz",
"integrity": "sha512-EOgfBuRE5SiIPIpEDYe2HO0D7a4z5bexIgaAdJFma/dH5hx1kwO+u/qb2g3qKyjG+iA3l8MlmTj/Xd72uahaAw==",
"requires": {
"cross-fetch": "^3.1.5"
}
@ -17092,9 +17092,9 @@
}
},
"next-sitemap": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/next-sitemap/-/next-sitemap-4.0.6.tgz",
"integrity": "sha512-pZ9tynYe6mRR189qZqcOlWVgM1Gxo07BJQW0AjerKmLwQOt+6FQMdaDgifgCt6jDT3Y3EG/+NUDDZRcd0gbPkA==",
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/next-sitemap/-/next-sitemap-4.0.7.tgz",
"integrity": "sha512-S2g5IwJeO0+ecmFq981fb+Mw9YWmntOuN/qTCxclSkUibOJ8qKIOye0vn6NEJ1S4tKhbY+MTYKgJpNdFZYxLoA==",
"dev": true,
"requires": {
"@corex/deepmerge": "^4.0.37",

View File

@ -16,7 +16,7 @@
"generate": "graphql-codegen --config graphql-codegen.config.js",
"tsc": "tsc",
"prettier": "prettier --end-of-line auto --write .",
"update": "ncu --interactive --format group"
"upgrade": "ncu --interactive --format group"
},
"dependencies": {
"@fontsource/opendyslexic": "^4.5.4",
@ -33,7 +33,7 @@
"markdown-to-jsx": "^7.2.0",
"marked": "^4.3.0",
"material-symbols": "^0.5.5",
"meilisearch": "^0.31.1",
"meilisearch": "^0.32.3",
"next": "^13.3.0",
"nodemailer": "^6.9.1",
"rc-slider": "^10.1.1",
@ -73,7 +73,7 @@
"eslint-plugin-import": "^2.27.5",
"graphql": "^16.6.0",
"graphql-request": "5.1.0",
"next-sitemap": "^4.0.6",
"next-sitemap": "^4.0.7",
"prettier": "^2.8.7",
"prettier-plugin-tailwindcss": "^0.2.7",
"tailwindcss": "^3.3.1",

View File

@ -1,5 +1,4 @@
// eslint-disable-next-line import/named
import { Placement } from "tippy.js";
import type { Placement } from "tippy.js";
import { Button } from "./Button";
import { ToolTip } from "components/ToolTip";
import { cJoin } from "helpers/className";

View File

@ -5,9 +5,14 @@ import { sendAnalytics } from "helpers/analytics";
import { atoms } from "contexts/atoms";
import { useAtomPair, useAtomSetter } from "helpers/atoms";
import { TextInput } from "components/Inputs/TextInput";
import { containsHighlight, CustomSearchResponse, meiliSearch } from "helpers/search";
import {
containsHighlight,
CustomSearchResponse,
filterHitsWithHighlight,
meiliMultiSearch,
} from "helpers/search";
import { PreviewCard, TranslatedPreviewCard } from "components/PreviewCard";
import { filterDefined, filterHasAttributes, isDefined } from "helpers/asserts";
import { filterHasAttributes, isDefined } from "helpers/asserts";
import {
MeiliContent,
MeiliIndices,
@ -35,20 +40,28 @@ const SEARCH_LIMIT = 8;
* COMPONENT
*/
interface MultiResult {
libraryItems?: CustomSearchResponse<MeiliLibraryItem>;
contents?: CustomSearchResponse<MeiliContent>;
videos?: CustomSearchResponse<MeiliVideo>;
posts?: CustomSearchResponse<MeiliPost>;
wikiPages?: CustomSearchResponse<MeiliWikiPage>;
weapons?: CustomSearchResponse<MeiliWeapon>;
}
export const SearchPopup = (): JSX.Element => {
const [isSearchOpened, setSearchOpened] = useAtomPair(atoms.layout.searchOpened);
const [query, setQuery] = useState("");
const { format } = useFormat();
const [libraryItems, setLibraryItems] = useState<CustomSearchResponse<MeiliLibraryItem>>();
const [contents, setContents] = useState<CustomSearchResponse<MeiliContent>>();
const [videos, setVideos] = useState<CustomSearchResponse<MeiliVideo>>();
const [posts, setPosts] = useState<CustomSearchResponse<MeiliPost>>();
const [wikiPages, setWikiPages] = useState<CustomSearchResponse<MeiliWikiPage>>();
const [weapons, setWeapons] = useState<CustomSearchResponse<MeiliWeapon>>();
const [multiResult, setMultiResult] = useState<MultiResult>({});
useEffect(() => {
const fetchLibraryItems = async () => {
const searchResult = await meiliSearch(MeiliIndices.LIBRARY_ITEM, query, {
const fetchMultiResult = async () => {
const searchResults = (
await meiliMultiSearch([
{
indexUid: MeiliIndices.LIBRARY_ITEM,
q: query,
limit: SEARCH_LIMIT,
attributesToRetrieve: [
"title",
@ -64,38 +77,18 @@ export const SearchPopup = (): JSX.Element => {
],
attributesToHighlight: ["title", "subtitle", "descriptions"],
attributesToCrop: ["descriptions"],
});
searchResult.hits = searchResult.hits.map((item) => {
if (Object.keys(item._matchesPosition).some((match) => match.startsWith("descriptions"))) {
item._formatted.descriptions = filterDefined(item._formatted.descriptions).filter(
(description) => containsHighlight(JSON.stringify(description))
);
}
return item;
});
setLibraryItems(searchResult);
};
const fetchContents = async () => {
const searchResult = await meiliSearch(MeiliIndices.CONTENT, query, {
},
{
indexUid: MeiliIndices.CONTENT,
q: query,
limit: SEARCH_LIMIT,
attributesToRetrieve: ["translations", "id", "slug", "categories", "type", "thumbnail"],
attributesToHighlight: ["translations"],
attributesToCrop: ["translations.displayable_description"],
});
searchResult.hits = searchResult.hits.map((item) => {
if (Object.keys(item._matchesPosition).some((match) => match.startsWith("translations"))) {
item._formatted.translations = filterDefined(item._formatted.translations).filter(
(translation) => containsHighlight(JSON.stringify(translation))
);
}
return item;
});
setContents(searchResult);
};
const fetchVideos = async () => {
const searchResult = await meiliSearch(MeiliIndices.VIDEOS, query, {
},
{
indexUid: MeiliIndices.VIDEOS,
q: query,
limit: SEARCH_LIMIT,
attributesToRetrieve: [
"title",
@ -108,50 +101,31 @@ export const SearchPopup = (): JSX.Element => {
],
attributesToHighlight: ["title", "channel", "description"],
attributesToCrop: ["description"],
});
setVideos(searchResult);
};
const fetchPosts = async () => {
const searchResult = await meiliSearch(MeiliIndices.POST, query, {
},
{
indexUid: MeiliIndices.POST,
q: query,
limit: SEARCH_LIMIT,
attributesToRetrieve: ["translations", "thumbnail", "slug", "date", "categories"],
attributesToHighlight: ["translations.title", "translations.excerpt", "translations.body"],
attributesToHighlight: [
"translations.title",
"translations.excerpt",
"translations.body",
],
attributesToCrop: ["translations.body"],
filter: ["hidden = false"],
});
searchResult.hits = searchResult.hits.map((item) => {
if (Object.keys(item._matchesPosition).some((match) => match.startsWith("translations"))) {
item._formatted.translations = filterDefined(item._formatted.translations).filter(
(translation) => JSON.stringify(translation).includes("</mark>")
);
}
return item;
});
setPosts(searchResult);
};
const fetchWeapons = async () => {
const searchResult = await meiliSearch(MeiliIndices.WEAPON, query, {
},
{
indexUid: MeiliIndices.WEAPON,
q: query,
limit: SEARCH_LIMIT,
attributesToRetrieve: ["*"],
attributesToHighlight: ["translations.description", "translations.names"],
attributesToCrop: ["translations.description"],
sort: ["slug:asc"],
});
searchResult.hits = searchResult.hits.map((item) => {
if (Object.keys(item._matchesPosition).some((match) => match.startsWith("translations"))) {
item._formatted.translations = filterDefined(item._formatted.translations).filter(
(translation) => JSON.stringify(translation).includes("</mark>")
);
}
return item;
});
setWeapons(searchResult);
};
const fetchWikiPages = async () => {
const searchResult = await meiliSearch(MeiliIndices.WIKI_PAGE, query, {
},
{
indexUid: MeiliIndices.WIKI_PAGE,
q: query,
limit: SEARCH_LIMIT,
attributesToHighlight: [
"translations.title",
@ -160,35 +134,60 @@ export const SearchPopup = (): JSX.Element => {
"translations.displayable_description",
],
attributesToCrop: ["translations.displayable_description"],
});
searchResult.hits = searchResult.hits.map((item) => {
if (
Object.keys(item._matchesPosition).filter((match) => match.startsWith("translations"))
.length > 0
) {
item._formatted.translations = filterDefined(item._formatted.translations).filter(
(translation) => JSON.stringify(translation).includes("</mark>")
},
])
).results;
const result: MultiResult = {};
searchResults.map((searchResult) => {
switch (searchResult.indexUid) {
case MeiliIndices.LIBRARY_ITEM: {
result.libraryItems = filterHitsWithHighlight<MeiliLibraryItem>(
searchResult,
"descriptions"
);
break;
}
case MeiliIndices.CONTENT: {
result.contents = filterHitsWithHighlight<MeiliContent>(searchResult, "translations");
break;
}
case MeiliIndices.VIDEOS: {
result.videos = filterHitsWithHighlight<MeiliVideo>(searchResult);
break;
}
case MeiliIndices.POST: {
result.posts = filterHitsWithHighlight<MeiliPost>(searchResult, "translations");
break;
}
case MeiliIndices.WEAPON: {
result.weapons = filterHitsWithHighlight<MeiliWeapon>(searchResult, "translations");
break;
}
case MeiliIndices.WIKI_PAGE: {
result.wikiPages = filterHitsWithHighlight<MeiliWikiPage>(searchResult, "translations");
break;
}
default: {
console.log("What the fuck?");
}
}
return item;
});
setWikiPages(searchResult);
setMultiResult(result);
};
if (query === "") {
setWikiPages(undefined);
setLibraryItems(undefined);
setContents(undefined);
setVideos(undefined);
setPosts(undefined);
setWeapons(undefined);
setMultiResult({});
} else {
fetchWikiPages();
fetchLibraryItems();
fetchContents();
fetchVideos();
fetchPosts();
fetchWeapons();
fetchMultiResult();
}
}, [query]);
@ -207,15 +206,15 @@ export const SearchPopup = (): JSX.Element => {
<TextInput onChange={setQuery} value={query} placeholder={format("search_title")} />
<div className="flex w-full flex-wrap gap-12 gap-x-16">
{isDefined(libraryItems) && (
{isDefined(multiResult.libraryItems) && (
<SearchResultSection
title={format("library")}
icon="auto_stories"
href={`/library?page=1&query=${query}\
&sort=0&primary=true&secondary=true&subitems=true&status=all`}
totalHits={libraryItems.estimatedTotalHits}>
totalHits={multiResult.libraryItems.estimatedTotalHits}>
<div className="flex flex-wrap items-start gap-x-6 gap-y-8">
{libraryItems.hits.map((item) => (
{multiResult.libraryItems.hits.map((item) => (
<TranslatedPreviewCard
key={item.id}
className="w-56"
@ -255,14 +254,14 @@ export const SearchPopup = (): JSX.Element => {
</SearchResultSection>
)}
{isDefined(contents) && (
{isDefined(multiResult.contents) && (
<SearchResultSection
title={format("contents")}
icon="workspaces"
href={`/contents/all?page=1&query=${query}&sort=0`}
totalHits={contents.estimatedTotalHits}>
totalHits={multiResult.contents.estimatedTotalHits}>
<div className="flex flex-wrap items-start gap-x-6 gap-y-8">
{contents.hits.map((item) => (
{multiResult.contents.hits.map((item) => (
<TranslatedPreviewCard
key={item.id}
className="w-56"
@ -300,14 +299,14 @@ export const SearchPopup = (): JSX.Element => {
</SearchResultSection>
)}
{isDefined(wikiPages) && (
{isDefined(multiResult.wikiPages) && (
<SearchResultSection
title={format("wiki")}
icon="travel_explore"
href={`/wiki?page=1&query=${query}`}
totalHits={wikiPages.estimatedTotalHits}>
totalHits={multiResult.wikiPages.estimatedTotalHits}>
<div className="flex flex-wrap items-start gap-x-6 gap-y-8">
{wikiPages.hits.map((item) => (
{multiResult.wikiPages.hits.map((item) => (
<TranslatedPreviewCard
key={item.id}
className="w-56"
@ -352,14 +351,14 @@ export const SearchPopup = (): JSX.Element => {
</SearchResultSection>
)}
{isDefined(posts) && (
{isDefined(multiResult.posts) && (
<SearchResultSection
title={format("news")}
icon="newspaper"
href={`/news?page=1&query=${query}`}
totalHits={posts.estimatedTotalHits}>
totalHits={multiResult.posts.estimatedTotalHits}>
<div className="flex flex-wrap items-start gap-x-6 gap-y-8">
{posts.hits.map((item) => (
{multiResult.posts.hits.map((item) => (
<TranslatedPreviewCard
className="w-56"
key={item.id}
@ -395,14 +394,14 @@ export const SearchPopup = (): JSX.Element => {
</SearchResultSection>
)}
{isDefined(videos) && (
{isDefined(multiResult.videos) && (
<SearchResultSection
title={format("videos")}
icon="movie"
href={`/archives/videos?page=1&query=${query}&sort=1&gone=`}
totalHits={videos.estimatedTotalHits}>
totalHits={multiResult.videos.estimatedTotalHits}>
<div className="flex flex-wrap items-start gap-x-6 gap-y-8">
{videos.hits.map((item) => (
{multiResult.videos.hits.map((item) => (
<PreviewCard
className="w-56"
key={item.uid}
@ -435,14 +434,14 @@ export const SearchPopup = (): JSX.Element => {
</SearchResultSection>
)}
{isDefined(weapons) && (
{isDefined(multiResult.weapons) && (
<SearchResultSection
title={format("weapon", { count: Infinity })}
icon="shield"
href={`/wiki/weapons?page=1&query=${query}`}
totalHits={weapons.estimatedTotalHits}>
totalHits={multiResult.weapons.estimatedTotalHits}>
<div className="flex flex-wrap items-start gap-x-6 gap-y-8">
{weapons.hits.map((item) => (
{multiResult.weapons.hits.map((item) => (
<TranslatedPreviewCard
key={item.id}
className="w-56"

View File

@ -1,5 +1,5 @@
// eslint-disable-next-line import/named
import Tippy, { TippyProps } from "@tippyjs/react";
import Tippy from "@tippyjs/react";
import type { TippyProps } from "@tippyjs/react";
import { cJoin } from "helpers/className";
import "tippy.js/animations/scale-subtle.css";

View File

@ -1,7 +1,6 @@
import { atom, PrimitiveAtom, Atom, WritableAtom, useAtom } from "jotai";
import { Dispatch, SetStateAction } from "react";
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-arguments
type AtomPair<T> = [Atom<T>, WritableAtom<null, [newText: T], void>];
export const atomPairing = <T>(anAtom: PrimitiveAtom<T>): AtomPair<T> => {

View File

@ -58,7 +58,6 @@ export const prettyInlineTitle = (
return result;
};
/* eslint-disable id-denylist */
export const prettyItemSubType = (
metadata:
| {

View File

@ -4,7 +4,6 @@ export const isUntangibleGroupItem = (
metadata:
| {
__typename: string;
// eslint-disable-next-line id-denylist
subtype?: { data?: { attributes?: { slug: string } | null } | null } | null;
}
| null

View File

@ -2,28 +2,30 @@ type LoggerMode = "both" | "client" | "server";
const isServer = typeof window === "undefined";
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export const getLogger = (prefix: string, mode: LoggerMode = "client") => {
type Logger = {
error: (message?: unknown, ...optionalParams: unknown[]) => void;
warn: (message?: unknown, ...optionalParams: unknown[]) => void;
log: (message?: unknown, ...optionalParams: unknown[]) => void;
info: (message?: unknown, ...optionalParams: unknown[]) => void;
debug: (message?: unknown, ...optionalParams: unknown[]) => void;
};
export const getLogger = (prefix: string, mode: LoggerMode = "client"): Logger => {
if ((mode === "client" && isServer) || (mode === "server" && !isServer)) {
return {
error: () => null,
warn: () => null,
log: () => null,
info: () => null,
debug: () => null,
error: () => undefined,
warn: () => undefined,
log: () => undefined,
info: () => undefined,
debug: () => undefined,
};
}
return {
error: (message?: unknown, ...optionalParams: unknown[]) =>
console.error(prefix, message, ...optionalParams),
warn: (message?: unknown, ...optionalParams: unknown[]) =>
console.warn(prefix, message, ...optionalParams),
log: (message?: unknown, ...optionalParams: unknown[]) =>
console.log(prefix, message, ...optionalParams),
info: (message?: unknown, ...optionalParams: unknown[]) =>
console.info(prefix, message, ...optionalParams),
debug: (message?: unknown, ...optionalParams: unknown[]) =>
console.debug(prefix, message, ...optionalParams),
error: (message, ...optionalParams) => console.error(prefix, message, ...optionalParams),
warn: (message, ...optionalParams) => console.warn(prefix, message, ...optionalParams),
log: (message, ...optionalParams) => console.log(prefix, message, ...optionalParams),
info: (message, ...optionalParams) => console.info(prefix, message, ...optionalParams),
debug: (message, ...optionalParams) => console.debug(prefix, message, ...optionalParams),
};
};

View File

@ -1,7 +1,13 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// eslint-disable-next-line import/named
import { MatchesPosition, MeiliSearch, SearchParams, SearchResponse } from "meilisearch";
import { isDefined } from "./asserts";
import { MeiliSearch } from "meilisearch";
import type {
SearchParams,
MatchesPosition,
SearchResponse,
MultiSearchQuery,
MultiSearchResponse,
MultiSearchResult,
} from "meilisearch";
import { filterDefined, isDefined } from "./asserts";
import { MeiliDocumentsType } from "shared/meilisearch-graphql-typings/meiliTypes";
const meili = new MeiliSearch({
@ -12,20 +18,61 @@ const meili = new MeiliSearch({
interface CustomSearchParams
extends Omit<
SearchParams,
"cropMarker" | "highlightPostTag" | "highlightPreTag" | "q" | "showMatchesPosition"
| "cropLength"
| "cropMarker"
| "cropMarker"
| "highlightPostTag"
| "highlightPreTag"
| "q"
| "showMatchesPosition"
> {}
type CustomHit<T = Record<string, any>> = T & {
type CustomHit<T = Record<string, unknown>> = T & {
_formatted: Partial<T>;
_matchesPosition: MatchesPosition<T>;
};
type CustomHits<T = Record<string, any>> = CustomHit<T>[];
type CustomHits<T = Record<string, unknown>> = CustomHit<T>[];
export interface CustomSearchResponse<T> extends Omit<SearchResponse<T>, "hits"> {
hits: CustomHits<T>;
}
export const meiliMultiSearch = async (queries: MultiSearchQuery[]): Promise<MultiSearchResponse> =>
await meili.multiSearch({
queries: queries.map((query) => ({
attributesToHighlight: ["*"],
...query,
highlightPreTag: "<mark>",
highlightPostTag: "</mark>",
showMatchesPosition: true,
cropLength: 20,
cropMarker: "...",
})),
});
export const filterHitsWithHighlight = <T extends MeiliDocumentsType["documents"]>(
searchResult: CustomSearchResponse<T> | MultiSearchResult<Record<string, unknown>>,
keyToFilter?: keyof T
): CustomSearchResponse<T> => {
const result = searchResult as unknown as CustomSearchResponse<T>;
if (isDefined(keyToFilter)) {
result.hits = result.hits.map((item) => {
if (
Object.keys(item._matchesPosition).some((match) => match.startsWith(keyToFilter as string))
) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
item._formatted[keyToFilter] = filterDefined(item._formatted[keyToFilter]).filter(
(translation) => JSON.stringify(translation).includes("</mark>")
);
}
return item;
});
}
return result;
};
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export const meiliSearch = async <I extends MeiliDocumentsType["index"]>(
indexName: I,

View File

@ -12,16 +12,16 @@ import { TextInput } from "components/Inputs/TextInput";
import { WithLabel } from "components/Inputs/WithLabel";
import { Button } from "components/Inputs/Button";
import { useDeviceSupportsHover } from "hooks/useMediaQuery";
import {
filterDefined,
filterHasAttributes,
isDefined,
isDefinedAndNotEmpty,
} from "helpers/asserts";
import { filterHasAttributes, isDefined, isDefinedAndNotEmpty } from "helpers/asserts";
import { getOpenGraph } from "helpers/openGraph";
import { HorizontalLine } from "components/HorizontalLine";
import { sendAnalytics } from "helpers/analytics";
import { containsHighlight, CustomSearchResponse, meiliSearch } from "helpers/search";
import {
containsHighlight,
CustomSearchResponse,
filterHitsWithHighlight,
meiliSearch,
} from "helpers/search";
import { MeiliContent, MeiliIndices } from "shared/meilisearch-graphql-typings/meiliTypes";
import { useTypedRouter } from "hooks/useTypedRouter";
import { TranslatedPreviewCard } from "components/PreviewCard";
@ -97,15 +97,7 @@ const Contents = (props: Props): JSX.Element => {
page,
sort: isDefined(currentSortingMethod) ? [currentSortingMethod.meiliAttribute] : undefined,
});
searchResult.hits = searchResult.hits.map((item) => {
if (Object.keys(item._matchesPosition).some((match) => match.startsWith("translations"))) {
item._formatted.translations = filterDefined(item._formatted.translations).filter(
(translation) => containsHighlight(JSON.stringify(translation))
);
}
return item;
});
setContents(searchResult);
setContents(filterHitsWithHighlight<MeiliContent>(searchResult, "translations"));
};
fetchPosts();
}, [query, page, sortingMethod, sortingMethods]);

View File

@ -1,4 +1,3 @@
/* eslint-disable id-denylist */
import { GetStaticProps } from "next";
import { ReactNode, useState } from "react";
import Slider from "rc-slider";

View File

@ -14,17 +14,16 @@ import { TextInput } from "components/Inputs/TextInput";
import { Button } from "components/Inputs/Button";
import { useDeviceSupportsHover } from "hooks/useMediaQuery";
import { ButtonGroup } from "components/Inputs/ButtonGroup";
import {
filterDefined,
filterHasAttributes,
isDefined,
isDefinedAndNotEmpty,
isUndefined,
} from "helpers/asserts";
import { filterHasAttributes, isDefined, isDefinedAndNotEmpty, isUndefined } from "helpers/asserts";
import { getOpenGraph } from "helpers/openGraph";
import { HorizontalLine } from "components/HorizontalLine";
import { sendAnalytics } from "helpers/analytics";
import { containsHighlight, CustomSearchResponse, meiliSearch } from "helpers/search";
import {
containsHighlight,
CustomSearchResponse,
filterHitsWithHighlight,
meiliSearch,
} from "helpers/search";
import { MeiliIndices, MeiliLibraryItem } from "shared/meilisearch-graphql-typings/meiliTypes";
import { useTypedRouter } from "hooks/useTypedRouter";
import { TranslatedPreviewCard } from "components/PreviewCard";
@ -178,15 +177,7 @@ const Library = (props: Props): JSX.Element => {
sort: isDefined(currentSortingMethod) ? [currentSortingMethod.meiliAttribute] : undefined,
filter,
});
searchResult.hits = searchResult.hits.map((item) => {
if (Object.keys(item._matchesPosition).some((match) => match.startsWith("descriptions"))) {
item._formatted.descriptions = filterDefined(item._formatted.descriptions).filter(
(description) => containsHighlight(JSON.stringify(description))
);
}
return item;
});
setLibraryItems(searchResult);
setLibraryItems(filterHitsWithHighlight<MeiliLibraryItem>(searchResult, "descriptions"));
};
fetchLibraryItems();
}, [

View File

@ -11,12 +11,7 @@ import { WithLabel } from "components/Inputs/WithLabel";
import { TextInput } from "components/Inputs/TextInput";
import { Button } from "components/Inputs/Button";
import { useDeviceSupportsHover } from "hooks/useMediaQuery";
import {
filterDefined,
filterHasAttributes,
isDefined,
isDefinedAndNotEmpty,
} from "helpers/asserts";
import { filterHasAttributes, isDefined, isDefinedAndNotEmpty } from "helpers/asserts";
import { getOpenGraph } from "helpers/openGraph";
import { TranslatedPreviewCard } from "components/PreviewCard";
import { HorizontalLine } from "components/HorizontalLine";
@ -24,7 +19,12 @@ import { sendAnalytics } from "helpers/analytics";
import { Terminal } from "components/Cli/Terminal";
import { atoms } from "contexts/atoms";
import { useAtomGetter } from "helpers/atoms";
import { containsHighlight, CustomSearchResponse, meiliSearch } from "helpers/search";
import {
containsHighlight,
CustomSearchResponse,
filterHitsWithHighlight,
meiliSearch,
} from "helpers/search";
import { MeiliIndices, MeiliPost } from "shared/meilisearch-graphql-typings/meiliTypes";
import { useTypedRouter } from "hooks/useTypedRouter";
import { prettySlug } from "helpers/formatters";
@ -84,15 +84,7 @@ const News = ({ ...otherProps }: Props): JSX.Element => {
sort: ["sortable_date:desc"],
filter: ["hidden = false"],
});
searchResult.hits = searchResult.hits.map((item) => {
if (Object.keys(item._matchesPosition).some((match) => match.startsWith("translations"))) {
item._formatted.translations = filterDefined(item._formatted.translations).filter(
(translation) => JSON.stringify(translation).includes("</mark>")
);
}
return item;
});
setPosts(searchResult);
setPosts(filterHitsWithHighlight<MeiliPost>(searchResult, "translations"));
};
fetchPosts();
}, [query, page]);

View File

@ -10,16 +10,16 @@ import { PanelHeader } from "components/PanelComponents/PanelHeader";
import { TextInput } from "components/Inputs/TextInput";
import { useTypedRouter } from "hooks/useTypedRouter";
import { useFormat } from "hooks/useFormat";
import {
filterDefined,
filterHasAttributes,
isDefined,
isDefinedAndNotEmpty,
} from "helpers/asserts";
import { filterHasAttributes, isDefined, isDefinedAndNotEmpty } from "helpers/asserts";
import { sendAnalytics } from "helpers/analytics";
import { Button } from "components/Inputs/Button";
import { HorizontalLine } from "components/HorizontalLine";
import { containsHighlight, CustomSearchResponse, meiliSearch } from "helpers/search";
import {
containsHighlight,
CustomSearchResponse,
filterHitsWithHighlight,
meiliSearch,
} from "helpers/search";
import { MeiliIndices, MeiliWeapon } from "shared/meilisearch-graphql-typings/meiliTypes";
import { ContentPanel, ContentPanelWidthSizes } from "components/Containers/ContentPanel";
import { Paginator } from "components/Containers/Paginator";
@ -79,17 +79,7 @@ const Weapons = (props: Props): JSX.Element => {
attributesToCrop: ["translations.description"],
sort: ["slug:asc"],
});
searchResult.hits = searchResult.hits.map((item) => {
if (Object.keys(item._matchesPosition).some((match) => match.startsWith("translations"))) {
item._formatted.translations = filterDefined(item._formatted.translations).filter(
(translation) => JSON.stringify(translation).includes("</mark>")
);
}
return item;
});
setWeapons(searchResult);
setWeapons(filterHitsWithHighlight<MeiliWeapon>(searchResult, "translations"));
};
fetchPosts();
}, [query, page]);