@mohasinac/appkit 4.9.5 → 4.10.0
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/dist/_internal/server/features/auctions/actions.js +3 -2
- package/dist/_internal/server/features/auctions/service.d.ts +4 -3
- package/dist/_internal/server/features/auctions/service.js +6 -6
- package/dist/_internal/server/features/bundles/data.d.ts +9 -0
- package/dist/_internal/server/features/bundles/data.js +14 -0
- package/dist/_internal/server/features/bundles/index.d.ts +1 -1
- package/dist/_internal/server/features/bundles/index.js +1 -1
- package/dist/_internal/server/features/catalogue/og.d.ts +20 -0
- package/dist/_internal/server/features/catalogue/og.js +27 -0
- package/dist/_internal/server/features/grouped/data.d.ts +12 -0
- package/dist/_internal/server/features/grouped/data.js +26 -1
- package/dist/_internal/server/features/grouped/index.d.ts +1 -1
- package/dist/_internal/server/features/grouped/index.js +1 -1
- package/dist/_internal/server/features/products/data.d.ts +23 -0
- package/dist/_internal/server/features/products/data.js +159 -0
- package/dist/_internal/server/features/products/index.d.ts +1 -1
- package/dist/_internal/server/features/products/index.js +1 -1
- package/dist/_internal/shared/features/auctions/config.d.ts +20 -0
- package/dist/_internal/shared/features/auctions/config.js +31 -1
- package/dist/_internal/shared/listing-types/art/config.d.ts +1 -1
- package/dist/_internal/shared/listing-types/art/config.js +1 -1
- package/dist/_internal/shared/listing-types/classified/config.d.ts +1 -1
- package/dist/_internal/shared/listing-types/classified/config.js +1 -1
- package/dist/_internal/shared/listing-types/digital-code/config.d.ts +1 -1
- package/dist/_internal/shared/listing-types/digital-code/config.js +1 -1
- package/dist/_internal/shared/listing-types/live/config.d.ts +1 -1
- package/dist/_internal/shared/listing-types/live/config.js +1 -1
- package/dist/_internal/shared/listing-types/prize-draw/config.d.ts +1 -1
- package/dist/_internal/shared/listing-types/prize-draw/config.js +1 -1
- package/dist/_internal/shared/listing-types/stickers/config.d.ts +1 -1
- package/dist/_internal/shared/listing-types/stickers/config.js +1 -1
- package/dist/client.d.ts +0 -6
- package/dist/client.js +1 -4
- package/dist/constants/api-endpoints.d.ts +3 -0
- package/dist/constants/api-endpoints.js +1 -0
- package/dist/features/admin/components/AdminSiteSettingsView.js +12 -4
- package/dist/features/admin/schemas/firestore.d.ts +9 -0
- package/dist/features/admin/schemas/firestore.js +6 -0
- package/dist/features/auctions/actions/bid-actions.js +4 -1
- package/dist/features/auctions/components/AuctionBottomActions.d.ts +2 -1
- package/dist/features/auctions/components/AuctionBottomActions.js +14 -1
- package/dist/features/auctions/components/AuctionDetailPageView.js +21 -9
- package/dist/features/auctions/components/LiveMinIncrement.d.ts +18 -0
- package/dist/features/auctions/components/LiveMinIncrement.js +17 -0
- package/dist/features/auctions/components/PlaceBidFormClient.d.ts +5 -2
- package/dist/features/auctions/components/PlaceBidFormClient.js +7 -3
- package/dist/features/catalogue/components/PublicCatalogueItemDetailView.d.ts +11 -0
- package/dist/features/catalogue/components/PublicCatalogueItemDetailView.js +33 -0
- package/dist/features/catalogue/components/PublicCatalogueView.js +2 -1
- package/dist/features/catalogue/index.d.ts +2 -0
- package/dist/features/catalogue/index.js +1 -0
- package/dist/features/categories/components/BundleDetailView.d.ts +3 -1
- package/dist/features/categories/components/BundleDetailView.js +3 -2
- package/dist/features/classified/components/ClassifiedContactSellerPanel.d.ts +12 -0
- package/dist/features/classified/components/ClassifiedContactSellerPanel.js +49 -0
- package/dist/features/classified/components/ClassifiedDetailPageView.d.ts +7 -0
- package/dist/features/classified/components/ClassifiedDetailPageView.js +69 -0
- package/dist/features/contact/components/ContactPageView.js +8 -2
- package/dist/features/digital-codes/components/DigitalCodeDetailPageView.d.ts +19 -0
- package/dist/features/digital-codes/components/DigitalCodeDetailPageView.js +79 -0
- package/dist/features/grouped/components/GroupedListingsCarousel.d.ts +13 -0
- package/dist/features/grouped/components/GroupedListingsCarousel.js +31 -0
- package/dist/features/grouped/repository/grouped-listings.repository.d.ts +8 -0
- package/dist/features/grouped/repository/grouped-listings.repository.js +16 -0
- package/dist/features/layout/BottomActions.js +4 -3
- package/dist/features/layout/BottomActionsContext.d.ts +10 -0
- package/dist/features/layout/BottomActionsContext.js +5 -1
- package/dist/features/layout/hooks/useBottomActions.d.ts +2 -0
- package/dist/features/layout/hooks/useBottomActions.js +4 -1
- package/dist/features/live/components/LiveItemDetailPageView.d.ts +9 -0
- package/dist/features/live/components/LiveItemDetailPageView.js +74 -0
- package/dist/features/media/MediaVideo.js +17 -1
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +9 -1
- package/dist/features/products/components/PrizeDrawDetailPageView.js +11 -1
- package/dist/features/products/components/ProductDetailPageView.js +11 -116
- package/dist/features/products/components/RelatedItemsSection.d.ts +16 -0
- package/dist/features/products/components/RelatedItemsSection.js +17 -0
- package/dist/features/seller/components/SellerProductShell.js +1 -1
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +83 -3
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/next/routing/route-map.d.ts +2 -0
- package/dist/next/routing/route-map.js +7 -0
- package/dist/seed/bids-seed-data.js +48 -3
- package/dist/seed/grouped-listings-seed-data.js +167 -0
- package/dist/seed/payouts-seed-data.js +4 -4
- package/dist/seed/products-art-seed-data.js +188 -5
- package/dist/seed/products-auctions-seed-data.js +215 -6
- package/dist/seed/products-classifieds-seed-data.js +255 -8
- package/dist/seed/products-digital-codes-seed-data.js +258 -19
- package/dist/seed/products-live-items-seed-data.js +143 -10
- package/dist/seed/products-preorders-seed-data.js +213 -8
- package/dist/seed/products-prize-draws-seed-data.js +198 -0
- package/dist/seed/products-standard-seed-data.js +141 -0
- package/dist/seed/products-stickers-seed-data.js +178 -5
- package/dist/seed/site-settings-seed-data.js +10 -29
- package/dist/seed/store-extensions-seed-data.js +1 -1
- package/dist/server-entry.d.ts +4 -1
- package/dist/server-entry.js +4 -1
- package/dist/server.d.ts +4 -2
- package/dist/server.js +4 -2
- package/dist/styles.css +29 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/ImageLightbox.js +37 -3
- package/dist/ui/components/ImageLightbox.style.css +28 -0
- package/dist/utils/media-url.d.ts +9 -0
- package/dist/utils/media-url.js +39 -0
- package/package.json +1 -1
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
4
4
|
import { createPortal } from "react-dom";
|
|
5
|
-
import { ChevronLeft, ChevronRight, X, ZoomIn, ZoomOut, RotateCw, Maximize2 } from "lucide-react";
|
|
5
|
+
import { ChevronLeft, ChevronRight, X, ZoomIn, ZoomOut, RotateCw, Maximize2, Minimize2 } from "lucide-react";
|
|
6
6
|
import { Button } from "./Button";
|
|
7
7
|
import { Div } from "./Div";
|
|
8
8
|
import { Text, Span } from "./Typography";
|
|
9
9
|
import { MediaImage } from "../../features/media/MediaImage";
|
|
10
|
+
import { getYouTubeVideoId } from "../../utils/media-url";
|
|
10
11
|
const CLS_CLOSE_BTN = "w-10 h-10 p-0 !min-h-0 rounded-full bg-white/15 hover:bg-error-surface text-white flex items-center justify-center";
|
|
11
12
|
const MIN_ZOOM = 10;
|
|
12
13
|
const MAX_ZOOM = 300;
|
|
@@ -15,6 +16,7 @@ export function ImageLightbox({ images, activeIndex, onClose, onNavigate, showTh
|
|
|
15
16
|
const [currentIndex, setCurrentIndex] = useState(activeIndex ?? 0);
|
|
16
17
|
const [zoom, setZoom] = useState(100);
|
|
17
18
|
const [rotation, setRotation] = useState(0);
|
|
19
|
+
const [isFullscreen, setIsFullscreen] = useState(false);
|
|
18
20
|
const overlayRef = useRef(null);
|
|
19
21
|
// Sync external activeIndex and reset transforms
|
|
20
22
|
useEffect(() => {
|
|
@@ -33,6 +35,37 @@ export function ImageLightbox({ images, activeIndex, onClose, onNavigate, showTh
|
|
|
33
35
|
document.body.style.overflow = "hidden";
|
|
34
36
|
return () => { document.body.style.overflow = prev; };
|
|
35
37
|
}, [isOpen]);
|
|
38
|
+
// Track native Fullscreen API state so the toggle button + icon stay in sync
|
|
39
|
+
// even when the user exits via Esc/browser chrome rather than our button.
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
const onFsChange = () => {
|
|
42
|
+
const doc = document;
|
|
43
|
+
const fsEl = document.fullscreenElement ?? doc.webkitFullscreenElement ?? null;
|
|
44
|
+
setIsFullscreen(fsEl === overlayRef.current);
|
|
45
|
+
};
|
|
46
|
+
document.addEventListener("fullscreenchange", onFsChange);
|
|
47
|
+
document.addEventListener("webkitfullscreenchange", onFsChange);
|
|
48
|
+
return () => {
|
|
49
|
+
document.removeEventListener("fullscreenchange", onFsChange);
|
|
50
|
+
document.removeEventListener("webkitfullscreenchange", onFsChange);
|
|
51
|
+
};
|
|
52
|
+
}, []);
|
|
53
|
+
// Exit native fullscreen when the lightbox itself closes.
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
const doc = document;
|
|
56
|
+
if (!isOpen && (document.fullscreenElement || doc.webkitFullscreenElement)) {
|
|
57
|
+
(document.exitFullscreen?.() ?? doc.webkitExitFullscreen?.())?.catch?.(() => { });
|
|
58
|
+
}
|
|
59
|
+
}, [isOpen]);
|
|
60
|
+
const toggleFullscreen = useCallback(() => {
|
|
61
|
+
const doc = document;
|
|
62
|
+
if (document.fullscreenElement || doc.webkitFullscreenElement) {
|
|
63
|
+
(document.exitFullscreen?.() ?? doc.webkitExitFullscreen?.())?.catch?.(() => { });
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const el = overlayRef.current;
|
|
67
|
+
(el?.requestFullscreen?.() ?? el?.webkitRequestFullscreen?.())?.catch?.(() => { });
|
|
68
|
+
}, []);
|
|
36
69
|
const navigate = useCallback((dir) => {
|
|
37
70
|
setCurrentIndex((prev) => {
|
|
38
71
|
const next = (prev + dir + images.length) % images.length;
|
|
@@ -89,11 +122,12 @@ export function ImageLightbox({ images, activeIndex, onClose, onNavigate, showTh
|
|
|
89
122
|
return null;
|
|
90
123
|
const image = images[currentIndex];
|
|
91
124
|
const hasMultiple = images.length > 1;
|
|
125
|
+
const youtubeId = image.kind === "video" ? getYouTubeVideoId(image.src) : null;
|
|
92
126
|
const iconBtnClass = "w-10 h-10 p-0 !min-h-0 rounded-full bg-white/15 hover:bg-white/30 text-white flex items-center justify-center";
|
|
93
|
-
return createPortal(_jsxs("div", { ref: overlayRef, tabIndex: -1, className: "appkit-lightbox", role: "dialog", "aria-modal": "true", "aria-label": "Image lightbox", onKeyDown: handleKeyDown, onWheel: handleWheel, children: [_jsxs("div", { className: "absolute top-0 left-0 right-0 z-10 flex items-center justify-between px-4 py-3 bg-gradient-to-b from-black/60 to-transparent", children: [_jsx(Span, { size: "sm", weight: "medium", className: "text-white/70", children: hasMultiple ? `${currentIndex + 1} / ${images.length}` : "" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: () => adjustZoom(-ZOOM_STEP), className: iconBtnClass, "aria-label": "Zoom out", disabled: zoom <= MIN_ZOOM, children: _jsx(ZoomOut, { className: "w-4 h-4" }) }), _jsxs("button", { type: "button", onClick: () => { setZoom(100); setRotation(0); }, className: "text-white/70 hover:text-white text-xs font-mono min-w-[3rem] text-center", "aria-label": "Reset zoom", children: [zoom, "%"] }), _jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: () => adjustZoom(ZOOM_STEP), className: iconBtnClass, "aria-label": "Zoom in", disabled: zoom >= MAX_ZOOM, children: _jsx(ZoomIn, { className: "w-4 h-4" }) }), _jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: () => setRotation((r) => (r + 90) % 360), className: iconBtnClass, "aria-label": "Rotate 90\u00B0", children: _jsx(RotateCw, { className: "w-4 h-4" }) }), _jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick:
|
|
127
|
+
return createPortal(_jsxs("div", { ref: overlayRef, tabIndex: -1, className: "appkit-lightbox", role: "dialog", "aria-modal": "true", "aria-label": "Image lightbox", onKeyDown: handleKeyDown, onWheel: handleWheel, children: [_jsxs("div", { className: "absolute top-0 left-0 right-0 z-10 flex items-center justify-between px-4 py-3 bg-gradient-to-b from-black/60 to-transparent", children: [_jsx(Span, { size: "sm", weight: "medium", className: "text-white/70", children: hasMultiple ? `${currentIndex + 1} / ${images.length}` : "" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: () => adjustZoom(-ZOOM_STEP), className: iconBtnClass, "aria-label": "Zoom out", disabled: zoom <= MIN_ZOOM, children: _jsx(ZoomOut, { className: "w-4 h-4" }) }), _jsxs("button", { type: "button", onClick: () => { setZoom(100); setRotation(0); }, className: "text-white/70 hover:text-white text-xs font-mono min-w-[3rem] text-center", "aria-label": "Reset zoom", children: [zoom, "%"] }), _jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: () => adjustZoom(ZOOM_STEP), className: iconBtnClass, "aria-label": "Zoom in", disabled: zoom >= MAX_ZOOM, children: _jsx(ZoomIn, { className: "w-4 h-4" }) }), _jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: () => setRotation((r) => (r + 90) % 360), className: iconBtnClass, "aria-label": "Rotate 90\u00B0", children: _jsx(RotateCw, { className: "w-4 h-4" }) }), _jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: toggleFullscreen, className: iconBtnClass, "aria-label": isFullscreen ? "Exit fullscreen" : "Enter fullscreen", children: isFullscreen ? _jsx(Minimize2, { className: "w-4 h-4" }) : _jsx(Maximize2, { className: "w-4 h-4" }) }), _jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: onClose, className: CLS_CLOSE_BTN, "aria-label": "Close lightbox", children: _jsx(X, { className: "w-5 h-5" }) })] })] }), hasMultiple && (_jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: () => navigate(-1), className: "absolute left-4 top-1/2 -translate-y-1/2 w-12 h-12 p-0 !min-h-0 rounded-full bg-white/15 hover:bg-white/30 text-white z-10 flex items-center justify-center", "aria-label": "Previous image", children: _jsx(ChevronLeft, { className: "w-7 h-7" }) })), _jsxs("div", { className: "appkit-lightbox__image-wrap", style: { cursor: zoom > 100 ? "grab" : "default" }, children: [image.kind === "video" ? (youtubeId ? (_jsx("iframe", { src: `https://www.youtube-nocookie.com/embed/${youtubeId}?playsinline=1&rel=0`, title: image.alt ?? "Video", allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share", allowFullScreen: true, className: "appkit-lightbox__img appkit-lightbox__video border-0" })) : (_jsx("video", { src: image.src, poster: image.poster, controls: true, playsInline: true, "aria-label": image.alt ?? "Video", className: "appkit-lightbox__img appkit-lightbox__video", style: {
|
|
94
128
|
transform: `scale(${zoom / 100}) rotate(${rotation}deg)`,
|
|
95
129
|
transition: "transform 0.2s ease",
|
|
96
|
-
} })) : (_jsx(MediaImage, { src: image.src, alt: image.alt ?? "", size: "hero", objectFit: "contain", className: "appkit-lightbox__img", style: {
|
|
130
|
+
} }))) : (_jsx(MediaImage, { src: image.src, alt: image.alt ?? "", size: "hero", objectFit: "contain", className: "appkit-lightbox__img", style: {
|
|
97
131
|
transform: `scale(${zoom / 100}) rotate(${rotation}deg)`,
|
|
98
132
|
transition: "transform 0.2s ease",
|
|
99
133
|
} })), image.badge && (_jsx(Div, { className: "absolute left-3 top-3 rounded bg-black/70 px-2 py-1 text-xs font-semibold text-white pointer-events-none", children: image.badge }))] }), (image.caption || image.sub) && (_jsxs("div", { className: "flex-shrink-0 text-center px-8 pb-2", children: [image.caption && (_jsx(Text, { size: "sm", variant: "secondary", className: "!text-white/80", children: image.caption })), image.sub && (_jsx(Text, { size: "sm", variant: "secondary", className: "!text-white/50", children: image.sub }))] })), showThumbnails && images.length > 1 && (_jsx("div", { className: "flex-shrink-0 flex gap-2 overflow-x-auto px-4 pb-3 justify-center", children: images.map((thumb, i) => (_jsx("button", { type: "button", onClick: () => {
|
|
@@ -37,6 +37,14 @@
|
|
|
37
37
|
position: relative;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
@media (max-width: 640px) {
|
|
41
|
+
.appkit-lightbox__image-wrap {
|
|
42
|
+
/* Tighter than the desktop 4rem so a video's min-width (below) has room
|
|
43
|
+
to fit inside narrow phone viewports without forcing horizontal scroll. */
|
|
44
|
+
padding: 1.5rem 1rem;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
40
48
|
.appkit-lightbox__img {
|
|
41
49
|
max-height: 100%;
|
|
42
50
|
max-width: 100%;
|
|
@@ -44,6 +52,26 @@
|
|
|
44
52
|
user-select: none;
|
|
45
53
|
}
|
|
46
54
|
|
|
55
|
+
/* -- Video / YouTube-embed area ----------------------- */
|
|
56
|
+
/* Applied alongside .appkit-lightbox__img on the theater-mode <video>/<iframe>
|
|
57
|
+
so a video always occupies a real, tappable box — never the browser's tiny
|
|
58
|
+
~300x150 default when a src fails to load or an embed reports no intrinsic
|
|
59
|
+
size (Recurrent Root Cause Pattern #27-adjacent: video needs its own sizing
|
|
60
|
+
contract, it can't just inherit the image one). */
|
|
61
|
+
.appkit-lightbox__video {
|
|
62
|
+
width: 100%;
|
|
63
|
+
height: 100%;
|
|
64
|
+
min-width: 260px;
|
|
65
|
+
min-height: 190px;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@media (min-width: 768px) {
|
|
69
|
+
.appkit-lightbox__video {
|
|
70
|
+
min-width: 480px;
|
|
71
|
+
min-height: 320px;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
47
75
|
/* -- Zoom hint --------------------------------------- */
|
|
48
76
|
|
|
49
77
|
.appkit-lightbox__zoom-hint {
|
|
@@ -16,3 +16,12 @@ export declare const GCS_HOST = "storage.googleapis.com";
|
|
|
16
16
|
* - Falsy → undefined
|
|
17
17
|
*/
|
|
18
18
|
export declare function resolveMediaUrl(url: string | null | undefined): string | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Extracts the 11-char YouTube video ID from a watch/share/embed URL, or
|
|
21
|
+
* `null` when `url` isn't a recognized YouTube URL. A video field can be
|
|
22
|
+
* sourced via `MediaUploadField`'s "YouTube" tab (see `showYoutube` there),
|
|
23
|
+
* which stores a `youtube.com/watch?v=...` URL — that URL is never a raw
|
|
24
|
+
* playable media file, so every `<video src>` renderer (`MediaVideo`,
|
|
25
|
+
* `ImageLightbox`) must check this first and fall back to an iframe embed.
|
|
26
|
+
*/
|
|
27
|
+
export declare function getYouTubeVideoId(url: string | null | undefined): string | null;
|
package/dist/utils/media-url.js
CHANGED
|
@@ -37,3 +37,42 @@ export function resolveMediaUrl(url) {
|
|
|
37
37
|
return url;
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
|
+
const YOUTUBE_HOSTS = new Set([
|
|
41
|
+
"youtube.com",
|
|
42
|
+
"m.youtube.com",
|
|
43
|
+
"youtu.be",
|
|
44
|
+
"youtube-nocookie.com",
|
|
45
|
+
]);
|
|
46
|
+
/**
|
|
47
|
+
* Extracts the 11-char YouTube video ID from a watch/share/embed URL, or
|
|
48
|
+
* `null` when `url` isn't a recognized YouTube URL. A video field can be
|
|
49
|
+
* sourced via `MediaUploadField`'s "YouTube" tab (see `showYoutube` there),
|
|
50
|
+
* which stores a `youtube.com/watch?v=...` URL — that URL is never a raw
|
|
51
|
+
* playable media file, so every `<video src>` renderer (`MediaVideo`,
|
|
52
|
+
* `ImageLightbox`) must check this first and fall back to an iframe embed.
|
|
53
|
+
*/
|
|
54
|
+
export function getYouTubeVideoId(url) {
|
|
55
|
+
if (!url)
|
|
56
|
+
return null;
|
|
57
|
+
try {
|
|
58
|
+
const parsed = new URL(url);
|
|
59
|
+
const host = parsed.hostname.replace(/^www\./, "");
|
|
60
|
+
if (!YOUTUBE_HOSTS.has(host))
|
|
61
|
+
return null;
|
|
62
|
+
if (host === "youtu.be") {
|
|
63
|
+
const id = parsed.pathname.slice(1);
|
|
64
|
+
return /^[\w-]{11}$/.test(id) ? id : null;
|
|
65
|
+
}
|
|
66
|
+
const fromQuery = parsed.searchParams.get("v");
|
|
67
|
+
if (fromQuery && /^[\w-]{11}$/.test(fromQuery))
|
|
68
|
+
return fromQuery;
|
|
69
|
+
const embedMatch = parsed.pathname.match(/\/embed\/([\w-]{11})/);
|
|
70
|
+
if (embedMatch)
|
|
71
|
+
return embedMatch[1];
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
catch (_err) {
|
|
75
|
+
void normalizeError(_err);
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|