refactor(MainModal): and related components for improved tab management and loading states

This commit is contained in:
alexsparkes
2025-10-28 12:39:11 +00:00
parent 16b113f7eb
commit ae3c135660
9 changed files with 290 additions and 187 deletions

View File

@@ -1,62 +1,6 @@
import variables from 'config/variables';
import { memo, useState, useEffect } from 'react';
import {
MdSettings as Settings,
MdWidgets as Addons,
MdShoppingBasket as Marketplace,
MdMenu as Navbar,
MdEmojiPeople as Greeting,
MdAccessAlarm as Time,
MdOutlineFormatQuote as Quote,
MdLink as QuickLinks,
MdDateRange as Date,
MdOutlineTextsms as Message,
MdOutlinePhoto as Background,
MdSearch,
MdCloudQueue as Weather,
MdFormatPaint as Appearance,
MdTranslate as Language,
MdOutlineSettings as Advanced,
MdBugReport as Experimental,
MdOutlineAssessment as Stats,
MdOutlineNewReleases as Changelog,
MdInfoOutline as About,
MdOutlineExtension as Added,
MdAddCircleOutline as Create,
MdViewAgenda as Overview,
MdCollectionsBookmark as Collections,
} from 'react-icons/md';
const iconMapping = {
[variables.getMessage('modals.main.marketplace.product.overview')]: <Overview />,
[variables.getMessage('modals.main.navbar.settings')]: <Settings />,
[variables.getMessage('modals.main.navbar.addons')]: <Addons />,
[variables.getMessage('modals.main.navbar.marketplace')]: <Marketplace />,
[variables.getMessage('modals.main.settings.sections.appearance.navbar.title')]: <Navbar />,
[variables.getMessage('modals.main.settings.sections.greeting.title')]: <Greeting />,
[variables.getMessage('modals.main.settings.sections.time.title')]: <Time />,
[variables.getMessage('modals.main.settings.sections.quicklinks.title')]: <QuickLinks />,
[variables.getMessage('modals.main.settings.sections.quote.title')]: <Quote />,
[variables.getMessage('modals.main.settings.sections.date.title')]: <Date />,
[variables.getMessage('modals.main.settings.sections.message.title')]: <Message />,
[variables.getMessage('modals.main.settings.sections.background.title')]: <Background />,
[variables.getMessage('modals.main.settings.sections.search.title')]: <MdSearch />,
[variables.getMessage('modals.main.settings.sections.weather.title')]: <Weather />,
[variables.getMessage('modals.main.settings.sections.appearance.title')]: <Appearance />,
[variables.getMessage('modals.main.settings.sections.language.title')]: <Language />,
[variables.getMessage('modals.main.settings.sections.advanced.title')]: <Advanced />,
[variables.getMessage('modals.main.settings.sections.stats.title')]: <Stats />,
[variables.getMessage('modals.main.settings.sections.experimental.title')]: <Experimental />,
[variables.getMessage('modals.main.settings.sections.changelog.title')]: <Changelog />,
[variables.getMessage('modals.main.settings.sections.about.title')]: <About />,
[variables.getMessage('modals.main.addons.added')]: <Added />,
[variables.getMessage('modals.main.addons.create.title')]: <Create />,
[variables.getMessage('modals.main.marketplace.all')]: <Addons />,
[variables.getMessage('modals.main.marketplace.photo_packs')]: <Background />,
[variables.getMessage('modals.main.marketplace.quote_packs')]: <Quote />,
[variables.getMessage('modals.main.marketplace.preset_settings')]: <Advanced />,
[variables.getMessage('modals.main.marketplace.collections')]: <Collections />,
};
import { getIconComponent, DIVIDER_LABELS, MUE_TITLE_LABELS } from '../constants/tabConfig';
function Tab({ label, currentTab, onClick, navbarTab }) {
const [isExperimental, setIsExperimental] = useState(true);
@@ -65,39 +9,34 @@ function Tab({ label, currentTab, onClick, navbarTab }) {
setIsExperimental(localStorage.getItem('experimental') !== 'false');
}, []);
let className = navbarTab ? 'navbar-item' : 'tab-list-item';
if (currentTab === label) {
className += navbarTab ? ' navbar-item-active' : ' tab-list-active';
}
// Get the icon component for this label
const IconComponent = getIconComponent(label, variables);
const icon = iconMapping[label];
const divider = [
variables.getMessage('modals.main.settings.sections.weather.title'),
variables.getMessage('modals.main.settings.sections.language.title'),
variables.getMessage('modals.main.marketplace.all'),
variables.getMessage('modals.main.settings.sections.experimental.title'),
].includes(label);
// Determine if this label should have a divider after it
const hasDivider = DIVIDER_LABELS.some((key) => variables.getMessage(key) === label);
const mue = [
variables.getMessage('modals.main.marketplace.product.overview'),
variables.getMessage('modals.main.addons.added'),
variables.getMessage('modals.main.marketplace.all'),
].includes(label);
// Determine if this label should have "Mue" title before it
const hasMueTitle = MUE_TITLE_LABELS.some((key) => variables.getMessage(key) === label);
if (
label === variables.getMessage('modals.main.settings.sections.experimental.title') &&
!isExperimental
) {
// Build className
const baseClass = navbarTab ? 'navbar-item' : 'tab-list-item';
const activeClass = navbarTab ? 'navbar-item-active' : 'tab-list-active';
const className = `${baseClass}${currentTab === label ? ` ${activeClass}` : ''}`;
// Hide experimental tab if experimental mode is disabled
const isExperimentalTab =
label === variables.getMessage('modals.main.settings.sections.experimental.title');
if (isExperimentalTab && !isExperimental) {
return <hr />;
}
return (
<>
{mue && <span className="mainTitle">Mue</span>}
{hasMueTitle && <span className="mainTitle">Mue</span>}
<button className={className} onClick={() => onClick(label)}>
{icon} <span>{label}</span>
{IconComponent && <IconComponent />} <span>{label}</span>
</button>
{divider && <hr />}
{hasDivider && <hr />}
</>
);
}

