Display the name of the creator for flags & skins (#3510)

## Description:

So artists get credit for the work they do.

## 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
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced

## Please put your Discord username so you can be contacted if a bug or
regression is found:

evan
This commit is contained in:
Evan
2026-03-24 15:17:25 -07:00
committed by GitHub
parent 4bf18dfafe
commit dbba1dccb5
7 changed files with 69 additions and 15 deletions
+3
View File
@@ -919,6 +919,9 @@
"select_skin": "Select Skin",
"selected": "selected"
},
"cosmetics": {
"artist_label": "Artist:"
},
"flag_input": {
"title": "Select Flag",
"button_title": "Pick a flag!",
+6 -1
View File
@@ -39,7 +39,12 @@ export class FlagInputModal extends BaseModal {
.map(
([key, flag]) => html`
<flag-button
.flag=${{ key: `flag:${key}`, name: flag.name, url: flag.url }}
.flag=${{
key: `flag:${key}`,
name: flag.name,
url: flag.url,
artist: flag.artist,
}}
.selected=${selectedFlag === `flag:${key}`}
.onSelect=${onSelect}
></flag-button>
+1
View File
@@ -188,6 +188,7 @@ export class StoreModal extends BaseModal {
name: flag.name,
url: flag.url,
product: flag.product,
artist: flag.artist,
}}
.selected=${selectedFlag === `flag:${key}`}
.requiresPurchase=${rel === "purchasable"}
+37
View File
@@ -0,0 +1,37 @@
import { html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators.js";
import { translateText } from "../Utils";
@customElement("artist-info")
export class ArtistInfo extends LitElement {
@property({ type: String })
artist?: string;
createRenderRoot() {
return this;
}
render() {
if (!this.artist) {
return nothing;
}
return html`
<div
class="absolute -top-1 -right-1 z-10 group/artist"
@click=${(e: Event) => e.stopPropagation()}
>
<div
class="w-6 h-6 rounded-full bg-white/20 hover:bg-white/40 flex items-center justify-center cursor-help transition-colors duration-150"
>
<span class="text-xs font-bold text-white/70">?</span>
</div>
<div
class="hidden group-hover/artist:block absolute top-7 right-0 bg-zinc-800 text-white text-xs px-2.5 py-1.5 rounded shadow-lg whitespace-nowrap z-20 border border-white/10"
>
${translateText("cosmetics.artist_label")} ${this.artist}
</div>
</div>
`;
}
}
+14 -12
View File
@@ -2,6 +2,7 @@ import { html, LitElement } from "lit";
import { customElement, property } from "lit/decorators.js";
import { Product } from "../../core/CosmeticSchemas";
import { translateCosmetic } from "../Cosmetics";
import "./ArtistInfo";
import "./PurchaseButton";
export interface FlagItem {
@@ -9,6 +10,7 @@ export interface FlagItem {
name: string;
url: string;
product?: Product | null;
artist?: string;
}
@customElement("flag-button")
@@ -39,7 +41,7 @@ export class FlagButton extends LitElement {
render() {
return html`
<div
class="flex flex-col items-center justify-between gap-1 p-2 bg-white/5 backdrop-blur-sm border rounded-lg w-36 h-full transition-all duration-200 ${this
class="flex flex-col items-center justify-between gap-1 p-1.5 bg-white/5 backdrop-blur-sm border rounded-lg w-36 h-full transition-all duration-200 ${this
.selected
? "border-green-500 shadow-[0_0_15px_rgba(34,197,94,0.5)]"
: "hover:bg-white/10 hover:border-white/20 hover:shadow-xl border-white/10"}"
@@ -50,17 +52,17 @@ export class FlagButton extends LitElement {
?disabled=${this.requiresPurchase}
@click=${this.handleClick}
>
<div class="flex flex-col items-center w-full">
<div
class="text-[10px] font-bold text-white uppercase tracking-wider mb-0.5 text-center truncate w-full ${this
.requiresPurchase
? "opacity-50"
: ""}"
title="${translateCosmetic("flags", this.flag.name)}"
>
${translateCosmetic("flags", this.flag.name)}
</div>
<div class="h-[14px] mb-1 w-full"></div>
<artist-info .artist=${this.flag.artist}></artist-info>
<div
class="text-[10px] font-bold text-white uppercase tracking-wider mt-1 ${this
.flag.artist
? "pr-5"
: ""} text-center truncate w-full ${this.requiresPurchase
? "opacity-50"
: ""}"
title="${translateCosmetic("flags", this.flag.name)}"
>
${translateCosmetic("flags", this.flag.name)}
</div>
<div
+6 -2
View File
@@ -11,6 +11,7 @@ import { PatternDecoder } from "../../core/PatternDecoder";
import { PlayerPattern } from "../../core/Schemas";
import { translateCosmetic } from "../Cosmetics";
import { translateText } from "../Utils";
import "./ArtistInfo";
import "./PurchaseButton";
export const BUTTON_WIDTH = 150;
@@ -72,10 +73,13 @@ export class PatternButton extends LitElement {
?disabled=${this.requiresPurchase}
@click=${this.handleClick}
>
<artist-info .artist=${this.pattern?.artist}></artist-info>
<div class="flex flex-col items-center w-full">
<div
class="text-xs font-bold text-white uppercase tracking-wider mb-1 text-center truncate w-full ${this
.requiresPurchase
class="text-xs font-bold text-white uppercase tracking-wider mb-1 ${this
.pattern?.artist
? "pr-5"
: ""} text-center truncate w-full ${this.requiresPurchase
? "opacity-50"
: ""}"
title="${isDefaultPattern
+2
View File
@@ -63,6 +63,7 @@ export const PatternSchema = z.object({
.optional(),
affiliateCode: z.string().nullable(),
product: ProductSchema.nullable(),
artist: z.string().optional(),
});
export const FlagSchema = z.object({
@@ -70,6 +71,7 @@ export const FlagSchema = z.object({
url: z.string(),
affiliateCode: z.string().nullable(),
product: ProductSchema.nullable(),
artist: z.string().optional(),
});
// Schema for resources/cosmetics/cosmetics.json