Compare commits

..

No commits in common. "c7cf1d21116ea110a4619f56973f224c8e773463" and "b06149cf220fe06042ca2acd0df720dacf1eb87c" have entirely different histories.

11 changed files with 55 additions and 117 deletions

View file

@ -115,7 +115,7 @@ class OutfitsController < ApplicationController
def outfit_params
params.require(:outfit).permit(
:name, :starred, :alt_style_id, item_ids: {worn: [], closeted: []},
:name, :starred, item_ids: {worn: [], closeted: []},
biology: [:species_id, :color_id, :pose])
end

View file

@ -23,7 +23,7 @@ import {
useToast,
useToken,
} from "@chakra-ui/react";
import { ChevronDownIcon, WarningTwoIcon } from "@chakra-ui/icons";
import { ChevronDownIcon } from "@chakra-ui/icons";
import { loadable } from "../util";
import { petAppearanceFragment } from "../components/useOutfitAppearance";
@ -42,7 +42,6 @@ import twemojiSunglasses from "../images/twemoji/sunglasses.svg";
import twemojiQuestion from "../images/twemoji/question.svg";
import twemojiMasc from "../images/twemoji/masc.svg";
import twemojiFem from "../images/twemoji/fem.svg";
import twemojiHourglass from "../images/twemoji/hourglass.svg";
const PosePickerSupport = loadable(() => import("./support/PosePickerSupport"));
@ -219,11 +218,18 @@ function PosePicker({
gap="2"
index={tabIndex}
onChange={setTabIndex}
// HACK: To only apply `initialFocusRef` to the selected input
// in the *active* tab, we just use `isLazy` to only *render*
// the active tab. We could also watch the tab state and set
// the ref accordingly!
isLazy
>
<TabList paddingX="2" paddingY="0">
<Tab width="50%">Expressions</Tab>
<Tab width="50%">Styles</Tab>
</TabList>
<SupportOnly>
<TabList paddingX="2" paddingY="0">
<Tab width="50%">Expressions</Tab>
<Tab width="50%">Styles</Tab>
</TabList>
</SupportOnly>
<TabPanels position="relative">
<TabPanel paddingX="4" paddingY="0">
{isInSupportMode ? (
@ -232,9 +238,7 @@ function PosePicker({
colorId={colorId}
pose={pose}
appearanceId={appearanceId}
initialFocusRef={
tabIndex === 0 ? initialFocusRef : null
}
initialFocusRef={initialFocusRef}
dispatchToOutfit={dispatchToOutfit}
/>
) : (
@ -242,9 +246,7 @@ function PosePicker({
<PosePickerTable
poseInfos={poseInfos}
onChange={onChangePose}
initialFocusRef={
tabIndex === 0 ? initialFocusRef : null
}
initialFocusRef={initialFocusRef}
/>
{numStandardPoses == 0 && (
<PosePickerEmptyExplanation />
@ -265,7 +267,7 @@ function PosePicker({
selectedStyleId={altStyleId}
altStyles={altStyles}
onChange={onChangeStyle}
initialFocusRef={tabIndex === 1 ? initialFocusRef : null}
initialFocusRef={initialFocusRef}
/>
<StyleExplanation />
</TabPanel>
@ -422,22 +424,14 @@ function PosePickerTable({ poseInfos, onChange, initialFocusRef }) {
</tbody>
</table>
{poseInfos.unconverted.isAvailable && (
<Flex
align="center"
justify="center"
gap="1"
<PoseOption
poseInfo={poseInfos.unconverted}
onChange={onChange}
inputRef={poseInfos.unconverted.isSelected && initialFocusRef}
size="sm"
label="Unconverted"
marginTop="2"
marginBottom="2"
>
<PoseOption
poseInfo={poseInfos.unconverted}
onChange={onChange}
inputRef={poseInfos.unconverted.isSelected && initialFocusRef}
size="sm"
label="Retired UC"
/>
<RetiredUCWarning isSelected={poseInfos.unconverted.isSelected} />
</Flex>
/>
)}
</Box>
);
@ -630,40 +624,6 @@ function PosePickerEmptyExplanation() {
);
}
function RetiredUCWarning({ isSelected }) {
return (
<Popover placement="right" trigger="hover">
<PopoverTrigger>
<Box
as="button"
tabIndex="0"
aria-label="Warning"
cursor="help"
lineHeight="1"
opacity={isSelected ? "1" : "0.75"}
transform={isSelected ? "scale(1)" : "scale(0.8)"}
color={isSelected ? "yellow.500" : "inherit"}
transition="all 0.2s"
padding="1"
>
<WarningTwoIcon />
</Box>
</PopoverTrigger>
<PopoverContent
background="blackAlpha.800"
borderColor="blackAlpha.900"
color="white"
padding="2"
fontSize="sm"
>
"Unconverted" pets are no longer available on Neopets.com, and have been
replaced with the very similar Styles feature. We're just keeping this
as an archive!
</PopoverContent>
</Popover>
);
}
function StyleSelect({
selectedStyleId,
altStyles,
@ -788,6 +748,10 @@ function StyleExplanation() {
Styling Chamber
</Box>
. Not all items fit Alt Style pets. The pet's color doesn't have to match.
<SupportOnly>
<br />
WIP: Only Support staff see this tab for now! 💖
</SupportOnly>
</Box>
);
}
@ -925,7 +889,7 @@ function getIcon(pose) {
} else if (["SICK_MASC", "SICK_FEM"].includes(pose)) {
return twemojiSick;
} else if (pose === "UNCONVERTED") {
return twemojiHourglass;
return twemojiSunglasses;
} else {
return twemojiSmile;
}
@ -939,7 +903,7 @@ function getLabel(pose) {
} else if (pose === "SICK_MASC" || pose === "SICK_FEM") {
return "Sick";
} else if (pose === "UNCONVERTED") {
return "Retired UC";
return "Classic UC";
} else {
return "Default";
}

View file

@ -69,7 +69,6 @@ function useOutfitSaving(outfitState, dispatchToOutfit) {
speciesId: outfitState.speciesId,
colorId: outfitState.colorId,
pose: outfitState.pose,
altStyleId: outfitState.altStyleId,
wornItemIds: [...outfitState.wornItemIds],
closetedItemIds: [...outfitState.closetedItemIds],
})

View file

@ -447,7 +447,6 @@ function getOutfitStateFromOutfitData(outfit) {
speciesId: outfit.speciesId,
colorId: outfit.colorId,
pose: outfit.pose,
altStyleId: outfit.altStyleId,
wornItemIds: new Set(outfit.wornItemIds),
closetedItemIds: new Set(outfit.closetedItemIds),
};
@ -704,6 +703,12 @@ function buildOutfitQueryString(outfitState) {
color: colorId || "",
pose: pose || "",
});
for (const itemId of wornItemIds) {
params.append("objects[]", itemId);
}
for (const itemId of closetedItemIds) {
params.append("closet[]", itemId);
}
if (altStyleId != null) {
params.append("style", altStyleId);
}
@ -712,12 +717,6 @@ function buildOutfitQueryString(outfitState) {
// refers to "PetState", the database table name for pet appearances.
params.append("state", appearanceId);
}
for (const itemId of wornItemIds) {
params.append("objects[]", itemId);
}
for (const itemId of closetedItemIds) {
params.append("closet[]", itemId);
}
return params.toString();
}

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#FFE8B6" d="M21 18c0-2.001 3.246-3.369 5-6 2-3 2-10 2-10H8s0 7 2 10c1.754 2.631 5 3.999 5 6s-3.246 3.369-5 6c-2 3-2 10-2 10h20s0-7-2-10c-1.754-2.631-5-3.999-5-6z"/><path fill="#FFAC33" d="M20.999 24c-.999 0-2.057-1-2.057-2C19 20.287 19 19.154 19 18c0-3.22 3.034-4.561 4.9-7H12.1c1.865 2.439 4.9 3.78 4.9 7 0 1.155 0 2.289.058 4 0 1-1.058 2-2.058 2-2 0-3.595 1.784-4 3-1 3-1 7-1 7h16s0-4-1-7c-.405-1.216-2.001-3-4.001-3z"/><path fill="#3B88C3" d="M30 34c0 1.104-.896 2-2 2H8c-1.104 0-2-.896-2-2s.896-2 2-2h20c1.104 0 2 .896 2 2zm0-32c0 1.104-.896 2-2 2H8c-1.104 0-2-.896-2-2s.896-2 2-2h20c1.104 0 2 .896 2 2z"/></svg>

Before

Width:  |  Height:  |  Size: 688 B

View file

@ -30,9 +30,7 @@ export function useDeleteOutfitMutation(options = {}) {
...options,
mutationFn: deleteOutfit,
onSuccess: (emptyData, id, context) => {
queryClient.invalidateQueries({
queryKey: ["outfits", String(id)],
});
queryClient.invalidateQueries({ queryKey: ["outfits", String(id)] });
if (options.onSuccess) {
options.onSuccess(emptyData, id, context);
}
@ -44,9 +42,7 @@ async function loadSavedOutfit(id) {
const res = await fetch(`/outfits/${encodeURIComponent(id)}.json`);
if (!res.ok) {
throw new Error(
`loading outfit failed: ${res.status} ${res.statusText}`,
);
throw new Error(`loading outfit failed: ${res.status} ${res.statusText}`);
}
return res.json().then(normalizeOutfit);
@ -58,7 +54,6 @@ async function saveOutfit({
speciesId,
colorId,
pose,
altStyleId,
wornItemIds,
closetedItemIds,
}) {
@ -70,7 +65,6 @@ async function saveOutfit({
color_id: colorId,
pose: pose,
},
alt_style_id: altStyleId,
item_ids: { worn: wornItemIds, closeted: closetedItemIds },
},
};
@ -97,9 +91,7 @@ async function saveOutfit({
}
if (!res.ok) {
throw new Error(
`saving outfit failed: ${res.status} ${res.statusText}`,
);
throw new Error(`saving outfit failed: ${res.status} ${res.statusText}`);
}
return res.json().then(normalizeOutfit);
@ -114,9 +106,7 @@ async function deleteOutfit(id) {
});
if (!res.ok) {
throw new Error(
`deleting outfit failed: ${res.status} ${res.statusText}`,
);
throw new Error(`deleting outfit failed: ${res.status} ${res.statusText}`);
}
}
@ -127,11 +117,8 @@ function normalizeOutfit(outfit) {
speciesId: String(outfit.species_id),
colorId: String(outfit.color_id),
pose: outfit.pose,
altStyleId: outfit.alt_style_id ? String(outfit.alt_style_id) : null,
wornItemIds: (outfit.item_ids?.worn || []).map((id) => String(id)),
closetedItemIds: (outfit.item_ids?.closeted || []).map((id) =>
String(id),
),
closetedItemIds: (outfit.item_ids?.closeted || []).map((id) => String(id)),
creator: outfit.user ? { id: String(outfit.user.id) } : null,
createdAt: outfit.created_at,
updatedAt: outfit.updated_at,

View file

@ -4,7 +4,6 @@ class Outfit < ApplicationRecord
class_name: 'ItemOutfitRelationship'
has_many :worn_items, through: :worn_item_outfit_relationships, source: :item
belongs_to :alt_style, optional: true
belongs_to :pet_state, optional: true # We validate presence below!
belongs_to :user, optional: true
@ -83,8 +82,7 @@ class Outfit < ApplicationRecord
def as_json(more_options={})
serializable_hash(
only: [:id, :name, :pet_state_id, :starred, :created_at, :updated_at,
:alt_style_id],
only: [:id, :name, :pet_state_id, :starred, :created_at, :updated_at],
methods: [:color_id, :species_id, :pose, :item_ids, :user]
)
end

View file

@ -1,12 +1,12 @@
- title "Styling Studio"
%p
Here's all the new NC Pet Styles we have! They're available in the app too,
by opening the emotion picker and clicking the "Styles" tab.
We're getting set up with the new NC Pet Styles! They're not ready in the app
yet, but here's what we have so far!
%p
If you have an Alt Style we don't, please model it by entering your pet's
name on the homepage! Thank you! 💖
If you have one we don't, please model it by entering your pet's name on the
homepage! Thank you! 💖
%p
Also, heads-up: Style tokens are pretty different from normal wearables, so

View file

@ -3,11 +3,11 @@
= advertise_campaign_progress @campaign
.notice
%strong Alt styles are ready now!
You can find them by opening the emotion picker, then clicking "Styles".
%strong Happy NC UC day!
We're working on Styling Studio support,
= link_to("here's what we have so far", alt_styles_path) + "!"
%br
= link_to("Here's our reference page, too!", alt_styles_path)
Thanks again for all your help, let us know how it works for you! 💖
Thank you for helping us model the new styles, we appreciate it lots!!! 💖
%p#pet-not-found.alert= t 'pets.load.not_found'

View file

@ -1,5 +0,0 @@
class AddAltStyleIdToOutfits < ActiveRecord::Migration[7.1]
def change
add_reference :outfits, :alt_style, null: true, foreign_key: true
end
end

View file

@ -10,13 +10,13 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.1].define(version: 2024_02_01_134440) do
create_table "alt_styles", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t|
ActiveRecord::Schema[7.1].define(version: 2024_01_29_114639) do
create_table "alt_styles", charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t|
t.integer "species_id", null: false
t.integer "color_id", null: false
t.integer "body_id", null: false
t.datetime "created_at", precision: nil, null: false
t.datetime "updated_at", precision: nil, null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["color_id"], name: "index_alt_styles_on_color_id"
t.index ["species_id"], name: "index_alt_styles_on_species_id"
end
@ -125,11 +125,11 @@ ActiveRecord::Schema[7.1].define(version: 2024_02_01_134440) do
t.index ["outfit_id", "is_worn"], name: "index_item_outfit_relationships_on_outfit_id_and_is_worn"
end
create_table "item_translations", id: :integer, charset: "latin1", collation: "latin1_swedish_ci", force: :cascade do |t|
create_table "item_translations", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t|
t.integer "item_id"
t.string "locale"
t.string "name"
t.text "description"
t.text "description", size: :medium
t.string "rarity"
t.datetime "created_at", precision: nil
t.datetime "updated_at", precision: nil
@ -191,8 +191,6 @@ ActiveRecord::Schema[7.1].define(version: 2024_02_01_134440) do
t.string "image"
t.string "image_layers_hash"
t.boolean "image_enqueued", default: false, null: false
t.bigint "alt_style_id"
t.index ["alt_style_id"], name: "index_outfits_on_alt_style_id"
t.index ["pet_state_id"], name: "index_outfits_on_pet_state_id"
t.index ["user_id"], name: "index_outfits_on_user_id"
end
@ -316,5 +314,4 @@ ActiveRecord::Schema[7.1].define(version: 2024_02_01_134440) do
add_foreign_key "alt_styles", "colors"
add_foreign_key "alt_styles", "species"
add_foreign_key "outfits", "alt_styles"
end