sanity-plugin-wistia-input
Version:
Wistia input for Sanity v5
545 lines (544 loc) • 21.5 kB
JavaScript
import { setIfMissing, set, unset, definePlugin } from "sanity";
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
import { memo, useState, useMemo, useRef, useEffect, useCallback } from "react";
import { Box, Card, Text, Flex, Spinner, Tooltip, Stack, Badge, Button, Heading, useToast, Dialog } from "@sanity/ui";
import { FolderIcon, LockIcon, LaunchIcon, ChevronLeftIcon, LinkIcon, SearchIcon, ResetIcon, DocumentVideoIcon } from "@sanity/icons";
const wistiaMedia = {
name: "wistiaMedia",
type: "object",
title: "Wistia media",
fields: [
{
type: "number",
name: "id"
},
{
type: "string",
name: "hashed_id"
}
],
preview: {
select: {
title: "title",
subtitle: "hashed_id"
},
prepare({ title, subtitle }) {
return {
title: title || "Wistia media",
subtitle
};
}
}
}, playerUrl = (mediaUrl) => {
let params = new URLSearchParams(), wistiaSettings = {
playbar: !0,
playButton: !0,
seo: !1,
controlsVisibleOnLoad: !0,
autoPlay: !1,
doNotTrack: !0,
preload: "none",
volumeControl: !0,
copyLinkAndThumbnailEnabled: !0,
fullscreenButton: !0,
wmode: "transparent",
videoFoam: !1,
fitStrategy: "fill"
};
for (let key in wistiaSettings)
params.append(key, wistiaSettings[key]);
return `${mediaUrl}?${params.toString()}`;
};
function Player({ mediaUrl }) {
return /* @__PURE__ */ jsx(
"div",
{
style: {
width: "100%",
borderRadius: "3px",
overflow: "hidden"
},
children: mediaUrl && /* @__PURE__ */ jsx(
"iframe",
{
allow: "autoplay; fullscreen;",
"allow-transparency": "true",
width: "100%",
style: {
aspectRatio: "16/9",
border: 0,
display: "block"
},
src: playerUrl(mediaUrl)
}
)
}
);
}
const WistiaFoldersComponent = ({
folders,
loadingFolderId,
error,
hasMore,
loadingMore,
onFolderClick,
onLoadMore,
config
}) => {
const [hoveredId, setHoveredId] = useState(null);
return error ? /* @__PURE__ */ jsx(Box, { padding: 3, children: /* @__PURE__ */ jsx(Card, { padding: [3, 3, 4], radius: 2, shadow: 1, tone: "critical", children: /* @__PURE__ */ jsx(Text, { align: "center", children: error }) }) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
folders.length ? folders.map((folder) => /* @__PURE__ */ jsx(
Card,
{
paddingTop: 3,
paddingBottom: 3,
paddingRight: 3,
paddingLeft: 4,
radius: 1,
style: { cursor: "pointer" },
onClick: () => onFolderClick(folder),
tone: loadingFolderId === folder.id || hoveredId === folder.id ? "primary" : "inherit",
onMouseEnter: () => setHoveredId(folder.id),
onMouseLeave: () => setHoveredId(null),
as: "button",
children: /* @__PURE__ */ jsxs(Flex, { justify: "space-between", align: "center", gap: 2, children: [
/* @__PURE__ */ jsxs(Flex, { align: "center", gap: 3, paddingLeft: 1, children: [
/* @__PURE__ */ jsx(Box, { flex: "none", children: loadingFolderId === folder.id ? /* @__PURE__ */ jsx(Spinner, { size: 2 }) : /* @__PURE__ */ jsx(Text, { size: 2, children: folder.public ? /* @__PURE__ */ jsx(FolderIcon, {}) : /* @__PURE__ */ jsx(Box, { flex: "none", children: /* @__PURE__ */ jsx(Text, { size: 2, children: /* @__PURE__ */ jsx(
Tooltip,
{
content: /* @__PURE__ */ jsx(Text, { muted: !0, size: 1, children: "Private" }),
fallbackPlacements: ["right", "left"],
placement: "top",
portal: !0,
children: /* @__PURE__ */ jsx(LockIcon, {})
}
) }) }) }) }),
/* @__PURE__ */ jsx(Stack, { space: 2, children: /* @__PURE__ */ jsx(Text, { size: 2, weight: "semibold", children: folder.name }) })
] }),
/* @__PURE__ */ jsxs(Flex, { align: "center", gap: 3, children: [
/* @__PURE__ */ jsx(Badge, { "aria-label": `${folder.media_count} media items`, tone: "default", children: folder.media_count }),
/* @__PURE__ */ jsx(
Tooltip,
{
content: /* @__PURE__ */ jsx(Text, { size: 1, children: "Open in Wistia" }),
placement: "top",
fallbackPlacements: ["left", "bottom"],
portal: !0,
children: /* @__PURE__ */ jsx(
Button,
{
as: "a",
href: `https://${config.accountSubdomain || "app"}.wistia.com/folders/${folder.hashed_id}`,
target: "_blank",
rel: "noopener noreferrer",
icon: LaunchIcon,
mode: "bleed",
padding: 2,
onClick: (e) => e.stopPropagation()
}
)
}
)
] })
] })
},
folder.id
)) : /* @__PURE__ */ jsx(Card, { padding: 4, children: /* @__PURE__ */ jsx(Text, { align: "center", muted: !0, size: 1, children: "No folders found." }) }),
hasMore && /* @__PURE__ */ jsx(Card, { padding: 3, borderTop: !0, children: /* @__PURE__ */ jsx(
Button,
{
mode: "bleed",
text: loadingMore ? "Loading\u2026" : "Show more",
onClick: onLoadMore,
disabled: loadingMore,
width: "fill"
}
) })
] });
};
var Folder = memo(WistiaFoldersComponent);
const groupBy = (array) => array.reduce((rv, x) => {
const key = x.section ?? "undefined";
return rv[key] = rv[key] ? [...rv[key], x] : [x], rv;
}, {}), wistiaMediasComponent = ({
medias,
hasMore,
loadingMore,
onVideoClick,
onLoadMore,
config
}) => {
const [hoveredId, setHoveredId] = useState(null), groupedMedias = useMemo(() => groupBy(medias), [medias]);
return /* @__PURE__ */ jsxs(Fragment, { children: [
Object.keys(groupedMedias).length ? Object.keys(groupedMedias).map((section) => /* @__PURE__ */ jsxs("div", { children: [
section !== "undefined" && /* @__PURE__ */ jsx(
Card,
{
padding: 3,
paddingLeft: 4,
borderBottom: !0,
style: { position: "sticky", top: 0, zIndex: 1 },
children: /* @__PURE__ */ jsx(Heading, { as: "h3", size: 1, children: section })
}
),
groupedMedias[section].map((media) => /* @__PURE__ */ jsx(
Card,
{
padding: 3,
paddingLeft: 4,
radius: 1,
style: { cursor: "pointer" },
onClick: () => onVideoClick({ id: media.id, hashed_id: media.hashed_id }),
tone: hoveredId === media.id ? "primary" : "inherit",
onMouseEnter: () => setHoveredId(media.id),
onMouseLeave: () => setHoveredId(null),
as: "button",
children: /* @__PURE__ */ jsxs(Flex, { gap: 3, align: "center", children: [
/* @__PURE__ */ jsx(Card, { padding: 0, overflow: "hidden", radius: 2, shadow: 1, flex: "none", children: /* @__PURE__ */ jsx(
"img",
{
src: media.thumbnail.url,
width: 70,
height: Math.round(70 / (media.thumbnail.width / media.thumbnail.height)),
style: {
display: "block"
},
alt: media.name
}
) }),
/* @__PURE__ */ jsx(Stack, { space: 2, children: /* @__PURE__ */ jsx(Text, { title: media.name, textOverflow: "ellipsis", weight: "semibold", children: media.name }) }),
/* @__PURE__ */ jsx(Box, { flex: "none", style: { marginLeft: "auto" }, children: /* @__PURE__ */ jsx(Text, { muted: !0, children: new Date(media.duration * 1e3).toISOString().slice(11, 19) }) }),
/* @__PURE__ */ jsx(
Tooltip,
{
content: /* @__PURE__ */ jsx(Text, { size: 1, children: "Open in Wistia" }),
placement: "top",
fallbackPlacements: ["left", "bottom"],
portal: !0,
children: /* @__PURE__ */ jsx(
Button,
{
as: "a",
href: `https://${config.accountSubdomain || "app"}.wistia.com/medias/${media.hashed_id}`,
target: "_blank",
rel: "noopener noreferrer",
icon: LaunchIcon,
mode: "bleed",
padding: 2,
onClick: (e) => e.stopPropagation()
}
)
}
)
] })
},
media.id
))
] }, section)) : /* @__PURE__ */ jsx(Card, { padding: 5, children: /* @__PURE__ */ jsx(Text, { align: "center", muted: !0, size: 1, children: "No media found." }) }),
hasMore && /* @__PURE__ */ jsx(Card, { padding: 3, borderTop: !0, children: /* @__PURE__ */ jsx(
Button,
{
mode: "bleed",
text: loadingMore ? "Loading\u2026" : "Show more",
onClick: onLoadMore,
disabled: loadingMore,
width: "fill"
}
) })
] });
};
var Medias = memo(wistiaMediasComponent);
const RESULTS_PER_PAGE = 100, WISTIA_API_URL = "https://api.wistia.com/modern";
function useWistiaData(path, config, onFirstLoad) {
const [data, setData] = useState([]), [loadingMore, setLoadingMore] = useState(!1), [error, setError] = useState(""), [page, setPage] = useState(1), [hasMore, setHasMore] = useState(!1), wistiaAPIOptions = {
headers: {
"X-Wistia-API-Version": "2026-01",
Authorization: `Bearer ${config.token}`
}
}, onFirstLoadRef = useRef(onFirstLoad);
onFirstLoadRef.current = onFirstLoad, useEffect(() => {
if (!path) {
setData([]), setHasMore(!1), setError(""), setPage(1);
return;
}
setPage(1), fetch(`${WISTIA_API_URL}${path}&page=1&per_page=${RESULTS_PER_PAGE}`, wistiaAPIOptions).then((r) => {
if (!r.ok)
throw new Error(
r.status === 401 ? "401 Not authorised - check your API key permissions." : `${r.status} error`
);
return r.json();
}).then((result) => {
setHasMore(result.length === RESULTS_PER_PAGE), setData(result), onFirstLoadRef.current?.(result);
}).catch((err) => setError(err.message));
}, [path, config.token]);
const loadMore = useCallback(() => {
if (!path) return;
const nextPage = page + 1;
setPage(nextPage), setLoadingMore(!0), fetch(`${WISTIA_API_URL}${path}&page=${nextPage}&per_page=${RESULTS_PER_PAGE}`, wistiaAPIOptions).then((r) => r.json()).then((result) => {
setHasMore(result.length === RESULTS_PER_PAGE), setData((prev) => [...prev, ...result]), setLoadingMore(!1);
}).catch(console.error);
}, [page, path, config.token]);
return { data, loadingMore, error, hasMore, loadMore };
}
const WistiaInputComponent = (props) => {
const { value, onChange, config, schemaType, renderDefault } = props, { push: pushToast } = useToast(), [isPickerMounted, setIsPickerMounted] = useState(!1), [isDialogOpen, setIsDialogOpen] = useState(!1), [isFolderLoading, setIsFolderLoading] = useState(!1), [selectedFolder, setSelectedFolder] = useState(null), [loadingFolderId, setLoadingFolderId] = useState(null), [visibleFolder, setVisibleFolder] = useState(null), {
data: folders,
loadingMore: foldersLoadingMore,
error: foldersError,
hasMore: hasMoreFolders,
loadMore: loadMoreFolders
} = useWistiaData(
isPickerMounted ? "/folders?sort_by=updated&sort_direction=0" : null,
config,
() => {
setIsFolderLoading(!1), setIsDialogOpen(!0);
}
), {
data: medias,
loadingMore: mediasLoadingMore,
hasMore: hasMoreMedias,
loadMore: loadMoreMedias
} = useWistiaData(
selectedFolder ? `/medias?folder_id=${selectedFolder.id}&sort_by=name` : null,
config,
() => {
setLoadingFolderId(null), setVisibleFolder(selectedFolder);
}
), handleChange = useCallback(
(newValue) => {
setIsPickerMounted(!1), setIsDialogOpen(!1), setSelectedFolder(null), setVisibleFolder(null), onChange([
setIfMissing({ _type: schemaType.name }),
set(newValue.hashed_id, ["hashed_id"]),
set(newValue.id, ["id"])
]);
},
[onChange, schemaType]
), handleClear = useCallback(() => {
onChange(unset());
}, [onChange]), handleCopyUrl = useCallback(() => {
navigator.clipboard.writeText(`https://fast.wistia.net/embed/iframe/${value?.hashed_id}`), pushToast({ closable: !0, status: "success", title: "URL copied to clipboard" });
}, [value, pushToast]), handleOpenPicker = useCallback(() => {
setIsFolderLoading(!0), setIsPickerMounted(!0);
}, []), handleClosePicker = useCallback(() => {
setIsPickerMounted(!1), setIsDialogOpen(!1), setSelectedFolder(null), setVisibleFolder(null), setLoadingFolderId(null), setIsFolderLoading(!1);
}, []), handleFolderClick = useCallback((folder) => {
setLoadingFolderId(folder.id), setSelectedFolder(folder);
}, []);
if (!config?.token?.length)
return /* @__PURE__ */ jsx(Card, { padding: [3, 3, 4], radius: 2, shadow: 1, tone: "critical", children: /* @__PURE__ */ jsxs(Text, { align: "center", children: [
"Missing required API token in Sanity config.",
" ",
/* @__PURE__ */ jsx("a", { href: "https://docs.wistia.com/docs/making-api-requests", children: "See Wistia documentation." })
] }) });
const mediaUrl = value?.hashed_id ? `https://fast.wistia.net/embed/iframe/${value.hashed_id}` : null, picker = isDialogOpen && /* @__PURE__ */ jsx(
Dialog,
{
id: "wistia-picker",
onClose: handleClosePicker,
animate: !0,
width: 1,
header: visibleFolder ? /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
/* @__PURE__ */ jsxs(Flex, { justify: "space-between", children: [
/* @__PURE__ */ jsx(
Button,
{
icon: ChevronLeftIcon,
onClick: () => {
setSelectedFolder(null), setVisibleFolder(null);
},
mode: "bleed",
padding: 2,
space: 2,
text: "All folders"
}
),
/* @__PURE__ */ jsx(
Button,
{
as: "a",
href: `https://${config.accountSubdomain || "app"}.wistia.com/folders/${visibleFolder.hashed_id}`,
target: "_blank",
rel: "noopener noreferrer",
icon: LaunchIcon,
mode: "bleed",
padding: 2,
space: 2,
text: "Open in Wistia"
}
)
] }),
/* @__PURE__ */ jsx(Flex, { justify: "space-between", align: "center", flex: 1, paddingBottom: 0, children: /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
/* @__PURE__ */ jsx(Heading, { as: "h2", size: 2, children: visibleFolder.name }),
visibleFolder.description ? /* @__PURE__ */ jsx(Text, { textOverflow: "ellipsis", size: 1, muted: !0, children: visibleFolder.description.replace(/<[^>]*>/g, "").trim() }) : null
] }) })
] }) : "Select a folder",
footer: visibleFolder ? /* @__PURE__ */ jsx(Card, { tone: "transparent", paddingX: 4, paddingY: 4, children: /* @__PURE__ */ jsxs(Flex, { align: "center", justify: "flex-start", gap: 3, children: [
!visibleFolder.public && /* @__PURE__ */ jsx(Badge, { tone: "caution", children: "Private" }),
/* @__PURE__ */ jsxs(Badge, { title: `${visibleFolder.media_count} items`, tone: "default", children: [
visibleFolder.media_count,
" items"
] }),
/* @__PURE__ */ jsxs(Text, { title: visibleFolder.updated, size: 1, children: [
/* @__PURE__ */ jsx("b", { children: "Updated" }),
" ",
new Date(visibleFolder.updated).toLocaleDateString(void 0, {
day: "numeric",
month: "short",
year: "numeric",
hour: "2-digit",
minute: "2-digit"
})
] })
] }) }) : void 0,
children: visibleFolder ? /* @__PURE__ */ jsx(
Medias,
{
config,
medias,
hasMore: hasMoreMedias,
loadingMore: mediasLoadingMore,
onVideoClick: handleChange,
onLoadMore: loadMoreMedias
}
) : /* @__PURE__ */ jsx(
Folder,
{
config,
folders,
loadingFolderId,
error: foldersError,
hasMore: hasMoreFolders,
loadingMore: foldersLoadingMore,
onFolderClick: handleFolderClick,
onLoadMore: loadMoreFolders
}
)
}
), extraFields = config.fields?.length ? /* @__PURE__ */ jsx(Stack, { marginTop: 4, children: renderDefault({ ...props, members: props.members.filter(
(m) => m.kind === "field" && config.fields.some((f) => f.name === m.name)
) }) }) : null;
return mediaUrl ? /* @__PURE__ */ jsxs(Stack, { space: 0, children: [
/* @__PURE__ */ jsxs(Card, { radius: 2, shadow: 1, padding: 2, children: [
/* @__PURE__ */ jsxs(Flex, { justify: "space-between", align: "center", padding: 0, paddingBottom: 2, gap: 1, children: [
/* @__PURE__ */ jsxs(Flex, { gap: 1, children: [
/* @__PURE__ */ jsx(Tooltip, { animate: !0, content: /* @__PURE__ */ jsx(Text, { size: 1, children: "Open in Wistia" }), placement: "top", portal: !0, children: /* @__PURE__ */ jsx(
Button,
{
as: "a",
href: `https://${config.accountSubdomain || "app"}.wistia.com/medias/${value?.hashed_id}`,
target: "_blank",
rel: "noopener noreferrer",
icon: LaunchIcon,
mode: "bleed",
padding: 2
}
) }),
/* @__PURE__ */ jsx(Tooltip, { animate: !0, content: /* @__PURE__ */ jsx(Text, { size: 1, children: "Copy embed URL" }), placement: "top", portal: !0, children: /* @__PURE__ */ jsx(Button, { mode: "bleed", padding: 2, icon: LinkIcon, onClick: handleCopyUrl }) })
] }),
/* @__PURE__ */ jsxs(Flex, { align: "center", gap: 1, children: [
/* @__PURE__ */ jsx(
Button,
{
text: "Replace",
padding: 2,
space: 2,
mode: "bleed",
icon: SearchIcon,
loading: isFolderLoading,
onClick: handleOpenPicker
}
),
/* @__PURE__ */ jsx(
Button,
{
text: "Clear",
padding: 2,
space: 2,
mode: "bleed",
icon: ResetIcon,
tone: "critical",
onClick: handleClear
}
)
] })
] }),
/* @__PURE__ */ jsx(Player, { mediaUrl })
] }),
picker,
extraFields
] }) : /* @__PURE__ */ jsxs(Stack, { space: 2, children: [
/* @__PURE__ */ jsx(Card, { tone: "inherit", radius: 2, border: !0, padding: 2, paddingLeft: 3, children: /* @__PURE__ */ jsxs(Flex, { justify: "space-between", align: "center", children: [
/* @__PURE__ */ jsxs(Flex, { align: "center", gap: 3, children: [
/* @__PURE__ */ jsx(Box, { flex: "none", children: /* @__PURE__ */ jsx(Text, { size: 2, children: /* @__PURE__ */ jsx(DocumentVideoIcon, {}) }) }),
/* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: "No media selected" })
] }),
/* @__PURE__ */ jsxs(Flex, { align: "center", gap: 1, children: [
/* @__PURE__ */ jsx(
Button,
{
as: "a",
href: `https://${config.accountSubdomain || "app"}.wistia.com/home`,
target: "_blank",
rel: "noopener noreferrer",
icon: LaunchIcon,
mode: "bleed",
onClick: (e) => e.stopPropagation(),
text: "Open Wistia",
padding: 2,
space: 2
}
),
/* @__PURE__ */ jsx(
Button,
{
mode: "bleed",
text: "Select",
icon: SearchIcon,
loading: isFolderLoading,
onClick: handleOpenPicker,
padding: 2,
space: 2
}
)
] })
] }) }),
picker,
extraFields
] });
};
var Preview = (props) => {
const { subtitle: hashed_id, renderDefault } = props;
return /* @__PURE__ */ jsxs(Stack, { space: 1, children: [
renderDefault({ ...props }),
hashed_id && /* @__PURE__ */ jsx(Player, { mediaUrl: `https://fast.wistia.net/embed/iframe/${hashed_id}` })
] });
};
function wistiaMediaRender(config) {
return {
components: {
input: (props) => /* @__PURE__ */ jsx(WistiaInputComponent, { config, ...props }),
preview: (props) => /* @__PURE__ */ jsx(Preview, { ...props })
}
};
}
const wistiaInput = definePlugin((config) => ({
name: "sanity-plugin-wistia-input",
schema: {
types: [
{
...wistiaMedia,
fields: [
...wistiaMedia.fields,
...(config.fields ?? []).filter(
(f) => !wistiaMedia.fields.some((base) => base.name === f.name)
)
],
...wistiaMediaRender(config)
}
]
}
}));
export {
wistiaInput
};
//# sourceMappingURL=index.mjs.map