mirror of
https://github.com/mue/mue.git
synced 2026-07-16 05:23:49 +02:00
refactor(marketplace): WIP implementation of collection
This commit is contained in:
@@ -208,8 +208,8 @@ const TabNavbar = ({ modalClose }) => {
|
||||
key={tab.id}
|
||||
onClick={() => changeTab(tab.id)}
|
||||
className={`${
|
||||
activeTab === tab.id ? '' : 'hover:text-white/60'
|
||||
} flex flex-row gap-2 items-center relative rounded-sm px-3 py-1.5 text-sm text-white outline-sky-400 transition focus-visible:outline-2`}
|
||||
activeTab === tab.id ? '' : 'hover:text-white/70'
|
||||
} transition-all duration-800 ease-in-out flex flex-row gap-2 items-center relative rounded-sm px-3 py-1.5 text-sm text-white outline-sky-400 transition focus-visible:outline-2`}
|
||||
style={{
|
||||
WebkitTapHighlightColor: 'transparent',
|
||||
}}
|
||||
|
||||
@@ -81,60 +81,61 @@
|
||||
padding: 5px 10px;
|
||||
}
|
||||
}
|
||||
.itemPage {
|
||||
table {
|
||||
border-collapse: separate;
|
||||
border-radius: 10px;
|
||||
margin-top: 20px;
|
||||
|
||||
table {
|
||||
border-collapse: separate;
|
||||
border-radius: 10px;
|
||||
margin-top: 20px;
|
||||
|
||||
@include themed {
|
||||
box-shadow: 0 0 0 1px t($modal-sidebarActive);
|
||||
padding: 0;
|
||||
border: 0;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
tr:first-child {
|
||||
@include themed {
|
||||
border-radius: t($borderRadius);
|
||||
color: t($subColor);
|
||||
box-shadow: 0 0 0 1px t($modal-sidebarActive);
|
||||
padding: 0;
|
||||
border: 0;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
letter-spacing: 2px;
|
||||
|
||||
th {
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
tr {
|
||||
th:last-child {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
@include themed {
|
||||
color: t($subColor);
|
||||
}
|
||||
}
|
||||
|
||||
tr:not(:first-child) {
|
||||
@include themed {
|
||||
background: t($modal-secondaryColour);
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 90%;
|
||||
margin: 10px;
|
||||
|
||||
tr:first-child {
|
||||
@include themed {
|
||||
color: t($color);
|
||||
border-radius: t($borderRadius);
|
||||
color: t($subColor);
|
||||
}
|
||||
|
||||
letter-spacing: 2px;
|
||||
|
||||
th {
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
tr {
|
||||
th:last-child {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
@include themed {
|
||||
color: t($subColor);
|
||||
}
|
||||
}
|
||||
|
||||
tr:not(:first-child) {
|
||||
@include themed {
|
||||
background: t($modal-secondaryColour);
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 90%;
|
||||
margin: 10px;
|
||||
|
||||
@include themed {
|
||||
color: t($color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@ export const useMarketData = () => useContext(MarketDataContext);
|
||||
export const MarketplaceDataProvider = ({ children }) => {
|
||||
const [done, setDone] = useState(false);
|
||||
const [items, setItems] = useState([]);
|
||||
const [collections, setCollections] = useState([]);
|
||||
const [selectedItem, setSelectedItem] = useState(null);
|
||||
let numOfRequests = 0;
|
||||
const controller = new AbortController();
|
||||
|
||||
const getItems = async (type = 'all') => {
|
||||
@@ -28,10 +30,32 @@ export const MarketplaceDataProvider = ({ children }) => {
|
||||
return;
|
||||
}
|
||||
|
||||
//console.log(data);
|
||||
numOfRequests++;
|
||||
console.log("Request number: ", numOfRequests);
|
||||
|
||||
setItems(sortItems(data, 'z-a'));
|
||||
setDone(true);
|
||||
};
|
||||
|
||||
const getCollections = async () => {
|
||||
setDone(false);
|
||||
const { data } = await (
|
||||
await fetch(`${variables.constants.API_URL}/marketplace/collections`, {
|
||||
signal: controller.signal,
|
||||
})
|
||||
).json();
|
||||
|
||||
if (controller.signal.aborted === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(data);
|
||||
|
||||
setCollections(data);
|
||||
setDone(true);
|
||||
}
|
||||
|
||||
const getItemData = async (itemType, itemName) => {
|
||||
const response = await fetch(
|
||||
`${variables.constants.API_URL}/marketplace/item/${itemType}/${itemName}`,
|
||||
@@ -46,7 +70,7 @@ export const MarketplaceDataProvider = ({ children }) => {
|
||||
});
|
||||
};
|
||||
return (
|
||||
<MarketDataContext.Provider value={{ done, items, selectedItem, getItems, getItemData, setSelectedItem }}>
|
||||
<MarketDataContext.Provider value={{ done, items, selectedItem, getItems, getCollections, getItemData, setSelectedItem, collections }}>
|
||||
{children}
|
||||
</MarketDataContext.Provider>
|
||||
);
|
||||
|
||||
@@ -2,29 +2,30 @@ import { MdOutlineArrowForward, MdOutlineOpenInNew } from 'react-icons/md';
|
||||
import { Button } from 'components/Elements';
|
||||
import variables from 'config/variables';
|
||||
|
||||
const Collection = ({ collection, collectionFunction }) => {
|
||||
const { news, background_colour, img, display_name, description } = collection;
|
||||
const Collection = ({ collections, collectionFunction }) => {
|
||||
const randomIndex = Math.floor(Math.random() * collections.length);
|
||||
const collection = collections[randomIndex];
|
||||
|
||||
const getStyle = () => {
|
||||
if (news) {
|
||||
return { backgroundColor: background_colour };
|
||||
if (collection?.news) {
|
||||
return { backgroundColor: collection?.background_colour };
|
||||
}
|
||||
|
||||
return {
|
||||
backgroundImage: `linear-gradient(to left, #000, transparent, #000), url('${img}')`,
|
||||
backgroundImage: `linear-gradient(to left, #000, transparent, #000), url('${collection?.img}')`,
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="collection" style={getStyle()}>
|
||||
<div className="content">
|
||||
<span className="title">{display_name} using component</span>
|
||||
<span className="subtitle">{description ? description.substr(0, 75) : ''}</span>
|
||||
<span className="title">{collection?.display_name}</span>
|
||||
<span className="subtitle">{collection?.description ? collection?.description.substr(0, 75) : ''}</span>
|
||||
</div>
|
||||
{collection.news === true ? (
|
||||
{collection?.news === true ? (
|
||||
<a
|
||||
className="btn-collection"
|
||||
href={collection.news_link}
|
||||
href={collection?.news_link}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
@@ -33,7 +34,7 @@ const Collection = ({ collection, collectionFunction }) => {
|
||||
) : (
|
||||
<Button
|
||||
type="collection"
|
||||
onClick={() => collectionFunction(collection.name)}
|
||||
onClick={() => collectionFunction(collection?.name)}
|
||||
icon={<MdOutlineArrowForward />}
|
||||
label={variables.getMessage('marketplace:explore_collection')}
|
||||
iconPlacement={'right'}
|
||||
|
||||
@@ -1,191 +1,148 @@
|
||||
import variables from 'config/variables';
|
||||
import React, { memo } from 'react';
|
||||
import { MdAutoFixHigh, MdOutlineArrowForward, MdOutlineOpenInNew } from 'react-icons/md';
|
||||
import placeholderIcon from 'assets/icons/marketplace-placeholder.png';
|
||||
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';
|
||||
import { useMarketData } from 'features/marketplace/api/MarketplaceDataContext';
|
||||
import { MdOpenInNew } from "react-icons/md";
|
||||
|
||||
import { Button } from 'components/Elements';
|
||||
function ItemCard({ item, type, onCollection, isCurator, cardStyle }) {
|
||||
const { getItemData, selectedItem, setSelectedItem } = useMarketData();
|
||||
const { setSubTab } = useTab();
|
||||
|
||||
function filterItems(item, filter) {
|
||||
const lowerCaseFilter = filter.toLowerCase();
|
||||
return (
|
||||
item.name?.toLowerCase().includes(lowerCaseFilter) ||
|
||||
filter === '' ||
|
||||
item.author?.toLowerCase().includes(lowerCaseFilter) ||
|
||||
item.type?.toLowerCase().includes(lowerCaseFilter)
|
||||
);
|
||||
}
|
||||
|
||||
function ItemCard({ item, toggleFunction, 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={() => toggleFunction(item)}
|
||||
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('marketplace:by', { author: item.author })}
|
||||
</span>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
|
||||
{type === 'all' && !onCollection ? (
|
||||
<span className="card-type">
|
||||
{variables.getMessage('marketplace:' + item.type)}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
const SelectItem = () => {
|
||||
console.log('Item selected: ', item.display_name);
|
||||
console.log('Item type: ', item.type);
|
||||
getItemData(item.type, item.name).then((data) => {
|
||||
console.log('Selected item: ', data);
|
||||
//setSelectedItem(data);
|
||||
setSubTab(data.display_name);
|
||||
});
|
||||
};
|
||||
|
||||
const getName = (name) => {
|
||||
const nameMappings = {
|
||||
photos: 'photo_packs',
|
||||
quotes: 'quote_packs',
|
||||
settings: 'preset_settings',
|
||||
};
|
||||
return nameMappings[name] || name;
|
||||
};
|
||||
|
||||
switch (cardStyle) {
|
||||
case 'list':
|
||||
return (
|
||||
<tr key={item.name} className="py-5 hover:bg-white/5 rounded-lg cursor-pointer" onClick={SelectItem}>
|
||||
<td className="flex flex-row items-center gap-10 py-3">
|
||||
<img
|
||||
className="w-10 h-10 rounded-lg"
|
||||
alt="icon"
|
||||
draggable={false}
|
||||
src={item.icon_url}
|
||||
onError={(e) => {
|
||||
e.target.onerror = null;
|
||||
e.target.src = placeholderIcon;
|
||||
}}
|
||||
/>
|
||||
{item.display_name}
|
||||
</td>
|
||||
<td>{variables.getMessage('marketplace:' + getName(item.type)) || 'marketplace'}</td>
|
||||
<td>{item.author}</td>
|
||||
<td><MdOpenInNew /></td>
|
||||
</tr>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<motion.div
|
||||
whileHover={{ y: -10 }}
|
||||
transition={{ type: 'spring', stiffness: 150, damping: 15, mass: 0.5 }}
|
||||
className="item"
|
||||
onClick={SelectItem}
|
||||
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('marketplace:by', { author: item.author })}
|
||||
</span>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
|
||||
{type === true && !onCollection ? (
|
||||
<span className="card-type">{variables.getMessage('marketplace:' + item.type)}</span>
|
||||
) : null}
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function Items({
|
||||
isCurator,
|
||||
type,
|
||||
items,
|
||||
collection,
|
||||
toggleFunction,
|
||||
collectionFunction,
|
||||
onCollection,
|
||||
filter,
|
||||
moreByCreator,
|
||||
showCreateYourOwn,
|
||||
}) {
|
||||
const shouldShowCollection =
|
||||
((collection && !onCollection && (filter === null || filter === '')) ||
|
||||
(type === 'collections' && !onCollection && (filter === null || filter === ''))) &&
|
||||
type !== 'preset_settings';
|
||||
const NewItems = ({ items, view }) => {
|
||||
const { setSubTab } = useTab();
|
||||
|
||||
return (
|
||||
<>
|
||||
{shouldShowCollection && (
|
||||
<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('marketplace:learn_more')} <MdOutlineOpenInNew />
|
||||
</a>
|
||||
) : (
|
||||
<Button
|
||||
type="collection"
|
||||
onClick={() => collectionFunction(collection?.name)}
|
||||
icon={<MdOutlineArrowForward />}
|
||||
label={variables.getMessage('marketplace:explore_collection')}
|
||||
iconPlacement={'right'}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-row gap-2 my-3">
|
||||
<div
|
||||
onClick={() => {
|
||||
this.setState({ type: 'all' });
|
||||
this.getItems();
|
||||
}}
|
||||
className="transition-all duration-200 bg-white rounded-full px-6 py-2 text-base text-black"
|
||||
>
|
||||
All
|
||||
</div>
|
||||
<div
|
||||
onClick={() => {
|
||||
this.setState({ type: 'photo_packs' });
|
||||
this.getItems();
|
||||
}}
|
||||
className="transition-all duration-200 bg-[#333] hover:bg-[#222222] cursor-pointer rounded-full px-6 py-2 text-base text-white"
|
||||
>
|
||||
Photo Packs
|
||||
</div>
|
||||
<div
|
||||
onClick={() => {
|
||||
this.setState({ type: 'quote_packs' });
|
||||
this.getItems();
|
||||
}}
|
||||
className="transition-all duration-200 bg-[#333] hover:bg-[#222222] cursor-pointer rounded-full px-6 py-2 text-base text-white"
|
||||
>
|
||||
Quote Packs
|
||||
</div>
|
||||
</div>
|
||||
<div className={`items ${moreByCreator ? 'creatorItems' : ''}`}>
|
||||
{items
|
||||
?.filter((item) => filterItems(item, filter))
|
||||
.map((item, index) => (
|
||||
switch (view) {
|
||||
case 'list':
|
||||
return (
|
||||
<table className="w-full">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th>{variables.getMessage('settings:sections.quote.author')}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
{items.map((item, index) => (
|
||||
<ItemCard
|
||||
onClick={() => setSubTab(item.name)}
|
||||
item={item}
|
||||
type={true}
|
||||
key={index}
|
||||
cardStyle="list"
|
||||
/>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<div className="items">
|
||||
{items.map((item, index) => (
|
||||
<ItemCard
|
||||
isCurator={isCurator}
|
||||
onClick={() => setSubTab(item.name)}
|
||||
item={item}
|
||||
toggleFunction={toggleFunction}
|
||||
type={type}
|
||||
onCollection={onCollection}
|
||||
type={true}
|
||||
key={index}
|
||||
cardStyle="card"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className="loader"></div>
|
||||
{!onCollection && showCreateYourOwn ? (
|
||||
<div className="createYourOwn">
|
||||
<MdAutoFixHigh />
|
||||
<span className="title">{variables.getMessage('marketplace:cant_find')}</span>
|
||||
<span className="subtitle">
|
||||
{variables.getMessage('marketplace:knowledgebase_one') + ' '}
|
||||
<a
|
||||
className="link"
|
||||
target="_blank"
|
||||
href={variables.constants.KNOWLEDGEBASE}
|
||||
rel="noreferrer"
|
||||
>
|
||||
{variables.getMessage('marketplace:knowledgebase_two')}
|
||||
</a>
|
||||
{' ' + variables.getMessage('marketplace:knowledgebase_three')}
|
||||
</span>
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const MemoizedItems = memo(Items);
|
||||
export { MemoizedItems as default, MemoizedItems as Items };
|
||||
export { NewItems };
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
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';
|
||||
import { useMarketData } from 'features/marketplace/api/MarketplaceDataContext';
|
||||
|
||||
function ItemCard({ item, onClick, type, onCollection, isCurator }) {
|
||||
const { getItemData, selectedItem, setSelectedItem } = useMarketData();
|
||||
const { setSubTab } = useTab();
|
||||
|
||||
item._onCollection = onCollection;
|
||||
|
||||
const SelectItem = () => {
|
||||
console.log("Item selected: ", item.display_name);
|
||||
console.log("Item type: ", item.type);
|
||||
getItemData(item.type, item.name).then((data) => {
|
||||
console.log("Selected item: ", data);
|
||||
//setSelectedItem(data);
|
||||
setSubTab(data.display_name);
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
whileHover={{ y: -10 }}
|
||||
transition={{ type: 'spring', stiffness: 150, damping: 15, mass: 0.5 }}
|
||||
className="item"
|
||||
onClick={SelectItem}
|
||||
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('marketplace:by', { author: item.author })}
|
||||
</span>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
|
||||
{type === 'all' && !onCollection ? (
|
||||
<span className="card-type">
|
||||
{variables.getMessage('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 };
|
||||
191
src/features/marketplace/components/Items/OldItems.jsx
Normal file
191
src/features/marketplace/components/Items/OldItems.jsx
Normal file
@@ -0,0 +1,191 @@
|
||||
import variables from 'config/variables';
|
||||
import React, { memo } from 'react';
|
||||
import { MdAutoFixHigh, MdOutlineArrowForward, MdOutlineOpenInNew } from 'react-icons/md';
|
||||
import placeholderIcon from 'assets/icons/marketplace-placeholder.png';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
import { Button } from 'components/Elements';
|
||||
|
||||
function filterItems(item, filter) {
|
||||
const lowerCaseFilter = filter.toLowerCase();
|
||||
return (
|
||||
item.name?.toLowerCase().includes(lowerCaseFilter) ||
|
||||
filter === '' ||
|
||||
item.author?.toLowerCase().includes(lowerCaseFilter) ||
|
||||
item.type?.toLowerCase().includes(lowerCaseFilter)
|
||||
);
|
||||
}
|
||||
|
||||
function ItemCard({ item, toggleFunction, 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={() => toggleFunction(item)}
|
||||
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('marketplace:by', { author: item.author })}
|
||||
</span>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
|
||||
{type === 'all' && !onCollection ? (
|
||||
<span className="card-type">
|
||||
{variables.getMessage('marketplace:' + item.type)}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
function Items({
|
||||
isCurator,
|
||||
type,
|
||||
items,
|
||||
collection,
|
||||
toggleFunction,
|
||||
collectionFunction,
|
||||
onCollection,
|
||||
filter,
|
||||
moreByCreator,
|
||||
showCreateYourOwn,
|
||||
}) {
|
||||
const shouldShowCollection =
|
||||
((collection && !onCollection && (filter === null || filter === '')) ||
|
||||
(type === 'collections' && !onCollection && (filter === null || filter === ''))) &&
|
||||
type !== 'preset_settings';
|
||||
|
||||
return (
|
||||
<>
|
||||
{shouldShowCollection && (
|
||||
<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('marketplace:learn_more')} <MdOutlineOpenInNew />
|
||||
</a>
|
||||
) : (
|
||||
<Button
|
||||
type="collection"
|
||||
onClick={() => collectionFunction(collection?.name)}
|
||||
icon={<MdOutlineArrowForward />}
|
||||
label={variables.getMessage('marketplace:explore_collection')}
|
||||
iconPlacement={'right'}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-row gap-2 my-3">
|
||||
<div
|
||||
onClick={() => {
|
||||
this.setState({ type: 'all' });
|
||||
this.getItems();
|
||||
}}
|
||||
className="transition-all duration-200 bg-white rounded-full px-6 py-2 text-base text-black"
|
||||
>
|
||||
All
|
||||
</div>
|
||||
<div
|
||||
onClick={() => {
|
||||
this.setState({ type: 'photo_packs' });
|
||||
this.getItems();
|
||||
}}
|
||||
className="transition-all duration-200 bg-[#333] hover:bg-[#222222] cursor-pointer rounded-full px-6 py-2 text-base text-white"
|
||||
>
|
||||
Photo Packs
|
||||
</div>
|
||||
<div
|
||||
onClick={() => {
|
||||
this.setState({ type: 'quote_packs' });
|
||||
this.getItems();
|
||||
}}
|
||||
className="transition-all duration-200 bg-[#333] hover:bg-[#222222] cursor-pointer rounded-full px-6 py-2 text-base text-white"
|
||||
>
|
||||
Quote Packs
|
||||
</div>
|
||||
</div>
|
||||
<div className={`items ${moreByCreator ? 'creatorItems' : ''}`}>
|
||||
{items
|
||||
?.filter((item) => filterItems(item, filter))
|
||||
.map((item, index) => (
|
||||
<ItemCard
|
||||
isCurator={isCurator}
|
||||
item={item}
|
||||
toggleFunction={toggleFunction}
|
||||
type={type}
|
||||
onCollection={onCollection}
|
||||
key={index}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className="loader"></div>
|
||||
{!onCollection && showCreateYourOwn ? (
|
||||
<div className="createYourOwn">
|
||||
<MdAutoFixHigh />
|
||||
<span className="title">{variables.getMessage('marketplace:cant_find')}</span>
|
||||
<span className="subtitle">
|
||||
{variables.getMessage('marketplace:knowledgebase_one') + ' '}
|
||||
<a
|
||||
className="link"
|
||||
target="_blank"
|
||||
href={variables.constants.KNOWLEDGEBASE}
|
||||
rel="noreferrer"
|
||||
>
|
||||
{variables.getMessage('marketplace:knowledgebase_two')}
|
||||
</a>
|
||||
{' ' + variables.getMessage('marketplace:knowledgebase_three')}
|
||||
</span>
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const MemoizedItems = memo(Items);
|
||||
export { MemoizedItems as default, MemoizedItems as Items };
|
||||
@@ -5,8 +5,8 @@ import { toast } from 'react-toastify';
|
||||
import Modal from 'react-modal';
|
||||
|
||||
import { SideloadFailedModal } from '../components/Elements/SideloadFailedModal/SideloadFailedModal';
|
||||
import ItemPage from './ItemPage';
|
||||
import Items from '../components/Items/Items';
|
||||
import ItemPage from './oldItemPage';
|
||||
import Items from '../components/Items/OldItems';
|
||||
import { Dropdown, FileUpload } from 'components/Form/Settings';
|
||||
import { Header, CustomActions } from 'components/Layout/Settings';
|
||||
import { Button } from 'components/Elements';
|
||||
|
||||
@@ -3,8 +3,8 @@ import { useState, useEffect } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { MdWifiOff, MdLocalMall, MdOutlineArrowForward, MdLibraryAdd } from 'react-icons/md';
|
||||
|
||||
import ItemPage from './ItemPage';
|
||||
import Items from '../components/Items/Items';
|
||||
import ItemPage from './oldItemPage';
|
||||
import Items from '../components/Items/OldItems';
|
||||
|
||||
import { Header } from 'components/Layout/Settings';
|
||||
import { Button } from 'components/Elements';
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import variables from 'config/variables';
|
||||
import { PureComponent, Fragment } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useTab } from 'components/Elements/MainModal/backend/TabContext';
|
||||
import placeholderIcon from 'assets/icons/marketplace-placeholder.png';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { ShareModal } from 'components/Elements';
|
||||
import {
|
||||
MdIosShare,
|
||||
MdFlag,
|
||||
@@ -14,465 +16,343 @@ import {
|
||||
MdClose,
|
||||
MdLibraryAdd,
|
||||
} 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 '../components/Elements/Carousel';
|
||||
import { ShareModal } from 'components/Elements';
|
||||
import placeholderIcon from 'assets/icons/marketplace-placeholder.png';
|
||||
import { Items } from '../components/Items/Items';
|
||||
|
||||
import Modal from 'react-modal';
|
||||
import Markdown from 'markdown-to-jsx';
|
||||
import { Button } from 'components/Elements';
|
||||
import { useMarketData } from 'features/marketplace/api/MarketplaceDataContext';
|
||||
import { Carousel } from '../components/Elements/Carousel';
|
||||
|
||||
class ItemPage extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
console.log(this.props)
|
||||
this.state = {
|
||||
showUpdateButton:
|
||||
this.props.data.local.installed === true && this.props.data.local.version !== this.props.data.version,
|
||||
shareModal: false,
|
||||
count: 5,
|
||||
moreByCurator: [],
|
||||
};
|
||||
this.buttons = {
|
||||
uninstall: (
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => this.manage('uninstall')}
|
||||
icon={<MdClose />}
|
||||
label={variables.getMessage('marketplace:product.buttons.remove')}
|
||||
/>
|
||||
),
|
||||
install: (
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => this.manage('install')}
|
||||
icon={<MdLibraryAdd />}
|
||||
label={variables.getMessage('marketplace:product.buttons.addtomue')}
|
||||
/>
|
||||
),
|
||||
};
|
||||
}
|
||||
const ItemPage = () => {
|
||||
const { subTab } = useTab();
|
||||
const controller = new AbortController();
|
||||
const [count, setCount] = useState(5);
|
||||
const [item, setItemData] = useState(null);
|
||||
const [shareModal, setShareModal] = useState(false);
|
||||
const { selectedItem } = useMarketData();
|
||||
|
||||
async getCurator(name) {
|
||||
try {
|
||||
const { data } = await (
|
||||
await fetch(`${variables.constants.API_URL}/marketplace/curator/${name}`)
|
||||
).json();
|
||||
this.setState({
|
||||
moreByCurator: data.items,
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.getCurator(this.props.data.author);
|
||||
document.querySelector('#modal').scrollTop = 0;
|
||||
this.setState({
|
||||
button: this.props.data.local.installed ? this.buttons.uninstall : this.buttons.install,
|
||||
})
|
||||
}
|
||||
|
||||
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 getName = (name) => {
|
||||
const nameMappings = {
|
||||
photos: 'photo_packs',
|
||||
quotes: 'quote_packs',
|
||||
settings: 'preset_settings',
|
||||
};
|
||||
return nameMappings[name] || name;
|
||||
}
|
||||
};
|
||||
|
||||
manage(type) {
|
||||
if (type === 'install') {
|
||||
install(this.props.data.type, this.props.data.data);
|
||||
} else {
|
||||
uninstall(this.props.data.type,this.props.data.display_name);
|
||||
}
|
||||
const moreInfoItem = (icon, header, text) => (
|
||||
<div className="infoItem">
|
||||
{icon}
|
||||
<div className="text">
|
||||
<span className="header">{header}</span>
|
||||
<span>{text}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
render() {
|
||||
const locale = localStorage.getItem('language');
|
||||
const shortLocale = locale.includes('_') ? locale.split('_')[0] : locale;
|
||||
let languageNames = new Intl.DisplayNames([shortLocale], { type: 'language' });
|
||||
|
||||
const convertedType = (() => {
|
||||
const map = {
|
||||
photos: 'photo_packs',
|
||||
quotes: 'quote_packs',
|
||||
settings: 'preset_settings',
|
||||
};
|
||||
return map[this.props.data.data.type];
|
||||
})();
|
||||
|
||||
const moreByCurator = this.state.moreByCurator
|
||||
.filter((item) => item.type === convertedType && item.name !== this.props.data.data.name)
|
||||
.sort(() => 0.5 - Math.random())
|
||||
.slice(0, 3);
|
||||
|
||||
if (!this.props.data.display_name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// prevent console error
|
||||
let iconsrc = this.props.data.icon;
|
||||
if (!this.props.data.icon) {
|
||||
iconsrc = null;
|
||||
}
|
||||
|
||||
let updateButton;
|
||||
if (this.state.showUpdateButton) {
|
||||
updateButton = (
|
||||
<Fragment key="update">
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => this.updateAddon()}
|
||||
label={variables.getMessage('addons:product.buttons.update_addon')}
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
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('marketplace:product.sideload_warning'));
|
||||
}
|
||||
|
||||
if (this.props.data.data.image_api === true) {
|
||||
return template(variables.getMessage('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('marketplace:product.not_in_language'));
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const moreInfoItem = (icon, header, text) => (
|
||||
<div className="infoItem">
|
||||
{icon}
|
||||
const itemWarning = () => {
|
||||
const template = (message) => (
|
||||
<div className="itemWarning">
|
||||
<MdOutlineWarning />
|
||||
<div className="text">
|
||||
<span className="header">{header}</span>
|
||||
<span>{text}</span>
|
||||
<span className="header">Warning</span>
|
||||
<span>{message}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
let dateObj, formattedDate;
|
||||
if (this.props.data.data.updated_at) {
|
||||
dateObj = new Date(this.props.data.data.updated_at);
|
||||
formattedDate = new Intl.DateTimeFormat(shortLocale, {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: '2-digit',
|
||||
}).format(dateObj);
|
||||
/*if (this.props.data.data.sideload === true) {
|
||||
return template(variables.getMessage('marketplace:product.sideload_warning'));
|
||||
}
|
||||
|
||||
if (this.props.data.data.image_api === true) {
|
||||
return template(variables.getMessage('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('marketplace:product.not_in_language'));
|
||||
}
|
||||
}*/
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const ItemDetails = () => {
|
||||
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('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}
|
||||
/>
|
||||
<div className="itemPage">
|
||||
<div className="itemShowcase">
|
||||
<div className="subHeader">
|
||||
{moreInfoItem(
|
||||
<MdAccountCircle />,
|
||||
variables.getMessage('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>
|
||||
<div className="marketplaceDescription">
|
||||
<span className="title">{variables.getMessage('marketplace:product.details')}</span>
|
||||
<div className="moreInfo">
|
||||
{selectedItem?.updated_at &&
|
||||
moreInfoItem(
|
||||
<MdCalendarMonth />,
|
||||
variables.getMessage('marketplace:product.updated_at'),
|
||||
formattedDate,
|
||||
)}
|
||||
{this.props.data.data.settings && (
|
||||
<img
|
||||
alt="product"
|
||||
draggable={false}
|
||||
src={iconsrc}
|
||||
onError={(e) => {
|
||||
e.target.onerror = null;
|
||||
e.target.src = placeholderIcon;
|
||||
}}
|
||||
/>
|
||||
{selectedItem?.quotes &&
|
||||
moreInfoItem(
|
||||
<MdFormatQuote />,
|
||||
variables.getMessage('marketplace:product.no_quotes'),
|
||||
selectedItem?.quotes.length,
|
||||
)}
|
||||
<div className="marketplaceDescription">
|
||||
<span className="title">
|
||||
{variables.getMessage('marketplace:product.description')}
|
||||
{selectedItem?.photos &&
|
||||
moreInfoItem(
|
||||
<MdImage />,
|
||||
variables.getMessage('marketplace:product.no_images'),
|
||||
selectedItem?.photos.length,
|
||||
)}
|
||||
{selectedItem?.quotes && selectedItem?.language
|
||||
? moreInfoItem(
|
||||
<MdTranslate />,
|
||||
variables.getMessage('settings:sections.language.title'),
|
||||
languageNames.of(selectedItem?.language),
|
||||
)
|
||||
: null}
|
||||
{moreInfoItem(
|
||||
<MdStyle />,
|
||||
variables.getMessage('settings:sections.background.type.title'),
|
||||
variables.getMessage('marketplace:' + getName(selectedItem?.type)) || 'marketplace',
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ItemShowcase = () => {
|
||||
switch (selectedItem?.type) {
|
||||
case 'quotes':
|
||||
return (
|
||||
<>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>{variables.getMessage('settings:sections.quote.title')}</th>
|
||||
<th>{variables.getMessage('settings:sections.quote.author')}</th>
|
||||
</tr>
|
||||
{selectedItem?.quotes.slice(0, 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={() => count === selectedItem?.quotes.length}>
|
||||
{count !== selectedItem?.quotes.length
|
||||
? variables.getMessage('marketplace:product.show_all')
|
||||
: variables.getMessage('marketplace:product.show_less')}
|
||||
</span>
|
||||
<Markdown>{this.props.data.data.description}</Markdown>
|
||||
</div>
|
||||
{this.props.data.data.quotes && (
|
||||
<>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>{variables.getMessage('settings:sections.quote.title')}</th>
|
||||
<th>{variables.getMessage('settings:sections.quote.author')}</th>
|
||||
</>
|
||||
);
|
||||
case 'settings':
|
||||
return (
|
||||
<>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>{variables.getMessage('marketplace:product.setting')}</th>
|
||||
<th>{variables.getMessage('marketplace:product.value')}</th>
|
||||
</tr>
|
||||
{Object.entries(selectedItem?.settings)
|
||||
.slice(0, count)
|
||||
.map(([key, value]) => (
|
||||
<tr key={key}>
|
||||
<td>{key}</td>
|
||||
<td>{value}</td>
|
||||
</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('marketplace:product.show_all')
|
||||
: variables.getMessage('marketplace:product.show_less')}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{this.props.data.data.settings && (
|
||||
<>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>{variables.getMessage('marketplace:product.setting')}</th>
|
||||
<th>{variables.getMessage('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('marketplace:product.show_all')
|
||||
: variables.getMessage('marketplace:product.show_less')}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="marketplaceDescription">
|
||||
<span className="title">
|
||||
{variables.getMessage('marketplace:product.details')}
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<div className="showMoreItems">
|
||||
<span className="link" onClick={() => this.incrementCount('settings')}>
|
||||
{count !== selectedItem?.settings.length
|
||||
? variables.getMessage('marketplace:product.show_all')
|
||||
: variables.getMessage('marketplace:product.show_less')}
|
||||
</span>
|
||||
<div className="moreInfo">
|
||||
{this.props.data.data.updated_at &&
|
||||
moreInfoItem(
|
||||
<MdCalendarMonth />,
|
||||
variables.getMessage('marketplace:product.updated_at'),
|
||||
formattedDate,
|
||||
)}
|
||||
{this.props.data.data.quotes &&
|
||||
moreInfoItem(
|
||||
<MdFormatQuote />,
|
||||
variables.getMessage('marketplace:product.no_quotes'),
|
||||
this.props.data.data.quotes.length,
|
||||
)}
|
||||
{this.props.data.data.photos &&
|
||||
moreInfoItem(
|
||||
<MdImage />,
|
||||
variables.getMessage('marketplace:product.no_images'),
|
||||
this.props.data.data.photos.length,
|
||||
)}
|
||||
{this.props.data.data.quotes && this.props.data.data.language
|
||||
? moreInfoItem(
|
||||
<MdTranslate />,
|
||||
variables.getMessage('settings:sections.language.title'),
|
||||
languageNames.of(this.props.data.data.language),
|
||||
)
|
||||
: null}
|
||||
{moreInfoItem(
|
||||
<MdStyle />,
|
||||
variables.getMessage('settings:sections.background.type.title'),
|
||||
variables.getMessage(
|
||||
'marketplace:' + this.getName(this.props.data.data.type),
|
||||
) || 'marketplace',
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="itemInfo"
|
||||
style={{
|
||||
backgroundImage: `url("${this.props.data.data.icon_url}")`,
|
||||
</>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const sidePanel = () => {
|
||||
return (
|
||||
<div
|
||||
className="itemInfo"
|
||||
style={{
|
||||
backgroundImage: `url("${selectedItem?.icon_url || placeholderIcon}")`,
|
||||
}}
|
||||
>
|
||||
<div className="front">
|
||||
<img
|
||||
className="icon"
|
||||
alt="icon"
|
||||
draggable={false}
|
||||
src={selectedItem?.icon_url}
|
||||
onError={(e) => {
|
||||
e.target.onerror = null;
|
||||
e.target.src = placeholderIcon;
|
||||
}}
|
||||
>
|
||||
<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' ? (
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => this.manage('install')}
|
||||
icon={<MdLibraryAdd />}
|
||||
label={variables.getMessage('marketplace:product.buttons.addtomue')}
|
||||
/>
|
||||
) : (
|
||||
<p style={{ textAlign: 'center' }}>
|
||||
{variables.getMessage('marketplace:product.buttons.not_available_preview')}
|
||||
</p>
|
||||
)}
|
||||
{selectedItem?.sideload !== true && (
|
||||
<div className="iconButtons">
|
||||
<Button
|
||||
type="icon"
|
||||
onClick={() => setShareModal(true)}
|
||||
icon={<MdIosShare />}
|
||||
tooltipTitle={variables.getMessage('widgets.quote.share')}
|
||||
tooltipKey="share"
|
||||
/>
|
||||
<Button
|
||||
type="icon"
|
||||
onClick={() =>
|
||||
window.open(
|
||||
variables.constants.REPORT_ITEM +
|
||||
selectedItem?.display_name.split(' ').join('+'),
|
||||
'_blank',
|
||||
)
|
||||
}
|
||||
icon={<MdFlag />}
|
||||
tooltipTitle={variables.getMessage('marketplace:product.buttons.report')}
|
||||
tooltipKey="report"
|
||||
/>
|
||||
{localStorage.getItem('welcomePreview') !== 'true' ? (
|
||||
this.state.button
|
||||
) : (
|
||||
<p style={{ textAlign: 'center' }}>
|
||||
{variables.getMessage(
|
||||
'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(
|
||||
'marketplace:product.buttons.report',
|
||||
)}
|
||||
tooltipKey="report"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{this.props.data.data.in_collections?.length > 0 && (
|
||||
<div>
|
||||
<div className="inCollection">
|
||||
<span className="subtitle">
|
||||
{variables.getMessage('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>
|
||||
)}
|
||||
{selectedItem?.in_collections?.length > 0 && (
|
||||
<div>
|
||||
<div className="inCollection">
|
||||
<span className="subtitle">
|
||||
{variables.getMessage('marketplace:product.part_of')}
|
||||
</span>
|
||||
<span
|
||||
className="title"
|
||||
/*onClick={() =>
|
||||
this.props.toggleFunction(
|
||||
'collection',
|
||||
selectedItem?.in_collections[0].name,
|
||||
)
|
||||
}*/
|
||||
>
|
||||
{selectedItem?.in_collections[0].display_name}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const locale = localStorage.getItem('language');
|
||||
const shortLocale = locale.includes('_') ? locale.split('_')[0] : locale;
|
||||
let languageNames = new Intl.DisplayNames([shortLocale], { type: 'language' });
|
||||
|
||||
let dateObj, formattedDate;
|
||||
if (selectedItem?.updated_at) {
|
||||
dateObj = new Date(selectedItem?.updated_at);
|
||||
formattedDate = new Intl.DateTimeFormat(shortLocale, {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: '2-digit',
|
||||
}).format(dateObj);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
document.querySelector('#modal').scrollTop = 0;
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
closeTimeoutMS={300}
|
||||
isOpen={shareModal}
|
||||
className="Modal mainModal"
|
||||
overlayClassName="Overlay"
|
||||
ariaHideApp={false}
|
||||
onRequestClose={() => setShareModal(false)}
|
||||
>
|
||||
<ShareModal
|
||||
data={variables.constants.API_URL + '/marketplace/share/' + btoa(selectedItem?.name)}
|
||||
modalClose={() => setShareModal(false)}
|
||||
/>
|
||||
</Modal>
|
||||
<div className="itemPage">
|
||||
<div className="itemShowcase">
|
||||
<div className="subHeader">
|
||||
{moreInfoItem(
|
||||
<MdAccountCircle />,
|
||||
variables.getMessage('marketplace:product.created_by'),
|
||||
selectedItem?.author,
|
||||
)}
|
||||
{itemWarning()}
|
||||
</div>
|
||||
{selectedItem?.photos && (
|
||||
<div className="carousel">
|
||||
<div className="carousel_container">
|
||||
<Carousel data={selectedItem?.photos} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{selectedItem?.settings && selectedItem?.screenshot_url !== null && (
|
||||
<img
|
||||
alt="product"
|
||||
draggable={false}
|
||||
src={selectedItem?.screenshot_url}
|
||||
onError={(e) => {
|
||||
e.target.onerror = null;
|
||||
e.target.src = placeholderIcon;
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<div className="marketplaceDescription">
|
||||
<span className="title">{variables.getMessage('marketplace:product.description')}</span>
|
||||
<Markdown>{selectedItem?.description}</Markdown>
|
||||
</div>
|
||||
<ItemShowcase />
|
||||
<ItemDetails />
|
||||
</div>
|
||||
{sidePanel()}
|
||||
</div>
|
||||
{/*{moreByCurator.length > 1 && (
|
||||
<div className="moreFromCurator">
|
||||
<span className="title">
|
||||
{variables.getMessage('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>
|
||||
{moreByCurator.length > 1 && (
|
||||
<div className="moreFromCurator">
|
||||
<span className="title">
|
||||
{variables.getMessage('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 { ItemPage as default, ItemPage };
|
||||
|
||||
@@ -1,374 +0,0 @@
|
||||
import variables from 'config/variables';
|
||||
import { useTab } from 'components/Elements/MainModal/backend/TabContext';
|
||||
import placeholderIcon from 'assets/icons/marketplace-placeholder.png';
|
||||
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';
|
||||
import { Button } from 'components/Elements';
|
||||
import { useMarketData } from 'features/marketplace/api/MarketplaceDataContext';
|
||||
import { Carousel } from '../components/Elements/Carousel';
|
||||
|
||||
const NewItemPage = () => {
|
||||
const { subTab } = useTab();
|
||||
const controller = new AbortController();
|
||||
const [count, setCount] = useState(5);
|
||||
const [item, setItemData] = useState(null);
|
||||
const [shareModal, setShareModal] = useState(false);
|
||||
const { selectedItem } = useMarketData();
|
||||
|
||||
const getName = (name) => {
|
||||
const nameMappings = {
|
||||
photos: 'photo_packs',
|
||||
quotes: 'quote_packs',
|
||||
settings: 'preset_settings',
|
||||
};
|
||||
return nameMappings[name] || name;
|
||||
};
|
||||
|
||||
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('marketplace:product.sideload_warning'));
|
||||
}
|
||||
|
||||
if (this.props.data.data.image_api === true) {
|
||||
return template(variables.getMessage('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('marketplace:product.not_in_language'));
|
||||
}
|
||||
}*/
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const ItemDetails = () => {
|
||||
return (
|
||||
<div className="marketplaceDescription">
|
||||
<span className="title">
|
||||
{variables.getMessage('marketplace:product.details')}
|
||||
</span>
|
||||
<div className="moreInfo">
|
||||
{selectedItem?.updated_at &&
|
||||
moreInfoItem(
|
||||
<MdCalendarMonth />,
|
||||
variables.getMessage('marketplace:product.updated_at'),
|
||||
formattedDate,
|
||||
)}
|
||||
{selectedItem?.quotes &&
|
||||
moreInfoItem(
|
||||
<MdFormatQuote />,
|
||||
variables.getMessage('marketplace:product.no_quotes'),
|
||||
selectedItem.quotes.length,
|
||||
)}
|
||||
{selectedItem?.photos &&
|
||||
moreInfoItem(
|
||||
<MdImage />,
|
||||
variables.getMessage('marketplace:product.no_images'),
|
||||
selectedItem.photos.length,
|
||||
)}
|
||||
{selectedItem?.quotes && selectedItem?.language
|
||||
? moreInfoItem(
|
||||
<MdTranslate />,
|
||||
variables.getMessage('settings:sections.language.title'),
|
||||
languageNames.of(selectedItem.language),
|
||||
)
|
||||
: null}
|
||||
{moreInfoItem(
|
||||
<MdStyle />,
|
||||
variables.getMessage('settings:sections.background.type.title'),
|
||||
variables.getMessage(
|
||||
'marketplace:' + getName(selectedItem.type),
|
||||
) || 'marketplace',
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ItemShowcase = () => {
|
||||
switch (selectedItem.type) {
|
||||
case 'quotes':
|
||||
return (
|
||||
<>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>{variables.getMessage('settings:sections.quote.title')}</th>
|
||||
<th>{variables.getMessage('settings:sections.quote.author')}</th>
|
||||
</tr>
|
||||
{selectedItem.quotes.slice(0, 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={() => count === selectedItem.quotes.length}>
|
||||
{count !== selectedItem.quotes.length
|
||||
? variables.getMessage('marketplace:product.show_all')
|
||||
: variables.getMessage('marketplace:product.show_less')}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
case 'settings':
|
||||
return (
|
||||
<>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>{variables.getMessage('marketplace:product.setting')}</th>
|
||||
<th>{variables.getMessage('marketplace:product.value')}</th>
|
||||
</tr>
|
||||
{Object.entries(selectedItem.settings)
|
||||
.slice(0, 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')}>
|
||||
{count !== selectedItem.settings.length
|
||||
? variables.getMessage('marketplace:product.show_all')
|
||||
: variables.getMessage('marketplace:product.show_less')}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const locale = localStorage.getItem('language');
|
||||
const shortLocale = locale.includes('_') ? locale.split('_')[0] : locale;
|
||||
let languageNames = new Intl.DisplayNames([shortLocale], { type: 'language' });
|
||||
|
||||
let dateObj, formattedDate;
|
||||
if (selectedItem.updated_at) {
|
||||
dateObj = new Date(selectedItem.updated_at);
|
||||
formattedDate = new Intl.DateTimeFormat(shortLocale, {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: '2-digit',
|
||||
}).format(dateObj);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
closeTimeoutMS={300}
|
||||
isOpen={shareModal}
|
||||
className="Modal mainModal"
|
||||
overlayClassName="Overlay"
|
||||
ariaHideApp={false}
|
||||
onRequestClose={() => setShareModal(false)}
|
||||
>
|
||||
<ShareModal
|
||||
data={variables.constants.API_URL + '/marketplace/share/' + btoa(selectedItem.name)}
|
||||
modalClose={() => setShareModal(false)}
|
||||
/>
|
||||
</Modal>
|
||||
{/*<Header
|
||||
title={
|
||||
this.props.addons
|
||||
? variables.getMessage('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}
|
||||
/>*/}
|
||||
<div className="itemPage">
|
||||
<div className="itemShowcase">
|
||||
<div className="subHeader">
|
||||
{moreInfoItem(
|
||||
<MdAccountCircle />,
|
||||
variables.getMessage('marketplace:product.created_by'),
|
||||
selectedItem.author,
|
||||
)}
|
||||
{itemWarning()}
|
||||
</div>
|
||||
{selectedItem.photos && (
|
||||
<div className="carousel">
|
||||
<div className="carousel_container">
|
||||
<Carousel data={selectedItem.photos} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{/*{selectedItem.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('marketplace:product.description')}
|
||||
</span>
|
||||
<Markdown>{selectedItem.description}</Markdown>
|
||||
</div>
|
||||
<ItemShowcase />
|
||||
<ItemDetails />
|
||||
</div>
|
||||
<div
|
||||
className="itemInfo"
|
||||
style={{
|
||||
backgroundImage: `url("${selectedItem.icon_url || placeholderIcon}")`,
|
||||
}}
|
||||
>
|
||||
<div className="front">
|
||||
<img
|
||||
className="icon"
|
||||
alt="icon"
|
||||
draggable={false}
|
||||
src={selectedItem.icon_url}
|
||||
onError={(e) => {
|
||||
e.target.onerror = null;
|
||||
e.target.src = placeholderIcon;
|
||||
}}
|
||||
/>
|
||||
{localStorage.getItem('welcomePreview') !== 'true' ? (
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => this.manage('install')}
|
||||
icon={<MdLibraryAdd />}
|
||||
label={variables.getMessage('marketplace:product.buttons.addtomue')}
|
||||
/>
|
||||
) : (
|
||||
<p style={{ textAlign: 'center' }}>
|
||||
{variables.getMessage(
|
||||
'marketplace:product.buttons.not_available_preview',
|
||||
)}
|
||||
</p>
|
||||
)}
|
||||
{selectedItem.sideload !== true && (
|
||||
<div className="iconButtons">
|
||||
<Button
|
||||
type="icon"
|
||||
onClick={() => setShareModal(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(
|
||||
'marketplace:product.buttons.report',
|
||||
)}
|
||||
tooltipKey="report"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{selectedItem.in_collections?.length > 0 && (
|
||||
<div>
|
||||
<div className="inCollection">
|
||||
<span className="subtitle">
|
||||
{variables.getMessage('marketplace:product.part_of')}
|
||||
</span>
|
||||
<span
|
||||
className="title"
|
||||
/*onClick={() =>
|
||||
this.props.toggleFunction(
|
||||
'collection',
|
||||
selectedItem.in_collections[0].name,
|
||||
)
|
||||
}*/
|
||||
>
|
||||
{selectedItem.in_collections[0].display_name}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/*{moreByCurator.length > 1 && (
|
||||
<div className="moreFromCurator">
|
||||
<span className="title">
|
||||
{variables.getMessage('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 };
|
||||
478
src/features/marketplace/views/oldItemPage.jsx
Normal file
478
src/features/marketplace/views/oldItemPage.jsx
Normal file
@@ -0,0 +1,478 @@
|
||||
import variables from 'config/variables';
|
||||
import { PureComponent, Fragment } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import {
|
||||
MdIosShare,
|
||||
MdFlag,
|
||||
MdAccountCircle,
|
||||
MdCalendarMonth,
|
||||
MdFormatQuote,
|
||||
MdImage,
|
||||
MdTranslate,
|
||||
MdOutlineWarning,
|
||||
MdStyle,
|
||||
MdClose,
|
||||
MdLibraryAdd,
|
||||
} 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 '../components/Elements/Carousel';
|
||||
import { ShareModal } from 'components/Elements';
|
||||
import placeholderIcon from 'assets/icons/marketplace-placeholder.png';
|
||||
import { Items } from '../components/Items/OldItems';
|
||||
|
||||
import Markdown from 'markdown-to-jsx';
|
||||
|
||||
class ItemPage extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
console.log(this.props)
|
||||
this.state = {
|
||||
showUpdateButton:
|
||||
this.props.data.local.installed === true && this.props.data.local.version !== this.props.data.version,
|
||||
shareModal: false,
|
||||
count: 5,
|
||||
moreByCurator: [],
|
||||
};
|
||||
this.buttons = {
|
||||
uninstall: (
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => this.manage('uninstall')}
|
||||
icon={<MdClose />}
|
||||
label={variables.getMessage('marketplace:product.buttons.remove')}
|
||||
/>
|
||||
),
|
||||
install: (
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => this.manage('install')}
|
||||
icon={<MdLibraryAdd />}
|
||||
label={variables.getMessage('marketplace:product.buttons.addtomue')}
|
||||
/>
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
async getCurator(name) {
|
||||
try {
|
||||
const { data } = await (
|
||||
await fetch(`${variables.constants.API_URL}/marketplace/curator/${name}`)
|
||||
).json();
|
||||
this.setState({
|
||||
moreByCurator: data.items,
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.getCurator(this.props.data.author);
|
||||
document.querySelector('#modal').scrollTop = 0;
|
||||
this.setState({
|
||||
button: this.props.data.local.installed ? this.buttons.uninstall : this.buttons.install,
|
||||
})
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
manage(type) {
|
||||
if (type === 'install') {
|
||||
install(this.props.data.type, this.props.data.data);
|
||||
} else {
|
||||
uninstall(this.props.data.type,this.props.data.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');
|
||||
}
|
||||
|
||||
render() {
|
||||
const locale = localStorage.getItem('language');
|
||||
const shortLocale = locale.includes('_') ? locale.split('_')[0] : locale;
|
||||
let languageNames = new Intl.DisplayNames([shortLocale], { type: 'language' });
|
||||
|
||||
const convertedType = (() => {
|
||||
const map = {
|
||||
photos: 'photo_packs',
|
||||
quotes: 'quote_packs',
|
||||
settings: 'preset_settings',
|
||||
};
|
||||
return map[this.props.data.data.type];
|
||||
})();
|
||||
|
||||
const moreByCurator = this.state.moreByCurator
|
||||
.filter((item) => item.type === convertedType && item.name !== this.props.data.data.name)
|
||||
.sort(() => 0.5 - Math.random())
|
||||
.slice(0, 3);
|
||||
|
||||
if (!this.props.data.display_name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// prevent console error
|
||||
let iconsrc = this.props.data.icon;
|
||||
if (!this.props.data.icon) {
|
||||
iconsrc = null;
|
||||
}
|
||||
|
||||
let updateButton;
|
||||
if (this.state.showUpdateButton) {
|
||||
updateButton = (
|
||||
<Fragment key="update">
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => this.updateAddon()}
|
||||
label={variables.getMessage('addons:product.buttons.update_addon')}
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
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('marketplace:product.sideload_warning'));
|
||||
}
|
||||
|
||||
if (this.props.data.data.image_api === true) {
|
||||
return template(variables.getMessage('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('marketplace:product.not_in_language'));
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const moreInfoItem = (icon, header, text) => (
|
||||
<div className="infoItem">
|
||||
{icon}
|
||||
<div className="text">
|
||||
<span className="header">{header}</span>
|
||||
<span>{text}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
let dateObj, formattedDate;
|
||||
if (this.props.data.data.updated_at) {
|
||||
dateObj = new Date(this.props.data.data.updated_at);
|
||||
formattedDate = new Intl.DateTimeFormat(shortLocale, {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: '2-digit',
|
||||
}).format(dateObj);
|
||||
}
|
||||
|
||||
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('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}
|
||||
/>
|
||||
<div className="itemPage">
|
||||
<div className="itemShowcase">
|
||||
<div className="subHeader">
|
||||
{moreInfoItem(
|
||||
<MdAccountCircle />,
|
||||
variables.getMessage('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('marketplace:product.description')}
|
||||
</span>
|
||||
<Markdown>{this.props.data.data.description}</Markdown>
|
||||
</div>
|
||||
{this.props.data.data.quotes && (
|
||||
<>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>{variables.getMessage('settings:sections.quote.title')}</th>
|
||||
<th>{variables.getMessage('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('marketplace:product.show_all')
|
||||
: variables.getMessage('marketplace:product.show_less')}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{this.props.data.data.settings && (
|
||||
<>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>{variables.getMessage('marketplace:product.setting')}</th>
|
||||
<th>{variables.getMessage('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('marketplace:product.show_all')
|
||||
: variables.getMessage('marketplace:product.show_less')}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="marketplaceDescription">
|
||||
<span className="title">
|
||||
{variables.getMessage('marketplace:product.details')}
|
||||
</span>
|
||||
<div className="moreInfo">
|
||||
{this.props.data.data.updated_at &&
|
||||
moreInfoItem(
|
||||
<MdCalendarMonth />,
|
||||
variables.getMessage('marketplace:product.updated_at'),
|
||||
formattedDate,
|
||||
)}
|
||||
{this.props.data.data.quotes &&
|
||||
moreInfoItem(
|
||||
<MdFormatQuote />,
|
||||
variables.getMessage('marketplace:product.no_quotes'),
|
||||
this.props.data.data.quotes.length,
|
||||
)}
|
||||
{this.props.data.data.photos &&
|
||||
moreInfoItem(
|
||||
<MdImage />,
|
||||
variables.getMessage('marketplace:product.no_images'),
|
||||
this.props.data.data.photos.length,
|
||||
)}
|
||||
{this.props.data.data.quotes && this.props.data.data.language
|
||||
? moreInfoItem(
|
||||
<MdTranslate />,
|
||||
variables.getMessage('settings:sections.language.title'),
|
||||
languageNames.of(this.props.data.data.language),
|
||||
)
|
||||
: null}
|
||||
{moreInfoItem(
|
||||
<MdStyle />,
|
||||
variables.getMessage('settings:sections.background.type.title'),
|
||||
variables.getMessage(
|
||||
'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(
|
||||
'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(
|
||||
'marketplace:product.buttons.report',
|
||||
)}
|
||||
tooltipKey="report"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{this.props.data.data.in_collections?.length > 0 && (
|
||||
<div>
|
||||
<div className="inCollection">
|
||||
<span className="subtitle">
|
||||
{variables.getMessage('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('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 { ItemPage as default, ItemPage };
|
||||
@@ -1,21 +1,146 @@
|
||||
import { memo, useEffect } from 'react';
|
||||
import { memo, useEffect, useState } from 'react';
|
||||
import variables from 'config/variables';
|
||||
import { useMarketData } from 'features/marketplace/api/MarketplaceDataContext';
|
||||
import MarketplaceTab from '../../marketplace/views/Browse';
|
||||
import { NewItems as Items } from '../../marketplace/components/Items/NewItems';
|
||||
import { useTab } from 'components/Elements/MainModal/backend/TabContext';
|
||||
import { NewItemPage } from '../../marketplace/views/newItemPage';
|
||||
|
||||
|
||||
import { ItemPage } from '../../marketplace/views/ItemPage';
|
||||
import { NewItems as Items } from '../../marketplace/components/Items/Items';
|
||||
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import Collection from '../../marketplace/components/Collection/Collection';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import { PiGridFourFill } from 'react-icons/pi';
|
||||
import {
|
||||
MdFormatListBulleted,
|
||||
MdFilterListAlt,
|
||||
MdOutlineArrowForward,
|
||||
MdOutlineOpenInNew,
|
||||
} from 'react-icons/md';
|
||||
|
||||
function Marketplace(props) {
|
||||
const { done, items, getItems } = useMarketData();
|
||||
const { done, items, collections, getItems, getCollections, selectedItem } = useMarketData();
|
||||
const { subTab } = useTab();
|
||||
const [itemsView, setItemsView] = useState('grid');
|
||||
const [itemsFilter, setItemsFilter] = useState('all');
|
||||
const [filteredItems, setFilteredItems] = useState([]);
|
||||
const [sortItems, setSortItems] = useState('a-z');
|
||||
|
||||
const FilterOptions = () => {
|
||||
return (
|
||||
<div className="flex flex-row gap-2 my-3">
|
||||
<div
|
||||
onClick={() => {
|
||||
setItemsFilter('all');
|
||||
}}
|
||||
className={clsx(
|
||||
'cursor-pointer transition-all duration-200 rounded-full px-6 py-2 text-base ',
|
||||
{
|
||||
'bg-white text-black': itemsFilter === 'all',
|
||||
'bg-[#333] hover:bg-[#222222] text-white cursor:pointer': itemsFilter !== 'all',
|
||||
},
|
||||
)}
|
||||
>
|
||||
All
|
||||
</div>
|
||||
<div
|
||||
onClick={() => {
|
||||
setItemsFilter('photo_packs');
|
||||
}}
|
||||
className={clsx(
|
||||
'cursor-pointer transition-all duration-200 rounded-full px-6 py-2 text-base ',
|
||||
{
|
||||
'bg-white text-black': itemsFilter === 'photo_packs',
|
||||
'bg-[#333] hover:bg-[#222222] text-white cursor:pointer':
|
||||
itemsFilter !== 'photo_packs',
|
||||
},
|
||||
)}
|
||||
>
|
||||
{variables.getMessage('marketplace:photo_packs') || 'marketplace'}
|
||||
</div>
|
||||
<div
|
||||
onClick={() => {
|
||||
setItemsFilter('quote_packs');
|
||||
}}
|
||||
className={clsx(
|
||||
'cursor-pointer transition-all duration-200 rounded-full px-6 py-2 text-base ',
|
||||
{
|
||||
'bg-white text-black': itemsFilter === 'quote_packs',
|
||||
'bg-[#333] hover:bg-[#222222] text-white': itemsFilter !== 'quote_packs',
|
||||
},
|
||||
)}
|
||||
>
|
||||
{variables.getMessage('marketplace:quote_packs') || 'marketplace'}
|
||||
</div>
|
||||
<div
|
||||
onClick={() => {
|
||||
setItemsFilter('preset_settings');
|
||||
}}
|
||||
className={clsx(
|
||||
'cursor-pointer transition-all duration-200 rounded-full px-6 py-2 text-base ',
|
||||
{
|
||||
'bg-white text-black': itemsFilter === 'preset_settings',
|
||||
'bg-[#333] hover:bg-[#222222] text-white': itemsFilter !== 'preset_settings',
|
||||
},
|
||||
)}
|
||||
>
|
||||
{variables.getMessage('marketplace:preset_settings') || 'marketplace'}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ItemView = () => {
|
||||
return (
|
||||
<div className="flex flex-row gap-2">
|
||||
<button
|
||||
onClick={() => setItemsView('grid')}
|
||||
className={clsx('cursor:pointer h-[40px] w-[40px] grid place-items-center rounded-lg', {
|
||||
'bg-[#333]': itemsView === 'grid',
|
||||
})}
|
||||
>
|
||||
<PiGridFourFill />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setItemsView('list')}
|
||||
className={clsx('cursor:pointer h-[40px] w-[40px] grid place-items-center rounded-lg', {
|
||||
'bg-[#333]': itemsView === 'list',
|
||||
})}
|
||||
>
|
||||
<MdFormatListBulleted />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (navigator.onLine === false || localStorage.getItem('offlineMode') === 'true') {
|
||||
return;
|
||||
}
|
||||
getItems();
|
||||
}, []);
|
||||
getCollections();
|
||||
}, []); // Only runs once when the component mounts
|
||||
|
||||
useEffect(() => {
|
||||
switch (itemsFilter) {
|
||||
case 'all':
|
||||
setFilteredItems(items);
|
||||
break;
|
||||
case 'photo_packs':
|
||||
setFilteredItems(items.filter((item) => item.type === 'photo_packs'));
|
||||
break;
|
||||
case 'quote_packs':
|
||||
setFilteredItems(items.filter((item) => item.type === 'quote_packs'));
|
||||
break;
|
||||
case 'preset_settings':
|
||||
setFilteredItems(items.filter((item) => item.type === 'preset_settings'));
|
||||
break;
|
||||
default:
|
||||
setFilteredItems(items);
|
||||
}
|
||||
}, [itemsFilter, items]); // Only depends on itemsFilter and items
|
||||
|
||||
return (
|
||||
<AnimatePresence mode="wait">
|
||||
@@ -27,12 +152,19 @@ function Marketplace(props) {
|
||||
transition={{ duration: 0.8 }}
|
||||
>
|
||||
{subTab === '' ? (
|
||||
<motion.div key="items">
|
||||
<Items items={items} />
|
||||
</motion.div>
|
||||
<>
|
||||
<Collection collections={collections} />
|
||||
<motion.div key="items">
|
||||
<div className="w-full flex flex-row justify-between items-center">
|
||||
{FilterOptions()}
|
||||
<div className="flex flex-row gap-2">{ItemView()}</div>
|
||||
</div>
|
||||
<Items items={filteredItems} view={itemsView} />
|
||||
</motion.div>
|
||||
</>
|
||||
) : (
|
||||
<motion.div key="itempage">
|
||||
<NewItemPage />
|
||||
<ItemPage />
|
||||
</motion.div>
|
||||
)}
|
||||
</motion.div>
|
||||
|
||||
Reference in New Issue
Block a user