refactor(features): Change organisation of welcome + Marketplace

This commit is contained in:
alexsparkes
2024-02-27 17:07:32 +00:00
parent 6041372860
commit dbd85cdc95
43 changed files with 7 additions and 7 deletions

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, Content } from '../components/Layout';
function ChooseLanguage() {
return (
<Content>
<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>
</Content>
);
}
export { ChooseLanguage as default, ChooseLanguage };

View File

@@ -0,0 +1,45 @@
import variables from 'config/variables';
import languages from '@/i18n/languages.json';
import { Header, Content } from '../components/Layout';
function Final(props) {
return (
<Content>
<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>
</Content>
);
}
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 'utils/settings';
import { Header, Content } from '../components/Layout';
import default_settings from 'utils/data/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 (
<Content>
<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>
</Content>
);
}
export { ImportSettings as default, ImportSettings };

View File

@@ -0,0 +1,91 @@
import variables from 'config/variables';
import { useState, useEffect, useCallback } from 'react';
import { Header, Content } from '../components/Layout';
import { MdOutlineWavingHand, MdOpenInNew } from 'react-icons/md';
import { FaDiscord, FaGithub } from 'react-icons/fa';
const DISCORD_LINK = 'https://discord.gg/' + variables.constants.DISCORD_SERVER;
const GITHUB_LINK =
'https://github.com/' + variables.constants.ORG_NAME + '/' + variables.constants.REPO_NAME;
function WelcomeNotice({ config }) {
const { icon: Icon, title, subtitle, link } = config;
return (
<div className="welcomeNotice">
<div className="icon">
<Icon />
</div>
<div className="text">
<span className="title">{title}</span>
<span className="subtitle">{subtitle}</span>
</div>
{link && (
<a href={link} target="_blank" rel="noopener noreferrer">
<MdOpenInNew />
{variables.getMessage('modals.welcome.sections.intro.notices.github_open')}
</a>
)}
</div>
);
}
function Intro() {
const [welcomeImage, setWelcomeImage] = useState(0);
const updateWelcomeImage = useCallback(() => {
setWelcomeImage((prevWelcomeImage) => (prevWelcomeImage < 3 ? prevWelcomeImage + 1 : 0));
}, []);
const ShareYourMue = (
<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>
);
useEffect(() => {
const timer = setInterval(updateWelcomeImage, 3000);
return () => clearInterval(timer);
}, [updateWelcomeImage]);
return (
<Content>
<Header title={variables.getMessage('modals.welcome.sections.intro.title')} />
{ShareYourMue}
<WelcomeNotice
config={{
icon: MdOutlineWavingHand,
title: variables.getMessage('modals.welcome.sections.intro.title'),
subtitle: variables.getMessage('modals.welcome.sections.intro.description'),
}}
/>
<WelcomeNotice
config={{
icon: FaDiscord,
title: variables.getMessage('modals.welcome.sections.intro.notices.discord_title'),
subtitle: variables.getMessage(
'modals.welcome.sections.intro.notices.discord_description',
),
link: DISCORD_LINK,
}}
/>
<WelcomeNotice
config={{
icon: FaGithub,
title: variables.getMessage('modals.welcome.sections.intro.notices.github_title'),
subtitle: variables.getMessage(
'modals.welcome.sections.intro.notices.github_description',
),
link: GITHUB_LINK,
}}
/>
</Content>
);
}
export { Intro as default, Intro };

View File

@@ -0,0 +1,78 @@
import variables from 'config/variables';
import { MdOutlineOpenInNew } from 'react-icons/md';
import { Checkbox } from 'components/Form/Settings';
import { Header, Content } from '../components/Layout';
function OfflineMode() {
return (
<>
<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>
</>
);
}
function DuckDuckGoProxy() {
return (
<>
<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>
</>
);
}
function Links() {
return (
<>
<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>
</>
);
}
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

@@ -0,0 +1,53 @@
import variables from 'config/variables';
import { MdArchive, MdOutlineWhatshot } from 'react-icons/md';
import { useState } from 'react';
import { Header, Content } from '../components/Layout';
const STYLES = {
NEW: 'new',
LEGACY: 'legacy',
};
const StyleSelection = () => {
const widgetStyle = localStorage.getItem('widgetStyle') || STYLES.NEW;
const [style, setStyle] = useState(widgetStyle);
const changeStyle = (type) => {
setStyle(type);
localStorage.setItem('widgetStyle', type);
};
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>
</Content>
);
};
export { StyleSelection as default, StyleSelection };

