From c2de6f7167b3b9172f1f73b7a92afaa40cee6808 Mon Sep 17 00:00:00 2001 From: Emi Matchu Date: Tue, 30 Jan 2024 07:01:03 -0800 Subject: [PATCH] Display alt styles in outfit editor when selected Yay it works(*)! But two major missing pieces: - Outfit saving doesn't persist it at all - Item compatibility is unaffected: items will still appear in search and in the preview, even when they don't fit anymore. --- app/controllers/alt_styles_controller.rb | 3 +- .../WardrobePreviewAndControls.js | 1 + .../WardrobePage/support/ItemSupportDrawer.js | 3 +- .../wardrobe-2020/components/OutfitPreview.js | 2 + .../components/useOutfitAppearance.js | 19 +++++--- .../wardrobe-2020/loaders/alt-styles.js | 43 +++++++++++++++++++ app/models/swf_asset.rb | 2 +- 7 files changed, 65 insertions(+), 8 deletions(-) diff --git a/app/controllers/alt_styles_controller.rb b/app/controllers/alt_styles_controller.rb index a182d42c..1e27b9f5 100644 --- a/app/controllers/alt_styles_controller.rb +++ b/app/controllers/alt_styles_controller.rb @@ -11,7 +11,8 @@ class AltStylesController < ApplicationController respond_to do |format| format.html { render } format.json { - render json: @alt_styles.as_json( + render json: @alt_styles.includes(swf_assets: [:zone]).as_json( + include: {swf_assets: {include: [:zone], methods: [:image_url]}}, methods: [:series_name, :adjective_name, :thumbnail_url], ) } diff --git a/app/javascript/wardrobe-2020/WardrobePage/WardrobePreviewAndControls.js b/app/javascript/wardrobe-2020/WardrobePage/WardrobePreviewAndControls.js index c4d34b93..b83ae076 100644 --- a/app/javascript/wardrobe-2020/WardrobePage/WardrobePreviewAndControls.js +++ b/app/javascript/wardrobe-2020/WardrobePage/WardrobePreviewAndControls.js @@ -24,6 +24,7 @@ function WardrobePreviewAndControls({ speciesId: outfitState.speciesId, colorId: outfitState.colorId, pose: outfitState.pose, + altStyleId: outfitState.altStyleId, appearanceId: outfitState.appearanceId, wornItemIds: outfitState.wornItemIds, onChangeHasAnimations: setHasAnimations, diff --git a/app/javascript/wardrobe-2020/WardrobePage/support/ItemSupportDrawer.js b/app/javascript/wardrobe-2020/WardrobePage/support/ItemSupportDrawer.js index 8b93216b..3e6f299f 100644 --- a/app/javascript/wardrobe-2020/WardrobePage/support/ItemSupportDrawer.js +++ b/app/javascript/wardrobe-2020/WardrobePage/support/ItemSupportDrawer.js @@ -413,11 +413,12 @@ function ItemSupportPetCompatibilityRuleFields({ */ function ItemSupportAppearanceLayers({ item }) { const outfitState = React.useContext(OutfitStateContext); - const { speciesId, colorId, pose, appearanceId } = outfitState; + const { speciesId, colorId, pose, altStyleId, appearanceId } = outfitState; const { error, visibleLayers } = useOutfitAppearance({ speciesId, colorId, pose, + altStyleId, appearanceId, wornItemIds: [item.id], }); diff --git a/app/javascript/wardrobe-2020/components/OutfitPreview.js b/app/javascript/wardrobe-2020/components/OutfitPreview.js index 21a7f432..9a781420 100644 --- a/app/javascript/wardrobe-2020/components/OutfitPreview.js +++ b/app/javascript/wardrobe-2020/components/OutfitPreview.js @@ -52,6 +52,7 @@ export function useOutfitPreview({ speciesId, colorId, pose, + altStyleId, wornItemIds, appearanceId = null, isLoading = false, @@ -68,6 +69,7 @@ export function useOutfitPreview({ speciesId, colorId, pose, + altStyleId, appearanceId, wornItemIds, }); diff --git a/app/javascript/wardrobe-2020/components/useOutfitAppearance.js b/app/javascript/wardrobe-2020/components/useOutfitAppearance.js index 45cc7f9c..ecd27165 100644 --- a/app/javascript/wardrobe-2020/components/useOutfitAppearance.js +++ b/app/javascript/wardrobe-2020/components/useOutfitAppearance.js @@ -1,17 +1,20 @@ import React from "react"; import gql from "graphql-tag"; import { useQuery } from "@apollo/client"; + import getVisibleLayers, { itemAppearanceFragmentForGetVisibleLayers, petAppearanceFragmentForGetVisibleLayers, -} from "../components/getVisibleLayers"; +} from "./getVisibleLayers"; +import { useAltStyle } from "../loaders/alt-styles"; /** * useOutfitAppearance downloads the outfit's appearance data, and returns * visibleLayers for rendering. */ export default function useOutfitAppearance(outfitState) { - const { wornItemIds, speciesId, colorId, pose, appearanceId } = outfitState; + const { wornItemIds, speciesId, colorId, pose, altStyleId, appearanceId } = + outfitState; // We split this query out from the other one, so that we can HTTP cache it. // @@ -102,7 +105,13 @@ export default function useOutfitAppearance(outfitState) { }, ); - const petAppearance = data1?.petAppearance; + const { + isLoading: loading3, + error: error3, + data: altStyle, + } = useAltStyle(altStyleId, speciesId); + + const petAppearance = altStyle?.appearance ?? data1?.petAppearance; const items = data2?.items; const itemAppearances = React.useMemo( () => (items || []).map((i) => i.appearance), @@ -116,8 +125,8 @@ export default function useOutfitAppearance(outfitState) { const bodyId = petAppearance?.bodyId; return { - loading: loading1 || loading2, - error: error1 || error2, + loading: loading1 || loading2 || loading3, + error: error1 || error2 || error3, petAppearance, items: items || [], itemAppearances, diff --git a/app/javascript/wardrobe-2020/loaders/alt-styles.js b/app/javascript/wardrobe-2020/loaders/alt-styles.js index 4f7d1f76..222646ef 100644 --- a/app/javascript/wardrobe-2020/loaders/alt-styles.js +++ b/app/javascript/wardrobe-2020/loaders/alt-styles.js @@ -8,6 +8,17 @@ export function useAltStylesForSpecies(speciesId, options = {}) { }); } +// NOTE: This is actually just a wrapper for `useAltStylesForSpecies`, to share +// the same cache key! +export function useAltStyle(id, speciesId, options = {}) { + const query = useAltStylesForSpecies(speciesId, options); + + return { + ...query, + data: query.data?.find((s) => s.id === id) ?? null, + }; +} + async function loadAltStylesForSpecies(speciesId) { const res = await fetch( `/species/${encodeURIComponent(speciesId)}/alt-styles.json`, @@ -35,5 +46,37 @@ function normalizeAltStyle(altStyleData) { seriesName: altStyleData.series_name, adjectiveName: altStyleData.adjective_name, thumbnailUrl: altStyleData.thumbnail_url, + + // This matches the PetAppearanceForOutfitPreview GQL fragment! + appearance: { + bodyId: String(altStyleData.body_id), + pose: "UNKNOWN", + isGlitched: false, + species: { id: String(altStyleData.species_id) }, + color: { id: String(altStyleData.species_id) }, + layers: altStyleData.swf_assets.map(normalizeSwfAssetToLayer), + restrictedZones: [], + }, + }; +} + +function normalizeSwfAssetToLayer(swfAssetData) { + return { + id: String(swfAssetData.id), + zone: { + id: String(swfAssetData.zone.id), + depth: swfAssetData.zone.depth, + label: swfAssetData.zone.label, + }, + bodyId: swfAssetData.body_id, + knownGlitches: [], // TODO + + // HACK: We're just simplifying this adapter, but it would be better to + // actually check what file formats the manifest says! + // TODO: For example, these do generally have SVGs, we could use them! + svgUrl: null, + canvasMovieLibraryUrl: null, + imageUrl: swfAssetData.image_url, + swfUrl: swfAssetData.url, }; } diff --git a/app/models/swf_asset.rb b/app/models/swf_asset.rb index a3478bbe..225332d8 100644 --- a/app/models/swf_asset.rb +++ b/app/models/swf_asset.rb @@ -46,7 +46,7 @@ class SwfAsset < ApplicationRecord size_key = size.join('x') image_dir = "#{self['type']}/#{partition_path}#{self.remote_id}" - "//#{host}/#{image_dir}/#{size_key}.png?#{image_version}" + "https://#{host}/#{image_dir}/#{size_key}.png?#{image_version}" end def images