Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Easy import asset flow #1051

Merged
merged 16 commits into from
Jan 28, 2025
Merged
3 changes: 3 additions & 0 deletions packages/@dcl/inspector/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"typescript.tsdk": "node_modules/typescript/lib"
}
7 changes: 7 additions & 0 deletions packages/@dcl/inspector/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/@dcl/inspector/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"esbuild": "^0.18.17",
"ethereum-cryptography": "^2.1.2",
"fp-future": "^1.0.1",
"gltf-validator": "^2.0.0-dev.3.10",
"hotkeys-js": "^3.13.5",
"jest-environment-jsdom": "^29.5.0",
"jest-puppeteer": "^9.0.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@
.AssetPreview canvas {
width: 100%;
}

.AssetPreview .GltfPreview.hidden {
visibility: hidden;
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import * as React from 'react'
import { useCallback, useMemo, useState } from 'react'
import { PreviewCamera, PreviewProjection } from '@dcl/schemas'
import cx from 'classnames'
import { WearablePreview } from 'decentraland-ui'
import { IoIosImage } from 'react-icons/io'
import { AiFillSound } from 'react-icons/ai'
import { IoVideocamOutline } from 'react-icons/io5'
import { FaFile } from 'react-icons/fa'

import { isAsset as isGltf } from '../EntityInspector/GltfInspector/utils'
import { toWearableWithBlobs } from './utils'
import { Props } from './types'

Expand All @@ -14,36 +16,57 @@ const WIDTH = 300
const HEIGHT = 300

export function AssetPreview({ value, resources, onScreenshot, onLoad }: Props) {
return (
<div className="AssetPreview">
{isGltf(value.name) ? (
<GltfPreview value={value} resources={resources} onScreenshot={onScreenshot} onLoad={onLoad} />
) : value.name.endsWith('png') ? (
<PngPreview value={value} onScreenshot={onScreenshot} onLoad={onLoad} />
) : (
<IoIosImage />
)}
</div>
)
const preview = useMemo(() => {
const ext = value.name.split('.').pop()
switch (ext) {
case 'gltf':
case 'glb':
return <GltfPreview value={value} resources={resources} onScreenshot={onScreenshot} onLoad={onLoad} />
case 'png':
case 'jpg':
case 'jpeg':
return <PngPreview value={value} onScreenshot={onScreenshot} onLoad={onLoad} />
case 'mp3':
case 'wav':
case 'ogg':
return <AiFillSound />
case 'mp4':
return <IoVideocamOutline />
default:
return <FaFile />
}
}, [])

return <div className="AssetPreview">{preview}</div>
}

function GltfPreview({ value, resources, onScreenshot, onLoad }: Props) {
const handleLoad = React.useCallback(() => {
const [loading, setLoading] = useState(true)
const handleLoad = useCallback(() => {
onLoad?.()
const wp = WearablePreview.createController(value.name)
void wp.scene.getScreenshot(WIDTH, HEIGHT).then(($) => onScreenshot($))
void wp.scene.getScreenshot(WIDTH, HEIGHT).then(($) => {
setTimeout(() => {
onScreenshot($)
setLoading(false)
}, 1000) // ugly hack to avoid iframe flickering...
})
}, [onLoad])

return (
<WearablePreview
id={value.name}
blob={toWearableWithBlobs(value, resources)}
disableAutoRotate
disableBackground
projection={PreviewProjection.ORTHOGRAPHIC}
camera={PreviewCamera.STATIC}
onLoad={handleLoad}
/>
<>
<div className={cx('GltfPreview', { hidden: loading })}>
<WearablePreview
id={value.name}
blob={toWearableWithBlobs(value, resources)}
disableAutoRotate
disableBackground
projection={PreviewProjection.ORTHOGRAPHIC}
camera={PreviewCamera.STATIC}
onLoad={handleLoad}
/>
</div>
</>
)
}

Expand Down
22 changes: 13 additions & 9 deletions packages/@dcl/inspector/src/components/Assets/Assets.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
padding-left: 8px;
display: flex;
background: var(--tree-bg-color);
height: 36px;
padding: 5px 0;
position: relative;
align-items: center;
justify-content: center;
}

.Assets .Assets-buttons > div > div {
Expand Down Expand Up @@ -61,20 +63,22 @@
user-select: none;
}

.Assets .Assets-buttons > div:last-child svg {
width: 16px;
height: 16px;
.Assets .Assets-buttons > button:first-child {
margin: 3px;
position: absolute;
left: 0px;
margin-left: 8px;
display: flex;
align-items: center;
}

.Assets .Assets-buttons > div:last-child {
position: absolute;
right: 0px;
margin-right: 8px;
.Assets .Assets-buttons > button:first-child svg {
margin-right: 5px;
}

.Assets .Assets-content {
background: var(--tree-bg-color) !important;
height: calc(100% - 36px);
height: 100%;
}

.Assets .Assets-content.Hide {
Expand Down
39 changes: 23 additions & 16 deletions packages/@dcl/inspector/src/components/Assets/Assets.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback } from 'react'
import React, { useCallback, useRef } from 'react'
import cx from 'classnames'
import { MdImageSearch } from 'react-icons/md'
import { HiOutlinePlus } from 'react-icons/hi'
Expand All @@ -17,6 +17,8 @@ import { CustomAssets } from '../CustomAssets'
import { selectCustomAssets } from '../../redux/app'
import { RenameAsset } from '../RenameAsset'
import { CreateCustomAsset } from '../CreateCustomAsset'
import { InputRef } from '../FileInput/FileInput'
import { Button } from '../Button'

import './Assets.css'

Expand All @@ -31,6 +33,7 @@ function Assets({ isAssetsPanelCollapsed }: { isAssetsPanelCollapsed: boolean })
const dispatch = useAppDispatch()
const tab = useAppSelector(getSelectedAssetsTab)
const customAssets = useAppSelector(selectCustomAssets)
const inputRef = useRef<InputRef>(null)

const handleTabClick = useCallback(
(tab: AssetsTab) => () => {
Expand All @@ -47,9 +50,17 @@ function Assets({ isAssetsPanelCollapsed }: { isAssetsPanelCollapsed: boolean })
const assetToRename = useAppSelector(selectAssetToRename)
const stagedCustomAsset = useAppSelector(selectStagedCustomAsset)

const handleImportClick = useCallback(() => {
inputRef.current?.onClick()
}, [inputRef])

return (
<div className="Assets">
<div className="Assets-buttons">
<Button onClick={handleImportClick}>
<HiOutlinePlus />
IMPORT ASSETS
</Button>
<div className="tab" onClick={handleTabClick(AssetsTab.FileSystem)} data-test-id={AssetsTab.FileSystem}>
<div className={cx({ underlined: tab === AssetsTab.FileSystem })}>
<FolderOpen />
Expand All @@ -70,22 +81,18 @@ function Assets({ isAssetsPanelCollapsed }: { isAssetsPanelCollapsed: boolean })
<span>ASSET PACKS</span>
</div>
</div>
<div className="tab" onClick={handleTabClick(AssetsTab.Import)} data-test-id={AssetsTab.Import}>
<div>
<HiOutlinePlus />
</div>
</div>
</div>
<div className={cx('Assets-content', { Hide: isAssetsPanelCollapsed })}>
{tab === AssetsTab.AssetsPack && <AssetsCatalog catalog={filteredCatalog} />}
{tab === AssetsTab.FileSystem && <ProjectAssetExplorer />}
{tab === AssetsTab.Import && <ImportAsset onSave={handleTabClick(AssetsTab.FileSystem)} />}
{tab === AssetsTab.CustomAssets && <CustomAssets />}
{tab === AssetsTab.RenameAsset && assetToRename && (
<RenameAsset assetId={assetToRename.id} currentName={assetToRename.name} />
)}
{tab === AssetsTab.CreateCustomAsset && stagedCustomAsset && <CreateCustomAsset />}
</div>
<ImportAsset onSave={handleTabClick(AssetsTab.FileSystem)} ref={inputRef}>
<div className={cx('Assets-content', { Hide: isAssetsPanelCollapsed })}>
{tab === AssetsTab.AssetsPack && <AssetsCatalog catalog={filteredCatalog} />}
{tab === AssetsTab.FileSystem && <ProjectAssetExplorer />}
{tab === AssetsTab.CustomAssets && <CustomAssets />}
{tab === AssetsTab.RenameAsset && assetToRename && (
<RenameAsset assetId={assetToRename.id} currentName={assetToRename.name} />
)}
{tab === AssetsTab.CreateCustomAsset && stagedCustomAsset && <CreateCustomAsset />}
</div>
</ImportAsset>
</div>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'

import { AssetPack } from '../../lib/logic/catalog'
import { analytics, Event } from '../../lib/logic/analytics'
import { useAppDispatch } from '../../redux/hooks'
import { selectAssetsTab } from '../../redux/ui'
import { AssetsTab } from '../../redux/ui/types'

import { Header } from './Header'
import { Themes } from './Themes'
Expand All @@ -16,16 +13,11 @@ import { Props } from './types'
import './AssetsCatalog.css'

const AssetsCatalog: React.FC<Props> = ({ catalog }) => {
const dispatch = useAppDispatch()
const [selectedTheme, setSelectedTheme] = useState<AssetPack>()
const [search, setSearch] = useState<string>('')

const handleThemeChange = useCallback((value?: AssetPack) => setSelectedTheme(value), [setSelectedTheme])

const handleUploadAsset = useCallback(() => {
dispatch(selectAssetsTab({ tab: AssetsTab.Import }))
}, [])

const handleSearchAssets = useCallback(
(value: string) => {
setSearch(value)
Expand Down Expand Up @@ -63,8 +55,8 @@ const AssetsCatalog: React.FC<Props> = ({ catalog }) => {
}, [search, filteredCatalog])

const renderEmptySearch = useCallback(() => {
const ctaMethod = selectedTheme ? handleThemeChange : handleUploadAsset
const ctaText = selectedTheme ? 'search all categories' : 'upload your own asset'
const ctaMethod = selectedTheme ? handleThemeChange : () => undefined
const ctaText = selectedTheme ? 'search all categories' : 'upload your own asset by drag & drop'
return (
<div className="empty-search">
<span>No results for '{search}'.</span>
Expand All @@ -77,7 +69,7 @@ const AssetsCatalog: React.FC<Props> = ({ catalog }) => {
</span>
</div>
)
}, [search, selectedTheme, handleThemeChange, handleUploadAsset])
}, [search, selectedTheme, handleThemeChange])

const renderAssets = useCallback(() => {
if (filteredCatalog.length > 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,15 @@
}

.CreateCustomAsset .preview-container .AssetPreview {
display: none;
}

.CreateCustomAsset .preview-container .thumbnail {
width: 100%;
height: 100%;
background-color: var(--background-gray);
background-size: cover;
background-position: center;
}

.CreateCustomAsset .loader-container {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,12 @@ const CreateCustomAsset: React.FC = () => {
<div className="file-container">
{previewFile && resources !== null ? (
<div className="preview-container">
{isGeneratingThumbnail && (
{isGeneratingThumbnail && !thumbnail ? (
<div className="loader-container">
<Loader active size="small" />
</div>
) : (
<div className="thumbnail" style={{ backgroundImage: `url(${thumbnail})` }}></div>
)}
<AssetPreview value={previewFile} resources={resources} onScreenshot={handleScreenshot} />
</div>
Expand Down
Loading
Loading