import variables from 'config/variables'; import { MARKETPLACE_URL } from 'config/constants'; import { memo, useEffect, useRef, useState } from 'react'; import { MdOutlineWifiOff } from 'react-icons/md'; import Modal from 'react-modal'; import Tabs from 'components/Elements/MainModal/backend/Tabs'; import { useMarketplaceInstall } from 'features/marketplace/components/hooks/useMarketplaceInstall'; import Lightbox from 'features/marketplace/components/Elements/Lightbox/Lightbox'; function DiscoverContent({ category, onBreadcrumbsChange }) { const iframeRef = useRef(null); const [isLoading, setIsLoading] = useState(true); const [showLightbox, setShowLightbox] = useState(false); const [lightboxImg, setLightboxImg] = useState(null); const { installItem, uninstallItem } = useMarketplaceInstall(); // Check for offline mode const offlineMode = localStorage.getItem('offlineMode') === 'true'; // Check for preview mode const isPreviewMode = localStorage.getItem('showWelcome') === 'true'; const previewParam = isPreviewMode ? '&preview=true' : ''; const isOffline = navigator.onLine === false || offlineMode; // Clear breadcrumbs when component unmounts (navigating away from discover) useEffect(() => { return () => { if (onBreadcrumbsChange) { onBreadcrumbsChange([]); } }; }, [onBreadcrumbsChange]); useEffect(() => { // Show loader when category changes setIsLoading(true); // Clear breadcrumbs when navigating to a new category if (onBreadcrumbsChange) { onBreadcrumbsChange([]); } // Get current theme const theme = localStorage.getItem('theme') || 'auto'; const themeParam = `&theme=${theme}`; // Update iframe src with category if (iframeRef.current) { // Collections use path-based routing, others use query params if (category === 'collections') { iframeRef.current.src = `${MARKETPLACE_URL}/collections?embed=true${previewParam}${themeParam}`; } else { iframeRef.current.src = `${MARKETPLACE_URL}?embed=true&type=${category}${previewParam}${themeParam}`; } } }, [category, onBreadcrumbsChange, previewParam]); useEffect(() => { // Check for item parameter in URL and update iframe const checkAndLoadItem = () => { const hash = window.location.hash; const urlParams = new URLSearchParams(hash.split('?')[1]); const itemId = urlParams.get('item'); if (itemId && iframeRef.current) { setIsLoading(true); // Get current theme const theme = localStorage.getItem('theme') || 'auto'; const themeParam = `&theme=${theme}`; // Get item from localStorage to determine type const installed = JSON.parse(localStorage.getItem('installed')) || []; const item = installed.find((i) => i.name === itemId); if (item) { // Map item type to URL path const pathMap = { photo_packs: 'packs', quote_packs: 'packs', preset_settings: 'presets', }; const pathSegment = pathMap[item.type] || 'packs'; const itemIdToUse = item.id || itemId; // Navigate to /packs/{id} or /presets/{id} iframeRef.current.src = `${MARKETPLACE_URL}/${pathSegment}/${itemIdToUse}?embed=true${previewParam}${themeParam}`; } else { // Fallback if item not found in localStorage iframeRef.current.src = `${MARKETPLACE_URL}/packs/${itemId}?embed=true${previewParam}${themeParam}`; } } }; // Check on mount and when category changes checkAndLoadItem(); // Listen for hash changes and popstate (from history.pushState) window.addEventListener('hashchange', checkAndLoadItem); window.addEventListener('popstate', checkAndLoadItem); return () => { window.removeEventListener('hashchange', checkAndLoadItem); window.removeEventListener('popstate', checkAndLoadItem); }; }, [category, previewParam]); useEffect(() => { // Listen for postMessage events from the iframe const handleMessage = (event) => { // Verify the origin if needed const marketplaceOrigin = new URL(MARKETPLACE_URL).origin; if (event.origin !== marketplaceOrigin) { return; } const { type, payload } = event.data; switch (type) { case 'marketplace:item:install': if (payload?.item) { installItem(payload.item.type, payload.item); // Send confirmation back to iframe if (iframeRef.current?.contentWindow) { iframeRef.current.contentWindow.postMessage( { type: 'marketplace:item:installed', payload: { id: payload.item.id || payload.item.name, installed: true }, }, MARKETPLACE_URL ); } } break; case 'marketplace:item:uninstall': if (payload?.item) { uninstallItem(payload.item.type, payload.item.name || payload.item.display_name); // Send confirmation back to iframe if (iframeRef.current?.contentWindow) { iframeRef.current.contentWindow.postMessage( { type: 'marketplace:item:installed', payload: { id: payload.item.id || payload.item.name, installed: false }, }, MARKETPLACE_URL ); } } break; case 'marketplace:item:check-installed': if (payload?.id) { // Check if item is installed const installed = JSON.parse(localStorage.getItem('installed')) || []; const isInstalled = installed.some((item) => item.id === payload.id); // Send status back to iframe if (iframeRef.current?.contentWindow) { iframeRef.current.contentWindow.postMessage( { type: 'marketplace:item:installed', payload: { id: payload.id, installed: isInstalled }, }, MARKETPLACE_URL ); } } break; case 'marketplace:breadcrumbs': if (payload?.breadcrumbs && onBreadcrumbsChange) { onBreadcrumbsChange(payload.breadcrumbs); } break; case 'marketplace:lightbox': if (payload?.photo) { setLightboxImg(payload.photo.url); setShowLightbox(true); } break; default: break; } }; window.addEventListener('message', handleMessage); return () => { window.removeEventListener('message', handleMessage); }; }, [installItem, uninstallItem, onBreadcrumbsChange]); const handleLoad = () => { setIsLoading(false); // Send theme to iframe after it loads if (iframeRef.current?.contentWindow) { const theme = localStorage.getItem('theme') || 'auto'; iframeRef.current.contentWindow.postMessage( { type: 'marketplace:theme', payload: { theme }, }, MARKETPLACE_URL ); } }; // Show offline error message if offline if (isOffline) { return (
{variables.getMessage('modals.main.marketplace.offline.description')}