mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-22 11:17:05 +02:00
Fix: keep tank order stable on select
Tanks sharing the same tier, nation, class and type were shown in an arbitrary order that changed after selecting a tank. The meta sort had no tiebreaker, and the filtered list was sorted and reversed in place, so each re-sort started from the previous reversed order and flipped tied tanks around. - Added tank ID as the first sort key in `metaSortTank` so tied tanks always come out in the same order - Sorted a copy of the tank search results, pre-sorted by ID, instead of the cached list - Added an `exclude` option to tank search and used it in Compare so tanks already being compared can't be added again, including through select all or by swapping another slot Fixes #313
This commit is contained in:
1 parent
116c30be88
commit
273881c460
4 files changed
+28
-4
No files matched your search
@@ -12,6 +12,7 @@ export function metaSortTank(
|
||||
gameDefinitions: GameDefinitions,
|
||||
) {
|
||||
return tanks
|
||||
.sort((a, b) => a.id - b.id)
|
||||
.sort((a, b) => b.tier - a.tier)
|
||||
.sort(
|
||||
(a, b) => treeTypeOrder.indexOf(b.type) - treeTypeOrder.indexOf(a.type),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { PlusIcon, TrashIcon } from "@radix-ui/react-icons";
|
||||
import { Button, Dialog, Flex, SegmentedControl } from "@radix-ui/themes";
|
||||
import { useMemo } from "react";
|
||||
import { api } from "../../core/blitzkit/api";
|
||||
import { tankToCompareMember } from "../../core/blitzkit/tankToCompareMember";
|
||||
import { useLocale } from "../../hooks/useLocale";
|
||||
@@ -22,6 +23,11 @@ export function Controls({
|
||||
onAddTankDialogOpenChange,
|
||||
}: ControlsProps) {
|
||||
const deltaMode = ComparePersistent.use((state) => state.deltaMode);
|
||||
const members = CompareEphemeral.use((state) => state.members);
|
||||
const memberIds = useMemo(
|
||||
() => members.map((member) => member.tank.id),
|
||||
[members],
|
||||
);
|
||||
const { strings } = useLocale();
|
||||
|
||||
return (
|
||||
@@ -53,6 +59,7 @@ export function Controls({
|
||||
>
|
||||
<TankSearch
|
||||
compact
|
||||
exclude={memberIds}
|
||||
onSelect={(tank) => {
|
||||
CompareEphemeral.mutate((draft) => {
|
||||
draft.members.push(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ExternalLinkIcon, LoopIcon, TrashIcon } from "@radix-ui/react-icons";
|
||||
import { Dialog, Flex, IconButton } from "@radix-ui/themes";
|
||||
import { useState } from "react";
|
||||
import { useMemo, useState } from "react";
|
||||
import { api } from "../../core/blitzkit/api";
|
||||
import { tankToCompareMember } from "../../core/blitzkit/tankToCompareMember";
|
||||
import { useLocale } from "../../hooks/useLocale";
|
||||
@@ -18,6 +18,14 @@ const provisionDefinitions = await api.provisionDefinitions();
|
||||
export function TankControl({ index, slug }: TankControlProps) {
|
||||
const { locale, strings } = useLocale();
|
||||
const [switchTankDialogOpen, setSwitchTankDialogOpen] = useState(false);
|
||||
const members = CompareEphemeral.use((state) => state.members);
|
||||
const otherMemberIds = useMemo(
|
||||
() =>
|
||||
members
|
||||
.filter((_, memberIndex) => memberIndex !== index)
|
||||
.map((member) => member.tank.id),
|
||||
[members, index],
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex gap="3" justify="center" style={{ width: "100%" }}>
|
||||
@@ -57,6 +65,7 @@ export function TankControl({ index, slug }: TankControlProps) {
|
||||
>
|
||||
<TankSearch
|
||||
compact
|
||||
exclude={otherMemberIds}
|
||||
onSelect={(tank) => {
|
||||
CompareEphemeral.mutate((draft) => {
|
||||
draft.members[index] = tankToCompareMember(
|
||||
|
||||
@@ -38,6 +38,7 @@ export type TankSearchProps = MaybeSkeletonComponentProps &
|
||||
compact?: boolean;
|
||||
onSelect?: (tank: TankDefinition) => void;
|
||||
onSelectAll?: (tanks: TankDefinition[]) => void;
|
||||
exclude?: number[];
|
||||
};
|
||||
|
||||
const PREVIEW_COUNT = 20;
|
||||
@@ -52,14 +53,14 @@ const [gameDefinitions, modelDefinitions, tankDefinitions, tankNames] =
|
||||
]);
|
||||
|
||||
export const TankSearch = memo<TankSearchProps>(
|
||||
({ compact, onSelect, onSelectAll, skeleton, ...props }) => {
|
||||
({ compact, onSelect, onSelectAll, exclude, skeleton, ...props }) => {
|
||||
const { strings, locale } = useLocale();
|
||||
const wargaming = App.use((state) => state.logins.wargaming);
|
||||
const awaitedTanksDefinitionsArray = Object.values(tankDefinitions.tanks);
|
||||
const tankopediaSort = TankSort.use();
|
||||
const tankFilters = TankFilters.use();
|
||||
|
||||
const filtered = usePromise(
|
||||
const allFiltered = usePromise(
|
||||
() => {
|
||||
if (tankFilters.search === null) {
|
||||
return filterTanks(
|
||||
@@ -81,9 +82,15 @@ export const TankSearch = memo<TankSearchProps>(
|
||||
[tankFilters] as any,
|
||||
);
|
||||
const sorted = useMemo(() => {
|
||||
const filtered = allFiltered.filter(
|
||||
(tank) => !exclude?.includes(tank.id),
|
||||
);
|
||||
|
||||
if (tankFilters.search === null) {
|
||||
let sorted: TankDefinition[];
|
||||
|
||||
filtered.sort((a, b) => a.id - b.id);
|
||||
|
||||
switch (tankopediaSort.by) {
|
||||
case "meta.none":
|
||||
sorted = metaSortTank(filtered, gameDefinitions);
|
||||
@@ -417,7 +424,7 @@ export const TankSearch = memo<TankSearchProps>(
|
||||
} else {
|
||||
return filtered;
|
||||
}
|
||||
}, [tankFilters, tankopediaSort]);
|
||||
}, [tankFilters, tankopediaSort, exclude]);
|
||||
|
||||
const [loadedCards, setLoadedCards] = useState(DEFAULT_LOADED_CARDS);
|
||||
const tanks = sorted.slice(0, loadedCards);
|
||||
|
||||
Reference in new issue
Block a user