lightnet 3.9.1 → 3.10.1
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 +24 -0
- package/README.md +4 -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/node_modules/.bin/tailwind +2 -2
- package/__e2e__/fixtures/basics/node_modules/.bin/tailwindcss +2 -2
- package/__e2e__/fixtures/basics/node_modules/.bin/tsc +2 -2
- package/__e2e__/fixtures/basics/node_modules/.bin/tsserver +2 -2
- package/__e2e__/fixtures/basics/package.json +9 -9
- 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 +18 -11
- package/src/admin/api/fs/writeText.ts +50 -0
- package/src/admin/components/form/FieldErrors.tsx +19 -0
- package/src/admin/components/form/SubmitButton.tsx +77 -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 +1 -0
- package/src/admin/i18n/translations.ts +5 -0
- package/src/admin/pages/AdminRoute.astro +16 -0
- package/src/admin/pages/media/EditForm.tsx +58 -0
- package/src/admin/pages/media/EditRoute.astro +33 -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 +8 -0
- package/src/api/media/[mediaId].ts +16 -0
- package/src/{pages/api → api}/versions.ts +1 -1
- package/src/astro-integration/config.ts +19 -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/translations/ar.yml +1 -0
- package/src/i18n/translations/bn.yml +1 -0
- package/src/i18n/translations/de.yml +1 -0
- package/src/i18n/translations/en.yml +3 -0
- package/src/i18n/translations/es.yml +1 -0
- package/src/i18n/translations/fi.yml +1 -0
- package/src/i18n/translations/fr.yml +1 -0
- package/src/i18n/translations/hi.yml +1 -0
- package/src/i18n/translations/kk.yml +1 -0
- package/src/i18n/translations/pt.yml +1 -0
- package/src/i18n/translations/ru.yml +1 -0
- package/src/i18n/translations/uk.yml +1 -0
- package/src/i18n/translations/zh.yml +1 -0
- package/src/i18n/translations.ts +21 -7
- package/src/layouts/Page.astro +3 -2
- package/src/layouts/components/Footer.astro +24 -0
- package/src/layouts/components/LightNetLogo.svg +1 -0
- 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/SearchListItem.tsx +1 -1
- package/src/pages/search-page/hooks/use-search.ts +3 -3
- package/tailwind.config.ts +1 -0
- /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
|
}
|
|
@@ -81,3 +93,5 @@ export type LightNetTranslationKey =
|
|
|
81
93
|
| "ln.search.placeholder"
|
|
82
94
|
| "ln.search.title"
|
|
83
95
|
| "ln.share.url-copied-to-clipboard"
|
|
96
|
+
| "ln.footer.powered-by-lightnet"
|
|
97
|
+
| AdminTranslationKey
|
package/src/layouts/Page.astro
CHANGED
|
@@ -5,6 +5,7 @@ import config from "virtual:lightnet/config"
|
|
|
5
5
|
|
|
6
6
|
import { resolveLanguage } from "../i18n/resolve-language"
|
|
7
7
|
import Favicon from "./components/Favicon.astro"
|
|
8
|
+
import Footer from "./components/Footer.astro"
|
|
8
9
|
import Header from "./components/Header.astro"
|
|
9
10
|
import PreloadReact from "./components/PreloadReact"
|
|
10
11
|
import ViewTransition from "./components/ViewTransition.astro"
|
|
@@ -30,7 +31,7 @@ const language = resolveLanguage(currentLocale)
|
|
|
30
31
|
<title>{title ? `${title} | ${configTitle}` : configTitle}</title>
|
|
31
32
|
{description && <meta name="description" content={description} />}
|
|
32
33
|
{config.manifest && <link rel="manifest" href={config.manifest} />}
|
|
33
|
-
<link rel="prefetch" href="/api/search.json" />
|
|
34
|
+
<link rel="prefetch" href="/api/internal/search.json" />
|
|
34
35
|
<Favicon />
|
|
35
36
|
<ViewTransition />
|
|
36
37
|
</head>
|
|
@@ -41,7 +42,7 @@ const language = resolveLanguage(currentLocale)
|
|
|
41
42
|
<main class="grow pb-8 pt-14 sm:py-20">
|
|
42
43
|
<slot />
|
|
43
44
|
</main>
|
|
44
|
-
{CustomFooter
|
|
45
|
+
{CustomFooter ? <CustomFooter /> : <Footer />}
|
|
45
46
|
<PreloadReact client:idle />
|
|
46
47
|
</body>
|
|
47
48
|
</html>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
import config from "virtual:lightnet/config"
|
|
3
|
+
|
|
4
|
+
import LightNetLogo from "./LightNetLogo.svg"
|
|
5
|
+
|
|
6
|
+
if (!config.credits) {
|
|
7
|
+
return
|
|
8
|
+
}
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
<footer class="w-full border-t border-gray-300">
|
|
12
|
+
<div
|
|
13
|
+
class="mx-auto flex w-full max-w-screen-xl justify-end px-4 py-4 md:px-8"
|
|
14
|
+
>
|
|
15
|
+
<a
|
|
16
|
+
class="flex items-center gap-2 py-2 text-sm text-gray-800 hover:underline"
|
|
17
|
+
href="https://lightnet.community"
|
|
18
|
+
target="_blank"
|
|
19
|
+
>
|
|
20
|
+
<img src={LightNetLogo.src} alt="" class="h-5 w-auto" loading="lazy" />
|
|
21
|
+
{Astro.locals.i18n.t("ln.footer.powered-by-lightnet")}
|
|
22
|
+
</a>
|
|
23
|
+
</div>
|
|
24
|
+
</footer>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg viewBox="16 16 141 173" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M88.007 180.642c-21.767-5.846-39.688-19.304-40.574-42.862-.887-23.558 17.751-43.334 45.426-40.84v-7.844c.011-4.03 4.977-5.495 8.021-2.928l29.136 23.729c2.155 1.821 2.15 5.16-.019 7.08l-29.117 24.152c-3.059 2.72-8.031.681-8.02-3.349v-8.005c-2.916-.453-5.137-.579-6.708-.453-25.207 2.016-23.083 44.6 20.454 42.388 18.705-.951 49.148-21.73 50.394-53.985 0-48.62-60.4-64.433-45.205-97.762 1.529-3.354-4.627-4.867-8.501-3.393-15.573 4.29-32.752 13.714-42.526 41.693-1.251 3.581-2.824 12.038-4.168 15.558-1.242 3.533-3.034 11.445-7.996 11.025-6.501-.55-4.236-11.168-2.815-15.74 1.197-3.854-4.14-7.109-9.028-2.073-8.3 8.549-16.598 19.982-19.804 37.823-1.259 7.001-1.327 16.903.101 25.208 6.204 36.183 37.705 59.276 68.356 58.915 7.58-.09 7.787-6.942 2.593-8.337Z" fill="#374151"/></svg>
|
|
@@ -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,7 +1,7 @@
|
|
|
1
1
|
import CoverImageDecorator from "../../../components/CoverImageDecorator"
|
|
2
2
|
import Icon from "../../../components/Icon"
|
|
3
3
|
import { detailsPagePath } from "../../../utils/paths"
|
|
4
|
-
import type { SearchItem } from "
|
|
4
|
+
import type { SearchItem } from "../api/search-response"
|
|
5
5
|
|
|
6
6
|
export type MediaType = {
|
|
7
7
|
name: string
|
|
@@ -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
|
File without changes
|