feat: recently veiwed tanks

This commit is contained in:
tresabhi committed 2024-11-11 10:50:15 -06:00
1 parent ea013c4df8
commit 387a67266f
9 files changed
+80 -14

No files matched your search

@@ -0,0 +1,30 @@
import { fetchTankDefinitions } from '@blitzkit/core';
import { Flex, Separator, Text } from '@radix-ui/themes';
import { TankopediaPersistent } from '../../../stores/tankopediaPersistent';
import { TankCard } from './TankCard';
import { TankCardWrapper } from './TankCardWrapper';
const tankDefinitions = await fetchTankDefinitions();
export function RecentlyViewed() {
const tankopediaPersistentStore = TankopediaPersistent.useStore();
// non-reactive because it is a little weird that it updates instantly even before the page loads
const recentlyViewed = tankopediaPersistentStore.getState().recentlyViewed;
if (recentlyViewed.length === 0) return null;
return (
<Flex direction="column" gap="2" mt="2" mb="6">
<Text color="gray" align="center">
Recently viewed
</Text>
<TankCardWrapper>
{recentlyViewed.map((id) => (
<TankCard tank={tankDefinitions.tanks[id]} key={id} />
))}
</TankCardWrapper>
<Separator size="4" />
</Flex>
);
}
@@ -10,10 +10,13 @@ import {
} from '@blitzkit/core';
import { useStore } from '@nanostores/react';
import { Flex, Link, Text } from '@radix-ui/themes';
import { uniq } from 'lodash-es';
import { useMemo } from 'react';
import { resolveReload } from '../../../../core/blitzkit/resolveReload';
import { TankopediaPersistent } from '../../../../stores/tankopediaPersistent';
import { $tankopediaSort } from '../../../../stores/tankopediaSort';
import { classIcons } from '../../../ClassIcon';
import { MAX_RECENTLY_VIEWED } from '../../constants';
import './index.css';
interface TankCardProps {
@@ -24,6 +27,7 @@ interface TankCardProps {
const modelDefinitions = await fetchModelDefinitions();
export function TankCard({ tank, onSelect }: TankCardProps) {
const mutateTankopediaPersistent = TankopediaPersistent.useMutation();
const Icon = classIcons[tank.class];
const tankopediaSort = useStore($tankopediaSort);
const discriminator = useMemo(() => {
@@ -155,7 +159,15 @@ export function TankCard({ tank, onSelect }: TankCardProps) {
: 'gray'
}
highContrast={tank.type === TankType.RESEARCHABLE}
onClick={onSelect ? () => onSelect(tank) : undefined}
onClick={() => {
onSelect?.(tank);
mutateTankopediaPersistent((draft) => {
draft.recentlyViewed = uniq([tank.id, ...draft.recentlyViewed]).slice(
0,
MAX_RECENTLY_VIEWED,
);
});
}}
className="tank-search-card"
style={{
backgroundImage: `url(${asset(`flags/scratched/${tank.nation}.webp`)})`,
@@ -5,3 +5,5 @@ export const treeTypeOrder = [
TankType.PREMIUM,
TankType.COLLECTOR,
];
export const MAX_RECENTLY_VIEWED = 9;
@@ -12,11 +12,12 @@ import {
import { useStore } from '@nanostores/react';
import { Callout, Flex, Link, Text, type FlexProps } from '@radix-ui/themes';
import fuzzysort from 'fuzzysort';
import { times } from 'lodash-es';
import { times, uniq } from 'lodash-es';
import { memo, useEffect, useMemo, useState } from 'react';
import { filterTank } from '../../core/blitzkit/filterTank';
import { resolveReload } from '../../core/blitzkit/resolveReload';
import { $tankFilters } from '../../stores/tankFilters';
import { TankopediaPersistent } from '../../stores/tankopediaPersistent';
import {
SORT_NAMES,
SORT_UNITS,
@@ -25,11 +26,12 @@ import { $tankopediaSort } from '../../stores/tankopediaSort';
import { ExperimentIcon } from '../ExperimentIcon';
import { FilterControl } from './components/FilterControl';
import { NoResults } from './components/NoResults';
import { RecentlyViewed } from './components/RecentlyViewed';
import { SearchBar } from './components/SearchBar';
import { SkeletonTankCard } from './components/SkeletonTankCard';
import { TankCard } from './components/TankCard';
import { TankCardWrapper } from './components/TankCardWrapper';
import { treeTypeOrder } from './constants';
import { MAX_RECENTLY_VIEWED, treeTypeOrder } from './constants';
type TankSearchProps = Omit<FlexProps, 'onSelect'> & {
compact?: boolean;
@@ -47,10 +49,10 @@ const tankNames = await fetchTankNames();
export const TankSearch = memo<TankSearchProps>(
({ compact, onSelect, onSelectAll, ...props }) => {
const mutateTankopediaPersistent = TankopediaPersistent.useMutation();
const awaitedTanksDefinitionsArray = Object.values(tankDefinitions.tanks);
const tankFilters = useStore($tankFilters);
const tankopediaSort = useStore($tankopediaSort);
const tanksFiltered = useMemo(() => {
if (tankFilters.search === undefined) {
const filtered = awaitedTanksDefinitionsArray.filter((tank) =>
@@ -443,6 +445,8 @@ export const TankSearch = memo<TankSearchProps>(
<FilterControl compact={compact} />
)}
<RecentlyViewed />
<Flex mt="2" gap="1" align="center" justify="center" direction="column">
<Flex gap="2">
<Text color="gray">
@@ -457,6 +461,12 @@ export const TankSearch = memo<TankSearchProps>(
onClick={(event) => {
event.preventDefault();
onSelectAll(tanksFiltered);
mutateTankopediaPersistent((draft) => {
draft.recentlyViewed = uniq([
...tanksFiltered.map(({ id }) => id),
...draft.recentlyViewed,
]).slice(0, MAX_RECENTLY_VIEWED);
});
}}
>
Select all
+1 -1
View File
@@ -34,7 +34,7 @@ const testTanks = tanks
const mostPlayedTanks = tankAverages
.sort((a, b) => b.samples.d_1 - a.samples.d_1)
.map(({ id }) => tankDefinitions.tanks[id])
.filter(Boolean);
.filter((tank) => tank !== undefined && !tank.testing);
const popularTanks = [...testTanks, ...mostPlayedTanks].slice(0, 8);
---
@@ -18,6 +18,7 @@ import { tankToCompareMember } from '../../../core/blitzkit/tankToCompareMember'
import { App } from '../../../stores/app';
import { CompareEphemeral } from '../../../stores/compareEphemeral';
import { ComparePersistent } from '../../../stores/comparePersistent';
import { TankopediaPersistent } from '../../../stores/tankopediaPersistent';
const modelDefinitions = await fetchModelDefinitions();
const equipmentDefinitions = await fetchEquipmentDefinitions();
@@ -28,11 +29,15 @@ const tankDefinitions = await fetchTankDefinitions();
export function Page() {
return (
<App.Provider>
<ComparePersistent.Provider>
<CompareEphemeral.Provider data={createDefaultSkills(skillDefinitions)}>
<Content />
</CompareEphemeral.Provider>
</ComparePersistent.Provider>
<TankopediaPersistent.Provider>
<ComparePersistent.Provider>
<CompareEphemeral.Provider
data={createDefaultSkills(skillDefinitions)}
>
<Content />
</CompareEphemeral.Provider>
</ComparePersistent.Provider>
</TankopediaPersistent.Provider>
</App.Provider>
);
}
@@ -1,13 +1,16 @@
import { PageWrapper } from '../../../components/PageWrapper';
import { TankSearch } from '../../../components/TankSearch';
import { App } from '../../../stores/app';
import { TankopediaPersistent } from '../../../stores/tankopediaPersistent';
export function Page() {
return (
<PageWrapper color="purple" size={1028}>
<App.Provider>
<TankSearch />
</App.Provider>
<TankopediaPersistent.Provider>
<App.Provider>
<TankSearch />
</App.Provider>
</TankopediaPersistent.Provider>
</PageWrapper>
);
}
@@ -5,5 +5,7 @@ import { Page } from './_index';
---
<ToolLayout tool={tankopediaTool}>
<Page client:load />
<Page client:only="react">
<Page slot="fallback" />
</Page>
</ToolLayout>
@@ -19,6 +19,7 @@ export interface TankopediaPersistentStore {
showSpacedArmor: boolean;
showExternalModules: boolean;
showPrimaryArmor: boolean;
recentlyViewed: number[];
sort: {
by: TankopediaSortBy;
@@ -41,6 +42,7 @@ export const TankopediaPersistent = createContextualStore(() =>
showSpacedArmor: true,
showExternalModules: true,
showPrimaryArmor: true,
recentlyViewed: [],
sort: {
by: 'meta.none',
direction: 'ascending',