Add limit + load more logic to builds page

This commit is contained in:
Kalle
2022-09-25 14:55:20 +03:00
parent cb369985c8
commit a5fad589e8
11 changed files with 301 additions and 33 deletions

View File

@@ -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>

View File

@@ -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 />;
}

View File

@@ -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"],

View File

@@ -35,4 +35,4 @@ from
group by
"BuildWithWeapon"."id"
order by
"BuildWithWeapon"."updatedAt" desc
"BuildWithWeapon"."updatedAt" desc

View File

@@ -56,3 +56,5 @@ order by
else "PlusTier"."tier"
end asc,
"BuildWithWeapon"."updatedAt" desc
limit
@limit

View File

@@ -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);

View File

@@ -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"]),
],
],
});
}
}

View File

@@ -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>
);
}

View File

@@ -86,6 +86,10 @@
margin-block: var(--s-4);
}
.m-0-auto {
margin: 0 auto;
}
.hidden {
display: none;
}

View File

@@ -25,6 +25,7 @@
"actions.add": "Add",
"actions.remove": "Remove",
"actions.delete": "Delete",
"actions.loadMore": "Load more",
"results": "Results",

View File

@@ -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>