refactor(welcome): Improve readability of sections

This commit is contained in:
alexsparkes 2024-02-19 19:51:12 +00:00
parent fc9d1c3f65
commit ffb1c513c9
13 changed files with 209 additions and 140 deletions

View File

@ -3,11 +3,11 @@ import { MdOutlineOpenInNew } from 'react-icons/md';
import languages from '@/i18n/languages.json'; import languages from '@/i18n/languages.json';
import { Radio } from 'components/Form/Settings'; import { Radio } from 'components/Form/Settings';
import { Header } from '../components/Layout'; import { Header, Content } from '../components/Layout';
function ChooseLanguage() { function ChooseLanguage() {
return ( return (
<> <Content>
<Header <Header
title={variables.getMessage('modals.welcome.sections.language.title')} title={variables.getMessage('modals.welcome.sections.language.title')}
subtitle={variables.getMessage('modals.welcome.sections.language.description')} subtitle={variables.getMessage('modals.welcome.sections.language.description')}
@ -33,7 +33,7 @@ function ChooseLanguage() {
<div className="languageSettings"> <div className="languageSettings">
<Radio name="language" options={languages} category="welcomeLanguage" /> <Radio name="language" options={languages} category="welcomeLanguage" />
</div> </div>
</> </Content>
); );
} }

View File

@ -1,10 +1,10 @@
import variables from 'config/variables'; import variables from 'config/variables';
import languages from '@/i18n/languages.json'; import languages from '@/i18n/languages.json';
import { Header } from '../components/Layout'; import { Header, Content } from '../components/Layout';
function Final(props) { function Final(props) {
return ( return (
<> <Content>
<Header <Header
title={variables.getMessage('modals.welcome.sections.final.title')} title={variables.getMessage('modals.welcome.sections.final.title')}
subtitle={variables.getMessage('modals.welcome.sections.final.description')} subtitle={variables.getMessage('modals.welcome.sections.final.description')}
@ -38,7 +38,7 @@ function Final(props) {
</div> </div>
)}*/} )}*/}
</div> </div>
</> </Content>
); );
} }

View File

@ -3,7 +3,7 @@ import { useState } from 'react';
import { FileUpload } from 'components/Form/Settings'; import { FileUpload } from 'components/Form/Settings';
import { MdCloudUpload } from 'react-icons/md'; import { MdCloudUpload } from 'react-icons/md';
import { importSettings as importSettingsFunction } from 'utils/settings'; import { importSettings as importSettingsFunction } from 'utils/settings';
import { Header } from '../components/Layout'; import { Header, Content } from '../components/Layout';
import default_settings from 'utils/data/default_settings.json'; import default_settings from 'utils/data/default_settings.json';
function ImportSettings(props) { function ImportSettings(props) {
@ -43,7 +43,7 @@ function ImportSettings(props) {
props.switchTab(5); props.switchTab(5);
}; };
return ( return (
<> <Content>
<Header <Header
title={variables.getMessage('modals.welcome.sections.settings.title')} title={variables.getMessage('modals.welcome.sections.settings.title')}
subtitle={variables.getMessage('modals.welcome.sections.settings.description')} subtitle={variables.getMessage('modals.welcome.sections.settings.description')}
@ -62,7 +62,7 @@ function ImportSettings(props) {
<span className="subtitle"> <span className="subtitle">
{variables.getMessage('modals.welcome.sections.settings.tip')} {variables.getMessage('modals.welcome.sections.settings.tip')}
</span> </span>
</> </Content>
); );
} }

View File

@ -1,7 +1,7 @@
import variables from 'config/variables'; import variables from 'config/variables';
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { Header } from '../components/Layout'; import { Header, Content } from '../components/Layout';
import { MdOutlineWavingHand, MdOpenInNew } from 'react-icons/md'; import { MdOutlineWavingHand, MdOpenInNew } from 'react-icons/md';
import { FaDiscord, FaGithub } from 'react-icons/fa'; import { FaDiscord, FaGithub } from 'react-icons/fa';
@ -18,7 +18,7 @@ useEffect(() => {
}, [welcomeImage]); }, [welcomeImage]);
return ( return (
<> <Content>
<Header title={variables.getMessage('modals.welcome.sections.intro.title')} /> <Header title={variables.getMessage('modals.welcome.sections.intro.title')} />
<div className="examples"> <div className="examples">
<img <img
@ -75,7 +75,7 @@ useEffect(() => {
{variables.getMessage('modals.welcome.sections.intro.notices.github_open')} {variables.getMessage('modals.welcome.sections.intro.notices.github_open')}
</a> </a>
</div> </div>
</> </Content>
); );
} }

