From ac8ef0630d552de7cd41da035465df62fc65d7b0 Mon Sep 17 00:00:00 2001 From: Kalle <38327916+Sendouc@users.noreply.github.com> Date: Tue, 8 Sep 2026 20:37:36 +0300 Subject: [PATCH] Add customizability to weapon widget --- app/db/seed/dev/users.ts | 2 +- .../PlusVotingRepository.server.test.ts | 4 ++- .../XRankPlacementRepository.server.ts | 11 ++++++ app/features/user-page/UserRepository.test.ts | 2 +- .../components/WidgetSettingsForm.tsx | 2 ++ .../core/widgets/portfolio-loaders.server.ts | 18 ++++++++-- .../user-page/core/widgets/portfolio.test.ts | 2 +- .../user-page/core/widgets/portfolio.ts | 10 ++++-- .../core/widgets/widget-form-schemas.ts | 10 ++++++ app/features/user-page/user-page-constants.ts | 1 + .../user-page/user-page-schemas.test.ts | 35 +++++++++++++++++++ ...26-09-08-weapon-pool-widget-custom-list.md | 5 +++ e2e/pages/user/user-edit-widgets-page.ts | 10 ++++++ e2e/user-page.spec.ts | 27 ++++++++++++++ locales/da/forms.json | 1 + locales/de/forms.json | 1 + locales/en/forms.json | 1 + locales/en/user.json | 2 +- locales/es-ES/forms.json | 1 + locales/es-US/forms.json | 1 + locales/fr-CA/forms.json | 1 + locales/fr-EU/forms.json | 1 + locales/he/forms.json | 1 + locales/it/forms.json | 1 + locales/ja/forms.json | 1 + locales/ko/forms.json | 1 + locales/nl/forms.json | 1 + locales/pl/forms.json | 1 + locales/pt-BR/forms.json | 1 + locales/ru/forms.json | 1 + locales/zh/forms.json | 1 + ...60908162417-weapon-pool-widget-settings.ts | 18 ++++++++++ scripts/benchmark-db/cases.ts | 6 ++++ 33 files changed, 172 insertions(+), 9 deletions(-) create mode 100644 changelog/2026-09-08-weapon-pool-widget-custom-list.md create mode 100644 migrations/20260908162417-weapon-pool-widget-settings.ts diff --git a/app/db/seed/dev/users.ts b/app/db/seed/dev/users.ts index a695ba73b..ee4f0655c 100644 --- a/app/db/seed/dev/users.ts +++ b/app/db/seed/dev/users.ts @@ -269,7 +269,7 @@ export function nzapWidgets(): StoredWidget[] { settings: { controller: "s2-pro-con", motionSens: 50, stickSens: 5 }, }, { id: "social-links" }, - { id: "weapon-pool" }, + { id: "weapon-pool", settings: { weaponPool: [] } }, { id: "map-mode-preferences" }, { id: "badges-owned", settings: { favoriteBadgeIds: [] } }, { id: "trophies-owned" }, diff --git a/app/features/plus-voting/PlusVotingRepository.server.test.ts b/app/features/plus-voting/PlusVotingRepository.server.test.ts index c94ab48ad..434c94098 100644 --- a/app/features/plus-voting/PlusVotingRepository.server.test.ts +++ b/app/features/plus-voting/PlusVotingRepository.server.test.ts @@ -54,7 +54,9 @@ describe("PlusVotingRepository.findAllUsersForVoting", () => { }); test("returns no bio for a user without a bio widget", async () => { - const bio = await bioOf([{ id: "weapon-pool" }]); + const bio = await bioOf([ + { id: "weapon-pool", settings: { weaponPool: [] } }, + ]); expect(bio).toBeNull(); }); diff --git a/app/features/top-search/XRankPlacementRepository.server.ts b/app/features/top-search/XRankPlacementRepository.server.ts index 3bf4b2373..ec9d1d2a3 100644 --- a/app/features/top-search/XRankPlacementRepository.server.ts +++ b/app/features/top-search/XRankPlacementRepository.server.ts @@ -82,6 +82,17 @@ export async function isPlayerLinkedByUserId(userId: number): Promise { return Boolean(player); } +/** Weapons the user has a ten-star badge for, from their linked X Rank placements. */ +export async function findTenStarWeaponSplIdsByUserId(userId: number) { + const rows = await db + .selectFrom("TenStarWeapon") + .select("TenStarWeapon.weaponSplId") + .where("TenStarWeapon.userId", "=", userId) + .execute(); + + return rows.map((row) => row.weaponSplId); +} + /** From the linked player's denormalized `SplatoonPlayer.peakXp` (see {@link refreshAllPeakXp}); `null` without one. */ export async function findPeakVerifiedXpByUserId( userId: Tables["User"]["id"], diff --git a/app/features/user-page/UserRepository.test.ts b/app/features/user-page/UserRepository.test.ts index 8375fb96d..72bdff1ce 100644 --- a/app/features/user-page/UserRepository.test.ts +++ b/app/features/user-page/UserRepository.test.ts @@ -519,7 +519,7 @@ describe("UserRepository", () => { describe("UserRepository.findStoredWidgetsByUserId", () => { const sixMainWidgets: Parameters[1] = { widgets: [ - { id: "weapon-pool" }, + { id: "weapon-pool", settings: { weaponPool: [] } }, { id: "trophies-owned" }, { id: "badges-owned", settings: { favoriteBadgeIds: [] } }, { id: "badges-authored" }, diff --git a/app/features/user-page/components/WidgetSettingsForm.tsx b/app/features/user-page/components/WidgetSettingsForm.tsx index fd74a6767..3ecc9aeee 100644 --- a/app/features/user-page/components/WidgetSettingsForm.tsx +++ b/app/features/user-page/components/WidgetSettingsForm.tsx @@ -85,6 +85,8 @@ function WidgetFormFields({ widgetId }: { widgetId: string }) { ); case "peak-xp-weapon": return ; + case "weapon-pool": + return ; case "sens": return ; case "art": diff --git a/app/features/user-page/core/widgets/portfolio-loaders.server.ts b/app/features/user-page/core/widgets/portfolio-loaders.server.ts index b9d14a143..e386977dd 100644 --- a/app/features/user-page/core/widgets/portfolio-loaders.server.ts +++ b/app/features/user-page/core/widgets/portfolio-loaders.server.ts @@ -299,8 +299,22 @@ export const WIDGET_LOADERS = { commissions: async (userId: number) => { return UserRepository.findCommissionsByUserId(userId); }, - "weapon-pool": async (userId: number) => { - return MatchProfileRepository.findWeaponPoolByUserId(userId); + "weapon-pool": async ( + userId: number, + settings: ExtractWidgetSettings<"weapon-pool">, + ) => { + if (settings.weaponPool.length === 0) { + return MatchProfileRepository.findWeaponPoolByUserId(userId); + } + + const tenStarWeaponSplIds = + await XRankPlacementRepository.findTenStarWeaponSplIdsByUserId(userId); + + return settings.weaponPool.map((weapon) => ({ + weaponSplId: weapon.id, + isFavorite: weapon.isFavorite ? 1 : 0, + isTenStar: tenStarWeaponSplIds.includes(weapon.id) ? 1 : 0, + })); }, "social-links": async (userId: number) => { return UserRepository.findSocialLinksByUserId(userId); diff --git a/app/features/user-page/core/widgets/portfolio.test.ts b/app/features/user-page/core/widgets/portfolio.test.ts index 5498ba05e..bb4815e59 100644 --- a/app/features/user-page/core/widgets/portfolio.test.ts +++ b/app/features/user-page/core/widgets/portfolio.test.ts @@ -3,7 +3,7 @@ import { widgetsAvailableTo } from "./portfolio"; import type { StoredWidget } from "./types"; const MAIN_WIDGETS: StoredWidget[] = [ - { id: "weapon-pool" }, + { id: "weapon-pool", settings: { weaponPool: [] } }, { id: "trophies-owned" }, { id: "badges-owned", settings: { favoriteBadgeIds: [] } }, { id: "badges-authored" }, diff --git a/app/features/user-page/core/widgets/portfolio.ts b/app/features/user-page/core/widgets/portfolio.ts index 0d63d7104..e84a7dfa7 100644 --- a/app/features/user-page/core/widgets/portfolio.ts +++ b/app/features/user-page/core/widgets/portfolio.ts @@ -19,6 +19,7 @@ import { sensSchema, tierListSchema, timezoneSchema, + weaponPoolWidgetSchema, xRankPeaksSchema, } from "./widget-form-schemas"; @@ -54,7 +55,12 @@ export const ALL_WIDGETS = { schema: favoriteStageSchema, defaultSettings: { stageId: 1 }, }), - defineWidget({ id: "weapon-pool", slot: "main" }), + defineWidget({ + id: "weapon-pool", + slot: "main", + schema: weaponPoolWidgetSchema, + defaultSettings: { weaponPool: [] }, + }), defineWidget({ id: "lfg-posts", slot: "main", navItem: "lfg" }), defineWidget({ id: "sens", @@ -239,7 +245,7 @@ export const ALL_WIDGETS = { * showed before it was widget based. */ export const DEFAULT_WIDGETS: StoredWidget[] = [ - { id: "weapon-pool" }, + { id: "weapon-pool", settings: { weaponPool: [] } }, { id: "x-rank-peaks", settings: { division: "both" } }, { id: "badges-owned", settings: { favoriteBadgeIds: [] } }, { id: "bio", settings: { bio: null } }, diff --git a/app/features/user-page/core/widgets/widget-form-schemas.ts b/app/features/user-page/core/widgets/widget-form-schemas.ts index c82481c4a..e99acc23b 100644 --- a/app/features/user-page/core/widgets/widget-form-schemas.ts +++ b/app/features/user-page/core/widgets/widget-form-schemas.ts @@ -15,6 +15,7 @@ import { textArea, textAreaOptional, textField, + weaponPool, weaponSelect, } from "~/form/fields"; import type { FormObjectSchema, SelectOption } from "~/form/types"; @@ -94,6 +95,14 @@ export const peakXpWeaponSchema = v.object({ }), }); +export const weaponPoolWidgetSchema = v.object({ + weaponPool: weaponPool({ + label: "labels.weaponPool", + bottomText: "bottomTexts.weaponPoolWidget", + maxCount: USER.WEAPON_POOL_WIDGET_MAX, + }), +}); + const CONTROLLERS = [ "s1-pro-con", "s2-pro-con", @@ -203,6 +212,7 @@ const WIDGET_FORM_SCHEMAS: Record = { "favorite-stage": favoriteStageSchema, "peak-xp-unverified": peakXpUnverifiedSchema, "peak-xp-weapon": peakXpWeaponSchema, + "weapon-pool": weaponPoolWidgetSchema, sens: sensSchema, art: artSchema, links: linksSchema, diff --git a/app/features/user-page/user-page-constants.ts b/app/features/user-page/user-page-constants.ts index 30ca5090a..d7dbec5fb 100644 --- a/app/features/user-page/user-page-constants.ts +++ b/app/features/user-page/user-page-constants.ts @@ -14,6 +14,7 @@ export const USER = { MAX_SIDE_WIDGETS_SUPPORTER: 7, GAME_BADGES_MAX: 8, GAME_BADGES_SMALL_MAX: 4, + WEAPON_POOL_WIDGET_MAX: 7, COUNTDOWN_TITLE_MAX_LENGTH: 50, MARKDOWN_WIDGET_MAX_LENGTH: 2000, PEAK_XP_MIN: 1000, diff --git a/app/features/user-page/user-page-schemas.test.ts b/app/features/user-page/user-page-schemas.test.ts index 2ab86cf98..3dbd045eb 100644 --- a/app/features/user-page/user-page-schemas.test.ts +++ b/app/features/user-page/user-page-schemas.test.ts @@ -54,6 +54,30 @@ describe("widgetsEditSchema", () => { settings: { controller: "s1-pro-con", motionSens: 51, stickSens: null }, }, }, + { + why: "the same weapon twice in the weapon pool widget", + widget: { + id: "weapon-pool", + settings: { + weaponPool: [ + { id: 40, isFavorite: false }, + { id: 40, isFavorite: true }, + ], + }, + }, + }, + { + why: "more weapons than the weapon pool widget allows", + widget: { + id: "weapon-pool", + settings: { + weaponPool: [0, 10, 20, 30, 40, 50, 60, 70].map((id) => ({ + id, + isFavorite: false, + })), + }, + }, + }, ])("rejects $why", ({ widget }) => { const result = v.safeParse(widgetsEditSchema(true), { widgets: JSON.stringify([widget]), @@ -81,6 +105,17 @@ describe("widgetsEditSchema", () => { settings: { controller: "s1-pro-con", motionSens: -25, stickSens: 5 }, }, }, + { + why: "an empty weapon pool widget list", + widget: { id: "weapon-pool", settings: { weaponPool: [] } }, + }, + { + why: "a weapon pool widget list with a favorite", + widget: { + id: "weapon-pool", + settings: { weaponPool: [{ id: 40, isFavorite: true }] }, + }, + }, ])("accepts $why", ({ widget }) => { const result = v.safeParse(widgetsEditSchema(true), { widgets: JSON.stringify([widget]), diff --git a/changelog/2026-09-08-weapon-pool-widget-custom-list.md b/changelog/2026-09-08-weapon-pool-widget-custom-list.md new file mode 100644 index 000000000..01d2b9826 --- /dev/null +++ b/changelog/2026-09-08-weapon-pool-widget-custom-list.md @@ -0,0 +1,5 @@ +--- +navItem: u +type: feature +--- +Weapon pool widget: choose the weapons to show (up to 7), or leave it empty to keep showing your match profile pool diff --git a/e2e/pages/user/user-edit-widgets-page.ts b/e2e/pages/user/user-edit-widgets-page.ts index d4dc702b9..3c2725167 100644 --- a/e2e/pages/user/user-edit-widgets-page.ts +++ b/e2e/pages/user/user-edit-widgets-page.ts @@ -52,6 +52,16 @@ export class UserEditWidgetsPage { await this.locators.badgesSelector.selectOption(String(badgeId)); } + /** Adds one weapon to the weapon pool widget's settings, expanded after opening them. */ + async selectWeaponPoolWeapon(weaponName: string) { + await this.page.getByTestId("weapon-select").click(); + await this.page.getByPlaceholder("Search weapons...").fill(weaponName); + await this.page + .getByRole("listbox") + .getByTestId(`weapon-select-option-${weaponName}`) + .click(); + } + /** Fills the bio widget's settings, expanded after adding it or opening them. */ async fillBio(text: string) { await this.page.getByLabel("Bio").fill(text); diff --git a/e2e/user-page.spec.ts b/e2e/user-page.spec.ts index d0488fec6..402b814e6 100644 --- a/e2e/user-page.spec.ts +++ b/e2e/user-page.spec.ts @@ -215,6 +215,33 @@ test.describe("User page", () => { } }); + test("shows the weapon pool widget's own list over the match profile pool", async ({ + page, + factories, + }) => { + await factories.UserFactory.grant(ADMIN_ID, { + matchProfile: { + weaponPool: [{ id: 1100, isFavorite: false }], + }, + }); + + await impersonate(page); + + const editWidgetsPage = new UserEditWidgetsPage(page); + await editWidgetsPage.goto(ADMIN_DISCORD_ID); + await editWidgetsPage.openWidgetSettings("weapon-pool"); + await editWidgetsPage.selectWeaponPoolWeapon("Luna Blaster"); + await editWidgetsPage.selectWeaponPoolWeapon("Splattershot"); + await editWidgetsPage.save(); + + const userPage = new UserPage(page); + await userPage.goto(ADMIN_DISCORD_ID); + + await expect(userPage.weaponPoolImage(200, 1)).toBeVisible(); + await expect(userPage.weaponPoolImage(40, 2)).toBeVisible(); + await isNotVisible(userPage.weaponPoolImage(1100, 1)); + }); + test("chooses result highlights which the results list then shows by default", async ({ page, factories, diff --git a/locales/da/forms.json b/locales/da/forms.json index 4b1397cd6..4df643ff3 100644 --- a/locales/da/forms.json +++ b/locales/da/forms.json @@ -75,6 +75,7 @@ "errors.noOnlySpecialCharacters": "", "errors.customRoleRequired": "", "labels.weaponPool": "Våbenpulje", + "bottomTexts.weaponPoolWidget": "", "placeholders.chatMessage": "", "placeholders.weaponPoolFull": "", "placeholders.vodStartTimestamp": "", diff --git a/locales/de/forms.json b/locales/de/forms.json index 8f30a4b17..c9e233edd 100644 --- a/locales/de/forms.json +++ b/locales/de/forms.json @@ -75,6 +75,7 @@ "errors.noOnlySpecialCharacters": "", "errors.customRoleRequired": "", "labels.weaponPool": "Waffenpool", + "bottomTexts.weaponPoolWidget": "", "placeholders.chatMessage": "", "placeholders.weaponPoolFull": "", "placeholders.vodStartTimestamp": "", diff --git a/locales/en/forms.json b/locales/en/forms.json index 5d1f871e2..8f4ec317b 100644 --- a/locales/en/forms.json +++ b/locales/en/forms.json @@ -75,6 +75,7 @@ "errors.noOnlySpecialCharacters": "Name can't be only special characters", "errors.customRoleRequired": "Enter a name for the custom role", "labels.weaponPool": "Weapon pool", + "bottomTexts.weaponPoolWidget": "Leave empty to show your match profile weapon pool", "placeholders.chatMessage": "Press enter to send", "placeholders.weaponPoolFull": "Pool full - remove a weapon to add more", "placeholders.vodStartTimestamp": "10:22", diff --git a/locales/en/user.json b/locales/en/user.json index acdf34157..80a0874a2 100644 --- a/locales/en/user.json +++ b/locales/en/user.json @@ -127,7 +127,7 @@ "widgets.description.top-500-weapons-splatanas": "Show splatanas you've reached top 500 with", "widgets.description.x-rank-peaks": "Show your peak X Rank placement for each mode, optionally select division", "widgets.description.builds": "Display your 3 most recent builds", - "widgets.description.weapon-pool": "Display your match profile weapon pool (edit on settings page)", + "widgets.description.weapon-pool": "Display your match profile weapon pool or a custom list of weapons", "widgets.description.sens": "Show your sensitivity settings and controller of choice", "widgets.description.art": "Display your 3 most recent art pieces", "widgets.description.commissions": "Show your commission status and details", diff --git a/locales/es-ES/forms.json b/locales/es-ES/forms.json index a13fa0a8f..e6844c341 100644 --- a/locales/es-ES/forms.json +++ b/locales/es-ES/forms.json @@ -75,6 +75,7 @@ "errors.noOnlySpecialCharacters": "El nombre no puede ser solo caracteres especiales", "errors.customRoleRequired": "Introduce un nombre para el rol personalizado", "labels.weaponPool": "Selección de armas", + "bottomTexts.weaponPoolWidget": "", "placeholders.chatMessage": "Presiona Enter para enviar", "placeholders.weaponPoolFull": "Selección llena - elimina un arma para añadir más", "placeholders.vodStartTimestamp": "10:22", diff --git a/locales/es-US/forms.json b/locales/es-US/forms.json index 142254478..a1c5651fa 100644 --- a/locales/es-US/forms.json +++ b/locales/es-US/forms.json @@ -75,6 +75,7 @@ "errors.noOnlySpecialCharacters": "El nombre no puede ser solo caracteres especiales", "errors.customRoleRequired": "Introduce un nombre para el rol personalizado", "labels.weaponPool": "Grupo de armas", + "bottomTexts.weaponPoolWidget": "", "placeholders.chatMessage": "Presionar 'enter' para enviar", "placeholders.weaponPoolFull": "Selección llena - elimina un arma para añadir más", "placeholders.vodStartTimestamp": "10:22", diff --git a/locales/fr-CA/forms.json b/locales/fr-CA/forms.json index 48d3a3020..0bb62c774 100644 --- a/locales/fr-CA/forms.json +++ b/locales/fr-CA/forms.json @@ -75,6 +75,7 @@ "errors.noOnlySpecialCharacters": "", "errors.customRoleRequired": "", "labels.weaponPool": "Armes jouées", + "bottomTexts.weaponPoolWidget": "", "placeholders.chatMessage": "", "placeholders.weaponPoolFull": "", "placeholders.vodStartTimestamp": "", diff --git a/locales/fr-EU/forms.json b/locales/fr-EU/forms.json index 2f870a571..c31262794 100644 --- a/locales/fr-EU/forms.json +++ b/locales/fr-EU/forms.json @@ -75,6 +75,7 @@ "errors.noOnlySpecialCharacters": "", "errors.customRoleRequired": "", "labels.weaponPool": "Armes jouées", + "bottomTexts.weaponPoolWidget": "", "placeholders.chatMessage": "Appuyer sur entrer pour envoyer", "placeholders.weaponPoolFull": "", "placeholders.vodStartTimestamp": "", diff --git a/locales/he/forms.json b/locales/he/forms.json index 7163342bd..251c243c0 100644 --- a/locales/he/forms.json +++ b/locales/he/forms.json @@ -75,6 +75,7 @@ "errors.noOnlySpecialCharacters": "", "errors.customRoleRequired": "", "labels.weaponPool": "מאגר נשקים", + "bottomTexts.weaponPoolWidget": "", "placeholders.chatMessage": "", "placeholders.weaponPoolFull": "", "placeholders.vodStartTimestamp": "", diff --git a/locales/it/forms.json b/locales/it/forms.json index 84d2a4f5a..4cc94fe7e 100644 --- a/locales/it/forms.json +++ b/locales/it/forms.json @@ -75,6 +75,7 @@ "errors.noOnlySpecialCharacters": "", "errors.customRoleRequired": "", "labels.weaponPool": "Pool armi", + "bottomTexts.weaponPoolWidget": "", "placeholders.chatMessage": "Premi Invio per inviare", "placeholders.weaponPoolFull": "", "placeholders.vodStartTimestamp": "", diff --git a/locales/ja/forms.json b/locales/ja/forms.json index 7d4c26707..130a2dfb2 100644 --- a/locales/ja/forms.json +++ b/locales/ja/forms.json @@ -75,6 +75,7 @@ "errors.noOnlySpecialCharacters": "", "errors.customRoleRequired": "", "labels.weaponPool": "使用ブキ", + "bottomTexts.weaponPoolWidget": "", "placeholders.chatMessage": "送信するには enter を押してください", "placeholders.weaponPoolFull": "", "placeholders.vodStartTimestamp": "", diff --git a/locales/ko/forms.json b/locales/ko/forms.json index e0c6470c4..9713f5bfb 100644 --- a/locales/ko/forms.json +++ b/locales/ko/forms.json @@ -75,6 +75,7 @@ "errors.noOnlySpecialCharacters": "", "errors.customRoleRequired": "", "labels.weaponPool": "", + "bottomTexts.weaponPoolWidget": "", "placeholders.chatMessage": "", "placeholders.weaponPoolFull": "", "placeholders.vodStartTimestamp": "", diff --git a/locales/nl/forms.json b/locales/nl/forms.json index 4f8490cca..5f80798cf 100644 --- a/locales/nl/forms.json +++ b/locales/nl/forms.json @@ -75,6 +75,7 @@ "errors.noOnlySpecialCharacters": "", "errors.customRoleRequired": "", "labels.weaponPool": "", + "bottomTexts.weaponPoolWidget": "", "placeholders.chatMessage": "", "placeholders.weaponPoolFull": "", "placeholders.vodStartTimestamp": "", diff --git a/locales/pl/forms.json b/locales/pl/forms.json index 1935eec4c..14da8d479 100644 --- a/locales/pl/forms.json +++ b/locales/pl/forms.json @@ -75,6 +75,7 @@ "errors.noOnlySpecialCharacters": "", "errors.customRoleRequired": "", "labels.weaponPool": "Pula broni", + "bottomTexts.weaponPoolWidget": "", "placeholders.chatMessage": "", "placeholders.weaponPoolFull": "", "placeholders.vodStartTimestamp": "", diff --git a/locales/pt-BR/forms.json b/locales/pt-BR/forms.json index b66ef97eb..3af6da436 100644 --- a/locales/pt-BR/forms.json +++ b/locales/pt-BR/forms.json @@ -75,6 +75,7 @@ "errors.noOnlySpecialCharacters": "", "errors.customRoleRequired": "", "labels.weaponPool": "Seleção de armas", + "bottomTexts.weaponPoolWidget": "", "placeholders.chatMessage": "Aperte enter para enviar", "placeholders.weaponPoolFull": "", "placeholders.vodStartTimestamp": "", diff --git a/locales/ru/forms.json b/locales/ru/forms.json index e563e9cbf..3c0bc1a5a 100644 --- a/locales/ru/forms.json +++ b/locales/ru/forms.json @@ -75,6 +75,7 @@ "errors.noOnlySpecialCharacters": "", "errors.customRoleRequired": "", "labels.weaponPool": "Используемое оружие", + "bottomTexts.weaponPoolWidget": "", "placeholders.chatMessage": "Нажмите enter, чтобы отправить", "placeholders.weaponPoolFull": "", "placeholders.vodStartTimestamp": "", diff --git a/locales/zh/forms.json b/locales/zh/forms.json index d9b79d9a4..73625d528 100644 --- a/locales/zh/forms.json +++ b/locales/zh/forms.json @@ -75,6 +75,7 @@ "errors.noOnlySpecialCharacters": "名称不能仅由特殊字符组成", "errors.customRoleRequired": "请输入自定义职责的名称", "labels.weaponPool": "武器池", + "bottomTexts.weaponPoolWidget": "", "placeholders.chatMessage": "按回车键发送", "placeholders.weaponPoolFull": "武器池已满。请移除一个武器以添加新武器", "placeholders.vodStartTimestamp": "", diff --git a/migrations/20260908162417-weapon-pool-widget-settings.ts b/migrations/20260908162417-weapon-pool-widget-settings.ts new file mode 100644 index 000000000..24cbac277 --- /dev/null +++ b/migrations/20260908162417-weapon-pool-widget-settings.ts @@ -0,0 +1,18 @@ +import { type Kysely, sql } from "kysely"; + +/** + * The weapon pool widget gained its own optional weapon list. Rows saved before + * that have no settings, so they get an empty list which keeps showing the match + * profile pool as before. + */ +export async function up(db: Kysely): Promise { + await sql` + update "UserWidget" + set "widget" = json_object( + 'id', 'weapon-pool', + 'settings', json_object('weaponPool', json('[]')) + ) + where json_extract("widget", '$.id') = 'weapon-pool' + and json_extract("widget", '$.settings') is null + `.execute(db); +} diff --git a/scripts/benchmark-db/cases.ts b/scripts/benchmark-db/cases.ts index 0934723a0..a4b063c24 100644 --- a/scripts/benchmark-db/cases.ts +++ b/scripts/benchmark-db/cases.ts @@ -880,6 +880,12 @@ export function buildCases(fx: Fixtures): { add("XRankPlacementRepository.isPlayerLinkedByUserId", fx.xrank, (xrank) => XRankPlacementRepository.isPlayerLinkedByUserId(xrank.userId), ); + add( + "XRankPlacementRepository.findTenStarWeaponSplIdsByUserId", + fx.xrank, + (xrank) => + XRankPlacementRepository.findTenStarWeaponSplIdsByUserId(xrank.userId), + ); add( "XRankPlacementRepository.findPeakVerifiedXpByUserId", fx.xrank,