Skip to content

Commit

Permalink
feat: make NFTs unique (#515)
Browse files Browse the repository at this point in the history
  • Loading branch information
belohlavek authored and Ariel Barmat committed Jun 4, 2019
1 parent e675ab7 commit 700f2c7
Show file tree
Hide file tree
Showing 15 changed files with 137 additions and 27 deletions.
5 changes: 5 additions & 0 deletions src/components/AssetCard/AssetCard.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,8 @@
.AssetCard.is-dragging {
opacity: 0.5;
}

.AssetCard.disabled {
opacity: 0.2;
cursor: not-allowed;
}
3 changes: 3 additions & 0 deletions src/components/AssetCard/AssetCard.dnd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ export const assetSource: DragSourceSpec<Props, DragObject> = {
return {
asset: props.asset
}
},
canDrag(props) {
return !props.asset.isDisabled
}
}

Expand Down
5 changes: 4 additions & 1 deletion src/components/AssetCard/HorizontalCard/HorizontalCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@ export default class HorizontalCard extends React.PureComponent<Props> {
if (isDragging) {
classes += ' is-dragging'
}
if (asset.isDisabled) {
classes += ' disabled'
}

return (
<div className={classes}>
<img className="thumbnail" src={thumbnail} alt="" />
<img className="thumbnail" src={thumbnail} alt="" draggable={false} />
<Header size="small" className="title">
{name}
</Header>
Expand Down
5 changes: 4 additions & 1 deletion src/components/AssetCard/VerticalCard/VerticalCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,17 @@ export default class VerticalCard extends React.PureComponent<Props> {
if (asset.category === GROUND_CATEGORY) {
classes += ' ground'
}
if (asset.isDisabled) {
classes += ' disabled'
}

return (
<Popup
content={name}
position="top center"
trigger={
<div className={classes}>
<img className="thumbnail" src={thumbnail} alt="" />
<img className="thumbnail" src={thumbnail} alt="" draggable={false} />
</div>
}
hideOnScroll={true}
Expand Down
2 changes: 1 addition & 1 deletion src/components/EditorPage/ItemDragLayer/ItemDragLayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function getItemStyles(props: CollectedProps) {
class ItemDragLayer extends React.PureComponent<CollectedProps> {
render() {
const { asset } = this.props
if (!asset) return null
if (!asset || asset.isDisabled) return null
const { thumbnail } = asset

return (
Expand Down
27 changes: 16 additions & 11 deletions src/components/TopBar/TopBar.container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {
getSelectedEntityId,
isLoading,
areEntitiesOutOfBoundaries,
isReady
isReady,
getEnabledTools
} from 'modules/editor/selectors'
import { openModal } from 'modules/modal/actions'
import { setGizmo, togglePreview, toggleSidebar } from 'modules/editor/actions'
Expand All @@ -18,16 +19,20 @@ import { getCurrentMetrics } from 'modules/scene/selectors'
import { MapStateProps, MapDispatchProps, MapDispatch } from './TopBar.types'
import TopBar from './TopBar'

const mapState = (state: RootState): MapStateProps => ({
gizmo: getGizmo(state),
currentProject: getCurrentProject(state),
metrics: getCurrentMetrics(state),
selectedEntityId: getSelectedEntityId(state),
isLoading: !isReady(state) || isLoading(state),
isPreviewing: isPreviewing(state),
isSidebarOpen: isSidebarOpen(state),
areEntitiesOutOfBoundaries: areEntitiesOutOfBoundaries(state)
})
const mapState = (state: RootState): MapStateProps => {
const selectedEntityId = getSelectedEntityId(state)
return {
gizmo: getGizmo(state),
currentProject: getCurrentProject(state),
metrics: getCurrentMetrics(state),
selectedEntityId,
isLoading: !isReady(state) || isLoading(state),
isPreviewing: isPreviewing(state),
isSidebarOpen: isSidebarOpen(state),
enabledTools: getEnabledTools(selectedEntityId)(state),
areEntitiesOutOfBoundaries: areEntitiesOutOfBoundaries(state)
}
}

const mapDispatch = (dispatch: MapDispatch): MapDispatchProps => ({
onSetGizmo: gizmo => dispatch(setGizmo(gizmo)),
Expand Down
23 changes: 17 additions & 6 deletions src/components/TopBar/TopBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,18 @@ export default class TopBar extends React.PureComponent<Props> {
}

render() {
const { gizmo, currentProject, isLoading, isPreviewing, isSidebarOpen, selectedEntityId, onReset, onDelete, onDuplicate } = this.props
const {
gizmo,
currentProject,
isLoading,
isPreviewing,
isSidebarOpen,
selectedEntityId,
enabledTools,
onReset,
onDelete,
onDuplicate
} = this.props

return (
<Grid className="TopBar">
Expand All @@ -82,27 +93,27 @@ export default class TopBar extends React.PureComponent<Props> {
<Chip
icon="move"
isActive={gizmo === Gizmo.MOVE && !!selectedEntityId}
isDisabled={!selectedEntityId}
isDisabled={!enabledTools.move}
onClick={this.handleMoveMode}
/>
</ShortcutTooltip>
<ShortcutTooltip shortcut={Shortcut.ROTATE} position="bottom center" className="tool" popupClassName="top-bar-popup">
<Chip
icon="rotate"
isActive={gizmo === Gizmo.ROTATE && !!selectedEntityId}
isDisabled={!selectedEntityId}
isDisabled={!enabledTools.rotate}
onClick={this.handleRotateMode}
/>
</ShortcutTooltip>
</span>
<ShortcutTooltip shortcut={Shortcut.RESET_ITEM} position="bottom center" className="tool" popupClassName="top-bar-popup">
<Chip icon="undo" isDisabled={!selectedEntityId} onClick={onReset} />
<Chip icon="undo" isDisabled={!enabledTools.reset} onClick={onReset} />
</ShortcutTooltip>
<ShortcutTooltip shortcut={Shortcut.DUPLICATE_ITEM} position="bottom center" className="tool" popupClassName="top-bar-popup">
<Chip icon="duplicate" isDisabled={!selectedEntityId} onClick={onDuplicate} />
<Chip icon="duplicate" isDisabled={!enabledTools.duplicate} onClick={onDuplicate} />
</ShortcutTooltip>
<ShortcutTooltip shortcut={Shortcut.DELETE_ITEM} position="bottom center" className="tool" popupClassName="top-bar-popup">
<Chip icon="delete" isDisabled={!selectedEntityId} onClick={onDelete} />
<Chip icon="delete" isDisabled={!enabledTools.delete} onClick={onDelete} />
</ShortcutTooltip>
</div>
</Grid.Row>
Expand Down
2 changes: 2 additions & 0 deletions src/components/TopBar/TopBar.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export type Props = {
isLoading: boolean
isPreviewing: boolean
isSidebarOpen: boolean
enabledTools: Record<string, boolean>
areEntitiesOutOfBoundaries: boolean
onSetGizmo: typeof setGizmo
onTogglePreview: typeof togglePreview
Expand All @@ -42,6 +43,7 @@ export type MapStateProps = Pick<
| 'isPreviewing'
| 'isSidebarOpen'
| 'selectedEntityId'
| 'enabledTools'
| 'areEntitiesOutOfBoundaries'
>

Expand Down
43 changes: 43 additions & 0 deletions src/modules/asset/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { AssetState } from 'modules/asset/reducer'
import { Asset, GROUND_CATEGORY } from 'modules/asset/types'
import { ModelById } from 'decentraland-dapps/dist/lib/types'
import { COLLECTIBLE_ASSET_PACK_ID } from 'modules/ui/sidebar/utils'
import { ComponentDefinition, ComponentType } from 'modules/scene/types'
import { getComponentsByType } from 'modules/scene/selectors'
import { isNFT, isGround } from './utils'

export const getState: (state: RootState) => AssetState = state => state.asset

Expand Down Expand Up @@ -44,3 +47,43 @@ export const getCollectibleAssets = createSelector<RootState, AssetState['data']
return out
}
)

export const getDisabledAssets = createSelector<
RootState,
ComponentDefinition<ComponentType.NFTShape>[],
ComponentDefinition<ComponentType.GLTFShape>[],
ModelById<Asset>,
string[]
>(
getComponentsByType(ComponentType.NFTShape),
getComponentsByType(ComponentType.GLTFShape),
getData,
(nfts, gltfs, assets) => {
let result: string[] = []

for (let assetId in assets) {
const asset = assets[assetId]

if (isNFT(asset)) {
const component = nfts.find(nft => nft.data.url.split('/').pop() === asset.id)
if (component) {
result.push(asset.id)
}
} else if (isGround(asset)) {
const component = gltfs.find(gltf => {
const { src } = gltf.data
const assetPath = src.substring(src.indexOf('/') + 1, src.length)
if (Object.keys(asset.contents).includes(assetPath)) {
return true
}
return false
})

if (component) {
result.push(asset.id)
}
}
}
return result
}
)
1 change: 1 addition & 0 deletions src/modules/asset/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export const GROUND_CATEGORY = 'ground'

export type Asset = BaseAsset & {
assetPackId: string | null // collectibles and custom gltfs wouldn't have asset
isDisabled?: boolean
}

export type BaseAsset = AssetResource & {
Expand Down
9 changes: 9 additions & 0 deletions src/modules/asset/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Asset } from 'modules/asset/types'
import { COLLECTIBLE_ASSET_PACK_ID, CategoryName } from 'modules/ui/sidebar/utils'

export function getMappings(asset: Asset) {
const mappings: Record<string, string> = {}
Expand All @@ -10,3 +11,11 @@ export function getMappings(asset: Asset) {
}
return mappings
}

export function isNFT(asset: Asset) {
return asset.assetPackId === COLLECTIBLE_ASSET_PACK_ID
}

export function isGround(asset: Asset) {
return asset.category === CategoryName.GROUND_CATEGORY
}
18 changes: 16 additions & 2 deletions src/modules/editor/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { createSelector } from 'reselect'

import { RootState } from 'modules/common/types'
import { ComponentDefinition, ComponentType } from 'modules/scene/types'
import { getComponentByType } from 'modules/scene/selectors'
import { getComponentsByType, getEntityComponentByType } from 'modules/scene/selectors'

export const getState = (state: RootState) => state.editor
export const getGizmo = (state: RootState) => getState(state).gizmo
Expand All @@ -15,7 +15,7 @@ export const isLoading = (state: RootState) => getState(state).isLoading
export const getEntitiesOutOfBoundaries = (state: RootState) => getState(state).entitiesOutOfBoundaries
export const areEntitiesOutOfBoundaries = (state: RootState) => getState(state).entitiesOutOfBoundaries.length > 0
export const getSceneMappings = createSelector<RootState, ComponentDefinition<ComponentType.GLTFShape>[], Record<string, string>>(
getComponentByType<ComponentType.GLTFShape>(ComponentType.GLTFShape),
getComponentsByType<ComponentType.GLTFShape>(ComponentType.GLTFShape),
components =>
components.reduce<Record<string, string>>(
(mappings, component) => ({
Expand All @@ -25,3 +25,17 @@ export const getSceneMappings = createSelector<RootState, ComponentDefinition<Co
{}
)
)
export const getEnabledTools = (selectedEntityId: string | null) =>
createSelector<RootState, ComponentDefinition<ComponentType.NFTShape> | null, any>(
getEntityComponentByType(selectedEntityId, ComponentType.NFTShape),
nftShape => {
const isNFT = !!nftShape
return {
move: !!selectedEntityId,
rotate: !!selectedEntityId,
duplicate: !!selectedEntityId && !isNFT,
reset: !!selectedEntityId,
delete: !!selectedEntityId
}
}
)
2 changes: 2 additions & 0 deletions src/modules/scene/sagas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@ function* handleDuplicateItem(_: DuplicateItemAction) {
getEntityComponentByType(selectedEntityId, ComponentType.Transform)
)

if (shape && shape.type === ComponentType.NFTShape) return

if (!shape || !transform) return

const {
Expand Down
4 changes: 2 additions & 2 deletions src/modules/scene/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export const getEntityComponents = (entityId: string) =>
}
)

export const getEntityComponentByType = <T extends ComponentType>(entityId: string, type: T) =>
export const getEntityComponentByType = <T extends ComponentType>(entityId: string | null, type: T) =>
createSelector<RootState, Scene['entities'], Scene['components'], ComponentDefinition<T> | null>(
getEntities,
getComponents,
Expand Down Expand Up @@ -97,7 +97,7 @@ export const getEntityShape = <T extends ComponentType>(entityId: string) =>
}
)

export const getComponentByType = <T extends ComponentType>(type: T) => (state: RootState) => {
export const getComponentsByType = <T extends ComponentType>(type: T) => (state: RootState) => {
const scene = getCurrentScene(state)
if (!scene) return []

Expand Down
15 changes: 12 additions & 3 deletions src/modules/ui/sidebar/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { createSelector } from 'reselect'
import { RootState } from 'modules/common/types'
import { SidebarState } from 'modules/ui/sidebar/reducer'
import { Category, SidebarView } from 'modules/ui/sidebar/types'
import { getData as getAssets, isLoading as isLoadingAssets } from 'modules/asset/selectors'
import { getData as getAssets, isLoading as isLoadingAssets, getDisabledAssets } from 'modules/asset/selectors'
import { AssetState } from 'modules/asset/reducer'
import { Asset } from 'modules/asset/types'
import { AssetPackState } from 'modules/assetPack/reducer'
Expand Down Expand Up @@ -59,15 +59,17 @@ export const getSideBarCategories = createSelector<
string,
string | null,
SidebarView,
string[],
AssetState['data'],
Category[]
>(
getSelectedAssetPack,
getSearch,
getSelectedCategory,
getSidebarView,
getDisabledAssets,
getAssets,
(selectedAssetPack, search, selectedCategory, view, assets) => {
(selectedAssetPack, search, selectedCategory, view, disabledAssets, assets) => {
const categories: { [categoryName: string]: Category } = {}

let results = Object.values(assets)
Expand All @@ -93,7 +95,14 @@ export const getSideBarCategories = createSelector<
thumbnail: ''
}
}
categories[asset.category].assets.push(asset)

if (disabledAssets.includes(asset.id)) {
let newAsset = { ...asset }
newAsset.isDisabled = true
categories[asset.category].assets.push(newAsset)
} else {
categories[asset.category].assets.push(asset)
}
}

let categoryArray = Object.values(categories).map<Category>(({ name }) => {
Expand Down

1 comment on commit 700f2c7

@vercel
Copy link

@vercel vercel bot commented on 700f2c7 Jun 4, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully aliased the URL https://builder-bwncqepefr.now.sh to the following alias.

Please sign in to comment.