better handling of missing poses

This commit is contained in:
Matt Dunn-Rankin 2020-05-02 22:59:30 -07:00
parent 2300fe4fbe
commit 5ea717c391
2 changed files with 87 additions and 48 deletions

View file

@ -25,14 +25,20 @@ function OutfitPreview({ outfitState }) {
); );
} }
return <OutfitLayers loading={loading} visibleLayers={visibleLayers} />; return (
<OutfitLayers
loading={loading}
visibleLayers={visibleLayers}
doAnimations
/>
);
} }
/** /**
* OutfitLayers is the raw UI component for rendering outfit layers. It's * OutfitLayers is the raw UI component for rendering outfit layers. It's
* used both in the main outfit preview, and in other minor UIs! * used both in the main outfit preview, and in other minor UIs!
*/ */
export function OutfitLayers({ loading, visibleLayers }) { export function OutfitLayers({ loading, visibleLayers, doAnimations = false }) {
return ( return (
<Box pos="relative" height="100%" width="100%"> <Box pos="relative" height="100%" width="100%">
<TransitionGroup> <TransitionGroup>
@ -41,6 +47,7 @@ export function OutfitLayers({ loading, visibleLayers }) {
// happens here, when the layer exits the DOM. // happens here, when the layer exits the DOM.
<CSSTransition <CSSTransition
key={layer.id} key={layer.id}
exit={doAnimations}
classNames={css` classNames={css`
&-exit { &-exit {
opacity: 1; opacity: 1;
@ -62,14 +69,17 @@ export function OutfitLayers({ loading, visibleLayers }) {
// We manage the fade-in and fade-out separately! The fade-in // We manage the fade-in and fade-out separately! The fade-in
// happens here, when the <Image> finishes preloading and // happens here, when the <Image> finishes preloading and
// applies the src to the underlying <img>. // applies the src to the underlying <img>.
className={css` className={
opacity: 0.01; doAnimations &&
css`
opacity: 0.01;
&[src] { &[src] {
opacity: 1; opacity: 1;
transition: opacity 0.2s; transition: opacity 0.2s;
} }
`} `
}
// This sets up the cache to not need to reload images during // This sets up the cache to not need to reload images during
// download! // download!
// TODO: Re-enable this once we get our change into Chakra // TODO: Re-enable this once we get our change into Chakra

View file

@ -46,7 +46,8 @@ function PosePicker({
} }
// If there's only one pose anyway, don't bother showing a picker! // If there's only one pose anyway, don't bother showing a picker!
const numAvailablePoses = Object.values(poses).filter((p) => p).length; const numAvailablePoses = Object.values(poses).filter((p) => p.isAvailable)
.length;
if (numAvailablePoses <= 1) { if (numAvailablePoses <= 1) {
return null; return null;
} }
@ -206,15 +207,15 @@ const GENDER_PRESENTATION_STRINGS = {
function PoseButton({ pose, onChange, inputRef }) { function PoseButton({ pose, onChange, inputRef }) {
const theme = useTheme(); const theme = useTheme();
if (!pose) {
return null;
}
const genderPresentationStr = const genderPresentationStr =
GENDER_PRESENTATION_STRINGS[pose.genderPresentation]; GENDER_PRESENTATION_STRINGS[pose.genderPresentation];
const emotionStr = EMOTION_STRINGS[pose.emotion]; const emotionStr = EMOTION_STRINGS[pose.emotion];
let label = `${emotionStr} and ${genderPresentationStr}`;
if (!pose.isAvailable) {
label += ` (not modeled yet)`;
}
return ( return (
<Box <Box
as="label" as="label"
@ -228,24 +229,28 @@ function PoseButton({ pose, onChange, inputRef }) {
<VisuallyHidden <VisuallyHidden
as="input" as="input"
type="radio" type="radio"
aria-label={`${emotionStr} and ${genderPresentationStr}`} aria-label={label}
name="pose" name="pose"
value={`${pose.emotion}-${pose.genderPresentation}`} value={`${pose.emotion}-${pose.genderPresentation}`}
checked={pose.isSelected} checked={pose.isSelected}
disabled={!pose.isAvailable}
onChange={onChange} onChange={onChange}
ref={inputRef || null} ref={inputRef || null}
/> />
<Box <Box
aria-hidden
rounded="full" rounded="full"
boxShadow="md" boxShadow="md"
overflow="hidden" overflow="hidden"
width="50px" width="50px"
height="50px" height="50px"
title={ title={
// A lil debug output, so that we can quickly identify glitched pose.isAvailable
// PetStates and manually mark them as glitched! ? // A lil debug output, so that we can quickly identify glitched
window.location.hostname.includes("localhost") && // PetStates and manually mark them as glitched!
`#${pose.petStateId}` window.location.hostname.includes("localhost") &&
`#${pose.petStateId}`
: "Not modeled yet"
} }
position="relative" position="relative"
className={css` className={css`
@ -267,29 +272,50 @@ function PoseButton({ pose, onChange, inputRef }) {
left="0" left="0"
right="0" right="0"
zIndex="2" zIndex="2"
className={css` className={cx(
border: 0px solid ${theme.colors.green["600"]}; css`
transition: border-width 0.2s; border: 0px solid ${theme.colors.green["600"]};
transition: border-width 0.2s;
input:checked + * & { &.not-available {
border-width: 1px; border-color: ${theme.colors.gray["500"]};
} border-width: 1px;
}
input:focus + * & { input:checked + * & {
border-width: 3px; border-width: 1px;
} }
`}
input:focus + * & {
border-width: 3px;
}
`,
!pose.isAvailable && "not-available"
)}
/> />
<Box {pose.isAvailable ? (
width="50px" <Box
height="50px" width="50px"
transform={ height="50px"
transformsBySpeciesId[pose.speciesId] || transform={
transformsBySpeciesId.default transformsBySpeciesId[pose.speciesId] ||
} transformsBySpeciesId.default
> }
<OutfitLayers visibleLayers={getVisibleLayers(pose, [])} /> >
</Box> <OutfitLayers visibleLayers={getVisibleLayers(pose, [])} />
</Box>
) : (
<Flex align="center" justify="center">
<Box
fontFamily="Delicious"
fontSize="3xl"
fontWeight="900"
color="gray.600"
>
?
</Box>
</Flex>
)}
</Box> </Box>
</Box> </Box>
); );
@ -320,15 +346,18 @@ function usePoses(outfitState) {
); );
const petAppearances = data?.petAppearances || []; const petAppearances = data?.petAppearances || [];
const buildPose = (e, gp) => ({ const buildPose = (e, gp) => {
...petAppearances.find( const appearance = petAppearances.find(
(pa) => pa.emotion === e && pa.genderPresentation === gp (pa) => pa.emotion === e && pa.genderPresentation === gp
), );
speciesId, return {
isSelected: ...appearance,
outfitState.emotion === e && outfitState.genderPresentation === gp, speciesId,
}); isAvailable: Boolean(appearance),
console.log(outfitState.emotion, outfitState.genderPresentation, outfitState); isSelected:
outfitState.emotion === e && outfitState.genderPresentation === gp,
};
};
const poses = { const poses = {
happyMasc: buildPose("HAPPY", "MASCULINE"), happyMasc: buildPose("HAPPY", "MASCULINE"),