Skip to content

Commit 9549dfb

Browse files
author
Para
authored
feat: enhance multimedia support across DexAppBuilder (#628)
* feat: enhance multimedia support across DexAppBuilder - updated various components to handle audio and video files, including AssetMedia, ImageInput, and MediaDialog. - improved UI responsiveness and added controls for video playback. - Adjusted language files to ensure consistency in messaging regarding NFT creation and media handling. * feat: add validation and error handling for multimedia file selection in ImagePicker, MediaPicker, and ImageInput - Implemented file validation for image, audio, and video types across components. - Enhanced MediaDialog to accept custom file types for improved user experience.
1 parent 3324504 commit 9549dfb

File tree

35 files changed

+1751
-315
lines changed

35 files changed

+1751
-315
lines changed

.changeset/loose-schools-march.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@dexkit/dexappbuilder-viewer": minor
3+
"dexappbuilder": minor
4+
"@dexkit/ui": minor
5+
"@dexkit/web3forms": minor
6+
---
7+
8+
Add multimedia support. Media uploading and media NFT creation and handling

apps/dexappbuilder/compiled-lang/en-US.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -701,7 +701,7 @@
701701
"info.color": "Info Color",
702702
"info.create.collection.dialog": "Please don't close modal or reload app till finish data submission!",
703703
"info.create.collection.page": "Create a collection of NFT's easily using our contract wizard. You now can use our generate AI feature to generate an image for your collection. Please note that you need to hold {holdAmount} KIT in one of our supported networks: BSC, Polygon or Ethereum (Max. 50 images per month). Fill description first and generate image. If you need support or a bigger plan for AI generation please reach us on our <a>dedicated Discord channel</a> or email [email protected]!",
704-
"info.create.nfts": "To create NFTs, name and image are mandatory. When creating the nfts please wait till end, could take a while to finish!",
704+
"info.create.nfts": "To create NFTs, name and image are mandatory. When creating the NFTs please wait till end, could take a while to finish!",
705705
"info.create.nfts.dialog": "Please don't close modal or reload app till finish data submission!",
706706
"info.edit.nft.dialog": "Please don't close modal or reload app till finish data submission!",
707707
"info.quick.wizard.store.account": "Fill first your store account with the address from where you want to sell your NFTs.",

apps/dexappbuilder/lang/en-US.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3644,7 +3644,7 @@
36443644
"defaultMessage": "Please don't close modal or reload app till finish data submission!"
36453645
},
36463646
"info.create.nfts": {
3647-
"defaultMessage": "To create NFTs, name and image are mandatory. When creating the nfts please wait till end, could take a while to finish!"
3647+
"defaultMessage": "To create NFTs, name and image are mandatory. When creating the NFTs please wait till end, could take a while to finish!"
36483648
},
36493649
"info.create.nfts.dialog": {
36503650
"defaultMessage": "Please don't close modal or reload app till finish data submission!"

apps/dexappbuilder/lang/main.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1739,7 +1739,7 @@
17391739
"defaultMessage": "Create a collection of NFT's easily using our contract wizard. You now can use our generate AI feature to generate an image for your collection. Please note that you need to hold {holdAmount} KIT in one of our supported networks: BSC, Polygon or Ethereum (Max. 50 images per month). Fill description first and generate image. If you need support or a bigger plan for AI generation please reach us on our <a>dedicated Discord channel</a> or email [email protected]!"
17401740
},
17411741
"info.create.nfts": {
1742-
"defaultMessage": "To create NFTs, name and image are mandatory. When creating the nfts please wait till end, could take a while to finish!"
1742+
"defaultMessage": "To create NFTs, name and image are mandatory. When creating the NFTs please wait till end, could take a while to finish!"
17431743
},
17441744
"info.create.nfts.dialog": {
17451745
"defaultMessage": "Please don't close modal or reload app till finish data submission!"

apps/dexappbuilder/pages/_site/[site]/collection/[network]/[address]/index.tsx

Lines changed: 120 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ import Box from '@mui/material/Box';
22
import Typography from '@mui/material/Typography';
33
import type { GetStaticProps, GetStaticPropsContext, NextPage } from 'next';
44

5-
import { getIntegrationData } from '@/modules/wizard/services/integrations';
6-
import { ChainId, MY_APPS_ENDPOINT } from '@dexkit/core';
75
import { NETWORK_FROM_SLUG } from '@dexkit/core/constants/networks';
86
import { Asset } from '@dexkit/core/types';
97
import { isAddressEqual, omitNull } from '@dexkit/core/utils';
@@ -33,7 +31,6 @@ import {
3331
} from '@mui/material';
3432
import { QueryClient, dehydrate } from '@tanstack/react-query';
3533
import { ThirdwebSDK, useContractType } from '@thirdweb-dev/react';
36-
import axios from 'axios';
3734
import { NextSeo } from 'next-seo';
3835
import { useRouter } from 'next/router';
3936
import { useMemo, useState } from 'react';
@@ -78,138 +75,138 @@ const CollectionPage: NextPage<{
7875
disableSecondarySells: boolean;
7976
isLock: boolean;
8077
}) => {
81-
const router = useRouter();
82-
const { formatMessage } = useIntl();
83-
const { address, network } = router.query;
84-
const chainId = NETWORK_FROM_SLUG(network as string)?.chainId;
85-
const [search, setSearch] = useState<string>();
78+
const router = useRouter();
79+
const { formatMessage } = useIntl();
80+
const { address, network } = router.query;
81+
const chainId = NETWORK_FROM_SLUG(network as string)?.chainId;
82+
const [search, setSearch] = useState<string>();
8683

87-
const { data: contractType } = useContractType(address as string);
84+
const { data: contractType } = useContractType(address as string);
8885

89-
const isDrop = useMemo(() => {
90-
return contractType?.endsWith('drop');
91-
}, [contractType]);
86+
const isDrop = useMemo(() => {
87+
return contractType?.endsWith('drop');
88+
}, [contractType]);
9289

93-
const { data: collection } = useCollection(address as string, chainId);
90+
const { data: collection } = useCollection(address as string, chainId);
9491

95-
const handleChangeSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
96-
setSearch(e.target.value);
97-
};
92+
const handleChangeSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
93+
setSearch(e.target.value);
94+
};
9895

