refactor(welcome): Partition into different files + shared components

- This took too long and destroyed my sanity
This commit is contained in:
alexsparkes 2024-02-18 23:04:35 +00:00
parent d45b7a4a6a
commit 89a16092da
20 changed files with 481 additions and 30 deletions

View File

@ -19,8 +19,8 @@ export default class About extends PureComponent {
update: variables.getMessage('modals.main.settings.sections.about.version.checking_update'),
loading: variables.getMessage('modals.main.loading'),
image: document.body.classList.contains('dark')
? 'icons/mue_about.png'
: 'icons/mue_about.png',
? 'src/assets/icons/mue_about.png'
: 'src/assets/icons/mue_about.png',
};
this.controller = new AbortController();
}

View File

@ -0,0 +1,40 @@
import variables from 'config/variables';
import { MdOutlineOpenInNew } from 'react-icons/md';
import languages from '@/i18n/languages.json';
import { Radio } from 'components/Form/Settings';
import { Header } from '../components/Layout';
function ChooseLanguage() {
return (
<>
<Header
title={variables.getMessage('modals.welcome.sections.language.title')}
subtitle={variables.getMessage('modals.welcome.sections.language.description')}
/>
<a
href={variables.constants.TRANSLATIONS_URL}
className="link"
target="_blank"
rel="noopener noreferrer"
>
GitHub
<MdOutlineOpenInNew />
</a>
<a
href={variables.constants.WEBLATE_URL}
className="link"
target="_blank"
rel="noopener noreferrer"
>
Weblate
<MdOutlineOpenInNew />
</a>
<div className="languageSettings">
<Radio name="language" options={languages} category="welcomeLanguage" />
</div>
</>
);
}
export { ChooseLanguage as default, ChooseLanguage };

View File

@ -0,0 +1,45 @@
import variables from 'config/variables';
import languages from '@/i18n/languages.json';
import { Header } from '../components/Layout';
function Final(props) {
return (
<>
<Header
title={variables.getMessage('modals.welcome.sections.final.title')}
subtitle={variables.getMessage('modals.welcome.sections.final.description')}
/>
<span className="title">{variables.getMessage('modals.welcome.sections.final.changes')}</span>
<span className="subtitle">
{variables.getMessage('modals.welcome.sections.final.changes_description')}
</span>
<div className="themesToggleArea themesToggleAreaWelcome">
<div className="toggle" onClick={() => props.switchTab(1)}>
<span>
{variables.getMessage('modals.main.settings.sections.language.title')}:{' '}
{languages.find((i) => i.value === localStorage.getItem('language')).name}
</span>
</div>
<div className="toggle" onClick={() => props.switchTab(3)}>
<span>
{variables.getMessage('modals.main.settings.sections.appearance.theme.title')}:{' '}
{variables.getMessage(
'modals.main.settings.sections.appearance.theme.' + localStorage.getItem('theme'),
)}
</span>
</div>
{/*}
{this.state.importedSettings.length !== 0 && (
<div className="toggle" onClick={() => this.props.switchTab(2)}>
{variables.getMessage('modals.main.settings.sections.final.imported', {
amount: this.state.importedSettings.length,
})}{' '}
{this.state.importedSettings.length}
</div>
)}*/}
</div>
</>
);
}
export { Final as default, Final };

View File

@ -0,0 +1,69 @@
import variables from 'config/variables';
import { useState } from 'react';
import { FileUpload } from 'components/Form/Settings';
import { MdCloudUpload } from 'react-icons/md';
import { importSettings as importSettingsFunction } from 'modules/helpers/settings/modals';
import { Header } from '../components/Layout';
import default_settings from 'modules/default_settings.json';
function ImportSettings(props) {
const [importedSettings, setImportedSettings] = useState([]);
const importSettings = (e) => {
importSettingsFunction(e);
const settings = [];
const data = JSON.parse(e.target.result);
Object.keys(data).forEach((setting) => {
// language and theme already shown, the others are only used internally
if (
setting === 'language' ||
setting === 'theme' ||
setting === 'firstRun' ||
setting === 'showWelcome' ||
setting === 'showReminder'
) {
return;
}
const defaultSetting = default_settings.find((i) => i.name === setting);
if (defaultSetting !== undefined) {
if (data[setting] === String(defaultSetting.value)) {
return;
}
}
settings.push({
name: setting,
value: data[setting],
});
});
setImportedSettings(settings);
props.switchTab(5);
};
return (
<>
<Header
title={variables.getMessage('modals.welcome.sections.settings.title')}
subtitle={variables.getMessage('modals.welcome.sections.settings.description')}
/>
<button className="upload" onClick={() => document.getElementById('file-input').click()}>
<MdCloudUpload />
<span>{variables.getMessage('modals.main.settings.buttons.import')}</span>
</button>
<FileUpload
id="file-input"
accept="application/json"
type="settings"
loadFunction={(e) => importSettings(e)}
/>
<span className="title">{variables.getMessage('modals.welcome.tip')}</span>
<span className="subtitle">
{variables.getMessage('modals.welcome.sections.settings.tip')}
</span>
</>
);
}
export { ImportSettings as default, ImportSettings };

