Support affiliate patterns (#2027)

## Description:

Patterns have an optional affiliateCode that is associated with an
affiliate/youtuber. These patterns are not shown by default. You can see
them by going to openfront.io/#affiliate=XXX


## 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:
evanpelle
2025-09-07 14:36:44 -07:00
committed by GitHub
parent b0f8eb862e
commit 9c91992603
3 changed files with 32 additions and 5 deletions
+7
View File
@@ -483,6 +483,13 @@ class Client {
console.log(`joining lobby ${lobbyId}`);
}
}
if (decodedHash.startsWith("#affiliate=")) {
const affiliateCode = decodedHash.replace("#affiliate=", "");
strip();
if (affiliateCode) {
this.patternsModal.open(affiliateCode);
}
}
}
private async handleJoinLobby(event: CustomEvent<JoinLobbyEvent>) {
+24 -5
View File
@@ -27,6 +27,8 @@ export class TerritoryPatternsModal extends LitElement {
private isActive = false;
private affiliateCode: string | null = null;
constructor() {
super();
}
@@ -51,6 +53,17 @@ export class TerritoryPatternsModal extends LitElement {
private renderPatternGrid(): TemplateResult {
const buttons: TemplateResult[] = [];
for (const [name, pattern] of this.patterns) {
if (this.affiliateCode === null) {
if (pattern.affiliateCode !== null && pattern.product !== null) {
// Patterns with affiliate code are not for sale by default.
continue;
}
} else {
if (pattern.affiliateCode !== this.affiliateCode) {
continue;
}
}
buttons.push(html`
<pattern-button
.pattern=${pattern}
@@ -65,10 +78,14 @@ export class TerritoryPatternsModal extends LitElement {
class="flex flex-wrap gap-4 p-2"
style="justify-content: center; align-items: flex-start;"
>
<pattern-button
.pattern=${null}
.onSelect=${(p: Pattern | null) => this.selectPattern(null)}
></pattern-button>
${this.affiliateCode === null
? html`
<pattern-button
.pattern=${null}
.onSelect=${(p: Pattern | null) => this.selectPattern(null)}
></pattern-button>
`
: html``}
${buttons}
</div>
`;
@@ -86,13 +103,15 @@ export class TerritoryPatternsModal extends LitElement {
`;
}
public async open() {
public async open(affiliateCode?: string) {
this.isActive = true;
this.affiliateCode = affiliateCode ?? null;
await this.refresh();
}
public close() {
this.isActive = false;
this.affiliateCode = null;
this.modalEl?.close();
}
+1
View File
@@ -44,6 +44,7 @@ export const PatternSchema = z
export const PatternInfoSchema = z.object({
name: PatternNameSchema,
pattern: PatternSchema,
affiliateCode: z.string().nullable(),
product: ProductSchema.nullable(),
});