mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-13 06:36:49 -05:00
Add limit + load more logic to builds page
This commit is contained in:
@@ -60,7 +60,7 @@ type LinkButtonProps = Pick<
|
||||
ButtonProps,
|
||||
"variant" | "children" | "className" | "tiny"
|
||||
> &
|
||||
Pick<LinkProps, "to" | "prefetch"> & { "data-cy"?: string } & {
|
||||
Pick<LinkProps, "to" | "prefetch" | "state"> & { "data-cy"?: string } & {
|
||||
isExternal?: boolean;
|
||||
};
|
||||
|
||||
@@ -72,6 +72,7 @@ export function LinkButton({
|
||||
to,
|
||||
prefetch,
|
||||
isExternal,
|
||||
state,
|
||||
"data-cy": testId,
|
||||
}: LinkButtonProps) {
|
||||
if (isExternal) {
|
||||
@@ -92,6 +93,7 @@ export function LinkButton({
|
||||
to={to}
|
||||
data-cy={testId}
|
||||
prefetch={prefetch}
|
||||
state={state}
|
||||
>
|
||||
{children}
|
||||
</Link>
|
||||
|
||||
@@ -2,15 +2,24 @@
|
||||
// https://github.com/remix-run/remix/issues/186#issuecomment-1178395835
|
||||
|
||||
import { ScrollRestoration, useLocation } from "@remix-run/react";
|
||||
import * as React from "react";
|
||||
|
||||
export function ConditionalScrollRestoration() {
|
||||
const isFirstRenderRef = React.useRef(true);
|
||||
const location = useLocation();
|
||||
|
||||
React.useEffect(() => {
|
||||
isFirstRenderRef.current = false;
|
||||
}, []);
|
||||
|
||||
if (
|
||||
!isFirstRenderRef.current &&
|
||||
location.state != null &&
|
||||
typeof location.state === "object" &&
|
||||
(location.state as { scroll: boolean }).scroll === false
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <ScrollRestoration />;
|
||||
}
|
||||
|
||||
@@ -36,6 +36,9 @@ export const BUILD = {
|
||||
MAX_COUNT: 250,
|
||||
} as const;
|
||||
|
||||
export const BUILDS_PAGE_BATCH_SIZE = 24;
|
||||
export const BUILDS_PAGE_MAX_BUILDS = 240;
|
||||
|
||||
export const EMPTY_BUILD: BuildAbilitiesTupleWithUnknown = [
|
||||
["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"],
|
||||
["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"],
|
||||
|
||||
@@ -35,4 +35,4 @@ from
|
||||
group by
|
||||
"BuildWithWeapon"."id"
|
||||
order by
|
||||
"BuildWithWeapon"."updatedAt" desc
|
||||
"BuildWithWeapon"."updatedAt" desc
|
||||
|
||||
@@ -56,3 +56,5 @@ order by
|
||||
else "PlusTier"."tier"
|
||||
end asc,
|
||||
"BuildWithWeapon"."updatedAt" desc
|
||||
limit
|
||||
@limit
|
||||
|
||||
@@ -109,11 +109,18 @@ export function buildsByUserId(userId: Build["ownerId"]) {
|
||||
type BuildsByWeaponIdRow = BuildsByUserRow &
|
||||
Pick<User, "discordId" | "discordName" | "discordDiscriminator">;
|
||||
|
||||
export function buildsByWeaponId(weaponId: BuildWeapon["weaponSplId"]) {
|
||||
export function buildsByWeaponId({
|
||||
weaponId,
|
||||
limit,
|
||||
}: {
|
||||
weaponId: BuildWeapon["weaponSplId"];
|
||||
limit: number;
|
||||
}) {
|
||||
const rows = buildsByWeaponIdStm.all({
|
||||
weaponId,
|
||||
// default to impossible weapon id so we can always have same amount of placeholder values
|
||||
altWeaponId: weaponIdToAltId.get(weaponId) ?? -1,
|
||||
limit,
|
||||
}) as Array<BuildsByWeaponIdRow>;
|
||||
|
||||
return rows.map(augmentBuild);
|
||||
|
||||
@@ -46,6 +46,7 @@ const basicSeeds = [
|
||||
calendarEventBadges,
|
||||
calendarEventResults,
|
||||
adminBuilds,
|
||||
manySplattershotBuilds,
|
||||
];
|
||||
|
||||
export function seed() {
|
||||
@@ -498,6 +499,12 @@ function calendarEventResults() {
|
||||
}
|
||||
}
|
||||
|
||||
const randomAbility = (legalTypes: AbilityType[]) => {
|
||||
const randomOrderAbilities = shuffle([...abilities]);
|
||||
|
||||
return randomOrderAbilities.find((a) => legalTypes.includes(a.type))!.name;
|
||||
};
|
||||
|
||||
function adminBuilds() {
|
||||
for (let i = 0; i < 50; i++) {
|
||||
const randomOrderHeadGear = shuffle(headGearIds.slice());
|
||||
@@ -505,13 +512,6 @@ function adminBuilds() {
|
||||
const randomOrderShoesGear = shuffle(shoesGearIds.slice());
|
||||
const randomOrderWeaponIds = shuffle(mainWeaponIds.slice());
|
||||
|
||||
const randomAbility = (legalTypes: AbilityType[]) => {
|
||||
const randomOrderAbilities = shuffle([...abilities]);
|
||||
|
||||
return randomOrderAbilities.find((a) => legalTypes.includes(a.type))!
|
||||
.name;
|
||||
};
|
||||
|
||||
db.builds.create({
|
||||
title: `${capitalize(faker.word.adjective())} ${capitalize(
|
||||
faker.word.noun()
|
||||
@@ -553,3 +553,59 @@ function adminBuilds() {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function manySplattershotBuilds() {
|
||||
for (let i = 0; i < 500; i++) {
|
||||
const SPLATTERSHOT_ID = 40;
|
||||
|
||||
const randomOrderHeadGear = shuffle(headGearIds.slice());
|
||||
const randomOrderClothesGear = shuffle(clothesGearIds.slice());
|
||||
const randomOrderShoesGear = shuffle(shoesGearIds.slice());
|
||||
const randomOrderWeaponIds = shuffle(mainWeaponIds.slice()).filter(
|
||||
(id) => id !== SPLATTERSHOT_ID
|
||||
);
|
||||
const users = userIdsInRandomOrder();
|
||||
|
||||
db.builds.create({
|
||||
title: `${capitalize(faker.word.adjective())} ${capitalize(
|
||||
faker.word.noun()
|
||||
)}`,
|
||||
ownerId: users.pop()!,
|
||||
description: Math.random() < 0.75 ? faker.lorem.paragraph() : null,
|
||||
headGearSplId: randomOrderHeadGear[0]!,
|
||||
clothesGearSplId: randomOrderClothesGear[0]!,
|
||||
shoesGearSplId: randomOrderShoesGear[0]!,
|
||||
weaponSplIds: new Array(
|
||||
faker.helpers.arrayElement([1, 1, 1, 2, 2, 3, 4, 5])
|
||||
)
|
||||
.fill(null)
|
||||
.map((_, i) =>
|
||||
i === 0 ? SPLATTERSHOT_ID : randomOrderWeaponIds.pop()!
|
||||
),
|
||||
modes:
|
||||
Math.random() < 0.75
|
||||
? modesShort.filter(() => Math.random() < 0.5)
|
||||
: null,
|
||||
abilities: [
|
||||
[
|
||||
randomAbility(["HEAD_MAIN_ONLY", "STACKABLE"]),
|
||||
randomAbility(["STACKABLE"]),
|
||||
randomAbility(["STACKABLE"]),
|
||||
randomAbility(["STACKABLE"]),
|
||||
],
|
||||
[
|
||||
randomAbility(["CLOTHES_MAIN_ONLY", "STACKABLE"]),
|
||||
randomAbility(["STACKABLE"]),
|
||||
randomAbility(["STACKABLE"]),
|
||||
randomAbility(["STACKABLE"]),
|
||||
],
|
||||
[
|
||||
randomAbility(["SHOES_MAIN_ONLY", "STACKABLE"]),
|
||||
randomAbility(["STACKABLE"]),
|
||||
randomAbility(["STACKABLE"]),
|
||||
randomAbility(["STACKABLE"]),
|
||||
],
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,10 @@ import {
|
||||
type SerializeFrom,
|
||||
} from "@remix-run/node";
|
||||
import { useLoaderData } from "@remix-run/react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { BuildCard } from "~/components/BuildCard";
|
||||
import { LinkButton } from "~/components/Button";
|
||||
import { BUILDS_PAGE_BATCH_SIZE, BUILDS_PAGE_MAX_BUILDS } from "~/constants";
|
||||
import { db } from "~/db";
|
||||
import { i18next } from "~/modules/i18n";
|
||||
import { mainWeaponIds, weaponIdIsNotAlt } from "~/modules/in-game-lists";
|
||||
@@ -33,28 +36,53 @@ export const loader = async ({ request, params }: LoaderArgs) => {
|
||||
throw new Response(null, { status: 404 });
|
||||
}
|
||||
|
||||
const url = new URL(request.url);
|
||||
const limit = Math.min(
|
||||
Number(url.searchParams.get("limit") ?? BUILDS_PAGE_BATCH_SIZE),
|
||||
BUILDS_PAGE_MAX_BUILDS
|
||||
);
|
||||
|
||||
return {
|
||||
weaponId,
|
||||
title: makeTitle([t(`weapons:MAIN_${weaponId}`), t("common:pages.builds")]),
|
||||
builds: db.builds.buildsByWeaponId(weaponId),
|
||||
builds: db.builds.buildsByWeaponId({
|
||||
weaponId,
|
||||
limit,
|
||||
}),
|
||||
limit,
|
||||
};
|
||||
};
|
||||
|
||||
export default function WeaponsBuildsPage() {
|
||||
const data = useLoaderData<typeof loader>();
|
||||
const { t } = useTranslation(["common"]);
|
||||
|
||||
return (
|
||||
<div className="builds-container">
|
||||
{data.builds.map((build) => {
|
||||
return (
|
||||
<BuildCard
|
||||
key={build.id}
|
||||
build={build}
|
||||
owner={build}
|
||||
canEdit={false}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<div className="stack lg">
|
||||
<div className="builds-container">
|
||||
{data.builds.map((build) => {
|
||||
return (
|
||||
<BuildCard
|
||||
key={build.id}
|
||||
build={build}
|
||||
owner={build}
|
||||
canEdit={false}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{data.limit < BUILDS_PAGE_MAX_BUILDS &&
|
||||
// not considering edge case where there are amount of builds equal to current limit
|
||||
data.builds.length === data.limit && (
|
||||
<LinkButton
|
||||
className="m-0-auto"
|
||||
tiny
|
||||
to={`?limit=${data.limit + BUILDS_PAGE_BATCH_SIZE}`}
|
||||
state={{ scroll: false }}
|
||||
>
|
||||
{t("common:actions.loadMore")}
|
||||
</LinkButton>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -86,6 +86,10 @@
|
||||
margin-block: var(--s-4);
|
||||
}
|
||||
|
||||
.m-0-auto {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"actions.add": "Add",
|
||||
"actions.remove": "Remove",
|
||||
"actions.delete": "Delete",
|
||||
"actions.loadMore": "Load more",
|
||||
|
||||
"results": "Results",
|
||||
|
||||
|
||||
@@ -18,9 +18,27 @@
|
||||
|
||||
**44/44**
|
||||
|
||||
### 🟢 common.json
|
||||
### 🟡 common.json
|
||||
|
||||
**36/36**
|
||||
**36/48**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
|
||||
- actions.loadMore
|
||||
- weapon.category.SHOOTERS
|
||||
- weapon.category.BLASTERS
|
||||
- weapon.category.ROLLERS
|
||||
- weapon.category.BRUSHES
|
||||
- weapon.category.CHARGERS
|
||||
- weapon.category.SLOSHERS
|
||||
- weapon.category.SPLATLINGS
|
||||
- weapon.category.DUALIES
|
||||
- weapon.category.BRELLAS
|
||||
- weapon.category.STRINGERS
|
||||
- weapon.category.SPLATANAS
|
||||
|
||||
</details>
|
||||
|
||||
### 🟢 contributions.json
|
||||
|
||||
@@ -40,7 +58,61 @@
|
||||
|
||||
---
|
||||
|
||||
## /de (🟢 Done)
|
||||
## /de (🟡 In progress)
|
||||
|
||||
### 🟢 analyzer.json
|
||||
|
||||
**91/91**
|
||||
|
||||
### 🟢 badges.json
|
||||
|
||||
**7/7**
|
||||
|
||||
### 🟢 builds.json
|
||||
|
||||
**11/11**
|
||||
|
||||
### 🟢 calendar.json
|
||||
|
||||
**44/44**
|
||||
|
||||
### 🟡 common.json
|
||||
|
||||
**36/48**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
|
||||
- actions.loadMore
|
||||
- weapon.category.SHOOTERS
|
||||
- weapon.category.BLASTERS
|
||||
- weapon.category.ROLLERS
|
||||
- weapon.category.BRUSHES
|
||||
- weapon.category.CHARGERS
|
||||
- weapon.category.SLOSHERS
|
||||
- weapon.category.SPLATLINGS
|
||||
- weapon.category.DUALIES
|
||||
- weapon.category.BRELLAS
|
||||
- weapon.category.STRINGERS
|
||||
- weapon.category.SPLATANAS
|
||||
|
||||
</details>
|
||||
|
||||
### 🟢 contributions.json
|
||||
|
||||
**6/6**
|
||||
|
||||
### 🟢 faq.json
|
||||
|
||||
**6/6**
|
||||
|
||||
### 🟢 front.json
|
||||
|
||||
**9/9**
|
||||
|
||||
### 🟢 user.json
|
||||
|
||||
**7/7**
|
||||
|
||||
---
|
||||
|
||||
@@ -96,9 +168,27 @@
|
||||
|
||||
**44/44**
|
||||
|
||||
### 🟢 common.json
|
||||
### 🟡 common.json
|
||||
|
||||
**36/36**
|
||||
**36/48**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
|
||||
- actions.loadMore
|
||||
- weapon.category.SHOOTERS
|
||||
- weapon.category.BLASTERS
|
||||
- weapon.category.ROLLERS
|
||||
- weapon.category.BRUSHES
|
||||
- weapon.category.CHARGERS
|
||||
- weapon.category.SLOSHERS
|
||||
- weapon.category.SPLATLINGS
|
||||
- weapon.category.DUALIES
|
||||
- weapon.category.BRELLAS
|
||||
- weapon.category.STRINGERS
|
||||
- weapon.category.SPLATANAS
|
||||
|
||||
</details>
|
||||
|
||||
### 🟡 contributions.json
|
||||
|
||||
@@ -152,12 +242,24 @@
|
||||
|
||||
### 🟡 common.json
|
||||
|
||||
**35/36**
|
||||
**35/48**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
|
||||
- pages.analyzer
|
||||
- actions.loadMore
|
||||
- weapon.category.SHOOTERS
|
||||
- weapon.category.BLASTERS
|
||||
- weapon.category.ROLLERS
|
||||
- weapon.category.BRUSHES
|
||||
- weapon.category.CHARGERS
|
||||
- weapon.category.SLOSHERS
|
||||
- weapon.category.SPLATLINGS
|
||||
- weapon.category.DUALIES
|
||||
- weapon.category.BRELLAS
|
||||
- weapon.category.STRINGERS
|
||||
- weapon.category.SPLATANAS
|
||||
|
||||
</details>
|
||||
|
||||
@@ -218,12 +320,24 @@
|
||||
|
||||
### 🟡 common.json
|
||||
|
||||
**35/36**
|
||||
**35/48**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
|
||||
- pages.analyzer
|
||||
- actions.loadMore
|
||||
- weapon.category.SHOOTERS
|
||||
- weapon.category.BLASTERS
|
||||
- weapon.category.ROLLERS
|
||||
- weapon.category.BRUSHES
|
||||
- weapon.category.CHARGERS
|
||||
- weapon.category.SLOSHERS
|
||||
- weapon.category.SPLATLINGS
|
||||
- weapon.category.DUALIES
|
||||
- weapon.category.BRELLAS
|
||||
- weapon.category.STRINGERS
|
||||
- weapon.category.SPLATANAS
|
||||
|
||||
</details>
|
||||
|
||||
@@ -279,12 +393,24 @@
|
||||
|
||||
### 🟡 common.json
|
||||
|
||||
**35/36**
|
||||
**35/48**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
|
||||
- pages.analyzer
|
||||
- actions.loadMore
|
||||
- weapon.category.SHOOTERS
|
||||
- weapon.category.BLASTERS
|
||||
- weapon.category.ROLLERS
|
||||
- weapon.category.BRUSHES
|
||||
- weapon.category.CHARGERS
|
||||
- weapon.category.SLOSHERS
|
||||
- weapon.category.SPLATLINGS
|
||||
- weapon.category.DUALIES
|
||||
- weapon.category.BRELLAS
|
||||
- weapon.category.STRINGERS
|
||||
- weapon.category.SPLATANAS
|
||||
|
||||
</details>
|
||||
|
||||
@@ -365,9 +491,27 @@
|
||||
|
||||
**44/44**
|
||||
|
||||
### 🟢 common.json
|
||||
### 🟡 common.json
|
||||
|
||||
**36/36**
|
||||
**36/48**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
|
||||
- actions.loadMore
|
||||
- weapon.category.SHOOTERS
|
||||
- weapon.category.BLASTERS
|
||||
- weapon.category.ROLLERS
|
||||
- weapon.category.BRUSHES
|
||||
- weapon.category.CHARGERS
|
||||
- weapon.category.SLOSHERS
|
||||
- weapon.category.SPLATLINGS
|
||||
- weapon.category.DUALIES
|
||||
- weapon.category.BRELLAS
|
||||
- weapon.category.STRINGERS
|
||||
- weapon.category.SPLATANAS
|
||||
|
||||
</details>
|
||||
|
||||
### 🟡 contributions.json
|
||||
|
||||
@@ -421,12 +565,24 @@
|
||||
|
||||
### 🟡 common.json
|
||||
|
||||
**35/36**
|
||||
**35/48**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
|
||||
- pages.analyzer
|
||||
- actions.loadMore
|
||||
- weapon.category.SHOOTERS
|
||||
- weapon.category.BLASTERS
|
||||
- weapon.category.ROLLERS
|
||||
- weapon.category.BRUSHES
|
||||
- weapon.category.CHARGERS
|
||||
- weapon.category.SLOSHERS
|
||||
- weapon.category.SPLATLINGS
|
||||
- weapon.category.DUALIES
|
||||
- weapon.category.BRELLAS
|
||||
- weapon.category.STRINGERS
|
||||
- weapon.category.SPLATANAS
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user