View File

@ -0,0 +1,82 @@
import variables from 'config/variables';
import { useState, useEffect } from 'react';
import { Header } from '../components/Layout';
import { MdOutlineWavingHand, MdOpenInNew } from 'react-icons/md';
import { FaDiscord, FaGithub } from 'react-icons/fa';
function Intro() {
const [welcomeImage, setWelcomeImage] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setWelcomeImage(prevWelcomeImage => prevWelcomeImage < 3 ? prevWelcomeImage + 1 : 0);
}, 3000);
// Cleanup function to clear the interval when the component unmounts
return () => clearInterval(timer);
}, [welcomeImage]);
return (
<>
<Header title={variables.getMessage('modals.welcome.sections.intro.title')} />
<div className="examples">
<img
src={`/src/assets/welcome-images/example${welcomeImage + 1}.webp`}
alt="Example Mue setup"
draggable={false}
/>
<span className="shareYourMue">#shareyourmue</span>
</div>
<div className="welcomeNotice">
<div className="icon">
<MdOutlineWavingHand />
</div>
<div className="text">
<span className="title">
{variables.getMessage('modals.welcome.sections.intro.title')}
</span>
<span className="subtitle">
{variables.getMessage('modals.welcome.sections.intro.description')}
</span>
</div>
</div>
<div className="welcomeNotice">
<div className="icon">
<FaDiscord />
</div>
<div className="text">
<span className="title">
{variables.getMessage('modals.welcome.sections.intro.notices.discord_title')}
</span>
<span className="subtitle">
{variables.getMessage('modals.welcome.sections.intro.notices.discord_description')}
</span>
</div>
<a href="https://discord.gg/zv8C9F8" target="_blank" rel="noopener noreferrer">
<MdOpenInNew />{' '}
{variables.getMessage('modals.welcome.sections.intro.notices.discord_join')}
</a>
</div>
<div className="welcomeNotice">
<div className="icon">
<FaGithub />
</div>
<div className="text">
<span className="title">
{variables.getMessage('modals.welcome.sections.intro.notices.github_title')}
</span>
<span className="subtitle">
{variables.getMessage('modals.welcome.sections.intro.notices.github_description')}
</span>
</div>
<a href="https://github.com/mue/mue" target="_blank" rel="noopener noreferrer">
<MdOpenInNew />
{variables.getMessage('modals.welcome.sections.intro.notices.github_open')}
</a>
</div>
</>
);
}
export { Intro as default, Intro };

View File

@ -0,0 +1,59 @@
import variables from 'config/variables';
import { MdOutlineOpenInNew } from 'react-icons/md';
import { Checkbox } from 'components/Form/Settings';
import { Header } from '../components/Layout';
function PrivacyOptions() {
return (
<>
<Header
title={variables.getMessage('modals.welcome.sections.privacy.title')}
subtitle={variables.getMessage('modals.welcome.sections.privacy.description')}
/>
<Checkbox
name="offlineMode"
text={variables.getMessage('modals.main.settings.sections.advanced.offline_mode')}
element=".other"
/>
<span className="subtitle">
{variables.getMessage('modals.welcome.sections.privacy.offline_mode_description')}
</span>
<Checkbox
name="ddgProxy"
text={
variables.getMessage('modals.main.settings.sections.background.ddg_image_proxy') +
' (' +
variables.getMessage('modals.main.settings.sections.background.title') +
')'
}
/>
<span className="subtitle">
{variables.getMessage('modals.welcome.sections.privacy.ddg_proxy_description')}
</span>
<span className="title">
{variables.getMessage('modals.welcome.sections.privacy.links.title')}
</span>
<a
className="link"
href={variables.constants.PRIVACY_URL}
target="_blank"
rel="noopener noreferrer"
>
{variables.getMessage('modals.welcome.sections.privacy.links.privacy_policy')}
<MdOutlineOpenInNew />
</a>
<a
className="link"
href={'https://github.com/' + variables.constants.ORG_NAME}
target="_blank"
rel="noopener noreferrer"
>
{variables.getMessage('modals.welcome.sections.privacy.links.source_code')}
<MdOutlineOpenInNew />
</a>
</>
);
}
export { PrivacyOptions as default, PrivacyOptions };

