mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-05-09 12:13:10 -05:00
* Read in x rank placements script
* Xsearch initial
* XSearch initial
* XSearch with select
* Add badges
* XSearch player page initial
* Consider only one build by user for popular builds Closes #1312
* Revert "Side nav labels on hover (#1290)"
This reverts commit 6e839c6c2d.
* Added and fixed DAnish translations (#1315)
* Added Danish translations
Added Danish translations for the builds.json file.
Translations for the following variables have been added
stats.count.title # used the Danish word for "average" instead of "stats", as it makes more sense in Danish.
stats.ap.title
stats.percentage.title
stats.all
linkButton.abilityStats
linkButton.popularBuilds
noPopularBuilds
* Added Danish translations
Added Danish translations in the calander.json file
tag.desc.SZ
tag.desc.TW
tag.desc.S1
tag.desc.S2
tag.desc.SR
tag.desc.CARDS
* Added Danish translations
Added Danish translations in the common.json file
The following translations have been added
pages.vods
tag.name.SZ
tag.name.TW
tag.name.S1
tag.name.S2
tag.name.SR
tag.name.CARDS
* Added Danish translations in the faq.json file
Added the following Danish translations in the faq.json file
q7
a7
* Updated the DA/game-misc.json file
Added the names for the 3.0 maps.
* Added Danish translation for team.json
Added the following Danish translations for team.json file
- roles.MIDLINE
Fixed the following translation for the team.json file
- "roles.FRONTLINE": # fixed a typo
* Created a vods.json file for the Danish trans
* Fixed typo
Fixed the following translation in the Da\contributions.json file
"yaga" # fixed a Typo of "våben"
* Add Chinese Translation (#1314)
* Update contributions.json
* Update contributions.json
* Update faq.json
* Update user.json
* Create team.json
* Update common.json
* Update analyzer.json
* Prettier
* Prettier
* Prettier
* Update builds.json
* Update calendar.json
* Update common.json
* Update faq.json
* Update team.json
* Create vods.json
* Admin link player action
* Make PlacementTable rounded if only child
* Fix arrow disappeared
* Placements on user page initial
* Remove S2 site link
* Add badge
* Add feature to README
* Fix type error
* Different badge text if XP badge
* Add badge winners script
* Better user avatar + name wrapping for mobile
* Allow script to skip users
* Fix bad align when no weapons in weapon pool
* Add aliases to player name
* Add division icon to placements table
* Link to user page
* Link to xsearch on player details page
* Top 500 icons to user build page
* Working query but slow for weapons page
* Fix lint complaints
* Add cache to builds
* Remove useless SWR value
* Group months in xsearch
* Add xsearch to nav
* Add meta
* Remove TW for now
* Add splatoon3.ink as contributor
* Remove unneeded TODO
* Fix TODOs related to fetching monthYears
* Add FAQ question
* Leaderboards to nav
* Fix sploosh build stat pages returning 404
* Add badge
* Add article
* Patch 3.1
* Fix Prettier issue
* Add badge
* Add April SR gear
* Add badges
* Rename badge
* Add badge
* Add zh badge translation (#1322)
* Splatoon Competitive Guide article (#1316)
* Create splatoon-competitive-guide.md
* Update splatoon-competitive-guide.md
* Fix ToC Issues
* Update splatoon-competitive-guide.md
* Update splatoon-competitive-guide.md
* Update splatoon-competitive-guide.md
* Corrections
* Formatting
---------
Co-authored-by: Kalle <38327916+Sendouc@users.noreply.github.com>
* Fix typos
* Rename table and adjust columns
* Make cache ttl 0 in dev
* Make placements container a bit nicer for mobile
* Rename leftover xxx
* Placements script allow passing number as arg
* Skip leaderboards for now
* Add translations
* Rename placements folder to top-search
* Add placements to seed
* Add E2E tests
* Read in x rank placements script
* Xsearch initial
* XSearch initial
* XSearch with select
* XSearch player page initial
* Admin link player action
* Make PlacementTable rounded if only child
* Fix arrow disappeared
* Placements on user page initial
* Fix bad align when no weapons in weapon pool
* Add aliases to player name
* Add division icon to placements table
* Link to user page
* Link to xsearch on player details page
* Top 500 icons to user build page
* Working query but slow for weapons page
* Fix lint complaints
* Add cache to builds
* Remove useless SWR value
* Group months in xsearch
* Add xsearch to nav
* Add meta
* Remove TW for now
* Add splatoon3.ink as contributor
* Remove unneeded TODO
* Fix TODOs related to fetching monthYears
* Add FAQ question
* Leaderboards to nav
* Fix sploosh build stat pages returning 404
* Rename table and adjust columns
* Make cache ttl 0 in dev
* Make placements container a bit nicer for mobile
* Rename leftover xxx
* Placements script allow passing number as arg
* Skip leaderboards for now
* Add translations
* Rename placements folder to top-search
* Add placements to seed
* Add E2E tests
* Rename url variable and unify
* Tweaks
---------
Co-authored-by: Frederik <112972665+FrederikFraJylland@users.noreply.github.com>
Co-authored-by: Gell <61431488+gellneko@users.noreply.github.com>
Co-authored-by: Zen <99558412+zenpk@users.noreply.github.com>
Co-authored-by: Teddi <83455454+teddinotteddy@users.noreply.github.com>
273 lines
7.3 KiB
TypeScript
273 lines
7.3 KiB
TypeScript
import clsx from "clsx";
|
|
import { useTranslation } from "~/hooks/useTranslation";
|
|
import { Link } from "react-router-dom";
|
|
import type {
|
|
Build,
|
|
BuildWeapon,
|
|
GearType,
|
|
UserWithPlusTier,
|
|
} from "~/db/types";
|
|
import { useIsMounted } from "~/hooks/useIsMounted";
|
|
import { useUser } from "~/modules/auth";
|
|
import type {
|
|
Ability as AbilityType,
|
|
ModeShort,
|
|
} from "~/modules/in-game-lists";
|
|
import type { BuildAbilitiesTuple } from "~/modules/in-game-lists/types";
|
|
import { databaseTimestampToDate } from "~/utils/dates";
|
|
import { discordFullName, gearTypeToInitial } from "~/utils/strings";
|
|
import {
|
|
analyzerPage,
|
|
gearImageUrl,
|
|
mainWeaponImageUrl,
|
|
modeImageUrl,
|
|
mySlugify,
|
|
navIconUrl,
|
|
userBuildsPage,
|
|
weaponBuildPage,
|
|
} from "~/utils/urls";
|
|
import { Ability } from "./Ability";
|
|
import { Button, LinkButton } from "./Button";
|
|
import { FormWithConfirm } from "./FormWithConfirm";
|
|
import { TrashIcon } from "./icons/Trash";
|
|
import { EditIcon } from "./icons/Edit";
|
|
import { Image } from "./Image";
|
|
import { Popover } from "./Popover";
|
|
import { InfoIcon } from "./icons/Info";
|
|
import type { BuildWeaponWithTop500Info } from "~/db/models/builds/queries.server";
|
|
|
|
interface BuildProps {
|
|
build: Pick<
|
|
Build,
|
|
| "id"
|
|
| "title"
|
|
| "description"
|
|
| "clothesGearSplId"
|
|
| "headGearSplId"
|
|
| "shoesGearSplId"
|
|
| "updatedAt"
|
|
> & {
|
|
abilities: BuildAbilitiesTuple;
|
|
modes: ModeShort[] | null;
|
|
weapons: Array<{
|
|
weaponSplId: BuildWeapon["weaponSplId"];
|
|
minRank: number | null;
|
|
maxPower: number | null;
|
|
}>;
|
|
};
|
|
owner?: Pick<
|
|
UserWithPlusTier,
|
|
"discordId" | "discordName" | "discordDiscriminator" | "plusTier"
|
|
>;
|
|
canEdit?: boolean;
|
|
}
|
|
|
|
export function BuildCard({ build, owner, canEdit = false }: BuildProps) {
|
|
const user = useUser();
|
|
const { t } = useTranslation(["weapons", "builds", "common", "game-misc"]);
|
|
const { i18n } = useTranslation();
|
|
const isMounted = useIsMounted();
|
|
|
|
const {
|
|
id,
|
|
title,
|
|
description,
|
|
clothesGearSplId,
|
|
headGearSplId,
|
|
shoesGearSplId,
|
|
updatedAt,
|
|
abilities,
|
|
modes,
|
|
weapons,
|
|
} = build;
|
|
|
|
return (
|
|
<div className="build">
|
|
<div>
|
|
<div className="build__top-row">
|
|
{modes && modes.length > 0 && (
|
|
<div className="build__modes">
|
|
{modes.map((mode) => (
|
|
<Image
|
|
key={mode}
|
|
alt={t(`game-misc:MODE_LONG_${mode}` as any)}
|
|
title={t(`game-misc:MODE_LONG_${mode}` as any)}
|
|
path={modeImageUrl(mode)}
|
|
width={18}
|
|
height={18}
|
|
/>
|
|
))}
|
|
</div>
|
|
)}
|
|
<h2 className="build__title">{title}</h2>
|
|
</div>
|
|
<div className="build__date-author-row">
|
|
{owner && (
|
|
<>
|
|
<Link to={userBuildsPage(owner)}>{discordFullName(owner)}</Link>
|
|
<div>•</div>
|
|
</>
|
|
)}
|
|
{owner?.plusTier ? (
|
|
<>
|
|
<span>+{owner.plusTier}</span>
|
|
<div>•</div>
|
|
</>
|
|
) : null}
|
|
<time className={clsx({ invisible: !isMounted })}>
|
|
{isMounted
|
|
? databaseTimestampToDate(updatedAt).toLocaleDateString(
|
|
i18n.language,
|
|
{
|
|
day: "numeric",
|
|
month: "numeric",
|
|
year: "numeric",
|
|
}
|
|
)
|
|
: "t"}
|
|
</time>
|
|
</div>
|
|
</div>
|
|
<div className="build__weapons">
|
|
{weapons.map((weapon) => (
|
|
<RoundWeaponImage key={weapon.weaponSplId} weapon={weapon} />
|
|
))}
|
|
{weapons.length === 1 && (
|
|
<div className="build__weapon-text">
|
|
{t(`weapons:MAIN_${weapons[0]!.weaponSplId}` as any)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
<div className="build__gear-abilities">
|
|
<AbilitiesRowWithGear
|
|
gearType="HEAD"
|
|
abilities={abilities[0]}
|
|
gearId={headGearSplId}
|
|
/>
|
|
<AbilitiesRowWithGear
|
|
gearType="CLOTHES"
|
|
abilities={abilities[1]}
|
|
gearId={clothesGearSplId}
|
|
/>
|
|
<AbilitiesRowWithGear
|
|
gearType="SHOES"
|
|
abilities={abilities[2]}
|
|
gearId={shoesGearSplId}
|
|
/>
|
|
</div>
|
|
<div className="build__bottom-row">
|
|
<Link
|
|
to={analyzerPage({
|
|
weaponId: weapons[0]!.weaponSplId,
|
|
abilities: abilities.flat(),
|
|
})}
|
|
>
|
|
<Image
|
|
alt={t("common:pages.analyzer")}
|
|
className="build__icon"
|
|
path={navIconUrl("analyzer")}
|
|
/>
|
|
</Link>
|
|
{description ? (
|
|
<Popover
|
|
buttonChildren={<InfoIcon className="build__icon" />}
|
|
triggerClassName="minimal tiny build__small-text"
|
|
>
|
|
{description}
|
|
</Popover>
|
|
) : null}
|
|
{canEdit && (
|
|
<>
|
|
<LinkButton
|
|
className="build__small-text"
|
|
variant="minimal"
|
|
size="tiny"
|
|
to={`new?buildId=${id}&userId=${user!.id}`}
|
|
>
|
|
<EditIcon className="build__icon" />
|
|
</LinkButton>
|
|
<FormWithConfirm
|
|
dialogHeading={t("builds:deleteConfirm", { title })}
|
|
fields={[["buildToDeleteId", id]]}
|
|
>
|
|
<Button
|
|
className="build__small-text"
|
|
variant="minimal-destructive"
|
|
size="tiny"
|
|
type="submit"
|
|
>
|
|
<TrashIcon className="build__icon" />
|
|
</Button>
|
|
</FormWithConfirm>
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function RoundWeaponImage({ weapon }: { weapon: BuildWeaponWithTop500Info }) {
|
|
const { weaponSplId, maxPower, minRank } = weapon;
|
|
|
|
const { t } = useTranslation(["weapons"]);
|
|
const slug = mySlugify(t(`weapons:MAIN_${weaponSplId}`, { lng: "en" }));
|
|
|
|
const isTop500 = typeof maxPower === "number" && typeof minRank === "number";
|
|
|
|
return (
|
|
<div key={weaponSplId} className="build__weapon">
|
|
{isTop500 ? (
|
|
<Image
|
|
className="build__top500"
|
|
path={navIconUrl("xsearch")}
|
|
alt=""
|
|
title={`Max X Power: ${maxPower} | Best Rank: ${minRank}`}
|
|
height={24}
|
|
width={24}
|
|
testId="top500-crown"
|
|
/>
|
|
) : null}
|
|
<Link to={weaponBuildPage(slug)}>
|
|
<Image
|
|
path={mainWeaponImageUrl(weaponSplId)}
|
|
alt={t(`weapons:MAIN_${weaponSplId}` as any)}
|
|
title={t(`weapons:MAIN_${weaponSplId}` as any)}
|
|
height={36}
|
|
width={36}
|
|
/>
|
|
</Link>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function AbilitiesRowWithGear({
|
|
gearType,
|
|
abilities,
|
|
gearId,
|
|
}: {
|
|
gearType: GearType;
|
|
abilities: AbilityType[];
|
|
gearId: number;
|
|
}) {
|
|
const { t } = useTranslation(["gear"]);
|
|
const translatedGearName = t(
|
|
`gear:${gearTypeToInitial(gearType)}_${gearId}` as any
|
|
);
|
|
|
|
return (
|
|
<>
|
|
<Image
|
|
height={64}
|
|
width={64}
|
|
alt={translatedGearName}
|
|
title={translatedGearName}
|
|
path={gearImageUrl(gearType, gearId)}
|
|
className="build__gear"
|
|
/>
|
|
{abilities.map((ability, i) => (
|
|
<Ability key={i} ability={ability} size={i === 0 ? "MAIN" : "SUB"} />
|
|
))}
|
|
</>
|
|
);
|
|
}
|