mirror of
https://github.com/mue/mue.git
synced 2026-07-16 13:34:03 +02:00
refactor(features): Change organisation of welcome + Marketplace
This commit is contained in:
@@ -8,7 +8,7 @@ import Preview from '../../helpers/preview/Preview';
|
||||
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
import Welcome from './welcome/Welcome';
|
||||
import Welcome from 'features/welcome/Welcome';
|
||||
|
||||
export default class Modals extends PureComponent {
|
||||
constructor() {
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
import { MdOutlineArrowForward, MdOutlineOpenInNew } from 'react-icons/md';
|
||||
import { Button } from 'components/Elements';
|
||||
import variables from 'config/variables';
|
||||
|
||||
const Collection = ({ collection, toggle, collectionFunction }) => {
|
||||
const { news, background_colour, img, display_name, description, name } = collection;
|
||||
|
||||
const getStyle = () => {
|
||||
if (news) {
|
||||
return { backgroundColor: background_colour };
|
||||
}
|
||||
return {
|
||||
backgroundImage: `linear-gradient(to left, #000, transparent, #000), url('${variables.constants.DDG_IMAGE_PROXY + img}')`,
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="collection" style={getStyle()}>
|
||||
<div className="content">
|
||||
<span className="title">{display_name} using component</span>
|
||||
<span className="subtitle">{description}</span>
|
||||
</div>
|
||||
{collection.news === true ? (
|
||||
<a
|
||||
className="btn-collection"
|
||||
href={collection.news_link}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{variables.getMessage('modals.main.marketplace.learn_more')} <MdOutlineOpenInNew />
|
||||
</a>
|
||||
) : (
|
||||
<Button
|
||||
type="collection"
|
||||
onClick={() => collectionFunction(collection.name)}
|
||||
icon={<MdOutlineArrowForward />}
|
||||
label={variables.getMessage('modals.main.marketplace.explore_collection')}
|
||||
iconPlacement={'right'}
|
||||
/>
|
||||
)}
|
||||
{/*<Button
|
||||
type="collection"
|
||||
onClick={() => toggle('collection', name)}
|
||||
icon={<MdOutlineArrowForward />}
|
||||
label={variables.getMessage('modals.main.marketplace.explore_collection')}
|
||||
iconPlacement="right"
|
||||
/>*/}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Collection;
|
||||
export { Collection };
|
||||
@@ -1 +0,0 @@
|
||||
export * from './Collection';
|
||||
@@ -1,91 +0,0 @@
|
||||
import React, { useState, useEffect, useCallback, useRef, memo } from 'react';
|
||||
import { MdOutlineArrowForwardIos, MdOutlineArrowBackIos } from 'react-icons/md';
|
||||
import useEmblaCarousel from 'embla-carousel-react';
|
||||
import Autoplay from 'embla-carousel-autoplay';
|
||||
|
||||
import variables from 'config/variables';
|
||||
|
||||
import './carousel.scss';
|
||||
|
||||
function EmblaCarousel({ data }) {
|
||||
const autoplay = useRef(
|
||||
Autoplay({ delay: 3000, stopOnInteraction: false }, (emblaRoot) => emblaRoot.parentElement),
|
||||
);
|
||||
|
||||
const [emblaRef, emblaApi] = useEmblaCarousel({ loop: false }, [autoplay.current]);
|
||||
const [prevBtnEnabled, setPrevBtnEnabled] = useState(false);
|
||||
const [nextBtnEnabled, setNextBtnEnabled] = useState(false);
|
||||
|
||||
const scroll = useCallback(
|
||||
(direction) => {
|
||||
if (!emblaApi) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (direction === 'next') {
|
||||
emblaApi.scrollNext();
|
||||
} else {
|
||||
emblaApi.scrollPrev();
|
||||
}
|
||||
autoplay.current.reset();
|
||||
},
|
||||
[emblaApi],
|
||||
);
|
||||
|
||||
const onSelect = useCallback(() => {
|
||||
if (!emblaApi) {
|
||||
return;
|
||||
}
|
||||
setPrevBtnEnabled(emblaApi.canScrollPrev());
|
||||
setNextBtnEnabled(emblaApi.canScrollNext());
|
||||
}, [emblaApi]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!emblaApi) {
|
||||
return;
|
||||
}
|
||||
onSelect();
|
||||
emblaApi.on('select', onSelect);
|
||||
}, [emblaApi, onSelect]);
|
||||
|
||||
return (
|
||||
<div className="carousel">
|
||||
<div className="carousel_viewport" ref={emblaRef}>
|
||||
<div className="carousel_container">
|
||||
{data.map((photo, index) => (
|
||||
<div className="carousel_slide" key={index}>
|
||||
<div className="carousel_slide_inner">
|
||||
<img
|
||||
src={variables.constants.DDG_IMAGE_PROXY + photo.url.default}
|
||||
alt="Marketplace example screenshot"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
{data.length > 1 && (
|
||||
<>
|
||||
<button
|
||||
className="carousel_button prev btn-icon"
|
||||
onClick={() => scroll('prev')}
|
||||
disabled={!prevBtnEnabled}
|
||||
>
|
||||
<MdOutlineArrowBackIos />
|
||||
</button>
|
||||
<button
|
||||
className="carousel_button next btn-icon"
|
||||
onClick={() => scroll('next')}
|
||||
disabled={!nextBtnEnabled}
|
||||
>
|
||||
<MdOutlineArrowForwardIos />
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const Carousel = memo(EmblaCarousel);
|
||||
|
||||
export { Carousel as default, Carousel };
|
||||
@@ -1,84 +0,0 @@
|
||||
.carousel {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.carousel_viewport {
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
|
||||
&.is-draggable {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
&.is-dragging {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
border-radius: 15px !important;
|
||||
}
|
||||
|
||||
.carousel_container {
|
||||
display: flex;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
margin-left: -10px;
|
||||
}
|
||||
|
||||
.carousel_slide {
|
||||
position: relative;
|
||||
min-width: 100%;
|
||||
padding-left: 10px;
|
||||
|
||||
img {
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: auto;
|
||||
min-height: 100%;
|
||||
min-width: 100%;
|
||||
max-width: none;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
}
|
||||
|
||||
.carousel_slide_inner {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
height: 250px;
|
||||
}
|
||||
|
||||
.carousel_button {
|
||||
outline: 0;
|
||||
cursor: pointer;
|
||||
touch-action: manipulation;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
border: 0;
|
||||
width: 30px !important;
|
||||
height: 30px !important;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0;
|
||||
|
||||
&:disabled {
|
||||
cursor: default;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
&.prev {
|
||||
left: 27px;
|
||||
}
|
||||
|
||||
&.next {
|
||||
right: 27px;
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export * from './Carousel';
|
||||
@@ -1,19 +0,0 @@
|
||||
import { memo } from 'react';
|
||||
import variables from 'config/variables';
|
||||
|
||||
function Lightbox({ modalClose, img }) {
|
||||
variables.stats.postEvent('modal', 'Opened lightbox');
|
||||
|
||||
return (
|
||||
<>
|
||||
<span className="closeModal" onClick={modalClose}>
|
||||
×
|
||||
</span>
|
||||
<img src={img} className="lightboximg" draggable={false} alt="Item screenshot" />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const MemoizedLightbox = memo(Lightbox);
|
||||
export default MemoizedLightbox;
|
||||
export { MemoizedLightbox as Lightbox };
|
||||
@@ -1 +0,0 @@
|
||||
export * from './Lightbox';
|
||||
@@ -1,28 +0,0 @@
|
||||
import { memo } from 'react';
|
||||
import variables from 'config/variables';
|
||||
import { MdClose } from 'react-icons/md';
|
||||
import { Tooltip } from 'components/Elements';
|
||||
|
||||
function SideloadFailedModal({ modalClose, reason }) {
|
||||
return (
|
||||
<div className="smallModal">
|
||||
<div className="shareHeader">
|
||||
<span className="title">{variables.getMessage('modals.main.error_boundary.title')}</span>
|
||||
<Tooltip
|
||||
title={variables.getMessage('modals.main.settings.sections.advanced.reset_modal.cancel')}
|
||||
>
|
||||
<div className="close" onClick={modalClose}>
|
||||
<MdClose />
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<span>{variables.getMessage('modals.main.addons.sideload.failed')}</span>
|
||||
<span className="subtitle">{reason}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const MemoizedSideloadFailedModal = memo(SideloadFailedModal);
|
||||
|
||||
export default MemoizedSideloadFailedModal;
|
||||
export { MemoizedSideloadFailedModal as SideloadFailedModal };
|
||||
@@ -1 +0,0 @@
|
||||
export * from './SideloadFailedModal';
|
||||
@@ -1,3 +0,0 @@
|
||||
export * from './Carousel';
|
||||
export * from './SideloadFailedModal';
|
||||
export * from './Lightbox';
|
||||
@@ -1,307 +0,0 @@
|
||||
import variables from 'config/variables';
|
||||
import { PureComponent, Fragment } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import {
|
||||
MdIosShare,
|
||||
MdFlag,
|
||||
MdAccountCircle,
|
||||
MdBugReport,
|
||||
MdFormatQuote,
|
||||
MdImage,
|
||||
MdTranslate,
|
||||
MdExpandMore,
|
||||
MdExpandLess,
|
||||
MdStyle,
|
||||
} from 'react-icons/md';
|
||||
import Modal from 'react-modal';
|
||||
|
||||
import { Header } from 'components/Layout/Settings';
|
||||
import { Button } from 'components/Elements';
|
||||
|
||||
import { install, uninstall } from 'utils/marketplace';
|
||||
import { Carousel } from '../Elements/Carousel';
|
||||
import { ShareModal } from 'components/Elements';
|
||||
|
||||
class Item extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
showLightbox: false,
|
||||
showUpdateButton:
|
||||
this.props.addonInstalled === true &&
|
||||
this.props.addonInstalledVersion !== this.props.data.version,
|
||||
shareModal: false,
|
||||
count: 5,
|
||||
};
|
||||
}
|
||||
|
||||
updateAddon() {
|
||||
uninstall(this.props.data.type, this.props.data.display_name);
|
||||
install(this.props.data.type, this.props.data);
|
||||
toast(variables.getMessage('toasts.updated'));
|
||||
this.setState({
|
||||
showUpdateButton: false,
|
||||
});
|
||||
}
|
||||
|
||||
incrementCount(type) {
|
||||
const newCount =
|
||||
this.state.count !== this.props.data.data[type].length
|
||||
? this.props.data.data[type].length
|
||||
: 5;
|
||||
|
||||
this.setState({ count: newCount });
|
||||
}
|
||||
|
||||
getName(name) {
|
||||
const nameMappings = {
|
||||
photos: 'photo_packs',
|
||||
quotes: 'quote_packs',
|
||||
settings: 'preset_settings',
|
||||
};
|
||||
return nameMappings[name] || name;
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.props.data.display_name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// prevent console error
|
||||
let iconsrc = variables.constants.DDG_IMAGE_PROXY + this.props.data.icon;
|
||||
if (!this.props.data.icon) {
|
||||
iconsrc = null;
|
||||
}
|
||||
|
||||
let updateButton;
|
||||
if (this.state.showUpdateButton) {
|
||||
updateButton = (
|
||||
<Fragment key="update">
|
||||
<button className="removeFromMue" onClick={() => this.updateAddon()}>
|
||||
{variables.getMessage('modals.main.addons.product.buttons.update_addon')}
|
||||
</button>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
const moreInfoItem = (icon, header, text) => (
|
||||
<div className="infoItem">
|
||||
{icon}
|
||||
<div className="text">
|
||||
<span className="header">{header}</span>
|
||||
<span>{text}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div id="item">
|
||||
<Modal
|
||||
closeTimeoutMS={300}
|
||||
isOpen={this.state.shareModal}
|
||||
className="Modal mainModal"
|
||||
overlayClassName="Overlay"
|
||||
ariaHideApp={false}
|
||||
onRequestClose={() => this.setState({ shareModal: false })}
|
||||
>
|
||||
<ShareModal
|
||||
data={
|
||||
variables.constants.API_URL + '/marketplace/share/' + btoa(this.props.data.api_name)
|
||||
}
|
||||
modalClose={() => this.setState({ shareModal: false })}
|
||||
/>
|
||||
</Modal>
|
||||
<Header
|
||||
title={variables.getMessage('modals.main.navbar.marketplace')}
|
||||
secondaryTitle={this.props.data.data.display_name}
|
||||
report={false}
|
||||
goBack={this.props.toggleFunction}
|
||||
/>
|
||||
<div className="itemPage">
|
||||
<div className="itemShowcase">
|
||||
{this.props.data.data.photos && (
|
||||
<div className="carousel">
|
||||
<div className="carousel_container">
|
||||
<Carousel data={this.props.data.data.photos} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{this.props.data.data.settings && (
|
||||
<img
|
||||
alt="product"
|
||||
draggable={false}
|
||||
src={iconsrc}
|
||||
onClick={() => this.setState({ showLightbox: true })}
|
||||
/>
|
||||
)}
|
||||
{this.props.data.data.quotes && (
|
||||
<>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>{variables.getMessage('modals.main.settings.sections.quote.title')}</th>
|
||||
<th>{variables.getMessage('modals.main.settings.sections.quote.author')}</th>
|
||||
</tr>
|
||||
{this.props.data.data.quotes.slice(0, this.state.count).map((quote, index) => (
|
||||
<tr key={index}>
|
||||
<td>{quote.quote}</td>
|
||||
<td>{quote.author}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<div className="showMoreItems">
|
||||
<span className="link" onClick={() => this.incrementCount('quotes')}>
|
||||
{this.state.count !== this.props.data.data.quotes.length ? (
|
||||
<>
|
||||
<MdExpandMore />{' '}
|
||||
{variables.getMessage('modals.main.marketplace.product.show_all')}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<MdExpandLess />{' '}
|
||||
{variables.getMessage('modals.main.marketplace.product.show_less')}
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{this.props.data.data.settings && (
|
||||
<>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>{variables.getMessage('modals.main.marketplace.product.setting')}</th>
|
||||
<th>{variables.getMessage('modals.main.marketplace.product.value')}</th>
|
||||
</tr>
|
||||
{Object.entries(this.props.data.data.settings)
|
||||
.slice(0, this.state.count)
|
||||
.map(([key, value]) => (
|
||||
<tr key={key}>
|
||||
<td>{key}</td>
|
||||
<td>{value}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<div className="showMoreItems">
|
||||
<span className="link" onClick={() => this.incrementCount('settings')}>
|
||||
{this.state.count !== this.props.data.data.settings.length ? (
|
||||
<>
|
||||
<MdExpandMore />{' '}
|
||||
{variables.getMessage('modals.main.marketplace.product.show_all')}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<MdExpandLess />{' '}
|
||||
{variables.getMessage('modals.main.marketplace.product.show_less')}
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div>
|
||||
<p className="title">
|
||||
{variables.getMessage('modals.main.marketplace.product.description')}
|
||||
</p>
|
||||
<p dangerouslySetInnerHTML={{ __html: this.props.data.description }} />
|
||||
</div>
|
||||
<div className="moreInfo">
|
||||
{moreInfoItem(
|
||||
<MdBugReport />,
|
||||
variables.getMessage('modals.main.marketplace.product.version'),
|
||||
updateButton ? (
|
||||
<span>
|
||||
{this.props.data.version} (Installed: {this.props.data.addonInstalledVersion})
|
||||
</span>
|
||||
) : (
|
||||
<span>{this.props.data.version}</span>
|
||||
),
|
||||
)}
|
||||
{moreInfoItem(
|
||||
<MdAccountCircle />,
|
||||
variables.getMessage('modals.main.marketplace.product.author'),
|
||||
this.props.data.author,
|
||||
)}
|
||||
{this.props.data.data.quotes &&
|
||||
moreInfoItem(
|
||||
<MdFormatQuote />,
|
||||
variables.getMessage('modals.main.marketplace.product.no_quotes'),
|
||||
this.props.data.data.quotes.length,
|
||||
)}
|
||||
{this.props.data.data.photos &&
|
||||
moreInfoItem(
|
||||
<MdImage />,
|
||||
variables.getMessage('modals.main.marketplace.product.no_images'),
|
||||
this.props.data.data.photos.length,
|
||||
)}
|
||||
{this.props.data.data.quotes && this.props.data.data.language !== ''
|
||||
? moreInfoItem(
|
||||
<MdTranslate />,
|
||||
variables.getMessage('modals.main.settings.sections.language.title'),
|
||||
this.props.data.data.language,
|
||||
)
|
||||
: null}
|
||||
{moreInfoItem(
|
||||
<MdStyle />,
|
||||
variables.getMessage('modals.main.settings.sections.background.type.title'),
|
||||
variables.getMessage(
|
||||
'modals.main.marketplace.' + this.getName(this.props.data.data.type),
|
||||
) || 'marketplace',
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="itemInfo"
|
||||
style={{
|
||||
backgroundImage: `url("${
|
||||
variables.constants.DDG_IMAGE_PROXY + this.props.data.data.icon_url
|
||||
}")`,
|
||||
}}
|
||||
>
|
||||
<div className="front">
|
||||
<img
|
||||
className="icon"
|
||||
alt="icon"
|
||||
draggable={false}
|
||||
src={variables.constants.DDG_IMAGE_PROXY + this.props.data.data.icon_url}
|
||||
/>
|
||||
{this.props.button}
|
||||
<div className="iconButtons">
|
||||
<Button
|
||||
type="icon"
|
||||
onClick={() => this.setState({ shareModal: true })}
|
||||
icon={<MdIosShare />}
|
||||
tooltipTitle={variables.getMessage('widgets.quote.share')}
|
||||
tooltipKey="share"
|
||||
/>
|
||||
<Button
|
||||
type="icon"
|
||||
onClick={() => this.setState({ shareModal: true })}
|
||||
icon={<MdFlag />}
|
||||
tooltipTitle={variables.getMessage(
|
||||
'modals.main.marketplace.product.buttons.report',
|
||||
)}
|
||||
tooltipKey="report"
|
||||
/>
|
||||
</div>
|
||||
{this.props.data.data.collection && (
|
||||
<div className="inCollection">
|
||||
<span className="subtitle">
|
||||
{variables.getMessage('modals.main.marketplace.product.part_of')}
|
||||
</span>
|
||||
<span className="title">{this.props.data.data.collection}</span>
|
||||
<button>{variables.getMessage('modals.main.marketplace.product.explore')}</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export { Item as default, Item };
|
||||
@@ -1,119 +0,0 @@
|
||||
import variables from 'config/variables';
|
||||
import React, { memo } from 'react';
|
||||
import { MdAutoFixHigh, MdOutlineArrowForward, MdOutlineOpenInNew } from 'react-icons/md';
|
||||
|
||||
import { Button } from 'components/Elements';
|
||||
import MemoizedLightbox from '../Elements/Lightbox/Lightbox';
|
||||
|
||||
function Items({
|
||||
type,
|
||||
items,
|
||||
collection,
|
||||
toggleFunction,
|
||||
collectionFunction,
|
||||
onCollection,
|
||||
filter,
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
{(type === 'all' && !onCollection && (filter === null || filter === '')) ||
|
||||
(type === 'collections' && !onCollection && (filter === null || filter === '')) ? (
|
||||
<>
|
||||
<div
|
||||
className="collection"
|
||||
style={
|
||||
collection.news
|
||||
? { backgroundColor: collection.background_colour }
|
||||
: {
|
||||
backgroundImage: `linear-gradient(to right, rgba(0, 0, 0, 0.9), rgba(0, 0, 0, 0.7), transparent, rgba(0, 0, 0, 0.7), rgba(0 ,0, 0, 0.9)), url('${collection.img}')`,
|
||||
}
|
||||
}
|
||||
>
|
||||
<div className="content">
|
||||
<span className="title">{collection.display_name}</span>
|
||||
<span className="subtitle">{collection.description}</span>
|
||||
</div>
|
||||
{collection.news === true ? (
|
||||
<a
|
||||
className="btn-collection"
|
||||
href={collection.news_link}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{variables.getMessage('modals.main.marketplace.learn_more')} <MdOutlineOpenInNew />
|
||||
</a>
|
||||
) : (
|
||||
<Button
|
||||
type="collection"
|
||||
onClick={() => collectionFunction(collection.name)}
|
||||
icon={<MdOutlineArrowForward />}
|
||||
label={variables.getMessage('modals.main.marketplace.explore_collection')}
|
||||
iconPlacement={'right'}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
<div className="items">
|
||||
{items
|
||||
?.filter(
|
||||
(item) =>
|
||||
item.name.toLowerCase().includes(filter.toLowerCase()) ||
|
||||
filter === '' ||
|
||||
item.author.toLowerCase().includes(filter.toLowerCase()) ||
|
||||
item.type.toLowerCase().includes(filter.toLowerCase()),
|
||||
)
|
||||
.map((item) => (
|
||||
<div className="item" onClick={() => toggleFunction(item)} key={item.name}>
|
||||
<img
|
||||
className="item-back"
|
||||
alt=""
|
||||
draggable={false}
|
||||
src={variables.constants.DDG_IMAGE_PROXY + item.icon_url}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<img
|
||||
className="item-icon"
|
||||
alt="icon"
|
||||
draggable={false}
|
||||
src={variables.constants.DDG_IMAGE_PROXY + item.icon_url}
|
||||
/>
|
||||
<div className="card-details">
|
||||
<span className="card-title">{item.display_name || item.name}</span>
|
||||
<span className="card-subtitle">
|
||||
{variables.getMessage('modals.main.marketplace.by', { author: item.author })}
|
||||
</span>
|
||||
{type === 'all' && !onCollection ? (
|
||||
<span className="card-type">
|
||||
{variables.getMessage('modals.main.marketplace.' + item.type)}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="loader"></div>
|
||||
{type === 'all' && !onCollection ? (
|
||||
<div className="createYourOwn">
|
||||
<MdAutoFixHigh />
|
||||
<span className="title">{variables.getMessage('modals.main.marketplace.cant_find')}</span>
|
||||
<span className="subtitle">
|
||||
{variables.getMessage('modals.main.marketplace.knowledgebase_one') + ' '}
|
||||
<a
|
||||
className="link"
|
||||
target="_blank"
|
||||
href={variables.constants.KNOWLEDGEBASE}
|
||||
rel="noreferrer"
|
||||
>
|
||||
{variables.getMessage('modals.main.marketplace.knowledgebase_two')}
|
||||
</a>
|
||||
{' ' + variables.getMessage('modals.main.marketplace.knowledgebase_three')}
|
||||
</span>
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const MemoizedItems = memo(Items);
|
||||
export { MemoizedItems as default, MemoizedItems as Items };
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './Elements';
|
||||
export * from './Items';
|
||||
@@ -1,290 +0,0 @@
|
||||
import variables from 'config/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { MdUpdate, MdOutlineExtensionOff, MdSendTimeExtension } from 'react-icons/md';
|
||||
import { toast } from 'react-toastify';
|
||||
import Modal from 'react-modal';
|
||||
|
||||
import { SideloadFailedModal } from '../components/Elements/SideloadFailedModal/SideloadFailedModal';
|
||||
import Item from '../components/Items/Item';
|
||||
import Items from '../components/Items/Items';
|
||||
import { Dropdown, FileUpload } from 'components/Form/Settings';
|
||||
import { Header, CustomActions } from 'components/Layout/Settings';
|
||||
import { Button } from 'components/Elements';
|
||||
|
||||
import { install, uninstall, urlParser } from 'utils/marketplace';
|
||||
|
||||
export default class Added extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
installed: JSON.parse(localStorage.getItem('installed')),
|
||||
item: {},
|
||||
button: '',
|
||||
showFailed: false,
|
||||
failedReason: '',
|
||||
};
|
||||
this.buttons = {
|
||||
uninstall: (
|
||||
<button className="removeFromMue" onClick={() => this.uninstall()}>
|
||||
{variables.getMessage('modals.main.marketplace.product.buttons.remove')}
|
||||
</button>
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
installAddon(input) {
|
||||
let failedReason = '';
|
||||
if (!input.name) {
|
||||
failedReason = variables.getMessage('modals.main.addons.sideload.errors.no_name');
|
||||
} else if (!input.author) {
|
||||
failedReason = variables.getMessage('modals.main.addons.sideload.errors.no_author');
|
||||
} else if (!input.type) {
|
||||
failedReason = variables.getMessage('modals.main.addons.sideload.errors.no_type');
|
||||
} else if (!input.version) {
|
||||
failedReason = variables.getMessage('modals.main.addons.sideload.errors.no_version');
|
||||
} else if (
|
||||
input.type === 'photos' &&
|
||||
(!input.photos ||
|
||||
!input.photos.length ||
|
||||
!input.photos[0].url ||
|
||||
!input.photos[0].url.default ||
|
||||
!input.photos[0].photographer ||
|
||||
!input.photos[0].location)
|
||||
) {
|
||||
failedReason = variables.getMessage('modals.main.addons.sideload.errors.invalid_photos');
|
||||
} else if (
|
||||
input.type === 'quotes' &&
|
||||
(!input.quotes || !input.quotes.length || !input.quotes[0].quote || !input.quotes[0].author)
|
||||
) {
|
||||
failedReason = variables.getMessage('modals.main.addons.sideload.errors.invalid_quotes');
|
||||
}
|
||||
|
||||
if (failedReason !== '' && this.state.showFailed === false) {
|
||||
return this.setState({
|
||||
failedReason,
|
||||
showFailed: true,
|
||||
});
|
||||
}
|
||||
|
||||
install(input.type, input);
|
||||
toast(variables.getMessage('toasts.installed'));
|
||||
variables.stats.postEvent('marketplace', 'Sideload');
|
||||
}
|
||||
|
||||
getSideloadButton() {
|
||||
return (
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => document.getElementById('file-input').click()}
|
||||
ref={this.customDnd}
|
||||
icon={<MdSendTimeExtension />}
|
||||
label={variables.getMessage('modals.main.addons.sideload.title')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
toggle(type, data) {
|
||||
if (type === 'item') {
|
||||
const installed = JSON.parse(localStorage.getItem('installed'));
|
||||
const info = {
|
||||
data: installed.find((i) => i.name === data.name),
|
||||
};
|
||||
|
||||
this.setState({
|
||||
item: {
|
||||
type: info.data.type,
|
||||
display_name: info.data.name,
|
||||
author: info.data.author,
|
||||
description: urlParser(info.data.description.replace(/\n/g, '<br>')),
|
||||
//updated: info.updated,
|
||||
version: info.data.version,
|
||||
icon: info.data.screenshot_url,
|
||||
data: info.data,
|
||||
},
|
||||
button: this.buttons.uninstall,
|
||||
});
|
||||
variables.stats.postEvent('marketplace', 'Item viewed');
|
||||
} else {
|
||||
this.setState({
|
||||
item: {},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
uninstall() {
|
||||
uninstall(this.state.item.type, this.state.item.display_name);
|
||||
|
||||
toast(variables.getMessage('toasts.uninstalled'));
|
||||
|
||||
this.setState({
|
||||
button: '',
|
||||
installed: JSON.parse(localStorage.getItem('installed')),
|
||||
});
|
||||
|
||||
variables.stats.postEvent('marketplace', 'Uninstall');
|
||||
}
|
||||
|
||||
sortAddons(value, sendEvent) {
|
||||
let installed = JSON.parse(localStorage.getItem('installed'));
|
||||
switch (value) {
|
||||
case 'newest':
|
||||
installed.reverse();
|
||||
break;
|
||||
case 'oldest':
|
||||
break;
|
||||
case 'a-z':
|
||||
installed.sort();
|
||||
break;
|
||||
case 'z-a':
|
||||
installed.sort();
|
||||
installed.reverse();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
installed,
|
||||
});
|
||||
|
||||
if (sendEvent) {
|
||||
variables.stats.postEvent('marketplace', 'Sort');
|
||||
}
|
||||
}
|
||||
|
||||
updateCheck() {
|
||||
let updates = 0;
|
||||
this.state.installed.forEach(async (item) => {
|
||||
const data = await (
|
||||
await fetch(variables.constants.API_URL + 'marketplace//item/' + item.name)
|
||||
).json();
|
||||
if (data.version !== item.version) {
|
||||
updates++;
|
||||
}
|
||||
});
|
||||
|
||||
if (updates > 0) {
|
||||
toast(
|
||||
variables.getMessage('modals.main.addons.updates_available', {
|
||||
amount: updates,
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
toast(variables.getMessage('modals.main.addons.no_updates'));
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.sortAddons(localStorage.getItem('sortAddons'), false);
|
||||
}
|
||||
|
||||
render() {
|
||||
const sideLoadBackendElements = () => (
|
||||
<>
|
||||
<Modal
|
||||
closeTimeoutMS={100}
|
||||
onRequestClose={() => this.setState({ showFailed: false })}
|
||||
isOpen={this.state.showFailed}
|
||||
className="Modal resetmodal mainModal resetmodal"
|
||||
overlayClassName="Overlay resetoverlay"
|
||||
ariaHideApp={false}
|
||||
>
|
||||
<SideloadFailedModal
|
||||
modalClose={() => this.setState({ showFailed: false })}
|
||||
reason={this.state.failedReason}
|
||||
/>
|
||||
</Modal>
|
||||
<FileUpload
|
||||
id="file-input"
|
||||
type="settings"
|
||||
accept="application/json"
|
||||
loadFunction={(e) => this.installAddon(JSON.parse(e))}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
if (this.state.installed.length === 0) {
|
||||
return (
|
||||
<>
|
||||
<Header title={variables.getMessage('modals.main.navbar.addons')} report={false}>
|
||||
<CustomActions>{this.getSideloadButton()}</CustomActions>
|
||||
</Header>
|
||||
<div className="emptyItems">
|
||||
<div className="emptyNewMessage">
|
||||
<MdOutlineExtensionOff />
|
||||
<span className="title">
|
||||
{variables.getMessage('modals.main.addons.empty.title')}
|
||||
</span>
|
||||
<span className="subtitle">
|
||||
{variables.getMessage('modals.main.addons.empty.description')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.state.item.display_name) {
|
||||
return (
|
||||
<Item
|
||||
data={this.state.item}
|
||||
button={this.state.button}
|
||||
toggleFunction={() => this.toggle()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="modalHeader">
|
||||
<span className="mainTitle">{variables.getMessage('modals.main.addons.added')}</span>
|
||||
<div className="filter">
|
||||
{sideLoadBackendElements()}
|
||||
<div className="buttonSection">
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => this.updateCheck()}
|
||||
icon={<MdUpdate />}
|
||||
label={variables.getMessage('modals.main.addons.check_updates')}
|
||||
/>
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => document.getElementById('file-input').click()}
|
||||
icon={<MdSendTimeExtension />}
|
||||
label={variables.getMessage('modals.main.addons.sideload.title')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Dropdown
|
||||
label={variables.getMessage('modals.main.addons.sort.title')}
|
||||
name="sortAddons"
|
||||
onChange={(value) => this.sortAddons(value)}
|
||||
items={[
|
||||
{
|
||||
value: 'newest',
|
||||
text: variables.getMessage('modals.main.addons.sort.newest'),
|
||||
},
|
||||
{
|
||||
value: 'oldest',
|
||||
text: variables.getMessage('modals.main.addons.sort.oldest'),
|
||||
},
|
||||
{
|
||||
value: 'a-z',
|
||||
text: variables.getMessage('modals.main.addons.sort.a_z'),
|
||||
},
|
||||
{
|
||||
value: 'z-a',
|
||||
text: variables.getMessage('modals.main.addons.sort.z_a'),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<Items
|
||||
items={this.state.installed}
|
||||
filter=""
|
||||
toggleFunction={(input) => this.toggle('item', input)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,453 +0,0 @@
|
||||
import variables from 'config/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import {
|
||||
MdWifiOff,
|
||||
MdLocalMall,
|
||||
MdClose,
|
||||
MdSearch,
|
||||
MdOutlineArrowForward,
|
||||
MdLibraryAdd,
|
||||
} from 'react-icons/md';
|
||||
|
||||
import Item from '../components/Items/Item';
|
||||
import Items from '../components/Items/Items';
|
||||
import Dropdown from '../../../../../../components/Form/Settings/Dropdown/Dropdown';
|
||||
import { Header } from 'components/Layout/Settings';
|
||||
import { Button } from 'components/Elements';
|
||||
|
||||
import { install, urlParser, uninstall } from 'utils/marketplace';
|
||||
|
||||
class Marketplace extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
items: [],
|
||||
button: '',
|
||||
done: false,
|
||||
item: {},
|
||||
collection: false,
|
||||
filter: '',
|
||||
};
|
||||
this.buttons = {
|
||||
uninstall: (
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => this.manage('uninstall')}
|
||||
icon={<MdClose />}
|
||||
label={variables.getMessage('modals.main.marketplace.product.buttons.remove')}
|
||||
/>
|
||||
),
|
||||
install: (
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => this.manage('install')}
|
||||
icon={<MdLibraryAdd />}
|
||||
label={variables.getMessage('modals.main.marketplace.product.buttons.addtomue')}
|
||||
/>
|
||||
),
|
||||
};
|
||||
this.controller = new AbortController();
|
||||
}
|
||||
|
||||
async toggle(type, data) {
|
||||
if (type === 'item') {
|
||||
let info;
|
||||
// get item info
|
||||
try {
|
||||
let type = this.props.type;
|
||||
if (type === 'all' || type === 'collections') {
|
||||
type = data.type;
|
||||
}
|
||||
info = await (
|
||||
await fetch(`${variables.constants.API_URL}/marketplace/item/${type}/${data.name}`, {
|
||||
signal: this.controller.signal,
|
||||
})
|
||||
).json();
|
||||
} catch (e) {
|
||||
if (this.controller.signal.aborted === false) {
|
||||
return toast(variables.getMessage('toasts.error'));
|
||||
}
|
||||
}
|
||||
|
||||
if (this.controller.signal.aborted === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check if already installed
|
||||
let button = this.buttons.install;
|
||||
let addonInstalled = false;
|
||||
let addonInstalledVersion;
|
||||
|
||||
const installed = JSON.parse(localStorage.getItem('installed'));
|
||||
|
||||
if (installed.some((item) => item.name === info.data.name)) {
|
||||
button = this.buttons.uninstall;
|
||||
addonInstalled = true;
|
||||
for (let i = 0; i < installed.length; i++) {
|
||||
if (installed[i].name === info.data.name) {
|
||||
addonInstalledVersion = installed[i].version;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
item: {
|
||||
type: info.data.type,
|
||||
display_name: info.data.name,
|
||||
author: info.data.author,
|
||||
description: urlParser(info.data.description.replace(/\n/g, '<br>')),
|
||||
//updated: info.updated,
|
||||
version: info.data.version,
|
||||
icon: info.data.screenshot_url,
|
||||
data: info.data,
|
||||
addonInstalled,
|
||||
addonInstalledVersion,
|
||||
api_name: data.name,
|
||||
},
|
||||
button: button,
|
||||
});
|
||||
|
||||
variables.stats.postEvent('marketplace-item', `${this.state.item.display_name} viewed`);
|
||||
} else if (type === 'collection') {
|
||||
this.setState({
|
||||
done: false,
|
||||
});
|
||||
const collection = await (
|
||||
await fetch(`${variables.constants.API_URL}/marketplace/collection/${data}`, {
|
||||
signal: this.controller.signal,
|
||||
})
|
||||
).json();
|
||||
this.setState({
|
||||
items: collection.data.items,
|
||||
collectionTitle: collection.data.display_name,
|
||||
collectionDescription: collection.data.description,
|
||||
collectionImg: collection.data.img,
|
||||
collection: true,
|
||||
done: true,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
item: {},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async getItems() {
|
||||
const dataURL =
|
||||
this.props.type === 'collections'
|
||||
? variables.constants.API_URL + '/marketplace/collections'
|
||||
: variables.constants.API_URL + '/marketplace/items/' + this.props.type;
|
||||
const { data } = await (
|
||||
await fetch(dataURL, {
|
||||
signal: this.controller.signal,
|
||||
})
|
||||
).json();
|
||||
const collections = await (
|
||||
await fetch(variables.constants.API_URL + '/marketplace/collections', {
|
||||
signal: this.controller.signal,
|
||||
})
|
||||
).json();
|
||||
|
||||
if (this.controller.signal.aborted === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
items: data,
|
||||
oldItems: data,
|
||||
collections: collections.data,
|
||||
done: true,
|
||||
});
|
||||
|
||||
this.sortMarketplace(localStorage.getItem('sortMarketplace'), false);
|
||||
}
|
||||
|
||||
manage(type) {
|
||||
if (type === 'install') {
|
||||
install(this.state.item.type, this.state.item.data);
|
||||
} else {
|
||||
uninstall(this.state.item.type, this.state.item.display_name);
|
||||
}
|
||||
|
||||
toast(variables.getMessage('toasts.' + type + 'ed'));
|
||||
this.setState({
|
||||
button: type === 'install' ? this.buttons.uninstall : this.buttons.install,
|
||||
});
|
||||
|
||||
variables.stats.postEvent(
|
||||
'marketplace-item',
|
||||
`${this.state.item.display_name} ${type === 'install' ? 'installed' : 'uninstalled'}`,
|
||||
);
|
||||
variables.stats.postEvent('marketplace', type === 'install' ? 'Install' : 'Uninstall');
|
||||
}
|
||||
|
||||
async installCollection() {
|
||||
this.setState({ busy: true });
|
||||
try {
|
||||
const installed = JSON.parse(localStorage.getItem('installed'));
|
||||
for (const item of this.state.items) {
|
||||
if (installed.some((i) => i.name === item.display_name)) continue; // don't install if already installed
|
||||
let { data } = await (
|
||||
await fetch(`${variables.constants.API_URL}/marketplace/item/${item.type}/${item.name}`, {
|
||||
signal: this.controller.signal,
|
||||
})
|
||||
).json();
|
||||
install(data.type, data);
|
||||
variables.stats.postEvent('marketplace-item', `${item.display_name} installed}`);
|
||||
variables.stats.postEvent('marketplace', 'Install');
|
||||
}
|
||||
toast(variables.getMessage('toasts.installed'));
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
toast(variables.getMessage('toasts.error'));
|
||||
} finally {
|
||||
this.setState({ busy: false });
|
||||
}
|
||||
}
|
||||
|
||||
sortMarketplace(value, sendEvent) {
|
||||
let items = this.state.oldItems;
|
||||
switch (value) {
|
||||
case 'a-z':
|
||||
items.sort();
|
||||
// fix sort not working sometimes
|
||||
if (this.state.sortType === 'z-a') {
|
||||
items.reverse();
|
||||
}
|
||||
break;
|
||||
case 'z-a':
|
||||
items.sort();
|
||||
items.reverse();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
items: items,
|
||||
sortType: value,
|
||||
});
|
||||
|
||||
if (sendEvent) {
|
||||
variables.stats.postEvent('marketplace', 'Sort');
|
||||
}
|
||||
}
|
||||
|
||||
returnToMain() {
|
||||
this.setState({
|
||||
items: this.state.oldItems,
|
||||
collection: false,
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (navigator.onLine === false || localStorage.getItem('offlineMode') === 'true') {
|
||||
return;
|
||||
}
|
||||
|
||||
this.getItems();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
// stop making requests
|
||||
this.controller.abort();
|
||||
}
|
||||
|
||||
render() {
|
||||
const errorMessage = (msg) => {
|
||||
return (
|
||||
<>
|
||||
<div className="flexTopMarketplace">
|
||||
<span className="mainTitle">
|
||||
{variables.getMessage('modals.main.navbar.marketplace')}
|
||||
</span>
|
||||
</div>
|
||||
<div className="emptyItems">
|
||||
<div className="emptyMessage">{msg}</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
if (navigator.onLine === false || localStorage.getItem('offlineMode') === 'true') {
|
||||
return errorMessage(
|
||||
<>
|
||||
<MdWifiOff />
|
||||
<span className="title">
|
||||
{variables.getMessage('modals.main.marketplace.offline.title')}
|
||||
</span>
|
||||
<span className="subtitle">
|
||||
{variables.getMessage('modals.main.marketplace.offline.description')}
|
||||
</span>
|
||||
</>,
|
||||
);
|
||||
}
|
||||
|
||||
if (this.state.done === false) {
|
||||
return errorMessage(
|
||||
<>
|
||||
<div className="loaderHolder">
|
||||
<div id="loader"></div>
|
||||
<span className="subtitle">{variables.getMessage('modals.main.loading')}</span>
|
||||
</div>
|
||||
</>,
|
||||
);
|
||||
}
|
||||
|
||||
if (!this.state.items || this.state.items?.length === 0) {
|
||||
this.getItems();
|
||||
return (
|
||||
<>
|
||||
{errorMessage(
|
||||
<>
|
||||
<MdLocalMall />
|
||||
<span className="title">
|
||||
{variables.getMessage('modals.main.addons.empty.title')}
|
||||
</span>
|
||||
<span className="subtitle">
|
||||
{variables.getMessage('modals.main.marketplace.no_items')}
|
||||
</span>
|
||||
</>,
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.state.item.display_name) {
|
||||
return (
|
||||
<Item
|
||||
data={this.state.item}
|
||||
button={this.state.button}
|
||||
toggleFunction={() => this.toggle()}
|
||||
addonInstalled={this.state.item.addonInstalled}
|
||||
addonInstalledVersion={this.state.item.addonInstalledVersion}
|
||||
icon={this.state.item.screenshot_url}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{this.state.collection === true ? (
|
||||
<>
|
||||
<Header
|
||||
title={variables.getMessage('modals.main.navbar.marketplace')}
|
||||
secondaryTitle={variables.getMessage('modals.main.marketplace.collection')}
|
||||
report={false}
|
||||
goBack={() => this.returnToMain()}
|
||||
/>
|
||||
<div
|
||||
className="collectionPage"
|
||||
style={{
|
||||
backgroundImage: `linear-gradient(to bottom, transparent, black), url('${variables.constants.DDG_IMAGE_PROXY + this.state.collectionImg}')`,
|
||||
}}
|
||||
>
|
||||
<div className="nice-tag">
|
||||
{variables.getMessage('modals.main.marketplace.collection')}
|
||||
</div>
|
||||
<div className="content">
|
||||
<span className="mainTitle">{this.state.collectionTitle}</span>
|
||||
<span className="subtitle">{this.state.collectionDescription}</span>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
type="collection"
|
||||
onClick={() => this.installCollection()}
|
||||
disabled={this.state.busy}
|
||||
icon={<MdLibraryAdd />}
|
||||
label={variables.getMessage('modals.main.marketplace.add_all')}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="flexTopMarketplace">
|
||||
<span className="mainTitle">
|
||||
{variables.getMessage('modals.main.navbar.marketplace')}
|
||||
</span>
|
||||
</div>
|
||||
<div className="headerExtras marketplaceCondition">
|
||||
{this.props.type !== 'collections' && (
|
||||
<div>
|
||||
<form className="marketplaceSearch">
|
||||
<input
|
||||
label={variables.getMessage('widgets.search')}
|
||||
placeholder={variables.getMessage('widgets.search')}
|
||||
name="filter"
|
||||
id="filter"
|
||||
value={this.state.filter}
|
||||
onChange={(event) => this.setState({ filter: event.target.value })}
|
||||
/>
|
||||
<MdSearch />
|
||||
</form>
|
||||
</div>
|
||||
)}
|
||||
<Dropdown
|
||||
label={variables.getMessage('modals.main.addons.sort.title')}
|
||||
name="sortMarketplace"
|
||||
onChange={(value) => this.sortMarketplace(value)}
|
||||
items={[
|
||||
{
|
||||
value: 'a-z',
|
||||
text: variables.getMessage('modals.main.addons.sort.a_z'),
|
||||
},
|
||||
{
|
||||
value: 'z-a',
|
||||
text: variables.getMessage('modals.main.addons.sort.z_a'),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{this.props.type === 'collections' && !this.state.collection ? (
|
||||
this.state.items.map((item) => (
|
||||
<>
|
||||
{!item.news ? (
|
||||
<div
|
||||
className="collection"
|
||||
style={
|
||||
item.news
|
||||
? { backgroundColor: item.background_colour }
|
||||
: {
|
||||
backgroundImage: `linear-gradient(to left, #000, transparent, #000), url('${variables.constants.DDG_IMAGE_PROXY + item.img}')`,
|
||||
}
|
||||
}
|
||||
>
|
||||
<div className="content">
|
||||
<span className="title">{item.display_name}</span>
|
||||
<span className="subtitle">{item.description}</span>
|
||||
</div>
|
||||
<Button
|
||||
type="collection"
|
||||
onClick={() => this.toggle('collection', item.name)}
|
||||
icon={<MdOutlineArrowForward />}
|
||||
label={variables.getMessage('modals.main.marketplace.explore_collection')}
|
||||
iconPlacement="right"
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
))
|
||||
) : (
|
||||
<Items
|
||||
type={this.props.type}
|
||||
items={this.state.items}
|
||||
collection={
|
||||
this.state.collections[Math.floor(Math.random() * this.state.collections.length)] ||
|
||||
[]
|
||||
}
|
||||
onCollection={this.state.collection}
|
||||
toggleFunction={(input) => this.toggle('item', input)}
|
||||
collectionFunction={(input) => this.toggle('collection', input)}
|
||||
filter={this.state.filter}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Marketplace;
|
||||
@@ -1,37 +0,0 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
import variables from 'config/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { MdOutlineExtensionOff } from 'react-icons/md';
|
||||
|
||||
export default class Create extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<div className="flexTopMarketplace">
|
||||
<span className="mainTitle">
|
||||
{variables.getMessage('modals.main.addons.create.title')}
|
||||
</span>
|
||||
</div>
|
||||
<div className="emptyItems">
|
||||
<div className="emptyNewMessage">
|
||||
<MdOutlineExtensionOff />
|
||||
<span className="title">
|
||||
{variables.getMessage('modals.main.addons.create.moved_title')}
|
||||
</span>
|
||||
<span className="subtitle">
|
||||
{variables.getMessage('modals.main.addons.create.moved_description')}
|
||||
</span>
|
||||
<div className="createButtons">
|
||||
<button>Coming soon...</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import { PureComponent, createRef } from 'react';
|
||||
import { MdOutlineWifiOff } from 'react-icons/md';
|
||||
import Modal from 'react-modal';
|
||||
|
||||
import Lightbox from '../../marketplace/components/Elements/Lightbox/Lightbox';
|
||||
import Lightbox from '../../../../../marketplace/components/Elements/Lightbox/Lightbox';
|
||||
|
||||
export default class Changelog extends PureComponent {
|
||||
constructor() {
|
||||
|
||||
@@ -2,8 +2,8 @@ import variables from 'config/variables';
|
||||
import { memo } from 'react';
|
||||
import Tabs from './backend/Tabs';
|
||||
|
||||
import Added from '../marketplace/views/Added';
|
||||
import Create from '../marketplace/views/Create';
|
||||
import Added from '../../../../marketplace/views/Added';
|
||||
import Create from '../../../../marketplace/views/Create';
|
||||
|
||||
function Addons(props) {
|
||||
return (
|
||||
|
||||
@@ -2,7 +2,7 @@ import variables from 'config/variables';
|
||||
import { memo } from 'react';
|
||||
|
||||
import Tabs from './backend/Tabs';
|
||||
import MarketplaceTab from '../marketplace/views/Browse';
|
||||
import MarketplaceTab from '../../../../marketplace/views/Browse';
|
||||
|
||||
function Marketplace(props) {
|
||||
return (
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
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 };
|
||||
@@ -1,45 +0,0 @@
|
||||
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 };
|
||||
@@ -1,69 +0,0 @@
|
||||
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 };
|
||||
@@ -1,91 +0,0 @@
|
||||
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 };
|
||||
@@ -1,78 +0,0 @@
|
||||
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 };
|
||||
@@ -1,53 +0,0 @@
|
||||
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 };
|
||||
@@ -1,70 +0,0 @@
|
||||
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 };
|
||||
@@ -1,7 +0,0 @@
|
||||
export * from './Intro';
|
||||
export * from './ChooseLanguage';
|
||||
export * from './ImportSettings';
|
||||
export * from './ThemeSelection';
|
||||
export * from './StyleSelection';
|
||||
export * from './PrivacyOptions';
|
||||
export * from './Final';
|
||||
@@ -1,159 +0,0 @@
|
||||
// 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;
|
||||
@@ -1,432 +0,0 @@
|
||||
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;
|
||||
@@ -1,31 +0,0 @@
|
||||
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 };
|
||||
@@ -1 +0,0 @@
|
||||
export * from './AsideImage';
|
||||
@@ -1,31 +0,0 @@
|
||||
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 };
|
||||
@@ -1 +0,0 @@
|
||||
export * from './ProgressBar';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './ProgressBar';
|
||||
export * from './AsideImage';
|
||||
@@ -1,7 +0,0 @@
|
||||
const Content = ({ children }) => {
|
||||
return (
|
||||
<div className="content">{children}</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { Content as default, Content };
|
||||
@@ -1,12 +0,0 @@
|
||||
function Header({ title, subtitle }) {
|
||||
return (
|
||||
<>
|
||||
<span className="mainTitle">{title}</span>
|
||||
<span className="subtitle">
|
||||
{subtitle}
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export { Header as default, Header };
|
||||
@@ -1,7 +0,0 @@
|
||||
function Layout() {
|
||||
return (
|
||||
<h1>Cheese</h1>
|
||||
)
|
||||
}
|
||||
|
||||
export { Layout as default, Layout };
|
||||
@@ -1,7 +0,0 @@
|
||||
const Panel = ({ children, type }) => (
|
||||
<section className={type}>
|
||||
{children}
|
||||
</section>
|
||||
);
|
||||
|
||||
export { Panel as default, Panel };
|
||||
@@ -1,5 +0,0 @@
|
||||
const Wrapper = ({ children }) => (
|
||||
<div className="welcomeContent">{children}</div>
|
||||
)
|
||||
|
||||
export { Wrapper as default, Wrapper };
|
||||
@@ -1,4 +0,0 @@
|
||||
export * from './Wrapper';
|
||||
export * from './Panel';
|
||||
export * from './Header';
|
||||
export * from './Content';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './Layout';
|
||||
export * from './Elements';
|
||||
@@ -1,331 +0,0 @@
|
||||
@import '../main/scss/index';
|
||||
@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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user