View File

@@ -0,0 +1,70 @@
import variables from 'config/variables';
import { useState } from 'react';
import { MdAutoAwesome, MdLightMode, MdDarkMode } from 'react-icons/md';
import { loadSettings } from 'utils/settings';
import { Header, Content } from '../components/Layout';
const THEMES = {
AUTO: 'auto',
LIGHT: 'light',
DARK: 'dark',
};
function ThemeSelection() {
const currentTheme = localStorage.getItem('theme') || THEMES.AUTO;
const [theme, setTheme] = useState(currentTheme);
const changeTheme = (type) => {
setTheme(type);
localStorage.setItem('theme', type);
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 (
<Content>
<Header
title={variables.getMessage('modals.welcome.sections.theme.title')}
subtitle={variables.getMessage('modals.welcome.sections.theme.description')}
/>
<div className="themesToggleArea">
<div className={themeMapping[THEMES.AUTO].className} onClick={() => changeTheme(THEMES.AUTO)}>
{themeMapping[THEMES.AUTO].icon}
<span>{themeMapping[THEMES.AUTO].text}</span>
</div>
<div className="options">
{Object.entries(themeMapping)
.filter(([type]) => type !== THEMES.AUTO)
.map(([type, { className, icon, text }]) => (
<div className={className} onClick={() => changeTheme(type)} key={type}>
{icon}
<span>{text}</span>
</div>
))}
</div>
</div>
<span className="title">{variables.getMessage('modals.welcome.tip')}</span>
<span className="subtitle">{variables.getMessage('modals.welcome.sections.theme.tip')}</span>
</Content>
);
}
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

@@ -0,0 +1,159 @@
// Importing necessary libraries and components
import { useState, useEffect } from 'react';
import variables from 'config/variables';
import { MdArrowBackIosNew, MdArrowForwardIos, MdOutlinePreview } from 'react-icons/md';
import EventBus from 'utils/eventbus';
import { ProgressBar, AsideImage } 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';
// WelcomeModal component
function WelcomeModal({ modalClose, modalSkip }) {
// State variables
const [currentTab, setCurrentTab] = useState(0);
const [buttonText, setButtonText] = useState(variables.getMessage('modals.welcome.buttons.next'));
const finalTab = 6;
// useEffect hook to handle tab changes and event bus listener
useEffect(() => {
// Get the current welcome tab from local storage
const welcomeTab = localStorage.getItem('welcomeTab');
if (welcomeTab) {
const tab = Number(welcomeTab);
setCurrentTab(tab);
setButtonText(
tab !== finalTab + 1
? variables.getMessage('modals.welcome.buttons.next')
: variables.getMessage('modals.welcome.buttons.finish'),
);
}
// Listener for the 'refresh' event
const refreshListener = (data) => {
if (data === 'welcomeLanguage') {
localStorage.setItem('welcomeTab', currentTab);
localStorage.setItem('bgtransition', false);
window.location.reload();
}
};
// Subscribe to the 'refresh' event
EventBus.on('refresh', refreshListener);
// Cleanup function to unsubscribe from the 'refresh' event
return () => {
EventBus.off('refresh', refreshListener);
};
}, [currentTab, finalTab]);
// Function to update the current tab and button text
const updateTabAndButtonText = (newTab) => {
setCurrentTab(newTab);
setButtonText(
newTab !== finalTab
? variables.getMessage('modals.welcome.buttons.next')
: variables.getMessage('modals.welcome.buttons.finish'),
);
localStorage.setItem('bgtransition', true);
localStorage.removeItem('welcomeTab');
};
// Functions to navigate to the previous and next tabs
const prevTab = () => {
updateTabAndButtonText(currentTab - 1);
};
const nextTab = () => {
if (buttonText === variables.getMessage('modals.welcome.buttons.finish')) {
modalClose();
return;
}
updateTabAndButtonText(currentTab + 1);
};
// Function to switch to a specific tab
const switchToTab = (tab) => {
updateTabAndButtonText(tab);
};
// Navigation component
const Navigation = () => {
return (
<div className="welcomeButtons">
{currentTab !== 0 ? (
<Button
type="settings"
onClick={() => prevTab()}
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={() => nextTab()}
icon={<MdArrowForwardIos />}
label={buttonText}
iconPlacement={'right'}
/>
</div>
);
};
// Mapping of tab numbers to components
const tabComponents = {
0: <Intro />,
1: <ChooseLanguage />,
2: <ImportSettings switchTab={switchToTab} />,
3: <ThemeSelection />,
4: <StyleSelection />,
5: <PrivacyOptions />,
6: <Final currentTab={currentTab} switchTab={switchToTab} />,
};
// Current tab component
let CurrentTab = tabComponents[currentTab] || <Intro />;
// Render the WelcomeModal component
return (
<Wrapper>
<Panel type="aside">
<AsideImage currentTab={currentTab} />
<ProgressBar numberOfTabs={finalTab + 1} currentTab={currentTab} switchTab={switchToTab} />
</Panel>
<Panel type="content">
{CurrentTab}
<Navigation
currentTab={currentTab}
changeTab={switchToTab}
buttonText={buttonText}
modalSkip={modalSkip}
/>
</Panel>
</Wrapper>
);
}
// Export the WelcomeModal component
export default WelcomeModal;

View File

@@ -0,0 +1,432 @@
import variables from 'config/variables';
import { PureComponent } from 'react';
import {
MdCloudUpload,
MdAutoAwesome,
MdLightMode,
MdDarkMode,
MdOutlineWavingHand,
MdOpenInNew,
MdOutlineWhatshot,
MdArchive,
MdOutlineOpenInNew,
} from 'react-icons/md';
import { FaDiscord, FaGithub } from 'react-icons/fa';
import { Radio, Checkbox, FileUpload } from 'components/Form/Settings';
import { loadSettings, importSettings } from 'utils/settings';
import default_settings from 'utils/data/default_settings.json';
import languages from '@/i18n/languages.json';
class WelcomeSections extends PureComponent {
constructor() {
super();
this.state = {
// themes
autoClass: 'toggle auto active',
lightClass: 'toggle lightTheme',
darkClass: 'toggle darkTheme',
// styles
newStyle: 'toggle newStyle active',
legacyStyle: 'toggle legacyStyle',
// welcome
welcomeImage: 0,
// final
importedSettings: [],
};
this.changeWelcomeImg = this.changeWelcomeImg.bind(this);
this.welcomeImages = 3;
}
changeTheme(type) {
this.setState({
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);
}
changeStyle(type) {
this.setState({
newStyle: type === 'new' ? 'toggle newStyle active' : 'toggle newStyle',
legacyStyle: type === 'legacy' ? 'toggle legacyStyle active' : 'toggle legacyStyle',
});
localStorage.setItem('widgetStyle', type);
}
getSetting(name) {
const value = localStorage.getItem(name).replace('false', 'Off').replace('true', 'On');
return value.charAt(0).toUpperCase() + value.slice(1);
}
importSettings(e) {
importSettings(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],
});
});
this.setState({
importedSettings: settings,
});
this.props.switchTab(5);
}
changeWelcomeImg() {
let welcomeImage = this.state.welcomeImage;
this.setState({
welcomeImage: welcomeImage < 3 ? ++welcomeImage : 0,
});
this.timeout = setTimeout(this.changeWelcomeImg, 3000);
}
componentDidMount() {
this.timeout = setTimeout(this.changeWelcomeImg, 3000);
}
componentWillUnmount() {
if (this.timeout) {
clearTimeout(this.timeout);
this.timeout = null;
}
}
// cancel welcome image timer if not on welcome tab
componentDidUpdate() {
if (this.props.currentTab !== 0) {
if (this.timeout) {
clearTimeout(this.timeout);
this.timeout = null;
}
} else if (!this.timeout) {
this.timeout = setTimeout(this.changeWelcomeImg, 3000);
}
}
render() {
const intro = (
<>
<span className="mainTitle">
{variables.getMessage('modals.welcome.sections.intro.title')}
</span>
<div className="examples">
<img
src={`/src/assets/welcome-images/example${this.state.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>
</>
);
const chooseLanguage = (
<>
<span className="mainTitle">
{variables.getMessage('modals.welcome.sections.language.title')}
</span>
<span className="subtitle">
{variables.getMessage('modals.welcome.sections.language.description')}{' '}
</span>
<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>
</>
);
const theme = (
<>
<span className="mainTitle">
{variables.getMessage('modals.welcome.sections.theme.title')}
</span>
<span className="subtitle">
{variables.getMessage('modals.welcome.sections.theme.description')}
</span>
<div className="themesToggleArea">
<div className={this.state.autoClass} onClick={() => this.changeTheme('auto')}>
<MdAutoAwesome />
<span>
{variables.getMessage('modals.main.settings.sections.appearance.theme.auto')}
</span>
</div>
<div className="options">
<div className={this.state.lightClass} onClick={() => this.changeTheme('light')}>
<MdLightMode />
<span>
{variables.getMessage('modals.main.settings.sections.appearance.theme.light')}
</span>
</div>
<div className={this.state.darkClass} onClick={() => this.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>
</>
);
const style = (
<>
<span className="mainTitle">
{variables.getMessage('modals.welcome.sections.style.title')}
</span>
<span className="subtitle">
{variables.getMessage('modals.welcome.sections.style.description')}
</span>
<div className="themesToggleArea">
<div className="options">
<div className={this.state.legacyStyle} onClick={() => this.changeStyle('legacy')}>
<MdArchive />
<span>{variables.getMessage('modals.welcome.sections.style.legacy')}</span>
</div>
<div className={this.state.newStyle} onClick={() => this.changeStyle('new')}>
<MdOutlineWhatshot />
<span>{variables.getMessage('modals.welcome.sections.style.modern')}</span>
</div>
</div>
</div>
</>
);
const settings = (
<>
<span className="mainTitle">
{variables.getMessage('modals.welcome.sections.settings.title')}
</span>
<span className="subtitle">
{variables.getMessage('modals.welcome.sections.settings.description')}
</span>
<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) => this.importSettings(e)}
/>
<span className="title">{variables.getMessage('modals.welcome.tip')}</span>
<span className="subtitle">
{variables.getMessage('modals.welcome.sections.settings.tip')}
</span>
</>
);
const privacy = (
<>
<span className="mainTitle">
{variables.getMessage('modals.welcome.sections.privacy.title')}
</span>
<span className="subtitle">
{variables.getMessage('modals.welcome.sections.privacy.description')}
</span>
<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>
</>
);
const final = (
<>
<span className="mainTitle">
{variables.getMessage('modals.welcome.sections.final.title')}
</span>
<span className="subtitle">
{variables.getMessage('modals.welcome.sections.final.description')}
</span>
<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={() => this.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={() => this.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>
</>
);
switch (this.props.currentTab) {
case 1:
return chooseLanguage;
case 2:
return settings;
case 3:
return theme;
case 4:
return style;
case 5:
return privacy;
case 6:
return final;
// 0
default:
return intro;
}
}
}
export default WelcomeSections;

View File

@@ -0,0 +1,31 @@
const images = [
'/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',
];
function AsideImage({ currentTab }) {
const altTexts = [
'Celebration icon',
'Around the world icon',
'Add files icon',
'Dark mode icon',
'Making art icon',
'Private data icon',
'Upgrade icon',
];
return (
<img
className="showcaseimg"
alt={altTexts[currentTab]}
draggable={false}
src={images[currentTab]}
/>
);
}
export { AsideImage as default, AsideImage };

View File

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

View File

@@ -0,0 +1,31 @@
import { memo } from 'react';
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 (
<div className="progressbar">
{Array.from({ length: numberOfTabs }, (_, index) => (
<Step
key={index}
isActive={index === currentTab}
index={index}
onClick={() => switchTab(index)}
/>
))}
</div>
);
}
const MemoizedProgressBar = memo(ProgressBar);
export default MemoizedProgressBar;
export { MemoizedProgressBar as ProgressBar };

View File

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

View File

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

View File

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

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,7 @@
const Panel = ({ children, type }) => (
<section className={type}>
{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,4 @@
export * from './Wrapper';
export * from './Panel';
export * from './Header';
export * from './Content';

View File

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

View File

@@ -0,0 +1,331 @@
@import '../../scss/index.scss';
@import 'scss/variables';
.welcomemodal {
@include themed {
background-color: t($modal-background);
}
}
.welcomeContent {
@include themed {
background-color: t($modal-background);
}
.MuiFormControlLabel-root {
margin-right: 0;
}
.link {
display: flex;
flex-flow: row;
gap: 15px;
align-items: center;
}
@extend %tabText;
height: 80vh;
width: clamp(60vw, 1200px, 90vw);
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-rows: repeat(1, 1fr);
grid-gap: 0;
section.aside {
display: flex;
align-items: center;
justify-content: center;
@include themed {
background-color: t($modal-sidebar);
}
}
section.content {
display: flex;
flex-flow: column;
justify-content: space-between;
overflow-y: auto;
.content {
display: flex;
flex-flow: column;
padding: 25px;
gap: 20px;
}
}
}
.progressbar {
position: fixed;
bottom: 50px;
text-align: center;
display: inline;
overflow: hidden;
white-space: nowrap;
.step {
display: inline-block;
border-bottom: 2px solid grey;
padding: 10px 20px;
margin: 5px;
transition: 0.2s ease-in-out;
cursor: pointer;
border-radius: 10px 10px 0 0;
&:hover {
background: #dd4038;
border-radius: 10px;
border-bottom: 2px solid #dd4038;
}
}
.active {
background: #d21a11;
border-bottom: 2px solid #d21a11;
border-radius: 10px;
}
}
.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 {
.active {
background: t($modal-sidebarActive);
}
.toggle {
background: t($modal-sidebar);
text-align: center;
border-radius: 20px;
padding: 20px;
border: 3px solid t($modal-sidebarActive);
transition: 0.33s;
display: flex;
flex-direction: column;
align-items: center;
place-content: center center;
cursor: pointer;
&:hover {
background: t($modal-sidebarActive);
}
span {
font-size: 1rem;
}
svg {
font-size: 2.5em;
}
}
.auto {
svg {
font-size: 12px;
padding-right: 5px;
}
}
.options {
display: flex;
justify-content: space-between;
gap: 25px;
margin-top: 25px;
.lightTheme,
.darkTheme,
.legacyStyle,
.newStyle {
width: 50%;
padding: 50px;
span {
display: block;
}
}
}
}
}
.themesToggleArea .toggle {
margin-bottom: 10px;
}
.upload {
width: 100%;
height: 100%;
border-radius: 20px;
border: none;
outline: none;
padding: 50px;
display: flex;
flex-flow: column;
align-items: center;
transition: 0.3s;
@include themed {
background: t($modal-sidebar);
color: t($color);
cursor: pointer;
border: 3px solid t($modal-sidebarActive);
&:hover {
background: t($modal-sidebarActive);
}
}
svg {
font-size: 4em;
}
span {
font-size: 2em;
}
}
a.privacy {
text-decoration: none;
color: var(--modal-text);
font-size: 1rem;
&:hover {
color: #5352ed;
}
}
.examples {
display: flex;
flex-flow: column;
.shareYourMue {
width: -moz-fit-content;
width: fit-content;
}
img {
max-width: 60%;
border-radius: 10px 10px 10px 0;
}
}
.shareYourMue {
padding: 8px 20px;
border-radius: 0 0 10px 10px;
letter-spacing: 2px;
@include themed {
background-color: t($modal-sidebarActive);
}
}
.createButtons {
display: flex;
flex-flow: row;
justify-content: space-between;
margin-top: 15px;
button {
width: 150px;
height: 40px;
}
}
.showcaseimg {
width: 350px;
height: auto;
/* animation-name: float-in;
animation-duration: 1.2s;
animation-timing-function: ease-in; */
}
.welcomeContent {
.light {
.toggle.lightTheme {
background-color: rgb(219 219 219 / 72%);
}
}
.dark {
.toggle.darkTheme {
background-color: rgb(65 71 84 / 90%);
}
}
}
.welcomeNotice {
display: flex;
flex-flow: row;
gap: 25px;
padding: 25px;
align-items: center;
@include themed {
background-color: t($modal-sidebar);
border-radius: t($borderRadius);
}
.icon {
background: linear-gradient(238.7deg, #ff5c25 13.8%, #d21a11 49.49%, #ff456e 87.48%);
/* @include themed {
background-color: t($modal-sidebarActive);
} */
height: 50px !important;
width: 50px !important;
border-radius: 100%;
display: grid;
place-items: center;
text-align: center;
flex-shrink: 0;
color: #f18d91;
font-size: 24px;
}
.text {
display: flex;
flex-flow: column;
}
a {
text-decoration: none;
margin-left: auto;
padding: 0 20px;
@include modal-button(standard);
}
}
.toggle.active {
@include themed {
background-color: t($modal-sidebarActive) !important;
}
}
.welcomeButtons {
z-index: 999;
-webkit-backdrop-filter: blur(2px);
backdrop-filter: blur(2px);
position: sticky;
bottom: 0;
padding: 25px;
display: flex;
justify-content: flex-end;
gap: 20px;
button {
@include modal-button(standard);
}
}