Merge branch '8.0' of https://github.com/mue/mue into 8.0

This commit is contained in:
David Ralph
2024-06-15 11:35:35 +01:00
6 changed files with 558 additions and 15 deletions

View File

@@ -0,0 +1,77 @@
import variables from 'config/variables';
import { motion } from 'framer-motion';
import { useState } from 'react';
import placeholderIcon from 'assets/icons/marketplace-placeholder.png';
import { useTab } from 'components/Elements/MainModal/backend/TabContext';
function ItemCard({ item, onClick, type, onCollection, isCurator }) {
item._onCollection = onCollection;
return (
<motion.div
whileHover={{ y: -10 }}
transition={{ type: 'spring', stiffness: 150, damping: 15, mass: 0.5 }}
className="item"
onClick={onClick}
key={item.name}
>
<img
className="item-back"
alt=""
draggable={false}
src={item.icon_url}
onError={(e) => {
e.target.onerror = null;
e.target.src = placeholderIcon;
}}
aria-hidden="true"
/>
<img
className="item-icon"
alt="icon"
draggable={false}
src={item.icon_url}
onError={(e) => {
e.target.onerror = null;
e.target.src = placeholderIcon;
}}
/>
<div className="card-details">
<span className="card-title">{item.display_name || item.name}</span>
{!isCurator ? (
<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>
</motion.div>
);
}
const NewItems = ({ items }) => {
const { setSubTab } = useTab();
return (
<div className="items">
{items.map((item, index) => (
<ItemCard
onClick={() => setSubTab(item.name)}
//isCurator={isCurator}
item={item}
//toggleFunction={toggleFunction}
//type={type}
//onCollection={onCollection}
key={index}
/>
))}
</div>
);
};
export { NewItems };

View File

@@ -0,0 +1,419 @@
import variables from 'config/variables';
import { useTab } from 'components/Elements/MainModal/backend/TabContext';
import { useEffect, useState } from 'react';
import { ShareModal } from 'components/Elements';
import {
MdIosShare,
MdFlag,
MdAccountCircle,
MdCalendarMonth,
MdFormatQuote,
MdImage,
MdTranslate,
MdOutlineWarning,
MdStyle,
MdClose,
MdLibraryAdd,
} from 'react-icons/md';
import { Header } from 'components/Layout/Settings';
import Modal from 'react-modal';
import Markdown from 'markdown-to-jsx';
const NewItemPage = () => {
const { subTab } = useTab();
const controller = new AbortController();
const [itemName, setItemName] = useState('');
const [ItemDescription, setItemDescription] = useState('');
async function getItemData() {
const item = await (
await fetch(`${variables.constants.API_URL}/marketplace/item/quote_packs/${subTab}`, {
signal: controller.signal,
})
).json();
console.log(item);
setItemName(item.data.display_name);
setItemDescription(item.data.description);
}
useEffect(() => {
getItemData();
}, []);
const moreInfoItem = (icon, header, text) => (
<div className="infoItem">
{icon}
<div className="text">
<span className="header">{header}</span>
<span>{text}</span>
</div>
</div>
);
const itemWarning = () => {
const template = (message) => (
<div className="itemWarning">
<MdOutlineWarning />
<div className="text">
<span className="header">Warning</span>
<span>{message}</span>
</div>
</div>
);
if (this.props.data.data.sideload === true) {
return template(variables.getMessage('modals.main.marketplace.product.sideload_warning'));
}
if (this.props.data.data.image_api === true) {
return template(variables.getMessage('modals.main.marketplace.product.third_party_api'));
}
if (this.props.data.data.language !== undefined && this.props.data.data.language !== null) {
if (shortLocale !== this.props.data.data.language) {
return template(variables.getMessage('modals.main.marketplace.product.not_in_language'));
}
}
return null;
};
{
/*async function toggle(pageType, data) {
if (pageType === 'item') {
const toggleType = type === 'all' || type === 'collections' ? data.type : type;
const item = await (
await fetch(`${variables.constants.API_URL}/marketplace/item/${toggleType}/${data.name}`, {
signal: controller.signal,
})
).json();
if (controller.signal.aborted === true) {
return;
}
// check if already installed
let addonInstalled = false;
let addonInstalledVersion;
const installed = JSON.parse(localStorage.getItem('installed'));
if (installed.some((item) => item.name === item.data.name)) {
addonInstalled = true;
for (let i = 0; i < installed.length; i++) {
if (installed[i].name === item.data.name) {
addonInstalledVersion = installed[i].version;
break;
}
}
}
setItem({
onCollection: data._onCollection,
type: item.data.type,
display_name: item.data.name,
author: item.data.author,
description: item.data.description,
version: item.data.version,
icon: item.data.screenshot_url,
data: item.data,
local: {
installed: addonInstalled,
version: addonInstalledVersion,
},
slug: data.name,
});
setType('item');
variables.stats.postEvent('marketplace-item', `${item.display_name} viewed`);
} else if (pageType === 'collection') {
setDone(false);
setItem({});
const collection = await (
await fetch(`${variables.constants.API_URL}/marketplace/collection/${data}`, {
signal: controller.signal,
})
).json();
setItems(collection.data.items);
setCollection({
visible: true,
title: collection.data.display_name,
description: collection.data.description,
img: collection.data.img,
});
setType('collection');
} else {
setItem({});
setCollection({});
setType('normal');
}
}*/
}
return (
<>
{/*<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.slug)}
modalClose={() => this.setState({ shareModal: false })}
/>
</Modal>*/}
{/*<Header
title={
this.props.addons
? variables.getMessage('modals.main.addons.added')
: this.props.data.onCollection && this.props.data.data.in_collections?.length > 0
? this.props.data.data.in_collections[0].display_name
: variables.getMessage('modals.main.navbar.marketplace')
}
secondaryTitle={
this.props.data.data.sideload
? this.props.data.data.name
: this.props.data.data.display_name
}
report={false}
goBack={this.props.toggleFunction}
/>*/}
{itemName}
<div className="itemPage">
{/*<div className="itemShowcase">
<div className="subHeader">
{moreInfoItem(
<MdAccountCircle />,
variables.getMessage('modals.main.marketplace.product.created_by'),
this.props.data.author,
)}
{itemWarning()}
</div>
{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}
onError={(e) => {
e.target.onerror = null;
e.target.src = placeholderIcon;
}}
/>
)}*/}
<div className="marketplaceDescription">
<span className="title">
{variables.getMessage('modals.main.marketplace.product.description')}
</span>
<Markdown>{ItemDescription}</Markdown>
</div>
</div>
{/*}
{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
? variables.getMessage('modals.main.marketplace.product.show_all')
: 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
? variables.getMessage('modals.main.marketplace.product.show_all')
: variables.getMessage('modals.main.marketplace.product.show_less')}
</span>
</div>
</>
)}
<div className="marketplaceDescription">
<span className="title">
{variables.getMessage('modals.main.marketplace.product.details')}
</span>
<div className="moreInfo">
{this.props.data.data.updated_at &&
moreInfoItem(
<MdCalendarMonth />,
variables.getMessage('modals.main.marketplace.product.updated_at'),
formattedDate,
)}
{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'),
languageNames.of(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>
<div
className="itemInfo"
style={{
backgroundImage: `url("${this.props.data.data.icon_url}")`,
}}
>
<div className="front">
<img
className="icon"
alt="icon"
draggable={false}
src={this.props.data.data.icon_url}
onError={(e) => {
e.target.onerror = null;
e.target.src = placeholderIcon;
}}
/>
{localStorage.getItem('welcomePreview') !== 'true' ? (
this.state.button
) : (
<p style={{ textAlign: 'center' }}>
{variables.getMessage(
'modals.main.marketplace.product.buttons.not_available_preview',
)}
</p>
)}
{this.props.data.data.sideload !== true && (
<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={() =>
window.open(
variables.constants.REPORT_ITEM +
this.props.data.data.display_name.split(' ').join('+'),
'_blank',
)
}
icon={<MdFlag />}
tooltipTitle={variables.getMessage(
'modals.main.marketplace.product.buttons.report',
)}
tooltipKey="report"
/>
</div>
)}
{this.props.data.data.in_collections?.length > 0 && (
<div>
<div className="inCollection">
<span className="subtitle">
{variables.getMessage('modals.main.marketplace.product.part_of')}
</span>
<span
className="title"
onClick={() =>
this.props.toggleFunction(
'collection',
this.props.data.data.in_collections[0].name,
)
}
>
{this.props.data.data.in_collections[0].display_name}
</span>
</div>
</div>
)}
</div>
</div>
</div>
{/*}
{moreByCurator.length > 1 && (
<div className="moreFromCurator">
<span className="title">
{variables.getMessage('modals.main.marketplace.product.more_from_curator', {
name: this.props.data.author,
})}
</span>
<div>
<Items
isCurator={true}
type={this.props.data.data.type}
items={moreByCurator}
onCollection={this.state.collection}
toggleFunction={(input) => this.props.toggleFunction('item', input)}
collectionFunction={(input) => this.props.toggleFunction('collection', input)}
filter={''}
moreByCreator={true}
showCreateYourOwn={false}
/>
</div>
</div>
)}
*/}
</>
);
};
export { NewItemPage as default, NewItemPage };

View File

@@ -1,9 +1,49 @@
import variables from 'config/variables';
import { memo } from 'react';
import { memo, useState, useEffect } from 'react';
import MarketplaceTab from '../../marketplace/views/Browse';
import { sortItems } from '../../marketplace/api';
import { NewItems as Items } from '../../marketplace/components/Items/NewItems';
import { useTab } from 'components/Elements/MainModal/backend/TabContext';
import { NewItemPage } from '../../marketplace/views/newItemPage';
function Marketplace(props) {
const [done, setDone] = useState(false);
const [items, setItems] = useState([]);
const [type, setType] = useState('all');
const [selectedItem, setSelectedItem] = useState(null);
const { subTab } = useTab();
const controller = new AbortController();
async function getItems() {
setDone(false);
const dataURL =
variables.constants.API_URL +
(type === 'collections' ? '/marketplace/collections' : '/marketplace/items/' + type);
const { data } = await (
await fetch(dataURL, {
signal: controller.signal,
})
).json();
if (controller.signal.aborted === true) {
return;
}
setItems(sortItems(data, 'z-a'));
setDone(true);
}
useEffect(() => {
if (navigator.onLine === false || localStorage.getItem('offlineMode') === 'true') {
return;
}
getItems();
}, []);
return (
{
/*<Tabs changeTab={(type) => props.changeTab(type)} current="marketplace" modalClose={props.modalClose}>
@@ -29,7 +69,15 @@ function Marketplace(props) {
},
(
<div className="modalTabContent">
<MarketplaceTab type="all" />
{/*<MarketplaceTab type="all" />
{selectedItem !== null ? <span>{selectedItem?.display_name}</span> : null}*/}
{subTab === '' ? (
<Items items={items} />
) : (
<>
<NewItemPage />
</>
)}
</div>
)
);