import React from 'react'; import ContentstackSDK from '@contentstack/app-sdk'; import { ImageElement } from '~/components/ImageElement'; import { Icon } from '@contentstack/venus-components'; import { openImageVault } from '~/utils/imagevault'; import type { Lang } from '~/types/lang'; import type { ContentstackEmbeddedData, ContentstackPluginDefinition, ExtractedContentstackEmbeddedData, } from '~/types/contentstack'; function findThisPlugin(ext: ContentstackPluginDefinition) { return ext.type === 'rte_plugin' && ext.title === 'ImageVault'; } async function loadScript(url: string) { return new Promise((resolve, reject) => { if (!document) { throw new Error('Run in browser only'); } const head = document.head || document.getElementsByTagName('head')[0]; const script = document.createElement('script'); script.type = 'text/javascript'; script.async = true; script.src = url; script.onload = function () { this.onerror = this.onload = null; resolve(window.ImageVault); }; script.onerror = function () { this.onerror = this.onload = null; reject(`Failed to load ${this.src}`); }; head.appendChild(script); }); } function extractContentstackEmbeddedData( jwtLike: string ): ExtractedContentstackEmbeddedData | null { try { const base64str = jwtLike.replace(/-/g, "+").replace(/_/g, "/"); const jsonStr = decodeURIComponent( window .atob(base64str) .split("") .map(function (c) { return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2); }) .join("") ); const json = JSON.parse(jsonStr); json.exports = JSON.parse(json.exports); json.props.value = JSON.parse(json.props.value); if (IS_DEV) { console.log(`Contentstack Embedded Data`, json); } const { entryMetadata, utilis: { content_type: { schema }, extensions, }, requestProps: { stack: { api_key }, branch, }, }: ContentstackEmbeddedData = json.props.value; const titleField = schema.find((f) => f.uid === "title"); // We force this value with ! because we know it exists. // Otherwise this code would not run. const plugin = extensions.find(findThisPlugin)!; return { stack: { apiKey: api_key, }, branch, contentType: { uid: entryMetadata.contentTypeUid, }, entry: { locale: entryMetadata.locale, title: titleField && titleField.data_type === "text" ? titleField.value : "Untitled", uid: entryMetadata.entryUid, }, plugin, }; } catch (e) { console.log(`Unable to parse JWT like: ${jwtLike}`); } return null; } let ivloaded = false; function loadIV(plugin: ContentstackPluginDefinition) { if (plugin.src) { const url = new URL(plugin.src); url.pathname = "/scripts/imagevault-insert-media/insertmediawindow.min.js"; if (IS_DEV) { console.log(`Loading script: ${url.toString()}`); } loadScript(url.toString()); ivloaded = true; } } async function init() { try { const sdk = await ContentstackSDK.init(); const extensionObj = sdk?.location; const RTE = extensionObj?.RTEPlugin; let embeddedData: ExtractedContentstackEmbeddedData | null = null; const jwtLike = window.name.split("__").find((str) => str.startsWith("ey")); if (jwtLike) { embeddedData = extractContentstackEmbeddedData(jwtLike); if (embeddedData?.plugin) { loadIV(embeddedData.plugin); } } if (!RTE) return; const ImageVault = RTE("ImageVault", (rte) => { if (rte) { if (!ivloaded) { // Loading failed via embedded data above, try again with data inside RTE // @ts-expect-error: incorrect typings, requestProps is available at runtime const extensions = rte._adv.editor.requestProps .extensions as ContentstackPluginDefinition[]; const plugin = extensions.find(findThisPlugin); if (plugin) { loadIV(plugin); } } } return { title: "Choose assets from ImageVault", icon: , render: (props) => { if (rte) { return ( {props.children} ); } else { console.error("No instance of RTE found"); return (

An unexpected error occured, see console for more info.

); } }, display: ["toolbar"], elementType: ["void"], }; }); ImageVault.on("exec", async (rte) => { if (rte) { const savedSelection = rte?.selection?.get() ?? undefined; // @ts-expect-error: Added at runtime const appConfig = await rte.getConfig(); // This is the configuration for this instance of the plugin. // You edit this in the content types settings RTE plugin section. // @ts-expect-error: Added at runtime const pluginConfig = await rte.getFieldConfig(); const config = { ...appConfig, ...pluginConfig, }; openImageVault({ config, entryData: { //TODO: Add support for branches branch: embeddedData ? embeddedData.contentType.uid : "main", contentTypeUid: embeddedData ? embeddedData.contentType.uid : "Unknown", locale: embeddedData ? embeddedData.entry.locale : ("unknown" as Lang), stackApiKey: embeddedData ? embeddedData.stack.apiKey : "unknown", title: embeddedData ? embeddedData.entry.title : "Untitled", uid: embeddedData ? embeddedData.entry.uid : "unknown", }, onSuccess(result) { rte.insertNode( { // @ts-expect-error: incorrect typings type: "ImageVault", attrs: result, uid: crypto.randomUUID(), children: [{ text: "" }], }, { at: savedSelection } ); }, }); } }); return { ImageVault, }; } catch (e) { console.error({ e }); } } export default init();