Bye bye .png
... well mostly. Some places will still use it like PWA splash screens. Otherwise browser support for .avif strong enough now to do this now.
@@ -18,7 +18,7 @@ STORAGE_REGION=us-east-1
|
||||
STORAGE_BUCKET=sendou
|
||||
STORAGE_URL=http://127.0.0.1:9000
|
||||
|
||||
VITE_TOURNAMENT_DEFAULT_LOGO="tournament-logo-default.png"
|
||||
VITE_TOURNAMENT_DEFAULT_LOGO="tournament-logo-default.avif"
|
||||
|
||||
// Twitch integration for fetching streams
|
||||
TWITCH_CLIENT_ID=
|
||||
|
||||
@@ -7,4 +7,4 @@ BASE_URL=https://example.com
|
||||
SKALOP_SYSTEM_MESSAGE_URL=http://skalop.test
|
||||
SKALOP_TOKEN=test
|
||||
|
||||
VITE_TOURNAMENT_DEFAULT_LOGO="tournament-logo-test.png"
|
||||
VITE_TOURNAMENT_DEFAULT_LOGO="tournament-logo-test.avif"
|
||||
|
||||
@@ -35,7 +35,6 @@ interface ImageProps {
|
||||
testId?: string;
|
||||
onClick?: () => void;
|
||||
loading?: "lazy";
|
||||
forcePng?: boolean;
|
||||
}
|
||||
|
||||
export function Image({
|
||||
@@ -52,50 +51,18 @@ export function Image({
|
||||
containerStyle,
|
||||
onClick,
|
||||
loading,
|
||||
forcePng,
|
||||
}: ImageProps) {
|
||||
if (forcePng) {
|
||||
return (
|
||||
// biome-ignore lint/a11y/noStaticElementInteractions: Biome v2 migration
|
||||
<div
|
||||
title={title}
|
||||
className={containerClassName}
|
||||
style={containerStyle}
|
||||
onClick={onClick}
|
||||
>
|
||||
<img
|
||||
alt={alt}
|
||||
src={`${path}.png`}
|
||||
className={className}
|
||||
width={size ?? width}
|
||||
height={size ?? height}
|
||||
style={style}
|
||||
draggable="false"
|
||||
loading={loading}
|
||||
data-testid={testId}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
// biome-ignore lint/a11y/noStaticElementInteractions: Biome v2 migration
|
||||
<picture
|
||||
<div
|
||||
title={title}
|
||||
className={containerClassName}
|
||||
style={containerStyle}
|
||||
onClick={onClick}
|
||||
>
|
||||
<source
|
||||
type="image/avif"
|
||||
srcSet={`${path}.avif`}
|
||||
width={width}
|
||||
height={height}
|
||||
style={style}
|
||||
/>
|
||||
<img
|
||||
alt={alt}
|
||||
src={`${path}.png`}
|
||||
src={`${path}.avif`}
|
||||
className={className}
|
||||
width={size ?? width}
|
||||
height={size ?? height}
|
||||
@@ -104,7 +71,7 @@ export function Image({
|
||||
loading={loading}
|
||||
data-testid={testId}
|
||||
/>
|
||||
</picture>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -2729,7 +2729,7 @@ async function notifications() {
|
||||
{
|
||||
type: "TO_CHECK_IN_OPENED",
|
||||
meta: { tournamentId: 1, tournamentName: "PICNIC #2" },
|
||||
pictureUrl: "/static-assets/img/tournament-logos/pn.png",
|
||||
pictureUrl: "/static-assets/img/tournament-logos/pn.avif",
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ function EventSchedule({
|
||||
<div className="stack md horizontal items-center mx-auto">
|
||||
<img
|
||||
alt=""
|
||||
src={`http://localhost:5173/static-assets/img/tournament-logos/${imgId}.png`}
|
||||
src={`http://localhost:5173/static-assets/img/tournament-logos/${imgId}.avif`}
|
||||
style={{
|
||||
width: "4rem",
|
||||
height: "4rem",
|
||||
|
||||
@@ -117,7 +117,7 @@ export const loader = async ({ params }: LoaderFunctionArgs) => {
|
||||
name: badge.displayName,
|
||||
count: badge.count,
|
||||
gifUrl: `https://sendou.ink/static-assets/badges/${badge.code}.gif`,
|
||||
imageUrl: `https://sendou.ink/static-assets/badges/${badge.code}.png`,
|
||||
imageUrl: `https://sendou.ink/static-assets/badges/${badge.code}.avif`,
|
||||
})),
|
||||
teams: user.teams.map((team) => ({
|
||||
id: team.id,
|
||||
|
||||
@@ -152,7 +152,7 @@ export interface GetTournamentResponse {
|
||||
*/
|
||||
url: string;
|
||||
/**
|
||||
* @example "https://sendou.ink/static-assets/img/tournament-logos/itz.png"
|
||||
* @example "https://sendou.ink/static-assets/img/tournament-logos/itz.avif"
|
||||
*/
|
||||
logoUrl: string | null;
|
||||
/**
|
||||
@@ -458,7 +458,7 @@ type Badge = {
|
||||
name: string;
|
||||
count: number;
|
||||
/**
|
||||
* @example "https://sendou.ink/static-assets/badges/monday.png"
|
||||
* @example "https://sendou.ink/static-assets/badges/monday.avif"
|
||||
*/
|
||||
imageUrl: string;
|
||||
/**
|
||||
|
||||
@@ -434,7 +434,7 @@ function WeaponImageSelector() {
|
||||
<DraggableWeaponButton
|
||||
key={weaponId}
|
||||
id={`main-${weaponId}`}
|
||||
src={`${outlinedMainWeaponImageUrl(weaponId)}.png`}
|
||||
src={`${outlinedMainWeaponImageUrl(weaponId)}.avif`}
|
||||
imgPath={mainWeaponImageUrl(weaponId)}
|
||||
previewPath={outlinedMainWeaponImageUrl(weaponId)}
|
||||
alt={t(`weapons:MAIN_${weaponId}`)}
|
||||
@@ -458,7 +458,7 @@ function WeaponImageSelector() {
|
||||
<DraggableWeaponButton
|
||||
key={subWeaponId}
|
||||
id={`sub-${subWeaponId}`}
|
||||
src={`${subWeaponImageUrl(subWeaponId)}.png`}
|
||||
src={`${subWeaponImageUrl(subWeaponId)}.avif`}
|
||||
imgPath={subWeaponImageUrl(subWeaponId)}
|
||||
previewPath={subWeaponImageUrl(subWeaponId)}
|
||||
alt={t(`weapons:SUB_${subWeaponId}`)}
|
||||
@@ -485,7 +485,7 @@ function WeaponImageSelector() {
|
||||
<DraggableWeaponButton
|
||||
key={specialWeaponId}
|
||||
id={`special-${specialWeaponId}`}
|
||||
src={`${specialWeaponImageUrl(specialWeaponId)}.png`}
|
||||
src={`${specialWeaponImageUrl(specialWeaponId)}.avif`}
|
||||
imgPath={specialWeaponImageUrl(specialWeaponId)}
|
||||
previewPath={specialWeaponImageUrl(specialWeaponId)}
|
||||
alt={t(`weapons:SPECIAL_${specialWeaponId}`)}
|
||||
@@ -507,7 +507,7 @@ function WeaponImageSelector() {
|
||||
<DraggableWeaponButton
|
||||
key={mode}
|
||||
id={`mode-${mode}`}
|
||||
src={`${modeImageUrl(mode)}.png`}
|
||||
src={`${modeImageUrl(mode)}.avif`}
|
||||
imgPath={modeImageUrl(mode)}
|
||||
previewPath={modeImageUrl(mode)}
|
||||
alt={t(`game-misc:MODE_LONG_${mode}`)}
|
||||
|
||||
@@ -151,7 +151,7 @@ function MapButton({
|
||||
[styles.mapButtonWiggle]: wiggle,
|
||||
[styles.mapButtonGreyedOut]: selected || banned || tiebreaker,
|
||||
})}
|
||||
style={{ "--map-image-url": `url("${stageImageUrl(stageId)}.png")` }}
|
||||
style={{ "--map-image-url": `url("${stageImageUrl(stageId)}.avif")` }}
|
||||
onClick={onClick}
|
||||
disabled={banned}
|
||||
type="button"
|
||||
|
||||
@@ -136,10 +136,10 @@ export async function getSendouQSidebarStreams(): Promise<
|
||||
id: `sendouq-${matchId}`,
|
||||
name: `Match #${matchId}`,
|
||||
imageUrl: averageTier
|
||||
? `${tierImageUrl(averageTier.name)}.png`
|
||||
: `${navIconUrl("sendouq")}.png`,
|
||||
? `${tierImageUrl(averageTier.name)}.avif`
|
||||
: `${navIconUrl("sendouq")}.avif`,
|
||||
overlayIconUrl: averageTier
|
||||
? `${navIconUrl("sendouq")}.png`
|
||||
? `${navIconUrl("sendouq")}.avif`
|
||||
: undefined,
|
||||
url: SENDOUQ_STREAMS_PAGE,
|
||||
subtitle: averageTier
|
||||
|
||||
@@ -217,9 +217,9 @@ describe("GroupCard", () => {
|
||||
}),
|
||||
});
|
||||
|
||||
// Weapons are rendered as picture elements
|
||||
const pictures = screen.container.querySelectorAll("picture");
|
||||
expect(pictures.length).toBeGreaterThan(0);
|
||||
// Weapons are rendered as img elements
|
||||
const images = screen.container.querySelectorAll("img");
|
||||
expect(images.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -375,7 +375,7 @@ export function tournamentToSidebarEvent(
|
||||
};
|
||||
}
|
||||
|
||||
const SCRIMS_ICON_URL = `${navIconUrl("scrims")}.png`;
|
||||
const SCRIMS_ICON_URL = `${navIconUrl("scrims")}.avif`;
|
||||
|
||||
export function scrimToSidebarEvent(s: SidebarScrim): SidebarEvent {
|
||||
return {
|
||||
|
||||
@@ -7,10 +7,9 @@ import { TierListItemImage } from "./TierListItemImage";
|
||||
|
||||
interface DraggableItemProps {
|
||||
item: TierListItem;
|
||||
forcePng?: boolean;
|
||||
}
|
||||
|
||||
export function DraggableItem({ item, forcePng }: DraggableItemProps) {
|
||||
export function DraggableItem({ item }: DraggableItemProps) {
|
||||
const uniqueId = tierListItemId(item);
|
||||
|
||||
const {
|
||||
@@ -33,7 +32,7 @@ export function DraggableItem({ item, forcePng }: DraggableItemProps) {
|
||||
return (
|
||||
<div ref={setNodeRef} className={styles.item} style={style}>
|
||||
<div data-item-id={uniqueId} {...listeners} {...attributes}>
|
||||
<TierListItemImage item={item} forcePng={forcePng} />
|
||||
<TierListItemImage item={item} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -11,58 +11,38 @@ import styles from "./TierListItemImage.module.css";
|
||||
|
||||
interface TierListItemImageProps {
|
||||
item: TierListItem;
|
||||
forcePng?: boolean;
|
||||
}
|
||||
|
||||
export function TierListItemImage({ item, forcePng }: TierListItemImageProps) {
|
||||
export function TierListItemImage({ item }: TierListItemImageProps) {
|
||||
switch (item.type) {
|
||||
case "main-weapon":
|
||||
return (
|
||||
<div className={styles.imageWrapper}>
|
||||
<WeaponImage
|
||||
weaponSplId={item.id}
|
||||
variant="badge"
|
||||
size={48}
|
||||
forcePng={forcePng}
|
||||
/>
|
||||
<WeaponImage weaponSplId={item.id} variant="badge" size={48} />
|
||||
</div>
|
||||
);
|
||||
case "sub-weapon":
|
||||
return (
|
||||
<div className={styles.imageWrapper}>
|
||||
<SubWeaponImage subWeaponId={item.id} size={48} forcePng={forcePng} />
|
||||
<SubWeaponImage subWeaponId={item.id} size={48} />
|
||||
</div>
|
||||
);
|
||||
case "special-weapon":
|
||||
return (
|
||||
<div className={styles.imageWrapper}>
|
||||
<SpecialWeaponImage
|
||||
specialWeaponId={item.id}
|
||||
size={48}
|
||||
forcePng={forcePng}
|
||||
/>
|
||||
<SpecialWeaponImage specialWeaponId={item.id} size={48} />
|
||||
</div>
|
||||
);
|
||||
case "stage":
|
||||
return (
|
||||
<div className={styles.imageWrapper}>
|
||||
<StageImage
|
||||
stageId={item.id}
|
||||
width={80}
|
||||
className="rounded-sm"
|
||||
forcePng={forcePng}
|
||||
/>
|
||||
<StageImage stageId={item.id} width={80} className="rounded-sm" />
|
||||
</div>
|
||||
);
|
||||
case "mode":
|
||||
return (
|
||||
<div className={styles.imageWrapper}>
|
||||
<ModeImage
|
||||
mode={item.id}
|
||||
width={48}
|
||||
height={48}
|
||||
forcePng={forcePng}
|
||||
/>
|
||||
<ModeImage mode={item.id} width={48} height={48} />
|
||||
</div>
|
||||
);
|
||||
case "stage-mode": {
|
||||
@@ -71,18 +51,12 @@ export function TierListItemImage({ item, forcePng }: TierListItemImageProps) {
|
||||
return (
|
||||
<div className={styles.imageWrapper}>
|
||||
<div className="relative">
|
||||
<StageImage
|
||||
stageId={stageId}
|
||||
width={80}
|
||||
className="rounded-sm"
|
||||
forcePng={forcePng}
|
||||
/>
|
||||
<StageImage stageId={stageId} width={80} className="rounded-sm" />
|
||||
<ModeImage
|
||||
mode={mode as ModeShort}
|
||||
width={24}
|
||||
height={24}
|
||||
className={styles.modeOverlay}
|
||||
forcePng={forcePng}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -163,11 +163,7 @@ export function TierRow({ tier }: TierRowProps) {
|
||||
strategy={horizontalListSortingStrategy}
|
||||
>
|
||||
{items.map((item) => (
|
||||
<DraggableItem
|
||||
key={tierListItemId(item)}
|
||||
item={item}
|
||||
forcePng={screenshotMode}
|
||||
/>
|
||||
<DraggableItem key={tierListItemId(item)} item={item} />
|
||||
))}
|
||||
</SortableContext>
|
||||
) : null}
|
||||
|
||||
@@ -208,7 +208,7 @@ function MapButton({
|
||||
className={clsx(styles.mapButton, {
|
||||
[styles.mapButtonGreyedOut]: selected || disabled,
|
||||
})}
|
||||
style={{ "--map-image-url": `url("${stageImageUrl(stageId)}.png")` }}
|
||||
style={{ "--map-image-url": `url("${stageImageUrl(stageId)}.avif")` }}
|
||||
onClick={onClick}
|
||||
type="button"
|
||||
disabled={!onClick}
|
||||
|
||||
@@ -288,7 +288,7 @@ function FancyStageBanner({
|
||||
const gamesCompleted = data.results.length;
|
||||
|
||||
const stageNameToBannerImageUrl = (stageId: StageId) => {
|
||||
return `${stageImageUrl(stageId)}.png`;
|
||||
return `${stageImageUrl(stageId)}.avif`;
|
||||
};
|
||||
|
||||
const banPickingTeam = () => {
|
||||
|
||||
@@ -2192,7 +2192,7 @@ export const SWIM_OR_SINK_167 = (
|
||||
},
|
||||
],
|
||||
},
|
||||
logoUrl: "test.png",
|
||||
logoUrl: "test.avif",
|
||||
author: {
|
||||
id: 1402,
|
||||
username: "grace",
|
||||
|
||||
@@ -1459,7 +1459,7 @@ export const PADDLING_POOL_257 = () =>
|
||||
description:
|
||||
"Hosted by Dapple Productions.\n\nThe longest tournament series in Splatoon!\nEvery week a tournament!\n\n✓ DE or Groups into SE\n✓ All Modes (Picnic system)\n✓ Badge prize\n✓ A well-ran tournament experience\n\nCome join!",
|
||||
rules: null,
|
||||
logoUrl: "/test.png",
|
||||
logoUrl: "/test.avif",
|
||||
startTime: 1709748000,
|
||||
author: {
|
||||
id: 860,
|
||||
@@ -8214,7 +8214,7 @@ export const PADDLING_POOL_255 = () =>
|
||||
name: "Paddling Pool 255",
|
||||
description: null,
|
||||
rules: null,
|
||||
logoUrl: "/test.png",
|
||||
logoUrl: "/test.avif",
|
||||
startTime: 1708538400,
|
||||
author: {
|
||||
id: 860,
|
||||
@@ -15300,7 +15300,7 @@ export const IN_THE_ZONE_32 = ({
|
||||
name: "In The Zone 32",
|
||||
description: "Part of sendou.ink ranked season 2",
|
||||
rules: null,
|
||||
logoUrl: "/test.png",
|
||||
logoUrl: "/test.avif",
|
||||
startTime: 1707588000,
|
||||
author: {
|
||||
id: 274,
|
||||
|
||||
@@ -67,7 +67,7 @@ export const testTournament = ({
|
||||
tentativeTier: null,
|
||||
parentTournamentId: null,
|
||||
rules: null,
|
||||
logoUrl: "/test.png",
|
||||
logoUrl: "/test.avif",
|
||||
discordUrl: null,
|
||||
startTime: 1705858842,
|
||||
isFinalized: 0,
|
||||
|
||||
@@ -14,7 +14,7 @@ Check out `sendou.ink/app/features/api-public/schema.ts`
|
||||
|
||||
```bash
|
||||
sendou@macbook ~ % curl -H "Authorization: Bearer mytoken" https://sendou.ink/api/tournament/1
|
||||
{"name":"PICNIC mini","startTime":"2023-05-18T18:00:00.000Z","url":"https://sendou.ink/to/1/brackets","logoUrl":"https://sendou.ink/static-assets/img/tournament-logos/pn.png","teams":{"checkedInCount":25,"registeredCount":31},"brackets":[{"name":"Main bracket","type":"double_elimination"}],"organizationId":1,"isFinalized":true}%
|
||||
{"name":"PICNIC mini","startTime":"2023-05-18T18:00:00.000Z","url":"https://sendou.ink/to/1/brackets","logoUrl":"https://sendou.ink/static-assets/img/tournament-logos/pn.avif","teams":{"checkedInCount":25,"registeredCount":31},"brackets":[{"name":"Main bracket","type":"double_elimination"}],"organizationId":1,"isFinalized":true}%
|
||||
```
|
||||
|
||||
## Clients (unofficial)
|
||||
|
||||
|
Before Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 7.1 KiB |
|
Before Width: | Height: | Size: 8.0 KiB |
|
Before Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 7.1 KiB |
|
Before Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 8.0 KiB |
|
Before Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 803 B |
|
Before Width: | Height: | Size: 815 B |
|
Before Width: | Height: | Size: 784 B |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 7.1 KiB |
|
Before Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 840 B |
|
Before Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 14 KiB |