99-
const [buyNowChecked, setBuyNowChecked] = useState(false);
96+
const [buyNowChecked, setBuyNowChecked] = useState(false);
10097

101-
const handleChangeBuyNow = (event: React.ChangeEvent<HTMLInputElement>) => {
102-
setBuyNowChecked(event.target.checked);
103-
};
98+
const handleChangeBuyNow = (event: React.ChangeEvent<HTMLInputElement>) => {
99+
setBuyNowChecked(event.target.checked);
100+
};
104101

105-
const theme = useTheme();
106-
const isDesktop = useMediaQuery(theme.breakpoints.up('sm'));
107-
108-
const [isFiltersOpen, setIsFiltersOpen] = useState(false);
109-
110-
const handleCloseDrawer = () => setIsFiltersOpen(false);
111-
112-
const renderSidebar = (onClose?: () => void) => {
113-
return (
114-
<SidebarFilters
115-
title={<FormattedMessage id="filters" defaultMessage="Filters" />}
116-
onClose={onClose}
117-
>
118-
<SidebarFiltersContent>
119-
<Stack spacing={1}>
120-
<TextField
121-
fullWidth
122-
size="small"
123-
type="search"
124-
value={search}
125-
onChange={handleChangeSearch}
126-
placeholder={formatMessage({
127-
id: 'search.in.collection',
128-
defaultMessage: 'Search in collection',
129-
})}
130-
InputProps={{
131-
endAdornment: (
132-
<InputAdornment position="end">
133-
<Search color="primary" />
134-
</InputAdornment>
135-
),
136-
}}
137-
/>
138-
<Typography>
139-
<FormattedMessage defaultMessage={'Status'} id={'status'} />
140-
</Typography>
141-
<FormGroup>
142-
<FormControlLabel
143-
control={
144-
<Checkbox
145-
checked={buyNowChecked}
146-
onChange={handleChangeBuyNow}
147-
inputProps={{ 'aria-label': 'controlled' }}
148-
/>
149-
}
150-
label={
151-
<FormattedMessage defaultMessage={'Buy now'} id={'buy.now'} />
152-
}
153-
/>
154-
</FormGroup>
155-
<Typography>
156-
<FormattedMessage defaultMessage={'Traits'} id={'traits'} />
157-
</Typography>
158-
<CollectionTraits address={address as string} chainId={chainId} />
159-
</Stack>
160-
</SidebarFiltersContent>
161-
</SidebarFilters>
162-
);
163-
};
102+
const theme = useTheme();
103+
const isDesktop = useMediaQuery(theme.breakpoints.up('sm'));
164104

