diff --git a/package-lock.json b/package-lock.json
index 7ceec26db..d79e48aa1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,13 +11,13 @@
"dependencies": {
"@babylonjs/core": "^4.2.0",
"@babylonjs/loaders": "^4.2.0",
- "@dcl/builder-client": "^4.4.0",
+ "@dcl/builder-client": "^5.0.0",
"@dcl/builder-templates": "^0.2.0",
"@dcl/content-hash-tree": "^1.1.3",
"@dcl/crypto": "^3.4.5",
"@dcl/hashing": "^3.0.4",
"@dcl/mini-rpc": "^1.0.7",
- "@dcl/schemas": "^11.12.0",
+ "@dcl/schemas": "^13.2.2",
"@dcl/sdk": "7.5.5",
"@dcl/single-sign-on-client": "^0.1.0",
"@dcl/ui-env": "^1.5.0",
@@ -2261,13 +2261,13 @@
}
},
"node_modules/@dcl/builder-client": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/@dcl/builder-client/-/builder-client-4.4.0.tgz",
- "integrity": "sha512-T09X9dkBfqo87JinBh2QYEN4m+KV71oQIebl6pOyhcP3NQgYr8o2HyMGp0ewmKzDTE7U1MBqIgqqbBAllUmN0w==",
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@dcl/builder-client/-/builder-client-5.0.0.tgz",
+ "integrity": "sha512-6xyxQTNPfoZh0LzMAO0Wkavaq+nSwGHxmxdkCl6jPH66AOilGRXCETq7r1BYQTG1KBNcgN1nTrg4ZzrFSbso0g==",
"dependencies": {
"@dcl/crypto": "^3.0.1",
"@dcl/hashing": "^1.1.0",
- "@dcl/schemas": "^11.12.0",
+ "@dcl/schemas": "^13.2.2",
"ajv": "^8.11.0",
"ajv-errors": "^3.0.0",
"ajv-formats": "^3.0.1",
@@ -2723,9 +2723,9 @@
}
},
"node_modules/@dcl/schemas": {
- "version": "11.12.0",
- "resolved": "https://registry.npmjs.org/@dcl/schemas/-/schemas-11.12.0.tgz",
- "integrity": "sha512-L04KTucvxSnrHDAl3/rnkzhjfZ785dSSPeKarBVfzyuw41uyQ0Mh4HVFWjX9hC+f/nMpM5Adg5udlT5efmepcA==",
+ "version": "13.2.2",
+ "resolved": "https://registry.npmjs.org/@dcl/schemas/-/schemas-13.2.2.tgz",
+ "integrity": "sha512-mQDvLxNL/yoWdmDKFzfpi0xJoPAEmxAoMGzwCVcmiBbP3h5T6S6RjXV9rwkZ8OQKhDURbf6wEQTJc9WF3xEF0g==",
"dependencies": {
"ajv": "^8.11.0",
"ajv-errors": "^3.0.0",
@@ -11333,6 +11333,17 @@
"tslib": "^2.6.2"
}
},
+ "node_modules/decentraland-connect/node_modules/@dcl/schemas": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/@dcl/schemas/-/schemas-11.12.0.tgz",
+ "integrity": "sha512-L04KTucvxSnrHDAl3/rnkzhjfZ785dSSPeKarBVfzyuw41uyQ0Mh4HVFWjX9hC+f/nMpM5Adg5udlT5efmepcA==",
+ "dependencies": {
+ "ajv": "^8.11.0",
+ "ajv-errors": "^3.0.0",
+ "ajv-keywords": "^5.1.0",
+ "mitt": "^3.0.1"
+ }
+ },
"node_modules/decentraland-connect/node_modules/@noble/curves": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz",
@@ -11481,6 +11492,17 @@
"resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz",
"integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw=="
},
+ "node_modules/decentraland-dapps/node_modules/@dcl/schemas": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/@dcl/schemas/-/schemas-11.12.0.tgz",
+ "integrity": "sha512-L04KTucvxSnrHDAl3/rnkzhjfZ785dSSPeKarBVfzyuw41uyQ0Mh4HVFWjX9hC+f/nMpM5Adg5udlT5efmepcA==",
+ "dependencies": {
+ "ajv": "^8.11.0",
+ "ajv-errors": "^3.0.0",
+ "ajv-keywords": "^5.1.0",
+ "mitt": "^3.0.1"
+ }
+ },
"node_modules/decentraland-dapps/node_modules/@noble/curves": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz",
@@ -11854,6 +11876,17 @@
}
}
},
+ "node_modules/decentraland-ui/node_modules/@dcl/schemas": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/@dcl/schemas/-/schemas-11.12.0.tgz",
+ "integrity": "sha512-L04KTucvxSnrHDAl3/rnkzhjfZ785dSSPeKarBVfzyuw41uyQ0Mh4HVFWjX9hC+f/nMpM5Adg5udlT5efmepcA==",
+ "dependencies": {
+ "ajv": "^8.11.0",
+ "ajv-errors": "^3.0.0",
+ "ajv-keywords": "^5.1.0",
+ "mitt": "^3.0.1"
+ }
+ },
"node_modules/decentraland-ui/node_modules/uuid": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
diff --git a/package.json b/package.json
index 958d4f0c7..916d86302 100644
--- a/package.json
+++ b/package.json
@@ -5,13 +5,13 @@
"dependencies": {
"@babylonjs/core": "^4.2.0",
"@babylonjs/loaders": "^4.2.0",
- "@dcl/builder-client": "^4.4.0",
+ "@dcl/builder-client": "^5.0.0",
"@dcl/builder-templates": "^0.2.0",
"@dcl/content-hash-tree": "^1.1.3",
"@dcl/crypto": "^3.4.5",
"@dcl/hashing": "^3.0.4",
"@dcl/mini-rpc": "^1.0.7",
- "@dcl/schemas": "^11.12.0",
+ "@dcl/schemas": "^13.2.2",
"@dcl/sdk": "7.5.5",
"@dcl/single-sign-on-client": "^0.1.0",
"@dcl/ui-env": "^1.5.0",
diff --git a/src/components/CollectionsPage/CollectionsPage.tsx b/src/components/CollectionsPage/CollectionsPage.tsx
index a0dc8da4e..c32cd4260 100644
--- a/src/components/CollectionsPage/CollectionsPage.tsx
+++ b/src/components/CollectionsPage/CollectionsPage.tsx
@@ -76,7 +76,7 @@ export default function CollectionsPage(props: Props) {
}, [address, sort])
const handleNewThirdPartyCollection = useCallback(() => {
- onOpenModal('CreateLinkedWearablesCollectionModal')
+ onOpenModal('CreateThirdPartyCollectionModal')
}, [onOpenModal, isLinkedWearablesV2Enabled])
const handleNewCollection = useCallback(() => {
@@ -213,7 +213,7 @@ export default function CollectionsPage(props: Props) {
}
iconPosition="left"
@@ -236,7 +236,7 @@ export default function CollectionsPage(props: Props) {
)
- }, [search, isThirdPartyManager, handleSearchChange, handleOpenEditor, handleNewCollection])
+ }, [search, isThirdPartyManager, isLinkedWearablesV2Enabled, handleSearchChange, handleOpenEditor, handleNewCollection])
const renderViewActions = useCallback(() => {
return (
diff --git a/src/components/MappingEditor/MappingEditor.module.css b/src/components/MappingEditor/MappingEditor.module.css
index 5cff41846..96a20ec80 100644
--- a/src/components/MappingEditor/MappingEditor.module.css
+++ b/src/components/MappingEditor/MappingEditor.module.css
@@ -8,10 +8,22 @@
flex-direction: column;
}
-.main :global(.ui.dropdown > .text > img) {
+.mappingType :global(.ui.dropdown > .text > img) {
margin-top: 3px;
}
.mappingType :global(.ui.dropdown .menu > .item > img) {
margin-top: 0px;
}
+
+.linkedContractSelect :global(.divider.text),
+.linkedContractSelect :global(.visible.menu.transition) > div {
+ display: flex !important;
+ align-items: center !important;
+}
+
+.linkedContractSelect :global(.divider.text) img,
+.linkedContractSelect :global(.visible.menu.transition) img {
+ height: 25px;
+ width: 25px;
+}
diff --git a/src/components/MappingEditor/MappingEditor.tsx b/src/components/MappingEditor/MappingEditor.tsx
index 33a0f3244..43707e100 100644
--- a/src/components/MappingEditor/MappingEditor.tsx
+++ b/src/components/MappingEditor/MappingEditor.tsx
@@ -2,10 +2,14 @@ import { SyntheticEvent, useCallback, useMemo } from 'react'
import { DropdownProps, Field, InputOnChangeData, SelectField, TextAreaField, TextAreaProps } from 'decentraland-ui'
import { MappingType, MultipleMapping } from '@dcl/schemas'
import { t } from 'decentraland-dapps/dist/modules/translation'
+import { LinkedContractProtocol } from 'modules/thirdParty/types'
+import { shorten } from 'lib/address'
import allIcon from '../../icons/all.svg'
import multipleIcon from '../../icons/multiple.svg'
import singleIcon from '../../icons/single.svg'
import rangeIcon from '../../icons/range.svg'
+import ethereumSvg from '../../icons/ethereum.svg'
+import polygonSvg from '../../icons/polygon.svg'
import { Props } from './MappingEditor.types'
import styles from './MappingEditor.module.css'
@@ -15,9 +19,26 @@ const mappingTypeIcons = {
[MappingType.SINGLE]: singleIcon,
[MappingType.RANGE]: rangeIcon
}
+const imgSrcByNetwork = {
+ [LinkedContractProtocol.MAINNET]: ethereumSvg,
+ [LinkedContractProtocol.MATIC]: polygonSvg,
+ [LinkedContractProtocol.SEPOLIA]: ethereumSvg,
+ [LinkedContractProtocol.AMOY]: polygonSvg
+}
export const MappingEditor = (props: Props) => {
- const { mapping, error, disabled, onChange } = props
+ const { mapping, error, disabled, contract, contracts, onChange } = props
+ const linkedContractsOptions = useMemo(
+ () =>
+ contracts.map((contract, index) => ({
+ value: index,
+ key: index,
+ image: imgSrcByNetwork[contract.network],
+ text: shorten(contract.address, 14, 14)
+ })),
+ [contracts, imgSrcByNetwork]
+ )
+
const [mappingType, mappingValue] = useMemo(() => {
switch (mapping.type) {
case MappingType.MULTIPLE:
@@ -42,58 +63,87 @@ export const MappingEditor = (props: Props) => {
[]
)
- const handleMappingTypeChange = useCallback((_: SyntheticEvent, { value }: DropdownProps) => {
- const mappingType = value as MappingType
- switch (mappingType) {
- case MappingType.ANY:
- props.onChange({ type: mappingType })
- break
- case MappingType.MULTIPLE:
- props.onChange({ type: mappingType, ids: [] })
- break
- case MappingType.SINGLE:
- props.onChange({ type: mappingType, id: '' })
- break
- case MappingType.RANGE:
- props.onChange({ type: mappingType, to: '', from: '' })
- break
- }
- }, [])
+ const handleMappingTypeChange = useCallback(
+ (_: SyntheticEvent, { value }: DropdownProps) => {
+ const mappingType = value as MappingType
+ switch (mappingType) {
+ case MappingType.ANY:
+ onChange({ type: mappingType }, contract)
+ break
+ case MappingType.MULTIPLE:
+ onChange({ type: mappingType, ids: [] }, contract)
+ break
+ case MappingType.SINGLE:
+ onChange({ type: mappingType, id: '' }, contract)
+ break
+ case MappingType.RANGE:
+ onChange({ type: mappingType, to: '', from: '' }, contract)
+ break
+ }
+ },
+ [contract, onChange]
+ )
- const handleSingleMappingValueChange = useCallback((_: React.ChangeEvent, data: InputOnChangeData) => {
- onChange({ type: MappingType.SINGLE, id: data.value })
- }, [])
+ const handleSingleMappingValueChange = useCallback(
+ (_: React.ChangeEvent, data: InputOnChangeData) => {
+ onChange({ type: MappingType.SINGLE, id: data.value }, contract)
+ },
+ [contract]
+ )
- const handleMultipleMappingValueChange = useCallback((_: React.ChangeEvent, data: TextAreaProps) => {
- const ids =
- data.value
- ?.toString()
- .replaceAll(/[^0-9,\s]/g, '')
- .split(',')
- .map(value => value.trim()) ?? []
+ const handleMultipleMappingValueChange = useCallback(
+ (_: React.ChangeEvent, data: TextAreaProps) => {
+ const ids =
+ data.value
+ ?.toString()
+ .replaceAll(/[^0-9,\s]/g, '')
+ .split(',')
+ .map(value => value.trim()) ?? []
- onChange({
- type: MappingType.MULTIPLE,
- ids
- })
- }, [])
+ onChange(
+ {
+ type: MappingType.MULTIPLE,
+ ids
+ },
+ contract
+ )
+ },
+ [contract]
+ )
const handleFromMappingValueChange = useCallback(
(_: React.ChangeEvent, data: InputOnChangeData) => {
- onChange({ type: MappingType.RANGE, from: data.value, to: mappingValue.split(',')[1] })
+ onChange({ type: MappingType.RANGE, from: data.value, to: mappingValue.split(',')[1] }, contract)
},
- [mappingValue]
+ [mappingValue, contract]
)
const handleToMappingValueChange = useCallback(
(_: React.ChangeEvent, data: InputOnChangeData) => {
- onChange({ type: MappingType.RANGE, from: mappingValue.split(',')[0], to: data.value })
+ onChange({ type: MappingType.RANGE, from: mappingValue.split(',')[0], to: data.value }, contract)
},
- [mappingValue]
+ [mappingValue, contract]
+ )
+
+ const handleLinkedContractChange = useCallback(
+ (_: SyntheticEvent, { value }: DropdownProps) => {
+ onChange(mapping, contracts[value as number])
+ },
+ [mapping, contracts]
)
return (
+
void
+ onChange: (mapping: Mapping, contract: LinkedContract) => void
}
diff --git a/src/components/Modals/CreateAndEditMultipleItemsModal/CreateAndEditMultipleItemsModal.tsx b/src/components/Modals/CreateAndEditMultipleItemsModal/CreateAndEditMultipleItemsModal.tsx
index 0797f4901..e96d72b5e 100644
--- a/src/components/Modals/CreateAndEditMultipleItemsModal/CreateAndEditMultipleItemsModal.tsx
+++ b/src/components/Modals/CreateAndEditMultipleItemsModal/CreateAndEditMultipleItemsModal.tsx
@@ -1,6 +1,5 @@
import * as React from 'react'
import PQueue from 'p-queue'
-import uuid from 'uuid'
import {
ItemFactory,
loadFile,
@@ -24,14 +23,12 @@ import { config } from 'config'
import { EngineType, getModelData } from 'lib/getModelData'
import { getExtension, toMB } from 'lib/file'
import {
- getDefaultThirdPartyItemUrnSuffix,
buildThirdPartyURN,
- buildThirdPartyV2URN,
decodedCollectionsUrnAreEqual,
DecodedURN,
decodeURN,
+ getDefaultThirdPartyUrnSuffix,
isThirdPartyCollectionDecodedUrn,
- isThirdPartyV2CollectionDecodedUrn,
URNType
} from 'lib/urn'
import { convertImageIntoWearableThumbnail, dataURLToBlob, getImageType } from 'modules/media/utils'
@@ -176,15 +173,10 @@ export default class CreateAndEditMultipleItemsModal extends React.PureComponent
// Generate or set the correct URN for the items taking into consideration the selected collection
const decodedCollectionUrn: DecodedURN | null = collection?.urn ? decodeURN(collection.urn) : null
// Check if the collection is a third party collection
- if (
- decodedCollectionUrn &&
- (isThirdPartyCollectionDecodedUrn(decodedCollectionUrn) || isThirdPartyV2CollectionDecodedUrn(decodedCollectionUrn))
- ) {
+ if (decodedCollectionUrn && isThirdPartyCollectionDecodedUrn(decodedCollectionUrn)) {
const decodedUrn: DecodedURN | null = loadedFile.wearable.id ? decodeURN(loadedFile.wearable.id) : null
const thirdPartyTokenId =
- loadedFile.wearable.id &&
- decodedUrn &&
- (decodedUrn.type === URNType.COLLECTIONS_THIRDPARTY || decodedUrn.type === URNType.COLLECTIONS_THIRDPARTY_V2)
+ loadedFile.wearable.id && decodedUrn && decodedUrn.type === URNType.COLLECTIONS_THIRDPARTY
? decodedUrn.thirdPartyTokenId ?? null
: null
@@ -199,16 +191,7 @@ export default class CreateAndEditMultipleItemsModal extends React.PureComponent
buildThirdPartyURN(
decodedCollectionUrn.thirdPartyName,
decodedCollectionUrn.thirdPartyCollectionId,
- thirdPartyTokenId ?? uuid.v4()
- )
- )
- } else {
- itemFactory.withUrn(
- buildThirdPartyV2URN(
- decodedCollectionUrn.thirdPartyLinkedCollectionName,
- decodedCollectionUrn.linkedCollectionNetwork,
- decodedCollectionUrn.linkedCollectionAddress,
- getDefaultThirdPartyItemUrnSuffix(loadedFile.wearable.name)
+ thirdPartyTokenId ?? getDefaultThirdPartyUrnSuffix(loadedFile.wearable.name)
)
)
}
diff --git a/src/components/Modals/CreateCollectionSelectorModal/CreateCollectionSelectorModal.container.ts b/src/components/Modals/CreateCollectionSelectorModal/CreateCollectionSelectorModal.container.ts
index 67fb4502e..5ac4fcaae 100644
--- a/src/components/Modals/CreateCollectionSelectorModal/CreateCollectionSelectorModal.container.ts
+++ b/src/components/Modals/CreateCollectionSelectorModal/CreateCollectionSelectorModal.container.ts
@@ -15,9 +15,9 @@ const mapDispatch = (dispatch: MapDispatch, ownProps: OwnProps): MapDispatchProp
ownProps.onClose()
dispatch(openModal('CreateCollectionModal'))
},
- onCreateLinkedWearablesCollection: () => {
+ onCreateThirdPartyCollection: () => {
ownProps.onClose()
- dispatch(openModal('CreateLinkedWearablesCollectionModal'))
+ dispatch(openModal('CreateThirdPartyCollectionModal'))
}
})
diff --git a/src/components/Modals/CreateCollectionSelectorModal/CreateCollectionSelectorModal.spec.tsx b/src/components/Modals/CreateCollectionSelectorModal/CreateCollectionSelectorModal.spec.tsx
index 3e8ccf754..575a5aa3d 100644
--- a/src/components/Modals/CreateCollectionSelectorModal/CreateCollectionSelectorModal.spec.tsx
+++ b/src/components/Modals/CreateCollectionSelectorModal/CreateCollectionSelectorModal.spec.tsx
@@ -8,7 +8,7 @@ export function renderWorldContributorTab(props: Partial) {
return renderWithProviders(
) {
describe('when clicking on the create collection button', () => {
let renderedComponent: ReturnType
let onCreateCollection: jest.Mock
- let onCreateLinkedWearablesCollection: jest.Mock
+ let onCreateThirdPartyCollection: jest.Mock
beforeEach(() => {
onCreateCollection = jest.fn()
- onCreateLinkedWearablesCollection = jest.fn()
+ onCreateThirdPartyCollection = jest.fn()
renderedComponent = renderWorldContributorTab({
onCreateCollection,
- onCreateLinkedWearablesCollection,
+ onCreateThirdPartyCollection,
isThirdPartyManager: true
})
})
@@ -52,8 +52,8 @@ describe('when clicking on the create collection button', () => {
userEvent.click(createButton)
})
- it('should call the onCreateLinkedWearablesCollection method prop', () => {
- expect(onCreateLinkedWearablesCollection).toHaveBeenCalled()
+ it('should call the onCreateThirdPartyCollection method prop', () => {
+ expect(onCreateThirdPartyCollection).toHaveBeenCalled()
})
})
})
diff --git a/src/components/Modals/CreateCollectionSelectorModal/CreateCollectionSelectorModal.tsx b/src/components/Modals/CreateCollectionSelectorModal/CreateCollectionSelectorModal.tsx
index 0b5206b5c..dafe2cdf4 100644
--- a/src/components/Modals/CreateCollectionSelectorModal/CreateCollectionSelectorModal.tsx
+++ b/src/components/Modals/CreateCollectionSelectorModal/CreateCollectionSelectorModal.tsx
@@ -49,7 +49,7 @@ const COLLECTIONS_LEARN_MORE_URL = `${config.get('DOCS_URL')}/creator/wearables-
const LINKED_COLLECTIONS_LEARN_MORE_URL = `${config.get('DOCS_URL')}/creator/wearables/linked-wearables/`
export const CreateCollectionSelectorModal = (props: Props) => {
- const { onClose, onCreateCollection, onCreateLinkedWearablesCollection, name, isThirdPartyManager, isLoadingThirdParties } = props
+ const { onClose, onCreateCollection, onCreateThirdPartyCollection, name, isThirdPartyManager, isLoadingThirdParties } = props
return (
@@ -73,7 +73,7 @@ export const CreateCollectionSelectorModal = (props: Props) => {
image={polygonSvg}
title={t('create_collection_selector_modal.linked_collection.title')}
subtitle={t('create_collection_selector_modal.linked_collection.subtitle')}
- onCreate={onCreateLinkedWearablesCollection}
+ onCreate={onCreateThirdPartyCollection}
isLoading={isLoadingThirdParties}
disabled={!isThirdPartyManager || isLoadingThirdParties}
learnMoreUrl={LINKED_COLLECTIONS_LEARN_MORE_URL}
diff --git a/src/components/Modals/CreateCollectionSelectorModal/CreateCollectionSelectorModal.types.ts b/src/components/Modals/CreateCollectionSelectorModal/CreateCollectionSelectorModal.types.ts
index d4cdd6aad..6c9d2b50c 100644
--- a/src/components/Modals/CreateCollectionSelectorModal/CreateCollectionSelectorModal.types.ts
+++ b/src/components/Modals/CreateCollectionSelectorModal/CreateCollectionSelectorModal.types.ts
@@ -6,10 +6,10 @@ export type Props = ModalProps & {
isThirdPartyManager: boolean
isLoadingThirdParties: boolean
onCreateCollection: () => void
- onCreateLinkedWearablesCollection: () => void
+ onCreateThirdPartyCollection: () => void
}
export type MapStateProps = Pick
-export type MapDispatchProps = Pick
+export type MapDispatchProps = Pick
export type OwnProps = Pick
export type MapDispatch = Dispatch
diff --git a/src/components/Modals/CreateLinkedWearablesCollectionModal/CrateLinkedWearablesCollectionModal.container.ts b/src/components/Modals/CreateLinkedWearablesCollectionModal/CrateLinkedWearablesCollectionModal.container.ts
deleted file mode 100644
index 54e5cf861..000000000
--- a/src/components/Modals/CreateLinkedWearablesCollectionModal/CrateLinkedWearablesCollectionModal.container.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { connect } from 'react-redux'
-import { getAddress } from 'decentraland-dapps/dist/modules/wallet/selectors'
-import { isLoadingType } from 'decentraland-dapps/dist/modules/loading/selectors'
-import { RootState } from 'modules/common/types'
-import { getLoading } from 'modules/collection/selectors'
-import { openModal } from 'decentraland-dapps/dist/modules/modal'
-import { SAVE_COLLECTION_REQUEST, saveCollectionRequest } from 'modules/collection/actions'
-import { getWalletThirdParties, getError } from 'modules/thirdParty/selectors'
-import { MapStateProps, MapDispatchProps, MapDispatch, OwnProps } from './CreateLinkedWearablesCollectionModal.types'
-import { CreateLinkedWearablesCollectionModal } from './CreateLinkedWearablesCollectionModal'
-
-const mapState = (state: RootState): MapStateProps => ({
- ownerAddress: getAddress(state),
- thirdParties: getWalletThirdParties(state),
- error: getError(state),
- isCreatingCollection: isLoadingType(getLoading(state), SAVE_COLLECTION_REQUEST)
-})
-
-const mapDispatch = (dispatch: MapDispatch, ownProps: OwnProps): MapDispatchProps => ({
- onBack: () => {
- ownProps.onClose()
- dispatch(openModal('CreateCollectionSelectorModal'))
- },
- onSubmit: collection => dispatch(saveCollectionRequest(collection))
-})
-
-export default connect(mapState, mapDispatch)(CreateLinkedWearablesCollectionModal)
diff --git a/src/components/Modals/CreateLinkedWearablesCollectionModal/CreateLinkedWearablesCollectionModal.module.css b/src/components/Modals/CreateLinkedWearablesCollectionModal/CreateLinkedWearablesCollectionModal.module.css
deleted file mode 100644
index e8883bd5e..000000000
--- a/src/components/Modals/CreateLinkedWearablesCollectionModal/CreateLinkedWearablesCollectionModal.module.css
+++ /dev/null
@@ -1,11 +0,0 @@
-.linkedContractSelect :global(.divider.text),
-.linkedContractSelect :global(.visible.menu.transition) > div {
- display: flex !important;
- align-items: center !important;
-}
-
-.linkedContractSelect :global(.divider.text) img,
-.linkedContractSelect :global(.visible.menu.transition) img {
- height: 25px;
- width: 25px;
-}
diff --git a/src/components/Modals/CreateLinkedWearablesCollectionModal/CreateLinkedWearablesCollectionModal.tsx b/src/components/Modals/CreateLinkedWearablesCollectionModal/CreateLinkedWearablesCollectionModal.tsx
deleted file mode 100644
index 0d0243d9e..000000000
--- a/src/components/Modals/CreateLinkedWearablesCollectionModal/CreateLinkedWearablesCollectionModal.tsx
+++ /dev/null
@@ -1,199 +0,0 @@
-import { useState, useMemo, useCallback, FC, SyntheticEvent } from 'react'
-import slug from 'slug'
-import { Collection, TP_COLLECTION_NAME_MAX_LENGTH } from 'modules/collection/types'
-import {
- ModalNavigation,
- Button,
- Form,
- Field,
- ModalContent,
- ModalActions,
- SelectField,
- InputOnChangeData,
- DropdownProps
-} from 'decentraland-ui'
-import uuid from 'uuid'
-import { t } from 'decentraland-dapps/dist/modules/translation/utils'
-import Modal from 'decentraland-dapps/dist/containers/Modal'
-import { getAnalytics } from 'decentraland-dapps/dist/modules/analytics'
-import { LinkedContract, ThirdPartyVersion } from 'modules/thirdParty/types'
-import { LinkedContractProtocol, buildThirdPartyURN, buildThirdPartyV2URN, decodeURN } from 'lib/urn'
-import { getThirdPartyVersion } from 'modules/thirdParty/utils'
-import ethereumSvg from '../../../icons/ethereum.svg'
-import polygonSvg from '../../../icons/polygon.svg'
-import { Props } from './CreateLinkedWearablesCollectionModal.types'
-import styles from './CreateLinkedWearablesCollectionModal.module.css'
-
-const imgSrcByNetwork = {
- [LinkedContractProtocol.MAINNET]: ethereumSvg,
- [LinkedContractProtocol.MATIC]: polygonSvg,
- [LinkedContractProtocol.SEPOLIA]: ethereumSvg,
- [LinkedContractProtocol.AMOY]: polygonSvg
-}
-
-export const CreateLinkedWearablesCollectionModal: FC = (props: Props) => {
- const { name, thirdParties, onClose, isCreatingCollection, error, ownerAddress, onSubmit, onBack } = props
- const [collectionName, setCollectionName] = useState('')
- const [linkedContract, setLinkedContract] = useState()
- const [hasCollectionIdBeenTyped, setHasCollectionIdBeenTyped] = useState(false)
- const [collectionId, setCollectionId] = useState('')
- const [thirdPartyId, setThirdPartyId] = useState(thirdParties[0].id)
- const analytics = getAnalytics()
-
- const selectedThirdParty = useMemo(() => {
- return thirdParties.find(thirdParty => thirdParty.id === thirdPartyId) || thirdParties[0]
- }, [thirdParties, thirdPartyId])
- const selectedThirdPartyVersion = useMemo(
- () => (selectedThirdParty ? getThirdPartyVersion(selectedThirdParty) : undefined),
- [selectedThirdParty, getThirdPartyVersion]
- )
- const thirdPartyOptions = useMemo(() => thirdParties.map(thirdParty => ({ value: thirdParty.id, text: thirdParty.name })), [thirdParties])
- const linkedContractsOptions = useMemo(
- () =>
- selectedThirdParty?.contracts.map((contract, index) => ({
- value: index,
- key: index,
- image: imgSrcByNetwork[contract.network],
- text: contract.address
- })),
- [selectedThirdParty, imgSrcByNetwork]
- )
- const isCollectionNameInvalid = useMemo(() => collectionName.includes(':'), [collectionName])
-
- const handleNameChange = useCallback(
- (_: SyntheticEvent, data: InputOnChangeData) => {
- setCollectionName(data.value)
- setCollectionId(hasCollectionIdBeenTyped ? collectionId : slug(data.value))
- },
- [setCollectionName, hasCollectionIdBeenTyped, collectionId]
- )
- const handleThirdPartyChange = useCallback(
- (_: React.SyntheticEvent, data: DropdownProps) => {
- if (data.value) {
- setLinkedContract(undefined)
- setThirdPartyId(data.value.toString())
- }
- },
- [setThirdPartyId, setLinkedContract]
- )
- const handleLinkedContractChange = useCallback(
- (_: SyntheticEvent, data: DropdownProps) => {
- setLinkedContract(selectedThirdParty.contracts[data.value as number])
- },
- [selectedThirdParty, setLinkedContract]
- )
- const handleCollectionIdChange = useCallback(
- (_event: React.ChangeEvent, data: InputOnChangeData) => {
- setCollectionId(data.value)
- setHasCollectionIdBeenTyped(!!data.value)
- },
- [setCollectionId, setHasCollectionIdBeenTyped]
- )
-
- const handleSubmit = useCallback(() => {
- if (
- collectionName &&
- ownerAddress &&
- ((selectedThirdPartyVersion === ThirdPartyVersion.V2 && linkedContract) ||
- (selectedThirdPartyVersion === ThirdPartyVersion.V1 && collectionId))
- ) {
- const now = Date.now()
- const decodedURN = decodeURN(selectedThirdParty.id)
- const urn =
- selectedThirdPartyVersion === ThirdPartyVersion.V1
- ? buildThirdPartyURN(decodedURN.suffix, collectionId)
- : buildThirdPartyV2URN(decodedURN.suffix, linkedContract!.network, linkedContract!.address)
- const collection: Collection = {
- id: uuid.v4(),
- name: collectionName,
- owner: ownerAddress,
- urn,
- isPublished: false,
- isApproved: false,
- minters: [],
- managers: [],
- createdAt: now,
- updatedAt: now
- }
- onSubmit(collection)
- analytics.track('Create TP Collection', {
- collectionId: collection.id,
- version: selectedThirdPartyVersion,
- thirdPartyId: selectedThirdParty.id,
- linkedContract: linkedContract?.address,
- linkedContractNetwork: linkedContract?.network,
- collectionName
- })
- }
- }, [onSubmit, collectionId, collectionName, selectedThirdPartyVersion, selectedThirdParty, linkedContract, ownerAddress, analytics])
-
- const isSubmittable =
- collectionName &&
- ownerAddress &&
- !isCollectionNameInvalid &&
- ((selectedThirdPartyVersion === ThirdPartyVersion.V2 && linkedContract) ||
- (selectedThirdPartyVersion === ThirdPartyVersion.V1 && collectionId)) &&
- !isCreatingCollection
- const isLoading = isCreatingCollection
-
- return (
-
-
-
-
- )
-}
diff --git a/src/components/Modals/CreateLinkedWearablesCollectionModal/CreateLinkedWearablesCollectionModal.types.ts b/src/components/Modals/CreateLinkedWearablesCollectionModal/CreateLinkedWearablesCollectionModal.types.ts
deleted file mode 100644
index 3b808ab38..000000000
--- a/src/components/Modals/CreateLinkedWearablesCollectionModal/CreateLinkedWearablesCollectionModal.types.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { Dispatch } from 'redux'
-import { ModalProps } from 'decentraland-dapps/dist/providers/ModalProvider/ModalProvider.types'
-import { OpenModalAction } from 'decentraland-dapps/dist/modules/modal'
-import { SaveCollectionRequestAction, saveCollectionRequest } from 'modules/collection/actions'
-import { ThirdParty } from 'modules/thirdParty/types'
-
-export type Props = ModalProps & {
- ownerAddress?: string
- thirdParties: ThirdParty[]
- isCreatingCollection: boolean
- error: string | null
- onSubmit: typeof saveCollectionRequest
- onBack: () => void
-}
-
-export type MapStateProps = Pick
-export type MapDispatchProps = Pick
-export type OwnProps = Pick
-export type MapDispatch = Dispatch
diff --git a/src/components/Modals/CreateLinkedWearablesCollectionModal/index.ts b/src/components/Modals/CreateLinkedWearablesCollectionModal/index.ts
deleted file mode 100644
index a63c2275d..000000000
--- a/src/components/Modals/CreateLinkedWearablesCollectionModal/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export * from './CreateLinkedWearablesCollectionModal.types'
-import CreateLinkedWearablesCollectionModal from './CrateLinkedWearablesCollectionModal.container'
-export default CreateLinkedWearablesCollectionModal
diff --git a/src/components/Modals/CreateSingleItemModal/CreateSingleItemModal.container.ts b/src/components/Modals/CreateSingleItemModal/CreateSingleItemModal.container.ts
index 36f623ef3..a4099df97 100644
--- a/src/components/Modals/CreateSingleItemModal/CreateSingleItemModal.container.ts
+++ b/src/components/Modals/CreateSingleItemModal/CreateSingleItemModal.container.ts
@@ -4,8 +4,11 @@ import { isLoadingType } from 'decentraland-dapps/dist/modules/loading/selectors
import { RootState } from 'modules/common/types'
import { getCollection } from 'modules/collection/selectors'
import { Collection } from 'modules/collection/types'
+import { getIsLinkedWearablesV2Enabled } from 'modules/features/selectors'
import { saveItemRequest, SAVE_ITEM_REQUEST } from 'modules/item/actions'
import { getLoading, getError, getStatusByItemId } from 'modules/item/selectors'
+import { getCollectionThirdParty } from 'modules/thirdParty/selectors'
+import { isThirdPartyCollection } from 'modules/collection/utils'
import { MapStateProps, MapDispatchProps, MapDispatch, OwnProps } from './CreateSingleItemModal.types'
import CreateSingleItemModal from './CreateSingleItemModal'
@@ -13,11 +16,14 @@ const mapState = (state: RootState, ownProps: OwnProps): MapStateProps => {
const collection: Collection | null = ownProps.metadata.collectionId ? getCollection(state, ownProps.metadata.collectionId) : null
const statusByItemId = getStatusByItemId(state)
const itemStatus = ownProps.metadata.item ? statusByItemId[ownProps.metadata.item.id] : null
+ const contracts = collection && isThirdPartyCollection(collection) ? getCollectionThirdParty(state, collection)?.contracts ?? [] : []
return {
collection,
address: getAddress(state),
error: getError(state),
+ isThirdPartyV2Enabled: getIsLinkedWearablesV2Enabled(state),
+ contracts,
itemStatus,
isLoading: isLoadingType(getLoading(state), SAVE_ITEM_REQUEST)
}
diff --git a/src/components/Modals/CreateSingleItemModal/CreateSingleItemModal.tsx b/src/components/Modals/CreateSingleItemModal/CreateSingleItemModal.tsx
index 7ee85bd2f..19960f0f4 100644
--- a/src/components/Modals/CreateSingleItemModal/CreateSingleItemModal.tsx
+++ b/src/components/Modals/CreateSingleItemModal/CreateSingleItemModal.tsx
@@ -1,5 +1,4 @@
import * as React from 'react'
-import uuid from 'uuid'
import { ethers } from 'ethers'
import {
BodyPartCategory,
@@ -10,7 +9,10 @@ import {
PreviewProjection,
WearableCategory,
Mapping,
- MappingType
+ MappingType,
+ Mappings,
+ ContractNetwork,
+ ContractAddress
} from '@dcl/schemas'
import {
MAX_EMOTE_FILE_SIZE,
@@ -70,15 +72,12 @@ import {
import { EngineType, getItemData, getModelData } from 'lib/getModelData'
import { getExtension, toMB } from 'lib/file'
import {
- getDefaultThirdPartyItemUrnSuffix,
+ getDefaultThirdPartyUrnSuffix,
buildThirdPartyURN,
- buildThirdPartyV2URN,
DecodedURN,
decodeURN,
isThirdParty,
- isThirdPartyCollectionDecodedUrn,
- isThirdPartyV2CollectionDecodedUrn,
- URNType
+ isThirdPartyCollectionDecodedUrn
} from 'lib/urn'
import ItemDropdown from 'components/ItemDropdown'
import Icon from 'components/Icon'
@@ -106,6 +105,9 @@ import {
ITEM_LOADED_CHECK_DELAY
} from './CreateSingleItemModal.types'
import './CreateSingleItemModal.css'
+import { LinkedContract } from 'modules/thirdParty/types'
+
+const defaultMapping: Mapping = { type: MappingType.ANY }
export default class CreateSingleItemModal extends React.PureComponent {
state: State = this.getInitialState()
thumbnailInput = React.createRef()
@@ -113,8 +115,23 @@ export default class CreateSingleItemModal extends React.PureComponent()
timer: ReturnType | undefined
+ getDefaultMappings(
+ contracts: LinkedContract[],
+ isThirdPartyV2Enabled: boolean
+ ): Partial>> | undefined {
+ if (contracts.length === 0 || !isThirdPartyV2Enabled) {
+ return undefined
+ }
+
+ return {
+ [contracts[0].network]: {
+ [contracts[0].address]: [defaultMapping]
+ }
+ }
+ }
+
getInitialState() {
- const { metadata } = this.props
+ const { metadata, contracts, isThirdPartyV2Enabled } = this.props
const state: State = {
view: CreateItemView.IMPORT,
@@ -141,7 +158,7 @@ export default class CreateSingleItemModal extends React.PureComponent 0 ? item.mappings[0] : { type: MappingType.ANY }
+ state.mappings = item.mappings ?? this.getDefaultMappings(contracts, isThirdPartyV2Enabled)
if (addRepresentation) {
const missingBodyShape = getMissingBodyShapeType(item)
@@ -150,6 +167,8 @@ export default class CreateSingleItemModal extends React.PureComponent | null = collection?.urn ? decodeURN(collection.urn) : null
let urn: string | undefined
if (decodedCollectionUrn && isThirdPartyCollectionDecodedUrn(decodedCollectionUrn)) {
- urn = buildThirdPartyURN(decodedCollectionUrn.thirdPartyName, decodedCollectionUrn.thirdPartyCollectionId, uuid.v4())
- } else if (decodedCollectionUrn && isThirdPartyV2CollectionDecodedUrn(decodedCollectionUrn)) {
- urn = buildThirdPartyV2URN(
- decodedCollectionUrn.thirdPartyLinkedCollectionName,
- decodedCollectionUrn.linkedCollectionNetwork,
- decodedCollectionUrn.linkedCollectionContractAddress,
- getDefaultThirdPartyItemUrnSuffix(name)
+ urn = buildThirdPartyURN(
+ decodedCollectionUrn.thirdPartyName,
+ decodedCollectionUrn.thirdPartyCollectionId,
+ getDefaultThirdPartyUrnSuffix(name)
)
}
@@ -326,7 +342,7 @@ export default class CreateSingleItemModal extends React.PureComponent {
- switch (mapping.type) {
- case MappingType.SINGLE:
- return !!mapping.id
- case MappingType.MULTIPLE:
- return mapping.ids.length > 0
- case MappingType.RANGE:
- return !!mapping.from && !!mapping.to && BigInt(mapping.from) <= BigInt(mapping.to)
+ areMappingsValid = (mappings: Mappings): boolean => {
+ try {
+ const validate = Mappings.validate(mappings)
+ return !!validate
+ } catch (error) {
+ return false
}
}
- handleMappingChange = (mapping: Mapping) => {
- this.setState({ mapping })
+ getContract = (): LinkedContract | undefined => {
+ const { contracts } = this.props
+ const { mappings } = this.state
+ // If no mappings are selected, return the first contract
+ if (!mappings) {
+ return contracts[0]
+ }
+
+ // Get the first contract from the mappings data or the first contract from the contracts list
+ const firstNetwork = Object.keys(mappings)[0] as ContractNetwork
+ const contractsInNetwork = firstNetwork ? mappings[firstNetwork] : undefined
+ const firstContractAddress = contractsInNetwork ? Object.keys(contractsInNetwork)[0] : undefined
+ return firstContractAddress ? contracts.find(contract => contract.address === firstContractAddress) : contracts[0]
+ }
+
+ getMapping = (): Mapping => {
+ const { contracts, isThirdPartyV2Enabled } = this.props
+ const { mappings } = this.state
+ const contract = this.getContract()
+ if (!contract) {
+ return defaultMapping
+ }
+
+ // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
+ let mapping: Mapping | undefined
+ if (mappings) {
+ mapping = mappings[contract.network]?.[contract.address][0]
+ } else {
+ mapping = this.getDefaultMappings(contracts, isThirdPartyV2Enabled)?.[contract.network]?.[contract.address][0]
+ }
+
+ return mapping ?? defaultMapping
+ }
+
+ handleMappingChange = (mapping: Mapping, contract: LinkedContract) => {
+ const mappings: Mappings = {
+ [contract.network]: {
+ [contract.address]: [mapping]
+ }
+ }
+
+ this.setState({ mappings })
}
handleOpenDocs = () => window.open('https://docs.decentraland.org/3d-modeling/3d-models/', '_blank')
@@ -877,14 +931,13 @@ export default class CreateSingleItemModal extends React.PureComponent ({ value, text: t(`${type!}.category.${value}`) }))}
onChange={this.handleCategoryChange}
/>
- {belongsToAThirdPartyV2Collection ? (
-
- ) : null}
+ {isThirdPartyV2Enabled && contract && (
+
+ )}
>
)
}
@@ -972,10 +1025,10 @@ export default class CreateSingleItemModal extends React.PureComponent prop !== undefined)
- if ((belongsToAThirdPartyV2Collection && !mapping) || (belongsToAThirdPartyV2Collection && mapping && !this.isMappingValid(mapping))) {
+ if (isThirdPartyV2Enabled && ((!mappings && contracts.length > 0) || (mappings && !this.areMappingsValid(mappings)))) {
return false
}
diff --git a/src/components/Modals/CreateSingleItemModal/CreateSingleItemModal.types.ts b/src/components/Modals/CreateSingleItemModal/CreateSingleItemModal.types.ts
index 9e0239f88..ffa323880 100644
--- a/src/components/Modals/CreateSingleItemModal/CreateSingleItemModal.types.ts
+++ b/src/components/Modals/CreateSingleItemModal/CreateSingleItemModal.types.ts
@@ -1,10 +1,11 @@
import { Dispatch } from 'redux'
import { ModalProps } from 'decentraland-dapps/dist/providers/ModalProvider/ModalProvider.types'
-import { IPreviewController, Mapping, Rarity } from '@dcl/schemas'
+import { IPreviewController, Mappings, Rarity } from '@dcl/schemas'
import { Metrics } from 'modules/models/types'
import { Collection } from 'modules/collection/types'
import { saveItemRequest, SaveItemRequestAction } from 'modules/item/actions'
import { BodyShapeType, Item, ItemType, SyncStatus } from 'modules/item/types'
+import { LinkedContract } from 'modules/thirdParty/types'
export enum CreateItemView {
IMPORT = 'import',
@@ -20,7 +21,9 @@ export type Props = ModalProps & {
address?: string
metadata: CreateSingleItemModalMetadata
error: string | null
+ isThirdPartyV2Enabled: boolean
isLoading: boolean
+ contracts: LinkedContract[]
collection: Collection | null
itemStatus: SyncStatus | null
onSave: typeof saveItemRequest
@@ -52,7 +55,7 @@ export type StateData = {
requiredPermissions?: string[]
tags?: string[]
modelSize?: number
- mapping: Mapping
+ mappings: Mappings
blockVrmExport?: boolean
}
export type State = {
@@ -103,6 +106,9 @@ export type AcceptedFileProps = Pick<
| 'blockVrmExport'
>
export type OwnProps = Pick
-export type MapStateProps = Pick
+export type MapStateProps = Pick<
+ Props,
+ 'address' | 'error' | 'isLoading' | 'collection' | 'itemStatus' | 'isThirdPartyV2Enabled' | 'contracts'
+>
export type MapDispatchProps = Pick
export type MapDispatch = Dispatch
diff --git a/src/components/Modals/CreateThirdPartyCollectionModal/CreateThirdPartyCollectionModal.container.ts b/src/components/Modals/CreateThirdPartyCollectionModal/CreateThirdPartyCollectionModal.container.ts
index 59d5c32a7..1e4254319 100644
--- a/src/components/Modals/CreateThirdPartyCollectionModal/CreateThirdPartyCollectionModal.container.ts
+++ b/src/components/Modals/CreateThirdPartyCollectionModal/CreateThirdPartyCollectionModal.container.ts
@@ -3,19 +3,26 @@ import { getAddress } from 'decentraland-dapps/dist/modules/wallet/selectors'
import { isLoadingType } from 'decentraland-dapps/dist/modules/loading/selectors'
import { RootState } from 'modules/common/types'
import { getLoading } from 'modules/collection/selectors'
+import { openModal } from 'decentraland-dapps/dist/modules/modal'
import { SAVE_COLLECTION_REQUEST, saveCollectionRequest } from 'modules/collection/actions'
import { getWalletThirdParties, getError } from 'modules/thirdParty/selectors'
-import { MapStateProps, MapDispatchProps, MapDispatch } from './CreateThirdPartyCollectionModal.types'
-import CreateThirdPartyCollectionModal from './CreateThirdPartyCollectionModal'
+import { getIsLinkedWearablesV2Enabled } from 'modules/features/selectors'
+import { MapStateProps, MapDispatchProps, MapDispatch, OwnProps } from './CreateThirdPartyCollectionModal.types'
+import { CreateThirdPartyCollectionModal } from './CreateThirdPartyCollectionModal'
const mapState = (state: RootState): MapStateProps => ({
- address: getAddress(state),
+ ownerAddress: getAddress(state),
thirdParties: getWalletThirdParties(state),
error: getError(state),
- isLoading: isLoadingType(getLoading(state), SAVE_COLLECTION_REQUEST)
+ isThirdPartyV2Enabled: getIsLinkedWearablesV2Enabled(state),
+ isCreatingCollection: isLoadingType(getLoading(state), SAVE_COLLECTION_REQUEST)
})
-const mapDispatch = (dispatch: MapDispatch): MapDispatchProps => ({
+const mapDispatch = (dispatch: MapDispatch, ownProps: OwnProps): MapDispatchProps => ({
+ onBack: () => {
+ ownProps.onClose()
+ dispatch(openModal('CreateCollectionSelectorModal'))
+ },
onSubmit: collection => dispatch(saveCollectionRequest(collection))
})
diff --git a/src/components/Modals/CreateThirdPartyCollectionModal/CreateThirdPartyCollectionModal.tsx b/src/components/Modals/CreateThirdPartyCollectionModal/CreateThirdPartyCollectionModal.tsx
index 14ae9f3cc..331203271 100644
--- a/src/components/Modals/CreateThirdPartyCollectionModal/CreateThirdPartyCollectionModal.tsx
+++ b/src/components/Modals/CreateThirdPartyCollectionModal/CreateThirdPartyCollectionModal.tsx
@@ -1,6 +1,5 @@
-import * as React from 'react'
-import uuid from 'uuid'
-import slug from 'slug'
+import { useState, useMemo, useCallback, FC, SyntheticEvent } from 'react'
+import { Collection, TP_COLLECTION_NAME_MAX_LENGTH } from 'modules/collection/types'
import {
ModalNavigation,
Button,
@@ -8,40 +7,64 @@ import {
Field,
ModalContent,
ModalActions,
- InputOnChangeData,
SelectField,
+ InputOnChangeData,
DropdownProps
} from 'decentraland-ui'
-import { getAnalytics } from 'decentraland-dapps/dist/modules/analytics/utils'
+import uuid from 'uuid'
import { t } from 'decentraland-dapps/dist/modules/translation/utils'
import Modal from 'decentraland-dapps/dist/containers/Modal'
-import { buildThirdPartyURN, decodeURN } from 'lib/urn'
-import { Collection, TP_COLLECTION_NAME_MAX_LENGTH } from 'modules/collection/types'
-import { Props, State } from './CreateThirdPartyCollectionModal.types'
+import { getAnalytics } from 'decentraland-dapps/dist/modules/analytics'
+import { buildThirdPartyURN, decodeURN, getDefaultThirdPartyUrnSuffix } from 'lib/urn'
+import { Props } from './CreateThirdPartyCollectionModal.types'
-export default class CreateThirdPartyCollectionModal extends React.PureComponent {
- analytics = getAnalytics()
- state: State = {
- thirdPartyId: '',
- collectionName: '',
- urnSuffix: '',
- isTypedUrnSuffix: false
- }
+export const CreateThirdPartyCollectionModal: FC = (props: Props) => {
+ const { name, thirdParties, onClose, isCreatingCollection, isThirdPartyV2Enabled, error, ownerAddress, onSubmit, onBack } = props
+ const [collectionName, setCollectionName] = useState('')
+ const [hasCollectionIdBeenTyped, setHasCollectionIdBeenTyped] = useState(false)
+ const [collectionId, setCollectionId] = useState('')
+ const [thirdPartyId, setThirdPartyId] = useState(thirdParties[0].id)
+ const analytics = getAnalytics()
- handleSubmit = () => {
- const { address, onSubmit } = this.props
- const { collectionName, urnSuffix } = this.state
+ const selectedThirdParty = useMemo(() => {
+ return thirdParties.find(thirdParty => thirdParty.id === thirdPartyId) || thirdParties[0]
+ }, [thirdParties, thirdPartyId])
+ const thirdPartyOptions = useMemo(() => thirdParties.map(thirdParty => ({ value: thirdParty.id, text: thirdParty.name })), [thirdParties])
+ const isCollectionNameInvalid = useMemo(() => collectionName.includes(':'), [collectionName])
- if (collectionName && urnSuffix) {
- const now = Date.now()
- const thirdParty = this.getSelectedThirdParty()
- const decodedURN = decodeURN(thirdParty.id)
+ const handleNameChange = useCallback(
+ (_: SyntheticEvent, data: InputOnChangeData) => {
+ setCollectionName(data.value)
+ setCollectionId(hasCollectionIdBeenTyped ? collectionId : getDefaultThirdPartyUrnSuffix(data.value))
+ },
+ [setCollectionName, hasCollectionIdBeenTyped, collectionId]
+ )
+ const handleThirdPartyChange = useCallback(
+ (_: React.SyntheticEvent, data: DropdownProps) => {
+ if (data.value) {
+ setThirdPartyId(data.value.toString())
+ }
+ },
+ [setThirdPartyId]
+ )
+ const handleCollectionIdChange = useCallback(
+ (_event: React.ChangeEvent, data: InputOnChangeData) => {
+ setCollectionId(data.value)
+ setHasCollectionIdBeenTyped(!!data.value)
+ },
+ [setCollectionId, setHasCollectionIdBeenTyped]
+ )
+ const handleSubmit = useCallback(() => {
+ if (collectionName && ownerAddress && collectionId) {
+ const now = Date.now()
+ const decodedURN = decodeURN(selectedThirdParty.id)
+ const urn = buildThirdPartyURN(decodedURN.suffix, collectionId)
const collection: Collection = {
id: uuid.v4(),
name: collectionName,
- owner: address!,
- urn: buildThirdPartyURN(decodedURN.suffix, urnSuffix),
+ owner: ownerAddress,
+ urn,
isPublished: false,
isApproved: false,
minters: [],
@@ -50,81 +73,62 @@ export default class CreateThirdPartyCollectionModal extends React.PureComponent
updatedAt: now
}
onSubmit(collection)
- this.analytics.track('Create TP Collection', { collectionId: collection.id })
- }
- }
-
- handleIdChange = (_: React.SyntheticEvent, data: DropdownProps) => {
- if (data.value) {
- this.setState({ thirdPartyId: data.value.toString() })
+ analytics.track('Create TP Collection', {
+ collectionId: collection.id,
+ thirdPartyId: selectedThirdParty.id,
+ collectionName
+ })
}
- }
-
- handleNameChange = (_event: React.ChangeEvent, data: InputOnChangeData) => {
- const { urnSuffix, isTypedUrnSuffix } = this.state
- const collectionName = data.value
- const newUrnSuffix = isTypedUrnSuffix ? urnSuffix : slug(collectionName)
- this.setState({ collectionName, urnSuffix: newUrnSuffix })
- }
-
- handleUrnSuffixChange = (_event: React.ChangeEvent, data: InputOnChangeData) => {
- this.setState({ urnSuffix: slug(data.value), isTypedUrnSuffix: !!data.value })
- }
-
- getSelectedThirdParty() {
- const { thirdParties } = this.props
- const { thirdPartyId } = this.state
- return thirdParties.find(thirdParty => thirdParty.id === thirdPartyId) || thirdParties[0]
- }
-
- render() {
- const { name, thirdParties, onClose, isLoading, error } = this.props
- const { collectionName, urnSuffix } = this.state
- const isDisabled = !collectionName || isLoading
+ }, [onSubmit, collectionId, collectionName, selectedThirdParty, ownerAddress, analytics])
- // TODO: Check for repeated urnSuffix error (needs server update)
+ const isSubmittable = collectionName && ownerAddress && !isCollectionNameInvalid && collectionId
+ !isCreatingCollection
+ const isLoading = isCreatingCollection
- const selectedThirdParty = this.getSelectedThirdParty()
-
- return (
-
-
-
+
+ )
}
diff --git a/src/components/Modals/CreateThirdPartyCollectionModal/CreateThirdPartyCollectionModal.types.ts b/src/components/Modals/CreateThirdPartyCollectionModal/CreateThirdPartyCollectionModal.types.ts
index 3877159d3..ee6013e7b 100644
--- a/src/components/Modals/CreateThirdPartyCollectionModal/CreateThirdPartyCollectionModal.types.ts
+++ b/src/components/Modals/CreateThirdPartyCollectionModal/CreateThirdPartyCollectionModal.types.ts
@@ -1,23 +1,20 @@
import { Dispatch } from 'redux'
import { ModalProps } from 'decentraland-dapps/dist/providers/ModalProvider/ModalProvider.types'
+import { OpenModalAction } from 'decentraland-dapps/dist/modules/modal'
import { SaveCollectionRequestAction, saveCollectionRequest } from 'modules/collection/actions'
import { ThirdParty } from 'modules/thirdParty/types'
export type Props = ModalProps & {
- address?: string
+ ownerAddress?: string
thirdParties: ThirdParty[]
- isLoading: boolean
+ isCreatingCollection: boolean
+ isThirdPartyV2Enabled: boolean
error: string | null
onSubmit: typeof saveCollectionRequest
+ onBack: () => void
}
-export type State = {
- thirdPartyId: string
- collectionName: string
- urnSuffix: string
- isTypedUrnSuffix: boolean
-}
-
-export type MapStateProps = Pick
-export type MapDispatchProps = Pick
-export type MapDispatch = Dispatch
+export type MapStateProps = Pick
+export type MapDispatchProps = Pick
+export type OwnProps = Pick
+export type MapDispatch = Dispatch
diff --git a/src/components/Modals/CreateThirdPartyCollectionModal/index.ts b/src/components/Modals/CreateThirdPartyCollectionModal/index.ts
index 4e2cb724c..f0eaa109c 100644
--- a/src/components/Modals/CreateThirdPartyCollectionModal/index.ts
+++ b/src/components/Modals/CreateThirdPartyCollectionModal/index.ts
@@ -1,2 +1,3 @@
+export * from './CreateThirdPartyCollectionModal.types'
import CreateThirdPartyCollectionModal from './CreateThirdPartyCollectionModal.container'
export default CreateThirdPartyCollectionModal
diff --git a/src/components/Modals/EditURNModals/EditCollectionURNModal/EditCollectionURNModal.container.ts b/src/components/Modals/EditURNModals/EditCollectionURNModal/EditCollectionURNModal.container.ts
index a603740fc..a4b023ad9 100644
--- a/src/components/Modals/EditURNModals/EditCollectionURNModal/EditCollectionURNModal.container.ts
+++ b/src/components/Modals/EditURNModals/EditCollectionURNModal/EditCollectionURNModal.container.ts
@@ -1,7 +1,7 @@
import { connect } from 'react-redux'
import { RootState } from 'modules/common/types'
import { isLoadingType } from 'decentraland-dapps/dist/modules/loading/selectors'
-import { buildThirdPartyURN, DecodedURN, isThirdPartyCollectionDecodedUrn, URNType } from 'lib/urn'
+import { buildThirdPartyURN, DecodedURN, URNType } from 'lib/urn'
import { getLoading as getCollectionLoading, getError } from 'modules/collection/selectors'
import { saveCollectionRequest, SAVE_COLLECTION_REQUEST } from 'modules/collection/actions'
import { MapStateProps, MapDispatchProps, MapDispatch, OwnProps } from './EditCollectionURNModal.types'
@@ -19,10 +19,8 @@ const mapState = (state: RootState, ownProps: OwnProps): MapStateProps => {
const mapDispatch = (dispatch: MapDispatch, ownProps: OwnProps): MapDispatchProps => ({
onSave: urn => dispatch(saveCollectionRequest({ ...ownProps.metadata.collection, urn })),
- onBuildURN: (
- decodedURN: DecodedURN | DecodedURN,
- collectionId: string
- ) => (isThirdPartyCollectionDecodedUrn(decodedURN) ? buildThirdPartyURN(decodedURN.thirdPartyName, collectionId) : '')
+ onBuildURN: (decodedURN: DecodedURN, collectionId: string) =>
+ buildThirdPartyURN(decodedURN.thirdPartyName, collectionId)
})
export default connect(mapState, mapDispatch)(EditURNModal)
diff --git a/src/components/Modals/EditURNModals/EditCollectionURNModal/EditCollectionURNModal.types.ts b/src/components/Modals/EditURNModals/EditCollectionURNModal/EditCollectionURNModal.types.ts
index 21fa55e51..a460d3996 100644
--- a/src/components/Modals/EditURNModals/EditCollectionURNModal/EditCollectionURNModal.types.ts
+++ b/src/components/Modals/EditURNModals/EditCollectionURNModal/EditCollectionURNModal.types.ts
@@ -11,10 +11,7 @@ export type Props = ModalProps & {
metadata: EditURNModalMetadata
error: string | null
onSave: (urn: string) => ReturnType
- onBuildURN: (
- decodedURN: DecodedURN | DecodedURN,
- collectionId: string
- ) => string
+ onBuildURN: (decodedURN: DecodedURN, collectionId: string) => string
}
export type EditURNModalMetadata = {
diff --git a/src/components/Modals/EditURNModals/EditItemURNModal/EditItemURNModal.container.ts b/src/components/Modals/EditURNModals/EditItemURNModal/EditItemURNModal.container.ts
index 0a13d99fc..53c9e6409 100644
--- a/src/components/Modals/EditURNModals/EditItemURNModal/EditItemURNModal.container.ts
+++ b/src/components/Modals/EditURNModals/EditItemURNModal/EditItemURNModal.container.ts
@@ -1,7 +1,7 @@
import { connect } from 'react-redux'
import { RootState } from 'modules/common/types'
import { isLoadingType } from 'decentraland-dapps/dist/modules/loading/selectors'
-import { buildThirdPartyURN, buildThirdPartyV2URN, DecodedURN, URNType } from 'lib/urn'
+import { buildThirdPartyURN, DecodedURN, URNType } from 'lib/urn'
import { getError, getLoading as getItemLoading } from 'modules/item/selectors'
import { saveItemRequest, SAVE_ITEM_REQUEST } from 'modules/item/actions'
import { MapStateProps, MapDispatchProps, MapDispatch, OwnProps } from './EditItemURNModal.types'
@@ -19,15 +19,8 @@ const mapState = (state: RootState, ownProps: OwnProps): MapStateProps => {
const mapDispatch = (dispatch: MapDispatch, ownProps: OwnProps): MapDispatchProps => ({
onSave: (urn: string) => dispatch(saveItemRequest({ ...ownProps.metadata.item, urn }, {})),
- onBuildURN: (decodedURN: DecodedURN | DecodedURN, tokenId: string) =>
- decodedURN.type === URNType.COLLECTIONS_THIRDPARTY
- ? buildThirdPartyURN(decodedURN.thirdPartyName, decodedURN.thirdPartyCollectionId!, tokenId)
- : buildThirdPartyV2URN(
- decodedURN.thirdPartyLinkedCollectionName,
- decodedURN.linkedCollectionNetwork,
- decodedURN.linkedCollectionContractAddress,
- tokenId
- )
+ onBuildURN: (decodedURN: DecodedURN, tokenId: string) =>
+ buildThirdPartyURN(decodedURN.thirdPartyName, decodedURN.thirdPartyCollectionId!, tokenId)
})
export default connect(mapState, mapDispatch)(EditURNModal)
diff --git a/src/components/Modals/EditURNModals/EditItemURNModal/EditItemURNModal.types.ts b/src/components/Modals/EditURNModals/EditItemURNModal/EditItemURNModal.types.ts
index f2b4f8976..578eb24f9 100644
--- a/src/components/Modals/EditURNModals/EditItemURNModal/EditItemURNModal.types.ts
+++ b/src/components/Modals/EditURNModals/EditItemURNModal/EditItemURNModal.types.ts
@@ -11,10 +11,7 @@ export type Props = ModalProps & {
metadata: EditURNModalMetadata
error: string | null
onSave: (urn: string) => ReturnType
- onBuildURN: (
- decodedURN: DecodedURN | DecodedURN,
- tokenId: string
- ) => string
+ onBuildURN: (decodedURN: DecodedURN, tokenId: string) => string
}
export type EditURNModalMetadata = {
diff --git a/src/components/Modals/EditURNModals/EditURNModal/EditURNModal.tsx b/src/components/Modals/EditURNModals/EditURNModal/EditURNModal.tsx
index c20fbc163..a276f2fb2 100644
--- a/src/components/Modals/EditURNModals/EditURNModal/EditURNModal.tsx
+++ b/src/components/Modals/EditURNModals/EditURNModal/EditURNModal.tsx
@@ -7,7 +7,7 @@ import { DecodedURN, decodeURN, isThirdParty, URNType } from 'lib/urn'
import { Props, State } from './EditURNModal.types'
export default class EditURNModal extends React.PureComponent {
- decodedURN: DecodedURN | DecodedURN = this.decodeURN()
+ decodedURN: DecodedURN = this.decodeURN()
analytics = getAnalytics()
state: State = {
@@ -22,11 +22,7 @@ export default class EditURNModal extends React.PureComponent {
const { onSave, urn: oldURN } = this.props
const urn = this.getUpdatedURN()
if (isThirdParty(urn)) {
- const metric =
- (this.decodedURN.type === URNType.COLLECTIONS_THIRDPARTY && this.decodedURN.thirdPartyCollectionId) ||
- this.decodedURN.type === URNType.COLLECTIONS_THIRDPARTY_V2
- ? 'Change TP Item URN'
- : 'Change TP Collection URN'
+ const metric = this.decodedURN.thirdPartyCollectionId ? 'Change TP Item URN' : 'Change TP Collection URN'
this.analytics.track(metric, { oldURN, newURN: urn })
}
@@ -47,7 +43,7 @@ export default class EditURNModal extends React.PureComponent {
decodeURN() {
const { urn } = this.props
const decodedURN = decodeURN(urn)
- if (decodedURN.type !== URNType.COLLECTIONS_THIRDPARTY && decodedURN.type !== URNType.COLLECTIONS_THIRDPARTY_V2) {
+ if (decodedURN.type !== URNType.COLLECTIONS_THIRDPARTY) {
throw new Error(`Invalid URN type ${this.decodedURN.type}`)
}
return decodedURN
diff --git a/src/components/Modals/EditURNModals/EditURNModal/EditURNModal.types.ts b/src/components/Modals/EditURNModals/EditURNModal/EditURNModal.types.ts
index ebff9b8f7..4f9cce736 100644
--- a/src/components/Modals/EditURNModals/EditURNModal/EditURNModal.types.ts
+++ b/src/components/Modals/EditURNModals/EditURNModal/EditURNModal.types.ts
@@ -10,9 +10,6 @@ export type Props = ModalProps & {
urn: URN
isLoading: boolean
error: string | null
- onBuildURN: (
- decodedURN: DecodedURN | DecodedURN,
- newURNSection: string
- ) => string
+ onBuildURN: (decodedURN: DecodedURN, newURNSection: string) => string
onSave: (newURN: string) => void
}
diff --git a/src/components/Modals/index.ts b/src/components/Modals/index.ts
index e6560e87c..a01d47ebf 100644
--- a/src/components/Modals/index.ts
+++ b/src/components/Modals/index.ts
@@ -51,5 +51,4 @@ export { default as WorldsForENSOwnersAnnouncementModal } from './WorldsForENSOw
export { default as EnsMapAddressModal } from './ENSMapAddressModal'
export { default as ReclaimNameModal } from './ReclaimNameModal'
export { default as WorldPermissionsModal } from './WorldPermissionsModal'
-export { default as CreateLinkedWearablesCollectionModal } from './CreateLinkedWearablesCollectionModal'
export { CreateCollectionSelectorModal } from './CreateCollectionSelectorModal'
diff --git a/src/components/ThirdPartyCollectionDetailPage/CollectionItem/CollectionItem.tsx b/src/components/ThirdPartyCollectionDetailPage/CollectionItem/CollectionItem.tsx
index ef530bb58..4623e2eab 100644
--- a/src/components/ThirdPartyCollectionDetailPage/CollectionItem/CollectionItem.tsx
+++ b/src/components/ThirdPartyCollectionDetailPage/CollectionItem/CollectionItem.tsx
@@ -4,7 +4,7 @@ import { Grid, Dropdown, Icon, Button, Checkbox, CheckboxProps, Popup } from 'de
import { Link, useHistory } from 'react-router-dom'
import { locations } from 'routing/locations'
import { preventDefault } from 'lib/event'
-import { decodeURN, isThirdPartyCollectionDecodedUrn, isThirdPartyV2CollectionDecodedUrn } from 'lib/urn'
+import { decodeURN, isThirdPartyCollectionDecodedUrn } from 'lib/urn'
import ItemStatus from 'components/ItemStatus'
import { SyncStatus } from 'modules/item/types'
import { FromParam } from 'modules/location/types'
@@ -49,9 +49,7 @@ export default function CollectionItem({ item, status, selected, onSelect, onOpe
try {
const decodedURN = decodeURN(item.urn)
- return isThirdPartyCollectionDecodedUrn(decodedURN) || isThirdPartyV2CollectionDecodedUrn(decodedURN)
- ? decodedURN.thirdPartyTokenId ?? ''
- : ''
+ return isThirdPartyCollectionDecodedUrn(decodedURN) ? decodedURN.thirdPartyTokenId ?? '' : ''
} catch (error) {
return ''
}
diff --git a/src/lib/address.ts b/src/lib/address.ts
index 276383294..aa7f541ce 100644
--- a/src/lib/address.ts
+++ b/src/lib/address.ts
@@ -14,6 +14,6 @@ export const isValid = (addr: string) => {
return /^0x[a-fA-F0-9]{40}$/g.test(addr)
}
-export function shorten(address: string) {
- return address ? address.slice(0, 6) + '...' + address.slice(42 - 5) : ''
+export function shorten(address: string, leftChars: number = 6, rightChars: number = 5) {
+ return address ? address.slice(0, leftChars) + '...' + address.slice(42 - rightChars) : ''
}
diff --git a/src/lib/api/builder.ts b/src/lib/api/builder.ts
index 5baa34821..d780f5268 100644
--- a/src/lib/api/builder.ts
+++ b/src/lib/api/builder.ts
@@ -1,5 +1,5 @@
import { AxiosRequestConfig, AxiosError } from 'axios'
-import { Entity, Mapping, Rarity } from '@dcl/schemas'
+import { ContractAddress, ContractNetwork, Entity, Mapping, Rarity } from '@dcl/schemas'
import { BaseAPI, APIParam, RetryParams } from 'decentraland-dapps/dist/lib/api'
import { Omit } from 'decentraland-dapps/dist/lib/types'
import { config } from 'config'
@@ -76,7 +76,7 @@ export type RemoteItem = {
created_at: Date
updated_at: Date
utility: string | null
- mappings: Mapping[] | null
+ mappings: Partial>> | null
local_content_hash: string | null
catalyst_content_hash: string | null
}
diff --git a/src/lib/urn.spec.ts b/src/lib/urn.spec.ts
index 09c086579..6f64a0dea 100644
--- a/src/lib/urn.spec.ts
+++ b/src/lib/urn.spec.ts
@@ -12,13 +12,10 @@ import {
extractEntityId,
extractCollectionAddress,
extractTokenId,
- buildThirdPartyV2URN,
- LinkedContractProtocol,
DecodedURN,
- isThirdPartyV2CollectionDecodedUrn,
isThirdPartyCollectionDecodedUrn,
decodedCollectionsUrnAreEqual,
- getDefaultThirdPartyItemUrnSuffix
+ getDefaultThirdPartyUrnSuffix
} from './urn'
jest.mock('decentraland-dapps/dist/lib/eth')
@@ -78,42 +75,6 @@ describe('when building the third party URN', () => {
})
})
-describe('when building the third party v2 URN', () => {
- const thirdPartyName = 'some-tp-name'
- const linkedNetwork = LinkedContractProtocol.AMOY
- const linkedAddress = '0x123123'
-
- beforeEach(() => {
- ;(getChainIdByNetwork as jest.Mock).mockReturnValueOnce(ChainId.MATIC_MAINNET)
- })
-
- it('should return a valid third party collection urn', () => {
- expect(buildThirdPartyV2URN(thirdPartyName, linkedNetwork, linkedAddress)).toBe(
- `urn:decentraland:matic:collections-linked-wearables:${thirdPartyName}:${linkedNetwork}:${linkedAddress}`
- )
- })
-
- it('should get the chain id for the matic network', () => {
- buildThirdPartyV2URN(thirdPartyName, linkedNetwork, linkedAddress)
- expect(getChainIdByNetwork).toHaveBeenCalledWith(Network.MATIC)
- })
-
- describe('when supplying a token id', () => {
- const tokenId = 'a-wonderful-token-id'
-
- it('should return a valid third party item urn', () => {
- expect(buildThirdPartyV2URN(thirdPartyName, linkedNetwork, linkedAddress, tokenId)).toBe(
- `urn:decentraland:matic:collections-linked-wearables:${thirdPartyName}:${linkedNetwork}:${linkedAddress}:${tokenId}`
- )
- })
-
- it('should get the chain id for the matic network', () => {
- buildThirdPartyV2URN(thirdPartyName, linkedNetwork, linkedAddress, tokenId)
- expect(getChainIdByNetwork).toHaveBeenCalledWith(Network.MATIC)
- })
- })
-})
-
describe('when decoding an URN', () => {
describe('when the urn is empty', () => {
it('should return false', () => {
@@ -163,7 +124,7 @@ describe('when decoding an URN', () => {
})
})
- describe('when a valid third party v1 URN is used', () => {
+ describe('when a valid third party', () => {
const thirdPartyRecordURN = 'urn:decentraland:matic:collections-thirdparty:crypto-motors'
describe('when third party record urn is used', () => {
@@ -206,52 +167,6 @@ describe('when decoding an URN', () => {
})
})
- describe('when a valid third party v2 URN is used', () => {
- const thirdPartyRecordURN = 'urn:decentraland:matic:collections-linked-wearables:crypto-motors'
-
- describe('when third party record urn is used', () => {
- it('should decode and return each group', () => {
- expect(decodeURN(thirdPartyRecordURN)).toEqual({
- type: URNType.COLLECTIONS_THIRDPARTY_V2,
- protocol: URNProtocol.MATIC,
- suffix: 'crypto-motors',
- thirdPartyLinkedCollectionName: 'crypto-motors',
- linkedCollectionNetwork: undefined,
- linkedCollectionContractAddress: undefined,
- thirdPartyTokenId: undefined
- })
- })
- })
-
- describe('when third party collection urn is used', () => {
- it('should decode and return each group', () => {
- expect(decodeURN(thirdPartyRecordURN + ':amoy:0x74c78f5a4ab22f01d5fd08455cf0ff5c3367535c')).toEqual({
- type: URNType.COLLECTIONS_THIRDPARTY_V2,
- protocol: URNProtocol.MATIC,
- suffix: 'crypto-motors:amoy:0x74c78f5a4ab22f01d5fd08455cf0ff5c3367535c',
- thirdPartyLinkedCollectionName: 'crypto-motors',
- linkedCollectionNetwork: 'amoy',
- linkedCollectionContractAddress: '0x74c78f5a4ab22f01d5fd08455cf0ff5c3367535c',
- thirdPartyTokenId: undefined
- })
- })
- })
-
- describe('when a third party item urn is used', () => {
- it('should decode and return each group', () => {
- expect(decodeURN(thirdPartyRecordURN + ':amoy:0x74c78f5a4ab22f01d5fd08455cf0ff5c3367535c:better-token-id')).toEqual({
- type: URNType.COLLECTIONS_THIRDPARTY_V2,
- protocol: URNProtocol.MATIC,
- suffix: 'crypto-motors:amoy:0x74c78f5a4ab22f01d5fd08455cf0ff5c3367535c:better-token-id',
- thirdPartyLinkedCollectionName: 'crypto-motors',
- linkedCollectionNetwork: 'amoy',
- linkedCollectionContractAddress: '0x74c78f5a4ab22f01d5fd08455cf0ff5c3367535c',
- thirdPartyTokenId: 'better-token-id'
- })
- })
- })
- })
-
describe('when a valid entity urn is used', () => {
describe('and the URN is an entity with a baseUrl', () => {
it('should decode and return each group', () => {
@@ -276,7 +191,7 @@ describe('when decoding an URN', () => {
})
})
-describe('when extracting the third party item token id from a third party v1 URN', () => {
+describe('when extracting the third party item token id from an URN', () => {
describe('when the URN is not a valid third party URN', () => {
it("should throw an error signaling that the URN doesn't belong to a third party", () => {
expect(() =>
@@ -287,7 +202,7 @@ describe('when extracting the third party item token id from a third party v1 UR
})
})
- describe('when the URN is a valid third party v1 URN', () => {
+ describe('when the URN is a valid third party URN', () => {
it('should extract the collection and token ids', () => {
expect(extractThirdPartyTokenId('urn:decentraland:mumbai:collections-thirdparty:thirdparty2:collection-id:token-id')).toBe(
'collection-id:token-id'
@@ -296,28 +211,6 @@ describe('when extracting the third party item token id from a third party v1 UR
})
})
-describe('when extracting the third party item token id from a third party v2 URN', () => {
- describe('when the URN is not a valid third party URN', () => {
- it("should throw an error signaling that the URN doesn't belong to a third party", () => {
- expect(() =>
- extractThirdPartyTokenId('urn:decentraland:matic:collections-v2:0xc6d2000a7a1ddca92941f4e2b41360fe4ee2abd8')
- ).toThrowError(
- 'Tried to build a third party token for a non third party URN "urn:decentraland:matic:collections-v2:0xc6d2000a7a1ddca92941f4e2b41360fe4ee2abd8"'
- )
- })
- })
-
- describe('when the URN is a valid third party v2 URN', () => {
- it('should extract the collection and token ids', () => {
- expect(
- extractThirdPartyTokenId(
- 'urn:decentraland:matic:collections-linked-wearables:crypto-motors:amoy:0x74c78f5a4ab22f01d5fd08455cf0ff5c3367535c:better-token-id'
- )
- ).toBe('amoy:0x74c78f5a4ab22f01d5fd08455cf0ff5c3367535c:better-token-id')
- })
- })
-})
-
describe('when checking if a collection is a third party', () => {
let urn: string
@@ -444,140 +337,43 @@ describe('when checking if a decoded URN belongs to a third party one', () => {
})
})
-describe('when checking if a decoded URN belongs to a third party v2 one', () => {
- describe('and the decoded URN does not belong to a third party v2', () => {
- let decodedUrn: DecodedURN
-
- beforeEach(() => {
- decodedUrn = decodeURN('urn:decentraland:goerli:collections-v2:0xc6d2000a7a1ddca92941f4e2b41360fe4ee2abd8')
- })
-
- it('should return false', () => {
- expect(isThirdPartyV2CollectionDecodedUrn(decodedUrn)).toBe(false)
- })
- })
-
- describe('and the decoded URN belongs to a third party v2', () => {
- let decodedUrn: DecodedURN
-
- beforeEach(() => {
- decodedUrn = decodeURN(
- 'urn:decentraland:matic:collections-linked-wearables:crypto-motors:amoy:0x74c78f5a4ab22f01d5fd08455cf0ff5c3367535c:better-token-id'
- )
- })
-
- it('should return true', () => {
- expect(isThirdPartyV2CollectionDecodedUrn(decodedUrn)).toBe(true)
- })
- })
-})
-
describe('when checking if two decoded collection URNs are equal', () => {
let fistDecodedUrn: DecodedURN
let secondDecodedUrn: DecodedURN
- describe('and the URNs are of collections v2', () => {
- describe('and the URNs are different', () => {
- beforeEach(() => {
- fistDecodedUrn = decodeURN('urn:decentraland:amoy:collections-v2:0xc6d2000a7a1ddca92941f4e2b41360fe4ee2abd8')
- secondDecodedUrn = decodeURN('urn:decentraland:amoy:collections-v2:0x16a2040b2b1eeca12344f4e2b11260ae2ee2edc2')
- })
-
- it('should return false', () => {
- expect(decodedCollectionsUrnAreEqual(fistDecodedUrn, secondDecodedUrn)).toBe(false)
- })
- })
-
- describe('and the URNs are equal', () => {
- beforeEach(() => {
- fistDecodedUrn = decodeURN('urn:decentraland:amoy:collections-v2:0xc6d2000a7a1ddca92941f4e2b41360fe4ee2abd8')
- secondDecodedUrn = decodeURN('urn:decentraland:amoy:collections-v2:0xc6d2000a7a1ddca92941f4e2b41360fe4ee2abd8')
- })
-
- it('should return true', () => {
- expect(decodedCollectionsUrnAreEqual(fistDecodedUrn, secondDecodedUrn)).toBe(true)
- })
- })
- })
-
- describe('and the URNs are of third party v1 collections', () => {
- describe('and the URNs are different', () => {
- beforeEach(() => {
- fistDecodedUrn = decodeURN('urn:decentraland:amoy:collections-thirdparty:thirdparty2:collection-id')
- secondDecodedUrn = decodeURN('urn:decentraland:amoy:collections-thirdparty:thirdparty2:another-collection-id')
- })
-
- it('should return false', () => {
- expect(decodedCollectionsUrnAreEqual(fistDecodedUrn, secondDecodedUrn)).toBe(false)
- })
- })
-
- describe('and the URNs are equal', () => {
- beforeEach(() => {
- fistDecodedUrn = decodeURN('urn:decentraland:amoy:collections-thirdparty:thirdparty2:collection-id')
- secondDecodedUrn = decodeURN('urn:decentraland:amoy:collections-thirdparty:thirdparty2:collection-id')
- })
-
- it('should return true', () => {
- expect(decodedCollectionsUrnAreEqual(fistDecodedUrn, secondDecodedUrn)).toBe(true)
- })
- })
- })
-
- describe('and the URNs are of third party v2 collections', () => {
- describe('and the URNs are different', () => {
- beforeEach(() => {
- fistDecodedUrn = decodeURN(
- 'urn:decentraland:matic:collections-linked-wearables:crypto-motors:amoy:0x74c78f5a4ab22f01d5fd08455cf0ff5c3367535c'
- )
- secondDecodedUrn = decodeURN(
- 'urn:decentraland:matic:collections-linked-wearables:crypto-motors:amoy:0x21c28f5a2ab14f11d4fd08425cf0ea5c2367215c'
- )
- })
-
- it('should return false', () => {
- expect(decodedCollectionsUrnAreEqual(fistDecodedUrn, secondDecodedUrn)).toBe(false)
- })
+ describe('and the URNs are different', () => {
+ beforeEach(() => {
+ fistDecodedUrn = decodeURN('urn:decentraland:amoy:collections-v2:0xc6d2000a7a1ddca92941f4e2b41360fe4ee2abd8')
+ secondDecodedUrn = decodeURN('urn:decentraland:amoy:collections-v2:0x16a2040b2b1eeca12344f4e2b11260ae2ee2edc2')
})
- describe('and the URNs are equal', () => {
- beforeEach(() => {
- fistDecodedUrn = decodeURN(
- 'urn:decentraland:matic:collections-linked-wearables:crypto-motors:amoy:0x74c78f5a4ab22f01d5fd08455cf0ff5c3367535c'
- )
- secondDecodedUrn = decodeURN(
- 'urn:decentraland:matic:collections-linked-wearables:crypto-motors:amoy:0x74c78f5a4ab22f01d5fd08455cf0ff5c3367535c'
- )
- })
-
- it('should return true', () => {
- expect(decodedCollectionsUrnAreEqual(fistDecodedUrn, secondDecodedUrn)).toBe(true)
- })
+ it('should return false', () => {
+ expect(decodedCollectionsUrnAreEqual(fistDecodedUrn, secondDecodedUrn)).toBe(false)
})
})
- describe('and the URNs are of different types', () => {
+ describe('and the URNs are equal', () => {
beforeEach(() => {
fistDecodedUrn = decodeURN('urn:decentraland:amoy:collections-v2:0xc6d2000a7a1ddca92941f4e2b41360fe4ee2abd8')
- secondDecodedUrn = decodeURN('urn:decentraland:amoy:collections-thirdparty:thirdparty2:collection-id')
+ secondDecodedUrn = decodeURN('urn:decentraland:amoy:collections-v2:0xc6d2000a7a1ddca92941f4e2b41360fe4ee2abd8')
})
- it('should return false', () => {
- expect(decodedCollectionsUrnAreEqual(fistDecodedUrn, secondDecodedUrn)).toBe(false)
+ it('should return true', () => {
+ expect(decodedCollectionsUrnAreEqual(fistDecodedUrn, secondDecodedUrn)).toBe(true)
})
})
})
-describe('when getting a default third party item URN suffix', () => {
+describe('when getting a default third party URN suffix', () => {
describe('and the item name is empty', () => {
it('should return a string with the "default" word plus 4 random hex characters', () => {
- expect(getDefaultThirdPartyItemUrnSuffix('')).toMatch(/^default-[0-9a-f]{4}$/)
+ expect(getDefaultThirdPartyUrnSuffix('')).toMatch(/^default-[0-9a-f]{4}$/)
})
})
describe('and the item name is not empty', () => {
it('should return a string with the sluggled item name plus 4 random hex characters', () => {
- expect(getDefaultThirdPartyItemUrnSuffix('a wonderful item: name')).toMatch(/^a-wonderful-item-name-[0-9a-f]{4}$/)
+ expect(getDefaultThirdPartyUrnSuffix('a wonderful item: name')).toMatch(/^a-wonderful-item-name-[0-9a-f]{4}$/)
})
})
})
diff --git a/src/lib/urn.ts b/src/lib/urn.ts
index f2667723e..b21d4c4d3 100644
--- a/src/lib/urn.ts
+++ b/src/lib/urn.ts
@@ -1,5 +1,5 @@
-import { getURNProtocol, Network } from '@dcl/schemas'
import slug from 'slug'
+import { getURNProtocol, Network } from '@dcl/schemas'
import { getChainIdByNetwork } from 'decentraland-dapps/dist/lib/eth'
/**
@@ -20,7 +20,6 @@ import { getChainIdByNetwork } from 'decentraland-dapps/dist/lib/eth'
* base-avatars|
* collections-v2|
* collections-thirdparty|
- * collections-linked-wearables|
* entity
* ):
* (?
@@ -30,27 +29,19 @@ import { getChainIdByNetwork } from 'decentraland-dapps/dist/lib/eth'
* (?[^:|\\s]+)
* (:(?[^:|\\s]+))?
* (:(?[^:|\\s]+))?
- * )|
- * ((?<=collections-linked-wearables:)
- * (?[^:|\\s]+)
- * (:?mainnet|sepolia|matic|amoy)
- * (:?0x[a-fA-F0-9]{40})?
- * (:(?[^:|\\s]+))?)
+ * )
* ((?<=entity:)(?[^:|\\s]+)?\\?=\\&baseUrl=(?https:[^=\\s]+)?)
* )
* )
*/
const baseMatcher = 'urn:decentraland'
const protocolMatcher = '(?mainnet|goerli|sepolia|matic|mumbai|amoy|off-chain)'
-const typeMatcher = '(?base-avatars|collections-v2|collections-thirdparty|collections-linked-wearables|entity)'
+const typeMatcher = '(?base-avatars|collections-v2|collections-thirdparty|entity)'
const baseAvatarsSuffixMatcher = '((?<=base-avatars:)BaseMale|BaseFemale)'
const collectionsSuffixMatcher = '((?<=collections-v2:)(?0x[a-fA-F0-9]{40}))(:(?[^:|\\s]+))?'
-const thirdPartySuffixMatcher = '((?<=collections-thirdparty:)(?[^:|\\s]+)(:(?[^:|\\s]+))?)'
-const thirdPartyV2SuffixMatcher =
- '((?<=collections-linked-wearables:)(?[^:|\\s]+)(:(?mainnet|sepolia|matic|amoy):(?0x[a-fA-F0-9]{40}))?)'
-const thirdPartyMatchers = `(:?${thirdPartySuffixMatcher}|${thirdPartyV2SuffixMatcher})(:(?[^:|\\s]+))?`
-
+const thirdPartySuffixMatcher =
+ '((?<=collections-thirdparty:)(?[^:|\\s]+)(:(?[^:|\\s]+))?(:(?[^:|\\s]+))?)'
const entitySuffixMatcher = '((?<=entity:)(?[^\\?|\\s]+)(\\?=\\&baseUrl=(?[^\\?|\\s]+))?)'
export enum URNProtocol {
@@ -62,17 +53,10 @@ export enum URNProtocol {
AMOY = 'amoy',
OFF_CHAIN = 'off-chain'
}
-export enum LinkedContractProtocol {
- MAINNET = 'mainnet',
- SEPOLIA = 'sepolia',
- MATIC = 'matic',
- AMOY = 'amoy'
-}
export enum URNType {
BASE_AVATARS = 'base-avatars',
COLLECTIONS_V2 = 'collections-v2',
COLLECTIONS_THIRDPARTY = 'collections-thirdparty',
- COLLECTIONS_THIRDPARTY_V2 = 'collections-linked-wearables',
ENTITY = 'entity'
}
export type URN = string
@@ -89,26 +73,17 @@ type CollectionThirdPartyURN = {
thirdPartyCollectionId?: string
thirdPartyTokenId?: string
}
-type CollectionThirdPartyV2URN = {
- type: URNType.COLLECTIONS_THIRDPARTY_V2
- thirdPartyLinkedCollectionName: string
- linkedCollectionNetwork: LinkedContractProtocol
- linkedCollectionContractAddress: string
- thirdPartyTokenId?: string
-}
type EntityURN = { type: URNType.ENTITY; entityId: string; baseUrl?: string }
export type DecodedURN = BaseDecodedURN &
(T extends URNType.BASE_AVATARS
? BaseAvatarURN
: T extends URNType.COLLECTIONS_V2
? CollectionsV2URN
- : T extends URNType.COLLECTIONS_THIRDPARTY_V2
- ? CollectionThirdPartyV2URN
: T extends URNType.COLLECTIONS_THIRDPARTY
? CollectionThirdPartyURN
: T extends URNType.ENTITY
? EntityURN
- : BaseAvatarURN | CollectionsV2URN | CollectionThirdPartyURN | CollectionThirdPartyV2URN | EntityURN)
+ : BaseAvatarURN | CollectionsV2URN | CollectionThirdPartyURN | EntityURN)
export function buildThirdPartyURN(thirdPartyName: string, collectionId: string, tokenId?: string) {
let urn = `urn:decentraland:${getNetworkURNProtocol(Network.MATIC)}:collections-thirdparty:${thirdPartyName}:${collectionId}`
@@ -118,21 +93,6 @@ export function buildThirdPartyURN(thirdPartyName: string, collectionId: string,
return urn
}
-export function buildThirdPartyV2URN(
- thirdPartyName: string,
- thirdPartyLinkedContractNetwork: LinkedContractProtocol,
- thirdPartyLinkedContractAddress: string,
- tokenId?: string
-) {
- let urn = `urn:decentraland:${getNetworkURNProtocol(
- Network.MATIC
- )}:collections-linked-wearables:${thirdPartyName}:${thirdPartyLinkedContractNetwork}:${thirdPartyLinkedContractAddress}`
- if (tokenId) {
- urn += `:${tokenId}`
- }
- return urn
-}
-
export function buildCatalystItemURN(contractAddress: string, tokenId: string): URN {
return `urn:decentraland:${getNetworkURNProtocol(Network.MATIC)}:collections-v2:${contractAddress}:${tokenId}`
}
@@ -143,44 +103,32 @@ export function buildDefaultCatalystCollectionURN() {
export function extractThirdPartyId(urn: URN): string {
const decodedURN = decodeURN(urn)
- if (decodedURN.type !== URNType.COLLECTIONS_THIRDPARTY && decodedURN.type !== URNType.COLLECTIONS_THIRDPARTY_V2) {
+ if (decodedURN.type !== URNType.COLLECTIONS_THIRDPARTY) {
throw new Error('URN is not a third party URN')
}
- if (decodedURN.type === URNType.COLLECTIONS_THIRDPARTY) {
- return `urn:decentraland:${decodedURN.protocol}:collections-thirdparty:${decodedURN.thirdPartyName}`
- } else {
- return `urn:decentraland:${decodedURN.protocol}:collections-linked-wearables:${decodedURN.thirdPartyLinkedCollectionName}`
- }
+ return `urn:decentraland:${decodedURN.protocol}:collections-thirdparty:${decodedURN.thirdPartyName}`
}
export function extractThirdPartyTokenId(urn: URN) {
const decodedURN = decodeURN(urn)
-
- if (decodedURN.type === URNType.COLLECTIONS_THIRDPARTY) {
- const { thirdPartyCollectionId, thirdPartyTokenId } = decodedURN
- return `${thirdPartyCollectionId ?? ''}:${thirdPartyTokenId ?? ''}`
- } else if (decodedURN.type === URNType.COLLECTIONS_THIRDPARTY_V2) {
- const { linkedCollectionNetwork, linkedCollectionContractAddress, thirdPartyTokenId } = decodedURN
- return `${linkedCollectionNetwork}:${linkedCollectionContractAddress}:${thirdPartyTokenId ?? ''}`
+ if (decodedURN.type !== URNType.COLLECTIONS_THIRDPARTY) {
+ throw new Error(`Tried to build a third party token for a non third party URN "${urn}"`)
}
- throw new Error(`Tried to build a third party token for a non third party URN "${urn}"`)
+ const { thirdPartyCollectionId, thirdPartyTokenId } = decodedURN
+ return `${thirdPartyCollectionId ?? ''}:${thirdPartyTokenId ?? ''}`
}
// TODO: This logic is repeated in collection/util's `getCollectionType`, but being used only for items (item.urn).
// It should probably be replaced by a getItemType or we should see if it's better to only keep one way of doing this
-export function isThirdParty(urn?: string, version?: URNType.COLLECTIONS_THIRDPARTY | URNType.COLLECTIONS_THIRDPARTY_V2) {
+export function isThirdParty(urn?: string) {
if (!urn) {
return false
}
const decodedURN = decodeURN(urn)
- if (version) {
- return decodedURN.type === version
- }
-
- return decodedURN.type === URNType.COLLECTIONS_THIRDPARTY || decodedURN.type === URNType.COLLECTIONS_THIRDPARTY_V2
+ return decodedURN.type === URNType.COLLECTIONS_THIRDPARTY
}
export function extractEntityId(urn: URN): string {
@@ -194,7 +142,7 @@ export function extractEntityId(urn: URN): string {
export function decodeURN(urn: URN): DecodedURN {
const urnRegExp = new RegExp(
- `${baseMatcher}:(${protocolMatcher}:)?${typeMatcher}:(?${baseAvatarsSuffixMatcher}|${collectionsSuffixMatcher}|${thirdPartyMatchers}|${entitySuffixMatcher})`
+ `${baseMatcher}:(${protocolMatcher}:)?${typeMatcher}:(?${baseAvatarsSuffixMatcher}|${collectionsSuffixMatcher}|${thirdPartySuffixMatcher}|${entitySuffixMatcher})`
)
const matches = urnRegExp.exec(urn)
@@ -235,6 +183,11 @@ export function extractTokenId(urn: URN): string {
return `${collectionAddress}:${tokenId ?? ''}`
}
+export const isThirdPartyCollectionDecodedUrn = (
+ urn: DecodedURN
+): urn is DecodedURN & { thirdPartyName: string; thirdPartyCollectionId: string } =>
+ urn.type === URNType.COLLECTIONS_THIRDPARTY && !!urn.thirdPartyName && !!urn.thirdPartyCollectionId
+
export const decodedCollectionsUrnAreEqual = (urnA: DecodedURN, urnB: DecodedURN) => {
if (urnA.type !== urnB.type) {
return false
@@ -252,34 +205,10 @@ export const decodedCollectionsUrnAreEqual = (urnA: DecodedURN, urnB: DecodedURN
urnA.thirdPartyCollectionId === (urnB as DecodedURN).thirdPartyCollectionId &&
urnA.thirdPartyTokenId === (urnB as DecodedURN).thirdPartyTokenId
)
- case URNType.COLLECTIONS_THIRDPARTY_V2:
- return (
- urnA.thirdPartyLinkedCollectionName === (urnB as DecodedURN).thirdPartyLinkedCollectionName &&
- urnA.linkedCollectionNetwork === (urnB as DecodedURN).linkedCollectionNetwork &&
- urnA.linkedCollectionContractAddress === (urnB as DecodedURN).linkedCollectionContractAddress &&
- urnA.thirdPartyTokenId === (urnB as DecodedURN).thirdPartyTokenId
- )
}
}
-export const isThirdPartyCollectionDecodedUrn = (
- urn: DecodedURN
-): urn is DecodedURN & { thirdPartyName: string; thirdPartyCollectionId: string } =>
- urn.type === URNType.COLLECTIONS_THIRDPARTY && !!urn.thirdPartyName && !!urn.thirdPartyCollectionId
-
-export const isThirdPartyV2CollectionDecodedUrn = (
- urn: DecodedURN
-): urn is DecodedURN & {
- thirdPartyLinkedCollectionName: string
- linkedCollectionNetwork: string
- linkedCollectionAddress: string
-} =>
- urn.type === URNType.COLLECTIONS_THIRDPARTY_V2 &&
- !!urn.thirdPartyLinkedCollectionName &&
- !!urn.linkedCollectionNetwork &&
- !!urn.linkedCollectionContractAddress
-
-export const getDefaultThirdPartyItemUrnSuffix = (itemName: string) => {
+export const getDefaultThirdPartyUrnSuffix = (itemName: string) => {
const randHex = Array.from({ length: 4 }, () => Math.floor(Math.random() * 16).toString(16)).join('')
return `${slug(itemName.length > 0 ? itemName : 'default')}-${randHex}`
}
diff --git a/src/modules/collection/sagas.spec.ts b/src/modules/collection/sagas.spec.ts
index f6213b0b8..522fc32c1 100644
--- a/src/modules/collection/sagas.spec.ts
+++ b/src/modules/collection/sagas.spec.ts
@@ -1259,7 +1259,7 @@ describe('when saving a collection', () => {
return expectSaga(collectionSaga, mockBuilder, mockBuilderClient)
.provide([
[getContext('history'), { push: pushMock }],
- [select(getOpenModals), { CreateThirdPartyCollectionModal: true, CreateLinkedWearablesCollectionModal: true }],
+ [select(getOpenModals), { CreateThirdPartyCollectionModal: true }],
[call([mockBuilder, 'saveCollection'], thirdPartyCollection, ''), remoteCollection]
])
.dispatch(saveCollectionRequest(thirdPartyCollection))
@@ -1278,7 +1278,6 @@ describe('when saving a collection', () => {
.put(saveCollectionSuccess({ ...thirdPartyCollection, contractAddress: remoteCollection.contractAddress }))
.put(closeModal('CreateCollectionModal'))
.put(closeModal('CreateThirdPartyCollectionModal'))
- .put(closeModal('CreateLinkedWearablesCollectionModal'))
.put(closeModal('EditCollectionURNModal'))
.put(closeModal('EditCollectionNameModal'))
.dispatch(saveCollectionRequest(thirdPartyCollection))
diff --git a/src/modules/collection/sagas.ts b/src/modules/collection/sagas.ts
index 065bae60b..a6da794fa 100644
--- a/src/modules/collection/sagas.ts
+++ b/src/modules/collection/sagas.ts
@@ -247,11 +247,7 @@ export function* collectionSaga(legacyBuilderClient: BuilderAPI, client: Builder
const openModals: ModalState = yield select(getOpenModals)
const history: History = yield getContext('history')
- if (
- openModals['CreateCollectionModal'] ||
- openModals['CreateThirdPartyCollectionModal'] ||
- openModals['CreateLinkedWearablesCollectionModal']
- ) {
+ if (openModals['CreateCollectionModal'] || openModals['CreateThirdPartyCollectionModal']) {
// Redirect to the newly created collection detail
const { collection } = action.payload
const detailPageLocation = isTPCollection(collection) ? locations.thirdPartyCollectionDetail : locations.collectionDetail
@@ -262,7 +258,6 @@ export function* collectionSaga(legacyBuilderClient: BuilderAPI, client: Builder
yield put(closeModal('CreateCollectionModal'))
yield put(closeModal('CreateThirdPartyCollectionModal'))
yield put(closeModal('EditCollectionURNModal'))
- yield put(closeModal('CreateLinkedWearablesCollectionModal'))
yield put(closeModal('EditCollectionNameModal'))
}
diff --git a/src/modules/collection/selectors.ts b/src/modules/collection/selectors.ts
index 3ad812d6c..83838a154 100644
--- a/src/modules/collection/selectors.ts
+++ b/src/modules/collection/selectors.ts
@@ -81,8 +81,7 @@ export const getAuthorizedCollections = createSelector<
switch (type) {
case CollectionType.STANDARD:
return address && canSeeCollection(collection, address)
- case CollectionType.THIRD_PARTY:
- case CollectionType.THIRD_PARTY_V2: {
+ case CollectionType.THIRD_PARTY: {
const thirdParty = getThirdPartyForCollection(thirdParties, collection)
return address && thirdParty && isUserManagerOfThirdParty(address, thirdParty)
}
diff --git a/src/modules/collection/types.ts b/src/modules/collection/types.ts
index cad38b019..f3a36f9dc 100644
--- a/src/modules/collection/types.ts
+++ b/src/modules/collection/types.ts
@@ -23,8 +23,7 @@ export type Collection = {
export enum CollectionType {
STANDARD = 'standard',
- THIRD_PARTY = 'third_party',
- THIRD_PARTY_V2 = 'third_party_v2'
+ THIRD_PARTY = 'third_party'
}
export enum RoleType {
diff --git a/src/modules/collection/utils.ts b/src/modules/collection/utils.ts
index 7063711e4..e94101092 100644
--- a/src/modules/collection/utils.ts
+++ b/src/modules/collection/utils.ts
@@ -81,7 +81,7 @@ export function getCollectionBaseURI() {
export function isThirdPartyCollection(collection: Collection) {
const collectionType = getCollectionType(collection)
- return collectionType === CollectionType.THIRD_PARTY || collectionType === CollectionType.THIRD_PARTY_V2
+ return collectionType === CollectionType.THIRD_PARTY
}
export function getCollectionType(collection: Collection): CollectionType {
@@ -90,8 +90,6 @@ export function getCollectionType(collection: Collection): CollectionType {
switch (type) {
case URNType.COLLECTIONS_THIRDPARTY:
return CollectionType.THIRD_PARTY
- case URNType.COLLECTIONS_THIRDPARTY_V2:
- return CollectionType.THIRD_PARTY_V2
case URNType.COLLECTIONS_V2:
case URNType.BASE_AVATARS:
return CollectionType.STANDARD
diff --git a/src/modules/item/export.ts b/src/modules/item/export.ts
index 46ecac794..fbf5748b1 100644
--- a/src/modules/item/export.ts
+++ b/src/modules/item/export.ts
@@ -3,7 +3,7 @@ import { DeploymentPreparationData, buildEntity } from 'dcl-catalyst-client/dist
import { MerkleDistributorInfo } from '@dcl/content-hash-tree/dist/types'
import { calculateMultipleHashesADR32, calculateMultipleHashesADR32LegacyQmHash } from '@dcl/hashing'
import { BuilderAPI } from 'lib/api/builder'
-import { buildCatalystItemURN, decodeURN, isThirdPartyV2CollectionDecodedUrn } from 'lib/urn'
+import { buildCatalystItemURN } from 'lib/urn'
import { makeContentFiles, computeHashes } from 'modules/deployment/contentUtils'
import { Collection } from 'modules/collection/types'
import { Item, IMAGE_PATH, THUMBNAIL_PATH, ItemType, EntityHashingType, isEmoteItemType, VIDEO_PATH } from './types'
@@ -131,8 +131,6 @@ function buildTPItemEntityMetadata(item: Item, itemHash: string, tree: MerkleDis
throw new Error('Item does not have URN')
}
- const decodedURN = decodeURN(item.urn)
-
// The order of the metadata properties can't be changed. Changing it will result in a different content hash.
const baseEntityData = {
id: item.urn,
@@ -152,7 +150,7 @@ function buildTPItemEntityMetadata(item: Item, itemHash: string, tree: MerkleDis
thumbnail: THUMBNAIL_PATH,
metrics: item.metrics,
content: item.contents,
- ...(isThirdPartyV2CollectionDecodedUrn(decodedURN) && item.mappings ? { mappings: item.mappings } : {})
+ ...(item.mappings ? { mappings: item.mappings } : {})
}
return {
diff --git a/src/modules/item/reducer.ts b/src/modules/item/reducer.ts
index 58001ae11..45397b5b0 100644
--- a/src/modules/item/reducer.ts
+++ b/src/modules/item/reducer.ts
@@ -110,15 +110,7 @@ import {
} from 'modules/thirdParty/actions'
import { toItemObject } from './utils'
import { Item, BlockchainRarity } from './types'
-import {
- buildCatalystItemURN,
- buildThirdPartyURN,
- buildThirdPartyV2URN,
- decodeURN,
- isThirdPartyCollectionDecodedUrn,
- isThirdPartyV2CollectionDecodedUrn,
- URNType
-} from 'lib/urn'
+import { buildCatalystItemURN, buildThirdPartyURN, decodeURN, isThirdPartyCollectionDecodedUrn, URNType } from 'lib/urn'
import { CLOSE_MODAL, CloseModalAction } from 'decentraland-dapps/dist/modules/modal/actions'
export type ItemPaginationData = {
@@ -517,18 +509,11 @@ export function itemReducer(state: ItemState = INITIAL_STATE, action: ItemReduce
if (item.collectionId === collection.id && item.urn) {
let newItemURN: string
const itemURN = decodeURN(item.urn)
- if (isThirdPartyCollectionDecodedUrn(collectionURN) || isThirdPartyV2CollectionDecodedUrn(collectionURN)) {
- if (!isThirdPartyCollectionDecodedUrn(itemURN) && !isThirdPartyV2CollectionDecodedUrn(itemURN)) {
+ if (isThirdPartyCollectionDecodedUrn(collectionURN)) {
+ if (!isThirdPartyCollectionDecodedUrn(itemURN)) {
throw new Error(`The item ${item.id} is not part of a third-party collection but it should be`)
}
- newItemURN = isThirdPartyCollectionDecodedUrn(collectionURN)
- ? buildThirdPartyURN(collectionURN.thirdPartyName, collectionURN.thirdPartyCollectionId, itemURN.thirdPartyTokenId)
- : buildThirdPartyV2URN(
- collectionURN.thirdPartyLinkedCollectionName,
- collectionURN.linkedCollectionNetwork,
- collectionURN.linkedCollectionContractAddress,
- itemURN.thirdPartyTokenId
- )
+ newItemURN = buildThirdPartyURN(collectionURN.thirdPartyName, collectionURN.thirdPartyCollectionId, itemURN.thirdPartyTokenId)
} else if (collectionURN.type === URNType.COLLECTIONS_V2) {
if (itemURN.type !== URNType.COLLECTIONS_V2) {
throw new Error(`The item ${item.id} is not part of a decentraland collection but it should be`)
diff --git a/src/modules/item/sagas.ts b/src/modules/item/sagas.ts
index d7b2bbefd..1f03dd10e 100644
--- a/src/modules/item/sagas.ts
+++ b/src/modules/item/sagas.ts
@@ -447,6 +447,7 @@ export function* itemSaga(legacyBuilder: LegacyBuilderAPI, builder: BuilderClien
}
yield call([legacyBuilder, 'saveItem'], item, contents)
+ yield saveItemRequest(item, contents)
yield put(saveItemSuccess(item, contents))
} catch (error) {
yield put(saveItemFailure(actionItem, actionContents, isErrorWithMessage(error) ? error.message : 'Unknown error'))
diff --git a/src/modules/item/types.ts b/src/modules/item/types.ts
index 8b21de205..42c6c49ad 100644
--- a/src/modules/item/types.ts
+++ b/src/modules/item/types.ts
@@ -1,5 +1,15 @@
import { BuiltItem, Content } from '@dcl/builder-client'
-import { BodyShape, EmoteDataADR74, Wearable, WearableCategory, Rarity, HideableWearableCategory, Mapping } from '@dcl/schemas'
+import {
+ BodyShape,
+ EmoteDataADR74,
+ Wearable,
+ WearableCategory,
+ Rarity,
+ HideableWearableCategory,
+ Mapping,
+ ContractAddress,
+ ContractNetwork
+} from '@dcl/schemas'
import { AnimationMetrics, ModelMetrics } from 'modules/models/types'
import { Cheque } from 'modules/thirdParty/types'
@@ -112,7 +122,7 @@ export type Item = Omit & {
catalystContentHash: string | null
data: T extends ItemType.WEARABLE ? WearableData : EmoteDataADR74
metrics: T extends ItemType.WEARABLE ? ModelMetrics : AnimationMetrics
- mappings: Mapping[] | null
+ mappings: Partial>> | null
}
export const isEmoteItemType = (item: Item | Item): item is Item =>
diff --git a/src/modules/thirdParty/types.ts b/src/modules/thirdParty/types.ts
index 605999304..9ac8a56bd 100644
--- a/src/modules/thirdParty/types.ts
+++ b/src/modules/thirdParty/types.ts
@@ -1,4 +1,9 @@
-import { LinkedContractProtocol } from 'lib/urn'
+export enum LinkedContractProtocol {
+ MATIC = 'matic',
+ SEPOLIA = 'sepolia',
+ AMOY = 'amoy',
+ MAINNET = 'mainnet'
+}
export type ThirdParty = {
id: string
@@ -29,8 +34,3 @@ export type Slot = {
sigS: string
sigV: number
}
-
-export enum ThirdPartyVersion {
- V1 = 1,
- V2 = 2
-}
diff --git a/src/modules/thirdParty/utils.ts b/src/modules/thirdParty/utils.ts
index 030a637b6..02446c75a 100644
--- a/src/modules/thirdParty/utils.ts
+++ b/src/modules/thirdParty/utils.ts
@@ -7,15 +7,12 @@ import { ContractData, ContractName, getContract } from 'decentraland-transactio
import { extractThirdPartyId } from 'lib/urn'
import { Collection } from 'modules/collection/types'
import { Item } from 'modules/item/types'
-import { ThirdParty, ThirdPartyVersion } from './types'
+import { ThirdParty } from './types'
export function isUserManagerOfThirdParty(address: string, thirdParty: ThirdParty): boolean {
return thirdParty.managers.map(manager => manager.toLowerCase()).includes(address.toLowerCase())
}
-export const getThirdPartyVersion = (thirdParty: ThirdParty): number =>
- thirdParty.id.split(':')[3] === 'collections-linked-wearables' ? ThirdPartyVersion.V2 : ThirdPartyVersion.V1
-
export const getThirdPartyForCollection = (thirdParties: Record, collection: Collection): ThirdParty | undefined =>
thirdParties[extractThirdPartyId(collection.urn)]
diff --git a/src/routing/locations.ts b/src/routing/locations.ts
index 3d2ceac9f..c366b6f0e 100644
--- a/src/routing/locations.ts
+++ b/src/routing/locations.ts
@@ -37,7 +37,6 @@ export const locations = {
case CollectionType.STANDARD:
return injectParams(`/collections/${collectionId}`, { tab: 'tab' }, options)
case CollectionType.THIRD_PARTY:
- case CollectionType.THIRD_PARTY_V2:
return injectParams(locations.thirdPartyCollectionDetail(collectionId), { tab: 'tab' }, options)
default:
throw new Error(`Invalid collection type ${type as unknown as string}`)