Skip to content

Commit 99cf353

Browse files
feat: Add new item rows (#3146)
* feat: Add new mapping rows * feat: Add item row for the new mapped items * fix: getMapping
1 parent 38c7f61 commit 99cf353

36 files changed

+1005
-104
lines changed

package-lock.json

+4-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
"decentraland-ecs": "6.12.4-7784644013.commit-f770b3e",
4343
"decentraland-experiments": "^1.0.2",
4444
"decentraland-transactions": "^2.6.1",
45-
"decentraland-ui": "^6.1.2",
45+
"decentraland-ui": "^6.5.2",
4646
"ethers": "^5.6.8",
4747
"file-saver": "^2.0.1",
4848
"graphql": "^15.8.0",
@@ -158,4 +158,4 @@
158158
"decentraland-ecs": "6.12.4-7784644013.commit-f770b3e",
159159
"decentraland": "3.3.0"
160160
}
161-
}
161+
}

src/components/MappingEditor/MappingEditor.module.css

+68-1
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,60 @@
1+
/* Main class */
12
.main {
23
display: flex;
4+
}
5+
6+
.main.full {
37
flex-direction: column;
48
}
59

10+
.main.compact {
11+
flex-direction: row;
12+
align-items: center;
13+
gap: 15px;
14+
}
15+
16+
.main.compact :global(.dcl.select-field),
17+
.main.compact :global(.dcl.field) {
18+
min-width: unset;
19+
}
20+
21+
/* Mappings class */
22+
623
.mappings {
724
display: flex;
8-
flex-direction: column;
925
}
1026

27+
.mappings.compact {
28+
width: 100%;
29+
}
30+
31+
.mappings.compact > div {
32+
flex-grow: 1;
33+
}
34+
35+
/* Input margin */
36+
37+
.any,
38+
.multiple {
39+
margin-top: 19px;
40+
}
41+
42+
/* Mapping type class */
43+
1144
.mappingType :global(.ui.dropdown > .text > img) {
1245
margin-top: 3px;
1346
}
1447

48+
.mappingType.compact {
49+
margin-top: 17px;
50+
}
51+
1552
.mappingType :global(.ui.dropdown .menu > .item > img) {
1653
margin-top: 0px;
1754
}
1855

56+
/* Linked contract select class */
57+
1958
.linkedContractSelect :global(.divider.text),
2059
.linkedContractSelect :global(.visible.menu.transition) > div {
2160
display: flex !important;
@@ -27,3 +66,31 @@
2766
height: 25px;
2867
width: 25px;
2968
}
69+
70+
/* Range class */
71+
72+
.range {
73+
display: flex;
74+
}
75+
76+
.range.compact {
77+
flex-direction: row;
78+
align-items: center;
79+
gap: 15px;
80+
}
81+
82+
.range.full {
83+
flex-direction: column;
84+
}
85+
86+
/* To class */
87+
.to {
88+
text-transform: uppercase;
89+
color: var(--secondary-text);
90+
}
91+
92+
/* Multiple */
93+
94+
.multiple {
95+
width: 100%;
96+
}

src/components/MappingEditor/MappingEditor.tsx

+60-29
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { SyntheticEvent, useCallback, useMemo } from 'react'
2+
import classNames from 'classnames'
23
import { DropdownProps, Field, InputOnChangeData, SelectField, TextAreaField, TextAreaProps } from 'decentraland-ui'
34
import { MappingType, MultipleMapping } from '@dcl/schemas'
45
import { t } from 'decentraland-dapps/dist/modules/translation'
@@ -17,7 +18,7 @@ const mappingTypeIcons = {
1718
}
1819

