Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Homemade badges #1894

Merged
merged 3 commits into from
Sep 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion app/db/tables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,12 @@ export interface ArtUserMetadata {
}

export interface Badge {
id: GeneratedAlways<number>;
code: string;
displayName: string;
hue: number | null;
id: GeneratedAlways<number>;
/** Who made the badge? If null, a legacy badge. */
authorId: number | null;
}

export interface BadgeManager {
Expand Down
8 changes: 7 additions & 1 deletion app/features/badges/BadgeRepository.server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { jsonArrayFrom } from "kysely/helpers/sqlite";
import { jsonArrayFrom, jsonObjectFrom } from "kysely/helpers/sqlite";
import { db } from "~/db/sql";
import { COMMON_USER_FIELDS } from "~/utils/kysely.server";
import type { Unwrapped } from "~/utils/types";
Expand All @@ -17,6 +17,12 @@ export async function all() {
.whereRef("BadgeManager.badgeId", "=", "Badge.id")
.select(["userId"]),
).as("managers"),
jsonObjectFrom(
eb
.selectFrom("User")
.select(COMMON_USER_FIELDS)
.whereRef("User.id", "=", "Badge.authorId"),
).as("author"),
])
.execute();

Expand Down
2 changes: 1 addition & 1 deletion app/features/badges/components/BadgeDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type { Unpacked } from "~/utils/types";
import { badgeExplanationText } from "../badges-utils";

interface BadgeDisplayProps {
badges: Array<Tables["Badge"] & { count?: number }>;
badges: Array<Omit<Tables["Badge"], "authorId"> & { count?: number }>;
onBadgeRemove?: (badgeId: number) => void;
}

Expand Down
17 changes: 17 additions & 0 deletions app/features/badges/homemade.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
interface BadgeInfo {
// The name of the badge as it shows on the web page: "Awarded for winning {displayName}"
displayName: string;
// The file name of the badge: fileName.png, fileName.avif & fileName.gif
fileName: string;
// The Discord ID of the person who made the badge (not the person who commissioned it)
authorDiscordId: string;
}

