From 3d136775e25dd387bb09b462089b3e87b48a9275 Mon Sep 17 00:00:00 2001 From: sebseb7 Date: Sun, 20 Jul 2025 11:53:27 +0200 Subject: [PATCH] feat: enhance image loading and socket handling in Product and Images components, and update prerender logic in App and ProductDetailPage --- prerender-single-product.cjs | 2 +- prerender/renderer.cjs | 9 ++- src/App.js | 95 +++++++++++++++++++++-------- src/PrerenderProduct.js | 63 +++++++------------ src/components/CartItem.js | 2 +- src/components/Images.js | 25 ++++++-- src/components/Product.js | 68 +++++++++++++++------ src/components/ProductDetailPage.js | 19 +++++- src/components/header/SearchBar.js | 2 +- 9 files changed, 188 insertions(+), 97 deletions(-) diff --git a/prerender-single-product.cjs b/prerender-single-product.cjs index 9788079..802c404 100644 --- a/prerender-single-product.cjs +++ b/prerender-single-product.cjs @@ -57,7 +57,7 @@ const config = require("./prerender/config.cjs"); const shopConfig = require("./src/config.js").default; const { renderPage } = require("./prerender/renderer.cjs"); const { generateProductMetaTags, generateProductJsonLd } = require("./prerender/seo.cjs"); -const { fetchProductDetails } = require("./prerender/data-fetching.cjs"); +const { fetchProductDetails, saveProductImages } = require("./prerender/data-fetching.cjs"); // Import product component const PrerenderProduct = require("./src/PrerenderProduct.js").default; diff --git a/prerender/renderer.cjs b/prerender/renderer.cjs index 07aac0e..42e7d99 100644 --- a/prerender/renderer.cjs +++ b/prerender/renderer.cjs @@ -178,11 +178,16 @@ const renderPage = ( const prerenderFallbackScript = ` `; @@ -239,8 +244,10 @@ const renderPage = ( let newHtml; if (rootDivRegex.test(template)) { + if (!suppressLogs) console.log(` 📝 Root div found, replacing with ${renderedMarkup.length} chars of markup`); newHtml = template.replace(rootDivRegex, replacementHtml); } else { + if (!suppressLogs) console.log(` ⚠️ No root div found, appending to body`); newHtml = template.replace("", `${replacementHtml}`); } diff --git a/src/App.js b/src/App.js index bc928fe..65b7884 100644 --- a/src/App.js +++ b/src/App.js @@ -32,11 +32,15 @@ import Header from "./components/Header.js"; import Footer from "./components/Footer.js"; import MainPageLayout from "./components/MainPageLayout.js"; -// Lazy load all route components to reduce initial bundle size -const Content = lazy(() => import(/* webpackChunkName: "content" */ "./components/Content.js")); -const ProductDetailWithSocket = lazy(() => import(/* webpackChunkName: "product-detail" */ "./components/ProductDetailWithSocket.js")); -const ProfilePageWithSocket = lazy(() => import(/* webpackChunkName: "profile" */ "./pages/ProfilePage.js")); -const ResetPassword = lazy(() => import(/* webpackChunkName: "reset-password" */ "./pages/ResetPassword.js")); +// TEMPORARILY DISABLE ALL LAZY LOADING TO ELIMINATE CircularProgress +import Content from "./components/Content.js"; +import ProductDetailWithSocket from "./components/ProductDetailWithSocket.js"; +import ProfilePageWithSocket from "./pages/ProfilePage.js"; +import ResetPassword from "./pages/ResetPassword.js"; +// const Content = lazy(() => import(/* webpackChunkName: "content" */ "./components/Content.js")); +// const ProductDetailWithSocket = lazy(() => import(/* webpackChunkName: "product-detail" */ "./components/ProductDetailWithSocket.js")); +// const ProfilePageWithSocket = lazy(() => import(/* webpackChunkName: "profile" */ "./pages/ProfilePage.js")); +// const ResetPassword = lazy(() => import(/* webpackChunkName: "reset-password" */ "./pages/ResetPassword.js")); // Lazy load admin pages - only loaded when admin users access them const AdminPage = lazy(() => import(/* webpackChunkName: "admin" */ "./pages/AdminPage.js")); @@ -201,16 +205,25 @@ const AppContent = ({ currentTheme, onThemeChange }) => {
- - + // Use prerender fallback if available, otherwise show loading spinner + typeof window !== "undefined" && window.__PRERENDER_FALLBACK__ ? ( +
+ ) : ( + + + + ) }> @@ -280,7 +293,17 @@ const AppContent = ({ currentTheme, onThemeChange }) => { {/* Conditionally render the Chat Assistant */} {isChatOpen && ( - }> + + ) : ( + + ) + }> { {/* Development-only Theme Customizer Dialog */} {isDevelopment && isThemeCustomizerOpen && ( - }> + + ) : ( + + ) + }> setThemeCustomizerOpen(false)} @@ -382,16 +415,24 @@ const App = () => { - - + typeof window !== "undefined" && window.__PRERENDER_FALLBACK__ ? ( +
+ ) : ( + + + + ) } > { if(res.success){ window.tinyPicCache[picid] = URL.createObjectURL(new Blob([res.imageBuffer], { type: 'image/jpeg' })); diff --git a/src/components/Images.js b/src/components/Images.js index baca1b9..5a25788 100644 --- a/src/components/Images.js +++ b/src/components/Images.js @@ -12,7 +12,7 @@ import LoupeIcon from '@mui/icons-material/Loupe'; class Images extends Component { constructor(props) { super(props); - this.state = { mainPic:0,pics:[]}; + this.state = { mainPic:0,pics:[], needsSocketRetry: false }; } componentDidMount () { @@ -22,6 +22,15 @@ class Images extends Component { if (prevProps.fullscreenOpen !== this.props.fullscreenOpen) { this.updatePics(); } + + // Retry loading images if socket just became available + const wasConnected = prevProps.socketB && prevProps.socketB.connected; + const isNowConnected = this.props.socketB && this.props.socketB.connected; + + if (!wasConnected && isNowConnected && this.state.needsSocketRetry) { + this.setState({ needsSocketRetry: false }); + this.updatePics(); + } } updatePics = (newMainPic = this.state.mainPic) => { @@ -49,10 +58,10 @@ class Images extends Component { pics.push(window.smallPicCache[bildId]); this.loadPic(this.props.fullscreenOpen ? 'large' : 'medium',bildId,newMainPic); }else if(window.tinyPicCache[bildId]){ - pics.push(bildId); + pics.push(window.tinyPicCache[bildId]); this.loadPic(this.props.fullscreenOpen ? 'large' : 'medium',bildId,newMainPic); }else{ - pics.push(bildId); + pics.push(`/assets/images/prod${bildId}.jpg`); this.loadPic(this.props.fullscreenOpen ? 'large' : 'medium',bildId,newMainPic); } }else{ @@ -67,7 +76,8 @@ class Images extends Component { } } } - console.log('pics',pics); + console.log('DEBUG: pics array contents:', pics); + console.log('DEBUG: pics array types:', pics.map(p => typeof p + ': ' + p)); this.setState({ pics, mainPic: newMainPic }); }else{ if(this.state.pics.length > 0) this.setState({ pics:[], mainPic: newMainPic }); @@ -75,6 +85,13 @@ class Images extends Component { } loadPic = (size,bildId,index) => { + // Check if socketB is available and connected before emitting + if (!this.props.socketB || !this.props.socketB.connected) { + console.log("Images: socketB not available, will retry when connected"); + this.setState({ needsSocketRetry: true }); + return; + } + this.props.socketB.emit('getPic', { bildId, size }, (res) => { if(res.success){ const url = URL.createObjectURL(new Blob([res.imageBuffer], { type: 'image/jpeg' })); diff --git a/src/components/Product.js b/src/components/Product.js index afcc304..a6a0afd 100644 --- a/src/components/Product.js +++ b/src/components/Product.js @@ -28,25 +28,16 @@ class Product extends Component { }else{ this.state = {image: null, loading: true, error: false}; console.log("Product: Fetching image from socketB", this.props.socketB); - this.props.socketB.emit('getPic', { bildId, size:'small' }, (res) => { - if(res.success){ - window.smallPicCache[bildId] = URL.createObjectURL(new Blob([res.imageBuffer], { type: 'image/jpeg' })); - if (this._isMounted) { - this.setState({image: window.smallPicCache[bildId], loading: false}); - } else { - this.state.image = window.smallPicCache[bildId]; - this.state.loading = false; - } - }else{ - console.log('Fehler beim Laden des Bildes:', res); - if (this._isMounted) { - this.setState({error: true, loading: false}); - } else { - this.state.error = true; - this.state.loading = false; - } - } - }) + + // Check if socketB is available and connected before emitting + if (this.props.socketB && this.props.socketB.connected) { + this.loadImage(bildId); + } else { + // Socket not available, set error state or wait + console.log("Product: socketB not available, will retry when connected"); + this.state.error = true; + this.state.loading = false; + } } }else{ this.state = {image: null, loading: false, error: false}; @@ -57,6 +48,45 @@ class Product extends Component { this._isMounted = true; } + componentDidUpdate(prevProps) { + // Retry loading image if socket just became available + const wasConnected = prevProps.socketB && prevProps.socketB.connected; + const isNowConnected = this.props.socketB && this.props.socketB.connected; + + if (!wasConnected && isNowConnected && this.state.error && this.props.pictureList) { + // Socket just connected and we had an error, retry loading + const bildId = this.props.pictureList.split(',')[0]; + if (!window.smallPicCache[bildId]) { + this.setState({loading: true, error: false}); + this.loadImage(bildId); + } + } + } + + loadImage = (bildId) => { + if (this.props.socketB && this.props.socketB.connected) { + this.props.socketB.emit('getPic', { bildId, size:'small' }, (res) => { + if(res.success){ + window.smallPicCache[bildId] = URL.createObjectURL(new Blob([res.imageBuffer], { type: 'image/jpeg' })); + if (this._isMounted) { + this.setState({image: window.smallPicCache[bildId], loading: false}); + } else { + this.state.image = window.smallPicCache[bildId]; + this.state.loading = false; + } + }else{ + console.log('Fehler beim Laden des Bildes:', res); + if (this._isMounted) { + this.setState({error: true, loading: false}); + } else { + this.state.error = true; + this.state.loading = false; + } + } + }); + } + } + componentWillUnmount() { this._isMounted = false; } diff --git a/src/components/ProductDetailPage.js b/src/components/ProductDetailPage.js index 1041485..eaa6396 100644 --- a/src/components/ProductDetailPage.js +++ b/src/components/ProductDetailPage.js @@ -356,7 +356,6 @@ class ProductDetailPage extends Component { if (!this.props.socket || !this.props.socket.connected) { // Socket not connected yet, but don't show error immediately on first load // The componentDidUpdate will retry when socket connects - console.log("Socket not connected yet, waiting for connection to load product data"); return; } @@ -538,11 +537,27 @@ class ProductDetailPage extends Component { } }; - render() { + render() { const { product, loading, error, attributeImages, isSteckling, attributes, komponentenLoaded, komponentenData, komponentenImages, totalKomponentenPrice, totalSavings } = this.state; + // Debug alerts removed + + + if (loading) { + // Check if prerender fallback is available + if (typeof window !== "undefined" && window.__PRERENDER_FALLBACK__) { + return ( +
+ ); + } + + // Fallback to loading message if no prerender content return ( { ), endAdornment: ( - {loadingSuggestions && } + {loadingSuggestions && }