1920
export const MappingEditor = (props: Props) => {
20-
const { mapping, error, disabled, onChange } = props
21+
const { mapping, error, loading, disabled, isCompact, onChange } = props
2122

2223
const [mappingType, mappingValue] = useMemo(() => {
2324
switch (mapping.type) {
@@ -64,11 +65,11 @@ export const MappingEditor = (props: Props) => {
6465
[onChange]
6566
)
6667

67-
const handleSingleMappingValueChange = useCallback((_: React.ChangeEvent<HTMLInputElement>, data: InputOnChangeData) => {
68-
onChange({ type: MappingType.SINGLE, id: data.value })
68+
const handleSingleMappingValueChange = useCallback((_, data: InputOnChangeData) => {
69+
onChange({ type: MappingType.SINGLE, id: data.value.replaceAll(',', '') })
6970
}, [])
7071

71-
const handleMultipleMappingValueChange = useCallback((_: React.ChangeEvent<HTMLTextAreaElement>, data: TextAreaProps) => {
72+
const handleMultipleMappingValueChange = useCallback((_, data: TextAreaProps | InputOnChangeData) => {
7273
const ids =
7374
data.value
7475
?.toString()
@@ -97,64 +98,94 @@ export const MappingEditor = (props: Props) => {
9798
)
9899

99100
return (
100-
<div className={styles.main}>
101+
<div className={classNames(styles.main, isCompact ? styles.compact : styles.full)}>
101102
<SelectField
102-
label={t('mapping_editor.mapping_type_label')}
103+
label={isCompact ? undefined : t('mapping_editor.mapping_type_label')}
103104
onChange={handleMappingTypeChange}
104105
value={mappingType}
105-
className={styles.mappingType}
106+
disabled={disabled}
107+
className={classNames(styles.mappingType, isCompact ? styles.compact : styles.full)}
106108
options={mappingTypeOptions}
107-
/>{' '}
108-
<div className={styles.mappings}>
109+
/>
110+
<div className={classNames(styles.mappings, isCompact ? styles.compact : styles.full)}>
109111
{mappingType === MappingType.ANY ? (
110-
<Field label={t('mapping_editor.mapping_value_label')} disabled value={t('mapping_editor.mapping_value_any')} />
112+
<Field
113+
label={isCompact ? undefined : t('mapping_editor.mapping_value_label')}
114+
loading={loading}
115+
disabled
116+
className={styles.any}
117+
value={t('mapping_editor.mapping_value_any')}
118+
/>
111119
) : mappingType === MappingType.SINGLE ? (
112120
<Field
113-
label={t('mapping_editor.mapping_value_label')}
121+
label={isCompact ? undefined : t('mapping_editor.mapping_value_label')}
114122
disabled={disabled}
115-
type={mappingType === MappingType.SINGLE ? 'number' : 'text'}
123+
type="number"
116124
value={mappingValue}
125+
loading={loading}
117126
error={!!error}
118127
message={error}
119128
placeholder={'1234567890'}
120129
maxLength={78}
121130
onChange={handleSingleMappingValueChange}
122131
/>
123132
) : mappingType === MappingType.MULTIPLE ? (
124-
<TextAreaField
125-
label={t('mapping_editor.mapping_value_label')}
126-
disabled={disabled}
127-
info={
128-
mappingValue.length === 0 && !error
129-
? t('mapping_editor.mapping_value_multiple_info')
130-
: t('mapping_editor.mapping_value_multiple_amount_info', { count: (mapping as MultipleMapping).ids.length })
131-
}
132-
error={error}
133-
placeholder={'1, 2, 3, 4'}
134-
value={mappingValue}
135-
onChange={handleMultipleMappingValueChange}
136-
/>
133+
isCompact ? (
134+
<Field
135+
disabled={disabled}
136+
loading={loading}
137+
error={!!error}
138+
message={
139+
error ? error : t('mapping_editor.mapping_value_multiple_amount_info', { count: (mapping as MultipleMapping).ids.length })
140+
}
141+
placeholder="1, 2, 3, 4"
142+
value={mappingValue}
143+
className={styles.multiple}
144+
onChange={handleMultipleMappingValueChange}
145+
/>
146+
) : (
147+
<TextAreaField
148+
label={t('mapping_editor.mapping_value_label')}
149+
disabled={disabled}
150+
loading={loading}
151+
info={
152+
mappingValue.length === 0 && !error
153+
? t('mapping_editor.mapping_value_multiple_info')
154+
: t('mapping_editor.mapping_value_multiple_amount_info', { count: (mapping as MultipleMapping).ids.length })
155+
}
156+
className={styles.multiple}
157+
error={error}
158+
placeholder="1, 2, 3, 4"
159+
value={mappingValue}
160+
onChange={handleMultipleMappingValueChange}
161+
/>
162+
)
137163
) : mappingType === MappingType.RANGE ? (
138-
<>
164+
<div className={classNames(styles.range, isCompact ? styles.compact : styles.full)}>
139165
<Field
140-
label={t('mapping_editor.mapping_value_from_label')}
166+
label={isCompact ? undefined : t('mapping_editor.mapping_value_from_label')}
167+
error={!!error}
168+
message={error}
141169
disabled={disabled}
142170
type="number"
143171
placeholder={'1'}
144172
maxLength={78}
145173
value={mappingValue.split(',')[0]}
146174
onChange={handleFromMappingValueChange}
147175
/>
176+
{isCompact ? <div className={styles.to}>{t('mapping_editor.to')}</div> : null}
148177
<Field
149-
label={t('mapping_editor.mapping_value_to_label')}
178+
label={isCompact ? undefined : t('mapping_editor.mapping_value_to_label')}
150179
disabled={disabled}
180+
error={!!error}
181+
message={error}
151182
type="number"
152183
placeholder={'4000'}
153184
maxLength={78}
154185
value={mappingValue.split(',')[1]}
155186
onChange={handleToMappingValueChange}
156187
/>
157-
</>
188+
</div>
158189
) : null}
159190
</div>
160191
</div>

src/components/MappingEditor/MappingEditor.types.ts

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { Mapping } from '@dcl/schemas'
22

33
export type Props = {
44
mapping: Mapping
5+
loading?: boolean
6+
isCompact?: boolean
57
error?: string
68
disabled?: boolean
79
onChange: (mapping: Mapping) => void

src/components/MappingEditor/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './MappingEditor'
2+
export * from './MappingEditor.types'

src/components/Modals/CreateCollectionSelectorModal/CreateCollectionSelectorModal.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export const CreateCollectionSelectorModal = (props: Props) => {
5252
const { onClose, onCreateCollection, onCreateThirdPartyCollection, name, isThirdPartyManager, isLoadingThirdParties } = props
5353

5454
return (
55-
<Modal name={name} onClose={onClose} size="medium">
55+
<Modal name={name} onClose={onClose} size="large">
5656
<ModalNavigation
5757
title={t('create_collection_selector_modal.title')}
5858
subtitle={t('create_collection_selector_modal.subtitle')}

src/components/Modals/CreateSingleItemModal/CreateSingleItemModal.tsx

+7-19
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {
1010
WearableCategory,
1111
Mapping,
1212
MappingType,
13-
Mappings,
1413
ContractNetwork,
1514
ContractAddress
1615
} from '@dcl/schemas'
@@ -67,7 +66,8 @@ import {
6766
getEmotePlayModes,
6867
getBodyShapeTypeFromContents,
6968
isSmart,
70-
isWearable
69+
isWearable,
70+
buildItemMappings
7171
} from 'modules/item/utils'
7272
import { EngineType, getItemData, getModelData } from 'lib/getModelData'
7373
import { getExtension, toMB } from 'lib/file'
@@ -85,10 +85,12 @@ import ItemVideo from 'components/ItemVideo'
8585
import ItemRequiredPermission from 'components/ItemRequiredPermission'
8686
import ItemProperties from 'components/ItemProperties'
8787
import { Collection } from 'modules/collection/types'
88+
import { LinkedContract } from 'modules/thirdParty/types'
8889
import { calculateFileSize, calculateModelFinalSize } from 'modules/item/export'
8990
import { MAX_THUMBNAIL_SIZE } from 'modules/assetPack/utils'
91+
import { areMappingsValid } from 'modules/thirdParty/utils'
9092
import { Authorization } from 'lib/api/auth'
91-
import { MappingEditor } from 'components/MappingEditor/MappingEditor'
93+
import { MappingEditor } from 'components/MappingEditor'
9294
import { BUILDER_SERVER_URL, BuilderAPI } from 'lib/api/builder'
9395
import EditPriceAndBeneficiaryModal from '../EditPriceAndBeneficiaryModal'
9496
import ImportStep from './ImportStep/ImportStep'
@@ -106,7 +108,6 @@ import {
106108
ITEM_LOADED_CHECK_DELAY
107109
} from './CreateSingleItemModal.types'
108110
import './CreateSingleItemModal.css'
109-
import { LinkedContract } from 'modules/thirdParty/types'
110111

111112
const defaultMapping: Mapping = { type: MappingType.ANY }
112113
export default class CreateSingleItemModal extends React.PureComponent<Props, State> {
@@ -559,15 +560,6 @@ export default class CreateSingleItemModal extends React.PureComponent<Props, St
559560
})
560561
}
561562

562-
areMappingsValid = (mappings: Mappings): boolean => {
563-
try {
564-
const validate = Mappings.validate(mappings)
565-
return !!validate
566-
} catch (error) {
567-
return false
568-
}
569-
}
570-
571563
getLinkedContract(collection: Collection | undefined | null): LinkedContract | undefined {
572564
if (!collection?.linkedContractAddress || !collection?.linkedContractNetwork) {
573565
return undefined
@@ -606,11 +598,7 @@ export default class CreateSingleItemModal extends React.PureComponent<Props, St
606598
}
607599

608600
this.setState({
609-
mappings: {
610-
[contract.network]: {
611-
[contract.address]: [mapping]
612-
}
613-
}
601+
mappings: buildItemMappings(mapping, contract)
614602
})
615603
}
616604

@@ -1063,7 +1051,7 @@ export default class CreateSingleItemModal extends React.PureComponent<Props, St
10631051
const isSmartWearable = isSmart({ type, contents: this.state.contents })
10641052
const isRequirementMet = required.every(prop => prop !== undefined)
10651053

1066-
if (isThirdPartyV2Enabled && ((!mappings && linkedContract) || (mappings && !this.areMappingsValid(mappings)))) {
1054+
if (isThirdPartyV2Enabled && ((!mappings && linkedContract) || (mappings && !areMappingsValid(mappings)))) {
10671055
return false
10681056
}
10691057

0 commit comments

Comments
 (0)