feat(settings): add sub-section handling for settings navigation and deep linking

This commit is contained in:
alexsparkes
2026-01-24 16:24:26 +00:00
parent 8cbb5a5c92
commit 819057ed8b
20 changed files with 170 additions and 70 deletions

View File

@@ -23,6 +23,8 @@ function MainModal({ modalClose, deepLinkData }) {
const initialTab = deepLinkData?.tab || TAB_TYPES.SETTINGS;
const [currentTab, setCurrentTab] = useState(initialTab);
const [currentSection, setCurrentSection] = useState('');
const [currentSectionName, setCurrentSectionName] = useState('');
const [currentSubSection, setCurrentSubSection] = useState(null);
const [productView, setProductView] = useState(null);
const [resetDiscoverToAll, setResetDiscoverToAll] = useState(false);
const [navigationTrigger, setNavigationTrigger] = useState(null);
@@ -61,6 +63,8 @@ function MainModal({ modalClose, deepLinkData }) {
data: linkData.section,
timestamp: Date.now(),
});
// Set sub-section if present in hash
setCurrentSubSection(linkData.subSection || null);
return;
}
@@ -131,6 +135,9 @@ function MainModal({ modalClose, deepLinkData }) {
const handleSectionChange = (section, sectionName) => {
setCurrentSection(section);
setCurrentSectionName(sectionName);
// Clear sub-section when changing sections
setCurrentSubSection(null);
// Update URL hash when section changes
if (currentTab === TAB_TYPES.DISCOVER) {
// For Discover tab, update with the section type
@@ -151,6 +158,19 @@ function MainModal({ modalClose, deepLinkData }) {
}
};
const handleSubSectionChange = (subSection, sectionName) => {
setCurrentSubSection(subSection);
// Update URL hash when sub-section changes
if (currentTab === TAB_TYPES.SETTINGS && sectionName) {
if (subSection) {
updateHash(`#${currentTab}/${sectionName}/${subSection}`);
} else {
// Going back to section, remove sub-section from hash
updateHash(`#${currentTab}/${sectionName}`);
}
}
};
const handleProductView = (product) => {
setProductView(product);
// URL hash is already updated by child components (Browse.jsx)
@@ -182,9 +202,12 @@ function MainModal({ modalClose, deepLinkData }) {
<ModalTopBar
currentTab={currentTab}
currentSection={currentSection}
currentSectionName={currentSectionName}
currentSubSection={currentSubSection}
productView={productView}
iframeBreadcrumbs={iframeBreadcrumbs}
onTabChange={handleChangeTab}
onSubSectionChange={handleSubSectionChange}
onClose={modalClose}
onBack={handleBack}
onForward={handleForward}
@@ -199,6 +222,8 @@ function MainModal({ modalClose, deepLinkData }) {
deepLinkData={deepLinkData}
currentTab={currentTab}
onSectionChange={handleSectionChange}
onSubSectionChange={handleSubSectionChange}
currentSubSection={currentSubSection}
onProductView={handleProductView}
onBreadcrumbsChange={setIframeBreadcrumbs}
resetToAll={resetDiscoverToAll}

View File

@@ -19,9 +19,12 @@ const MARKETPLACE_TYPE_TO_KEY = {
function ModalTopBar({
currentTab,
currentSection,
currentSectionName,
currentSubSection,
productView,
iframeBreadcrumbs,
onTabChange,
onSubSectionChange,
onClose,
onBack,
onForward,
@@ -34,8 +37,25 @@ function ModalTopBar({
? variables.getMessage(currentTabButton.messageKey)
: '';
// Utility function to get translated sub-section label
const getSubSectionLabel = (subSection, sectionName) => {
if (!subSection || !sectionName) return subSection;
// Use the same translation pattern as the section components
const translationKey = `modals.main.settings.sections.${sectionName}.${subSection}.title`;
const translated = variables.getMessage(translationKey);
// If translation key is returned as-is or empty, it means translation doesn't exist
// Fall back to capitalized sub-section name
if (!translated || translated === translationKey) {
return subSection.charAt(0).toUpperCase() + subSection.slice(1);
}
return translated;
};
// Determine breadcrumb path with click handlers
let breadcrumbPath = [];
const breadcrumbPath = [];
if (currentTabLabel) {
breadcrumbPath.push({
@@ -63,9 +83,6 @@ function ModalTopBar({
onClick: null, // Current item - not clickable
});
} else if (productView) {
console.log('ModalTopBar productView:', productView);
console.log('fromCollection:', productView.fromCollection, 'isCollection:', productView.isCollection, 'collectionTitle:', productView.collectionTitle);
// If viewing a collection page itself (not a product within it)
if (productView.isCollection) {
// Show: Discover > Collection Name
@@ -77,14 +94,12 @@ function ModalTopBar({
// Viewing a product
// Show: Discover > Collection/Category > Product
if (productView.fromCollection && productView.collectionTitle) {
console.log('Showing collection breadcrumb:', productView.collectionTitle);
// If from a collection, show collection name
breadcrumbPath.push({
label: productView.collectionTitle,
onClick: productView.onBack || null,
});
} else {
console.log('Showing category breadcrumb');
// Otherwise show category
const categoryKey = MARKETPLACE_TYPE_TO_KEY[productView.type];
if (categoryKey) {
@@ -101,11 +116,19 @@ function ModalTopBar({
});
}
} else if (currentSection) {
// Show: Tab > Section
// Show: Tab > Section or Tab > Section > Sub-Section
breadcrumbPath.push({
label: currentSection,
onClick: null, // Current section - not clickable
onClick: currentSubSection ? () => onSubSectionChange(null) : null, // Clickable if sub-section is active
});
// Add sub-section if present
if (currentSubSection) {
breadcrumbPath.push({
label: getSubSectionLabel(currentSubSection, currentSectionName),
onClick: null, // Current sub-section - not clickable
});
}
}
}