Skip to content

Commit 2da3140

Browse files
committed
add renaming assets before import
1 parent a09b31d commit 2da3140

File tree

7 files changed

+63
-25
lines changed

7 files changed

+63
-25
lines changed

packages/@dcl/inspector/src/components/ImportAsset/ImportAsset.tsx

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ import React, { PropsWithChildren, useCallback, useEffect, useState } from 'reac
22
import cx from 'classnames'
33
import { HiOutlineUpload } from 'react-icons/hi'
44

5-
// import { removeBasePath } from '../../lib/logic/remove-base-path'
5+
import { removeBasePath } from '../../lib/logic/remove-base-path'
66
import { DIRECTORY, transformBase64ResourceToBinary, withAssetDir } from '../../lib/data-layer/host/fs-utils'
77
import { importAsset, saveThumbnail } from '../../redux/data-layer'
88
import { useAppDispatch, useAppSelector } from '../../redux/hooks'
9-
import { selectUploadFile, updateUploadFile } from '../../redux/app'
9+
import { selectAssetCatalog, selectUploadFile, updateUploadFile } from '../../redux/app'
1010

1111
import FileInput from '../FileInput'
1212
import { Modal } from '../Modal'
@@ -19,9 +19,9 @@ import {
1919
ACCEPTED_FILE_TYPES,
2020
formatFileName,
2121
convertAssetToBinary,
22-
determineAssetType
22+
buildAssetPath
2323
} from './utils'
24-
import { Asset, isModelAsset } from './types'
24+
import { Asset } from './types'
2525

