Skip to content

Commit

Permalink
feat: persistent try on (decentraland#576)
Browse files Browse the repository at this point in the history
* feat: use first fashion pose

* feat: make state persistent

* fix: remove bodyShape

* chore: analytics

* fix: only enable try on if has representation
  • Loading branch information
cazala authored Mar 14, 2022
1 parent f3e1551 commit be2a509
Show file tree
Hide file tree
Showing 9 changed files with 103 additions and 25 deletions.
9 changes: 7 additions & 2 deletions webapp/src/components/AssetImage/AssetImage.container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import { Avatar } from '@dcl/schemas'
import { connect } from 'react-redux'
import { RootState } from '../../modules/reducer'
import { getWallet } from '../../modules/wallet/selectors'
import { getIsTryingOn } from '../../modules/ui/preview/selectors'
import {
MapStateProps,
MapDispatchProps,
MapDispatch
} from './AssetImage.types'
import AssetImage from './AssetImage'
import { setIsTryingOn } from '../../modules/ui/preview/actions'

const mapState = (state: RootState): MapStateProps => {
const profiles = getProfiles(state)
Expand All @@ -19,10 +21,13 @@ const mapState = (state: RootState): MapStateProps => {
avatar = profile.avatars[0]
}
return {
avatar
avatar,
isTryingOn: getIsTryingOn(state)
}
}

const mapDispatch = (_dispatch: MapDispatch): MapDispatchProps => ({})
const mapDispatch = (dispatch: MapDispatch): MapDispatchProps => ({
onSetIsTryingOn: value => dispatch(setIsTryingOn(value))
})

export default connect(mapState, mapDispatch)(AssetImage)
51 changes: 33 additions & 18 deletions webapp/src/components/AssetImage/AssetImage.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React, { useCallback, useMemo, useState } from 'react'
import React, { useCallback, useEffect, useMemo, useState } from 'react'
import { LazyImage } from 'react-lazy-images'
import classNames from 'classnames'
import { BodyShape, NFTCategory, Rarity } from '@dcl/schemas'
import { T, t } from 'decentraland-dapps/dist/modules/translation/utils'
import { getAnalytics } from 'decentraland-dapps/dist/modules/analytics/utils'
import {
AvatarEmote,
Button,
Expand Down Expand Up @@ -59,15 +60,16 @@ const AssetImage = (props: Props) => {
zoom,
isSmall,
showMonospace,
avatar
avatar,
isTryingOn,
onSetIsTryingOn
} = props
const { parcel, estate, wearable, ens } = asset.data

const [isLoadingWearablePreview, setIsLoadingWearablePreview] = useState(
isDraggable
)
const [wearablePreviewError, setWearablePreviewError] = useState(false)
const [isTrying, setIsTrying] = useState(false)
const handleLoad = useCallback(() => {
setIsLoadingWearablePreview(false)
setWearablePreviewError(false)
Expand All @@ -78,22 +80,34 @@ const AssetImage = (props: Props) => {
setIsLoadingWearablePreview(false)
}, [])
const handleTryOut = useCallback(() => {
if (!isTrying) {
setIsTrying(true)
if (!isTryingOn) {
onSetIsTryingOn(true)
setIsLoadingWearablePreview(true)
}
}, [isTrying])
}, [isTryingOn, onSetIsTryingOn])
const handleShowWearable = useCallback(() => {
if (isTrying) {
setIsTrying(false)
if (isTryingOn) {
onSetIsTryingOn(false)
setIsLoadingWearablePreview(true)
}
}, [isTrying])
}, [isTryingOn, onSetIsTryingOn])

const estateSelection = useMemo(() => (estate ? getSelection(estate) : []), [
estate
])

const [isTracked, setIsTracked] = useState(false)

useEffect(() => {
const isPreview = asset.category === NFTCategory.WEARABLE && isDraggable
if (!isTracked && isPreview) {
getAnalytics().track('Init Preview', {
mode: isTryingOn ? 'avatar' : 'wearable'
})
setIsTracked(true)
}
}, []) // eslint-disable-line

switch (asset.category) {
case NFTCategory.PARCEL: {
const x = +parcel!.x
Expand Down Expand Up @@ -137,7 +151,6 @@ const AssetImage = (props: Props) => {
let tokenId: string | undefined
let skin
let hair
let bodyShape: 'male' | 'female' = 'male'
if ('itemId' in asset && asset.itemId) {
itemId = asset.itemId
} else if ('tokenId' in asset && asset.tokenId) {
Expand All @@ -146,9 +159,6 @@ const AssetImage = (props: Props) => {
if (avatar) {
skin = colorToHex(avatar.avatar.skin.color)
hair = colorToHex(avatar.avatar.hair.color)
bodyShape = avatar.avatar.bodyShape.toLowerCase().includes('female')
? 'female'
: 'male'
}

const hasRepresentation = avatar
Expand All @@ -165,19 +175,24 @@ const AssetImage = (props: Props) => {
? t('wearable_preview.missing_representation_error.male')
: t('wearable_preview.missing_representation_error.female')

const isTryingOnEnabled = isTryingOn && hasRepresentation

wearablePreview = (
<>
<WearablePreview
contractAddress={asset.contractAddress}
itemId={itemId}
tokenId={tokenId}
profile={
isTrying ? (avatar ? avatar.ethAddress : 'default') : undefined
isTryingOnEnabled
? avatar
? avatar.ethAddress
: 'default'
: undefined
}
skin={skin}
hair={hair}
bodyShape={bodyShape}
emote={AvatarEmote.FASHION_2}
emote={AvatarEmote.FASHION}
onLoad={handleLoad}
onError={handleError}
dev={isDev}
Expand Down Expand Up @@ -210,7 +225,7 @@ const AssetImage = (props: Props) => {
'preview-toggle',
'preview-toggle-wearable',
{
'is-active': !isTrying
'is-active': !isTryingOnEnabled
}
)}
onClick={handleShowWearable}
Expand All @@ -228,7 +243,7 @@ const AssetImage = (props: Props) => {
'preview-toggle',
'preview-toggle-avatar',
{
'is-active': isTrying,
'is-active': isTryingOnEnabled,
'is-disabled': !hasRepresentation
}
)}
Expand Down
12 changes: 9 additions & 3 deletions webapp/src/components/AssetImage/AssetImage.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import { Dispatch } from 'redux'
import { Avatar } from '@dcl/schemas'
import { Item } from '@dcl/schemas'
import { NFT } from '../../modules/nft/types'
import {
setIsTryingOn,
SetIsTryingOnAction
} from '../../modules/ui/preview/actions'

export type Props = {
asset: NFT | Item
Expand All @@ -13,8 +17,10 @@ export type Props = {
isSmall?: boolean
showMonospace?: boolean
avatar?: Avatar
isTryingOn: boolean
onSetIsTryingOn: typeof setIsTryingOn
}

export type MapStateProps = Pick<Props, 'avatar'>
export type MapDispatchProps = {}
export type MapDispatch = Dispatch
export type MapStateProps = Pick<Props, 'avatar' | 'isTryingOn'>
export type MapDispatchProps = Pick<Props, 'onSetIsTryingOn'>
export type MapDispatch = Dispatch<SetIsTryingOnAction>
9 changes: 9 additions & 0 deletions webapp/src/modules/analytics/track.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import {
FetchItemsSuccessAction,
FETCH_ITEMS_SUCCESS
} from '../item/actions'
import { SetIsTryingOnAction, SET_IS_TRYING_ON } from '../ui/preview/actions'
import { fromWei } from 'web3x/utils'

function track<T extends PayloadAction<string, any>>(
Expand Down Expand Up @@ -202,3 +203,11 @@ track<BuyItemSuccessAction>(BUY_ITEM_SUCCESS, 'Buy Item', ({ payload }) => ({
price: Number(fromWei(payload.item.price, 'ether')),
data: payload.item.data
}))

track<SetIsTryingOnAction>(
SET_IS_TRYING_ON,
'Toggle Preview Mode',
({ payload }) => ({
mode: payload.value ? 'avatar' : 'wearable'
})
)
5 changes: 4 additions & 1 deletion webapp/src/modules/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { fetchTilesRequest } from './tile/actions'
import { ARCHIVE_BID, UNARCHIVE_BID } from './bid/actions'
import { isDevelopment } from '../lib/environment'
import { GENERATE_IDENTITY_SUCCESS } from './identity/actions'
import { SET_IS_TRYING_ON } from './ui/preview/actions'

export const history = require('history').createBrowserHistory()

Expand Down Expand Up @@ -43,13 +44,15 @@ export function initStore() {
storageKey: 'marketplace-v2', // this is the key used to save the state in localStorage (required)
paths: [
['ui', 'archivedBidIds'],
['ui', 'preview', 'isTryingOn'],
['identity', 'data']
], // array of paths from state to be persisted (optional)
actions: [
CLEAR_TRANSACTIONS,
ARCHIVE_BID,
UNARCHIVE_BID,
GENERATE_IDENTITY_SUCCESS
GENERATE_IDENTITY_SUCCESS,
SET_IS_TRYING_ON
], // array of actions types that will trigger a SAVE (optional)
migrations: {} // migration object that will migrate your localstorage (optional)
})
Expand Down
6 changes: 6 additions & 0 deletions webapp/src/modules/ui/preview/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { action } from 'typesafe-actions'

export const SET_IS_TRYING_ON = 'Set is trying on'
export const setIsTryingOn = (value: boolean) =>
action(SET_IS_TRYING_ON, { value })
export type SetIsTryingOnAction = ReturnType<typeof setIsTryingOn>
27 changes: 27 additions & 0 deletions webapp/src/modules/ui/preview/reducer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { SetIsTryingOnAction, SET_IS_TRYING_ON } from './actions'

export type PreviewState = {
isTryingOn: boolean
}

const INITIAL_STATE: PreviewState = {
isTryingOn: false
}

type PreviewReducerAction = SetIsTryingOnAction

export function previewReducer(
state = INITIAL_STATE,
action: PreviewReducerAction
): PreviewState {
switch (action.type) {
case SET_IS_TRYING_ON: {
return {
...state,
isTryingOn: action.payload.value
}
}
default:
return state
}
}
4 changes: 4 additions & 0 deletions webapp/src/modules/ui/preview/selectors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { RootState } from '../../reducer'

export const getState = (state: RootState) => state.ui.preview
export const getIsTryingOn = (state: RootState) => getState(state).isTryingOn
5 changes: 4 additions & 1 deletion webapp/src/modules/ui/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@ import { combineReducers } from 'redux'
import { assetReducer as asset, AssetUIState } from './asset/reducer'
import { nftReducer as nft, NFTUIState } from './nft/reducer'
import { browseReducer as browse, BrowseUIState } from './browse/reducer'
import { previewReducer as preview, PreviewState } from './preview/reducer'

export type UIState = {
asset: AssetUIState
nft: NFTUIState
browse: BrowseUIState
preview: PreviewState
}

export const uiReducer = combineReducers({
asset,
nft,
browse
browse,
preview
})

0 comments on commit be2a509

Please sign in to comment.