Compare commits
3 commits
578528f468
...
cd136aa6a5
Author | SHA1 | Date | |
---|---|---|---|
cd136aa6a5 | |||
09846a4516 | |||
fc71f5b5a5 |
7 changed files with 72 additions and 30 deletions
|
@ -77,20 +77,22 @@ function ItemsPanel({ outfitState, outfitSaving, loading, dispatchToOutfit }) {
|
||||||
itemCount={outfitState.allItemIds.length}
|
itemCount={outfitState.allItemIds.length}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<TransitionGroup component={null}>
|
<>
|
||||||
{zonesAndItems.map(({ zoneId, zoneLabel, items }) => (
|
<TransitionGroup component={null}>
|
||||||
<CSSTransition
|
{zonesAndItems.map(({ zoneId, zoneLabel, items }) => (
|
||||||
key={zoneId}
|
<CSSTransition
|
||||||
{...fadeOutAndRollUpTransition(css)}
|
key={zoneId}
|
||||||
>
|
{...fadeOutAndRollUpTransition(css)}
|
||||||
<ItemZoneGroup
|
>
|
||||||
zoneLabel={zoneLabel}
|
<ItemZoneGroup
|
||||||
items={items}
|
zoneLabel={zoneLabel}
|
||||||
outfitState={outfitState}
|
items={items}
|
||||||
dispatchToOutfit={dispatchToOutfit}
|
outfitState={outfitState}
|
||||||
/>
|
dispatchToOutfit={dispatchToOutfit}
|
||||||
</CSSTransition>
|
/>
|
||||||
))}
|
</CSSTransition>
|
||||||
|
))}
|
||||||
|
</TransitionGroup>
|
||||||
{incompatibleItems.length > 0 && (
|
{incompatibleItems.length > 0 && (
|
||||||
<ItemZoneGroup
|
<ItemZoneGroup
|
||||||
zoneLabel="Incompatible"
|
zoneLabel="Incompatible"
|
||||||
|
@ -109,7 +111,7 @@ function ItemsPanel({ outfitState, outfitSaving, loading, dispatchToOutfit }) {
|
||||||
isDisabled
|
isDisabled
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</TransitionGroup>
|
</>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
@ -24,6 +24,7 @@ import {
|
||||||
Switch,
|
Switch,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
UnorderedList,
|
UnorderedList,
|
||||||
|
useBreakpointValue,
|
||||||
useClipboard,
|
useClipboard,
|
||||||
useToast,
|
useToast,
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
|
@ -78,6 +79,8 @@ function OutfitControls({
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
const toast = React.useMemo(() => _toast, []);
|
const toast = React.useMemo(() => _toast, []);
|
||||||
|
|
||||||
|
const speciesColorPickerSize = useBreakpointValue({ base: "sm", md: "md" });
|
||||||
|
|
||||||
const onSpeciesColorChange = React.useCallback(
|
const onSpeciesColorChange = React.useCallback(
|
||||||
(species, color, isValid, closestPose) => {
|
(species, color, isValid, closestPose) => {
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
|
@ -208,6 +211,7 @@ function OutfitControls({
|
||||||
{outfitState.speciesId && outfitState.colorId && (
|
{outfitState.speciesId && outfitState.colorId && (
|
||||||
<Flex
|
<Flex
|
||||||
gridArea="picker"
|
gridArea="picker"
|
||||||
|
align="center"
|
||||||
justify="center"
|
justify="center"
|
||||||
onClick={maybeUnlockFocus}
|
onClick={maybeUnlockFocus}
|
||||||
>
|
>
|
||||||
|
@ -223,6 +227,7 @@ function OutfitControls({
|
||||||
idealPose={outfitState.pose}
|
idealPose={outfitState.pose}
|
||||||
onChange={onSpeciesColorChange}
|
onChange={onSpeciesColorChange}
|
||||||
stateMustAlwaysBeValid
|
stateMustAlwaysBeValid
|
||||||
|
size={speciesColorPickerSize}
|
||||||
speciesTestId="wardrobe-species-picker"
|
speciesTestId="wardrobe-species-picker"
|
||||||
colorTestId="wardrobe-color-picker"
|
colorTestId="wardrobe-color-picker"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -293,7 +293,7 @@ function PosePickerButton({ pose, altStyle, isOpen, loading, ...props }, ref) {
|
||||||
{({ css, cx }) => (
|
{({ css, cx }) => (
|
||||||
<Button
|
<Button
|
||||||
variant="unstyled"
|
variant="unstyled"
|
||||||
boxShadow="md"
|
textShadow={`${theme.colors.blackAlpha["700"]} 0 1px 2px`}
|
||||||
d="flex"
|
d="flex"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
|
@ -307,10 +307,11 @@ function PosePickerButton({ pose, altStyle, isOpen, loading, ...props }, ref) {
|
||||||
className={cx(
|
className={cx(
|
||||||
css`
|
css`
|
||||||
border: 1px solid transparent !important;
|
border: 1px solid transparent !important;
|
||||||
color: ${theme.colors.gray["300"]};
|
color: ${theme.colors.gray["100"]};
|
||||||
cursor: ${loading ? "wait" : "pointer"} !important;
|
cursor: ${loading ? "wait" : "pointer"} !important;
|
||||||
transition:
|
transition:
|
||||||
color 0.2s,
|
color 0.2s,
|
||||||
|
background: 0.2s,
|
||||||
border-color 0.2s !important;
|
border-color 0.2s !important;
|
||||||
padding-left: 0.75em;
|
padding-left: 0.75em;
|
||||||
padding-right: 0.5em;
|
padding-right: 0.5em;
|
||||||
|
@ -319,6 +320,8 @@ function PosePickerButton({ pose, altStyle, isOpen, loading, ...props }, ref) {
|
||||||
&.is-open {
|
&.is-open {
|
||||||
border-color: ${theme.colors.gray["50"]} !important;
|
border-color: ${theme.colors.gray["50"]} !important;
|
||||||
color: ${theme.colors.gray["50"]};
|
color: ${theme.colors.gray["50"]};
|
||||||
|
background: ${theme.colors.blackAlpha["600"]};
|
||||||
|
text-shadow: transparent 0 1px 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
|
|
|
@ -129,6 +129,7 @@ function useOutfitState() {
|
||||||
$allItemIds: [ID!]!
|
$allItemIds: [ID!]!
|
||||||
$speciesId: ID!
|
$speciesId: ID!
|
||||||
$colorId: ID!
|
$colorId: ID!
|
||||||
|
$altStyleId: ID
|
||||||
) {
|
) {
|
||||||
items(ids: $allItemIds) {
|
items(ids: $allItemIds) {
|
||||||
# TODO: De-dupe this from SearchPanel?
|
# TODO: De-dupe this from SearchPanel?
|
||||||
|
@ -140,7 +141,11 @@ function useOutfitState() {
|
||||||
currentUserOwnsThis
|
currentUserOwnsThis
|
||||||
currentUserWantsThis
|
currentUserWantsThis
|
||||||
|
|
||||||
appearanceOn(speciesId: $speciesId, colorId: $colorId) {
|
appearanceOn(
|
||||||
|
speciesId: $speciesId
|
||||||
|
colorId: $colorId
|
||||||
|
altStyleId: $altStyleId
|
||||||
|
) {
|
||||||
# This enables us to quickly show the item when the user clicks it!
|
# This enables us to quickly show the item when the user clicks it!
|
||||||
...ItemAppearanceForOutfitPreview
|
...ItemAppearanceForOutfitPreview
|
||||||
|
|
||||||
|
@ -166,7 +171,7 @@ function useOutfitState() {
|
||||||
${itemAppearanceFragment}
|
${itemAppearanceFragment}
|
||||||
`,
|
`,
|
||||||
{
|
{
|
||||||
variables: { allItemIds, speciesId, colorId },
|
variables: { allItemIds, speciesId, colorId, altStyleId },
|
||||||
context: { sendAuth: true },
|
context: { sendAuth: true },
|
||||||
// Skip if this outfit has no items, as an optimization; or if we don't
|
// Skip if this outfit has no items, as an optimization; or if we don't
|
||||||
// have the species/color ID loaded yet because we're waiting on the
|
// have the species/color ID loaded yet because we're waiting on the
|
||||||
|
@ -448,7 +453,7 @@ function getOutfitStateFromOutfitData(outfit) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function findItemConflicts(itemIdToAdd, state, apolloClient) {
|
function findItemConflicts(itemIdToAdd, state, apolloClient) {
|
||||||
const { wornItemIds, speciesId, colorId } = state;
|
const { wornItemIds, speciesId, colorId, altStyleId } = state;
|
||||||
|
|
||||||
const { items } = apolloClient.readQuery({
|
const { items } = apolloClient.readQuery({
|
||||||
query: gql`
|
query: gql`
|
||||||
|
@ -456,10 +461,15 @@ function findItemConflicts(itemIdToAdd, state, apolloClient) {
|
||||||
$itemIds: [ID!]!
|
$itemIds: [ID!]!
|
||||||
$speciesId: ID!
|
$speciesId: ID!
|
||||||
$colorId: ID!
|
$colorId: ID!
|
||||||
|
$altStyleId: ID
|
||||||
) {
|
) {
|
||||||
items(ids: $itemIds) {
|
items(ids: $itemIds) {
|
||||||
id
|
id
|
||||||
appearanceOn(speciesId: $speciesId, colorId: $colorId) {
|
appearanceOn(
|
||||||
|
speciesId: $speciesId
|
||||||
|
colorId: $colorId
|
||||||
|
altStyleId: $altStyleId
|
||||||
|
) {
|
||||||
layers {
|
layers {
|
||||||
zone {
|
zone {
|
||||||
id
|
id
|
||||||
|
@ -477,6 +487,7 @@ function findItemConflicts(itemIdToAdd, state, apolloClient) {
|
||||||
itemIds: [itemIdToAdd, ...wornItemIds],
|
itemIds: [itemIdToAdd, ...wornItemIds],
|
||||||
speciesId,
|
speciesId,
|
||||||
colorId,
|
colorId,
|
||||||
|
altStyleId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const itemToAdd = items.find((i) => i.id === itemIdToAdd);
|
const itemToAdd = items.find((i) => i.id === itemIdToAdd);
|
||||||
|
|
|
@ -14,7 +14,7 @@ export function useSearchResults(
|
||||||
currentPageNumber,
|
currentPageNumber,
|
||||||
{ skip = false } = {},
|
{ skip = false } = {},
|
||||||
) {
|
) {
|
||||||
const { speciesId, colorId } = outfitState;
|
const { speciesId, colorId, altStyleId } = outfitState;
|
||||||
|
|
||||||
// We debounce the search query, so that we don't resend a new query whenever
|
// We debounce the search query, so that we don't resend a new query whenever
|
||||||
// the user types anything.
|
// the user types anything.
|
||||||
|
@ -56,6 +56,7 @@ export function useSearchResults(
|
||||||
$zoneIds: [ID!]!
|
$zoneIds: [ID!]!
|
||||||
$speciesId: ID!
|
$speciesId: ID!
|
||||||
$colorId: ID!
|
$colorId: ID!
|
||||||
|
$altStyleId: ID
|
||||||
$offset: Int!
|
$offset: Int!
|
||||||
$perPage: Int!
|
$perPage: Int!
|
||||||
) {
|
) {
|
||||||
|
@ -78,7 +79,11 @@ export function useSearchResults(
|
||||||
currentUserOwnsThis
|
currentUserOwnsThis
|
||||||
currentUserWantsThis
|
currentUserWantsThis
|
||||||
|
|
||||||
appearanceOn(speciesId: $speciesId, colorId: $colorId) {
|
appearanceOn(
|
||||||
|
speciesId: $speciesId
|
||||||
|
colorId: $colorId
|
||||||
|
altStyleId: $altStyleId
|
||||||
|
) {
|
||||||
# This enables us to quickly show the item when the user clicks it!
|
# This enables us to quickly show the item when the user clicks it!
|
||||||
...ItemAppearanceForOutfitPreview
|
...ItemAppearanceForOutfitPreview
|
||||||
|
|
||||||
|
@ -104,12 +109,13 @@ export function useSearchResults(
|
||||||
{
|
{
|
||||||
variables: {
|
variables: {
|
||||||
query: debouncedQuery.value,
|
query: debouncedQuery.value,
|
||||||
fitsPet: { speciesId, colorId },
|
fitsPet: { speciesId, colorId, altStyleId },
|
||||||
itemKind: debouncedQuery.filterToItemKind,
|
itemKind: debouncedQuery.filterToItemKind,
|
||||||
currentUserOwnsOrWants: debouncedQuery.filterToCurrentUserOwnsOrWants,
|
currentUserOwnsOrWants: debouncedQuery.filterToCurrentUserOwnsOrWants,
|
||||||
zoneIds: filterToZoneIds,
|
zoneIds: filterToZoneIds,
|
||||||
speciesId,
|
speciesId,
|
||||||
colorId,
|
colorId,
|
||||||
|
altStyleId,
|
||||||
offset,
|
offset,
|
||||||
perPage: SEARCH_PER_PAGE,
|
perPage: SEARCH_PER_PAGE,
|
||||||
},
|
},
|
||||||
|
|
|
@ -53,17 +53,26 @@ const typePolicies = {
|
||||||
return appearance;
|
return appearance;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, we're going to see if this is a standard color, in which
|
const { speciesId, colorId, altStyleId } = args;
|
||||||
// case we can reuse the standard color appearance if we already have
|
|
||||||
// it! This helps for fast loading when switching between standard
|
|
||||||
// colors.
|
|
||||||
const { speciesId, colorId } = args;
|
|
||||||
console.debug(
|
console.debug(
|
||||||
"[appearanceOn] seeking cached appearance",
|
"[appearanceOn] seeking cached appearance",
|
||||||
speciesId,
|
speciesId,
|
||||||
colorId,
|
colorId,
|
||||||
|
altStyleId,
|
||||||
readField("id"),
|
readField("id"),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// If this is an alt style, don't try to mess with clever caching.
|
||||||
|
// (Note that, if it's already in the cache, the first condition will
|
||||||
|
// catch that! This won't *always* force a fresh load!)
|
||||||
|
if (altStyleId != null) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, we're going to see if this is a standard color, in which
|
||||||
|
// case we can reuse the standard color appearance if we already have
|
||||||
|
// it! This helps for fast loading when switching between standard
|
||||||
|
// colors.
|
||||||
const speciesStandardBodyId = readField(
|
const speciesStandardBodyId = readField(
|
||||||
"standardBodyId",
|
"standardBodyId",
|
||||||
toReference({ __typename: "Species", id: speciesId }),
|
toReference({ __typename: "Species", id: speciesId }),
|
||||||
|
|
|
@ -83,12 +83,17 @@ export default function useOutfitAppearance(outfitState) {
|
||||||
query OutfitItemsAppearance(
|
query OutfitItemsAppearance(
|
||||||
$speciesId: ID!
|
$speciesId: ID!
|
||||||
$colorId: ID!
|
$colorId: ID!
|
||||||
|
$altStyleId: ID
|
||||||
$wornItemIds: [ID!]!
|
$wornItemIds: [ID!]!
|
||||||
) {
|
) {
|
||||||
items(ids: $wornItemIds) {
|
items(ids: $wornItemIds) {
|
||||||
id
|
id
|
||||||
name # HACK: This is for HTML5 detection UI in OutfitControls!
|
name # HACK: This is for HTML5 detection UI in OutfitControls!
|
||||||
appearance: appearanceOn(speciesId: $speciesId, colorId: $colorId) {
|
appearance: appearanceOn(
|
||||||
|
speciesId: $speciesId
|
||||||
|
colorId: $colorId
|
||||||
|
altStyleId: $altStyleId
|
||||||
|
) {
|
||||||
...ItemAppearanceForOutfitPreview
|
...ItemAppearanceForOutfitPreview
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,6 +104,7 @@ export default function useOutfitAppearance(outfitState) {
|
||||||
variables: {
|
variables: {
|
||||||
speciesId,
|
speciesId,
|
||||||
colorId,
|
colorId,
|
||||||
|
altStyleId,
|
||||||
wornItemIds,
|
wornItemIds,
|
||||||
},
|
},
|
||||||
skip: speciesId == null || colorId == null || wornItemIds.length === 0,
|
skip: speciesId == null || colorId == null || wornItemIds.length === 0,
|
||||||
|
|
Loading…
Reference in a new issue