165-
const renderDrawer = () => {
166-
return (
167-
<Drawer open={isFiltersOpen} onClose={handleCloseDrawer}>
168-
<Box
169-
sx={(theme) => ({ minWidth: `${theme.breakpoints.values.sm / 2}px` })}
105+
const [isFiltersOpen, setIsFiltersOpen] = useState(false);
106+
107+
const handleCloseDrawer = () => setIsFiltersOpen(false);
108+
109+
const renderSidebar = (onClose?: () => void) => {
110+
return (
111+
<SidebarFilters
112+
title={<FormattedMessage id="filters" defaultMessage="Filters" />}
113+
onClose={onClose}
170114
>
171-
{renderSidebar(handleCloseDrawer)}
172-
</Box>
173-
</Drawer>
115+
<SidebarFiltersContent>
116+
<Stack spacing={1}>
117+
<TextField
118+
fullWidth
119+
size="small"
120+
type="search"
121+
value={search}
122+
onChange={handleChangeSearch}
123+
placeholder={formatMessage({
124+
id: 'search.in.collection',
125+
defaultMessage: 'Search in collection',
126+
})}
127+
InputProps={{
128+
endAdornment: (
129+
<InputAdornment position="end">
130+
<Search color="primary" />
131+
</InputAdornment>
132+
),
133+
}}
134+
/>
135+
<Typography>
136+
<FormattedMessage defaultMessage={'Status'} id={'status'} />
137+
</Typography>
138+
<FormGroup>
139+
<FormControlLabel
140+
control={
141+
<Checkbox
142+
checked={buyNowChecked}
143+
onChange={handleChangeBuyNow}
144+
inputProps={{ 'aria-label': 'controlled' }}
145+
/>
146+
}
147+
label={
148+
<FormattedMessage defaultMessage={'Buy now'} id={'buy.now'} />
149+
}
150+
/>
151+
</FormGroup>
152+
<Typography>
153+
<FormattedMessage defaultMessage={'Traits'} id={'traits'} />
154+
</Typography>
155+
<CollectionTraits address={address as string} chainId={chainId} />
156+
</Stack>
157+
</SidebarFiltersContent>
158+
</SidebarFilters>
159+
);
160+
};
161+
162+
const renderDrawer = () => {
163+
return (
164+
<Drawer open={isFiltersOpen} onClose={handleCloseDrawer}>
165+
<Box
166+
sx={(theme) => ({ minWidth: `${theme.breakpoints.values.sm / 2}px` })}
167+
>
168+
{renderSidebar(handleCloseDrawer)}
169+
</Box>
170+
</Drawer>
171+
);
172+
};
173+
174+
const collectionPage = (
175+
<>
176+
<NextSeo title={collection?.name || ''} />
177+
{renderDrawer()}
178+
179+
<CollectionSection
180+
section={{
181+
type: 'collection',
182+
config: {
183+
address: address as string,
184+
network: network as string,
185+
hideFilters: isDesktop,
186+
hideAssets: false,
187+
hideDrops: !isDrop,
188+
hideHeader: false,
189+
showPageHeader: true,
190+
isLock,
191+
disableSecondarySells,
192+
showCollectionStats: true,
193+
showSidebarOnDesktop: true,
194+
},
195+
}}
196+
/>
197+
</>
174198
);
199+
if (disableSecondarySells) {
200+
return (
201+
<MainLayout>
202+
<Container>{collectionPage}</Container>
203+
</MainLayout>
204+
);
205+
} else {
206+
return <MainLayout disablePadding isPreview={false}>{collectionPage}</MainLayout>;
207+
}
175208
};
176209

177-
const collectionPage = (
178-
<>
179-
<NextSeo title={collection?.name || ''} />
180-
{renderDrawer()}
181-
182-
<CollectionSection
183-
section={{
184-
type: 'collection',
185-
config: {
186-
address: address as string,
187-
network: network as string,
188-
hideFilters: isDesktop,
189-
hideAssets: false,
190-
hideDrops: !isDrop,
191-
hideHeader: false,
192-
showPageHeader: true,
193-
isLock,
194-
disableSecondarySells,
195-
showCollectionStats: true,
196-
showSidebarOnDesktop: true,
197-
},
198-
}}
199-
/>
200-
</>
201-
);
202-
if (disableSecondarySells) {
203-
return (
204-
<MainLayout>
205-
<Container>{collectionPage}</Container>
206-
</MainLayout>
207-
);
208-
} else {
209-
return <MainLayout disablePadding isPreview={false}>{collectionPage}</MainLayout>;
210-
}
211-
};
212-
213210
function Wrapper(props: any) {
214211
const { chainId } = useWeb3React();
215212

apps/dexappbuilder/src/modules/contract-wizard/components/AssetListContractEdition.tsx

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -193,20 +193,45 @@ export function AssetListContractEdition({
193193
/>{' '}
194194
</Button>
195195
</Grid>
196-
<Grid size={2}>
197-
<Typography variant="h6">{asset.metadata?.name || ''}</Typography>
198-
<Box sx={{ maxWidth: '150px' }}>
199-
<AssetMedia asset={asset} />
200-
</Box>
201-
<Typography>{asset.metadata?.description || ''}</Typography>
196+
<Grid size={{ xs: 12, sm: 4, md: 3 }}>
197+
<Stack spacing={2} alignItems={{ xs: 'center', sm: 'flex-start' }}>
198+
<Typography variant="h6" textAlign={{ xs: 'center', sm: 'left' }}>
199+
{asset.metadata?.name || ''}
200+
</Typography>
201+
<Box sx={{
202+
maxWidth: { xs: '200px', sm: '150px' },
203+
width: '100%',
204+
display: 'flex',
205+
justifyContent: 'center'
206+
}}>
207+
<AssetMedia asset={asset} />
208+
</Box>
209+
<Typography
210+
variant="body2"
211+
textAlign={{ xs: 'center', sm: 'left' }}
212+
sx={{
213+
maxWidth: { xs: '100%', sm: '150px' },
214+
wordBreak: 'break-word'
215+
}}
216+
>
217+
{asset.metadata?.description || ''}
218+
</Typography>
219+
</Stack>
202220
</Grid>
203-
<Grid size={8}>
204-
<Stack spacing={2}>
205-
<Typography>
221+
<Grid size={{ xs: 12, sm: 8, md: 9 }}>
222+
<Stack spacing={3}>
223+
<Typography variant="h6" textAlign={{ xs: 'center', sm: 'left' }}>
206224
<FormattedMessage id={'you.own'} defaultMessage={'You own'} />:{' '}
207225
{isLoadingAsset ? ' ' : assetSelected?.balance?.toString() || 0}
208226
</Typography>
209-
<Stack spacing={2} direction={'row'}>
227+
<Stack
228+
spacing={2}
229+
direction={{ xs: 'column', sm: 'row' }}
230+
sx={{
231+
flexWrap: { xs: 'nowrap', sm: 'wrap' },
232+
alignItems: { xs: 'stretch', sm: 'flex-start' }
233+
}}
234+
>
210235
{isLoadingAsset ? (
211236
<Skeleton>
212237
<Button>
@@ -221,6 +246,8 @@ export function AssetListContractEdition({
221246
href={`/asset/${network}/${assetSelected?.contractAddress}/${assetSelected?.id}`}
222247
target="_blank"
223248
endIcon={<OpenInNewIcon />}
249+
size="medium"
250+
sx={{ width: { xs: '100%', sm: 'auto' } }}
224251
>
225252
<FormattedMessage
226253
id="view.nft"
@@ -242,6 +269,8 @@ export function AssetListContractEdition({
242269
href={`/drop/edition/${network}/${assetSelected?.contractAddress}/${assetSelected?.id}`}
243270
target="_blank"
244271
endIcon={<OpenInNewIcon />}
272+
size="medium"
273+
sx={{ width: { xs: '100%', sm: 'auto' } }}
245274
>
246275
<FormattedMessage
247276
id="view.nft.drop"
@@ -254,7 +283,12 @@ export function AssetListContractEdition({
254283
startIcon={<SendIcon />}
255284
variant="contained"
256285
disabled={assetSelected?.balance?.eq(0) || isLoadingAsset}
257-
sx={{ maxWidth: '200px' }}
286+
size="medium"
287+
sx={{
288+
width: { xs: '100%', sm: 'auto' },
289+
maxWidth: { xs: 'none', sm: '200px' },
290+
minWidth: { xs: 'auto', sm: '120px' }
291+
}}
258292
onClick={() => {
259293
setOpenTransferDialog(true);
260294
setAssetTransfer(assetSelected);

0 commit comments

Comments
 (0)