diff --git a/packages/i18n/strings/en.json b/packages/i18n/strings/en.json
index 35dce2336..367e61d45 100644
--- a/packages/i18n/strings/en.json
+++ b/packages/i18n/strings/en.json
@@ -862,14 +862,22 @@
"playlist": {
"reset": "Reset",
"shuffle": "Shuffle",
- "sort": "Sort by",
+ "remove": "Remove played",
+
+ "sort": {
+ "button": "Sort by",
+ "last_played": "Last played",
+ "least_played": "Least played"
+ },
"table": {
"index": "#",
"never": "Never",
"tank": "Tank",
"last_played": "Last played",
- "battles": "Battles"
+ "battles": "Career battles",
+ "unrecorded": "--",
+ "wins": "Wins"
},
"no_login_warning": "You are not logged in. BlitzKit will not keep track of your played tanks.",
"generate": "Generate playlist",
diff --git a/packages/website/src/components/PlaylistActions.tsx b/packages/website/src/components/PlaylistActions.tsx
index 3d8392122..117c85da6 100644
--- a/packages/website/src/components/PlaylistActions.tsx
+++ b/packages/website/src/components/PlaylistActions.tsx
@@ -1,11 +1,13 @@
import { fisherYates } from "@blitzkit/core";
import { CaretDownIcon } from "@radix-ui/react-icons";
-import { Button, Flex } from "@radix-ui/themes";
+import { Button, DropdownMenu, Flex } from "@radix-ui/themes";
import { useLocale } from "../hooks/useLocale";
+import { App } from "../stores/app";
import { Playlist } from "../stores/playlist";
export function PlaylistActions() {
const { strings } = useLocale();
+ const wargaming = App.use((state) => state.logins.wargaming);
return (
@@ -22,7 +24,7 @@ export function PlaylistActions() {
-
+ {wargaming && (
+ <>
+
+
+
+
+
+
+
+
+ {
+ Playlist.mutate((draft) => {
+ draft.list = draft.list?.sort(
+ (a, b) => (a.then?.last ?? 0) - (b.then?.last ?? 0)
+ );
+ });
+ }}
+ >
+ {strings.website.tools.playlist.sort.last_played}
+
+ {
+ Playlist.mutate((draft) => {
+ draft.list = draft.list?.sort(
+ (a, b) => (a.then?.battles ?? 0) - (b.then?.battles ?? 0)
+ );
+ });
+ }}
+ >
+ {strings.website.tools.playlist.sort.least_played}
+
+
+
+ >
+ )}
);
}
diff --git a/packages/website/src/components/PlaylistGenerator.tsx b/packages/website/src/components/PlaylistGenerator.tsx
index 1656e22a3..8a7260079 100644
--- a/packages/website/src/components/PlaylistGenerator.tsx
+++ b/packages/website/src/components/PlaylistGenerator.tsx
@@ -33,73 +33,81 @@ export function PlaylistGenerator() {
const tanksTrimmed = useMemo(() => tanks.slice(0, 65), [filters]);
return (
-
-
-
-
-
-
- {!wargaming && (
-
-
-
- {strings.website.tools.playlist.no_login_warning}
-
-
- )}
-
-
+
-
+
+
+
+
+ {!wargaming && (
+
+
+
+ {strings.website.tools.playlist.no_login_warning}
+
+
+ )}
+
+
+
- {tanksTrimmed.map((tank) => (
-
+ {tanksTrimmed.map((tank) => (
+
+ ))}
+
+
- ))}
+
-
-
-
-
- {literals(strings.website.tools.playlist.pool_size, {
- count: tanks.length,
- })}
-
+
+ {literals(strings.website.tools.playlist.pool_size, {
+ count: tanks.length,
+ })}
+
+
);
diff --git a/packages/website/src/components/PlaylistOrder.tsx b/packages/website/src/components/PlaylistOrder.tsx
index e5e8fcec2..f9fa71ab9 100644
--- a/packages/website/src/components/PlaylistOrder.tsx
+++ b/packages/website/src/components/PlaylistOrder.tsx
@@ -2,6 +2,7 @@ import { Box, Flex } from "@radix-ui/themes";
import { CurrentlyPlaying } from "./CurrentlyPlaying";
import { PlaylistActions } from "./PlaylistActions";
import { PlaylistTable } from "./PlaylistTable";
+import { PlaylistTracking } from "./PlaylistTracking";
export function PlaylistOrder() {
return (
@@ -15,6 +16,7 @@ export function PlaylistOrder() {
>
+
diff --git a/packages/website/src/components/PlaylistTable.tsx b/packages/website/src/components/PlaylistTable.tsx
index dcd91cd63..6759d0635 100644
--- a/packages/website/src/components/PlaylistTable.tsx
+++ b/packages/website/src/components/PlaylistTable.tsx
@@ -31,27 +31,31 @@ export function PlaylistTable() {
+
+
+
+
{strings.website.tools.playlist.table.tank}
- {strings.website.tools.playlist.table.last_played}
+ {strings.website.tools.playlist.table.wins}
{strings.website.tools.playlist.table.battles}
-
-
-
+
+ {strings.website.tools.playlist.table.last_played}
+
{list.map((props, index) => (
-
+
))}
diff --git a/packages/website/src/components/PlaylistTankEntry.tsx b/packages/website/src/components/PlaylistTankEntry.tsx
index f6f47dbd2..db424a9df 100644
--- a/packages/website/src/components/PlaylistTankEntry.tsx
+++ b/packages/website/src/components/PlaylistTankEntry.tsx
@@ -1,6 +1,11 @@
-import { MinusCircledIcon } from "@radix-ui/react-icons";
-import { Checkbox, IconButton, Table } from "@radix-ui/themes";
+import {
+ MinusCircledIcon,
+ TriangleDownIcon,
+ TriangleUpIcon,
+} from "@radix-ui/react-icons";
+import { Checkbox, Flex, IconButton, Table, Text } from "@radix-ui/themes";
import { memo } from "react";
+import { awaitableTankDefinitions } from "../core/awaitables/tankDefinitions";
import { useLocale } from "../hooks/useLocale";
import { Playlist, type PlaylistEntry } from "../stores/playlist";
import { TankRowHeaderCell } from "./TankRowHeaderCell";
@@ -9,27 +14,19 @@ interface Props extends PlaylistEntry {
index: number;
}
+const tankDefinitions = await awaitableTankDefinitions;
+
export const PlaylistTankEntry = memo(
- ({ tank, checked, last, battles, index }) => {
+ ({ id, checked, then, now, index }) => {
const { locale, strings } = useLocale();
const rtf = new Intl.RelativeTimeFormat(locale, { numeric: "auto" });
- const nowDays = Date.now() / (1000 * 60 * 60 * 24);
- const lastDays = last ? last / (60 * 60 * 24) : undefined;
+ const dateNow = Date.now();
+ const tank = tankDefinitions.tanks[id];
return (
{index + 1}
-
-
-
- {lastDays
- ? rtf.format(Math.floor(lastDays - nowDays), "day")
- : strings.website.tools.playlist.table.never}
-
-
- {battles}
-
{
Playlist.mutate((draft) => {
@@ -37,7 +34,16 @@ export const PlaylistTankEntry = memo(
});
}}
>
-
+ then.battles) ||
+ checked
+ }
+ />
@@ -53,13 +59,48 @@ export const PlaylistTankEntry = memo(
+
+
+
+
+ {now && then && now.battles !== then.battles ? (
+
+
+
+
+ {now.wins - then.wins}
+
+
+
+
+
+
+ {now.battles - then.battles - now.wins + then.wins}
+
+
+
+ ) : (
+ strings.website.tools.playlist.table.unrecorded
+ )}
+
+
+
+ {now?.battles ?? strings.website.tools.playlist.table.unrecorded}
+
+
+
+ {now && now.last !== 0
+ ? rtf.format(
+ Math.ceil((now.last - dateNow / 1000) / (60 * 60 * 24)),
+ "day"
+ )
+ : strings.website.tools.playlist.table.unrecorded}
+
);
},
(a, b) =>
a.checked === b.checked &&
- a.battles === b.battles &&
- a.last === b.last &&
- a.wins === b.wins &&
+ a.then?.battles === b.then?.battles &&
a.index === b.index
);
diff --git a/packages/website/src/components/PlaylistTracking.tsx b/packages/website/src/components/PlaylistTracking.tsx
new file mode 100644
index 000000000..dd8e3b34b
--- /dev/null
+++ b/packages/website/src/components/PlaylistTracking.tsx
@@ -0,0 +1,14 @@
+import { useEffect } from "react";
+import { updatePlaylist } from "../core/blitzkit/updatePlaylist";
+
+export function PlaylistTracking() {
+ useEffect(() => {
+ const interval = setInterval(updatePlaylist, 5000);
+
+ return () => {
+ clearInterval(interval);
+ };
+ }, []);
+
+ return null;
+}
diff --git a/packages/website/src/core/blitzkit/generatePlaylist.ts b/packages/website/src/core/blitzkit/generatePlaylist.ts
index a86510fbd..ccd8a13c7 100644
--- a/packages/website/src/core/blitzkit/generatePlaylist.ts
+++ b/packages/website/src/core/blitzkit/generatePlaylist.ts
@@ -28,12 +28,17 @@ export async function generatePlaylist() {
fisherYates(filtered);
Playlist.mutate((draft) => {
- draft.list = filtered.map((tank) => ({
- tank,
- battles: stats[tank.id]?.all.battles,
- wins: stats[tank.id]?.all.wins,
- last: stats[tank.id]?.last_battle_time,
- checked: false,
- }));
+ draft.list = filtered.map((tank) => {
+ const tankStats = stats[tank.id];
+ const then = tankStats
+ ? {
+ battles: tankStats.all.battles,
+ wins: tankStats.all.wins,
+ last: tankStats.last_battle_time,
+ }
+ : undefined;
+
+ return { id: tank.id, then, now: then };
+ });
});
}
diff --git a/packages/website/src/core/blitzkit/updatePlaylist.ts b/packages/website/src/core/blitzkit/updatePlaylist.ts
new file mode 100644
index 000000000..d49435766
--- /dev/null
+++ b/packages/website/src/core/blitzkit/updatePlaylist.ts
@@ -0,0 +1,32 @@
+import {
+ getTankStats,
+ idToRegion,
+ type IndividualTankStats,
+} from "@blitzkit/core";
+import { App } from "../../stores/app";
+import { Playlist } from "../../stores/playlist";
+
+export async function updatePlaylist() {
+ const id = App.state.logins.wargaming?.id;
+ const statsList = id ? await getTankStats(idToRegion(id), id) : undefined;
+ const stats: Record = {};
+
+ if (statsList) {
+ for (const tankStat of statsList) {
+ stats[tankStat.tank_id] = tankStat;
+ }
+ }
+
+ Playlist.mutate((draft) => {
+ draft.list?.map((item) => {
+ const tankStats = stats[item.id];
+ item.now = tankStats
+ ? {
+ battles: tankStats.all.battles,
+ wins: tankStats.all.wins,
+ last: tankStats.last_battle_time,
+ }
+ : undefined;
+ });
+ });
+}
diff --git a/packages/website/src/pages/[...locale]/playlist/index.astro b/packages/website/src/pages/[...locale]/playlist/index.astro
index 298a243d7..47582ba99 100644
--- a/packages/website/src/pages/[...locale]/playlist/index.astro
+++ b/packages/website/src/pages/[...locale]/playlist/index.astro
@@ -1,6 +1,7 @@
---
+import ToolOpenGraph from "../../../components/ToolOpenGraph.astro";
import { tools } from "../../../constants/tools";
-import ToolLayout from "../../../layouts/ToolLayout.astro";
+import BaseLayout from "../../../layouts/BaseLayout.astro";
import { Page } from "./_index.tsx";
export { getStaticPaths } from "../index.astro";
@@ -8,8 +9,10 @@ export { getStaticPaths } from "../index.astro";
const { locale } = Astro.params;
---
-
+
+
+
-
+
diff --git a/packages/website/src/stores/playlist.ts b/packages/website/src/stores/playlist.ts
index b0fcb496a..f2f2ba132 100644
--- a/packages/website/src/stores/playlist.ts
+++ b/packages/website/src/stores/playlist.ts
@@ -1,12 +1,10 @@
-import type { TankDefinition } from "@blitzkit/core";
import { Varuna } from "varuna";
export interface PlaylistEntry {
- tank: TankDefinition;
- battles?: number;
- wins?: number;
- checked: boolean;
- last?: number;
+ id: number;
+ then?: { battles: number; wins: number; last: number };
+ now?: { battles: number; wins: number; last: number };
+ checked?: boolean;
}
export interface Playlist {