Compare commits

...

3 commits

Author SHA1 Message Date
cd136aa6a5 Make species/color picker smaller on small screens, to fit pose picker
Now that the pose picker button can have text content too, things were
getting pretty cramped horizontally on smaller screens! Now we use the
`sm` size when it's small-time.
2024-02-01 05:31:34 -08:00
09846a4516 Make pose picker button easier to see on light backgrounds 2024-02-01 05:26:26 -08:00
fc71f5b5a5 Filter by Alt Style in item search and item appearance API calls
I'm not planning to port full Alt Style support over to the 2020
frontend, I really am winding that down, but adding a couple lil API
parameters are *by far* the easiest way to get Alt Styles working in
the main app because of how it calls the 2020 API. So here we are, just
using new parameters for DTI 2020 that I'm gonna deploy to impress-2020
first!
2024-02-01 04:58:30 -08:00
7 changed files with 72 additions and 30 deletions

View file

@ -77,6 +77,7 @@ function ItemsPanel({ outfitState, outfitSaving, loading, dispatchToOutfit }) {
itemCount={outfitState.allItemIds.length} itemCount={outfitState.allItemIds.length}
/> />
) : ( ) : (
<>
<TransitionGroup component={null}> <TransitionGroup component={null}>
{zonesAndItems.map(({ zoneId, zoneLabel, items }) => ( {zonesAndItems.map(({ zoneId, zoneLabel, items }) => (
<CSSTransition <CSSTransition
@ -91,6 +92,7 @@ function ItemsPanel({ outfitState, outfitSaving, loading, 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>

View file

@ -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"
/> />

View file

@ -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 {

View file

@ -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);

View file

@ -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,
}, },

View file

@ -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 }),

View file

@ -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,