diff --git a/packages/website/src/components/TankSearch/components/RecentlyViewed.tsx b/packages/website/src/components/TankSearch/components/RecentlyViewed.tsx new file mode 100644 index 000000000..22bdb3aec --- /dev/null +++ b/packages/website/src/components/TankSearch/components/RecentlyViewed.tsx @@ -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 ( + + + Recently viewed + + + {recentlyViewed.map((id) => ( + + ))} + + + + + ); +} diff --git a/packages/website/src/components/TankSearch/components/TankCard/index.tsx b/packages/website/src/components/TankSearch/components/TankCard/index.tsx index 984b2717e..54f16ac28 100644 --- a/packages/website/src/components/TankSearch/components/TankCard/index.tsx +++ b/packages/website/src/components/TankSearch/components/TankCard/index.tsx @@ -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`)})`, diff --git a/packages/website/src/components/TankSearch/constants.tsx b/packages/website/src/components/TankSearch/constants.tsx index b5c31148f..5dbcbb450 100644 --- a/packages/website/src/components/TankSearch/constants.tsx +++ b/packages/website/src/components/TankSearch/constants.tsx @@ -5,3 +5,5 @@ export const treeTypeOrder = [ TankType.PREMIUM, TankType.COLLECTOR, ]; + +export const MAX_RECENTLY_VIEWED = 9; diff --git a/packages/website/src/components/TankSearch/index.tsx b/packages/website/src/components/TankSearch/index.tsx index cc2db5552..6e37b7bfc 100644 --- a/packages/website/src/components/TankSearch/index.tsx +++ b/packages/website/src/components/TankSearch/index.tsx @@ -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 & { compact?: boolean; @@ -47,10 +49,10 @@ const tankNames = await fetchTankNames(); export const TankSearch = memo( ({ 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( )} + + @@ -457,6 +461,12 @@ export const TankSearch = memo( onClick={(event) => { event.preventDefault(); onSelectAll(tanksFiltered); + mutateTankopediaPersistent((draft) => { + draft.recentlyViewed = uniq([ + ...tanksFiltered.map(({ id }) => id), + ...draft.recentlyViewed, + ]).slice(0, MAX_RECENTLY_VIEWED); + }); }} > Select all diff --git a/packages/website/src/pages/index.astro b/packages/website/src/pages/index.astro index e4b365a98..ae1a31e75 100644 --- a/packages/website/src/pages/index.astro +++ b/packages/website/src/pages/index.astro @@ -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); --- diff --git a/packages/website/src/pages/tools/compare/_index.tsx b/packages/website/src/pages/tools/compare/_index.tsx index d993ba14b..104247bed 100644 --- a/packages/website/src/pages/tools/compare/_index.tsx +++ b/packages/website/src/pages/tools/compare/_index.tsx @@ -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 ( - - - - - + + + + + + + ); } diff --git a/packages/website/src/pages/tools/tankopedia/_index.tsx b/packages/website/src/pages/tools/tankopedia/_index.tsx index 016ad352d..583a884c4 100644 --- a/packages/website/src/pages/tools/tankopedia/_index.tsx +++ b/packages/website/src/pages/tools/tankopedia/_index.tsx @@ -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 ( - - - + + + + + ); } diff --git a/packages/website/src/pages/tools/tankopedia/index.astro b/packages/website/src/pages/tools/tankopedia/index.astro index 5238d6985..6e2055143 100644 --- a/packages/website/src/pages/tools/tankopedia/index.astro +++ b/packages/website/src/pages/tools/tankopedia/index.astro @@ -5,5 +5,7 @@ import { Page } from './_index'; --- - + + + diff --git a/packages/website/src/stores/tankopediaPersistent/index.ts b/packages/website/src/stores/tankopediaPersistent/index.ts index 032a135d7..770f95dd7 100644 --- a/packages/website/src/stores/tankopediaPersistent/index.ts +++ b/packages/website/src/stores/tankopediaPersistent/index.ts @@ -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',