lightnet 3.10.0 → 3.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/__e2e__/admin.spec.ts +113 -0
- package/__e2e__/fixtures/basics/astro.config.mjs +6 -0
- package/__e2e__/fixtures/basics/node_modules/.bin/astro +2 -2
- package/__e2e__/fixtures/basics/package.json +2 -2
- package/__e2e__/fixtures/basics/src/content/media/skate-sounds--en.json +15 -0
- package/__e2e__/fixtures/basics/src/content/media-types/audio.json +7 -0
- package/__e2e__/fixtures/basics/src/translations/de.yml +1 -0
- package/__e2e__/fixtures/basics/src/translations/en.yml +1 -0
- package/__e2e__/homepage.spec.ts +21 -0
- package/package.json +13 -6
- package/src/admin/api/fs/writeText.ts +50 -0
- package/src/admin/components/form/FieldErrors.tsx +22 -0
- package/src/admin/components/form/SubmitButton.tsx +79 -0
- package/src/admin/components/form/TextField.tsx +24 -0
- package/src/admin/components/form/form-context.ts +4 -0
- package/src/admin/components/form/index.ts +16 -0
- package/src/admin/i18n/translations/en.yml +11 -0
- package/src/admin/i18n/translations.ts +5 -0
- package/src/admin/pages/AdminRoute.astro +16 -0
- package/src/admin/pages/media/EditForm.tsx +73 -0
- package/src/admin/pages/media/EditRoute.astro +42 -0
- package/src/admin/pages/media/file-system.ts +37 -0
- package/src/admin/pages/media/media-item-store.ts +11 -0
- package/src/admin/types/media-item.ts +10 -0
- package/src/api/media/[mediaId].ts +16 -0
- package/src/{pages/api → api}/versions.ts +1 -1
- package/src/astro-integration/config.ts +15 -0
- package/src/astro-integration/integration.ts +44 -6
- package/src/components/CategoriesSection.astro +1 -1
- package/src/components/MediaGallerySection.astro +1 -1
- package/src/components/Toast.tsx +55 -0
- package/src/components/showToast.ts +61 -0
- package/src/content/astro-image.ts +1 -14
- package/src/content/content-schema.ts +10 -3
- package/src/content/get-media-items.ts +46 -1
- package/src/i18n/locals.d.ts +35 -28
- package/src/i18n/locals.ts +2 -1
- package/src/i18n/react/i18n-context.ts +32 -0
- package/src/i18n/react/prepare-i18n-config.ts +31 -0
- package/src/i18n/react/useI18n.ts +15 -0
- package/src/i18n/translate.ts +15 -3
- package/src/i18n/translations.ts +20 -7
- package/src/layouts/Page.astro +1 -1
- package/src/pages/details-page/components/MainDetailsSection.astro +5 -1
- package/src/pages/details-page/components/VideoDetailsSection.astro +5 -1
- package/src/pages/details-page/components/main-details/EditButton.astro +30 -0
- package/src/pages/details-page/components/main-details/ShareButton.astro +9 -13
- package/src/pages/{api → search-page/api}/search.ts +3 -3
- package/src/pages/search-page/components/LoadingSkeleton.tsx +3 -1
- package/src/pages/search-page/components/SearchFilter.astro +12 -3
- package/src/pages/search-page/components/SearchFilter.tsx +4 -7
- package/src/pages/search-page/components/SearchList.astro +7 -6
- package/src/pages/search-page/components/SearchList.tsx +12 -15
- package/src/pages/search-page/components/SearchListItem.tsx +3 -5
- package/src/pages/search-page/hooks/use-search.ts +3 -3
- package/tailwind.config.ts +1 -0
- package/src/pages/search-page/utils/search-filter-translations.ts +0 -20
- package/src/pages/search-page/utils/search-translations.ts +0 -11
- /package/src/pages/{api → search-page/api}/search-response.ts +0 -0
package/src/i18n/translations.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import YAML from "yaml"
|
|
2
2
|
|
|
3
|
+
import {
|
|
4
|
+
type AdminTranslationKey,
|
|
5
|
+
builtInAdminTranslations,
|
|
6
|
+
} from "../admin/i18n/translations"
|
|
7
|
+
|
|
3
8
|
const builtInTranslations = {
|
|
4
9
|
ar: () => import("./translations/ar.yml?raw"),
|
|
5
10
|
bn: () => import("./translations/bn.yml?raw"),
|
|
@@ -32,23 +37,30 @@ const userTranslations = Object.fromEntries(
|
|
|
32
37
|
)
|
|
33
38
|
|
|
34
39
|
export const loadTranslations = async (bcp47: string) => ({
|
|
35
|
-
...(await loadBuiltInTranslations(bcp47)),
|
|
40
|
+
...(await loadBuiltInTranslations(builtInTranslations, bcp47)),
|
|
41
|
+
...(await loadBuiltInTranslations(builtInAdminTranslations, bcp47)),
|
|
36
42
|
...(await loadUserTranslations(bcp47)),
|
|
37
43
|
})
|
|
38
44
|
|
|
39
|
-
function
|
|
40
|
-
|
|
45
|
+
function hasTranslations(
|
|
46
|
+
translationMap: Record<string, unknown>,
|
|
47
|
+
bcp47: string,
|
|
48
|
+
): bcp47 is BuiltInLanguage {
|
|
49
|
+
return Object.hasOwn(translationMap, bcp47)
|
|
41
50
|
}
|
|
42
51
|
|
|
43
|
-
|
|
44
|
-
|
|
52
|
+
const loadBuiltInTranslations = async (
|
|
53
|
+
translationMap: Record<string, () => Promise<typeof import("*?raw")>>,
|
|
54
|
+
bcp47: string,
|
|
55
|
+
) => {
|
|
56
|
+
if (!hasTranslations(translationMap, bcp47)) {
|
|
45
57
|
return {}
|
|
46
58
|
}
|
|
47
|
-
const yml = (await
|
|
59
|
+
const yml = (await translationMap[bcp47]()).default
|
|
48
60
|
return YAML.parse(yml)
|
|
49
61
|
}
|
|
50
62
|
|
|
51
|
-
|
|
63
|
+
const loadUserTranslations = async (bcp47: string) => {
|
|
52
64
|
if (!userTranslations[bcp47]) {
|
|
53
65
|
return {}
|
|
54
66
|
}
|
|
@@ -82,3 +94,4 @@ export type LightNetTranslationKey =
|
|
|
82
94
|
| "ln.search.title"
|
|
83
95
|
| "ln.share.url-copied-to-clipboard"
|
|
84
96
|
| "ln.footer.powered-by-lightnet"
|
|
97
|
+
| AdminTranslationKey
|
package/src/layouts/Page.astro
CHANGED
|
@@ -31,7 +31,7 @@ const language = resolveLanguage(currentLocale)
|
|
|
31
31
|
<title>{title ? `${title} | ${configTitle}` : configTitle}</title>
|
|
32
32
|
{description && <meta name="description" content={description} />}
|
|
33
33
|
{config.manifest && <link rel="manifest" href={config.manifest} />}
|
|
34
|
-
<link rel="prefetch" href="/api/search.json" />
|
|
34
|
+
<link rel="prefetch" href="/api/internal/search.json" />
|
|
35
35
|
<Favicon />
|
|
36
36
|
<ViewTransition />
|
|
37
37
|
</head>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
import Authors from "./main-details/Authors.astro"
|
|
3
3
|
import CoverImage from "./main-details/CoverImage.astro"
|
|
4
|
+
import EditButton from "./main-details/EditButton.astro"
|
|
4
5
|
import ShareButton from "./main-details/ShareButton.astro"
|
|
5
6
|
import Title from "./main-details/Title.astro"
|
|
6
7
|
|
|
@@ -19,7 +20,10 @@ const { mediaId, imageSize } = Astro.props
|
|
|
19
20
|
<div class="flex w-full grow flex-col items-center sm:items-start">
|
|
20
21
|
<Title className="text-center sm:text-start" mediaId={mediaId} />
|
|
21
22
|
<Authors className="mt-2" mediaId={mediaId} />
|
|
22
|
-
<
|
|
23
|
+
<div class="mt-4 flex gap-6">
|
|
24
|
+
<ShareButton />
|
|
25
|
+
<EditButton mediaId={mediaId} />
|
|
26
|
+
</div>
|
|
23
27
|
<slot />
|
|
24
28
|
</div>
|
|
25
29
|
</div>
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import VideoPlayer from "../../../components/VideoPlayer.astro"
|
|
3
3
|
import { getMediaItem } from "../../../content/get-media-items"
|
|
4
4
|
import Authors from "./main-details/Authors.astro"
|
|
5
|
+
import EditButton from "./main-details/EditButton.astro"
|
|
5
6
|
import ShareButton from "./main-details/ShareButton.astro"
|
|
6
7
|
import Title from "./main-details/Title.astro"
|
|
7
8
|
|
|
@@ -26,5 +27,8 @@ const item = await getMediaItem(mediaId)
|
|
|
26
27
|
>
|
|
27
28
|
<Title mediaId={mediaId} />
|
|
28
29
|
<Authors mediaId={mediaId} />
|
|
29
|
-
<
|
|
30
|
+
<div class="mt-4 flex gap-6">
|
|
31
|
+
<ShareButton />
|
|
32
|
+
<EditButton mediaId={mediaId} />
|
|
33
|
+
</div>
|
|
30
34
|
</div>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
import config from "virtual:lightnet/config"
|
|
3
|
+
|
|
4
|
+
import Icon from "../../../../components/Icon"
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
mediaId: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { mediaId } = Astro.props
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
<a
|
|
14
|
+
class="hidden cursor-pointer items-center gap-2 font-bold text-gray-700 underline"
|
|
15
|
+
id="edit-btn"
|
|
16
|
+
data-admin-enabled={config.experimental?.admin?.enabled}
|
|
17
|
+
href={`/${Astro.currentLocale}/admin/media/${mediaId}`}
|
|
18
|
+
><Icon className="mdi--square-edit-outline" ariaLabel="" />
|
|
19
|
+
{Astro.locals.i18n.t("ln.admin.edit")}</a
|
|
20
|
+
>
|
|
21
|
+
<script>
|
|
22
|
+
const btn: HTMLAnchorElement | null = document.querySelector("#edit-btn")
|
|
23
|
+
const showEditButton =
|
|
24
|
+
btn?.dataset.adminEnabled === "true" &&
|
|
25
|
+
(import.meta.env.DEV || localStorage.getItem("ln-admin-enabled") === "true")
|
|
26
|
+
if (showEditButton) {
|
|
27
|
+
btn?.classList.remove("hidden")
|
|
28
|
+
btn?.classList.add("flex")
|
|
29
|
+
}
|
|
30
|
+
</script>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
import Icon from "../../../../components/Icon"
|
|
3
|
+
import Toast from "../../../../components/Toast"
|
|
3
4
|
|
|
4
5
|
interface Props {
|
|
5
6
|
className?: string
|
|
@@ -13,16 +14,15 @@ interface Props {
|
|
|
13
14
|
><Icon className="mdi--share" ariaLabel="" />
|
|
14
15
|
{Astro.locals.i18n.t("ln.details.share")}</button
|
|
15
16
|
>
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
>
|
|
20
|
-
<div class="dy-alert dy-alert-success">
|
|
21
|
-
<span>{Astro.locals.i18n.t("ln.share.url-copied-to-clipboard")}</span>
|
|
22
|
-
</div>
|
|
23
|
-
</div>
|
|
17
|
+
<Toast id="share-success" variant="success">
|
|
18
|
+
{Astro.locals.i18n.t("ln.share.url-copied-to-clipboard")}
|
|
19
|
+
</Toast>
|
|
24
20
|
<script>
|
|
21
|
+
import { showToastById } from "../../../../components/showToast"
|
|
22
|
+
|
|
25
23
|
const btn = document.querySelector("#share-btn")
|
|
24
|
+
const toastId = "share-success"
|
|
25
|
+
|
|
26
26
|
btn?.addEventListener("click", () => {
|
|
27
27
|
if (navigator.share) {
|
|
28
28
|
navigator
|
|
@@ -34,11 +34,7 @@ interface Props {
|
|
|
34
34
|
navigator.clipboard
|
|
35
35
|
.writeText(window.location.href)
|
|
36
36
|
.then(() => {
|
|
37
|
-
|
|
38
|
-
toast.style.opacity = "100%"
|
|
39
|
-
setTimeout(() => {
|
|
40
|
-
toast.style.opacity = "0%"
|
|
41
|
-
}, 3000)
|
|
37
|
+
showToastById(toastId)
|
|
42
38
|
})
|
|
43
39
|
.catch((error) => console.log("Error copying URL to clipboard:", error))
|
|
44
40
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { APIRoute } from "astro"
|
|
2
2
|
import { getImage } from "astro:assets"
|
|
3
3
|
|
|
4
|
-
import type { MediaItemEntry } from "
|
|
5
|
-
import { getMediaItems } from "
|
|
6
|
-
import { markdownToText } from "
|
|
4
|
+
import type { MediaItemEntry } from "../../../content/content-schema"
|
|
5
|
+
import { getMediaItems } from "../../../content/get-media-items"
|
|
6
|
+
import { markdownToText } from "../../../utils/markdown"
|
|
7
7
|
import type { SearchItem } from "./search-response"
|
|
8
8
|
|
|
9
9
|
export const GET: APIRoute = async () => {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import Icon from "../../../components/Icon"
|
|
2
|
+
import { useI18n } from "../../../i18n/react/useI18n"
|
|
2
3
|
|
|
3
|
-
export default function LoadingSkeleton(
|
|
4
|
+
export default function LoadingSkeleton() {
|
|
5
|
+
const { direction } = useI18n()
|
|
4
6
|
return (
|
|
5
7
|
<div className="flex h-52 animate-pulse items-center overflow-hidden py-2 sm:h-64">
|
|
6
8
|
<div className="h-36 w-36 shrink-0 rounded-md bg-gray-200"></div>
|
|
@@ -4,7 +4,7 @@ import config from "virtual:lightnet/config"
|
|
|
4
4
|
import { getUsedCategories } from "../../../content/get-categories"
|
|
5
5
|
import { contentLanguages } from "../../../content/get-languages"
|
|
6
6
|
import { getMediaTypes } from "../../../content/get-media-types"
|
|
7
|
-
import {
|
|
7
|
+
import { prepareI18nConfig } from "../../../i18n/react/prepare-i18n-config"
|
|
8
8
|
import SearchFilterReact from "./SearchFilter.tsx"
|
|
9
9
|
|
|
10
10
|
const { t, currentLocale } = Astro.locals.i18n
|
|
@@ -49,7 +49,16 @@ if (
|
|
|
49
49
|
) {
|
|
50
50
|
initialLanguageFilter = currentLocale
|
|
51
51
|
}
|
|
52
|
-
const
|
|
52
|
+
const i18nConfig = prepareI18nConfig(Astro.locals.i18n, [
|
|
53
|
+
"ln.search.title",
|
|
54
|
+
"ln.language",
|
|
55
|
+
"ln.search.placeholder",
|
|
56
|
+
"ln.search.all-languages",
|
|
57
|
+
"ln.type",
|
|
58
|
+
"ln.search.all-types",
|
|
59
|
+
"ln.category",
|
|
60
|
+
"ln.search.all-categories",
|
|
61
|
+
])
|
|
53
62
|
---
|
|
54
63
|
|
|
55
64
|
<SearchFilterReact
|
|
@@ -57,7 +66,7 @@ const translations = provideTranslations(t)
|
|
|
57
66
|
languages={languages}
|
|
58
67
|
mediaTypes={mediaTypes}
|
|
59
68
|
categories={categories}
|
|
60
|
-
|
|
69
|
+
i18nConfig={i18nConfig}
|
|
61
70
|
languageFilterEnabled={languageFilterEnabled}
|
|
62
71
|
typesFilterEnabled={typesFilterEnabled}
|
|
63
72
|
categoriesFilterEnabled={categoriesFilterEnabled}
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import { useRef } from "react"
|
|
2
2
|
|
|
3
3
|
import Icon from "../../../components/Icon"
|
|
4
|
+
import { createI18n, type I18nConfig } from "../../../i18n/react/i18n-context"
|
|
4
5
|
import { useDebounce } from "../hooks/use-debounce"
|
|
5
6
|
import { useSearchQueryParam } from "../hooks/use-search-query-param"
|
|
6
|
-
import type {
|
|
7
|
-
TranslationKey,
|
|
8
|
-
Translations,
|
|
9
|
-
} from "../utils/search-filter-translations"
|
|
10
7
|
import { CATEGORY, LANGUAGE, SEARCH, TYPE } from "../utils/search-query"
|
|
11
8
|
import Select from "./Select"
|
|
12
9
|
|
|
@@ -16,7 +13,7 @@ interface Props {
|
|
|
16
13
|
languages: FilterValue[]
|
|
17
14
|
categories: FilterValue[]
|
|
18
15
|
mediaTypes: FilterValue[]
|
|
19
|
-
|
|
16
|
+
i18nConfig: I18nConfig
|
|
20
17
|
languageFilterEnabled: boolean
|
|
21
18
|
typesFilterEnabled: boolean
|
|
22
19
|
categoriesFilterEnabled: boolean
|
|
@@ -26,7 +23,7 @@ interface Props {
|
|
|
26
23
|
export default function SearchFilter({
|
|
27
24
|
categories,
|
|
28
25
|
mediaTypes,
|
|
29
|
-
|
|
26
|
+
i18nConfig,
|
|
30
27
|
languages,
|
|
31
28
|
languageFilterEnabled,
|
|
32
29
|
typesFilterEnabled,
|
|
@@ -40,7 +37,7 @@ export default function SearchFilter({
|
|
|
40
37
|
|
|
41
38
|
const searchInput = useRef<HTMLInputElement | null>(null)
|
|
42
39
|
|
|
43
|
-
const t = (
|
|
40
|
+
const { t } = createI18n(i18nConfig)
|
|
44
41
|
|
|
45
42
|
const debouncedSetSearch = useDebounce((value: string) => {
|
|
46
43
|
setSearch(value)
|
|
@@ -4,10 +4,10 @@ import { getCollection } from "astro:content"
|
|
|
4
4
|
import { getUsedCategories } from "../../../content/get-categories"
|
|
5
5
|
import { contentLanguages } from "../../../content/get-languages"
|
|
6
6
|
import { getMediaTypes } from "../../../content/get-media-types"
|
|
7
|
-
import {
|
|
7
|
+
import { prepareI18nConfig } from "../../../i18n/react/prepare-i18n-config"
|
|
8
8
|
import SearchListReact from "./SearchList.tsx"
|
|
9
9
|
|
|
10
|
-
const { t, currentLocale
|
|
10
|
+
const { t, currentLocale } = Astro.locals.i18n
|
|
11
11
|
|
|
12
12
|
const categories: Record<string, string> = {}
|
|
13
13
|
for (const { id, name } of await getUsedCategories(currentLocale, t)) {
|
|
@@ -34,15 +34,16 @@ const languages = Object.fromEntries(
|
|
|
34
34
|
|
|
35
35
|
const mediaItemsTotal = (await getCollection("media")).length
|
|
36
36
|
|
|
37
|
-
const translations = provideTranslations(t)
|
|
38
37
|
const showLanguage = contentLanguages.length > 1
|
|
38
|
+
|
|
39
|
+
const i18nConfig = prepareI18nConfig(Astro.locals.i18n, [
|
|
40
|
+
"ln.search.no-results",
|
|
41
|
+
])
|
|
39
42
|
---
|
|
40
43
|
|
|
41
44
|
<SearchListReact
|
|
42
45
|
client:load
|
|
43
|
-
|
|
44
|
-
direction={direction}
|
|
45
|
-
translations={translations}
|
|
46
|
+
i18nConfig={i18nConfig}
|
|
46
47
|
categories={categories}
|
|
47
48
|
mediaTypes={mediaTypes}
|
|
48
49
|
languages={languages}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { useWindowVirtualizer } from "@tanstack/react-virtual"
|
|
2
2
|
import { useEffect, useRef, useState } from "react"
|
|
3
3
|
|
|
4
|
+
import {
|
|
5
|
+
createI18n,
|
|
6
|
+
type I18nConfig,
|
|
7
|
+
I18nContext,
|
|
8
|
+
} from "../../../i18n/react/i18n-context"
|
|
4
9
|
import { useSearch } from "../hooks/use-search"
|
|
5
|
-
import type { TranslationKey, Translations } from "../utils/search-translations"
|
|
6
10
|
import LoadingSkeleton from "./LoadingSkeleton"
|
|
7
11
|
import SearchListItem, {
|
|
8
12
|
type MediaType,
|
|
@@ -10,9 +14,7 @@ import SearchListItem, {
|
|
|
10
14
|
} from "./SearchListItem"
|
|
11
15
|
|
|
12
16
|
interface Props {
|
|
13
|
-
|
|
14
|
-
translations: Translations
|
|
15
|
-
direction: "rtl" | "ltr"
|
|
17
|
+
i18nConfig: I18nConfig
|
|
16
18
|
categories: Record<string, string>
|
|
17
19
|
languages: Record<string, TranslatedLanguage>
|
|
18
20
|
showLanguage: boolean
|
|
@@ -21,11 +23,9 @@ interface Props {
|
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
export default function SearchList({
|
|
24
|
-
currentLocale,
|
|
25
26
|
categories,
|
|
26
|
-
|
|
27
|
+
i18nConfig,
|
|
27
28
|
languages,
|
|
28
|
-
direction,
|
|
29
29
|
showLanguage,
|
|
30
30
|
mediaTypes,
|
|
31
31
|
mediaItemsTotal,
|
|
@@ -63,11 +63,10 @@ export default function SearchList({
|
|
|
63
63
|
observer.disconnect()
|
|
64
64
|
}
|
|
65
65
|
}, [])
|
|
66
|
-
|
|
67
|
-
const t = (key: TranslationKey) => translations[key]
|
|
66
|
+
const i18n = createI18n(i18nConfig)
|
|
68
67
|
|
|
69
68
|
return (
|
|
70
|
-
|
|
69
|
+
<I18nContext.Provider value={i18n}>
|
|
71
70
|
<div ref={listRef} className="px-4 md:px-8">
|
|
72
71
|
<ol
|
|
73
72
|
className="relative w-full divide-y divide-gray-200"
|
|
@@ -89,13 +88,11 @@ export default function SearchList({
|
|
|
89
88
|
}}
|
|
90
89
|
>
|
|
91
90
|
{isLoading ? (
|
|
92
|
-
<LoadingSkeleton
|
|
91
|
+
<LoadingSkeleton />
|
|
93
92
|
) : (
|
|
94
93
|
<SearchListItem
|
|
95
94
|
item={item}
|
|
96
|
-
direction={direction}
|
|
97
95
|
showLanguage={showLanguage}
|
|
98
|
-
currentLocale={currentLocale}
|
|
99
96
|
categories={categories}
|
|
100
97
|
languages={languages}
|
|
101
98
|
mediaTypes={mediaTypes}
|
|
@@ -108,9 +105,9 @@ export default function SearchList({
|
|
|
108
105
|
</div>
|
|
109
106
|
{!results.length && !isLoading && (
|
|
110
107
|
<div className="mt-24 text-center font-bold text-gray-500">
|
|
111
|
-
{t("ln.search.no-results")}
|
|
108
|
+
{i18n.t("ln.search.no-results")}
|
|
112
109
|
</div>
|
|
113
110
|
)}
|
|
114
|
-
|
|
111
|
+
</I18nContext.Provider>
|
|
115
112
|
)
|
|
116
113
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import CoverImageDecorator from "../../../components/CoverImageDecorator"
|
|
2
2
|
import Icon from "../../../components/Icon"
|
|
3
|
+
import { useI18n } from "../../../i18n/react/useI18n"
|
|
3
4
|
import { detailsPagePath } from "../../../utils/paths"
|
|
4
|
-
import type { SearchItem } from "
|
|
5
|
+
import type { SearchItem } from "../api/search-response"
|
|
5
6
|
|
|
6
7
|
export type MediaType = {
|
|
7
8
|
name: string
|
|
@@ -16,8 +17,6 @@ export type TranslatedLanguage = {
|
|
|
16
17
|
|
|
17
18
|
interface Props {
|
|
18
19
|
item: SearchItem
|
|
19
|
-
currentLocale: string | undefined
|
|
20
|
-
direction: "rtl" | "ltr"
|
|
21
20
|
categories: Record<string, string>
|
|
22
21
|
languages: Record<string, TranslatedLanguage>
|
|
23
22
|
showLanguage: boolean
|
|
@@ -26,13 +25,12 @@ interface Props {
|
|
|
26
25
|
|
|
27
26
|
export default function SearchListItem({
|
|
28
27
|
item,
|
|
29
|
-
currentLocale,
|
|
30
28
|
categories,
|
|
31
29
|
languages,
|
|
32
|
-
direction,
|
|
33
30
|
showLanguage,
|
|
34
31
|
mediaTypes,
|
|
35
32
|
}: Props) {
|
|
33
|
+
const { currentLocale, direction } = useI18n()
|
|
36
34
|
const coverImageStyle = mediaTypes[item.type].coverImageStyle
|
|
37
35
|
return (
|
|
38
36
|
<a
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Fuse from "fuse.js"
|
|
2
2
|
import { useEffect, useMemo, useRef, useState } from "react"
|
|
3
3
|
|
|
4
|
-
import type { SearchItem, SearchResponse } from "
|
|
4
|
+
import type { SearchItem, SearchResponse } from "../api/search-response"
|
|
5
5
|
import { observeSearchQuery, type SearchQuery } from "../utils/search-query"
|
|
6
6
|
|
|
7
7
|
interface Context {
|
|
@@ -28,10 +28,10 @@ export function useSearch({ categories, mediaTypes, languages }: Context) {
|
|
|
28
28
|
})
|
|
29
29
|
const fetchData = async () => {
|
|
30
30
|
try {
|
|
31
|
-
const response = await fetch("/api/search.json")
|
|
31
|
+
const response = await fetch("/api/internal/search.json")
|
|
32
32
|
if (!response.ok) {
|
|
33
33
|
throw new Error(
|
|
34
|
-
"Was not able to load search results from /api/search.json.",
|
|
34
|
+
"Was not able to load search results from /api/internal/search.json.",
|
|
35
35
|
)
|
|
36
36
|
}
|
|
37
37
|
const { items }: SearchResponse = await response.json()
|
package/tailwind.config.ts
CHANGED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
const translationKeys = [
|
|
2
|
-
"ln.search.title",
|
|
3
|
-
"ln.language",
|
|
4
|
-
"ln.search.placeholder",
|
|
5
|
-
"ln.search.all-languages",
|
|
6
|
-
"ln.type",
|
|
7
|
-
"ln.search.all-types",
|
|
8
|
-
"ln.category",
|
|
9
|
-
"ln.search.all-categories",
|
|
10
|
-
] as const
|
|
11
|
-
|
|
12
|
-
export type TranslationKey = (typeof translationKeys)[number]
|
|
13
|
-
|
|
14
|
-
export type Translations = Record<TranslationKey, string>
|
|
15
|
-
|
|
16
|
-
export const provideTranslations = (translate: (key: string) => string) => {
|
|
17
|
-
return Object.fromEntries(
|
|
18
|
-
translationKeys.map((key) => [key, translate(key)]),
|
|
19
|
-
) as Translations
|
|
20
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
const translationKeys = ["ln.search.no-results"] as const
|
|
2
|
-
|
|
3
|
-
export type TranslationKey = (typeof translationKeys)[number]
|
|
4
|
-
|
|
5
|
-
export type Translations = Record<TranslationKey, string>
|
|
6
|
-
|
|
7
|
-
export const provideTranslations = (translate: (key: string) => string) => {
|
|
8
|
-
return Object.fromEntries(
|
|
9
|
-
translationKeys.map((key) => [key, translate(key)]),
|
|
10
|
-
) as Translations
|
|
11
|
-
}
|
|
File without changes
|