View File

@ -1,15 +1,11 @@
import variables from 'config/variables'; import variables from 'config/variables';
import { MdOutlineOpenInNew } from 'react-icons/md'; import { MdOutlineOpenInNew } from 'react-icons/md';
import { Checkbox } from 'components/Form/Settings'; import { Checkbox } from 'components/Form/Settings';
import { Header } from '../components/Layout'; import { Header, Content } from '../components/Layout';
function PrivacyOptions() { function OfflineMode() {
return ( return (
<> <>
<Header
title={variables.getMessage('modals.welcome.sections.privacy.title')}
subtitle={variables.getMessage('modals.welcome.sections.privacy.description')}
/>
<Checkbox <Checkbox
name="offlineMode" name="offlineMode"
text={variables.getMessage('modals.main.settings.sections.advanced.offline_mode')} text={variables.getMessage('modals.main.settings.sections.advanced.offline_mode')}
@ -18,18 +14,27 @@ function PrivacyOptions() {
<span className="subtitle"> <span className="subtitle">
{variables.getMessage('modals.welcome.sections.privacy.offline_mode_description')} {variables.getMessage('modals.welcome.sections.privacy.offline_mode_description')}
</span> </span>
</>
);
}
function DuckDuckGoProxy() {
return (
<>
<Checkbox <Checkbox
name="ddgProxy" name="ddgProxy"
text={ text={`${variables.getMessage('modals.main.settings.sections.background.ddg_image_proxy')} (${variables.getMessage('modals.main.settings.sections.background.title')})`}
variables.getMessage('modals.main.settings.sections.background.ddg_image_proxy') +
' (' +
variables.getMessage('modals.main.settings.sections.background.title') +
')'
}
/> />
<span className="subtitle"> <span className="subtitle">
{variables.getMessage('modals.welcome.sections.privacy.ddg_proxy_description')} {variables.getMessage('modals.welcome.sections.privacy.ddg_proxy_description')}
</span> </span>
</>
);
}
function Links() {
return (
<>
<span className="title"> <span className="title">
{variables.getMessage('modals.welcome.sections.privacy.links.title')} {variables.getMessage('modals.welcome.sections.privacy.links.title')}
</span> </span>
@ -45,7 +50,7 @@ function PrivacyOptions() {
<a <a
className="link" className="link"
href={'https://github.com/' + variables.constants.ORG_NAME} href={`https://github.com/${variables.constants.ORG_NAME}`}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
> >
@ -56,4 +61,18 @@ function PrivacyOptions() {
); );
} }
export { PrivacyOptions as default, PrivacyOptions }; function PrivacyOptions() {
return (
<Content>
<Header
title={variables.getMessage('modals.welcome.sections.privacy.title')}
subtitle={variables.getMessage('modals.welcome.sections.privacy.description')}
/>
<OfflineMode />
<DuckDuckGoProxy />
<Links />
</Content>
);
}
export { PrivacyOptions as default, PrivacyOptions };

View File

@ -1,41 +1,53 @@
import variables from 'config/variables'; import variables from 'config/variables';
import { MdArchive, MdOutlineWhatshot } from 'react-icons/md'; import { MdArchive, MdOutlineWhatshot } from 'react-icons/md';
import { useState } from 'react'; import { useState } from 'react';
import { Header } from '../components/Layout'; import { Header, Content } from '../components/Layout';
function StyleSelection() { const STYLES = {
const widgetStyle = localStorage.getItem('widgetStyle'); NEW: 'new',
const [style, setStyle] = useState({ LEGACY: 'legacy',
newStyle: widgetStyle === 'legacy' ? 'toggle newStyle' : 'toggle newStyle active', };
legacyStyle: widgetStyle === 'legacy' ? 'toggle legacyStyle active' : 'toggle legacyStyle',
});
const changeStyle = (type) => {
setStyle({
newStyle: type === 'new' ? 'toggle newStyle active' : 'toggle newStyle',
legacyStyle: type === 'legacy' ? 'toggle legacyStyle active' : 'toggle legacyStyle',
});
localStorage.setItem('widgetStyle', type);
};
return ( const StyleSelection = () => {
<> const widgetStyle = localStorage.getItem('widgetStyle') || STYLES.NEW;
<Header title={variables.getMessage('modals.welcome.sections.style.title')} subtitle={variables.getMessage('modals.welcome.sections.style.description')} /> const [style, setStyle] = useState(widgetStyle);
<div className="themesToggleArea">
<div className="options"> const changeStyle = (type) => {
<div className={style.legacyStyle} onClick={() => changeStyle('legacy')}> setStyle(type);
<MdArchive /> localStorage.setItem('widgetStyle', type);
<span>{variables.getMessage('modals.welcome.sections.style.legacy')}</span> };
const styleMapping = {
[STYLES.LEGACY]: {
className: style === STYLES.LEGACY ? 'toggle legacyStyle active' : 'toggle legacyStyle',
icon: <MdArchive />,
text: variables.getMessage('modals.welcome.sections.style.legacy'),
},
[STYLES.NEW]: {
className: style === STYLES.NEW ? 'toggle newStyle active' : 'toggle newStyle',
icon: <MdOutlineWhatshot />,
text: variables.getMessage('modals.welcome.sections.style.modern'),
},
};
return (
<Content>
<Header
title={variables.getMessage('modals.welcome.sections.style.title')}
subtitle={variables.getMessage('modals.welcome.sections.style.description')}
/>
<div className="themesToggleArea">
<div className="options">
{Object.entries(styleMapping).map(([type, { className, icon, text }]) => (
<div className={className} onClick={() => changeStyle(type)} key={type}>
{icon}
<span>{text}</span>
</div> </div>
<div className={style.newStyle} onClick={() => changeStyle('new')}> ))}
<MdOutlineWhatshot />
<span>{variables.getMessage('modals.welcome.sections.style.modern')}</span>
</div>
</div>
</div> </div>
</> </div>
) </Content>
} );
};
export { StyleSelection as default, StyleSelection }; export { StyleSelection as default, StyleSelection };

View File

@ -2,55 +2,69 @@ import variables from 'config/variables';
import { useState } from 'react'; import { useState } from 'react';
import { MdAutoAwesome, MdLightMode, MdDarkMode } from 'react-icons/md'; import { MdAutoAwesome, MdLightMode, MdDarkMode } from 'react-icons/md';
import { loadSettings } from 'utils/settings'; import { loadSettings } from 'utils/settings';
import { Header } from '../components/Layout'; import { Header, Content } from '../components/Layout';
const THEMES = {
AUTO: 'auto',
LIGHT: 'light',
DARK: 'dark',
};
function ThemeSelection() { function ThemeSelection() {
const [theme, setTheme] = useState({ const currentTheme = localStorage.getItem('theme') || THEMES.AUTO;
autoClass: 'toggle auto active', const [theme, setTheme] = useState(currentTheme);
lightClass: 'toggle lightTheme',
darkClass: 'toggle darkTheme',
});
const changeTheme = (type) => { const changeTheme = (type) => {
setTheme({ setTheme(type);
autoClass: type === 'auto' ? 'toggle auto active' : 'toggle auto',
lightClass: type === 'light' ? 'toggle lightTheme active' : 'toggle lightTheme',
darkClass: type === 'dark' ? 'toggle darkTheme active' : 'toggle darkTheme',
});
localStorage.setItem('theme', type); localStorage.setItem('theme', type);
loadSettings(true); loadSettings(true);
}; };
const themeMapping = {
[THEMES.AUTO]: {
className: theme === THEMES.AUTO ? 'toggle auto active' : 'toggle auto',
icon: <MdAutoAwesome />,
text: variables.getMessage('modals.main.settings.sections.appearance.theme.auto'),
},
[THEMES.LIGHT]: {
className: theme === THEMES.LIGHT ? 'toggle lightTheme active' : 'toggle lightTheme',
icon: <MdLightMode />,
text: variables.getMessage('modals.main.settings.sections.appearance.theme.light'),
},
[THEMES.DARK]: {
className: theme === THEMES.DARK ? 'toggle darkTheme active' : 'toggle darkTheme',
icon: <MdDarkMode />,
text: variables.getMessage('modals.main.settings.sections.appearance.theme.dark'),
},
};
return ( return (
<> <Content>
<Header <Header
title={variables.getMessage('modals.welcome.sections.theme.title')} title={variables.getMessage('modals.welcome.sections.theme.title')}
subtitle={variables.getMessage('modals.welcome.sections.theme.description')} subtitle={variables.getMessage('modals.welcome.sections.theme.description')}
/> />
<div className="themesToggleArea"> <div className="themesToggleArea">
<div className={theme.autoClass} onClick={() => changeTheme('auto')}> <div className={themeMapping[THEMES.AUTO].className} onClick={() => changeTheme(THEMES.AUTO)}>
<MdAutoAwesome /> {themeMapping[THEMES.AUTO].icon}
<span>{variables.getMessage('modals.main.settings.sections.appearance.theme.auto')}</span> <span>{themeMapping[THEMES.AUTO].text}</span>
</div> </div>
<div className="options"> <div className="options">
<div className={theme.lightClass} onClick={() => changeTheme('light')}> {Object.entries(themeMapping)
<MdLightMode /> .filter(([type]) => type !== THEMES.AUTO)
<span> .map(([type, { className, icon, text }]) => (
{variables.getMessage('modals.main.settings.sections.appearance.theme.light')} <div className={className} onClick={() => changeTheme(type)} key={type}>
</span> {icon}
</div> <span>{text}</span>
<div className={theme.darkClass} onClick={() => changeTheme('dark')}> </div>
<MdDarkMode /> ))}
<span>
{variables.getMessage('modals.main.settings.sections.appearance.theme.dark')}
</span>
</div>
</div> </div>
</div> </div>
<span className="title">{variables.getMessage('modals.welcome.tip')}</span> <span className="title">{variables.getMessage('modals.welcome.tip')}</span>
<span className="subtitle">{variables.getMessage('modals.welcome.sections.theme.tip')}</span> <span className="subtitle">{variables.getMessage('modals.welcome.sections.theme.tip')}</span>
</> </Content>
); );
} }
export { ThemeSelection as default, ThemeSelection }; export { ThemeSelection as default, ThemeSelection };

View File

@ -37,7 +37,6 @@ class WelcomeModal extends PureComponent {
if (minus) { if (minus) {
return this.setState({ return this.setState({
currentTab: this.state.currentTab - 1, currentTab: this.state.currentTab - 1,
image: this.images[this.state.currentTab - 1],
buttonText: variables.getMessage('modals.welcome.buttons.next'), buttonText: variables.getMessage('modals.welcome.buttons.next'),
}); });
} }
@ -48,7 +47,7 @@ class WelcomeModal extends PureComponent {
this.setState({ this.setState({
currentTab: this.state.currentTab + 1, currentTab: this.state.currentTab + 1,
image: this.images[this.state.currentTab + 1], image: [this.state.currentTab + 1],
buttonText: buttonText:
this.state.currentTab !== this.state.finalTab this.state.currentTab !== this.state.finalTab
? variables.getMessage('modals.welcome.buttons.next') ? variables.getMessage('modals.welcome.buttons.next')
@ -60,7 +59,6 @@ class WelcomeModal extends PureComponent {
switchTab(tab) { switchTab(tab) {
this.setState({ this.setState({
currentTab: tab, currentTab: tab,
image: this.images[tab],
buttonText: buttonText:
tab !== this.state.finalTab + 1 tab !== this.state.finalTab + 1
? variables.getMessage('modals.welcome.buttons.next') ? variables.getMessage('modals.welcome.buttons.next')
@ -76,7 +74,6 @@ class WelcomeModal extends PureComponent {
if (welcomeTab) { if (welcomeTab) {
this.setState({ this.setState({
currentTab: Number(welcomeTab), currentTab: Number(welcomeTab),
image: this.images[Number(welcomeTab)],
buttonText: buttonText:
Number(welcomeTab) !== this.state.finalTab + 1 Number(welcomeTab) !== this.state.finalTab + 1
? variables.getMessage('modals.welcome.buttons.next') ? variables.getMessage('modals.welcome.buttons.next')
@ -97,6 +94,38 @@ class WelcomeModal extends PureComponent {
EventBus.off('refresh'); EventBus.off('refresh');
} }
renderButtons() {
const { currentTab, buttonText } = this.state;
const { modalSkip } = this.props;
return (
<div className="welcomeButtons">
{currentTab !== 0 ? (
<Button
type="settings"
onClick={() => this.changeTab(true)}
icon={<MdArrowBackIosNew />}
label={variables.getMessage('modals.welcome.buttons.previous')}
/>
) : (
<Button
type="settings"
onClick={() => modalSkip()}
icon={<MdOutlinePreview />}
label={variables.getMessage('modals.welcome.buttons.preview')}
/>
)}
<Button
type="settings"
onClick={() => this.changeTab()}
icon={<MdArrowForwardIos />}
label={buttonText}
iconPlacement={'right'}
/>
</div>
);
}
render() { render() {
const tabComponents = { const tabComponents = {
0: <Intro />, 0: <Intro />,
@ -114,37 +143,14 @@ class WelcomeModal extends PureComponent {
<Panel type="aside"> <Panel type="aside">
<AsideImage currentTab={this.state.currentTab} /> <AsideImage currentTab={this.state.currentTab} />
<ProgressBar <ProgressBar
count={this.images} numberOfTabs={this.state.finalTab + 2}
currentTab={this.state.currentTab} currentTab={this.state.currentTab}
switchTab={(tab) => this.switchTab(tab)} switchTab={(tab) => this.switchTab(tab)}
/> />
</Panel> </Panel>
<Panel type="content"> <Panel type="content">
{CurrentSection} {CurrentSection}
<div className="welcomeButtons"> {this.renderButtons()}
{this.state.currentTab !== 0 ? (
<Button
type="settings"
onClick={() => this.changeTab(true)}
icon={<MdArrowBackIosNew />}
label={variables.getMessage('modals.welcome.buttons.previous')}
/>
) : (
<Button
type="settings"
onClick={() => this.props.modalSkip()}
icon={<MdOutlinePreview />}
label={variables.getMessage('modals.welcome.buttons.preview')}
/>
)}
<Button
type="settings"
onClick={() => this.changeTab()}
icon={<MdArrowForwardIos />}
label={this.state.buttonText}
iconPlacement={'right'}
/>
</div>
</Panel> </Panel>
</Wrapper> </Wrapper>
); );

View File

@ -1,22 +1,26 @@
import { memo } from 'react'; import { memo } from 'react';
function ProgressBar({ count, currentTab, switchTab }) { const Step = memo(({ isActive, index, onClick }) => {
const className = isActive ? 'step active' : 'step';
return (
<div className={className} onClick={onClick}>
<span>{index + 1}</span>
</div>
);
});
function ProgressBar({ numberOfTabs, currentTab, switchTab }) {
return ( return (
<div className="progressbar"> <div className="progressbar">
{count.map((num) => { {Array.from({ length: numberOfTabs }, (_, index) => (
let className = 'step'; <Step
key={index}
const index = count.indexOf(num); isActive={index === currentTab}
if (index === currentTab) { index={index}
className = 'step active'; onClick={() => switchTab(index)}
} />
))}
return (
<div className={className} key={index} onClick={() => switchTab(index)}>
<span>{index + 1}</span>
</div>
);
})}
</div> </div>
); );
} }

View File

@ -0,0 +1,7 @@
const Content = ({ children }) => {
return (
<div className="content">{children}</div>
)
}
export { Content as default, Content };

View File

@ -1,12 +1,6 @@
const Panel = ({ children, type }) => ( const Panel = ({ children, type }) => (
<section className={type}> <section className={type}>
{type === 'content' ? ( {children}
<div className="content">{children}</div>
) : type === 'aside' ? (
<>{children}</>
) : (
children
)}
</section> </section>
); );

View File

@ -1,3 +1,4 @@
export * from './Wrapper'; export * from './Wrapper';
export * from './Panel'; export * from './Panel';
export * from './Header'; export * from './Header';
export * from './Content';

View File

@ -89,6 +89,16 @@
} }
.themesToggleArea { .themesToggleArea {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: auto auto;
div:nth-child(1) {
grid-column: span 1 / span 1 !important;
}
div:nth-child(2),
div:nth-child(3) {
grid-column: span 1 / span 1 !important;
}
@include themed { @include themed {
.active { .active {
background: t($modal-sidebarActive); background: t($modal-sidebarActive);
@ -199,6 +209,7 @@ a.privacy {
flex-flow: column; flex-flow: column;
.shareYourMue { .shareYourMue {
width: -moz-fit-content;
width: fit-content; width: fit-content;
} }
@ -305,6 +316,7 @@ a.privacy {
.welcomeButtons { .welcomeButtons {
z-index: 999; z-index: 999;
-webkit-backdrop-filter: blur(2px);
backdrop-filter: blur(2px); backdrop-filter: blur(2px);
position: sticky; position: sticky;
bottom: 0; bottom: 0;