refactor: integrate CategoryService into SharedCarousel for improved category data management and enhance component structure
This commit is contained in:
@@ -1,275 +1,389 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React from "react";
|
||||
import Box from "@mui/material/Box";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import ChevronLeft from "@mui/icons-material/ChevronLeft";
|
||||
import ChevronRight from "@mui/icons-material/ChevronRight";
|
||||
import CategoryBox from "./CategoryBox.js";
|
||||
import { useCarousel } from "../contexts/CarouselContext.js";
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
|
||||
// Helper to process and set categories
|
||||
const processCategoryTree = (categoryTree) => {
|
||||
if (
|
||||
categoryTree &&
|
||||
categoryTree.id === 209 &&
|
||||
Array.isArray(categoryTree.children)
|
||||
) {
|
||||
return categoryTree.children;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
const ITEM_WIDTH = 130 + 16; // 130px width + 16px gap
|
||||
const AUTO_SCROLL_SPEED = 0.5; // px per frame (~60fps, so ~30px/sec)
|
||||
const AUTOSCROLL_RESTART_DELAY = 5000; // 5 seconds of inactivity before restarting autoscroll
|
||||
const SCROLLBAR_FLASH_DURATION = 3000; // 3 seconds to show the virtual scrollbar
|
||||
|
||||
// Check for cached data
|
||||
const getProductCache = () => {
|
||||
// Ensure cache exists
|
||||
if (typeof window !== "undefined") {
|
||||
window.productCache = window.productCache || {};
|
||||
return window.productCache;
|
||||
}
|
||||
if (typeof global !== "undefined" && global.window) {
|
||||
global.window.productCache = global.window.productCache || {};
|
||||
return global.window.productCache;
|
||||
}
|
||||
return {};
|
||||
};
|
||||
class SharedCarousel extends React.Component {
|
||||
_isMounted = false;
|
||||
categories = [];
|
||||
originalCategories = [];
|
||||
animationFrame = null;
|
||||
autoScrollActive = true;
|
||||
translateX = 0;
|
||||
inactivityTimer = null;
|
||||
scrollbarTimer = null;
|
||||
|
||||
// Initialize categories
|
||||
const initializeCategories = (language = 'en') => {
|
||||
const productCache = getProductCache();
|
||||
|
||||
// The cache is PRERENDERED - always use it first!
|
||||
// Try language-specific cache first, then fallback to default
|
||||
const categoryTree = productCache?.[`categoryTree_209_${language}`]?.categoryTree ||
|
||||
productCache?.["categoryTree_209"]?.categoryTree;
|
||||
|
||||
return categoryTree ? processCategoryTree(categoryTree) : [];
|
||||
};
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const { i18n } = props;
|
||||
const categories = window.categoryService.getSync(209);
|
||||
|
||||
const SharedCarousel = () => {
|
||||
const { carouselRef, filteredCategories, setFilteredCategories, moveCarousel } = useCarousel();
|
||||
const { t, i18n } = useTranslation();
|
||||
const [rootCategories, setRootCategories] = useState([]);
|
||||
const [currentLanguage, setCurrentLanguage] = useState(i18n.language || 'de');
|
||||
|
||||
useEffect(() => {
|
||||
const initialCategories = initializeCategories(currentLanguage);
|
||||
setRootCategories(initialCategories);
|
||||
}, [currentLanguage]);
|
||||
|
||||
// Also listen for i18n ready state
|
||||
useEffect(() => {
|
||||
if (i18n.isInitialized && i18n.language !== currentLanguage) {
|
||||
setCurrentLanguage(i18n.language);
|
||||
}
|
||||
}, [i18n.isInitialized, i18n.language, currentLanguage]);
|
||||
|
||||
// Listen for language changes
|
||||
useEffect(() => {
|
||||
const handleLanguageChange = (lng) => {
|
||||
setCurrentLanguage(lng);
|
||||
// Clear categories to force refetch
|
||||
setRootCategories([]);
|
||||
this.state = {
|
||||
categories: categories && categories.children && categories.children.length > 0 ? [...categories.children, ...categories.children] : [],
|
||||
currentLanguage: (i18n && i18n.language) || 'de',
|
||||
showScrollbar: false,
|
||||
};
|
||||
|
||||
i18n.on('languageChanged', handleLanguageChange);
|
||||
return () => {
|
||||
i18n.off('languageChanged', handleLanguageChange);
|
||||
};
|
||||
}, [i18n]);
|
||||
|
||||
useEffect(() => {
|
||||
// The cache is PRERENDERED - check it first
|
||||
const cache = getProductCache();
|
||||
console.log('SharedCarousel: Checking prerendered cache', cache);
|
||||
|
||||
// Try language-specific cache first, then fallback to default
|
||||
const categoryTree = cache?.[`categoryTree_209_${currentLanguage}`]?.categoryTree ||
|
||||
cache?.["categoryTree_209"]?.categoryTree;
|
||||
|
||||
// Use cache if available
|
||||
if (categoryTree) {
|
||||
console.log('SharedCarousel: Using prerendered cache');
|
||||
setRootCategories(processCategoryTree(categoryTree));
|
||||
return;
|
||||
if(categories && categories.children && categories.children.length > 0) {
|
||||
this.originalCategories = categories.children;
|
||||
this.categories = [...categories.children, ...categories.children];
|
||||
this.startAutoScroll();
|
||||
}
|
||||
|
||||
// Only fetch if needed
|
||||
if (rootCategories.length === 0 && typeof window !== "undefined") {
|
||||
console.log('SharedCarousel: No prerendered cache, fetching from socket');
|
||||
window.socketManager.emit("categoryList", { categoryId: 209, language: currentLanguage, requestTranslation: true }, (response) => {
|
||||
if (response && response.success) {
|
||||
// Use translated data if available, otherwise fall back to original
|
||||
const categoryTreeToUse = response.translation || response.categoryTree;
|
||||
|
||||
if (categoryTreeToUse) {
|
||||
// Store in cache with language-specific key
|
||||
if (!window.productCache) window.productCache = {};
|
||||
window.productCache[`categoryTree_209_${currentLanguage}`] = {
|
||||
categoryTree: categoryTreeToUse,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
const newCategories = categoryTreeToUse.children || [];
|
||||
setRootCategories(newCategories);
|
||||
}
|
||||
} else if (response && response.categoryTree) {
|
||||
// Fallback for old response format
|
||||
// Store in cache with language-specific key
|
||||
if (!window.productCache) window.productCache = {};
|
||||
window.productCache[`categoryTree_209_${currentLanguage}`] = {
|
||||
categoryTree: response.categoryTree,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
const newCategories = response.categoryTree.children || [];
|
||||
setRootCategories(newCategories);
|
||||
|
||||
this.carouselTrackRef = React.createRef();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._isMounted = true;
|
||||
if (!this.state.categories || this.state.categories.length === 0) {
|
||||
window.categoryService.get(209).then((response) => {
|
||||
if (this._isMounted && response.children && response.children.length > 0) {
|
||||
this.originalCategories = response.children;
|
||||
// Duplicate for seamless looping
|
||||
this.categories = [...response.children, ...response.children];
|
||||
this.setState({ categories: this.categories });
|
||||
this.startAutoScroll();
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [rootCategories.length, currentLanguage]);
|
||||
|
||||
useEffect(() => {
|
||||
const filtered = rootCategories.filter(
|
||||
(cat) => cat.id !== 689 && cat.id !== 706
|
||||
);
|
||||
setFilteredCategories(filtered);
|
||||
}, [rootCategories, setFilteredCategories]);
|
||||
|
||||
// Create duplicated array for seamless scrolling
|
||||
const displayCategories = [...filteredCategories, ...filteredCategories];
|
||||
|
||||
if (filteredCategories.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Box sx={{ mt: 3 }}>
|
||||
<Typography
|
||||
variant="h4"
|
||||
component="h1"
|
||||
sx={{
|
||||
mb: 2,
|
||||
fontFamily: "SwashingtonCP",
|
||||
color: "primary.main",
|
||||
textAlign: "center",
|
||||
textShadow: "3px 3px 10px rgba(0, 0, 0, 0.4)"
|
||||
}}
|
||||
>
|
||||
{t('navigation.categories')}
|
||||
</Typography>
|
||||
componentWillUnmount() {
|
||||
this._isMounted = false;
|
||||
this.stopAutoScroll();
|
||||
this.clearInactivityTimer();
|
||||
this.clearScrollbarTimer();
|
||||
}
|
||||
|
||||
<div
|
||||
className="carousel-wrapper"
|
||||
startAutoScroll = () => {
|
||||
this.autoScrollActive = true;
|
||||
if (!this.animationFrame) {
|
||||
this.animationFrame = requestAnimationFrame(this.handleAutoScroll);
|
||||
}
|
||||
};
|
||||
|
||||
stopAutoScroll = () => {
|
||||
this.autoScrollActive = false;
|
||||
if (this.animationFrame) {
|
||||
cancelAnimationFrame(this.animationFrame);
|
||||
this.animationFrame = null;
|
||||
}
|
||||
};
|
||||
|
||||
clearInactivityTimer = () => {
|
||||
if (this.inactivityTimer) {
|
||||
clearTimeout(this.inactivityTimer);
|
||||
this.inactivityTimer = null;
|
||||
}
|
||||
};
|
||||
|
||||
clearScrollbarTimer = () => {
|
||||
if (this.scrollbarTimer) {
|
||||
clearTimeout(this.scrollbarTimer);
|
||||
this.scrollbarTimer = null;
|
||||
}
|
||||
};
|
||||
|
||||
startInactivityTimer = () => {
|
||||
this.clearInactivityTimer();
|
||||
this.inactivityTimer = setTimeout(() => {
|
||||
if (this._isMounted) {
|
||||
this.startAutoScroll();
|
||||
}
|
||||
}, AUTOSCROLL_RESTART_DELAY);
|
||||
};
|
||||
|
||||
showScrollbarFlash = () => {
|
||||
this.clearScrollbarTimer();
|
||||
this.setState({ showScrollbar: true });
|
||||
|
||||
this.scrollbarTimer = setTimeout(() => {
|
||||
if (this._isMounted) {
|
||||
this.setState({ showScrollbar: false });
|
||||
}
|
||||
}, SCROLLBAR_FLASH_DURATION);
|
||||
};
|
||||
|
||||
handleAutoScroll = () => {
|
||||
if (!this.autoScrollActive || this.originalCategories.length === 0) return;
|
||||
|
||||
this.translateX -= AUTO_SCROLL_SPEED;
|
||||
this.updateTrackTransform();
|
||||
|
||||
const originalItemCount = this.originalCategories.length;
|
||||
const maxScroll = ITEM_WIDTH * originalItemCount;
|
||||
|
||||
// Check if we've scrolled past the first set of items
|
||||
if (Math.abs(this.translateX) >= maxScroll) {
|
||||
// Reset to beginning seamlessly
|
||||
this.translateX = 0;
|
||||
this.updateTrackTransform();
|
||||
}
|
||||
|
||||
this.animationFrame = requestAnimationFrame(this.handleAutoScroll);
|
||||
};
|
||||
|
||||
updateTrackTransform = () => {
|
||||
if (this.carouselTrackRef.current) {
|
||||
this.carouselTrackRef.current.style.transform = `translateX(${this.translateX}px)`;
|
||||
}
|
||||
};
|
||||
|
||||
handleLeftClick = () => {
|
||||
this.stopAutoScroll();
|
||||
this.scrollBy(1);
|
||||
this.showScrollbarFlash();
|
||||
this.startInactivityTimer();
|
||||
};
|
||||
|
||||
handleRightClick = () => {
|
||||
this.stopAutoScroll();
|
||||
this.scrollBy(-1);
|
||||
this.showScrollbarFlash();
|
||||
this.startInactivityTimer();
|
||||
};
|
||||
|
||||
scrollBy = (direction) => {
|
||||
if (this.originalCategories.length === 0) return;
|
||||
|
||||
// direction: 1 = left (scroll content right), -1 = right (scroll content left)
|
||||
const originalItemCount = this.originalCategories.length;
|
||||
const maxScroll = ITEM_WIDTH * originalItemCount;
|
||||
|
||||
this.translateX += direction * ITEM_WIDTH;
|
||||
|
||||
// Handle wrap-around when scrolling left (positive translateX)
|
||||
if (this.translateX > 0) {
|
||||
this.translateX = -(maxScroll - ITEM_WIDTH);
|
||||
}
|
||||
// Handle wrap-around when scrolling right (negative translateX beyond limit)
|
||||
else if (Math.abs(this.translateX) >= maxScroll) {
|
||||
this.translateX = 0;
|
||||
}
|
||||
|
||||
this.updateTrackTransform();
|
||||
|
||||
// Force scrollbar to update immediately after wrap-around
|
||||
if (this.state.showScrollbar) {
|
||||
this.forceUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
renderVirtualScrollbar = () => {
|
||||
if (!this.state.showScrollbar || this.originalCategories.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const originalItemCount = this.originalCategories.length;
|
||||
const viewportWidth = 1080; // carousel container max-width
|
||||
const itemsInView = Math.floor(viewportWidth / ITEM_WIDTH);
|
||||
|
||||
// Calculate which item is currently at the left edge (first visible)
|
||||
// Map translateX directly to item index using the same logic as scrollBy
|
||||
let currentItemIndex;
|
||||
|
||||
if (this.translateX === 0) {
|
||||
// At the beginning - item 0 is visible
|
||||
currentItemIndex = 0;
|
||||
} else if (this.translateX > 0) {
|
||||
// Wrapped to show end items (this happens when scrolling left past beginning)
|
||||
const maxScroll = ITEM_WIDTH * originalItemCount;
|
||||
const effectivePosition = maxScroll + this.translateX;
|
||||
currentItemIndex = Math.floor(effectivePosition / ITEM_WIDTH);
|
||||
} else {
|
||||
// Normal negative scrolling - calculate which item is at left edge
|
||||
currentItemIndex = Math.floor(Math.abs(this.translateX) / ITEM_WIDTH);
|
||||
}
|
||||
|
||||
// Ensure we stay within bounds
|
||||
currentItemIndex = Math.max(0, Math.min(currentItemIndex, originalItemCount - 1));
|
||||
|
||||
// Calculate scrollbar position: 0% when item 0 is first visible, 100% when last item is first visible
|
||||
const lastPossibleFirstItem = Math.max(0, originalItemCount - itemsInView);
|
||||
const thumbPosition = lastPossibleFirstItem > 0 ? Math.min((currentItemIndex / lastPossibleFirstItem) * 100, 100) : 0;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="virtual-scrollbar"
|
||||
style={{
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
width: '100%',
|
||||
maxWidth: '1200px',
|
||||
margin: '0 auto',
|
||||
padding: '0 20px',
|
||||
boxSizing: 'border-box'
|
||||
position: 'absolute',
|
||||
bottom: '5px',
|
||||
left: '50%',
|
||||
transform: 'translateX(-50%)',
|
||||
width: '200px',
|
||||
height: '4px',
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.1)',
|
||||
borderRadius: '2px',
|
||||
zIndex: 1000,
|
||||
opacity: this.state.showScrollbar ? 1 : 0,
|
||||
transition: 'opacity 0.3s ease-in-out'
|
||||
}}
|
||||
>
|
||||
{/* Left Arrow */}
|
||||
<IconButton
|
||||
onClick={() => moveCarousel("left")}
|
||||
aria-label="Vorherige Kategorien anzeigen"
|
||||
<div
|
||||
className="scrollbar-thumb"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '8px',
|
||||
transform: 'translateY(-50%)',
|
||||
zIndex: 1200,
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.9)',
|
||||
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)',
|
||||
width: '48px',
|
||||
height: '48px',
|
||||
borderRadius: '50%'
|
||||
top: '0',
|
||||
left: `${thumbPosition}%`,
|
||||
width: '20px',
|
||||
height: '4px',
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||
borderRadius: '2px',
|
||||
transform: 'translateX(-50%)',
|
||||
transition: 'left 0.2s ease-out'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { t } = this.props;
|
||||
const { categories } = this.state;
|
||||
|
||||
if(!categories || categories.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Box sx={{ mt: 3 }}>
|
||||
<Typography
|
||||
variant="h4"
|
||||
component="h1"
|
||||
sx={{
|
||||
mb: 2,
|
||||
fontFamily: "SwashingtonCP",
|
||||
color: "primary.main",
|
||||
textAlign: "center",
|
||||
textShadow: "3px 3px 10px rgba(0, 0, 0, 0.4)"
|
||||
}}
|
||||
>
|
||||
<ChevronLeft />
|
||||
</IconButton>
|
||||
{t('navigation.categories')}
|
||||
</Typography>
|
||||
|
||||
{/* Right Arrow */}
|
||||
<IconButton
|
||||
onClick={() => moveCarousel("right")}
|
||||
aria-label="Nächste Kategorien anzeigen"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
right: '8px',
|
||||
transform: 'translateY(-50%)',
|
||||
zIndex: 1200,
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.9)',
|
||||
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)',
|
||||
width: '48px',
|
||||
height: '48px',
|
||||
borderRadius: '50%'
|
||||
}}
|
||||
>
|
||||
<ChevronRight />
|
||||
</IconButton>
|
||||
|
||||
<div
|
||||
className="carousel-container"
|
||||
<div
|
||||
className="carousel-wrapper"
|
||||
style={{
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
padding: '20px 0',
|
||||
width: '100%',
|
||||
maxWidth: '1080px',
|
||||
maxWidth: '1200px',
|
||||
margin: '0 auto',
|
||||
zIndex: 1,
|
||||
padding: '0 20px',
|
||||
boxSizing: 'border-box'
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="home-carousel-track"
|
||||
ref={carouselRef}
|
||||
{/* Left Arrow */}
|
||||
<IconButton
|
||||
aria-label="Vorherige Kategorien anzeigen"
|
||||
onClick={this.handleLeftClick}
|
||||
style={{
|
||||
display: 'flex',
|
||||
gap: '16px',
|
||||
transition: 'none',
|
||||
alignItems: 'flex-start',
|
||||
width: 'fit-content',
|
||||
overflow: 'visible',
|
||||
position: 'relative',
|
||||
transform: 'translateX(0px)',
|
||||
margin: '0 auto'
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '8px',
|
||||
transform: 'translateY(-50%)',
|
||||
zIndex: 1200,
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.9)',
|
||||
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)',
|
||||
width: '48px',
|
||||
height: '48px',
|
||||
borderRadius: '50%'
|
||||
}}
|
||||
>
|
||||
{displayCategories.map((category, index) => (
|
||||
<div
|
||||
key={`${category.id}-${index}`}
|
||||
className="carousel-item"
|
||||
style={{
|
||||
flex: '0 0 130px',
|
||||
width: '130px',
|
||||
maxWidth: '130px',
|
||||
minWidth: '130px',
|
||||
height: '130px',
|
||||
maxHeight: '130px',
|
||||
minHeight: '130px',
|
||||
boxSizing: 'border-box',
|
||||
position: 'relative'
|
||||
}}
|
||||
>
|
||||
<CategoryBox
|
||||
id={category.id}
|
||||
name={category.name}
|
||||
seoName={category.seoName}
|
||||
image={category.image}
|
||||
bgcolor={category.bgcolor}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
<ChevronLeft />
|
||||
</IconButton>
|
||||
|
||||
{/* Right Arrow */}
|
||||
<IconButton
|
||||
aria-label="Nächste Kategorien anzeigen"
|
||||
onClick={this.handleRightClick}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
right: '8px',
|
||||
transform: 'translateY(-50%)',
|
||||
zIndex: 1200,
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.9)',
|
||||
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)',
|
||||
width: '48px',
|
||||
height: '48px',
|
||||
borderRadius: '50%'
|
||||
}}
|
||||
>
|
||||
<ChevronRight />
|
||||
</IconButton>
|
||||
|
||||
<div
|
||||
className="carousel-container"
|
||||
style={{
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
padding: '20px 0',
|
||||
width: '100%',
|
||||
maxWidth: '1080px',
|
||||
margin: '0 auto',
|
||||
zIndex: 1,
|
||||
boxSizing: 'border-box'
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="home-carousel-track"
|
||||
ref={this.carouselTrackRef}
|
||||
style={{
|
||||
display: 'flex',
|
||||
gap: '16px',
|
||||
transition: 'none',
|
||||
alignItems: 'flex-start',
|
||||
width: 'fit-content',
|
||||
overflow: 'visible',
|
||||
position: 'relative',
|
||||
transform: 'translateX(0px)',
|
||||
margin: '0 auto'
|
||||
}}
|
||||
>
|
||||
{categories.map((category, index) => (
|
||||
<div
|
||||
key={`${category.id}-${index}`}
|
||||
className="carousel-item"
|
||||
style={{
|
||||
flex: '0 0 130px',
|
||||
width: '130px',
|
||||
maxWidth: '130px',
|
||||
minWidth: '130px',
|
||||
height: '130px',
|
||||
maxHeight: '130px',
|
||||
minHeight: '130px',
|
||||
boxSizing: 'border-box',
|
||||
position: 'relative'
|
||||
}}
|
||||
>
|
||||
<CategoryBox
|
||||
id={category.id}
|
||||
name={category.name}
|
||||
seoName={category.seoName}
|
||||
image={category.image}
|
||||
bgcolor={category.bgcolor}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Virtual Scrollbar */}
|
||||
{this.renderVirtualScrollbar()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default SharedCarousel;
|
||||
export default withTranslation()(SharedCarousel);
|
||||
|
||||
@@ -4,6 +4,7 @@ import "./index.css";
|
||||
import App from "./App.js";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
import "./services/SocketManager.js";
|
||||
import "./services/CategoryService.js";
|
||||
|
||||
// Create a wrapper component with our class-based GoogleAuthProvider
|
||||
// This avoids the "Invalid hook call" error from GoogleOAuthProvider
|
||||
|
||||
40
src/services/CategoryService.js
Normal file
40
src/services/CategoryService.js
Normal file
@@ -0,0 +1,40 @@
|
||||
class CategoryService {
|
||||
constructor() {
|
||||
this.get = this.get.bind(this);
|
||||
}
|
||||
|
||||
getSync(categoryId, language = "de") {
|
||||
const cacheKey = `${categoryId}_${language}`;
|
||||
if (window.categoryCache && window.categoryCache[cacheKey]) {
|
||||
return window.categoryCache[cacheKey];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
get(categoryId, language = "de") {
|
||||
const cacheKey = `${categoryId}_${language}`;
|
||||
if (window.categoryCache && window.categoryCache[cacheKey]) {
|
||||
return Promise.resolve(window.categoryCache[cacheKey]);
|
||||
}
|
||||
|
||||
return new Promise((resolve) => {
|
||||
window.socketManager.emit("categoryList", {categoryId: categoryId, language: language}, (response) => {
|
||||
console.log("CategoryService", cacheKey);
|
||||
if (response.categoryTree) {
|
||||
if (!window.categoryCache) {
|
||||
window.categoryCache = {};
|
||||
}
|
||||
window.categoryCache[cacheKey] = response.categoryTree;
|
||||
resolve(response.categoryTree);
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const categoryService = new CategoryService();
|
||||
window.categoryService = categoryService;
|
||||
|
||||
export default categoryService;
|
||||
Reference in New Issue
Block a user