UNPKG

@wordpress/editor

Version:
302 lines (301 loc) 9.88 kB
// packages/editor/src/components/media-categories/index.js import { __, sprintf, _x } from "@wordpress/i18n"; import { dispatch, resolveSelect, select, subscribe } from "@wordpress/data"; import { decodeEntities } from "@wordpress/html-entities"; import { store as coreStore } from "@wordpress/core-data"; var getExternalLink = (url, text) => `<a ${getExternalLinkAttributes(url)}>${text}</a>`; var getExternalLinkAttributes = (url) => `href="${url}" target="_blank" rel="noopener"`; var getOpenverseLicense = (license, licenseVersion) => { let licenseName = license.trim(); if (license !== "pdm") { licenseName = license.toUpperCase().replace("SAMPLING", "Sampling"); } if (licenseVersion) { licenseName += ` ${licenseVersion}`; } if (!["pdm", "cc0"].includes(license)) { licenseName = `CC ${licenseName}`; } return licenseName; }; var getOpenverseCaption = (item) => { const { title, foreign_landing_url: foreignLandingUrl, creator, creator_url: creatorUrl, license, license_version: licenseVersion, license_url: licenseUrl } = item; const fullLicense = getOpenverseLicense(license, licenseVersion); const _creator = decodeEntities(creator); let _caption; if (_creator) { _caption = title ? sprintf( // translators: %1s: Title of a media work from Openverse; %2$s: Name of the work's creator; %3s: Work's licence e.g: "CC0 1.0". _x('"%1$s" by %2$s/ %3$s', "caption"), getExternalLink( foreignLandingUrl, decodeEntities(title) ), creatorUrl ? getExternalLink(creatorUrl, _creator) : _creator, licenseUrl ? getExternalLink( `${licenseUrl}?ref=openverse`, fullLicense ) : fullLicense ) : sprintf( // translators: %1s: Link attributes for a given Openverse media work; %2s: Name of the work's creator; %3s: Works's licence e.g: "CC0 1.0". _x("<a %1$s>Work</a> by %2$s/ %3$s", "caption"), getExternalLinkAttributes(foreignLandingUrl), creatorUrl ? getExternalLink(creatorUrl, _creator) : _creator, licenseUrl ? getExternalLink( `${licenseUrl}?ref=openverse`, fullLicense ) : fullLicense ); } else { _caption = title ? sprintf( // translators: %1s: Title of a media work from Openverse; %2s: Work's licence e.g: "CC0 1.0". _x('"%1$s"/ %2$s', "caption"), getExternalLink( foreignLandingUrl, decodeEntities(title) ), licenseUrl ? getExternalLink( `${licenseUrl}?ref=openverse`, fullLicense ) : fullLicense ) : sprintf( // translators: %1s: Link attributes for a given Openverse media work; %2s: Works's licence e.g: "CC0 1.0". _x("<a %1$s>Work</a>/ %2$s", "caption"), getExternalLinkAttributes(foreignLandingUrl), licenseUrl ? getExternalLink( `${licenseUrl}?ref=openverse`, fullLicense ) : fullLicense ); } return _caption.replace(/\s{2}/g, " "); }; var getCoreMediaQuery = (query = {}) => ({ ...query, orderBy: !!query?.search ? "relevance" : "date" }); var coreMediaFetch = async (query = {}) => { const finalQuery = getCoreMediaQuery(query); const records = await resolveSelect(coreStore).getEntityRecords( "postType", "attachment", finalQuery ); const totalItems = select(coreStore).getEntityRecordsTotalItems( "postType", "attachment", finalQuery ); const totalPages = select(coreStore).getEntityRecordsTotalPages( "postType", "attachment", finalQuery ); return { mediaItems: records.map((record) => ({ ...record, alt: record.alt_text, url: record.source_url, previewUrl: record.media_details?.sizes?.medium?.source_url, caption: record.caption?.raw })), totalItems, totalPages }; }; var getAttachedImagesQuery = (postId, query = {}) => ({ ...query, media_type: "image", parent: postId }); var normalizePostId = (postId) => { const parsedPostId = typeof postId === "number" ? postId : Number(postId); return Number.isInteger(parsedPostId) && parsedPostId > 0 ? parsedPostId : void 0; }; var saveAttachmentParent = (attachmentId, postId) => ( // `throwOnError` so a failed REST write rejects (rather than being silently // swallowed), letting the attach/detach handlers surface an error notice // instead of a false success. dispatch(coreStore).saveEntityRecord( "postType", "attachment", { id: attachmentId, post: postId }, { throwOnError: true } ) ); var getMediaItemType = (mediaItem) => mediaItem?.media_type || mediaItem?.mime_type?.split("/")[0] || mediaItem?.type; var getImageAttachmentIds = (mediaItems) => [ ...new Set( (Array.isArray(mediaItems) ? mediaItems : [mediaItems]).filter( (mediaItem) => getMediaItemType(mediaItem) === "image" ).map((mediaItem) => mediaItem?.id).filter(Boolean) ) ]; var invalidateAttachedImagesQueries = (postId, query = {}) => { const { invalidateResolution } = dispatch(coreStore); invalidateResolution("getEntityRecords", [ "postType", "attachment", getCoreMediaQuery(getAttachedImagesQuery(postId, query)) ]); }; var subscribeToMediaInvalidation = (args, onChange) => { const isResolved = () => select(coreStore).hasFinishedResolution("getEntityRecords", args); let wasResolved = isResolved(); return subscribe(() => { const nowResolved = isResolved(); if (wasResolved && !nowResolved) { onChange(); } wasResolved = nowResolved; }, coreStore); }; var createCoreMediaCategory = ({ getQuery, ...category }) => ({ ...category, async fetch(query = {}) { return coreMediaFetch(getQuery(query)); }, subscribe(onChange, query = {}) { return subscribeToMediaInvalidation( [ "postType", "attachment", getCoreMediaQuery(getQuery(query)) ], onChange ); } }); var getAttachedImagesCategory = (postId, typeLabel) => createCoreMediaCategory({ name: "attached-images", labels: { name: __("Attached images"), search_items: __("Search attachments") }, mediaType: "image", getQuery: (query) => getAttachedImagesQuery(postId, query), // The post type's singular label (e.g. "Page"), threaded through so the // shared panel can word its attach/detach copy for the current post type. postTypeLabel: typeLabel, // Empty-state message. Providing this also keeps the source in the tab // list when it has no items, so it stays discoverable and the first // image can be attached even with none yet. emptyMessage: typeLabel ? sprintf( // translators: %s: Name of the post type e.g: "Page". __("No images attached to this %s."), typeLabel ) : __("No images attached to this post."), async attach(mediaItems) { const attachmentIds = getImageAttachmentIds(mediaItems); await Promise.all( attachmentIds.map( (attachmentId) => saveAttachmentParent(attachmentId, postId) ) ); return attachmentIds.length; }, async detach(mediaItem) { await saveAttachmentParent(mediaItem.id, 0); }, invalidate(query = {}) { invalidateAttachedImagesQueries(postId, query); } }); var inserterMediaCategories = [ createCoreMediaCategory({ name: "images", labels: { name: __("Images"), search_items: __("Search images") }, mediaType: "image", getQuery: (query) => ({ ...query, media_type: "image" }) }), createCoreMediaCategory({ name: "videos", labels: { name: __("Videos"), search_items: __("Search videos") }, mediaType: "video", getQuery: (query) => ({ ...query, media_type: "video" }) }), createCoreMediaCategory({ name: "audio", labels: { name: __("Audio"), search_items: __("Search audio") }, mediaType: "audio", getQuery: (query) => ({ ...query, media_type: "audio" }) }), { name: "openverse", labels: { name: __("Openverse"), search_items: __("Search Openverse") }, mediaType: "image", async fetch(query = {}) { const defaultArgs = { mature: false, excluded_source: "flickr,inaturalist,wikimedia", license: "pdm,cc0" }; const finalQuery = { ...query, ...defaultArgs }; const mapFromInserterMediaRequest = { per_page: "page_size", search: "q" }; const url = new URL("https://api.openverse.org/v1/images/"); Object.entries(finalQuery).forEach(([key, value]) => { const queryKey = mapFromInserterMediaRequest[key] || key; url.searchParams.set(queryKey, value); }); const response = await window.fetch(url, { headers: { "User-Agent": "WordPress/inserter-media-fetch" } }); const jsonResponse = await response.json(); const results = jsonResponse.results; return results.map((result) => ({ ...result, // This is a temp solution for better titles, until Openverse API // completes the cleaning up of some titles of their upstream data. title: result.title?.toLowerCase().startsWith("file:") ? result.title.slice(5) : result.title, sourceId: result.id, id: void 0, caption: getOpenverseCaption(result), previewUrl: result.thumbnail })); }, getReportUrl: ({ sourceId }) => `https://wordpress.org/openverse/image/${sourceId}/report/`, isExternalResource: true } ]; function getInserterMediaCategories(postId, viewablePostTypeLabel) { const currentPostId = normalizePostId(postId); if (!currentPostId || !viewablePostTypeLabel) { return inserterMediaCategories; } return [ getAttachedImagesCategory(currentPostId, viewablePostTypeLabel), ...inserterMediaCategories ]; } export { getInserterMediaCategories as default }; //# sourceMappingURL=index.mjs.map