We download the schema from prod, and omit real data, but I didn't notice that we were still pulling the metadata of the auto increment counter for IDs! Now, we scrub that from the schema file we save.
I think it's great that we hide the button when it's not relevant, but that makes it hard to know that it exists. Here, we do some cute tricks to blink up the "Paused" button when it first appears, even if the user doesn't have the controls visible right now
We did this a while back too, but I guess something changed in Apollo: I guess it used to return identical item objects from the cache on its own, and now it returns brand new item objects. So we gotta do the object caching hacks ourselves!
This speeds up add/remove item state updates from 500ms to 100ms on my Mac, because we stop re-rendering all the Item components and their complex Chakra children.
This is especially worth doing now, because animations make long updates much more noticeable! (It interrupts the animation 😅)
Honestly kinda surprised this worked on the first go! I was worried something about the process would make the sorta like, instant-cache expectation not work.
Still thinking it might be considerate to like, keep a LRU cache of MovieClip options, so that we don't double-execute these scripts when adding stuff… we even re-execute the ones already applied lol 😅 and that adds lots of script tags to the body!
But yeah I'm not gonna push on it yet until I see evidence that it actually causes performance issues in practice
This is really very cute, but too many items it turns out are lod despite not actually being animated 🙃 it's helpful for looking for test cases tho, so I'm keeping it, but support only!
I also ended up really liking the icon-badge+tooltip design as a way to summarize lil things, so I'm trying Own/Want short badges in the same style.
reflecting further on the abstraction, I'm noticing that this isn't an Easel abstraction like I envisioned early, and that we're baking some Neopets stuff into it. And I think that's the right call, esp with the tricky MovieClip stuff coming up, where I think more barriers would hurt more than they help. So, a new name!
Did it revert? Or did I just never notice that it only worked on mount, not on new loading states?
Also, fixed a bug where we were injecting the script tag way too much, and triggering loading too much that way too!
still just for static stuff, but it's good to be working!
PosePicker got a bit broken, CSS scaling doesn't work quite right anymore, we might need to just up the internal resolution or something?
notable layout change is that the text content will now try to center itself, and we push the buttons off to the right. we also needed to tweak the layout code a bit to get the buttons to feel centered with the top two lines, bc centering against the full block just feels wrong, they want to be top-y in terms of positioning, but still feel centered-y in terms of visual balance
Honestly I don't think this is the long-term home for the Modeling link, I think it'll become a homepage-only link as we add more modules there. But I wanted to get it out of the way!
This wasn't actually super helpful to read anyway, and I think it was causing us to hit rate limits.
We can maybe add back a limited version to like, add path context of _where_ a span happened in the GQL tree, but like, I feel like that's typically been pretty intuitive so far.
Boom, now we can also run a clean MySQL test db on each test that wants it :)
the test I wrote as a sample is currently marked `it.skip` because it's not passing yet!
This updates the MySQL procedure to get the important special colors, but keeps the GQL behavior the same by only filtering to Blue. Just an incremental step before changing the behavior, to make sure I've gotten it right so far!
Snapshots significantly updated, but, from scanning it, I think that's expected changes from actual modeling progress. Hooray!
Fading the whole preview to a black overlay for the loading state was feeling aggressive, especially since loading delay wasn't working correctly!
In this change, I fix loading delay, and I add a nice subtle "corner" variant for outfit preview spinners :)
Here, we add loading skeletons to lots of individual elements, instead of doing a whole item placeholder skeleton. That helps when coming from pages where we have some data, like name and thumbnail, but things is isNc are still missing.
It looks nice, but also particularly means we can handle the loading for the preview separately, get that started faster and iterate better on it in dev!
Initially the spinner was only used in OutfitPreview, where the background was always pretty dark. Now that we use it in more general contexts, we need a light/dark distinction!
Also went and standardized out the `size` props
Here, we extract a lean WardrobePageLayout component, so that we can bundle it into the main app as a loading state for WardrobePage.
This means that clicking Start from the homepage will, instead of flashing the screen to white while WardrobePage loads, show the correctly-sized black/white page layout instead.
I think the Chakra upgrades made these overrides stop working? added !important so that they happen again!
The regression meant the homepage looked worse, always having the selects fade in :/
I'm using my first ever MySQL Store Procedure for clever cleverness in caching the modeling query!
I realized that checking for the latest contribution timestamp is a pretty reliable way of deciding when modeling data was last updated at all. If that timestamp hasn't changed, we can reuse the results!
I figured that, because query roundtrips are a bottleneck in this environment, I didn't want to make that query separately. So, I built a MySQL procedure to do the check on the database side!
I think I got all up in my head about direct queries for this one, because of a previous implementation I had in mind, and I forgot that I could just query species and color from the cache by reference without breaking out of the API provided to the cache function!
I also learned in here that I _can_ look up things from the root by doing `readField("allSpecies", {__ref: "ROOT_QUERY"})`, which I struggled to figure out my previous time. I couldn't figure out how to read an uncached field with arguments (I couldn't quite figure out how to build a proper FieldNode, and passing the string form seemed to provide `null` to the `species` cache field reader), but it's probably doable!
(the permissioning happens on the backend in the prev change! but yeah we send the auth token in the headers, so the backend knows who you are and whether to show you private data)
(also it is just owned items not in any list!)
Essentially I want the center-y visual balance around the image, and the name and _one_ line of tags. If there are extra tags, I want that to go down on its own, rather than bringing down the image+buttons to center against them.
The single-line-of-tags case is the most common, and I think it makes things feel more consistent for all the items to stick to that basic layout, while trying to keep that layout feeling center-y
I was getting annoyed by how, when you're using search, trying on an item will remove conflicting stuff, and then if you decide you don't like what you tried the old stuff _doesn't come back_
As of this change, it does! When you start a new search, we save the outfit state, and then whenever you change the items we ask "hey can these old ones safely be re-worn again?" and re-wear them if so.
On the search panel, not all items have the remove button, and it's confusing to have the other buttons be in inconsistent positions!
Move the Remove button to the left side of the list, so that everything else is positioned the same regardless
Okay, we handle the new pages correctly! Still some weird bugs when you send requests near each other? Probably wise to migrate to Apollo's new way of doing this
This is just an implementation thing, but I realized we can just insert the Zone data into the initial Apollo cache, instead of doing weird field definitions
I _do_ still want the @client tags in the queries though, to tell them not to make server requests at all
Been bothered by this for a while!
My hope is that this isn't a notable marginal performance hit—we were already walking the table and doing string ops anyway, I can't imagine adding to that is actually that much of a marginal lift, when the main bottleneck was probably reads. And the perf should be identical for simple single-word queries anyway. But we'll see how it feels!
This was a subtle little thing for a while! If you switch species/color, such that an item doesn't fit the pet anymore, we used to just hide it. Now, we show it in a list, so that you can understand what went wrong, and have the option to remove it.
Previously, if you switched species/color such that one of your items was no longer compatible, we _would_ still apply its zone restrictions to the visible layer set.
In this change, we fix that server-side, since I think it makes the most sense for an empty appearance to be truly empty!
This was bothering me, I'm surprised and pleased by how easy it seems it was to fix? :)
The strategy is just, look for groups that are provably redundant, and filter them out.
I hope it's correct! It's definitely cozier. Kyrii Mage items are good tests, they have a lot of interesting zones!
I figured that we'd want simpler UI in the ItemsPanel when possible… but now that we've got it pretty simple and comfy, I think the consistency is better
These are nice! :)
The `hideSimpleZones` option I'm not sure about yet, but I figure that:
1. For a new user just doing simple outfits, I feel like the double data on the items page just looks silly, so I want to streamline for that
2. But I _do_ want to let the user think about zone complexity when things _are_ multi-zone.
I did also consider just hiding the zone badge for the header you're under, but I figured the consistency of having the item and its badges look the same in all the places in the list was more important.
This is in preparation for hiding bio zone restrictions but showing item zone restrictions!
I also refactor the build-cached-data script substantially, to run GraphQL against the server instead of a custom query.
okay so the PetAppearance restrictions are stored on the asset, because that's how they're defined on Neopets.com too
but I think that's a confusing API, so here I define `PetAppearance.restrictedZones`, which just maps over the layers and aggregates the zones server-side, same as we would have done on the client
I think that's much easier to understand than having layer contain a field, but having to know that item restrictions _don't_ work that way, you know?
Huh, maybe this is a Firefox bug or something? but the container wasn't applying partial opacity correctly to its children, it was only doing 0 or 1, I think maybe because the children weren't static? I refactor here to make the DOM structure a bit more natural, and fade ins work again 🤷♀️
I'm not sure when this regressed, but changing the outfit was clearing out the whole preview and showing an empty loading state, instead of the intended behavior of showing a loading spinner over the old preview. This affected both the home page and the wardrobe page.
Yeah, so, huh. Fixed! I hope I didn't goof anything else with these effect trigger changes though 😅
The reason I'm doing this now is not just that it's annoying, but as a pair with the color change fix from just now, I want those color changes feeling buttery smooth!
Previously, when changing a pet's color, we would refresh the items panel and send a new network request for the item appearances, even though they're all the same. This is because item appearance data is queried by species/color, for ease of specification.
But! Item appearances are //cached// by body ID. So, if this is a standard color, it's not hard to look in the cache for the standard color's body ID!
Now, most color changes are faster and don't flicker the item panel anymore. We do still refresh the panel and send the requests for color changes that _do_ matter though, like standard <-> mutant!
ahh, in a recent change I made glitched states valid for canonical poses, but didn't make the corresponding change here! This meant that I think the PosePicker showed them, but other ways of getting to them didn't work, including the Candy Acara (who is 100% marked glitched) was no longer pickable at all
Oops, opening the dropdown would auto-focus the left arrow button, because it's now the first focusable element! Make explicit that we want the dropdown instead.
I noticed in prod that the Vercel edge cache can show old data in the Support tool right after you edit it and reload the page, which is super confusing!
In this change, we stop caching the endpoint we use for Support tools, so that the Support tools always feel real-time and trustworthy. (The standard pose picker might still be cached, so it could be a bit confusing for that to be out of sync, but at least you can toggle into Support mode and see that your changes happened _there_, so you don't panic that they're _gone_.)
Previously, I was filtering out glitched appearances from the canonical ones.
But now, I'm thinking it's better to serve glitched ones than no data for a pose at all.
I'm inspired by the case of the Candy Acara, which has _only_ glitched appearances in our db, and I'd like to mark them for reference—but then the site would treat it as no data at all.
Easier to move between appearances quickly! I'm adding this in anticipation of a use case of rapidly labeling Unknown appearances—I want it to be easy to label one and go to the next!
For most users, I want to hide the pose picker if there's not actually anything to pick from.
But I want Support users to be able to open it and use Support mode, to inspect and label Unknown poses!
In this change, I add new UI to show a question mark pose picker button, and a note explaining the empty picker; but only show them for Support users.
I also made a new `useSupport` hook, to replace `useSupportSecret`, now that I have a use case for "is support user?" that doesn't require the secret. Will migrate the rest!
Previously, toggling between the two PosePicker modes could dramatically disrupt the layout, because Popover didn't know to re-measure itself.
In this change, we add a hacky workaround to simulate a window resize event in moments when we know the content is changing. (The realistic ideal solution would still have these manual triggers, but would use an official API in Chakra to notify the Popper instance directly.)
Still just read-only stuff, but now you can look at all the different poses we have for a species/color!
Soon I'll make the pose/glitched stuff editable :3
Some sizable refactors here to add the ability to specify appearance ID as well as pose… most of the app still doesn't use it, it's mostly just lil extra logic to make it win if it's available!
(The rationale for making it an override, rather than always tracking appearance ID, is that it gets really inconvenient in practice to //wait// on looking up the appearance ID in order to start loading various queries. Species/color/pose is a more intuitive key, and works better and faster when the canonical appearance is what you want!)
similar to the layer zoning tools I just rolled out!
not thrilled about the outfit state hacks here bc of how we cache restrict on the appearance rather than the item, but oh well! this escape hatch is pretty easy and solid, and it's a cleanup for another day
Also did a code split here, now that this file is getting larger, to only load this for support users. I don't actually care about restricting console stuff to support users (I'd honestly rather not), but saving the bytes is worth it I think, since support mode is pretty easy to enter when we need to
This is in support of a caching issue in a hack tool coming next! Without this, the change to ItemAppearance restricted zones would make other ItemAppearance fields go missing (bc our hack tool didn't also specify them), so the query would re-execute over the network to find the missing fields we overwrote with nothingness—which would undo the local hack change.
A dev tool to preview what an item would look like in a different zone!
To use, open the browser console, and type `DTIHackLayerZone(layerId, zoneIdOrName)`, but replace `layerId` with the layer's DTI ID, and `zoneIdOrName` with either the zone ID or its name in quotes.
Previously, we were using a custom-y `id` field to help Apollo cross-reference `petAppearance` queries with the results from bulk `petAppearances` queries. Now, instead, we deprecate `petStateId`, and start using `id` to have the same stable value!
This is in anticipation of pet appearance support tools: a stable ID will make it easier to edit them, esp changing their pose (which would otherwise have changed the ID!)
Previously, we would load all `petAppearances` in `PosePicker`, and use cache keys to instantly find it again as a single `petAppearance` in `OutfitPreview` after switching poses.
In this change, we instead have `PosePicker` explicitly load all 6 poses as separate `petAppearance` queries. This simplifies cache sharing between the two components' queries: Apollo can do it automatically, because they were queried the same way in the first place.
I'm doing this in preparation for changing the `id` field of `PetAppearance`, to become `petStateId`. This will help me build pet appearance support tools, by giving the appearances stable identifiers that won't be affected by editing which pose an appearance is!
Huh, some 8-bit species are broken and use the standard body ID!
This was causing our body name query to prioritize 8-bit for standard assets, as the alphabetically-first compatible color; but 8-bit isn't marked standard, so the function kept it labeled 8-bit.
This should fix it and show "Standard Draik" when deleting an asset off the standard draik body!
In practice I saw that this doesn't actually tell you what you _really_ want to know about where the change happened! You want to know it was broken on the Acara or w/e.