export const homemadeBadges: BadgeInfo[] = [
// EXAMPLE
// {
// displayName: "Example Badge",
// fileName: "example",
// authorDiscordId: "123456789012345678",
// },
];
13 changes: 7 additions & 6 deletions app/features/badges/routes/badges.$id.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,15 @@ export default function BadgeDetailsPage() {
<div className="badges__explanation">
{badgeExplanationText(t, badge)}
</div>
<div
className={clsx("badges__managers", {
invisible: data.managers.length === 0,
})}
>
<div className="badges__managers">
{t("managedBy", {
users: data.managers.map((m) => m.username).join(", "),
users: data.managers.map((m) => m.username).join(", ") || "???",
})}{" "}
(
{t("madeBy", {
user: badge.author?.username ?? "borzoic",
})}
)
</div>
</div>
{isMod(user) || canEditBadgeOwners({ user, managers: data.managers }) ? (
Expand Down
15 changes: 3 additions & 12 deletions app/features/badges/routes/badges.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { SerializeFrom } from "@remix-run/node";
import { NavLink, Outlet, useLoaderData } from "@remix-run/react";
import { Link, NavLink, Outlet, useLoaderData } from "@remix-run/react";
import * as React from "react";
import { Trans } from "react-i18next";
import { useTranslation } from "react-i18next";
import { Badge } from "~/components/Badge";
import { Divider } from "~/components/Divider";
Expand All @@ -10,7 +9,7 @@ import { Main } from "~/components/Main";
import { SearchIcon } from "~/components/icons/Search";
import { useUser } from "~/features/auth/core/user";
import type { SendouRouteHandle } from "~/utils/remix";
import { BADGES_PAGE, BORZOIC_TWITTER, navIconUrl } from "~/utils/urls";
import { BADGES_PAGE, FAQ_PAGE, navIconUrl } from "~/utils/urls";
import * as BadgeRepository from "../BadgeRepository.server";

import "~/styles/badges.css";
Expand Down Expand Up @@ -98,16 +97,8 @@ export default function BadgesPageLayout() {
</div>
<div className="badges__general-info-texts">
<p>
<Trans i18nKey="madeBy" t={t}>
Badges by{" "}
<a href={BORZOIC_TWITTER} target="_blank" rel="noreferrer">
borzoic
</a>
</Trans>
</p>
{/* <p>
<Link to={FAQ_PAGE}>{t("forYourEvent")}</Link>
</p> */}
</p>
</div>
</Main>
);
Expand Down
62 changes: 62 additions & 0 deletions docs/badges.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Badge guide

## What are badges?

Badges are a virtual prize that users can win from tournaments and then display on their profile.

Current list of them can be seen here https://sendou.ink/badges

## Rules

Any badge to be added has to follow these rules. Badges that do not follow the rules will not be added to the site or can be removed at any time:

**Quality and consistency** has to be matching those already on the site.

**Uniqueness** i.e. the added badge has to be different enough from those already on the site.

**Variations** for a maximum of 3 per unique design (e.g. 1st, 2nd & 3rd place variations). For further additions it has be a completely different and unique design and not just recolor or small design changes.

**No AI** every badge has to be made by a human.

**Made for sendou.ink/approved usage** badges have to be commissioned for this exact purpose or otherwise agreed with the person who made them so that they fully understand what they are used for.

**As tournament prizes** the badge are meant to be given out as tournament prizes (awarded for reaching certain placement). If you have any other idea in mind then you need to check that with Sendou separately.

## Adding a new badge

1. First badge needs to be made
- 3D artists can use [picoCAD](https://johanpeitz.itch.io/picocad)
- Others can use the "[badges" Discord channel](https://discord.gg/sendou) to inquire about a commission
- Read rules from above carefully at this point and ask if you do not understand something
2. Create needed files
- .gif file, black solid background. Create via [picoCAD Web Viewer](https://lucatronica.github.io/picocad-web-viewer/)
- .png file. TODO: info on how to ceate
- .avif file. Create via e.g. [Squoosh](https://squoosh.app/) from the .png file
- All files should be squares. 512x512 is a good size for example
3. Make a pull request to the project
- You can request someone to help you on the ["development" Discord channel](https://discord.gg/sendou)
- In the PR add the 3 needed files to public/static-assets/badges folder:

![alt text](img/badges-1.png)

- Also update app/features/badges/homemade.ts file (read the comments to understand each value):

![alt text](img/badges-2.png)

4. Wait for Sendou to look into the pull request
- Sometimes this can take a while if Sendou is busy
- Changes might be requested

5. Wait for the site to be updated
- After the pull request is merged it does not automatically go to the site yet
- Normally the site updates a couple times a week, but this can vary

6. Request permissions
- After you see your badge on the /badges page you can request manager permissions to it on from the staff on the helpdesk channel

7. Give out the badges to tournament winners
- Badge can be given out via the /badges page

## Updating a badge

Make a new pull request making the changes you need. The `fileName` should always remain the same.
Binary file added docs/img/badges-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/badges-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion locales/da/badges.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"tournament_one": "Tildeldt for at vinde {{tournament}}",
"tournament_other": "Tildeldt for at vinde {{tournament}} (×{{count}})",
"forYourEvent": "Mærke til dit arrangement?",
"madeBy": "Mærker er lavet af <2>borzoic</2>",
"managedBy": "Administreres af {{users}}",
"own.divider": "Administrerede mærker",
"other.divider": "Andre mærker"
Expand Down
1 change: 0 additions & 1 deletion locales/de/badges.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,5 @@
"tournament_one": "Verliehen für den Sieg von {{tournament}}",
"tournament_other": "Verliehen für den Sieg von {{tournament}} (×{{count}})",
"forYourEvent": "Abzeichen für dein Event?",
"madeBy": "Abzeichen von <2>borzoic</2>",
"managedBy": "Verwaltet durch {{users}}"
}
2 changes: 1 addition & 1 deletion locales/en/badges.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"tournament_one": "Awarded for winning {{tournament}}",
"tournament_other": "Awarded for winning {{tournament}} (×{{count}})",
"forYourEvent": "Badge for your event?",
"madeBy": "Badges by <2>borzoic</2>",
"managedBy": "Managed by {{users}}",
"madeBy": "made by {{user}}",
"own.divider": "Managed badges",
"other.divider": "Other badges"
}
1 change: 0 additions & 1 deletion locales/es-ES/badges.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"tournament_one": "Recibido por ganar {{tournament}}",
"tournament_other": "Recibido por ganar {{tournament}} (×{{count}})",
"forYourEvent": "¿Insignia para tu evento?",
"madeBy": "Insignia por <2>borzoic</2>",
"managedBy": "Administrado por {{users}}",
"own.divider": "Insignias administradas",
"other.divider": "Otras insignias"
Expand Down
1 change: 0 additions & 1 deletion locales/es-US/badges.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"tournament_one": "Recibido por ganar {{tournament}}",
"tournament_other": "Recibido por ganar {{tournament}} (×{{count}})",
"forYourEvent": "¿Insignia para tu evento?",
"madeBy": "Insignia por <2>borzoic</2>",
"managedBy": "Administrado por {{users}}",
"own.divider": "Insignias administradas",
"other.divider": "Otras insignias"
Expand Down
1 change: 0 additions & 1 deletion locales/fr-CA/badges.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,5 @@
"tournament_one": "Attribué pour avoir gagné {{tournament}}",
"tournament_other": "Attribué pour avoir gagné {{tournament}} (×{{count}})",
"forYourEvent": "Un badge pour votre événement ?",
"madeBy": "Badges créés par <2>borzoic</2>",
"managedBy": "Géré par {{users}}"
}
1 change: 0 additions & 1 deletion locales/fr-EU/badges.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,5 @@
"tournament_one": "Attribué pour avoir gagné {{tournament}}",
"tournament_other": "Attribué pour avoir gagné {{tournament}} (×{{count}})",
"forYourEvent": "Un badge pour votre événement ?",
"madeBy": "Badges créés par <2>borzoic</2>",
"managedBy": "Géré par {{users}}"
}
1 change: 0 additions & 1 deletion locales/he/badges.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,5 @@
"tournament_one": "מוענק עבור ניצחון {{tournament}}",
"tournament_other": "מוענק עבור ניצחון {{tournament}} (×{{count}})",
"forYourEvent": "תג לאירוע שלכם?",
"madeBy": "תגים מאת <2>borzoic</2>",
"managedBy": "מנוהל על ידי {{users}}"
}
1 change: 0 additions & 1 deletion locales/it/badges.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,5 @@
"tournament_one": "Premio per aver vinto {{tournament}}",
"tournament_other": "Premio per aver vinto {{tournament}} (×{{count}})",
"forYourEvent": "Vuoi creare una medaglia per il tuo evento?",
"madeBy": "Medaglie fatte da <2>borzoic</2>",
"managedBy": "Gestito da {{users}}"
}
1 change: 0 additions & 1 deletion locales/ja/badges.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"tournament_one": "{{tournament}} の勝者",
"tournament_other": "{{tournament}} の勝者 (×{{count}})",
"forYourEvent": "イベント専用のバッジ?",
"madeBy": "<2>borzoic</2> によって作成されたバッジ",
"managedBy": "{{users}} によって管理されています",
"own.divider": "管理しているバッジ",
"other.divider": "他のバッジ"
Expand Down
1 change: 0 additions & 1 deletion locales/ko/badges.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,5 @@
"tournament_one": "{{tournament}} 우승 기념",
"tournament_other": "{{tournament}} (×{{count}}) 우승 기념",
"forYourEvent": "이벤트에 배지를 원하나요?",
"madeBy": "<2>borzoic</2> 제작",
"managedBy": "{{users}}가 관리"
}
1 change: 0 additions & 1 deletion locales/nl/badges.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,5 @@
"tournament_one": "Uitgereikt voor het winnen van {{tournament}}",
"tournament_other": "Uitgereikt voor het winnen van {{tournament}} (×{{count}})",
"forYourEvent": "Ook een badge voor jouw evenement?",
"madeBy": "Badges gemaakt door <2>borzoic</2>",
"managedBy": "Beheerd door {{users}}"
}
1 change: 0 additions & 1 deletion locales/pl/badges.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,5 @@
"tournament_one": "Nagrodzony/a za wygranie {{tournament}}",
"tournament_other": "Nagrodzony/a {{tournament}} (×{{count}})",
"forYourEvent": "Odznaka dla twojego eventu?",
"madeBy": "Odznaki robione przez <2>borzoic</2>",
"managedBy": "Zarządzane przez {{users}}"
}
1 change: 0 additions & 1 deletion locales/pt-BR/badges.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"tournament_one": "Premiado(a) por vencer o(a) {{tournament}}",
"tournament_other": "Premiado(a) por vencer o(a) {{tournament}} (×{{count}})",
"forYourEvent": "Quer insígnia(s) para o seu evento?",
"madeBy": "Insígnias por <2>borzoic</2>",
"managedBy": "Gerenciado por/pela {{users}}",
"own.divider": "Ingínias gerenciadas",
"other.divider": "Outras insígnias"
Expand Down
1 change: 0 additions & 1 deletion locales/ru/badges.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,5 @@
"tournament_one": "Награда за победу в {{tournament}}",
"tournament_other": "Награда за победу в {{tournament}} (×{{count}})",
"forYourEvent": "Значок для вашего события?",
"madeBy": "Значки от <2>borzoic</2>",
"managedBy": "Выдаётся {{users}}"
}
1 change: 0 additions & 1 deletion locales/zh/badges.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"tournament_one": "{{tournament}}冠军奖励",
"tournament_other": "{{tournament}} (×{{count}})冠军奖励",
"forYourEvent": "在我的活动中设置徽章",
"madeBy": "由<2>borzoic</2>设计",
"managedBy": "由{{users}}管理",
"own.divider": "管理徽章",
"other.divider": "其他徽章"
Expand Down
9 changes: 9 additions & 0 deletions migrations/071-homemade-badges.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export function up(db) {
db.transaction(() => {
db.prepare(/* sql */ `alter table "Badge" add "authorId" integer`).run();
})();

db.prepare(
/* sql */ `create index badge_author_id on "Badge"("authorId")`,
).run();
}
Loading
Loading