2626
import './ImportAsset.css'
2727
import { Error } from './Error'
@@ -34,13 +34,13 @@ interface PropTypes {
3434

3535
const ImportAsset = React.forwardRef<InputRef, PropsWithChildren<PropTypes>>(({ onSave, children }, inputRef) => {
3636
const dispatch = useAppDispatch()
37-
// const catalog = useAppSelector(selectAssetCatalog)
37+
const catalog = useAppSelector(selectAssetCatalog)
3838
const uploadFile = useAppSelector(selectUploadFile)
3939

4040
const [files, setFiles] = useState<Asset[]>([])
4141

4242
const [isHover, setIsHover] = useState(false)
43-
// const { basePath, assets } = catalog ?? { basePath: '', assets: [] }
43+
const { basePath, assets } = catalog ?? { basePath: '', assets: [] }
4444

4545
useEffect(() => {
4646
const isValidFile = uploadFile && 'name' in uploadFile
@@ -71,8 +71,7 @@ const ImportAsset = React.forwardRef<InputRef, PropsWithChildren<PropTypes>>(({
7171
// times. This can be improved by doing all the process once...
7272
for (const asset of assets) {
7373
const content = await convertAssetToBinary(asset)
74-
const classification = determineAssetType(asset.extension)
75-
const assetPackageName = isModelAsset(asset) ? `${classification}/${asset.name}` : classification
74+
const assetPackageName = buildAssetPath(asset)
7675

7776
dispatch(
7877
importAsset({
@@ -106,15 +105,17 @@ const ImportAsset = React.forwardRef<InputRef, PropsWithChildren<PropTypes>>(({
106105
[uploadFile]
107106
)
108107

109-
// const isNameUnique = useCallback((name: string, ext: string) => {
110-
// return !assets.find((asset) => {
111-
// const [packageName, otherAssetName] = removeBasePath(basePath, asset.path).split('/')
112-
// if (packageName === 'builder') return false
113-
// return otherAssetName?.toLocaleLowerCase() === name?.toLocaleLowerCase() + '.' + ext
114-
// })
115-
// }, [])
116-
117-
// const isNameRepeated = !isNameUnique(assetName, assetExtension)
108+
const validateName = useCallback(
109+
(asset: Asset, fileName: string) => {
110+
return !assets.find(($) => {
111+
const [packageName, ...otherAssetName] = removeBasePath(basePath, $.path).split('/')
112+
if (packageName === 'builder') return false
113+
const assetPath = buildAssetPath(asset)
114+
return otherAssetName.join('/') === `${assetPath}/${fileName}`
115+
})
116+
},
117+
[assets]
118+
)
118119

119120
return (
120121
<div className={cx('ImportAsset', { ImportAssetHover: isHover })}>
@@ -144,7 +145,7 @@ const ImportAsset = React.forwardRef<InputRef, PropsWithChildren<PropTypes>>(({
144145
>
145146
<h2>Import Assets</h2>
146147
{assetsAreValid(files) ? (
147-
<Slider assets={files} onSubmit={handleImport} />
148+
<Slider assets={files} onSubmit={handleImport} isNameValid={validateName} />
148149
) : (
149150
<Error assets={files} onSubmit={handleCloseModal} />
150151
)}

packages/@dcl/inspector/src/components/ImportAsset/Slider/Slider.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@
5454
height: 60%;
5555
}
5656

57+
.Slider .asset .name-error {
58+
font-size: 12px;
59+
color: var(--primary-main)
60+
}
61+
5762
.Slider .asset .size {
5863
text-align: center;
5964
font-size: 12px;

packages/@dcl/inspector/src/components/ImportAsset/Slider/Slider.tsx

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ import { AssetPreview } from '../../AssetPreview'
66
import { Button } from '../../Button'
77
import { Input } from '../../Input'
88

9-
import { getAssetSize, getAssetResources, determineAssetType } from '../utils'
9+
import { getAssetSize, getAssetResources, determineAssetType, formatFileName } from '../utils'
1010

1111
import { Asset } from '../types'
1212
import { PropTypes, Thumbnails } from './types'
1313

1414
import './Slider.css'
1515

16-
export function Slider({ assets, onSubmit }: PropTypes) {
16+
export function Slider({ assets, onSubmit, isNameValid }: PropTypes) {
1717
const [value, setValue] = useState(assets)
1818
const [slide, setSlide] = useState(0)
1919
const [screenshots, setScreenshots] = useState<Thumbnails>({})
@@ -70,6 +70,30 @@ export function Slider({ assets, onSubmit }: PropTypes) {
7070
return neededScreenshots.length === Object.keys(screenshots).length
7171
}, [value, screenshots])
7272

73+
const invalidNames = useMemo(() => {
74+
const all = new Set<string>()
75+
const invalid = new Set<string>()
76+
77+
for (const asset of value) {
78+
const name = formatFileName(asset)
79+
if (all.has(name) || !isNameValid(asset, name)) {
80+
invalid.add(name)
81+
} else {
82+
all.add(name)
83+
}
84+
}
85+
86+
return invalid
87+
}, [value])
88+
89+
const isNameUnique = useCallback(
90+
(asset: Asset) => {
91+
const name = formatFileName(asset)
92+
return !invalidNames.has(name)
93+
},
94+
[invalidNames]
95+
)
96+
7397
if (!value.length) return null
7498

7599
return (
@@ -87,6 +111,7 @@ export function Slider({ assets, onSubmit }: PropTypes) {
87111
<div>
88112
<AssetPreview value={$.blob} resources={getAssetResources($)} onScreenshot={handleScreenshot($)} />
89113
<Input value={$.name} onChange={handleNameChange(i)} />
114+
{!isNameUnique($) && <span className="name-error">Filename already exists</span>}
90115
</div>
91116
<span className="size">{getAssetSize($)}</span>
92117
</div>
@@ -98,7 +123,7 @@ export function Slider({ assets, onSubmit }: PropTypes) {
98123
</span>
99124
)}
100125
</div>
101-
<Button type="danger" size="big" onClick={handleSubmit} disabled={!allScreenshotsTaken}>
126+
<Button type="danger" size="big" onClick={handleSubmit} disabled={!allScreenshotsTaken || invalidNames.size > 0}>
102127
{importText}
103128
</Button>
104129
</div>

packages/@dcl/inspector/src/components/ImportAsset/Slider/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { Asset } from '../types'
22

33
export type PropTypes = {
44
assets: Asset[]
5-
onSubmit: (assets: Asset[]) => void
5+
onSubmit(assets: Asset[]): void
6+
isNameValid(asset: Asset, newName: string): boolean
67
}
78

89
export type Thumbnails = Record<string, string>

packages/@dcl/inspector/src/components/ImportAsset/utils.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,6 @@ const IGNORED_ERROR_CODES = [
121121
async function getGltf(file: File, getExternalResource: (uri: string) => Promise<Uint8Array>): Promise<Gltf> {
122122
try {
123123
const buffer = await file.arrayBuffer()
124-
// const resourceMap = new Map([...model.buffers, ...model.images].map(($) => [$.blob.name, $.blob]))
125124
const result: Gltf = await validator.validateBytes(new Uint8Array(buffer), {
126125
ignoredIssues: IGNORED_ERROR_CODES,
127126
externalResourceFunction: getExternalResource
@@ -145,7 +144,6 @@ async function validateGltf(gltf: Gltf): Promise<void> {
145144
}
146145
}
147146

148-
// Utility functions
149147
export function normalizeFileName(fileName: string): string {
150148
return fileName.trim().replace(/\s+/g, '_').toLowerCase()
151149
}
@@ -316,3 +314,9 @@ export function determineAssetType(extension: string): AssetType {
316314
return 'Other'
317315
}
318316
}
317+
318+
export function buildAssetPath(asset: Asset): string {
319+
const classification = determineAssetType(asset.extension)
320+
const assetPath = isModelAsset(asset) ? `${classification}/${asset.name}` : classification
321+
return assetPath
322+
}

packages/@dcl/inspector/src/components/Input/Input.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const getRolesFromTarget = (target: HTMLElement | null): Roles => {
1717
}
1818
}
1919

20-
const Input = ({ value, onCancel, onSubmit, onChange, onBlur, placeholder }: PropTypes) => {
20+
const Input = ({ value, onCancel, onSubmit, onChange, onBlur, placeholder, disabled }: PropTypes) => {
2121
const ref = useRef<HTMLInputElement>(null)
2222
const [stateValue, setStateValue] = useState(value)
2323

@@ -71,6 +71,7 @@ const Input = ({ value, onCancel, onSubmit, onChange, onBlur, placeholder }: Pro
7171
placeholder={placeholder}
7272
value={stateValue}
7373
onChange={handleTextChange}
74+
disabled={disabled}
7475
/>
7576
)
7677
}

packages/@dcl/inspector/src/components/Input/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export interface PropTypes {
22
value: string
33
placeholder?: string
4+
disabled?: boolean
45
onChange?: (value: string) => void
56
onCancel?: () => void
67
onSubmit?: (newValue: string) => void

0 commit comments

Comments
 (0)