View File

@ -0,0 +1,41 @@
import variables from 'config/variables';
import { MdArchive, MdOutlineWhatshot } from 'react-icons/md';
import { useState } from 'react';
import { Header } from '../components/Layout';
function StyleSelection() {
const widgetStyle = localStorage.getItem('widgetStyle');
const [style, setStyle] = useState({
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 (
<>
<Header title={variables.getMessage('modals.welcome.sections.style.title')} subtitle={variables.getMessage('modals.welcome.sections.style.description')} />
<div className="themesToggleArea">
<div className="options">
<div className={style.legacyStyle} onClick={() => changeStyle('legacy')}>
<MdArchive />
<span>{variables.getMessage('modals.welcome.sections.style.legacy')}</span>
</div>
<div className={style.newStyle} onClick={() => changeStyle('new')}>
<MdOutlineWhatshot />
<span>{variables.getMessage('modals.welcome.sections.style.modern')}</span>
</div>
</div>
</div>
</>
)
}
export { StyleSelection as default, StyleSelection };

View File

@ -0,0 +1,56 @@
import variables from 'config/variables';
import { useState } from 'react';
import { MdAutoAwesome, MdLightMode, MdDarkMode } from 'react-icons/md';
import { loadSettings } from 'modules/helpers/settings';
import { Header } from '../components/Layout';
function ThemeSelection() {
const [theme, setTheme] = useState({
autoClass: 'toggle auto active',
lightClass: 'toggle lightTheme',
darkClass: 'toggle darkTheme',
});
const changeTheme = (type) => {
setTheme({
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);
loadSettings(true);
};
return (
<>
<Header
title={variables.getMessage('modals.welcome.sections.theme.title')}
subtitle={variables.getMessage('modals.welcome.sections.theme.description')}
/>
<div className="themesToggleArea">
<div className={theme.autoClass} onClick={() => changeTheme('auto')}>
<MdAutoAwesome />
<span>{variables.getMessage('modals.main.settings.sections.appearance.theme.auto')}</span>
</div>
<div className="options">
<div className={theme.lightClass} onClick={() => changeTheme('light')}>
<MdLightMode />
<span>
{variables.getMessage('modals.main.settings.sections.appearance.theme.light')}
</span>
</div>
<div className={theme.darkClass} onClick={() => changeTheme('dark')}>
<MdDarkMode />
<span>
{variables.getMessage('modals.main.settings.sections.appearance.theme.dark')}
</span>
</div>
</div>
</div>
<span className="title">{variables.getMessage('modals.welcome.tip')}</span>
<span className="subtitle">{variables.getMessage('modals.welcome.sections.theme.tip')}</span>
</>
);
}
export { ThemeSelection as default, ThemeSelection };

View File

@ -0,0 +1,7 @@
export * from './Intro';
export * from './ChooseLanguage';
export * from './ImportSettings';
export * from './ThemeSelection';
export * from './StyleSelection';
export * from './PrivacyOptions';
export * from './Final';

View File

@ -4,29 +4,39 @@ import { MdArrowBackIosNew, MdArrowForwardIos, MdOutlinePreview } from 'react-ic
import EventBus from 'modules/helpers/eventbus';
import WelcomeSections from './WelcomeSections';
import ProgressBar from './ProgressBar';
import { ProgressBar } from './components/Elements';
import { Button } from 'components/Elements';
import { Wrapper, Panel } from './components/Layout';
import './welcome.scss';
import {
Intro,
ChooseLanguage,
ImportSettings,
ThemeSelection,
StyleSelection,
PrivacyOptions,
Final,
} from './Sections';
class WelcomeModal extends PureComponent {
constructor() {
super();
this.state = {
image: './././icons/undraw_celebration.svg',
image: '/src/assets/icons/undraw_celebration.svg',
currentTab: 0,
finalTab: 5,
buttonText: variables.getMessage('modals.welcome.buttons.next'),
};
this.images = [
'./././icons/undraw_celebration.svg',
'./././icons/undraw_around_the_world_modified.svg',
'./././icons/undraw_add_files_modified.svg',
'./././icons/undraw_dark_mode.svg',
'./././icons/undraw_making_art.svg',
'./././icons/undraw_private_data_modified.svg',
'./././icons/undraw_upgrade_modified.svg',
'/src/assets/icons/undraw_celebration.svg',
'/src/assets/icons/undraw_around_the_world_modified.svg',
'/src/assets/icons/undraw_add_files_modified.svg',
'/src/assets/icons/undraw_dark_mode.svg',
'/src/assets/icons/undraw_making_art.svg',
'/src/assets/icons/undraw_private_data_modified.svg',
'/src/assets/icons/undraw_upgrade_modified.svg',
];
}
@ -98,9 +108,20 @@ class WelcomeModal extends PureComponent {
}
render() {
const tabComponents = {
0: <Intro />,
1: <ChooseLanguage />,
2: <ImportSettings switchTab={(tab) => this.switchTab(tab)} />,
3: <ThemeSelection />,
4: <StyleSelection />,
5: <PrivacyOptions />,
6: <Final currentTab={this.state.currentTab} switchTab={(tab) => this.switchTab(tab)} />,
};
let CurrentSection = tabComponents[this.state.currentTab] || <Intro />;
return (
<div className="welcomeContent">
<section>
<Wrapper>
<Panel type="aside">
<img
className="showcaseimg"
alt="sidebar icon"
@ -112,14 +133,9 @@ class WelcomeModal extends PureComponent {
currentTab={this.state.currentTab}
switchTab={(tab) => this.switchTab(tab)}
/>
</section>
<section>
<div className="content">
<WelcomeSections
currentTab={this.state.currentTab}
switchTab={(tab) => this.switchTab(tab)}
/>
</div>
</Panel>
<Panel type="content">
{CurrentSection}
<div className="welcomeButtons">
{this.state.currentTab !== 0 ? (
<Button
@ -144,8 +160,8 @@ class WelcomeModal extends PureComponent {
iconPlacement={'right'}
/>
</div>
</section>
</div>
</Panel>
</Wrapper>
);
}
}

View File

@ -13,10 +13,7 @@ import {
MdOutlineOpenInNew,
} from 'react-icons/md';
import { FaDiscord, FaGithub } from 'react-icons/fa';
import Radio from '../../../components/Form/Settings/Radio/Radio';
import Checkbox from '../../../components/Form/Settings/Checkbox/Checkbox';
import FileUpload from '../../../components/Form/Settings/FileUpload/FileUpload';
import { Radio, Checkbox, FileUpload } from 'components/Form/Settings';
import { loadSettings } from 'modules/helpers/settings';
import { importSettings } from 'modules/helpers/settings/modals';
@ -146,7 +143,7 @@ class WelcomeSections extends PureComponent {
</span>
<div className="examples">
<img
src={`./welcome-images/example${this.state.welcomeImage + 1}.webp`}
src={`/src/assets/welcome-images/example${this.state.welcomeImage + 1}.webp`}
alt="Example Mue setup"
draggable={false}
/>

View File

@ -21,4 +21,7 @@ function ProgressBar({ count, currentTab, switchTab }) {
);
}
export default memo(ProgressBar);
const MemoizedProgressBar = memo(ProgressBar);
export default MemoizedProgressBar;
export { MemoizedProgressBar as ProgressBar };

View File

@ -0,0 +1 @@
export * from './ProgressBar';

View File

@ -0,0 +1 @@
export * from './ProgressBar';

View File

@ -0,0 +1,12 @@
function Header({ title, subtitle }) {
return (
<>
<span className="mainTitle">{title}</span>
<span className="subtitle">
{subtitle}
</span>
</>
);
}
export { Header as default, Header };

View File

@ -0,0 +1,7 @@
function Layout() {
return (
<h1>Cheese</h1>
)
}
export { Layout as default, Layout };

View File

@ -0,0 +1,5 @@
const Panel = ({ children, type }) => (
<section> {type === 'content' ? <div className="content">{children}</div> : children}</section>
);
export { Panel as default, Panel };

View File

@ -0,0 +1,5 @@
const Wrapper = ({ children }) => (
<div className="welcomeContent">{children}</div>
)
export { Wrapper as default, Wrapper };

View File

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

View File

@ -0,0 +1,2 @@
export * from './Layout';
export * from './Elements';