sendou.ink/app/components/Image.tsx
Kalle e7bbb565be
SendouQ (#1455)
* Tables

* Clocks

* Maplist preference selector

* Fix SSR

* Nav icon

* RankedOrScrim

* Map pool

* Create group

* Redirect logic

* Persist map pool

* Advance from preparing page

* Rename query

* Fix merge

* Fix migration order

* Seed groups

* Find looking groups SQL

* Renders something

* More UI work

* Back to 30min

* Likes/dislikes

* Always return own group

* Fix like order

* 3 tc/rm/cb -> 2

* Show only 3 weapons

* Pass group size

* Handle both liked and liked by same group

* Fix SQL

* Group preference frontend work

* Morphing

* Styling

* Don't show group controls if not manager

* Give/remove manager

* Leave group

* Leave with confirm

* Delete likes when morphing groups

* Clocks consistency

* Remove bad invariant

* Persist settings to local storage

* Fix initial value flashing

* Fix never resolving loading indicator

* REFRESH_GROUP

* Flip animations

* Tweaks

* Auto refresh logic

* Groups of 4 seed

* Reduce throwing

* Load full groups initial

* Create match

* Match UI initial

* Score reporter initial

* Push footer down on match page

* Score reporter knows when set ended

* Score reporting untested

* Show score after report

* Align better

* Look again with same group functionality

* More migrations

* Team on match page

* Show confirmer before reporting score

* Report weapons

* Report weapos again by admin + skill changing

* Handle no tiebreaker given to MapPool

* Remove unranked

* Remove support for "team id skill"

* no-wrap -> nowrap

* Preparing page work

* Use common GroupCard component

* Add some metas

* MemberAdder in looking page

* Fix GroupCard actions

* Fix SZ only map list including other modes

* Add season info

* Prompt login

* Joining team

* Manage group on preparing page

* Manage group on preparing page

* Seed past matches

* Add to seed

* No map list preference when full group + fix expiry

* Fix skill matchesCount calculation

* Tiers initial work

* Some progress on tiers

* Tiering logic

* MMR in group cards

* Name to challenge

* Team MMR

* Big team rank icons

* Adjust todos

* Match score report with confirm

* Allow regular members to report score

* Handle reporting weapons edge cases

* Add tier images

* Improve GroupCard spacing

* Refactor looking page

* Looking mobile UI

* Calculate skill only for current season

* Divide groups visually when reporting weapons

* Fix match page weapons sorting

* Add cache to user skills+tier calculation

* Admin report match score

* Initial leaderboard

* Cached leaderboard

* Weapon category lb's

* Populate SkillTeamUser in SendouQ

* Team leaderboard filtered down

* Add TODOs

* Seasons initlal

* Season weapons initial

* Weapons stylized

* Show rest weapons as +

* Hide peak if same as current

* Load matches SQL initial

* Season matches UI initial

* Take user id in account

* Add weapons

* Paginated matches

* Fix pages count logic

* Scroll top on data change

* Day headers for matches

* Link from user page to user seasons page

* Summarize maps + ui initial

* Map stats

* Player info tabs

* MMR chart

* Chart adjustments

* Handle basing team MMR on player MMR

* Set initial MMR

* Add info about discord to match page

* Season support to tournaments

* Get tournament skills as well for the graph

* WIP

* New team rating logic + misc other

* tiered -> tiered.server

* Update season starting time

* TODOs

* Add rules page

* Hide elements correctly when off-season

* Fix crash when only one player with skill

* How-to video

* Fix StartRank showing when not logged in

* Make user leaderboard the default

* Make Skill season non-nullable

* Add suggested pass to match

* Add rule

* identifierToUserIds helper

* Fix tiers not showing
2023-08-12 22:42:54 +03:00

163 lines
3.6 KiB
TypeScript

import type { TierName } from "~/features/mmr/mmr-constants";
import { useTranslation } from "~/hooks/useTranslation";
import type { MainWeaponId, ModeShort, StageId } from "~/modules/in-game-lists";
import {
TIER_PLUS_URL,
mainWeaponImageUrl,
modeImageUrl,
outlinedFiveStarMainWeaponImageUrl,
outlinedMainWeaponImageUrl,
stageImageUrl,
tierImageUrl,
} from "~/utils/urls";
interface ImageProps {
path: string;
alt: string;
title?: string;
className?: string;
containerClassName?: string;
width?: number;
height?: number;
size?: number;
style?: React.CSSProperties;
testId?: string;
onClick?: () => void;
}
export function Image({
path,
alt,
title,
className,
width,
height,
size,
style,
testId,
containerClassName,
onClick,
}: ImageProps) {
return (
<picture title={title} className={containerClassName} onClick={onClick}>
<source
type="image/avif"
srcSet={`${path}.avif`}
width={width}
height={height}
style={style}
/>
<img
alt={alt}
src={`${path}.png`}
className={className}
width={size ?? width}
height={size ?? height}
style={style}
draggable="false"
data-testid={testId}
/>
</picture>
);
}
type WeaponImageProps = {
weaponSplId: MainWeaponId;
variant: "badge" | "badge-5-star" | "build";
} & Omit<ImageProps, "path" | "alt">;
export function WeaponImage({
weaponSplId,
variant,
testId,
title,
...rest
}: WeaponImageProps) {
const { t } = useTranslation(["weapons"]);
return (
<Image
{...rest}
alt={title ?? t(`weapons:MAIN_${weaponSplId}`)}
title={title ?? t(`weapons:MAIN_${weaponSplId}`)}
testId={testId}
path={
variant === "badge"
? outlinedMainWeaponImageUrl(weaponSplId)
: variant == "badge-5-star"
? outlinedFiveStarMainWeaponImageUrl(weaponSplId)
: mainWeaponImageUrl(weaponSplId)
}
/>
);
}
type ModeImageProps = {
mode: ModeShort;
} & Omit<ImageProps, "path" | "alt">;
export function ModeImage({ mode, testId, title, ...rest }: ModeImageProps) {
const { t } = useTranslation(["game-misc"]);
return (
<Image
{...rest}
alt={title ?? t(`game-misc:MODE_LONG_${mode}`)}
title={title ?? t(`game-misc:MODE_LONG_${mode}`)}
testId={testId}
path={modeImageUrl(mode)}
/>
);
}
type StageImageProps = {
stageId: StageId;
} & Omit<ImageProps, "path" | "alt" | "title">;
export function StageImage({ stageId, testId, ...rest }: StageImageProps) {
const { t } = useTranslation(["game-misc"]);
return (
<Image
{...rest}
alt={t(`game-misc:STAGE_${stageId}`)}
title={t(`game-misc:STAGE_${stageId}`)}
testId={testId}
path={stageImageUrl(stageId)}
height={rest.height ?? (rest.width ? rest.width * 0.5625 : undefined)}
/>
);
}
type TierImageProps = {
tier: { name: TierName; isPlus: boolean };
} & Omit<ImageProps, "path" | "alt" | "title" | "size" | "height">;
export function TierImage({ tier, width = 200 }: TierImageProps) {
const title = `${tier.name}${tier.isPlus ? "+" : ""}`;
const height = width * 0.8675;
return (
<div className="tier__container">
<Image
path={tierImageUrl(tier.name)}
width={width}
height={height}
alt={title}
title={title}
containerClassName="tier__img"
/>
{tier.isPlus ? (
<Image
path={TIER_PLUS_URL}
width={width}
height={height}
alt=""
containerClassName="tier__img"
/>
) : null}
</div>
);
}