Files
mue/src/components/Elements/MainModal/Main.jsx
2025-10-29 23:46:15 +00:00

282 lines
9.5 KiB
JavaScript

import { Suspense, lazy, useState, memo, useEffect } from 'react';
import variables from 'config/variables';
import './scss/index.scss';
import ModalLoader from './components/ModalLoader';
import ModalTopBar from './components/ModalTopBar';
import { TAB_TYPES } from './constants/tabConfig';
import { updateHash, onHashChange } from 'utils/deepLinking';
const Settings = lazy(() => import('../../../features/misc/views/Settings'));
const Library = lazy(() => import('../../../features/misc/views/Library'));
const Discover = lazy(() => import('../../../features/misc/views/Discover'));
// Map tab types to their corresponding components
const TAB_COMPONENTS = {
[TAB_TYPES.SETTINGS]: Settings,
[TAB_TYPES.LIBRARY]: Library,
[TAB_TYPES.DISCOVER]: Discover,
};
function MainModal({ modalClose, deepLinkData }) {
// Initialize with deep link tab if provided, otherwise default to settings
const initialTab = deepLinkData?.tab || TAB_TYPES.SETTINGS;
const [currentTab, setCurrentTab] = useState(initialTab);
const [currentSection, setCurrentSection] = useState('');
const [productView, setProductView] = useState(null);
const [resetDiscoverToAll, setResetDiscoverToAll] = useState(false);
const [navigationHistory, setNavigationHistory] = useState([]);
const [historyIndex, setHistoryIndex] = useState(-1);
const [navigationTrigger, setNavigationTrigger] = useState(null);
const [skipNextHistoryAdd, setSkipNextHistoryAdd] = useState(0);
// Clear product view when changing tabs
useEffect(() => {
setProductView(null);
}, [currentTab]);
useEffect(() => {
// Listen for hash changes while modal is open
const cleanup = onHashChange((linkData) => {
if (linkData && linkData.tab !== currentTab) {
setCurrentTab(linkData.tab);
}
});
return cleanup;
}, [currentTab]);
const addToHistory = (state) => {
// Check if this state is different from the current one
const currentState = navigationHistory[historyIndex];
const isDifferent = !currentState ||
currentState.tab !== state.tab ||
currentState.section !== state.section ||
JSON.stringify(currentState.product) !== JSON.stringify(state.product);
if (isDifferent) {
const newHistory = navigationHistory.slice(0, historyIndex + 1);
newHistory.push(state);
setNavigationHistory(newHistory);
setHistoryIndex(newHistory.length - 1);
console.log('Added to history:', state, 'New history:', newHistory);
} else {
console.log('Skipping duplicate history entry:', state);
}
};
const handleChangeTab = (newTab) => {
// Only add to history if not navigating via history
if (skipNextHistoryAdd === 0) {
addToHistory({
tab: newTab,
section: '',
product: null,
});
} else {
setSkipNextHistoryAdd(0); // Reset skip counter
}
setCurrentTab(newTab);
// Update URL hash when tab changes
if (newTab === TAB_TYPES.DISCOVER) {
updateHash(`#${newTab}/all`);
} else if (newTab === TAB_TYPES.LIBRARY) {
updateHash(`#${newTab}/added`);
} else {
updateHash(`#${newTab}`);
}
};
const handleSectionChange = (section) => {
setCurrentSection(section);
// Update URL hash when section changes
if (currentTab === TAB_TYPES.DISCOVER) {
// For Discover tab, update with the section type
const sectionMap = {
[variables.getMessage('modals.main.marketplace.all')]: 'all',
[variables.getMessage('modals.main.marketplace.photo_packs')]: 'photo_packs',
[variables.getMessage('modals.main.marketplace.quote_packs')]: 'quote_packs',
[variables.getMessage('modals.main.marketplace.preset_settings')]: 'preset_settings',
[variables.getMessage('modals.main.marketplace.collections')]: 'collections',
};
const sectionKey = sectionMap[section];
if (sectionKey) {
updateHash(`#${currentTab}/${sectionKey}`);
}
}
// Don't add section changes to history - they're automatic
// Only track user-initiated navigation (tab switches and product views)
};
const handleProductView = (product) => {
setProductView(product);
// Add to navigation history only if not skipping
// Store only essential product info, not full object
if (product && skipNextHistoryAdd === 0) {
addToHistory({
tab: currentTab,
section: currentSection,
product: {
type: product.type,
name: product.name,
id: product.id,
},
});
} else if (skipNextHistoryAdd > 0) {
console.log('Skipping product view history add');
setSkipNextHistoryAdd(0); // Reset after use
}
};
const handleResetDiscoverToAll = () => {
setResetDiscoverToAll(true);
setTimeout(() => setResetDiscoverToAll(false), 100);
};
const handleBack = () => {
if (historyIndex > 0) {
const newIndex = historyIndex - 1;
const previousState = navigationHistory[newIndex];
console.log('Going back to:', previousState);
setHistoryIndex(newIndex);
// Set skip flag BEFORE changing any state
setSkipNextHistoryAdd(1);
// Change tab if different
if (previousState.tab !== currentTab) {
setCurrentTab(previousState.tab);
}
setCurrentSection(previousState.section);
setProductView(previousState.product);
// Trigger navigation in child components
if (previousState.product) {
// Viewing a product
setNavigationTrigger({
type: 'product',
data: previousState.product,
timestamp: Date.now(),
});
updateHash(`#${previousState.tab}/${previousState.product.type}/${previousState.product.id}`);
} else {
// Viewing main view
setNavigationTrigger({
type: 'main',
data: null,
timestamp: Date.now(),
});
if (previousState.tab === TAB_TYPES.DISCOVER) {
const sectionMap = {
[variables.getMessage('modals.main.marketplace.all')]: 'all',
[variables.getMessage('modals.main.marketplace.photo_packs')]: 'photo_packs',
[variables.getMessage('modals.main.marketplace.quote_packs')]: 'quote_packs',
[variables.getMessage('modals.main.marketplace.preset_settings')]: 'preset_settings',
[variables.getMessage('modals.main.marketplace.collections')]: 'collections',
};
const sectionKey = sectionMap[previousState.section] || 'all';
updateHash(`#${previousState.tab}/${sectionKey}`);
} else if (previousState.tab === TAB_TYPES.LIBRARY) {
updateHash(`#${previousState.tab}/added`);
} else {
updateHash(`#${previousState.tab}`);
}
}
}
};
const handleForward = () => {
if (historyIndex < navigationHistory.length - 1) {
const newIndex = historyIndex + 1;
const nextState = navigationHistory[newIndex];
console.log('Going forward to:', nextState);
setHistoryIndex(newIndex);
// Set skip flag BEFORE changing any state
setSkipNextHistoryAdd(1);
// Change tab if different
if (nextState.tab !== currentTab) {
setCurrentTab(nextState.tab);
}
setCurrentSection(nextState.section);
setProductView(nextState.product);
// Trigger navigation in child components
if (nextState.product) {
setNavigationTrigger({
type: 'product',
data: nextState.product,
timestamp: Date.now(),
});
updateHash(`#${nextState.tab}/${nextState.product.type}/${nextState.product.id}`);
} else {
setNavigationTrigger({
type: 'main',
data: null,
timestamp: Date.now(),
});
if (nextState.tab === TAB_TYPES.DISCOVER) {
const sectionMap = {
[variables.getMessage('modals.main.marketplace.all')]: 'all',
[variables.getMessage('modals.main.marketplace.photo_packs')]: 'photo_packs',
[variables.getMessage('modals.main.marketplace.quote_packs')]: 'quote_packs',
[variables.getMessage('modals.main.marketplace.preset_settings')]: 'preset_settings',
[variables.getMessage('modals.main.marketplace.collections')]: 'collections',
};
const sectionKey = sectionMap[nextState.section] || 'all';
updateHash(`#${nextState.tab}/${sectionKey}`);
} else if (nextState.tab === TAB_TYPES.LIBRARY) {
updateHash(`#${nextState.tab}/added`);
} else {
updateHash(`#${nextState.tab}`);
}
}
}
};
const canGoBack = historyIndex > 0;
const canGoForward = historyIndex < navigationHistory.length - 1;
const TabComponent = TAB_COMPONENTS[currentTab] || Settings;
return (
<div className="frame">
<ModalTopBar
currentTab={currentTab}
currentSection={currentSection}
productView={productView}
onTabChange={handleChangeTab}
onClose={modalClose}
onBack={handleBack}
onForward={handleForward}
canGoBack={canGoBack}
canGoForward={canGoForward}
/>
<Suspense fallback={<ModalLoader />}>
<TabComponent
changeTab={handleChangeTab}
deepLinkData={deepLinkData}
currentTab={currentTab}
onSectionChange={handleSectionChange}
onProductView={handleProductView}
resetToAll={resetDiscoverToAll}
onResetToAll={handleResetDiscoverToAll}
navigationTrigger={navigationTrigger}
/>
</Suspense>
</div>
);
}
export default memo(MainModal);