Display asset pack categories in asset store homepage (#4905)
BIN
newIDE/app/public/res/asset-categories/Backgrounds.jpeg
Normal file
After Width: | Height: | Size: 152 KiB |
BIN
newIDE/app/public/res/asset-categories/Characters.jpeg
Normal file
After Width: | Height: | Size: 128 KiB |
BIN
newIDE/app/public/res/asset-categories/Full_game_pack.jpeg
Normal file
After Width: | Height: | Size: 154 KiB |
BIN
newIDE/app/public/res/asset-categories/Interface.jpeg
Normal file
After Width: | Height: | Size: 133 KiB |
BIN
newIDE/app/public/res/asset-categories/Prefabs.jpeg
Normal file
After Width: | Height: | Size: 206 KiB |
BIN
newIDE/app/public/res/asset-categories/Props.jpeg
Normal file
After Width: | Height: | Size: 103 KiB |
BIN
newIDE/app/public/res/asset-categories/Visual_Effects.jpeg
Normal file
After Width: | Height: | Size: 146 KiB |
@@ -117,6 +117,7 @@ export const AssetStoreContext = React.createContext<AssetStoreState>({
|
||||
clearHistory: () => {},
|
||||
openSearchResultPage: () => {},
|
||||
openTagPage: tag => {},
|
||||
openAssetCategoryPage: category => {},
|
||||
openPackPage: assetPack => {},
|
||||
openDetailPage: assetShortHeader => {},
|
||||
openPrivateAssetPackInformationPage: privateAssetPackListingData => {},
|
||||
|
@@ -10,10 +10,12 @@ import { type PrivateAssetPackListingData } from '../Utils/GDevelopServices/Shop
|
||||
|
||||
export type AssetStorePageState = {|
|
||||
openedAssetPack: PublicAssetPack | PrivateAssetPack | null,
|
||||
openedAssetCategory: string | null,
|
||||
openedAssetShortHeader: ?AssetShortHeader,
|
||||
openedPrivateAssetPackListingData: ?PrivateAssetPackListingData,
|
||||
filtersState: FiltersState,
|
||||
scrollPosition?: ?number,
|
||||
displayAssets: boolean,
|
||||
|};
|
||||
|
||||
export type NavigationState = {|
|
||||
@@ -23,6 +25,7 @@ export type NavigationState = {|
|
||||
clearHistory: () => void,
|
||||
openSearchResultPage: () => void,
|
||||
openTagPage: string => void,
|
||||
openAssetCategoryPage: string => void,
|
||||
openPackPage: (PublicAssetPack | PrivateAssetPack) => void,
|
||||
openPrivateAssetPackInformationPage: PrivateAssetPackListingData => void,
|
||||
openDetailPage: AssetShortHeader => void,
|
||||
@@ -38,20 +41,31 @@ const noFilter: FiltersState = {
|
||||
|
||||
export const assetStoreHomePageState: AssetStorePageState = {
|
||||
openedAssetShortHeader: null,
|
||||
openedAssetCategory: null,
|
||||
openedAssetPack: null,
|
||||
openedPrivateAssetPackListingData: null,
|
||||
filtersState: noFilter,
|
||||
displayAssets: false,
|
||||
};
|
||||
|
||||
const searchPageState: AssetStorePageState = {
|
||||
openedAssetShortHeader: null,
|
||||
openedAssetCategory: null,
|
||||
openedAssetPack: null,
|
||||
openedPrivateAssetPackListingData: null,
|
||||
filtersState: noFilter,
|
||||
displayAssets: true,
|
||||
};
|
||||
|
||||
export const isHomePage = (pageState: AssetStorePageState) => {
|
||||
return pageState === assetStoreHomePageState;
|
||||
return (
|
||||
pageState === assetStoreHomePageState ||
|
||||
(!pageState.openedAssetShortHeader &&
|
||||
!pageState.openedPrivateAssetPackListingData &&
|
||||
!pageState.openedAssetPack &&
|
||||
pageState.filtersState === noFilter &&
|
||||
!pageState.displayAssets)
|
||||
);
|
||||
};
|
||||
|
||||
export const isSearchResultPage = (pageState: AssetStorePageState) => {
|
||||
@@ -122,8 +136,10 @@ export const useNavigation = (): NavigationState => {
|
||||
...previousHistory.previousPages,
|
||||
{
|
||||
openedAssetShortHeader: null,
|
||||
openedAssetCategory: null,
|
||||
openedAssetPack: null,
|
||||
openedPrivateAssetPackListingData: null,
|
||||
displayAssets: true,
|
||||
filtersState: {
|
||||
chosenCategory: {
|
||||
node: { name: tag, allChildrenTags: [], children: [] },
|
||||
@@ -138,33 +154,58 @@ export const useNavigation = (): NavigationState => {
|
||||
],
|
||||
}));
|
||||
},
|
||||
openPackPage: (assetPack: PublicAssetPack | PrivateAssetPack) => {
|
||||
openAssetCategoryPage: (category: string) => {
|
||||
setHistory(previousHistory => ({
|
||||
...previousHistory,
|
||||
previousPages: [
|
||||
...previousHistory.previousPages,
|
||||
{
|
||||
openedAssetShortHeader: null,
|
||||
openedAssetPack: assetPack,
|
||||
openedAssetCategory: category,
|
||||
openedAssetPack: null,
|
||||
openedPrivateAssetPackListingData: null,
|
||||
filtersState: {
|
||||
chosenCategory: {
|
||||
node: {
|
||||
name: assetPack.tag,
|
||||
allChildrenTags: [],
|
||||
children: [],
|
||||
},
|
||||
parentNodes: [],
|
||||
},
|
||||
chosenFilters: new Set(),
|
||||
addFilter: () => {},
|
||||
removeFilter: () => {},
|
||||
setChosenCategory: () => {},
|
||||
},
|
||||
filtersState: noFilter,
|
||||
displayAssets: false,
|
||||
},
|
||||
],
|
||||
}));
|
||||
},
|
||||
openPackPage: (assetPack: PublicAssetPack | PrivateAssetPack) => {
|
||||
setHistory(previousHistory => {
|
||||
const currentPage =
|
||||
previousHistory.previousPages[
|
||||
previousHistory.previousPages.length - 1
|
||||
];
|
||||
return {
|
||||
...previousHistory,
|
||||
previousPages: [
|
||||
...previousHistory.previousPages,
|
||||
{
|
||||
openedAssetShortHeader: null,
|
||||
openedAssetCategory:
|
||||
(currentPage && currentPage.openedAssetCategory) || null,
|
||||
openedAssetPack: assetPack,
|
||||
openedPrivateAssetPackListingData: null,
|
||||
displayAssets: true,
|
||||
filtersState: {
|
||||
chosenCategory: {
|
||||
node: {
|
||||
name: assetPack.tag,
|
||||
allChildrenTags: [],
|
||||
children: [],
|
||||
},
|
||||
parentNodes: [],
|
||||
},
|
||||
chosenFilters: new Set(),
|
||||
addFilter: () => {},
|
||||
removeFilter: () => {},
|
||||
setChosenCategory: () => {},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
},
|
||||
openPrivateAssetPackInformationPage: (
|
||||
assetPack: PrivateAssetPackListingData
|
||||
) => {
|
||||
@@ -174,9 +215,11 @@ export const useNavigation = (): NavigationState => {
|
||||
...previousHistory.previousPages,
|
||||
{
|
||||
openedAssetShortHeader: null,
|
||||
openedAssetCategory: null,
|
||||
openedAssetPack: null,
|
||||
openedPrivateAssetPackListingData: assetPack,
|
||||
filtersState: noFilter,
|
||||
displayAssets: false,
|
||||
},
|
||||
],
|
||||
}));
|
||||
@@ -188,9 +231,11 @@ export const useNavigation = (): NavigationState => {
|
||||
...previousHistory.previousPages,
|
||||
{
|
||||
openedAssetShortHeader: assetShortHeader,
|
||||
openedAssetCategory: null,
|
||||
openedAssetPack: null,
|
||||
openedPrivateAssetPackListingData: null,
|
||||
filtersState: noFilter,
|
||||
displayAssets: false,
|
||||
},
|
||||
],
|
||||
}));
|
||||
|
@@ -19,12 +19,54 @@ import { useResponsiveWindowWidth } from '../UI/Reponsive/ResponsiveWindowMeasur
|
||||
import AuthenticatedUserContext from '../Profile/AuthenticatedUserContext';
|
||||
import Paper from '../UI/Paper';
|
||||
import { mergeArraysPerGroup } from '../Utils/Array';
|
||||
import { textEllipsisStyle } from '../UI/TextEllipsis';
|
||||
|
||||
const columns = 3;
|
||||
const columnsForSmallWindow = 1;
|
||||
const columnsForMediumWindow = 2;
|
||||
const categoryColumns = 4;
|
||||
const categoryColumnsForSmallWindow = 2;
|
||||
const categoryColumnsForMediumWindow = 3;
|
||||
const cellSpacing = 2;
|
||||
|
||||
export const assetCategories = {
|
||||
'full-game-pack': {
|
||||
title: <Trans>Full Game Packs</Trans>,
|
||||
imageAlt: 'Full game asset packs category',
|
||||
imageSource: 'res/asset-categories/Full_game_pack.jpeg',
|
||||
},
|
||||
character: {
|
||||
title: <Trans>Characters</Trans>,
|
||||
imageAlt: 'Characters asset packs category',
|
||||
imageSource: 'res/asset-categories/Characters.jpeg',
|
||||
},
|
||||
props: {
|
||||
title: <Trans>Props</Trans>,
|
||||
imageAlt: 'Props asset packs category',
|
||||
imageSource: 'res/asset-categories/Props.jpeg',
|
||||
},
|
||||
background: {
|
||||
title: <Trans>Backgrounds</Trans>,
|
||||
imageAlt: 'Backgrounds asset packs category',
|
||||
imageSource: 'res/asset-categories/Backgrounds.jpeg',
|
||||
},
|
||||
'visual-effect': {
|
||||
title: <Trans>Visual Effects</Trans>,
|
||||
imageAlt: 'Visual effects asset packs category',
|
||||
imageSource: 'res/asset-categories/Visual_Effects.jpeg',
|
||||
},
|
||||
interface: {
|
||||
title: <Trans>UI/Interface</Trans>,
|
||||
imageAlt: 'User Interface asset packs category',
|
||||
imageSource: 'res/asset-categories/Interface.jpeg',
|
||||
},
|
||||
prefab: {
|
||||
title: <Trans>Prefabs (Ready-to-use Objects)</Trans>,
|
||||
imageAlt: 'Prefabs asset packs category',
|
||||
imageSource: 'res/asset-categories/Prefabs.jpeg',
|
||||
},
|
||||
};
|
||||
|
||||
const styles = {
|
||||
grid: { margin: '0 10px' },
|
||||
priceTagContainer: {
|
||||
@@ -49,10 +91,8 @@ const styles = {
|
||||
margin: 4,
|
||||
},
|
||||
packTitle: {
|
||||
textOverflow: 'ellipsis',
|
||||
overflow: 'hidden',
|
||||
...textEllipsisStyle,
|
||||
overflowWrap: 'break-word',
|
||||
whiteSpace: 'nowrap',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -75,14 +115,13 @@ const PublicAssetPackTile = ({
|
||||
}: {|
|
||||
assetPack: PublicAssetPack,
|
||||
onSelect: () => void,
|
||||
/** Props needed so that GidList component can adjust tile size */
|
||||
/** Props needed so that GridList component can adjust tile size */
|
||||
style?: any,
|
||||
|}) => {
|
||||
const classesForGridListItem = useStylesForGridListItem();
|
||||
return (
|
||||
<GridListTile
|
||||
classes={classesForGridListItem}
|
||||
key={assetPack.tag}
|
||||
tabIndex={0}
|
||||
onKeyPress={(event: SyntheticKeyboardEvent<HTMLLIElement>): void => {
|
||||
if (shouldValidate(event)) {
|
||||
@@ -133,7 +172,6 @@ export const PrivateAssetPackTile = ({
|
||||
return (
|
||||
<GridListTile
|
||||
classes={classesForGridListItem}
|
||||
key={assetPackListingData.id}
|
||||
tabIndex={0}
|
||||
onKeyPress={(event: SyntheticKeyboardEvent<HTMLLIElement>): void => {
|
||||
if (shouldValidate(event)) {
|
||||
@@ -150,14 +188,13 @@ export const PrivateAssetPackTile = ({
|
||||
src={assetPackListingData.thumbnailUrls[0]}
|
||||
alt={`Preview image of asset pack ${assetPackListingData.name}`}
|
||||
/>
|
||||
{!owned && (
|
||||
<div style={styles.priceTagContainer}>
|
||||
<PriceTag
|
||||
value={assetPackListingData.prices[0].value}
|
||||
withOverlay
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div style={styles.priceTagContainer}>
|
||||
<PriceTag
|
||||
value={assetPackListingData.prices[0].value}
|
||||
withOverlay
|
||||
owned={owned}
|
||||
/>
|
||||
</div>
|
||||
<Column>
|
||||
<Line justifyContent="space-between" noMargin>
|
||||
<Text style={styles.packTitle} size="body2">
|
||||
@@ -173,6 +210,51 @@ export const PrivateAssetPackTile = ({
|
||||
);
|
||||
};
|
||||
|
||||
export const CategoryTile = ({
|
||||
title,
|
||||
imageSource,
|
||||
imageAlt,
|
||||
onSelect,
|
||||
style,
|
||||
}: {|
|
||||
title: React.Node,
|
||||
imageSource: string,
|
||||
imageAlt: string,
|
||||
onSelect: () => void,
|
||||
/** Props needed so that GridList component can adjust tile size */
|
||||
style?: any,
|
||||
|}) => {
|
||||
const classesForGridListItem = useStylesForGridListItem();
|
||||
return (
|
||||
<GridListTile
|
||||
classes={classesForGridListItem}
|
||||
tabIndex={0}
|
||||
onKeyPress={(event: SyntheticKeyboardEvent<HTMLLIElement>): void => {
|
||||
if (shouldValidate(event)) {
|
||||
onSelect();
|
||||
}
|
||||
}}
|
||||
style={style}
|
||||
onClick={onSelect}
|
||||
>
|
||||
<Paper elevation={2} style={styles.paper} background="light">
|
||||
<CorsAwareImage
|
||||
style={styles.previewImage}
|
||||
src={imageSource}
|
||||
alt={imageAlt}
|
||||
/>
|
||||
<Column>
|
||||
<Line justifyContent="center" noMargin>
|
||||
<Text style={styles.packTitle} size="sub-title">
|
||||
{title}
|
||||
</Text>
|
||||
</Line>
|
||||
</Column>
|
||||
</Paper>
|
||||
</GridListTile>
|
||||
);
|
||||
};
|
||||
|
||||
export type AssetsHomeInterface = {|
|
||||
getScrollPosition: () => number,
|
||||
scrollToPosition: (y: number) => void,
|
||||
@@ -184,6 +266,8 @@ type Props = {|
|
||||
assetPackRandomOrdering: Array<number>,
|
||||
onPublicAssetPackSelection: PublicAssetPack => void,
|
||||
onPrivateAssetPackSelection: PrivateAssetPackListingData => void,
|
||||
onCategorySelection: string => void,
|
||||
openedAssetCategory: string | null,
|
||||
|};
|
||||
|
||||
export const AssetsHome = React.forwardRef<Props, AssetsHomeInterface>(
|
||||
@@ -194,6 +278,8 @@ export const AssetsHome = React.forwardRef<Props, AssetsHomeInterface>(
|
||||
assetPackRandomOrdering,
|
||||
onPublicAssetPackSelection,
|
||||
onPrivateAssetPackSelection,
|
||||
onCategorySelection,
|
||||
openedAssetCategory,
|
||||
}: Props,
|
||||
ref
|
||||
) => {
|
||||
@@ -219,18 +305,27 @@ export const AssetsHome = React.forwardRef<Props, AssetsHomeInterface>(
|
||||
},
|
||||
}));
|
||||
|
||||
const starterPacksTiles: Array<React.Node> = starterPacks.map(
|
||||
(assetPack, index) => (
|
||||
const starterPacksTiles: Array<React.Node> = starterPacks
|
||||
.filter(
|
||||
assetPack =>
|
||||
!openedAssetCategory ||
|
||||
assetPack.categories.includes(openedAssetCategory)
|
||||
)
|
||||
.map((assetPack, index) => (
|
||||
<PublicAssetPackTile
|
||||
assetPack={assetPack}
|
||||
onSelect={() => onPublicAssetPackSelection(assetPack)}
|
||||
key={`${assetPack.tag}-${index}`}
|
||||
/>
|
||||
)
|
||||
);
|
||||
));
|
||||
|
||||
const privateAssetPacksTiles: Array<React.Node> = privateAssetPacksListingData.map(
|
||||
assetPackListingData => (
|
||||
const privateAssetPacksTiles: Array<React.Node> = privateAssetPacksListingData
|
||||
.filter(
|
||||
assetPackListingData =>
|
||||
!openedAssetCategory ||
|
||||
assetPackListingData.categories.includes(openedAssetCategory)
|
||||
)
|
||||
.map(assetPackListingData => (
|
||||
<PrivateAssetPackTile
|
||||
assetPackListingData={assetPackListingData}
|
||||
onSelect={() => {
|
||||
@@ -244,8 +339,7 @@ export const AssetsHome = React.forwardRef<Props, AssetsHomeInterface>(
|
||||
}
|
||||
key={assetPackListingData.id}
|
||||
/>
|
||||
)
|
||||
);
|
||||
));
|
||||
|
||||
const allTiles = mergeArraysPerGroup(
|
||||
privateAssetPacksTiles,
|
||||
@@ -257,8 +351,63 @@ export const AssetsHome = React.forwardRef<Props, AssetsHomeInterface>(
|
||||
1
|
||||
);
|
||||
|
||||
const categoryTiles = Object.entries(assetCategories).map(
|
||||
// $FlowExpectedError - Object.entries does not infer well the type of the value.
|
||||
([id, { title, imageSource, imageAlt }]) => (
|
||||
<CategoryTile
|
||||
key={id}
|
||||
imageSource={imageSource}
|
||||
imageAlt={imageAlt}
|
||||
title={title}
|
||||
onSelect={() => {
|
||||
onCategorySelection(id);
|
||||
}}
|
||||
/>
|
||||
)
|
||||
);
|
||||
|
||||
const openedAssetCategoryTitle = openedAssetCategory
|
||||
? assetCategories[openedAssetCategory].title
|
||||
: null;
|
||||
|
||||
return (
|
||||
<ScrollView ref={scrollView}>
|
||||
{openedAssetCategory ? null : (
|
||||
<>
|
||||
<Column>
|
||||
<Line>
|
||||
<Text size="block-title">
|
||||
<Trans>Explore by category</Trans>
|
||||
</Text>
|
||||
</Line>
|
||||
</Column>
|
||||
<GridList
|
||||
cols={
|
||||
windowWidth === 'small'
|
||||
? categoryColumnsForSmallWindow
|
||||
: windowWidth === 'medium'
|
||||
? categoryColumnsForMediumWindow
|
||||
: categoryColumns
|
||||
}
|
||||
style={styles.grid}
|
||||
cellHeight="auto"
|
||||
spacing={cellSpacing}
|
||||
>
|
||||
{categoryTiles}
|
||||
</GridList>
|
||||
</>
|
||||
)}
|
||||
<Column>
|
||||
<Line>
|
||||
<Text size="block-title">
|
||||
{openedAssetCategoryTitle ? (
|
||||
openedAssetCategoryTitle
|
||||
) : (
|
||||
<Trans>All asset packs</Trans>
|
||||
)}
|
||||
</Text>
|
||||
</Line>
|
||||
</Column>
|
||||
<GridList
|
||||
cols={
|
||||
windowWidth === 'small'
|
||||
|
@@ -99,6 +99,7 @@ export const AssetStore = React.forwardRef<Props, AssetStoreInterface>(
|
||||
const {
|
||||
openedAssetPack,
|
||||
openedAssetShortHeader,
|
||||
openedAssetCategory,
|
||||
openedPrivateAssetPackListingData,
|
||||
filtersState,
|
||||
} = navigationState.getCurrentPage();
|
||||
@@ -164,7 +165,11 @@ export const AssetStore = React.forwardRef<Props, AssetStoreInterface>(
|
||||
return;
|
||||
}
|
||||
const scrollPosition = navigationState.getCurrentPage().scrollPosition;
|
||||
scrollPosition && scrollView.scrollToPosition(scrollPosition);
|
||||
if (scrollPosition) scrollView.scrollToPosition(scrollPosition);
|
||||
// If no saved scroll position, force scroll to 0 in case the displayed component
|
||||
// is the same as the previous page so the scroll is naturally kept between pages
|
||||
// although the user navigated and the scroll should be reset.
|
||||
else scrollView.scrollToPosition(0);
|
||||
hasAppliedSavedScrollPosition.current = true;
|
||||
},
|
||||
[getScrollView, navigationState]
|
||||
@@ -278,6 +283,14 @@ export const AssetStore = React.forwardRef<Props, AssetStoreInterface>(
|
||||
[receivedAssetPacks, saveScrollPosition, navigationState, setSearchText]
|
||||
);
|
||||
|
||||
const selectAssetCategory = React.useCallback(
|
||||
(category: string) => {
|
||||
saveScrollPosition();
|
||||
navigationState.openAssetCategoryPage(category);
|
||||
},
|
||||
[navigationState, saveScrollPosition]
|
||||
);
|
||||
|
||||
// If the user has received the pack they are currently viewing,
|
||||
// we update the window to show it if they are not already on the pack page.
|
||||
React.useEffect(
|
||||
@@ -424,18 +437,15 @@ export const AssetStore = React.forwardRef<Props, AssetStoreInterface>(
|
||||
/>
|
||||
</Column>
|
||||
</LineStackLayout>
|
||||
{!isOnHomePage && <Spacer />}
|
||||
<Spacer />
|
||||
<Column noMargin>
|
||||
<Line
|
||||
justifyContent="space-between"
|
||||
noMargin
|
||||
alignItems="center"
|
||||
>
|
||||
{isOnHomePage ? (
|
||||
<Text size="block-title">
|
||||
<Trans>Discover</Trans>
|
||||
</Text>
|
||||
) : (
|
||||
{(!isOnHomePage ||
|
||||
(isOnHomePage && !!openedAssetCategory)) && (
|
||||
<>
|
||||
<Column expand alignItems="flex-start" noMargin>
|
||||
<TextButton
|
||||
@@ -559,6 +569,8 @@ export const AssetStore = React.forwardRef<Props, AssetStoreInterface>(
|
||||
assetPackRandomOrdering={assetPackRandomOrdering}
|
||||
onPublicAssetPackSelection={selectPublicAssetPack}
|
||||
onPrivateAssetPackSelection={selectPrivateAssetPack}
|
||||
onCategorySelection={selectAssetCategory}
|
||||
openedAssetCategory={openedAssetCategory}
|
||||
/>
|
||||
) : (
|
||||
<PlaceholderLoader />
|
||||
|
38
newIDE/app/src/UI/Breadcrumbs.js
Normal file
@@ -0,0 +1,38 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import MuiBreadcrumbs from '@material-ui/core/Breadcrumbs';
|
||||
|
||||
import Text from './Text';
|
||||
import Link from './Link';
|
||||
|
||||
type BreadcrumbStep =
|
||||
| {| label: React.Node |}
|
||||
| {| label: React.Node, onClick: () => void, href: string |};
|
||||
|
||||
type Props = {|
|
||||
steps: Array<BreadcrumbStep>,
|
||||
|};
|
||||
|
||||
const Breadcrumbs = ({ steps }: Props) => {
|
||||
return (
|
||||
<MuiBreadcrumbs separator=">" aria-label="breadcrumb">
|
||||
{steps.map((step, index) =>
|
||||
step.onClick ? (
|
||||
<Link
|
||||
onClick={step.onClick}
|
||||
href={step.href}
|
||||
key={`breadcrumb${index}`}
|
||||
>
|
||||
{step.label}
|
||||
</Link>
|
||||
) : (
|
||||
<Text key={`breadcrumb${index}`} noMargin>
|
||||
{step.label}
|
||||
</Text>
|
||||
)
|
||||
)}
|
||||
</MuiBreadcrumbs>
|
||||
);
|
||||
};
|
||||
|
||||
export default Breadcrumbs;
|
@@ -6,6 +6,7 @@ import { type I18n as I18nType } from '@lingui/core';
|
||||
|
||||
import Text from './Text';
|
||||
import { makeStyles } from '@material-ui/core';
|
||||
import { Trans } from '@lingui/macro';
|
||||
|
||||
type Props = {|
|
||||
value: number,
|
||||
@@ -14,6 +15,7 @@ type Props = {|
|
||||
* we don't control the background (e.g. an image).
|
||||
*/
|
||||
withOverlay?: boolean,
|
||||
owned?: boolean,
|
||||
|};
|
||||
|
||||
const useStyles = makeStyles(theme => {
|
||||
@@ -54,14 +56,14 @@ export const formatPrice = (i18n: I18nType, value: number) =>
|
||||
})
|
||||
.replace(/\D00$/, '')}`;
|
||||
|
||||
function PriceTag({ value, withOverlay }: Props) {
|
||||
function PriceTag({ value, withOverlay, owned }: Props) {
|
||||
const classes = useStyles({ withOverlay });
|
||||
return (
|
||||
<I18n>
|
||||
{({ i18n }) => (
|
||||
<div className={classes.container}>
|
||||
<Text noMargin size="sub-title" color="inherit">
|
||||
{formatPrice(i18n, value)}
|
||||
{owned ? <Trans>✅ Owned</Trans> : formatPrice(i18n, value)}
|
||||
</Text>
|
||||
</div>
|
||||
)}
|
||||
|
@@ -74,6 +74,7 @@ export type PublicAssetPack = {|
|
||||
assetsCount: number,
|
||||
externalWebLink?: ?string,
|
||||
userFriendlyPrice?: ?string,
|
||||
categories: Array<string>,
|
||||
|};
|
||||
|
||||
export type PublicAssetPacks = {|
|
||||
|
@@ -20,6 +20,7 @@ export type PrivateAssetPackListingData = {|
|
||||
listing: 'ASSET_PACK',
|
||||
name: string,
|
||||
description: string,
|
||||
categories: Array<string>,
|
||||
updatedAt: string,
|
||||
createdAt: string,
|
||||
thumbnailUrls: string[],
|
||||
|
@@ -337,7 +337,20 @@ const defaultAuthenticatedUserWithNoSubscription: AuthenticatedUser = {
|
||||
subscription: noSubscription,
|
||||
usages: usagesForIndieUser,
|
||||
limits: limitsForNoSubscriptionUser,
|
||||
receivedAssetPacks: [],
|
||||
receivedAssetPacks: [
|
||||
{
|
||||
id: '07a9f974-aeca-4a3f-b501-4808400eda4f',
|
||||
createdAt: '2022-12-13T19:45:47.318Z',
|
||||
updatedAt: '2022-12-13T19:45:47.318Z',
|
||||
name: 'Gamepasses',
|
||||
longDescription: '',
|
||||
previewImageUrls: [
|
||||
'https://resources.gdevelop-app.com/staging/private-assets/Gamepasses/thumbnail.png',
|
||||
],
|
||||
tag: 'gamepasses',
|
||||
content: {},
|
||||
},
|
||||
],
|
||||
receivedAssetShortHeaders: [],
|
||||
onLogout: async () => {},
|
||||
onLogin: () => {},
|
||||
@@ -1332,6 +1345,7 @@ export const fakeAssetPacks: PublicAssetPacks = {
|
||||
{
|
||||
name: 'GDevelop Platformer',
|
||||
tag: 'platformer',
|
||||
categories: ['full-game-pack'],
|
||||
thumbnailUrl:
|
||||
'https://resources.gdevelop-app.com/assets/Packs/platformer.png',
|
||||
assetsCount: 16,
|
||||
@@ -1339,6 +1353,7 @@ export const fakeAssetPacks: PublicAssetPacks = {
|
||||
{
|
||||
name: 'Space Shooter',
|
||||
tag: 'space shooter',
|
||||
categories: [],
|
||||
thumbnailUrl:
|
||||
'https://resources.gdevelop-app.com/assets/Packs/space shooter.png',
|
||||
assetsCount: 140,
|
||||
@@ -1346,6 +1361,7 @@ export const fakeAssetPacks: PublicAssetPacks = {
|
||||
{
|
||||
name: 'Tanks',
|
||||
tag: 'tank pack',
|
||||
categories: [],
|
||||
thumbnailUrl:
|
||||
'https://resources.gdevelop-app.com/assets/Packs/tank pack.png',
|
||||
assetsCount: 32,
|
||||
@@ -1353,6 +1369,7 @@ export const fakeAssetPacks: PublicAssetPacks = {
|
||||
{
|
||||
name: 'Pixel Adventure',
|
||||
tag: 'pixel adventure pack',
|
||||
categories: ['full-game-pack'],
|
||||
thumbnailUrl:
|
||||
'https://resources.gdevelop-app.com/assets/Packs/pixel adventure pack.png',
|
||||
assetsCount: 80,
|
||||
@@ -1360,6 +1377,7 @@ export const fakeAssetPacks: PublicAssetPacks = {
|
||||
{
|
||||
name: 'Fake Paid External',
|
||||
tag: 'pirate bomb pack',
|
||||
categories: ['full-game-pack'],
|
||||
thumbnailUrl:
|
||||
'https://resources.gdevelop-app.com/assets/Packs/pirate bomb pack.png',
|
||||
assetsCount: 48,
|
||||
@@ -1369,6 +1387,7 @@ export const fakeAssetPacks: PublicAssetPacks = {
|
||||
{
|
||||
name: 'Particles',
|
||||
tag: 'pixel effects pack',
|
||||
categories: ['visual-effect'],
|
||||
thumbnailUrl:
|
||||
'https://resources.gdevelop-app.com/assets/Packs/pixel effects pack.png',
|
||||
assetsCount: 20,
|
||||
@@ -1376,12 +1395,14 @@ export const fakeAssetPacks: PublicAssetPacks = {
|
||||
{
|
||||
name: 'Emotes',
|
||||
tag: 'emote',
|
||||
categories: [],
|
||||
thumbnailUrl: 'https://resources.gdevelop-app.com/assets/Packs/emote.png',
|
||||
assetsCount: 176,
|
||||
},
|
||||
{
|
||||
name: 'Dinosaurus Characters',
|
||||
tag: '24x24 dino characters',
|
||||
categories: ['character'],
|
||||
thumbnailUrl:
|
||||
'https://resources.gdevelop-app.com/assets/Packs/24x24 dino characters.png',
|
||||
assetsCount: 5,
|
||||
@@ -1389,6 +1410,7 @@ export const fakeAssetPacks: PublicAssetPacks = {
|
||||
{
|
||||
name: 'Fake Paid Spinning Items',
|
||||
tag: '16x16 pixel art spinning items',
|
||||
categories: ['props'],
|
||||
thumbnailUrl:
|
||||
'https://resources.gdevelop-app.com/assets/Packs/16x16 pixel art spinning items.png',
|
||||
assetsCount: 30,
|
||||
@@ -1397,6 +1419,7 @@ export const fakeAssetPacks: PublicAssetPacks = {
|
||||
{
|
||||
name: 'RPG Items #2',
|
||||
tag: '16x16 pixel art rpg items',
|
||||
categories: ['props'],
|
||||
thumbnailUrl:
|
||||
'https://resources.gdevelop-app.com/assets/Packs/16x16 pixel art rpg items.png',
|
||||
assetsCount: 64,
|
||||
@@ -1404,6 +1427,7 @@ export const fakeAssetPacks: PublicAssetPacks = {
|
||||
{
|
||||
name: 'RPG Items',
|
||||
tag: '16x16 rpg item pack',
|
||||
categories: ['props'],
|
||||
thumbnailUrl:
|
||||
'https://resources.gdevelop-app.com/assets/Packs/16x16 rpg item pack.png',
|
||||
assetsCount: 144,
|
||||
@@ -1411,6 +1435,7 @@ export const fakeAssetPacks: PublicAssetPacks = {
|
||||
{
|
||||
name: 'Fantasy Icons',
|
||||
tag: '32x32 fantasy icons pack v2',
|
||||
categories: [],
|
||||
thumbnailUrl:
|
||||
'https://resources.gdevelop-app.com/assets/Packs/32x32 fantasy icons pack v2.png',
|
||||
assetsCount: 285,
|
||||
@@ -1418,6 +1443,7 @@ export const fakeAssetPacks: PublicAssetPacks = {
|
||||
{
|
||||
name: 'On-Screen Controls',
|
||||
tag: 'on-screen controls',
|
||||
categories: ['interface'],
|
||||
thumbnailUrl:
|
||||
'https://resources.gdevelop-app.com/assets/Packs/on-screen controls.png',
|
||||
assetsCount: 287,
|
||||
|
@@ -31,6 +31,7 @@ const privateAssetPackListingData = {
|
||||
listing: 'ASSET_PACK',
|
||||
description: '5 assets',
|
||||
name: 'French Food',
|
||||
categories: ['props'],
|
||||
prices: [{ value: 1500, name: 'default', stripePriceId: 'stripePriceId' }],
|
||||
};
|
||||
|
||||
|
@@ -34,6 +34,7 @@ const privateAssetPackListingData = {
|
||||
listing: 'ASSET_PACK',
|
||||
description: '5 assets',
|
||||
name: 'French Food',
|
||||
categories: ['props'],
|
||||
prices: [{ value: 1500, name: 'default', stripePriceId: 'stripePriceId' }],
|
||||
};
|
||||
|
||||
|
@@ -44,6 +44,7 @@ const getAssetPacksListingData = userId => [
|
||||
listing: 'ASSET_PACK',
|
||||
name: 'French food',
|
||||
description: 'The best asset pack about french food',
|
||||
categories: ['props'],
|
||||
updatedAt: '2021-11-18T10:19:50.417Z',
|
||||
createdAt: '2021-11-18T10:19:50.417Z',
|
||||
thumbnailUrls: [
|
||||
|
@@ -0,0 +1,43 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
|
||||
import muiDecorator from '../../ThemeDecorator';
|
||||
import paperDecorator from '../../PaperDecorator';
|
||||
|
||||
import { Column, Line } from '../../../UI/Grid';
|
||||
import Breadcrumbs from '../../../UI/Breadcrumbs';
|
||||
|
||||
export default {
|
||||
title: 'UI Building Blocks/Breadcrumbs',
|
||||
component: Breadcrumbs,
|
||||
decorators: [paperDecorator, muiDecorator],
|
||||
};
|
||||
|
||||
export const Default = () => (
|
||||
<Column>
|
||||
<Line>
|
||||
<Breadcrumbs
|
||||
steps={[
|
||||
{
|
||||
label: <span>Home</span>,
|
||||
onClick: action('Click on home'),
|
||||
href: '/',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Line>
|
||||
<Line>
|
||||
<Breadcrumbs
|
||||
steps={[
|
||||
{ label: <span>Home</span> },
|
||||
{
|
||||
label: <span>Categories</span>,
|
||||
onClick: action('Click on categories'),
|
||||
href: '/categories',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Line>
|
||||
</Column>
|
||||
);
|
@@ -21,6 +21,7 @@ export const Default = () => (
|
||||
<PriceTag value={850} />
|
||||
<PriceTag value={1200} />
|
||||
<PriceTag value={1225} />
|
||||
<PriceTag value={1225} owned />
|
||||
</LineStackLayout>
|
||||
<LineStackLayout>
|
||||
<div
|
||||
@@ -38,5 +39,21 @@ export const Default = () => (
|
||||
<PriceTag value={1200} withOverlay />
|
||||
</div>
|
||||
</LineStackLayout>
|
||||
<LineStackLayout>
|
||||
<div
|
||||
style={{
|
||||
backgroundSize: 'contain',
|
||||
backgroundImage:
|
||||
"url('https://resources.gdevelop-app.com/assets/Packs/wesxdz skullcup.png?gdUsage=img')",
|
||||
aspectRatio: '16 / 9',
|
||||
height: 200,
|
||||
padding: 10,
|
||||
display: 'flex',
|
||||
alignItems: 'flex-start',
|
||||
}}
|
||||
>
|
||||
<PriceTag value={1200} withOverlay owned />
|
||||
</div>
|
||||
</LineStackLayout>
|
||||
</ColumnStackLayout>
|
||||
);
|
||||
|