View File

@@ -1,21 +1,16 @@
import variables from 'config/variables';
import React, { useState, useEffect } from 'react';
import {
MdSettings,
MdOutlineShoppingBasket,
MdOutlineExtension,
MdRefresh,
MdClose,
} from 'react-icons/md';
import { useState } from 'react';
import Tab from './Tab';
import { Button } from 'components/Elements';
import ModalNavbar from '../components/ModalNavbar';
import ReminderInfo from '../components/ReminderInfo';
import ErrorBoundary from '../../../../features/misc/modals/ErrorBoundary';
const Tabs = (props) => {
const [currentTab, setCurrentTab] = useState(props.children[0].props.label);
const [currentName, setCurrentName] = useState(props.children[0].props.name);
const Tabs = ({ children, changeTab, current, navbar = false }) => {
const [currentTab, setCurrentTab] = useState(children[0]?.props.label);
const [currentName, setCurrentName] = useState(children[0]?.props.name);
const [showReminder, setShowReminder] = useState(localStorage.getItem('showReminder') === 'true');
const onClick = (tab, name) => {
const handleTabClick = (tab, name) => {
if (name !== currentName) {
variables.stats.postEvent('tab', `Opened ${name}`);
}
@@ -24,77 +19,30 @@ const Tabs = (props) => {
setCurrentName(name);
};
const hideReminder = () => {
localStorage.setItem('showReminder', false);
document.querySelector('.reminder-info').style.display = 'none';
const handleHideReminder = () => {
localStorage.setItem('showReminder', 'false');
setShowReminder(false);
};
const navbarButtons = [
{
tab: 'settings',
icon: <MdSettings />,
},
{
tab: 'addons',
icon: <MdOutlineExtension />,
},
{
tab: 'marketplace',
icon: <MdOutlineShoppingBasket />,
},
];
const reminderInfo = (
<div
className="reminder-info"
style={{ display: localStorage.getItem('showReminder') === 'true' ? 'flex' : 'none' }}
>
<div className="shareHeader">
<span className="title">{variables.getMessage('modals.main.settings.reminder.title')}</span>
<span className="closeModal" onClick={hideReminder}>
<MdClose />
</span>
</div>
<span className="subtitle">
{variables.getMessage('modals.main.settings.reminder.message')}
</span>
<button onClick={() => window.location.reload()}>
<MdRefresh />
{variables.getMessage('modals.main.error_boundary.refresh')}
</button>
</div>
);
return (
<div style={{ display: 'flex', width: '100%', minHeight: '100%' }}>
<div className="modalSidebar">
{props.children.map((tab, index) => (
{children.map((tab, index) => (
<Tab
currentTab={currentTab}
key={index}
currentTab={currentTab}
label={tab.props.label}
onClick={(nextTab) => onClick(nextTab, tab.props.name)}
navbarTab={props.navbar || false}
onClick={(nextTab) => handleTabClick(nextTab, tab.props.name)}
navbarTab={navbar}
/>
))}
{reminderInfo}
<ReminderInfo isVisible={showReminder} onHide={handleHideReminder} />
</div>
<div className="modalTabContent">
<div className="modalNavbar">
{navbarButtons.map(({ tab, icon }, index) => (
<Button
type="navigation"
onClick={() => props.changeTab(tab)}
icon={icon}
label={variables.getMessage(`modals.main.navbar.${tab}`)}
active={props.current === tab}
key={`${tab}-${index}`}
/>
))}
</div>
{props.children.map((tab, index) => {
<ModalNavbar currentTab={current} onChangeTab={changeTab} />
{children.map((tab, index) => {
if (tab.props.label !== currentTab) {
return undefined;
return null;
}
return (