refactor: enhance category data management in CategoryList and CategoryService by integrating async-mutex for improved concurrency control and simplifying state handling
This commit is contained in:
11
package-lock.json
generated
11
package-lock.json
generated
@@ -15,6 +15,7 @@
|
||||
"@mui/material": "^7.1.1",
|
||||
"@stripe/react-stripe-js": "^3.7.0",
|
||||
"@stripe/stripe-js": "^7.3.1",
|
||||
"async-mutex": "^0.5.0",
|
||||
"chart.js": "^4.5.0",
|
||||
"country-flag-icons": "^1.5.19",
|
||||
"html-react-parser": "^5.2.5",
|
||||
@@ -4200,6 +4201,15 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/async-mutex": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz",
|
||||
"integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
@@ -11829,7 +11839,6 @@
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"devOptional": true,
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/type-check": {
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
"@mui/material": "^7.1.1",
|
||||
"@stripe/react-stripe-js": "^3.7.0",
|
||||
"@stripe/stripe-js": "^7.3.1",
|
||||
"async-mutex": "^0.5.0",
|
||||
"chart.js": "^4.5.0",
|
||||
"country-flag-icons": "^1.5.19",
|
||||
"html-react-parser": "^5.2.5",
|
||||
|
||||
@@ -11,311 +11,129 @@ import CloseIcon from "@mui/icons-material/Close";
|
||||
import { withI18n } from "../../i18n/withTranslation.js";
|
||||
|
||||
class CategoryList extends Component {
|
||||
findCategoryById = (category, targetId) => {
|
||||
if (!category) return null;
|
||||
|
||||
if (category.seoName === targetId) {
|
||||
return category;
|
||||
}
|
||||
|
||||
if (category.children) {
|
||||
for (let child of category.children) {
|
||||
const found = this.findCategoryById(child, targetId);
|
||||
if (found) return found;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
getPathToCategory = (category, targetId, currentPath = []) => {
|
||||
if (!category) return null;
|
||||
|
||||
const newPath = [...currentPath, category];
|
||||
|
||||
if (category.seoName === targetId) {
|
||||
return newPath;
|
||||
}
|
||||
|
||||
if (category.children) {
|
||||
for (let child of category.children) {
|
||||
const found = this.getPathToCategory(child, targetId, newPath);
|
||||
if (found) return found;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
//const { i18n } = props;
|
||||
const categories = window.categoryService.getSync(209);
|
||||
|
||||
// Get current language from props (provided by withI18n HOC)
|
||||
const currentLanguage = props.languageContext?.currentLanguage || 'de';
|
||||
|
||||
// Check for cached data during SSR/initial render
|
||||
let initialState = {
|
||||
categoryTree: null,
|
||||
level1Categories: [], // Children of category 209 (Home) - always shown
|
||||
level2Categories: [], // Children of active level 1 category
|
||||
level3Categories: [], // Children of active level 2 category
|
||||
activePath: [], // Array of active category objects for each level
|
||||
fetchedCategories: false,
|
||||
mobileMenuOpen: false, // State for mobile collapsible menu
|
||||
currentLanguage: currentLanguage,
|
||||
this.state = {
|
||||
categories: categories && categories.children && categories.children.length > 0 ? categories.children : [],
|
||||
mobileMenuOpen: false,
|
||||
activeCategoryId: null // Will be set properly after categories are loaded
|
||||
};
|
||||
|
||||
// Try to get cached data for SSR
|
||||
try {
|
||||
// @note Check both global.window (SSR) and window (browser) for cache
|
||||
const productCache = (typeof global !== "undefined" && global.window && global.window.productCache) ||
|
||||
(typeof window !== "undefined" && window.productCache);
|
||||
|
||||
if (productCache) {
|
||||
const cacheKey = `categoryTree_209_${currentLanguage}`;
|
||||
const cachedData = productCache[cacheKey];
|
||||
if (cachedData && cachedData.categoryTree) {
|
||||
const { categoryTree, timestamp } = cachedData;
|
||||
const cacheAge = Date.now() - timestamp;
|
||||
const tenMinutes = 10 * 60 * 1000;
|
||||
|
||||
// Use cached data if it's fresh
|
||||
if (cacheAge < tenMinutes) {
|
||||
initialState.categoryTree = categoryTree;
|
||||
initialState.fetchedCategories = true;
|
||||
|
||||
// Process category tree to set up navigation
|
||||
const level1Categories =
|
||||
categoryTree && categoryTree.id === 209
|
||||
? categoryTree.children || []
|
||||
: [];
|
||||
initialState.level1Categories = level1Categories;
|
||||
|
||||
// Process active category path if needed
|
||||
if (props.activeCategoryId) {
|
||||
const activeCategory = this.findCategoryById(
|
||||
categoryTree,
|
||||
props.activeCategoryId
|
||||
);
|
||||
if (activeCategory) {
|
||||
const pathToActive = this.getPathToCategory(
|
||||
categoryTree,
|
||||
props.activeCategoryId
|
||||
);
|
||||
initialState.activePath = pathToActive
|
||||
? pathToActive.slice(1)
|
||||
: [];
|
||||
|
||||
if (initialState.activePath.length >= 1) {
|
||||
const level1Category = initialState.activePath[0];
|
||||
initialState.level2Categories = level1Category.children || [];
|
||||
}
|
||||
|
||||
if (initialState.activePath.length >= 2) {
|
||||
const level2Category = initialState.activePath[1];
|
||||
initialState.level3Categories = level2Category.children || [];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Error reading cache in constructor:", err);
|
||||
}
|
||||
|
||||
this.state = initialState;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.fetchCategories();
|
||||
if (!this.state.categories || this.state.categories.length === 0) {
|
||||
window.categoryService.get(209).then((response) => {
|
||||
console.log("response", response);
|
||||
if (response.children && response.children.length > 0) {
|
||||
this.setState({
|
||||
categories: response.children,
|
||||
activeCategoryId: this.getLevel1CategoryId(this.props.activeCategoryId)
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Categories are already loaded, set the initial activeCategoryId
|
||||
this.setState({
|
||||
activeCategoryId: this.getLevel1CategoryId(this.props.activeCategoryId)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
// Handle language changes
|
||||
const currentLanguage = this.props.languageContext?.currentLanguage || 'de';
|
||||
const prevLanguage = prevProps.languageContext?.currentLanguage || 'de';
|
||||
if (prevProps.activeCategoryId !== this.props.activeCategoryId) {
|
||||
//detect path here
|
||||
console.log("activeCategoryId updated", this.props.activeCategoryId);
|
||||
|
||||
// Get the active category ID of level 1 when prop is seoName
|
||||
const level1CategoryId = this.getLevel1CategoryId(this.props.activeCategoryId);
|
||||
|
||||
if (currentLanguage !== prevLanguage) {
|
||||
// Language changed, need to refetch categories
|
||||
this.setState({
|
||||
currentLanguage: currentLanguage,
|
||||
fetchedCategories: false,
|
||||
categoryTree: null,
|
||||
level1Categories: [],
|
||||
level2Categories: [],
|
||||
level3Categories: [],
|
||||
activePath: [],
|
||||
}, () => {
|
||||
this.fetchCategories();
|
||||
activeCategoryId: level1CategoryId
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// If activeCategoryId changes, update subcategories
|
||||
if (
|
||||
prevProps.activeCategoryId !== this.props.activeCategoryId &&
|
||||
this.state.categoryTree
|
||||
) {
|
||||
this.processCategoryTree(this.state.categoryTree);
|
||||
}
|
||||
}
|
||||
|
||||
fetchCategories = () => {
|
||||
if (this.state.fetchedCategories) {
|
||||
console.log('Categories already fetched, skipping');
|
||||
return;
|
||||
// Helper method to get level 1 category ID from seoName or numeric ID
|
||||
getLevel1CategoryId = (categoryIdOrSeoName) => {
|
||||
if (!categoryIdOrSeoName) return null;
|
||||
|
||||
// If it's already a numeric ID, check if it's a level 1 category
|
||||
if (typeof categoryIdOrSeoName === 'number') {
|
||||
// Check if this ID is directly under Home (209)
|
||||
const level1Category = this.state.categories.find(cat => cat.id === categoryIdOrSeoName);
|
||||
return level1Category ? categoryIdOrSeoName : this.findLevel1ParentId(categoryIdOrSeoName);
|
||||
}
|
||||
|
||||
const currentLanguage = this.state.currentLanguage || 'de';
|
||||
const windowObj = (typeof global !== "undefined" && global.window) ||
|
||||
(typeof window !== "undefined" && window);
|
||||
|
||||
// Ensure cache exists
|
||||
windowObj.productCache = windowObj.productCache || {};
|
||||
|
||||
// The cache is PRERENDERED - always use it first!
|
||||
console.log('CategoryList: Checking prerendered cache', windowObj.productCache);
|
||||
|
||||
// Use either language-specific or default cache
|
||||
const cacheKey = `categoryTree_209_${currentLanguage}`;
|
||||
const defaultCacheKey = "categoryTree_209";
|
||||
|
||||
// Try language-specific cache first, then fall back to default
|
||||
const categoryTree =
|
||||
windowObj.productCache[cacheKey]?.categoryTree ||
|
||||
windowObj.productCache[defaultCacheKey]?.categoryTree;
|
||||
|
||||
if (categoryTree) {
|
||||
console.log('CategoryList: Using prerendered cache');
|
||||
this.processCategoryTree(categoryTree);
|
||||
this.setState({ fetchedCategories: true });
|
||||
return;
|
||||
}
|
||||
|
||||
// Only fetch if no prerendered cache exists
|
||||
console.log('CategoryList: No prerendered cache, fetching from socket');
|
||||
windowObj.productCache[cacheKey] = { fetching: true, timestamp: Date.now() };
|
||||
this.setState({ fetchedCategories: true });
|
||||
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 global cache with timestamp
|
||||
try {
|
||||
const cacheKey = `categoryTree_209_${currentLanguage}`;
|
||||
if (windowObj && windowObj.productCache) {
|
||||
windowObj.productCache[cacheKey] = {
|
||||
categoryTree: categoryTreeToUse,
|
||||
timestamp: Date.now(),
|
||||
fetching: false,
|
||||
};
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Error writing to cache:", err);
|
||||
}
|
||||
this.processCategoryTree(categoryTreeToUse);
|
||||
} else {
|
||||
console.error('No category tree found in response');
|
||||
// Clear cache on error
|
||||
try {
|
||||
const cacheKey = `categoryTree_209_${currentLanguage}`;
|
||||
if (windowObj && windowObj.productCache) {
|
||||
windowObj.productCache[cacheKey] = {
|
||||
categoryTree: null,
|
||||
timestamp: Date.now(),
|
||||
fetching: false,
|
||||
};
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Error writing to cache:", err);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
categoryTree: null,
|
||||
level1Categories: [],
|
||||
level2Categories: [],
|
||||
level3Categories: [],
|
||||
activePath: [],
|
||||
});
|
||||
}
|
||||
} else {
|
||||
console.error('Failed to fetch categories:', response);
|
||||
try {
|
||||
const cacheKey = `categoryTree_209_${currentLanguage}`;
|
||||
if (windowObj && windowObj.productCache) {
|
||||
windowObj.productCache[cacheKey] = {
|
||||
categoryTree: null,
|
||||
timestamp: Date.now(),
|
||||
fetching: false,
|
||||
};
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Error writing to cache:", err);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
categoryTree: null,
|
||||
level1Categories: [],
|
||||
level2Categories: [],
|
||||
level3Categories: [],
|
||||
activePath: [],
|
||||
});
|
||||
// If it's a string (seoName), find the category and get its level 1 parent
|
||||
if (typeof categoryIdOrSeoName === 'string') {
|
||||
const categoryTreeCache = window.productCache && window.productCache['categoryTree_209'];
|
||||
if (!categoryTreeCache || !categoryTreeCache.categoryTree) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
processCategoryTree = (categoryTree) => {
|
||||
// Level 1 categories are always the children of category 209 (Home)
|
||||
const level1Categories =
|
||||
categoryTree && categoryTree.id === 209
|
||||
? categoryTree.children || []
|
||||
: [];
|
||||
const category = this.findCategoryBySeoName(categoryTreeCache.categoryTree, categoryIdOrSeoName);
|
||||
if (!category) return null;
|
||||
|
||||
// Build the navigation path and determine what to show at each level
|
||||
let level2Categories = [];
|
||||
let level3Categories = [];
|
||||
let activePath = [];
|
||||
// If the found category is already level 1 (direct child of Home)
|
||||
if (category.parentId === 209) {
|
||||
return category.id;
|
||||
}
|
||||
|
||||
if (this.props.activeCategoryId) {
|
||||
const activeCategory = this.findCategoryById(
|
||||
categoryTree,
|
||||
this.props.activeCategoryId
|
||||
);
|
||||
if (activeCategory) {
|
||||
// Build the path from root to active category
|
||||
const pathToActive = this.getPathToCategory(
|
||||
categoryTree,
|
||||
this.props.activeCategoryId
|
||||
);
|
||||
activePath = pathToActive.slice(1); // Remove root (209) from path
|
||||
// Find the level 1 parent of this category
|
||||
return this.findLevel1ParentId(category.id);
|
||||
}
|
||||
|
||||
// Determine what to show at each level based on the path depth
|
||||
if (activePath.length >= 1) {
|
||||
// Show children of the level 1 category
|
||||
const level1Category = activePath[0];
|
||||
level2Categories = level1Category.children || [];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (activePath.length >= 2) {
|
||||
// Show children of the level 2 category
|
||||
const level2Category = activePath[1];
|
||||
level3Categories = level2Category.children || [];
|
||||
}
|
||||
// Helper method to find category by seoName (similar to Content.js)
|
||||
findCategoryBySeoName = (categoryNode, seoName) => {
|
||||
if (!categoryNode) return null;
|
||||
|
||||
if (categoryNode.seoName === seoName) {
|
||||
return categoryNode;
|
||||
}
|
||||
|
||||
if (categoryNode.children) {
|
||||
for (const child of categoryNode.children) {
|
||||
const found = this.findCategoryBySeoName(child, seoName);
|
||||
if (found) return found;
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
categoryTree,
|
||||
level1Categories,
|
||||
level2Categories,
|
||||
level3Categories,
|
||||
activePath,
|
||||
fetchedCategories: true,
|
||||
});
|
||||
};
|
||||
return null;
|
||||
}
|
||||
|
||||
// Helper method to find the level 1 parent category ID
|
||||
findLevel1ParentId = (categoryId) => {
|
||||
const categoryTreeCache = window.productCache && window.productCache['categoryTree_209'];
|
||||
if (!categoryTreeCache || !categoryTreeCache.categoryTree) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const findParentPath = (node, targetId, path = []) => {
|
||||
if (node.id === targetId) {
|
||||
return [...path, node.id];
|
||||
}
|
||||
|
||||
if (node.children) {
|
||||
for (const child of node.children) {
|
||||
const result = findParentPath(child, targetId, [...path, node.id]);
|
||||
if (result) return result;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const path = findParentPath(categoryTreeCache.categoryTree, categoryId);
|
||||
if (!path || path.length < 3) return null; // path should be [209, level1Id, ...]
|
||||
|
||||
return path[1]; // Return the level 1 category ID (second in path after Home/209)
|
||||
}
|
||||
|
||||
handleMobileMenuToggle = () => {
|
||||
this.setState(prevState => ({
|
||||
@@ -331,10 +149,9 @@ class CategoryList extends Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { level1Categories, activePath, mobileMenuOpen } =
|
||||
this.state;
|
||||
const { categories, mobileMenuOpen, activeCategoryId } = this.state;
|
||||
|
||||
const renderCategoryRow = (categories, level = 1, isMobile = false) => (
|
||||
const renderCategoryRow = (categories, isMobile = false) => (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
@@ -351,7 +168,6 @@ class CategoryList extends Component {
|
||||
msOverflowStyle: "none",
|
||||
}}
|
||||
>
|
||||
{level === 1 && (
|
||||
<Button
|
||||
component={Link}
|
||||
to="/"
|
||||
@@ -372,7 +188,7 @@ class CategoryList extends Component {
|
||||
transition: "all 0.2s ease",
|
||||
textShadow: "0 1px 2px rgba(0,0,0,0.3)",
|
||||
position: "relative",
|
||||
...(this.props.activeCategoryId === null && {
|
||||
...(activeCategoryId === null && {
|
||||
bgcolor: "#fff",
|
||||
textShadow: "none",
|
||||
opacity: 1,
|
||||
@@ -396,7 +212,7 @@ class CategoryList extends Component {
|
||||
<HomeIcon sx={{
|
||||
fontSize: "1rem",
|
||||
mr: isMobile ? 1 : 0,
|
||||
color: this.props.activeCategoryId === null ? "#2e7d32" : "inherit"
|
||||
color: activeCategoryId === null ? "#2e7d32" : "inherit"
|
||||
}} />
|
||||
{isMobile && (
|
||||
<Box sx={{ position: "relative", display: "inline-block" }}>
|
||||
@@ -405,7 +221,7 @@ class CategoryList extends Component {
|
||||
className="bold-text"
|
||||
sx={{
|
||||
fontWeight: "bold",
|
||||
color: this.props.activeCategoryId === null ? "#2e7d32" : "transparent",
|
||||
color: activeCategoryId === null ? "#2e7d32" : "transparent",
|
||||
position: "relative",
|
||||
zIndex: 2,
|
||||
}}
|
||||
@@ -417,7 +233,7 @@ class CategoryList extends Component {
|
||||
className="thin-text"
|
||||
sx={{
|
||||
fontWeight: "400",
|
||||
color: this.props.activeCategoryId === null ? "transparent" : "inherit",
|
||||
color: activeCategoryId === null ? "transparent" : "inherit",
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
@@ -429,14 +245,10 @@ class CategoryList extends Component {
|
||||
</Box>
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
{this.state.fetchedCategories && categories.length > 0 ? (
|
||||
|
||||
{categories.length > 0 ? (
|
||||
<>
|
||||
{categories.map((category) => {
|
||||
// Determine if this category is active at this level
|
||||
const isActiveAtThisLevel =
|
||||
activePath[level - 1] &&
|
||||
activePath[level - 1].id === category.id;
|
||||
|
||||
return (
|
||||
<Button
|
||||
@@ -459,7 +271,7 @@ class CategoryList extends Component {
|
||||
transition: "all 0.2s ease",
|
||||
textShadow: "0 1px 2px rgba(0,0,0,0.3)",
|
||||
position: "relative",
|
||||
...(isActiveAtThisLevel && {
|
||||
...(activeCategoryId === category.id && {
|
||||
bgcolor: "#fff",
|
||||
textShadow: "none",
|
||||
opacity: 1,
|
||||
@@ -483,7 +295,7 @@ class CategoryList extends Component {
|
||||
className="bold-text"
|
||||
sx={{
|
||||
fontWeight: "bold",
|
||||
color: isActiveAtThisLevel ? "#2e7d32" : "transparent",
|
||||
color: activeCategoryId === category.id ? "#2e7d32" : "transparent",
|
||||
position: "relative",
|
||||
zIndex: 2,
|
||||
}}
|
||||
@@ -495,7 +307,7 @@ class CategoryList extends Component {
|
||||
className="thin-text"
|
||||
sx={{
|
||||
fontWeight: "400",
|
||||
color: isActiveAtThisLevel ? "transparent" : "inherit",
|
||||
color: activeCategoryId === category.id ? "transparent" : "inherit",
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
@@ -509,8 +321,7 @@ class CategoryList extends Component {
|
||||
);
|
||||
})}
|
||||
</>
|
||||
) : (
|
||||
level === 1 && !isMobile && (
|
||||
) : ( !isMobile && (
|
||||
<Typography
|
||||
variant="caption"
|
||||
color="inherit"
|
||||
@@ -549,25 +360,7 @@ class CategoryList extends Component {
|
||||
}}
|
||||
>
|
||||
<Container maxWidth="lg" sx={{ px: 2 }}>
|
||||
{/* Level 1 Categories Row - Always shown */}
|
||||
{renderCategoryRow(level1Categories, 1, false)}
|
||||
|
||||
{/* Level 2 Categories Row - Show when level 1 is selected */}
|
||||
{/* DISABLED FOR NOW
|
||||
{level2Categories.length > 0 && (
|
||||
<Box sx={{ mt: 0.5 }}>
|
||||
{renderCategoryRow(level2Categories, 2, false)}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Level 3 Categories Row - Show when level 2 is selected */}
|
||||
{/* DISABLED FOR NOW
|
||||
{level3Categories.length > 0 && (
|
||||
<Box sx={{ mt: 0.5 }}>
|
||||
{renderCategoryRow(level3Categories, 3, false)}
|
||||
</Box>
|
||||
)}
|
||||
*/}
|
||||
{renderCategoryRow(categories, false)}
|
||||
</Container>
|
||||
</Box>
|
||||
|
||||
@@ -621,7 +414,7 @@ class CategoryList extends Component {
|
||||
<Collapse in={mobileMenuOpen}>
|
||||
<Box sx={{ pb: 2 }}>
|
||||
{/* Level 1 Categories - Only level shown in mobile menu */}
|
||||
{renderCategoryRow(level1Categories, 1, true)}
|
||||
{renderCategoryRow(categories, true)}
|
||||
</Box>
|
||||
</Collapse>
|
||||
</Container>
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
import { Mutex } from 'async-mutex';
|
||||
|
||||
const mutex = new Mutex();
|
||||
|
||||
|
||||
class CategoryService {
|
||||
constructor() {
|
||||
this.get = this.get.bind(this);
|
||||
@@ -11,26 +16,37 @@ class CategoryService {
|
||||
return null;
|
||||
}
|
||||
|
||||
get(categoryId, language = "de") {
|
||||
const cacheKey = `${categoryId}_${language}`;
|
||||
if (window.categoryCache && window.categoryCache[cacheKey]) {
|
||||
return Promise.resolve(window.categoryCache[cacheKey]);
|
||||
}
|
||||
async get(categoryId, language = "de") {
|
||||
|
||||
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 = {};
|
||||
return await mutex.runExclusive(async () => {
|
||||
console.log("mutex locked");
|
||||
|
||||
|
||||
const cacheKey = `${categoryId}_${language}`;
|
||||
if (window.categoryCache && window.categoryCache[cacheKey]) {
|
||||
console.log("mutex unlocked and returning cached value");
|
||||
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;
|
||||
console.log("mutex unlocked and returning new value");
|
||||
resolve(response.categoryTree);
|
||||
} else {
|
||||
console.log("mutex unlocked and returning null");
|
||||
resolve(null);
|
||||
}
|
||||
window.categoryCache[cacheKey] = response.categoryTree;
|
||||
resolve(response.categoryTree);
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user