From edd3a1d91b35fc7768c9a7e1e03c77b30c1bc8c4 Mon Sep 17 00:00:00 2001
From: Remmy Cat Stock <3317423+remmycat@users.noreply.github.com>
Date: Tue, 25 Oct 2022 22:54:58 +0200
Subject: [PATCH] Improve MapPoolSelector UI
---
app/components/MapPoolSelector.tsx | 160 ++++++++++++++++++--
app/components/icons/ArrowLongLeft.tsx | 18 +++
app/hooks/useOnce.ts | 10 ++
app/modules/in-game-lists/modes.ts | 25 +--
app/modules/map-pool-serializer/map-pool.ts | 22 ++-
app/routes/calendar/$id/index.tsx | 8 +-
app/routes/calendar/new.tsx | 70 +++------
app/routes/maps.tsx | 119 ++++++++-------
app/styles/calendar-new.css | 2 +-
app/styles/maps.css | 40 +++--
app/styles/utils.css | 4 +
app/styles/vars.css | 5 +
app/utils/urls.ts | 10 +-
public/locales/de/common.json | 2 +
public/locales/de/game-misc.json | 7 +-
public/locales/en/common.json | 2 +
public/locales/en/game-misc.json | 7 +-
17 files changed, 355 insertions(+), 156 deletions(-)
create mode 100644 app/components/icons/ArrowLongLeft.tsx
create mode 100644 app/hooks/useOnce.ts
diff --git a/app/components/MapPoolSelector.tsx b/app/components/MapPoolSelector.tsx
index 6896fd97f..a4a495691 100644
--- a/app/components/MapPoolSelector.tsx
+++ b/app/components/MapPoolSelector.tsx
@@ -3,23 +3,73 @@ import { useTranslation } from "react-i18next";
import { Image } from "~/components/Image";
import type { ModeShort, StageId } from "~/modules/in-game-lists";
import { modes, stageIds } from "~/modules/in-game-lists";
-import { type MapPool } from "~/modules/map-pool-serializer";
+import { MapPool } from "~/modules/map-pool-serializer";
import { modeImageUrl, stageImageUrl } from "~/utils/urls";
+import { Button } from "~/components/Button";
+import { CrossIcon } from "./icons/Cross";
+import { ArrowLongLeftIcon } from "./icons/ArrowLongLeft";
+import * as React from "react";
+
+export type MapPoolSelectorProps = {
+ mapPool: MapPool;
+ handleRemoval?: () => void;
+ handleMapPoolChange: (mapPool: MapPool) => void;
+ className?: string;
+};
export function MapPoolSelector({
mapPool,
handleMapPoolChange,
-}: {
+ handleRemoval,
+ className,
+}: MapPoolSelectorProps) {
+ const { t } = useTranslation();
+
+ const handleStageModesChange = (newMapPool: MapPool) => {
+ handleMapPoolChange(newMapPool);
+ };
+
+ const handleClear = () => {
+ handleMapPoolChange(MapPool.EMPTY);
+ };
+
+ return (
+
+ );
+}
+
+export type MapPoolStagesProps = {
mapPool: MapPool;
- handleMapPoolChange?: ({
- mode,
- stageId,
- }: {
- mode: ModeShort;
- stageId: StageId;
- }) => void;
-}) {
- const { t } = useTranslation(["game-misc"]);
+ handleMapPoolChange?: (newMapPool: MapPool) => void;
+};
+
+export function MapPoolStages({
+ mapPool,
+ handleMapPoolChange,
+}: MapPoolStagesProps) {
+ const { t } = useTranslation(["game-misc", "common"]);
const isPresentational = !handleMapPoolChange;
@@ -29,6 +79,52 @@ export function MapPoolSelector({
return mapPool.hasStage(stageId);
};
+ const handleModeChange = ({
+ mode,
+ stageId,
+ }: {
+ mode: ModeShort;
+ stageId: StageId;
+ }) => {
+ const newMapPool = mapPool.parsed[mode].includes(stageId)
+ ? new MapPool({
+ ...mapPool.parsed,
+ [mode]: mapPool.parsed[mode].filter((id) => id !== stageId),
+ })
+ : new MapPool({
+ ...mapPool.parsed,
+ [mode]: [...mapPool.parsed[mode], stageId],
+ });
+
+ handleMapPoolChange?.(newMapPool);
+ };
+
+ const handleStageClear = (stageId: StageId) => {
+ const newMapPool = new MapPool({
+ TW: mapPool.parsed.TW.filter((id) => id !== stageId),
+ SZ: mapPool.parsed.SZ.filter((id) => id !== stageId),
+ TC: mapPool.parsed.TC.filter((id) => id !== stageId),
+ RM: mapPool.parsed.RM.filter((id) => id !== stageId),
+ CB: mapPool.parsed.CB.filter((id) => id !== stageId),
+ });
+
+ handleMapPoolChange?.(newMapPool);
+ };
+
+ const handleStageAdd = (stageId: StageId) => {
+ const newMapPool = new MapPool({
+ TW: [...mapPool.parsed.TW, stageId],
+ SZ: [...mapPool.parsed.SZ, stageId],
+ TC: [...mapPool.parsed.TC, stageId],
+ RM: [...mapPool.parsed.RM, stageId],
+ CB: [...mapPool.parsed.CB, stageId],
+ });
+
+ handleMapPoolChange?.(newMapPool);
+ };
+
+ const id = React.useId();
+
return (
{stageIds.filter(stageRowIsVisible).map((stageId) => (
@@ -40,8 +136,14 @@ export function MapPoolSelector({
width={80}
height={45}
/>
-
-
{t(`game-misc:STAGE_${stageId}`)}
+
+
+ {t(`game-misc:STAGE_${stageId}`)}
+
{modes.map((mode) => {
const selected = mapPool.parsed[mode.short].includes(stageId);
@@ -54,7 +156,8 @@ export function MapPoolSelector({
className={clsx("maps__mode", {
selected,
})}
- alt={mode.long}
+ title={t(`game-misc:MODE_LONG_${mode.short}`)}
+ alt={t(`game-misc:MODE_LONG_${mode.short}`)}
path={modeImageUrl(mode.short)}
width={33}
height={33}
@@ -69,15 +172,18 @@ export function MapPoolSelector({
selected,
})}
onClick={() =>
- handleMapPoolChange?.({ mode: mode.short, stageId })
+ handleModeChange?.({ mode: mode.short, stageId })
}
type="button"
+ title={t(`game-misc:MODE_LONG_${mode.short}`)}
+ aria-describedby={`${id}-stage-name-${stageId}`}
+ aria-pressed={selected}
>
);
})}
+ {!isPresentational &&
+ (mapPool.hasStage(stageId) ? (
+
diff --git a/app/components/icons/ArrowLongLeft.tsx b/app/components/icons/ArrowLongLeft.tsx
new file mode 100644
index 000000000..b419e99ea
--- /dev/null
+++ b/app/components/icons/ArrowLongLeft.tsx
@@ -0,0 +1,18 @@
+export function ArrowLongLeftIcon({ className }: { className?: string }) {
+ return (
+
+ );
+}
diff --git a/app/hooks/useOnce.ts b/app/hooks/useOnce.ts
new file mode 100644
index 000000000..4296bf40a
--- /dev/null
+++ b/app/hooks/useOnce.ts
@@ -0,0 +1,10 @@
+import { useMemo } from "react";
+
+/**
+ * Utility hook for calling `useMemo(f, [])`, when you're sure it needs no
+ * revalidation but feel bad for getting shamed by eslint everytime :D
+ */
+export function useOnce
(factory: () => T) {
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ return useMemo(factory, []);
+}
diff --git a/app/modules/in-game-lists/modes.ts b/app/modules/in-game-lists/modes.ts
index 62736c388..d44939f0f 100644
--- a/app/modules/in-game-lists/modes.ts
+++ b/app/modules/in-game-lists/modes.ts
@@ -1,24 +1,9 @@
export const modes = [
- {
- short: "TW",
- long: "Turf War",
- },
- {
- short: "SZ",
- long: "Splat Zones",
- },
- {
- short: "TC",
- long: "Tower Control",
- },
- {
- short: "RM",
- long: "Rainmaker",
- },
- {
- short: "CB",
- long: "Clam Blitz",
- },
+ { short: "TW" },
+ { short: "SZ" },
+ { short: "TC" },
+ { short: "RM" },
+ { short: "CB" },
] as const;
export const modesShort = modes.map((mode) => mode.short);
diff --git a/app/modules/map-pool-serializer/map-pool.ts b/app/modules/map-pool-serializer/map-pool.ts
index 8c26c654b..8639d5416 100644
--- a/app/modules/map-pool-serializer/map-pool.ts
+++ b/app/modules/map-pool-serializer/map-pool.ts
@@ -6,7 +6,11 @@ import type { ReadonlyMapPoolObject, MapPoolObject } from "./types";
import clone from "just-clone";
import type { MapPoolMap } from "~/db/types";
import { mapPoolListToMapPoolObject } from "~/modules/map-list-generator";
-import type { ModeShort, StageId } from "~/modules/in-game-lists";
+import {
+ type ModeShort,
+ type StageId,
+ stageIds,
+} from "~/modules/in-game-lists";
type DbMapPoolList = Array>;
@@ -84,4 +88,20 @@ export class MapPool {
toJSON() {
return this.parsed;
}
+
+ static EMPTY = new MapPool({
+ SZ: [],
+ TC: [],
+ CB: [],
+ RM: [],
+ TW: [],
+ });
+
+ static ANARCHY = new MapPool({
+ SZ: [...stageIds],
+ TC: [...stageIds],
+ CB: [...stageIds],
+ RM: [...stageIds],
+ TW: [],
+ });
}
diff --git a/app/routes/calendar/$id/index.tsx b/app/routes/calendar/$id/index.tsx
index bc9db4ffe..28b2b8c69 100644
--- a/app/routes/calendar/$id/index.tsx
+++ b/app/routes/calendar/$id/index.tsx
@@ -33,13 +33,13 @@ import { discordFullName, makeTitle } from "~/utils/strings";
import {
calendarEditPage,
calendarReportWinnersPage,
- mapsPage,
navIconUrl,
+ readonlyMapsPage,
resolveBaseUrl,
userPage,
} from "~/utils/urls";
import { actualNumber, id } from "~/utils/zod";
-import { MapPoolSelector } from "~/components/MapPoolSelector";
+import { MapPoolStages } from "~/components/MapPoolSelector";
import { Tags } from "../components/Tags";
import { MapPool } from "~/modules/map-pool-serializer";
@@ -245,10 +245,10 @@ function MapPoolInfo() {
return (
-
+
diff --git a/app/routes/calendar/new.tsx b/app/routes/calendar/new.tsx
index b19e1076d..5357ffadb 100644
--- a/app/routes/calendar/new.tsx
+++ b/app/routes/calendar/new.tsx
@@ -20,14 +20,12 @@ import { TrashIcon } from "~/components/icons/Trash";
import { Input } from "~/components/Input";
import { Label } from "~/components/Label";
import { Main } from "~/components/Main";
-import { Toggle } from "~/components/Toggle";
import { CALENDAR_EVENT } from "~/constants";
import { db } from "~/db";
import type { Badge as BadgeType, CalendarEventTag } from "~/db/types";
import { useIsMounted } from "~/hooks/useIsMounted";
import { requireUser } from "~/modules/auth";
import { i18next } from "~/modules/i18n";
-import type { ModeShort, StageId } from "~/modules/in-game-lists";
import { MapPool } from "~/modules/map-pool-serializer";
import { canEditCalendarEvent } from "~/permissions";
import calendarNewStyles from "~/styles/calendar-new.css";
@@ -519,61 +517,41 @@ function BadgesAdder() {
);
}
-const DEFAULT_MAP_POOL = new MapPool({
- SZ: [],
- TC: [],
- CB: [],
- RM: [],
- TW: [],
-});
function MapPoolSection() {
- const { t } = useTranslation(["game-misc", "calendar"]);
+ const { t } = useTranslation(["game-misc", "common"]);
const { eventToEdit } = useLoaderData();
const [mapPool, setMapPool] = React.useState(
- eventToEdit?.mapPool ? new MapPool(eventToEdit.mapPool) : DEFAULT_MAP_POOL
+ eventToEdit?.mapPool ? new MapPool(eventToEdit.mapPool) : MapPool.EMPTY
);
const [includeMapPool, setIncludeMapPool] = React.useState(
Boolean(eventToEdit?.mapPool)
);
- const handleMapPoolChange = ({
- mode,
- stageId,
- }: {
- mode: ModeShort;
- stageId: StageId;
- }) => {
- const newMapPool = new MapPool(
- mapPool.parsed[mode].includes(stageId)
- ? {
- ...mapPool.parsed,
- [mode]: mapPool.parsed[mode].filter((id) => id !== stageId),
- }
- : {
- ...mapPool.parsed,
- [mode]: [...mapPool.parsed[mode], stageId],
- }
- );
+ const id = React.useId();
- setMapPool(newMapPool);
- };
+ return includeMapPool ? (
+ <>
+
- return (
-
- {includeMapPool && (
-
- )}
-
-
-
- {includeMapPool && (
-
- )}
-
+
setIncludeMapPool(false)}
+ handleMapPoolChange={setMapPool}
+ />
+ >
+ ) : (
+
+
+ setIncludeMapPool(true)}
+ >
+ {t("common:actions.add")}
+
);
}
diff --git a/app/routes/maps.tsx b/app/routes/maps.tsx
index 61eb662cc..1c63249ea 100644
--- a/app/routes/maps.tsx
+++ b/app/routes/maps.tsx
@@ -17,12 +17,7 @@ import { Main } from "~/components/Main";
import { Toggle } from "~/components/Toggle";
import { db } from "~/db";
import { i18next } from "~/modules/i18n";
-import {
- stageIds,
- type ModeShort,
- type ModeWithStage,
- type StageId,
-} from "~/modules/in-game-lists";
+import { stageIds, type ModeWithStage } from "~/modules/in-game-lists";
import {
generateMapList,
mapPoolToNonEmptyModes,
@@ -33,11 +28,18 @@ import styles from "~/styles/maps.css";
import { makeTitle } from "~/utils/strings";
import { calendarEventPage, ipLabsMaps } from "~/utils/urls";
import { type SendouRouteHandle } from "~/utils/remix";
-import { MapPoolSelector } from "~/components/MapPoolSelector";
+import { MapPoolSelector, MapPoolStages } from "~/components/MapPoolSelector";
+import { EditIcon } from "~/components/icons/Edit";
+import { useOnce } from "~/hooks/useOnce";
const AMOUNT_OF_MAPS_IN_MAP_LIST = stageIds.length * 2;
-export const unstable_shouldReload: ShouldReloadFunction = () => false;
+export const unstable_shouldReload: ShouldReloadFunction = ({ url }) => {
+ const searchParams = new URL(url).searchParams;
+ // Only let loader reload data if we're not currently editing the map pool
+ // and persisting it in the search params.
+ return searchParams.has("readonly");
+};
export const links: LinksFunction = () => {
return [{ rel: "stylesheet", href: styles }];
@@ -81,34 +83,43 @@ export const loader = async ({ request }: LoaderArgs) => {
};
};
-const DEFAULT_MAP_POOL = new MapPool({
- SZ: [...stageIds],
- TC: [...stageIds],
- CB: [...stageIds],
- RM: [...stageIds],
- TW: [],
-});
-
export default function MapListPage() {
const { t } = useTranslation(["common"]);
const data = useLoaderData();
const [searchParams] = useSearchParams();
- const { mapPool, handleMapPoolChange } = useSearchParamMapPool();
+ const { mapPool, handleMapPoolChange, readonly, switchToEditMode } =
+ useSearchParamPersistedMapPool();
return (
- {data.calendarEvent && !searchParams.has("pool") && (
-
- {t("common:maps.mapPool")}:{" "}
-
- {data.calendarEvent.name}
-
+ {searchParams.has("readonly") && data.calendarEvent && (
+
+
+ {t("common:maps.mapPool")}:{" "}
+ {
+
+ {data.calendarEvent.name}
+
+ }
+
+
}
+ >
+ {t("common:actions.edit")}
+
)}
-
+ {readonly ? (
+
+ ) : (
+
+ )}
();
const [searchParams, setSearchParams] = useSearchParams();
- const mapPool = (() => {
+ const initialMapPool = useOnce(() => {
if (searchParams.has("pool")) {
return new MapPool(searchParams.get("pool")!);
}
- if (data?.mapPool) {
+ if (data.mapPool) {
return new MapPool(data.mapPool);
}
- return DEFAULT_MAP_POOL;
- })();
+ return MapPool.ANARCHY;
+ });
- const handleMapPoolChange = ({
- mode,
- stageId,
- }: {
- mode: ModeShort;
- stageId: StageId;
- }) => {
- const newMapPool = new MapPool(
- mapPool.parsed[mode].includes(stageId)
- ? {
- ...mapPool.parsed,
- [mode]: mapPool.parsed[mode].filter((id) => id !== stageId),
- }
- : {
- ...mapPool.parsed,
- [mode]: [...mapPool.parsed[mode], stageId],
- }
- );
+ const [mapPool, setMapPool] = React.useState(initialMapPool);
+ const handleMapPoolChange = (newMapPool: MapPool) => {
+ setMapPool(newMapPool);
setSearchParams(
{
pool: newMapPool.serialized,
@@ -165,9 +161,20 @@ function useSearchParamMapPool() {
);
};
+ const switchToEditMode = () => {
+ const newSearchParams = new URLSearchParams(searchParams);
+ newSearchParams.delete("readonly");
+ setSearchParams(newSearchParams, {
+ replace: false,
+ state: { scroll: false },
+ });
+ };
+
return {
mapPool,
+ readonly: searchParams.has("readonly"),
handleMapPoolChange,
+ switchToEditMode,
};
}
@@ -192,13 +199,16 @@ function MapListCreator({ mapPool }: { mapPool: MapPool }) {
setMapList(list);
};
+ const disabled =
+ mapPool.isEmpty() || (szEveryOther && !mapPool.hasMode("SZ"));
+
return (
-
+
{t("common:maps.createMapList")}
{mapList && (
@@ -206,7 +216,12 @@ function MapListCreator({ mapPool }: { mapPool: MapPool }) {
{mapList.map(({ mode, stageId }, i) => (
-
- {t(`game-misc:MODE_SHORT_${mode}`)}{" "}
+
+ {t(`game-misc:MODE_SHORT_${mode}`)}
+ {" "}
{t(`game-misc:STAGE_${stageId}`)}
))}
diff --git a/app/styles/calendar-new.css b/app/styles/calendar-new.css
index 291f6adc1..fdf61d77f 100644
--- a/app/styles/calendar-new.css
+++ b/app/styles/calendar-new.css
@@ -1,5 +1,5 @@
.calendar-new__container {
- max-width: 32rem;
+ max-width: 38rem;
}
.calendar-new__select {
diff --git a/app/styles/maps.css b/app/styles/maps.css
index cc680c1d4..5d064d305 100644
--- a/app/styles/maps.css
+++ b/app/styles/maps.css
@@ -1,9 +1,19 @@
.maps__container {
- max-width: 32rem;
+ max-width: 38rem;
+}
+
+.maps__pool-meta {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
}
.maps__pool-info {
- font-size: var(--fonts-xxs);
+ font-size: var(--fonts-xs);
+ font-weight: var(--bold);
+}
+
+.maps__pool-info a {
font-weight: var(--semi-bold);
}
@@ -49,29 +59,27 @@
.maps__mode-button {
padding: 0;
- padding: var(--s-1-5);
- border: none;
- border-radius: var(--rounded);
- background-color: var(--bg-darker);
+ padding: var(--s-1);
+ border: 2px solid var(--bg-darker);
+ border-radius: var(--rounded-full);
+ background-color: transparent;
color: var(--theme);
opacity: 1 !important;
outline: initial;
}
.maps__mode-button.selected {
- background-color: var(--theme-very-transparent);
+ border: 2px solid transparent;
+ background-color: var(--bg-mode-active);
}
.maps__stage-image {
border-radius: var(--rounded);
}
-.maps__mode {
- filter: grayscale(100%);
-}
-
-.maps__mode.selected {
- filter: unset;
+.maps__mode:not(.selected) {
+ filter: var(--inactive-image-filter);
+ opacity: 0.6;
}
.maps__map-list-creator {
@@ -96,3 +104,9 @@
font-weight: var(--semi-bold);
margin-block-start: var(--s-4);
}
+
+.maps__mode-abbr {
+ color: var(--text-lighter);
+ font-weight: var(--bold);
+ text-decoration: none;
+}
diff --git a/app/styles/utils.css b/app/styles/utils.css
index fef6c85f5..f9b542e43 100644
--- a/app/styles/utils.css
+++ b/app/styles/utils.css
@@ -126,6 +126,10 @@
justify-content: center;
}
+.justify-end {
+ justify-content: flex-end;
+}
+
.flex-wrap {
flex-wrap: wrap;
}
diff --git a/app/styles/vars.css b/app/styles/vars.css
index 71e346065..c626fc365 100644
--- a/app/styles/vars.css
+++ b/app/styles/vars.css
@@ -7,6 +7,7 @@ html {
--bg-darker-transparent: hsla(202deg 90% 90% / 65%);
--bg-ability: rgb(3 6 7);
--bg-badge: #000;
+ --bg-mode-active: hsl(255deg 66.7% 50% / 40%);
--abilities-button-bg: hsl(237deg 32% 30%);
--badge-text: rgb(255 255 255 / 95%);
--border: hsl(237deg 100% 86%);
@@ -31,6 +32,7 @@ html {
--theme-semi-transparent-vibrant: hsl(255deg 100% 81% / 75%);
--theme-secondary: hsl(85deg 66.7% 55.3%);
--rounded: 16px;
+ --rounded-full: 200px;
--rounded-sm: 10px;
--fonts-xl: 1.5rem;
--fonts-lg: 1.2rem;
@@ -73,6 +75,7 @@ html {
--s-96: 2rem;
--sparse: 0.4px;
--label-margin: var(--s-1);
+ --inactive-image-filter: grayscale(100%) brightness(30%);
}
html.dark {
@@ -84,6 +87,7 @@ html.dark {
--bg-darker-transparent: hsla(237.3deg 42.3% 26.6% / 90%);
--bg-ability: rgb(17 19 43);
--bg-badge: #000;
+ --bg-mode-active: var(--theme-transparent);
--abilities-button-bg: hsl(237.3deg 42.3% 26.6%);
--border: hsl(237.3deg 42.3% 45.6%);
--button-text: rgb(0 0 0 / 85%);
@@ -104,6 +108,7 @@ html.dark {
--theme-transparent-vibrant: hsl(255deg 78% 65% / 54%);
--theme-semi-transparent-vibrant: hsl(255deg 78% 65% / 75%);
--theme-secondary: hsl(85deg 66.7% 55.3%);
+ --inactive-image-filter: grayscale(100%) brightness(130%);
}
html.dark .light-mode-only {
diff --git a/app/utils/urls.ts b/app/utils/urls.ts
index bef1ce4dd..bb77c5894 100644
--- a/app/utils/urls.ts
+++ b/app/utils/urls.ts
@@ -1,5 +1,11 @@
import slugify from "slugify";
-import type { Badge, GearType, MapPoolMap, User } from "~/db/types";
+import type {
+ Badge,
+ CalendarEvent,
+ GearType,
+ MapPoolMap,
+ User,
+} from "~/db/types";
import type { ModeShort, weaponCategories } from "~/modules/in-game-lists";
import type {
Ability,
@@ -74,6 +80,8 @@ export const calendarReportWinnersPage = (eventId: number) =>
`/calendar/${eventId}/report-winners`;
export const mapsPage = (eventId?: MapPoolMap["calendarEventId"]) =>
`/maps${eventId ? `?eventId=${eventId}` : ""}`;
+export const readonlyMapsPage = (eventId: CalendarEvent["id"]) =>
+ `/maps?readonly&eventId=${eventId}`;
export const articlePage = (slug: string) => `/a/${slug}`;
export const analyzerPage = (args?: {
weaponId: MainWeaponId;
diff --git a/public/locales/de/common.json b/public/locales/de/common.json
index af292c7b5..7820cc344 100644
--- a/public/locales/de/common.json
+++ b/public/locales/de/common.json
@@ -34,6 +34,8 @@
"actions.delete": "Löschen",
"actions.loadMore": "Mehr laden",
"actions.close": "Schließen",
+ "actions.clear": "Leeren",
+ "actions.selectAll": "Alle auswählen",
"maps.createMapList": "Arenen-Liste erstellen",
"maps.halfSz": "50% Herrschaft",
diff --git a/public/locales/de/game-misc.json b/public/locales/de/game-misc.json
index 4e3c81ce7..d9e393f1c 100644
--- a/public/locales/de/game-misc.json
+++ b/public/locales/de/game-misc.json
@@ -15,5 +15,10 @@
"MODE_SHORT_SZ": "HS",
"MODE_SHORT_TC": "TK",
"MODE_SHORT_RM": "OG",
- "MODE_SHORT_CB": "MC"
+ "MODE_SHORT_CB": "MC",
+ "MODE_LONG_TW": "Revierkampf",
+ "MODE_LONG_SZ": "Herrschaft",
+ "MODE_LONG_TC": "Turmkommando",
+ "MODE_LONG_RM": "Operation Goldfisch",
+ "MODE_LONG_CB": "Muschelchaos"
}
diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index db52a437d..c9f7a376e 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -36,6 +36,8 @@
"actions.loadMore": "Load more",
"actions.copyToClipboard": "Copy to clipboard",
"actions.close": "Close",
+ "actions.clear": "Clear",
+ "actions.selectAll": "Select All",
"maps.createMapList": "Create map list",
"maps.halfSz": "50% SZ",
diff --git a/public/locales/en/game-misc.json b/public/locales/en/game-misc.json
index be8229456..780d33e38 100644
--- a/public/locales/en/game-misc.json
+++ b/public/locales/en/game-misc.json
@@ -15,5 +15,10 @@
"MODE_SHORT_SZ": "SZ",
"MODE_SHORT_TC": "TC",
"MODE_SHORT_RM": "RM",
- "MODE_SHORT_CB": "CB"
+ "MODE_SHORT_CB": "CB",
+ "MODE_LONG_TW": "Turf War",
+ "MODE_LONG_SZ": "Splat Zones",
+ "MODE_LONG_TC": "Tower Control",
+ "MODE_LONG_RM": "Rainmaker",
+ "MODE_LONG_CB": "Clam Blitz"
}