From c622e8581c08fc2738af5be47c0ecd1c7bdae553 Mon Sep 17 00:00:00 2001
From: Ryan <7389646+ryanbarlow97@users.noreply.github.com>
Date: Sun, 28 Jun 2026 03:09:05 +0100
Subject: [PATCH] collapse skins under one banner (#4432)
## Description:
merges skins under one item with a circular button below it:
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
## Please put your Discord username so you can be contacted if a bug or
regression is found:
w.o.n
---
src/client/Cosmetics.ts | 24 +++++
src/client/Store.ts | 17 +--
src/client/components/CosmeticButton.ts | 132 +++++++++++++++++++-----
tests/ResolveCosmetics.test.ts | 69 ++++++++++++-
4 files changed, 209 insertions(+), 33 deletions(-)
diff --git a/src/client/Cosmetics.ts b/src/client/Cosmetics.ts
index 42c5e1c62..9d9d666eb 100644
--- a/src/client/Cosmetics.ts
+++ b/src/client/Cosmetics.ts
@@ -477,6 +477,30 @@ export function resolveCosmetics(
return result;
}
+/**
+ * Groups resolved cosmetics so that colour-palette variants of the same pattern
+ * collapse into a single entry. Returns an array of groups in first-seen order
+ */
+export function groupCosmeticVariants(
+ items: ResolvedCosmetic[],
+): ResolvedCosmetic[][] {
+ const groups: ResolvedCosmetic[][] = [];
+ const patternGroupByName = new Map();
+ for (const item of items) {
+ if (item.type === "pattern" && item.cosmetic !== null) {
+ const name = item.cosmetic.name;
+ const existing = patternGroupByName.get(name);
+ if (existing !== undefined) {
+ groups[existing].push(item);
+ continue;
+ }
+ patternGroupByName.set(name, groups.length);
+ }
+ groups.push([item]);
+ }
+ return groups;
+}
+
export function resolvedToPlayerPattern(
resolved: ResolvedCosmetic,
): PlayerPattern | null {
diff --git a/src/client/Store.ts b/src/client/Store.ts
index 7189ebf6c..d2ba99006 100644
--- a/src/client/Store.ts
+++ b/src/client/Store.ts
@@ -10,6 +10,7 @@ import "./components/NotLoggedInWarning";
import { modalHeader } from "./components/ui/ModalHeader";
import {
fetchCosmetics,
+ groupCosmeticVariants,
purchaseCosmetic,
resolveCosmetics,
SUBSCRIPTIONS_ENABLED,
@@ -92,14 +93,17 @@ export class StoreModal extends BaseModal {
`;
}
+ // Collapse colour-palette variants of the same pattern into one tile; the
+ // variants become clickable colour swatches on the cosmetic-button.
return html`