From 9138b67eaea31fdd0fa334ff921285efc8d07ae3 Mon Sep 17 00:00:00 2001 From: Ben Hollis Date: Thu, 20 Jul 2023 11:06:54 -0700 Subject: [PATCH 01/12] Start on choosing ingame identifiers --- .../loadout-drawer/LoadoutDrawerHeader.tsx | 5 ++++ .../loadout-drawer/loadout-type-converters.ts | 25 +++++++++++++------ src/app/loadout/ingame/EditInGameLoadout.tsx | 9 ++++--- src/app/loadout/ingame/InGameLoadoutIcon.tsx | 17 ++++++++++++- .../ingame/InGameLoadoutIconSelectButton.tsx | 15 +++++++++++ 5 files changed, 60 insertions(+), 11 deletions(-) create mode 100644 src/app/loadout/ingame/InGameLoadoutIconSelectButton.tsx diff --git a/src/app/loadout-drawer/LoadoutDrawerHeader.tsx b/src/app/loadout-drawer/LoadoutDrawerHeader.tsx index cd11251546..916d00e3ef 100644 --- a/src/app/loadout-drawer/LoadoutDrawerHeader.tsx +++ b/src/app/loadout-drawer/LoadoutDrawerHeader.tsx @@ -2,6 +2,7 @@ import ClassIcon from 'app/dim-ui/ClassIcon'; import { WithSymbolsPicker } from 'app/dim-ui/destiny-symbols/SymbolsPicker'; import { useAutocomplete } from 'app/dim-ui/text-complete/text-complete'; import { t } from 'app/i18next-t'; +import InGameLoadoutIconSelectButton from 'app/loadout/ingame/InGameLoadoutIconSelectButton'; import React, { useRef } from 'react'; import { useSelector } from 'react-redux'; import styles from './LoadoutDrawerHeader.m.scss'; @@ -21,8 +22,12 @@ export default function LoadoutDrawerHeader({ const tags = useSelector(loadoutsHashtagsSelector); useAutocomplete(inputRef, tags); + // TODO: Only show the ingame loadout icon when the loadout has some ingame-loadout-compatible items in it? + // TODO: preview/warn that an incomplete loadout will inherit the current items for any missing slots + return (
+ { const destSlotLoadout = loadouts.find((l) => l.index === newSlotNum); diff --git a/src/app/loadout/ingame/InGameLoadoutIcon.tsx b/src/app/loadout/ingame/InGameLoadoutIcon.tsx index d12040714a..d255fc8e6a 100644 --- a/src/app/loadout/ingame/InGameLoadoutIcon.tsx +++ b/src/app/loadout/ingame/InGameLoadoutIcon.tsx @@ -1,5 +1,8 @@ +import { InGameLoadoutIdentifiers } from '@destinyitemmanager/dim-api-types'; import BungieImage, { bungieBackgroundStyle } from 'app/dim-ui/BungieImage'; +import { resolveInGameLoadoutIdentifiers } from 'app/loadout-drawer/loadout-type-converters'; import { InGameLoadout } from 'app/loadout-drawer/loadout-types'; +import { useD2Definitions } from 'app/manifest/selectors'; import clsx from 'clsx'; import styles from './InGameLoadoutIcon.m.scss'; @@ -8,7 +11,7 @@ export default function InGameLoadoutIcon({ className, size = 32, }: { - loadout: InGameLoadout; + loadout: Pick; className?: string; size?: number; }) { @@ -41,3 +44,15 @@ export function InGameLoadoutIconWithIndex({
); } + +export function InGameLoadoutIconFromIdentifiers({ + identifiers, + className, +}: { + identifiers: InGameLoadoutIdentifiers; + className?: string; +}) { + const defs = useD2Definitions()!; + const resolvedIdentifiers = resolveInGameLoadoutIdentifiers(defs, identifiers); + return ; +} diff --git a/src/app/loadout/ingame/InGameLoadoutIconSelectButton.tsx b/src/app/loadout/ingame/InGameLoadoutIconSelectButton.tsx new file mode 100644 index 0000000000..97c1b21fed --- /dev/null +++ b/src/app/loadout/ingame/InGameLoadoutIconSelectButton.tsx @@ -0,0 +1,15 @@ +import ClosableContainer from 'app/dim-ui/ClosableContainer'; +import { Loadout } from 'app/loadout-drawer/loadout-types'; +import { InGameLoadoutIconFromIdentifiers } from './InGameLoadoutIcon'; + +export default function InGameLoadoutIconSelectButton({ loadout }: { loadout: Loadout }) { + const identifiers = loadout.parameters?.inGameIdentifiers; + + let content =
Noooo
; + + if (identifiers) { + content = ; + } + + return console.log('close')}>{content}; +} From f0e070931d073650bf6f0d50e0a5163dde307f8f Mon Sep 17 00:00:00 2001 From: Ben Hollis Date: Tue, 22 Aug 2023 22:33:00 -0700 Subject: [PATCH 02/12] Edit loadout identifiers --- config/i18n.json | 1 + src/app/dim-ui/Sheet.tsx | 2 +- src/app/loadout-drawer/LoadoutDrawer.m.scss | 19 +++ .../loadout-drawer/LoadoutDrawer.m.scss.d.ts | 1 + src/app/loadout-drawer/LoadoutDrawer.tsx | 4 +- .../loadout-drawer/LoadoutDrawerHeader.tsx | 2 - .../loadout-drawer/loadout-drawer-reducer.ts | 9 +- .../loadout/ingame/EditInGameLoadout.m.scss | 50 +------- .../ingame/EditInGameLoadout.m.scss.d.ts | 6 - src/app/loadout/ingame/EditInGameLoadout.tsx | 120 +++--------------- .../EditInGameLoadoutIdentifiers.m.scss | 12 ++ .../EditInGameLoadoutIdentifiers.m.scss.d.ts | 8 ++ .../ingame/EditInGameLoadoutIdentifiers.tsx | 64 ++++++++++ src/app/loadout/ingame/InGameLoadoutIcon.tsx | 4 +- .../ingame/InGameLoadoutIconSelectButton.tsx | 15 --- ...nGameLoadoutIdentifiersSelectButton.m.scss | 10 ++ ...LoadoutIdentifiersSelectButton.m.scss.d.ts | 7 + .../InGameLoadoutIdentifiersSelectButton.tsx | 59 +++++++++ src/app/loadout/ingame/RadioButton.m.scss | 30 +++++ .../loadout/ingame/RadioButton.m.scss.d.ts | 10 ++ src/app/loadout/ingame/RadioButton.tsx | 44 +++++++ .../SelectInGameLoadoutIdentifiers.m.scss | 18 +++ ...SelectInGameLoadoutIdentifiers.m.scss.d.ts | 8 ++ .../ingame/SelectInGameLoadoutIdentifiers.tsx | 100 +++++++++++++++ src/images/no-loadout-identifiers.svg | 1 + src/locale/en.json | 1 + 26 files changed, 430 insertions(+), 175 deletions(-) create mode 100644 src/app/loadout/ingame/EditInGameLoadoutIdentifiers.m.scss create mode 100644 src/app/loadout/ingame/EditInGameLoadoutIdentifiers.m.scss.d.ts create mode 100644 src/app/loadout/ingame/EditInGameLoadoutIdentifiers.tsx delete mode 100644 src/app/loadout/ingame/InGameLoadoutIconSelectButton.tsx create mode 100644 src/app/loadout/ingame/InGameLoadoutIdentifiersSelectButton.m.scss create mode 100644 src/app/loadout/ingame/InGameLoadoutIdentifiersSelectButton.m.scss.d.ts create mode 100644 src/app/loadout/ingame/InGameLoadoutIdentifiersSelectButton.tsx create mode 100644 src/app/loadout/ingame/RadioButton.m.scss create mode 100644 src/app/loadout/ingame/RadioButton.m.scss.d.ts create mode 100644 src/app/loadout/ingame/RadioButton.tsx create mode 100644 src/app/loadout/ingame/SelectInGameLoadoutIdentifiers.m.scss create mode 100644 src/app/loadout/ingame/SelectInGameLoadoutIdentifiers.m.scss.d.ts create mode 100644 src/app/loadout/ingame/SelectInGameLoadoutIdentifiers.tsx create mode 100644 src/images/no-loadout-identifiers.svg diff --git a/config/i18n.json b/config/i18n.json index ec88769260..3facb0f7be 100644 --- a/config/i18n.json +++ b/config/i18n.json @@ -430,6 +430,7 @@ "DeletedBody": "Cleared the in-game loadout at slot {{index}}", "DeleteFailed": "Failed to delete loadout", "Save": "Update Loadout", + "SaveIdentifiers": "Update Identifiers", "SaveToDimLoadout": "Save as DIM Loadout", "Replace": "Replace Loadout {{index}}", "SnapshotFailed": "Failed to snapshot equipped loadout" diff --git a/src/app/dim-ui/Sheet.tsx b/src/app/dim-ui/Sheet.tsx index b7ab5fbc57..cbae6e8e4f 100644 --- a/src/app/dim-ui/Sheet.tsx +++ b/src/app/dim-ui/Sheet.tsx @@ -41,7 +41,7 @@ const SheetDisabledContext = createContext<(shown: boolean) => void>(() => { * takes an "onClose" function that can be used to close the sheet. Using onClose to close * the sheet ensures that it will animate away rather than simply disappearing. */ -type SheetContent = React.ReactNode | ((args: { onClose: () => void }) => React.ReactNode); +export type SheetContent = React.ReactNode | ((args: { onClose: () => void }) => React.ReactNode); interface Props { /** A static, non-scrollable header shown in line with the close button. */ diff --git a/src/app/loadout-drawer/LoadoutDrawer.m.scss b/src/app/loadout-drawer/LoadoutDrawer.m.scss index b9f749bba6..bcec6f6305 100644 --- a/src/app/loadout-drawer/LoadoutDrawer.m.scss +++ b/src/app/loadout-drawer/LoadoutDrawer.m.scss @@ -29,3 +29,22 @@ align-items: center; gap: 4px; } + +.header { + display: grid; + grid-template-columns: min-content 1fr; + grid-template-areas: + 'identifiers title' + 'identifiers notes'; + gap: 0 12px; + + > *:nth-child(1) { + grid-area: identifiers; + } + > *:nth-child(2) { + grid-area: title; + } + > *:nth-child(3) { + grid-area: notes; + } +} diff --git a/src/app/loadout-drawer/LoadoutDrawer.m.scss.d.ts b/src/app/loadout-drawer/LoadoutDrawer.m.scss.d.ts index 39c5178d7a..5447f4fd60 100644 --- a/src/app/loadout-drawer/LoadoutDrawer.m.scss.d.ts +++ b/src/app/loadout-drawer/LoadoutDrawer.m.scss.d.ts @@ -2,6 +2,7 @@ // Please do not change this file! interface CssExports { 'body': string; + 'header': string; 'inputGroup': string; 'notes': string; } diff --git a/src/app/loadout-drawer/LoadoutDrawer.tsx b/src/app/loadout-drawer/LoadoutDrawer.tsx index 4e51c40107..002f3dc42e 100644 --- a/src/app/loadout-drawer/LoadoutDrawer.tsx +++ b/src/app/loadout-drawer/LoadoutDrawer.tsx @@ -7,6 +7,7 @@ import { WithSymbolsPicker } from 'app/dim-ui/destiny-symbols/SymbolsPicker'; import { useAutocomplete } from 'app/dim-ui/text-complete/text-complete'; import { t } from 'app/i18next-t'; import { getStore } from 'app/inventory/stores-helpers'; +import InGameLoadoutIdentifiersSelectButton from 'app/loadout/ingame/InGameLoadoutIdentifiersSelectButton'; import { useDefinitions } from 'app/manifest/selectors'; import { searchFilterSelector } from 'app/search/search-filter'; import { AppIcon, addIcon, faRandom } from 'app/shell/icons'; @@ -181,7 +182,8 @@ export default function LoadoutDrawer({ setLoadout(setClassType(checked ? DestinyClass.Unknown : store.classType)); const header = ( -
+
+
{t('MovePopup.Notes')} diff --git a/src/app/loadout-drawer/LoadoutDrawerHeader.tsx b/src/app/loadout-drawer/LoadoutDrawerHeader.tsx index 916d00e3ef..b4277ec345 100644 --- a/src/app/loadout-drawer/LoadoutDrawerHeader.tsx +++ b/src/app/loadout-drawer/LoadoutDrawerHeader.tsx @@ -2,7 +2,6 @@ import ClassIcon from 'app/dim-ui/ClassIcon'; import { WithSymbolsPicker } from 'app/dim-ui/destiny-symbols/SymbolsPicker'; import { useAutocomplete } from 'app/dim-ui/text-complete/text-complete'; import { t } from 'app/i18next-t'; -import InGameLoadoutIconSelectButton from 'app/loadout/ingame/InGameLoadoutIconSelectButton'; import React, { useRef } from 'react'; import { useSelector } from 'react-redux'; import styles from './LoadoutDrawerHeader.m.scss'; @@ -27,7 +26,6 @@ export default function LoadoutDrawerHeader({ return (
- img { - margin: 0 !important; - height: 48px; - width: 48px; - } - &:hover, - &:active, - &.checked { - background-color: var(--theme-accent-primary); - color: var(--theme-text-invert); - > img { - filter: none !important; - } - } - &.imageButton { - padding: 2px; - } - &.hasLoadout { - &:hover, - &:active, - &.checked { - color: var(--theme-text) !important; - } - } -} - -.row { +.slots { display: flex; flex-flow: row wrap; gap: 4px; -} - -.slots { - composes: row; margin-bottom: 8px; - - > .button { - padding: 2px; - } } .emptySlot { diff --git a/src/app/loadout/ingame/EditInGameLoadout.m.scss.d.ts b/src/app/loadout/ingame/EditInGameLoadout.m.scss.d.ts index 95c62d5e58..f028d1bab6 100644 --- a/src/app/loadout/ingame/EditInGameLoadout.m.scss.d.ts +++ b/src/app/loadout/ingame/EditInGameLoadout.m.scss.d.ts @@ -1,15 +1,9 @@ // This file is automatically generated. // Please do not change this file! interface CssExports { - 'button': string; - 'checked': string; 'content': string; 'emptySlot': string; - 'hasLoadout': string; 'header': string; - 'imageButton': string; - 'preview': string; - 'row': string; 'slotNum': string; 'slots': string; } diff --git a/src/app/loadout/ingame/EditInGameLoadout.tsx b/src/app/loadout/ingame/EditInGameLoadout.tsx index 8f6d4e45df..f4b16c2402 100644 --- a/src/app/loadout/ingame/EditInGameLoadout.tsx +++ b/src/app/loadout/ingame/EditInGameLoadout.tsx @@ -1,17 +1,18 @@ -import BungieImage, { bungieBackgroundStyle } from 'app/dim-ui/BungieImage'; import Sheet from 'app/dim-ui/Sheet'; import { resolveInGameLoadoutIdentifiers } from 'app/loadout-drawer/loadout-type-converters'; import { InGameLoadout } from 'app/loadout-drawer/loadout-types'; import { useD2Definitions } from 'app/manifest/selectors'; import { useThunkDispatch } from 'app/store/thunk-dispatch'; import { RootState } from 'app/store/types'; -import { compareBy } from 'app/utils/comparators'; -import clsx from 'clsx'; import { t } from 'i18next'; import { useState } from 'react'; import { useSelector } from 'react-redux'; import styles from './EditInGameLoadout.m.scss'; import InGameLoadoutIcon from './InGameLoadoutIcon'; +import { RadioButton } from './RadioButton'; +import SelectInGameLoadoutIdentifiers, { + useIdentifierValues, +} from './SelectInGameLoadoutIdentifiers'; import { editInGameLoadout, snapshotInGameLoadout } from './ingame-loadout-apply'; import { availableLoadoutSlotsSelector, inGameLoadoutsForCharacterSelector } from './selectors'; @@ -31,15 +32,7 @@ export default function EditInGameLoadout({ const defs = useD2Definitions()!; const dispatch = useThunkDispatch(); - const names = Object.values(defs.LoadoutName.getAll()) - .filter((i) => !i.redacted) - .sort(compareBy((n) => n.index)); - const colors = Object.values(defs.LoadoutColor.getAll()) - .filter((i) => !i.redacted) - .sort(compareBy((n) => n.index)); - const icons = Object.values(defs.LoadoutIcon.getAll()) - .filter((i) => !i.redacted) - .sort(compareBy((n) => n.index)); + const [names, colors, icons] = useIdentifierValues(defs); const defaultName = names[0].hash; const defaultColor = colors[0].hash; const defaultIcon = icons[0].hash; @@ -70,12 +63,6 @@ export default function EditInGameLoadout({ loadout?.iconHash ?? overwrittenLoadout?.iconHash ?? defaultIcon ); - const { name, colorIcon, icon } = resolveInGameLoadoutIdentifiers(defs, { - nameHash, - colorHash, - iconHash, - }); - const handleSetSlot = (newSlotNum: number) => { const destSlotLoadout = loadouts.find((l) => l.index === newSlotNum); @@ -100,6 +87,12 @@ export default function EditInGameLoadout({ const handleSave = async (e: React.FormEvent) => { e.preventDefault(); try { + const { name, colorIcon, icon } = resolveInGameLoadoutIdentifiers(defs, { + nameHash, + colorHash, + iconHash, + }); + if (creating) { await dispatch( snapshotInGameLoadout({ @@ -159,7 +152,8 @@ export default function EditInGameLoadout({ option={i} value={slot} onSelected={handleSetSlot} - className={loadout ? styles.hasLoadout : undefined} + hasLoadout={Boolean(loadout)} + spaced > {loadout ? ( @@ -172,87 +166,15 @@ export default function EditInGameLoadout({ })}
)} -
- {' '} - {name} -
-
- {names.map((name) => ( - - {name.name} - - ))} -
-
- {icons.map((icon) => ( - - - - ))} -
-
- {colors.map((color) => ( - - - - ))} -
+
); } - -function RadioButton({ - option: hash, - name, - value, - onSelected, - children, - className, -}: { - option: number; - name: string; - value: number; - onSelected: (value: number) => void; - children: React.ReactNode; - className?: string; -}) { - return ( - - ); -} diff --git a/src/app/loadout/ingame/EditInGameLoadoutIdentifiers.m.scss b/src/app/loadout/ingame/EditInGameLoadoutIdentifiers.m.scss new file mode 100644 index 0000000000..5879deba4d --- /dev/null +++ b/src/app/loadout/ingame/EditInGameLoadoutIdentifiers.m.scss @@ -0,0 +1,12 @@ +@use '../../variables.scss' as *; + +.content { + display: flex; + flex-direction: column; + row-gap: 8px; + padding: 10px; +} + +.header { + margin: 0; +} diff --git a/src/app/loadout/ingame/EditInGameLoadoutIdentifiers.m.scss.d.ts b/src/app/loadout/ingame/EditInGameLoadoutIdentifiers.m.scss.d.ts new file mode 100644 index 0000000000..8c08efcfd8 --- /dev/null +++ b/src/app/loadout/ingame/EditInGameLoadoutIdentifiers.m.scss.d.ts @@ -0,0 +1,8 @@ +// This file is automatically generated. +// Please do not change this file! +interface CssExports { + 'content': string; + 'header': string; +} +export const cssExports: CssExports; +export default cssExports; diff --git a/src/app/loadout/ingame/EditInGameLoadoutIdentifiers.tsx b/src/app/loadout/ingame/EditInGameLoadoutIdentifiers.tsx new file mode 100644 index 0000000000..222f8339e8 --- /dev/null +++ b/src/app/loadout/ingame/EditInGameLoadoutIdentifiers.tsx @@ -0,0 +1,64 @@ +import { InGameLoadoutIdentifiers } from '@destinyitemmanager/dim-api-types'; +import Sheet, { SheetContent } from 'app/dim-ui/Sheet'; +import { useD2Definitions } from 'app/manifest/selectors'; +import { t } from 'i18next'; +import { useState } from 'react'; +import styles from './EditInGameLoadoutIdentifiers.m.scss'; +import SelectInGameLoadoutIdentifiers, { + useIdentifierValues, +} from './SelectInGameLoadoutIdentifiers'; + +/** An editor sheet for just editing the name/color/icon */ +export default function EditInGameLoadoutIdentifiers({ + identifiers, + onSave, + onClose, +}: { + identifiers: InGameLoadoutIdentifiers | undefined; + onSave: (updated: InGameLoadoutIdentifiers) => void; + onClose: () => void; +}) { + const defs = useD2Definitions()!; + + const [names, colors, icons] = useIdentifierValues(defs); + const defaultName = names[0].hash; + const defaultColor = colors[0].hash; + const defaultIcon = icons[0].hash; + + const [nameHash, setNameHash] = useState(identifiers?.nameHash ?? defaultName); + const [colorHash, setColorHash] = useState(identifiers?.colorHash ?? defaultColor); + const [iconHash, setIconHash] = useState(identifiers?.iconHash ?? defaultIcon); + + const handleSave = (onClose: () => void) => (e: React.FormEvent) => { + e.preventDefault(); + onSave({ nameHash, colorHash, iconHash }); + onClose(); + }; + + const footer: SheetContent = ({ onClose }) => ( +
+ +
+ ); + + return ( + {t('InGameLoadout.EditIdentifiers')}} + > +
+ +
+
+ ); +} diff --git a/src/app/loadout/ingame/InGameLoadoutIcon.tsx b/src/app/loadout/ingame/InGameLoadoutIcon.tsx index d255fc8e6a..718b21933c 100644 --- a/src/app/loadout/ingame/InGameLoadoutIcon.tsx +++ b/src/app/loadout/ingame/InGameLoadoutIcon.tsx @@ -48,11 +48,13 @@ export function InGameLoadoutIconWithIndex({ export function InGameLoadoutIconFromIdentifiers({ identifiers, className, + size = 32, }: { identifiers: InGameLoadoutIdentifiers; className?: string; + size?: number; }) { const defs = useD2Definitions()!; const resolvedIdentifiers = resolveInGameLoadoutIdentifiers(defs, identifiers); - return ; + return ; } diff --git a/src/app/loadout/ingame/InGameLoadoutIconSelectButton.tsx b/src/app/loadout/ingame/InGameLoadoutIconSelectButton.tsx deleted file mode 100644 index 97c1b21fed..0000000000 --- a/src/app/loadout/ingame/InGameLoadoutIconSelectButton.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import ClosableContainer from 'app/dim-ui/ClosableContainer'; -import { Loadout } from 'app/loadout-drawer/loadout-types'; -import { InGameLoadoutIconFromIdentifiers } from './InGameLoadoutIcon'; - -export default function InGameLoadoutIconSelectButton({ loadout }: { loadout: Loadout }) { - const identifiers = loadout.parameters?.inGameIdentifiers; - - let content =
Noooo
; - - if (identifiers) { - content = ; - } - - return console.log('close')}>{content}; -} diff --git a/src/app/loadout/ingame/InGameLoadoutIdentifiersSelectButton.m.scss b/src/app/loadout/ingame/InGameLoadoutIdentifiersSelectButton.m.scss new file mode 100644 index 0000000000..45b304f840 --- /dev/null +++ b/src/app/loadout/ingame/InGameLoadoutIdentifiersSelectButton.m.scss @@ -0,0 +1,10 @@ +.button { + composes: resetButton from '../../dim-ui/common.m.scss'; + width: 52px; + height: 52px; + padding: 2px; + &:hover, + &:focus-visible { + background-color: var(--theme-accent-primary); + } +} diff --git a/src/app/loadout/ingame/InGameLoadoutIdentifiersSelectButton.m.scss.d.ts b/src/app/loadout/ingame/InGameLoadoutIdentifiersSelectButton.m.scss.d.ts new file mode 100644 index 0000000000..9ff9a69ba2 --- /dev/null +++ b/src/app/loadout/ingame/InGameLoadoutIdentifiersSelectButton.m.scss.d.ts @@ -0,0 +1,7 @@ +// This file is automatically generated. +// Please do not change this file! +interface CssExports { + 'button': string; +} +export const cssExports: CssExports; +export default cssExports; diff --git a/src/app/loadout/ingame/InGameLoadoutIdentifiersSelectButton.tsx b/src/app/loadout/ingame/InGameLoadoutIdentifiersSelectButton.tsx new file mode 100644 index 0000000000..d500d56381 --- /dev/null +++ b/src/app/loadout/ingame/InGameLoadoutIdentifiersSelectButton.tsx @@ -0,0 +1,59 @@ +import { InGameLoadoutIdentifiers } from '@destinyitemmanager/dim-api-types'; +import ClosableContainer from 'app/dim-ui/ClosableContainer'; +import { t } from 'app/i18next-t'; +import { setInGameLoadoutIdentifiers } from 'app/loadout-drawer/loadout-drawer-reducer'; +import { Loadout } from 'app/loadout-drawer/loadout-types'; +import { Portal } from 'app/utils/temp-container'; +import noInGameLoadoutIdentifiers from 'images/no-loadout-identifiers.svg'; +import { useState } from 'react'; +import EditInGameLoadoutIdentifiers from './EditInGameLoadoutIdentifiers'; +import { InGameLoadoutIconFromIdentifiers } from './InGameLoadoutIcon'; +import styles from './InGameLoadoutIdentifiersSelectButton.m.scss'; + +export default function InGameLoadoutIdentifiersSelectButton({ + loadout, + setLoadout, +}: { + loadout: Loadout; + setLoadout: (f: (loadout: Loadout) => Loadout) => void; +}) { + const [sheetOpen, setSheetOpen] = useState(false); + const identifiers = loadout.parameters?.inGameIdentifiers; + + let content = ; + + const handleClearIdentifiers = () => setLoadout(setInGameLoadoutIdentifiers(undefined)); + const handleChooseIdentifiers = (identifiers: InGameLoadoutIdentifiers) => + setLoadout(setInGameLoadoutIdentifiers(identifiers)); + + if (identifiers) { + content = ; + } + + return ( + <> + + + + {sheetOpen && ( + + setSheetOpen(false)} + /> + + )} + + ); +} diff --git a/src/app/loadout/ingame/RadioButton.m.scss b/src/app/loadout/ingame/RadioButton.m.scss new file mode 100644 index 0000000000..5fa45a96c3 --- /dev/null +++ b/src/app/loadout/ingame/RadioButton.m.scss @@ -0,0 +1,30 @@ +@use '../../variables.scss' as *; + +.button { + composes: dim-button from global; + position: relative; + > img { + margin: 0 !important; + height: 48px; + width: 48px; + } + &:hover, + &:active, + &.checked { + background-color: var(--theme-accent-primary); + color: var(--theme-text-invert); + > img { + filter: none !important; + } + } + &.spaced { + padding: 2px; + } + &.hasLoadout { + &:hover, + &:active, + &.checked { + color: var(--theme-text) !important; + } + } +} diff --git a/src/app/loadout/ingame/RadioButton.m.scss.d.ts b/src/app/loadout/ingame/RadioButton.m.scss.d.ts new file mode 100644 index 0000000000..dfe18cb9f6 --- /dev/null +++ b/src/app/loadout/ingame/RadioButton.m.scss.d.ts @@ -0,0 +1,10 @@ +// This file is automatically generated. +// Please do not change this file! +interface CssExports { + 'button': string; + 'checked': string; + 'hasLoadout': string; + 'spaced': string; +} +export const cssExports: CssExports; +export default cssExports; diff --git a/src/app/loadout/ingame/RadioButton.tsx b/src/app/loadout/ingame/RadioButton.tsx new file mode 100644 index 0000000000..27adb0832a --- /dev/null +++ b/src/app/loadout/ingame/RadioButton.tsx @@ -0,0 +1,44 @@ +import clsx from 'clsx'; +import styles from './RadioButton.m.scss'; + +/** + * A radio button for the ingame loadout editor. + */ +export function RadioButton({ + option: hash, + name, + value, + onSelected, + children, + spaced, + hasLoadout, +}: { + option: number; + name: string; + value: number; + onSelected: (value: number) => void; + children: React.ReactNode; + spaced?: boolean; + hasLoadout?: boolean; +}) { + return ( + + ); +} diff --git a/src/app/loadout/ingame/SelectInGameLoadoutIdentifiers.m.scss b/src/app/loadout/ingame/SelectInGameLoadoutIdentifiers.m.scss new file mode 100644 index 0000000000..f8162ed37c --- /dev/null +++ b/src/app/loadout/ingame/SelectInGameLoadoutIdentifiers.m.scss @@ -0,0 +1,18 @@ +@use '../../variables.scss' as *; + +.preview { + display: flex; + flex-direction: row; + text-transform: uppercase; + letter-spacing: 2px; + font-size: 16px; + gap: 8px; + align-items: center; + margin-bottom: 8px; +} + +.row { + display: flex; + flex-flow: row wrap; + gap: 4px; +} diff --git a/src/app/loadout/ingame/SelectInGameLoadoutIdentifiers.m.scss.d.ts b/src/app/loadout/ingame/SelectInGameLoadoutIdentifiers.m.scss.d.ts new file mode 100644 index 0000000000..7ecf56ac22 --- /dev/null +++ b/src/app/loadout/ingame/SelectInGameLoadoutIdentifiers.m.scss.d.ts @@ -0,0 +1,8 @@ +// This file is automatically generated. +// Please do not change this file! +interface CssExports { + 'preview': string; + 'row': string; +} +export const cssExports: CssExports; +export default cssExports; diff --git a/src/app/loadout/ingame/SelectInGameLoadoutIdentifiers.tsx b/src/app/loadout/ingame/SelectInGameLoadoutIdentifiers.tsx new file mode 100644 index 0000000000..133d6c1a1f --- /dev/null +++ b/src/app/loadout/ingame/SelectInGameLoadoutIdentifiers.tsx @@ -0,0 +1,100 @@ +import { D2ManifestDefinitions } from 'app/destiny2/d2-definitions'; +import BungieImage, { bungieBackgroundStyle } from 'app/dim-ui/BungieImage'; +import { resolveInGameLoadoutIdentifiers } from 'app/loadout-drawer/loadout-type-converters'; +import { useD2Definitions } from 'app/manifest/selectors'; +import { compareBy } from 'app/utils/comparators'; +import { useMemo } from 'react'; +import { RadioButton } from './RadioButton'; +import styles from './SelectInGameLoadoutIdentifiers.m.scss'; + +/** Selection controls for choosing an ingame loadout identifier (name, color, + * icon). A controlled component. */ +export default function SelectInGameLoadoutIdentifiers({ + nameHash, + colorHash, + iconHash, + onNameHashChanged, + onColorHashChanged, + onIconHashChanged, +}: { + nameHash: number; + colorHash: number; + iconHash: number; + onNameHashChanged: (nameHash: number) => void; + onColorHashChanged: (colorHash: number) => void; + onIconHashChanged: (iconHash: number) => void; +}) { + const defs = useD2Definitions()!; + + const [names, colors, icons] = useIdentifierValues(defs); + const { name, colorIcon, icon } = resolveInGameLoadoutIdentifiers(defs, { + nameHash, + colorHash, + iconHash, + }); + + return ( + <> +
+ {' '} + {name} +
+
+ {names.map((name) => ( + + {name.name} + + ))} +
+
+ {icons.map((icon) => ( + + + + ))} +
+
+ {colors.map((color) => ( + + + + ))} +
+ + ); +} + +export function useIdentifierValues(defs: D2ManifestDefinitions) { + return useMemo(() => { + const names = Object.values(defs.LoadoutName.getAll()) + .filter((i) => !i.redacted) + .sort(compareBy((n) => n.index)); + const colors = Object.values(defs.LoadoutColor.getAll()) + .filter((i) => !i.redacted) + .sort(compareBy((n) => n.index)); + const icons = Object.values(defs.LoadoutIcon.getAll()) + .filter((i) => !i.redacted) + .sort(compareBy((n) => n.index)); + return [names, colors, icons] as const; + }, []); +} diff --git a/src/images/no-loadout-identifiers.svg b/src/images/no-loadout-identifiers.svg new file mode 100644 index 0000000000..bf3ff59fda --- /dev/null +++ b/src/images/no-loadout-identifiers.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/locale/en.json b/src/locale/en.json index f1c0269168..30bf98be48 100644 --- a/src/locale/en.json +++ b/src/locale/en.json @@ -419,6 +419,7 @@ "PrepareEquip": "Prepare Equip", "Replace": "Replace Loadout {{index}}", "Save": "Update Loadout", + "SaveIdentifiers": "Update Identifiers", "SnapshotFailed": "Failed to snapshot equipped loadout" }, "Infusion": { From 43921ff27ee560a7d4d542c68786f712a54f5983 Mon Sep 17 00:00:00 2001 From: Ben Hollis Date: Tue, 22 Aug 2023 22:49:31 -0700 Subject: [PATCH 03/12] Show ingame icon wherever loadouts appear? --- src/app/inventory/MoveNotifications.tsx | 10 ++++++++-- src/app/item-triage/ItemTriage.tsx | 13 +++++++++++-- src/app/loadout/LoadoutView.m.scss | 4 +++- src/app/loadout/LoadoutView.tsx | 7 +++++++ src/app/loadout/Loadouts.m.scss | 7 +++++++ src/app/loadout/Loadouts.m.scss.d.ts | 1 + src/app/loadout/Loadouts.tsx | 13 ++++++++++++- src/app/loadout/loadout-menu/LoadoutPopup.m.scss | 5 +++++ .../loadout/loadout-menu/LoadoutPopup.m.scss.d.ts | 1 + src/app/loadout/loadout-menu/LoadoutPopup.tsx | 12 +++++++++++- src/app/organizer/Columns.tsx | 11 +++++++++-- 11 files changed, 75 insertions(+), 9 deletions(-) diff --git a/src/app/inventory/MoveNotifications.tsx b/src/app/inventory/MoveNotifications.tsx index 635ef64cde..2db31a0254 100644 --- a/src/app/inventory/MoveNotifications.tsx +++ b/src/app/inventory/MoveNotifications.tsx @@ -8,7 +8,9 @@ import { LoadoutSocketOverrideState, } from 'app/loadout-drawer/loadout-apply-state'; import { InGameLoadout, Loadout, isInGameLoadout } from 'app/loadout-drawer/loadout-types'; -import InGameLoadoutIcon from 'app/loadout/ingame/InGameLoadoutIcon'; +import InGameLoadoutIcon, { + InGameLoadoutIconFromIdentifiers, +} from 'app/loadout/ingame/InGameLoadoutIcon'; import { useD2Definitions } from 'app/manifest/selectors'; import { NotificationError, NotifyInput } from 'app/notifications/notifications'; import { AppIcon, faCheckCircle, faExclamationCircle, refreshIcon } from 'app/shell/icons'; @@ -71,7 +73,11 @@ export function loadoutNotification( }), duration: 5_000, title: t('Loadouts.NotificationTitle', { name: loadout.name }), - icon: isInGameLoadout(loadout) && , + icon: isInGameLoadout(loadout) ? ( + + ) : loadout.parameters?.inGameIdentifiers ? ( + + ) : undefined, body: , onCancel: cancel, }; diff --git a/src/app/item-triage/ItemTriage.tsx b/src/app/item-triage/ItemTriage.tsx index ab20df7cad..301cc2823d 100644 --- a/src/app/item-triage/ItemTriage.tsx +++ b/src/app/item-triage/ItemTriage.tsx @@ -16,7 +16,9 @@ import { hideItemPopup } from 'app/item-popup/item-popup'; import { editLoadout } from 'app/loadout-drawer/loadout-events'; import { isInGameLoadout } from 'app/loadout-drawer/loadout-types'; import { loadoutsByItemSelector } from 'app/loadout-drawer/selectors'; -import InGameLoadoutIcon from 'app/loadout/ingame/InGameLoadoutIcon'; +import InGameLoadoutIcon, { + InGameLoadoutIconFromIdentifiers, +} from 'app/loadout/ingame/InGameLoadoutIcon'; import { filterFactorySelector } from 'app/search/search-filter'; import { loadoutToSearchString } from 'app/search/search-filters/loadouts'; import { AppIcon, compareIcon, editIcon } from 'app/shell/icons'; @@ -156,7 +158,14 @@ function LoadoutsTriageSection({ item }: { item: DimItem }) { return (
  • {isDimLoadout ? ( - + <> + + {loadout.parameters?.inGameIdentifiers && ( + + )} + ) : ( )} diff --git a/src/app/loadout/LoadoutView.m.scss b/src/app/loadout/LoadoutView.m.scss index b21ee14a8c..cce526e142 100644 --- a/src/app/loadout/LoadoutView.m.scss +++ b/src/app/loadout/LoadoutView.m.scss @@ -13,7 +13,9 @@ gap: 1em; h2 { - display: block; + display: flex; + flex-direction: row; + gap: 8px; margin: 0 !important; font-size: 16px; } diff --git a/src/app/loadout/LoadoutView.tsx b/src/app/loadout/LoadoutView.tsx index 7caabc833e..fceb02f161 100644 --- a/src/app/loadout/LoadoutView.tsx +++ b/src/app/loadout/LoadoutView.tsx @@ -19,6 +19,7 @@ import _ from 'lodash'; import { ReactNode, useMemo } from 'react'; import { useSelector } from 'react-redux'; import styles from './LoadoutView.m.scss'; +import { InGameLoadoutIconFromIdentifiers } from './ingame/InGameLoadoutIcon'; import LoadoutItemCategorySection from './loadout-ui/LoadoutItemCategorySection'; import { LoadoutArtifactUnlocks, LoadoutMods } from './loadout-ui/LoadoutMods'; import LoadoutSubclassSection from './loadout-ui/LoadoutSubclassSection'; @@ -110,6 +111,12 @@ export default function LoadoutView({

    + {loadout.parameters?.inGameIdentifiers && ( + + )} {loadout.classType === DestinyClass.Unknown && ( )} diff --git a/src/app/loadout/Loadouts.m.scss b/src/app/loadout/Loadouts.m.scss index 8e4e273f47..4643006cb2 100644 --- a/src/app/loadout/Loadouts.m.scss +++ b/src/app/loadout/Loadouts.m.scss @@ -32,3 +32,10 @@ .loadoutRow { padding-bottom: 10px; } + +.loadoutMenuItem { + img { + height: 16px; + width: 16px; + } +} diff --git a/src/app/loadout/Loadouts.m.scss.d.ts b/src/app/loadout/Loadouts.m.scss.d.ts index a44f96dfb9..4392260f59 100644 --- a/src/app/loadout/Loadouts.m.scss.d.ts +++ b/src/app/loadout/Loadouts.m.scss.d.ts @@ -2,6 +2,7 @@ // Please do not change this file! interface CssExports { 'hashtagTip': string; + 'loadoutMenuItem': string; 'loadoutRow': string; 'menu': string; 'menuButton': string; diff --git a/src/app/loadout/Loadouts.tsx b/src/app/loadout/Loadouts.tsx index b02c77438a..f739d4bbe2 100644 --- a/src/app/loadout/Loadouts.tsx +++ b/src/app/loadout/Loadouts.tsx @@ -26,6 +26,7 @@ import styles from './Loadouts.m.scss'; import LoadoutRow from './LoadoutsRow'; import EditInGameLoadout from './ingame/EditInGameLoadout'; import { InGameLoadoutDetails } from './ingame/InGameLoadoutDetailsSheet'; +import { InGameLoadoutIconFromIdentifiers } from './ingame/InGameLoadoutIcon'; import { InGameLoadoutStrip } from './ingame/InGameLoadoutStrip'; import LoadoutImportSheet from './loadout-share/LoadoutImportSheet'; import LoadoutShareSheet from './loadout-share/LoadoutShareSheet'; @@ -171,7 +172,17 @@ function Loadouts({ account }: { account: DestinyAccount }) {

    {!isPhonePortrait && loadouts.map((loadout) => ( - scrollToLoadout(loadout.id)} key={loadout.id}> + scrollToLoadout(loadout.id)} + key={loadout.id} + className={styles.loadoutMenuItem} + > + {loadout.parameters?.inGameIdentifiers && ( + + )} ))} diff --git a/src/app/loadout/loadout-menu/LoadoutPopup.m.scss b/src/app/loadout/loadout-menu/LoadoutPopup.m.scss index 35cf80ff24..dadf24717a 100644 --- a/src/app/loadout/loadout-menu/LoadoutPopup.m.scss +++ b/src/app/loadout/loadout-menu/LoadoutPopup.m.scss @@ -82,6 +82,7 @@ > :global(.app-icon), > img { width: 12px; + height: auto; margin-right: 5px; text-align: center; @@ -202,3 +203,7 @@ } display: block; } + +.loadoutIcon { + width: 16px !important; +} diff --git a/src/app/loadout/loadout-menu/LoadoutPopup.m.scss.d.ts b/src/app/loadout/loadout-menu/LoadoutPopup.m.scss.d.ts index 22d79099c1..54263df5bf 100644 --- a/src/app/loadout/loadout-menu/LoadoutPopup.m.scss.d.ts +++ b/src/app/loadout/loadout-menu/LoadoutPopup.m.scss.d.ts @@ -8,6 +8,7 @@ interface CssExports { 'inGameLoadoutButton': string; 'inGameLoadouts': string; 'list': string; + 'loadoutIcon': string; 'loadoutTypeIcon': string; 'menuItem': string; 'moreLoadouts': string; diff --git a/src/app/loadout/loadout-menu/LoadoutPopup.tsx b/src/app/loadout/loadout-menu/LoadoutPopup.tsx index 55e055bf28..56cf8ef32f 100644 --- a/src/app/loadout/loadout-menu/LoadoutPopup.tsx +++ b/src/app/loadout/loadout-menu/LoadoutPopup.tsx @@ -54,7 +54,10 @@ import consumablesIcon from 'destiny-icons/general/consumables.svg'; import React, { useState } from 'react'; import { useSelector } from 'react-redux'; import { Link } from 'react-router-dom'; -import { InGameLoadoutIconWithIndex } from '../ingame/InGameLoadoutIcon'; +import { + InGameLoadoutIconFromIdentifiers, + InGameLoadoutIconWithIndex, +} from '../ingame/InGameLoadoutIcon'; import { applyInGameLoadout } from '../ingame/ingame-loadout-apply'; import { inGameLoadoutsForCharacterSelector } from '../ingame/selectors'; import { @@ -299,6 +302,13 @@ export default function LoadoutPopup({ {(dimStore.isVault || loadout.classType === DestinyClass.Unknown) && ( )} + {loadout.parameters?.inGameIdentifiers && ( + + )} {isMissingItems(defs, allItems, dimStore.id, loadout) && ( {isInGameLoadout(loadout) ? ( - {isInGameLoadout(loadout) && } + {loadout.name} ) : ( @@ -644,6 +646,11 @@ function LoadoutsCell({ !e.shiftKey && editLoadout(loadout, owner, { isNew: false }) } > + {loadout.parameters?.inGameIdentifiers && ( + + )} {loadout.name} )} From 33f61639d18f2624d1366660ade7aa8d5ba55bd8 Mon Sep 17 00:00:00 2001 From: Ben Hollis Date: Wed, 23 Aug 2023 10:35:20 -0700 Subject: [PATCH 04/12] Scale it back to only the loadouts list --- src/app/inventory/MoveNotifications.tsx | 10 ++-------- src/app/item-triage/ItemTriage.tsx | 13 ++----------- src/app/loadout/Loadouts.m.scss | 7 ------- src/app/loadout/Loadouts.m.scss.d.ts | 1 - src/app/loadout/Loadouts.tsx | 13 +------------ src/app/loadout/loadout-menu/LoadoutPopup.tsx | 12 +----------- src/app/organizer/Columns.tsx | 11 ++--------- 7 files changed, 8 insertions(+), 59 deletions(-) diff --git a/src/app/inventory/MoveNotifications.tsx b/src/app/inventory/MoveNotifications.tsx index 2db31a0254..635ef64cde 100644 --- a/src/app/inventory/MoveNotifications.tsx +++ b/src/app/inventory/MoveNotifications.tsx @@ -8,9 +8,7 @@ import { LoadoutSocketOverrideState, } from 'app/loadout-drawer/loadout-apply-state'; import { InGameLoadout, Loadout, isInGameLoadout } from 'app/loadout-drawer/loadout-types'; -import InGameLoadoutIcon, { - InGameLoadoutIconFromIdentifiers, -} from 'app/loadout/ingame/InGameLoadoutIcon'; +import InGameLoadoutIcon from 'app/loadout/ingame/InGameLoadoutIcon'; import { useD2Definitions } from 'app/manifest/selectors'; import { NotificationError, NotifyInput } from 'app/notifications/notifications'; import { AppIcon, faCheckCircle, faExclamationCircle, refreshIcon } from 'app/shell/icons'; @@ -73,11 +71,7 @@ export function loadoutNotification( }), duration: 5_000, title: t('Loadouts.NotificationTitle', { name: loadout.name }), - icon: isInGameLoadout(loadout) ? ( - - ) : loadout.parameters?.inGameIdentifiers ? ( - - ) : undefined, + icon: isInGameLoadout(loadout) && , body: , onCancel: cancel, }; diff --git a/src/app/item-triage/ItemTriage.tsx b/src/app/item-triage/ItemTriage.tsx index 301cc2823d..ab20df7cad 100644 --- a/src/app/item-triage/ItemTriage.tsx +++ b/src/app/item-triage/ItemTriage.tsx @@ -16,9 +16,7 @@ import { hideItemPopup } from 'app/item-popup/item-popup'; import { editLoadout } from 'app/loadout-drawer/loadout-events'; import { isInGameLoadout } from 'app/loadout-drawer/loadout-types'; import { loadoutsByItemSelector } from 'app/loadout-drawer/selectors'; -import InGameLoadoutIcon, { - InGameLoadoutIconFromIdentifiers, -} from 'app/loadout/ingame/InGameLoadoutIcon'; +import InGameLoadoutIcon from 'app/loadout/ingame/InGameLoadoutIcon'; import { filterFactorySelector } from 'app/search/search-filter'; import { loadoutToSearchString } from 'app/search/search-filters/loadouts'; import { AppIcon, compareIcon, editIcon } from 'app/shell/icons'; @@ -158,14 +156,7 @@ function LoadoutsTriageSection({ item }: { item: DimItem }) { return (
  • {isDimLoadout ? ( - <> - - {loadout.parameters?.inGameIdentifiers && ( - - )} - + ) : ( )} diff --git a/src/app/loadout/Loadouts.m.scss b/src/app/loadout/Loadouts.m.scss index 4643006cb2..8e4e273f47 100644 --- a/src/app/loadout/Loadouts.m.scss +++ b/src/app/loadout/Loadouts.m.scss @@ -32,10 +32,3 @@ .loadoutRow { padding-bottom: 10px; } - -.loadoutMenuItem { - img { - height: 16px; - width: 16px; - } -} diff --git a/src/app/loadout/Loadouts.m.scss.d.ts b/src/app/loadout/Loadouts.m.scss.d.ts index 4392260f59..a44f96dfb9 100644 --- a/src/app/loadout/Loadouts.m.scss.d.ts +++ b/src/app/loadout/Loadouts.m.scss.d.ts @@ -2,7 +2,6 @@ // Please do not change this file! interface CssExports { 'hashtagTip': string; - 'loadoutMenuItem': string; 'loadoutRow': string; 'menu': string; 'menuButton': string; diff --git a/src/app/loadout/Loadouts.tsx b/src/app/loadout/Loadouts.tsx index f739d4bbe2..b02c77438a 100644 --- a/src/app/loadout/Loadouts.tsx +++ b/src/app/loadout/Loadouts.tsx @@ -26,7 +26,6 @@ import styles from './Loadouts.m.scss'; import LoadoutRow from './LoadoutsRow'; import EditInGameLoadout from './ingame/EditInGameLoadout'; import { InGameLoadoutDetails } from './ingame/InGameLoadoutDetailsSheet'; -import { InGameLoadoutIconFromIdentifiers } from './ingame/InGameLoadoutIcon'; import { InGameLoadoutStrip } from './ingame/InGameLoadoutStrip'; import LoadoutImportSheet from './loadout-share/LoadoutImportSheet'; import LoadoutShareSheet from './loadout-share/LoadoutShareSheet'; @@ -172,17 +171,7 @@ function Loadouts({ account }: { account: DestinyAccount }) {
  • {!isPhonePortrait && loadouts.map((loadout) => ( - scrollToLoadout(loadout.id)} - key={loadout.id} - className={styles.loadoutMenuItem} - > - {loadout.parameters?.inGameIdentifiers && ( - - )} + scrollToLoadout(loadout.id)} key={loadout.id}> ))} diff --git a/src/app/loadout/loadout-menu/LoadoutPopup.tsx b/src/app/loadout/loadout-menu/LoadoutPopup.tsx index 56cf8ef32f..55e055bf28 100644 --- a/src/app/loadout/loadout-menu/LoadoutPopup.tsx +++ b/src/app/loadout/loadout-menu/LoadoutPopup.tsx @@ -54,10 +54,7 @@ import consumablesIcon from 'destiny-icons/general/consumables.svg'; import React, { useState } from 'react'; import { useSelector } from 'react-redux'; import { Link } from 'react-router-dom'; -import { - InGameLoadoutIconFromIdentifiers, - InGameLoadoutIconWithIndex, -} from '../ingame/InGameLoadoutIcon'; +import { InGameLoadoutIconWithIndex } from '../ingame/InGameLoadoutIcon'; import { applyInGameLoadout } from '../ingame/ingame-loadout-apply'; import { inGameLoadoutsForCharacterSelector } from '../ingame/selectors'; import { @@ -302,13 +299,6 @@ export default function LoadoutPopup({ {(dimStore.isVault || loadout.classType === DestinyClass.Unknown) && ( )} - {loadout.parameters?.inGameIdentifiers && ( - - )} {isMissingItems(defs, allItems, dimStore.id, loadout) && ( {isInGameLoadout(loadout) ? ( - + {isInGameLoadout(loadout) && } {loadout.name} ) : ( @@ -646,11 +644,6 @@ function LoadoutsCell({ !e.shiftKey && editLoadout(loadout, owner, { isNew: false }) } > - {loadout.parameters?.inGameIdentifiers && ( - - )} {loadout.name} )} From 978f3e9e4e3536668657fccca3ef95092c895a16 Mon Sep 17 00:00:00 2001 From: Ben Hollis Date: Wed, 23 Aug 2023 10:52:17 -0700 Subject: [PATCH 05/12] Def dep --- src/app/loadout/ingame/SelectInGameLoadoutIdentifiers.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/loadout/ingame/SelectInGameLoadoutIdentifiers.tsx b/src/app/loadout/ingame/SelectInGameLoadoutIdentifiers.tsx index 133d6c1a1f..270301b335 100644 --- a/src/app/loadout/ingame/SelectInGameLoadoutIdentifiers.tsx +++ b/src/app/loadout/ingame/SelectInGameLoadoutIdentifiers.tsx @@ -96,5 +96,5 @@ export function useIdentifierValues(defs: D2ManifestDefinitions) { .filter((i) => !i.redacted) .sort(compareBy((n) => n.index)); return [names, colors, icons] as const; - }, []); + }, [defs]); } From 397aba1e39412d2a96ae3f48851346bf8a814555 Mon Sep 17 00:00:00 2001 From: Ben Hollis Date: Wed, 23 Aug 2023 17:49:08 -0700 Subject: [PATCH 06/12] Remove portal --- .../ingame/InGameLoadoutIdentifiersSelectButton.tsx | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/app/loadout/ingame/InGameLoadoutIdentifiersSelectButton.tsx b/src/app/loadout/ingame/InGameLoadoutIdentifiersSelectButton.tsx index d500d56381..474bbdad80 100644 --- a/src/app/loadout/ingame/InGameLoadoutIdentifiersSelectButton.tsx +++ b/src/app/loadout/ingame/InGameLoadoutIdentifiersSelectButton.tsx @@ -3,7 +3,6 @@ import ClosableContainer from 'app/dim-ui/ClosableContainer'; import { t } from 'app/i18next-t'; import { setInGameLoadoutIdentifiers } from 'app/loadout-drawer/loadout-drawer-reducer'; import { Loadout } from 'app/loadout-drawer/loadout-types'; -import { Portal } from 'app/utils/temp-container'; import noInGameLoadoutIdentifiers from 'images/no-loadout-identifiers.svg'; import { useState } from 'react'; import EditInGameLoadoutIdentifiers from './EditInGameLoadoutIdentifiers'; @@ -46,13 +45,11 @@ export default function InGameLoadoutIdentifiersSelectButton({ {sheetOpen && ( - - setSheetOpen(false)} - /> - + setSheetOpen(false)} + /> )} ); From 279ce5be4fd402e6f6a77b36aec6597c5fce7c0e Mon Sep 17 00:00:00 2001 From: Ben Hollis Date: Sat, 26 Aug 2023 17:30:49 -0700 Subject: [PATCH 07/12] Remove class icon --- src/app/loadout-drawer/LoadoutDrawer.tsx | 2 +- src/app/loadout-drawer/LoadoutDrawerHeader.tsx | 2 -- src/app/loadout/Loadouts.tsx | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/app/loadout-drawer/LoadoutDrawer.tsx b/src/app/loadout-drawer/LoadoutDrawer.tsx index 002f3dc42e..d35b0d8bcd 100644 --- a/src/app/loadout-drawer/LoadoutDrawer.tsx +++ b/src/app/loadout-drawer/LoadoutDrawer.tsx @@ -94,7 +94,7 @@ export default function LoadoutDrawer({ return (...args: T) => setLoadout(fn(...args)); } - const store = getStore(stores, storeId)!; + const store = getStore(stores, storeId); const onAddItem = useCallback( (item: DimItem, equip?: boolean) => setLoadout(addItem(defs, item, equip)), diff --git a/src/app/loadout-drawer/LoadoutDrawerHeader.tsx b/src/app/loadout-drawer/LoadoutDrawerHeader.tsx index b4277ec345..9c76ea3415 100644 --- a/src/app/loadout-drawer/LoadoutDrawerHeader.tsx +++ b/src/app/loadout-drawer/LoadoutDrawerHeader.tsx @@ -1,4 +1,3 @@ -import ClassIcon from 'app/dim-ui/ClassIcon'; import { WithSymbolsPicker } from 'app/dim-ui/destiny-symbols/SymbolsPicker'; import { useAutocomplete } from 'app/dim-ui/text-complete/text-complete'; import { t } from 'app/i18next-t'; @@ -26,7 +25,6 @@ export default function LoadoutDrawerHeader({ return (
    - {t('Storage.DimSyncNotEnabled')}

    )} -

    {t('Loadouts.InGameLoadouts')}

    Date: Sat, 26 Aug 2023 17:37:32 -0700 Subject: [PATCH 08/12] Remove unused class --- src/app/loadout/loadout-menu/LoadoutPopup.m.scss | 4 ---- src/app/loadout/loadout-menu/LoadoutPopup.m.scss.d.ts | 1 - 2 files changed, 5 deletions(-) diff --git a/src/app/loadout/loadout-menu/LoadoutPopup.m.scss b/src/app/loadout/loadout-menu/LoadoutPopup.m.scss index dadf24717a..ff6552d629 100644 --- a/src/app/loadout/loadout-menu/LoadoutPopup.m.scss +++ b/src/app/loadout/loadout-menu/LoadoutPopup.m.scss @@ -203,7 +203,3 @@ } display: block; } - -.loadoutIcon { - width: 16px !important; -} diff --git a/src/app/loadout/loadout-menu/LoadoutPopup.m.scss.d.ts b/src/app/loadout/loadout-menu/LoadoutPopup.m.scss.d.ts index 54263df5bf..22d79099c1 100644 --- a/src/app/loadout/loadout-menu/LoadoutPopup.m.scss.d.ts +++ b/src/app/loadout/loadout-menu/LoadoutPopup.m.scss.d.ts @@ -8,7 +8,6 @@ interface CssExports { 'inGameLoadoutButton': string; 'inGameLoadouts': string; 'list': string; - 'loadoutIcon': string; 'loadoutTypeIcon': string; 'menuItem': string; 'moreLoadouts': string; From 8fdb8ca0f33040f4d65603f5d3ee5f872e86ed15 Mon Sep 17 00:00:00 2001 From: Ben Hollis Date: Fri, 1 Sep 2023 12:55:13 -0700 Subject: [PATCH 09/12] Revert "Remove class icon" This reverts commit 279ce5be4fd402e6f6a77b36aec6597c5fce7c0e. --- src/app/loadout-drawer/LoadoutDrawerHeader.tsx | 2 ++ src/app/loadout/Loadouts.tsx | 1 + 2 files changed, 3 insertions(+) diff --git a/src/app/loadout-drawer/LoadoutDrawerHeader.tsx b/src/app/loadout-drawer/LoadoutDrawerHeader.tsx index 9c76ea3415..b4277ec345 100644 --- a/src/app/loadout-drawer/LoadoutDrawerHeader.tsx +++ b/src/app/loadout-drawer/LoadoutDrawerHeader.tsx @@ -1,3 +1,4 @@ +import ClassIcon from 'app/dim-ui/ClassIcon'; import { WithSymbolsPicker } from 'app/dim-ui/destiny-symbols/SymbolsPicker'; import { useAutocomplete } from 'app/dim-ui/text-complete/text-complete'; import { t } from 'app/i18next-t'; @@ -25,6 +26,7 @@ export default function LoadoutDrawerHeader({ return (
    + {t('Storage.DimSyncNotEnabled')}

    )} +

    {t('Loadouts.InGameLoadouts')}

    Date: Fri, 1 Sep 2023 13:31:08 -0700 Subject: [PATCH 10/12] Show loadout class only when it's from a share --- config/i18n.json | 3 ++ src/app/loadout-drawer/LoadoutDrawer.m.scss | 30 +++++++------ .../loadout-drawer/LoadoutDrawer.m.scss.d.ts | 2 + src/app/loadout-drawer/LoadoutDrawer.tsx | 44 +++++++++++++------ .../loadout-drawer/LoadoutDrawerContainer.tsx | 23 ++++------ .../loadout-drawer/LoadoutDrawerHeader.m.scss | 14 +----- .../LoadoutDrawerHeader.m.scss.d.ts | 1 - .../loadout-drawer/LoadoutDrawerHeader.tsx | 35 ++++++--------- src/app/loadout-drawer/loadout-events.ts | 18 +++++--- .../loadout-share/LoadoutImportSheet.tsx | 2 +- src/locale/en.json | 3 ++ 11 files changed, 91 insertions(+), 84 deletions(-) diff --git a/config/i18n.json b/config/i18n.json index 6463c3c24d..45b4618693 100644 --- a/config/i18n.json +++ b/config/i18n.json @@ -649,6 +649,9 @@ "CannotCustomizeSubclass": "This subclass cannot be configured", "ChooseItem": "Add {{name}}", "Classified": "Some of your items are classified, and cannot be included in the max power calculation.", + "ClassType": "Any class loadout", + "ClassType_male": "{{className}} loadout", + "ClassType_female": "{{className}} loadout", "ClassTypeMismatch": "A {{className}} item cannot be added to this loadout", "ClassTypeMissing": "You do not have a {{className}} to create a loadout for", "ClearSection": "Remove all", diff --git a/src/app/loadout-drawer/LoadoutDrawer.m.scss b/src/app/loadout-drawer/LoadoutDrawer.m.scss index bcec6f6305..c12494bc9d 100644 --- a/src/app/loadout-drawer/LoadoutDrawer.m.scss +++ b/src/app/loadout-drawer/LoadoutDrawer.m.scss @@ -10,8 +10,8 @@ } .notes { - margin-top: 8px; font-size: 14px; + margin-top: 4px; summary { font-weight: bold; @@ -31,20 +31,22 @@ } .header { - display: grid; - grid-template-columns: min-content 1fr; - grid-template-areas: - 'identifiers title' - 'identifiers notes'; + composes: flexRow from '../dim-ui/common.m.scss'; gap: 0 12px; +} - > *:nth-child(1) { - grid-area: identifiers; - } - > *:nth-child(2) { - grid-area: title; - } - > *:nth-child(3) { - grid-area: notes; +.headerDetails { + composes: flexColumn from '../dim-ui/common.m.scss'; + flex: 1; + gap: 4px; +} + +.classType { + composes: flexRow from '../dim-ui/common.m.scss'; + align-items: center; + gap: 0.25em; + + svg { + height: 16px; } } diff --git a/src/app/loadout-drawer/LoadoutDrawer.m.scss.d.ts b/src/app/loadout-drawer/LoadoutDrawer.m.scss.d.ts index 5447f4fd60..449dc6cbef 100644 --- a/src/app/loadout-drawer/LoadoutDrawer.m.scss.d.ts +++ b/src/app/loadout-drawer/LoadoutDrawer.m.scss.d.ts @@ -2,7 +2,9 @@ // Please do not change this file! interface CssExports { 'body': string; + 'classType': string; 'header': string; + 'headerDetails': string; 'inputGroup': string; 'notes': string; } diff --git a/src/app/loadout-drawer/LoadoutDrawer.tsx b/src/app/loadout-drawer/LoadoutDrawer.tsx index 06009e67c4..52b2a147c0 100644 --- a/src/app/loadout-drawer/LoadoutDrawer.tsx +++ b/src/app/loadout-drawer/LoadoutDrawer.tsx @@ -3,6 +3,7 @@ import { D2ManifestDefinitions } from 'app/destiny2/d2-definitions'; import { apiPermissionGrantedSelector } from 'app/dim-api/selectors'; import { AlertIcon } from 'app/dim-ui/AlertIcon'; import CheckButton from 'app/dim-ui/CheckButton'; +import ClassIcon from 'app/dim-ui/ClassIcon'; import { WithSymbolsPicker } from 'app/dim-ui/destiny-symbols/SymbolsPicker'; import { useAutocomplete } from 'app/dim-ui/text-complete/text-complete'; import { t } from 'app/i18next-t'; @@ -61,6 +62,7 @@ export default function LoadoutDrawer({ initialLoadout, storeId, isNew, + fromExternal, onClose, }: { initialLoadout: Loadout; @@ -71,6 +73,7 @@ export default function LoadoutDrawer({ */ storeId: string; isNew: boolean; + fromExternal: boolean; onClose: () => void; }) { const dispatch = useThunkDispatch(); @@ -175,22 +178,35 @@ export default function LoadoutDrawer({ const toggleAnyClass = (checked: boolean) => setLoadout(setClassType(checked ? DestinyClass.Unknown : store.classType)); + const showInGameLoadoutIdentifiers = + Boolean(loadout.parameters?.inGameIdentifiers) || loadout.items.length > 0; + const header = (
    - - -
    - {t('MovePopup.Notes')} - setLoadout(setNotes(val))}> - - -
    + {showInGameLoadoutIdentifiers && ( + + )} +
    + {fromExternal && ( +
    + + {t('Loadouts.ClassType', { className: store.className, context: store.genderName })} +
    + )} + +
    + {t('MovePopup.Notes')} + setLoadout(setNotes(val))}> + + +
    +
    ); diff --git a/src/app/loadout-drawer/LoadoutDrawerContainer.tsx b/src/app/loadout-drawer/LoadoutDrawerContainer.tsx index 8a6540f990..7629425591 100644 --- a/src/app/loadout-drawer/LoadoutDrawerContainer.tsx +++ b/src/app/loadout-drawer/LoadoutDrawerContainer.tsx @@ -13,8 +13,7 @@ import { DestinyClass } from 'bungie-api-ts/destiny2'; import { Suspense, lazy, useCallback, useEffect, useState } from 'react'; import { useSelector } from 'react-redux'; import { useLocation, useNavigate } from 'react-router'; -import { addItem$, editLoadout$ } from './loadout-events'; -import { Loadout } from './loadout-types'; +import { EditLoadoutState, addItem$, editLoadout$ } from './loadout-events'; import { convertToLoadoutItem, newLoadout, pickBackingStore } from './loadout-utils'; const LoadoutDrawer = lazy( @@ -43,12 +42,7 @@ export default function LoadoutDrawerContainer({ account }: { account: DestinyAc // TODO: Alternately we could come up with the concept of a // `useControlledReducer` that applied a reducer to mutate an object whose // state is handled outside the component. - const [initialLoadout, setInitialLoadout] = useState<{ - loadout: Loadout; - storeId: string; - showClass: boolean; - isNew: boolean; - }>(); + const [initialLoadout, setInitialLoadout] = useState(); const handleDrawerClose = useCallback(() => { setInitialLoadout(undefined); @@ -60,7 +54,8 @@ export default function LoadoutDrawerContainer({ account }: { account: DestinyAc useEventBusListener( editLoadout$, useCallback( - ({ loadout, storeId, showClass, isNew }) => { + (state) => { + const { storeId, loadout } = state; // Fall back to current store because otherwise there's no way to delete loadouts // the user doesn't have a class for. const editingStore = @@ -73,12 +68,7 @@ export default function LoadoutDrawerContainer({ account }: { account: DestinyAc return; } - setInitialLoadout({ - loadout, - storeId: editingStore.id, - showClass: Boolean(showClass), - isNew: Boolean(isNew), - }); + setInitialLoadout({ ...state, storeId: editingStore.id }); }, [stores, defs] ) @@ -113,6 +103,7 @@ export default function LoadoutDrawerContainer({ account }: { account: DestinyAc storeId: owner.id, isNew: true, showClass: true, + fromExternal: true, }); } }, @@ -140,6 +131,7 @@ export default function LoadoutDrawerContainer({ account }: { account: DestinyAc storeId, isNew: true, showClass: false, + fromExternal: true, }); // Clear the loadout from params if the URL contained one... navigate(pathname, { replace: true }); @@ -173,6 +165,7 @@ export default function LoadoutDrawerContainer({ account }: { account: DestinyAc storeId={initialLoadout.storeId} isNew={initialLoadout.isNew} onClose={handleDrawerClose} + fromExternal={initialLoadout.fromExternal} /> ) : ( - - - - -
    + + + ); } diff --git a/src/app/loadout-drawer/loadout-events.ts b/src/app/loadout-drawer/loadout-events.ts index 12a6cc8d10..cb5f5a0dad 100644 --- a/src/app/loadout-drawer/loadout-events.ts +++ b/src/app/loadout-drawer/loadout-events.ts @@ -2,12 +2,15 @@ import { DimItem } from 'app/inventory/item-types'; import { EventBus } from 'app/utils/observable'; import { Loadout } from './loadout-types'; -export const editLoadout$ = new EventBus<{ +export interface EditLoadoutState { loadout: Loadout; - showClass?: boolean; - isNew?: boolean; + showClass: boolean; + isNew: boolean; storeId: string; -}>(); + fromExternal: boolean; +} + +export const editLoadout$ = new EventBus(); export const addItem$ = new EventBus(); /** @@ -16,13 +19,18 @@ export const addItem$ = new EventBus(); export function editLoadout( loadout: Loadout, storeId: string, - { showClass = true, isNew = true }: { showClass?: boolean; isNew?: boolean } = {} + { + showClass = true, + isNew = true, + fromExternal = false, + }: { showClass?: boolean; isNew?: boolean; fromExternal?: boolean } = {} ) { editLoadout$.next({ storeId, loadout, showClass, isNew, + fromExternal, }); } diff --git a/src/app/loadout/loadout-share/LoadoutImportSheet.tsx b/src/app/loadout/loadout-share/LoadoutImportSheet.tsx index 7108caab3f..7b9aaa45c5 100644 --- a/src/app/loadout/loadout-share/LoadoutImportSheet.tsx +++ b/src/app/loadout/loadout-share/LoadoutImportSheet.tsx @@ -42,7 +42,7 @@ export default function LoadoutImportSheet({ const loadout = await getDecodedLoadout(decodedUrl); if (!canceled) { setState('ok'); - editLoadout(loadout, currentStoreId, { isNew: true }); + editLoadout(loadout, currentStoreId, { isNew: true, fromExternal: true }); onClose(); } } catch (e) { diff --git a/src/locale/en.json b/src/locale/en.json index 6dcf76f2eb..fdeb57a637 100644 --- a/src/locale/en.json +++ b/src/locale/en.json @@ -644,8 +644,11 @@ "CancelEditing": "Cancel Editing", "CannotCustomizeSubclass": "This subclass cannot be configured", "ChooseItem": "Add {{name}}", + "ClassType": "Any class loadout", "ClassTypeMismatch": "A {{className}} item cannot be added to this loadout", "ClassTypeMissing": "You do not have a {{className}} to create a loadout for", + "ClassType_female": "{{className}} loadout", + "ClassType_male": "{{className}} loadout", "Classified": "Some of your items are classified, and cannot be included in the max power calculation.", "ClearLoadoutParameters": "Remove Loadout Optimizer settings", "ClearSection": "Remove all", From ede032f2c8a60e8923f2c75fd63cbbf5e59555d9 Mon Sep 17 00:00:00 2001 From: Ben Hollis Date: Mon, 2 Sep 2024 18:48:28 -0700 Subject: [PATCH 11/12] Feature flag it --- config/feature-flags.ts | 2 ++ src/app/loadout-drawer/LoadoutDrawer.tsx | 3 ++- src/app/loadout/ingame/EditInGameLoadout.tsx | 2 +- src/app/loadout/ingame/RadioButton.m.scss | 4 ++-- src/app/loadout/ingame/SelectInGameLoadoutIdentifiers.tsx | 2 +- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/config/feature-flags.ts b/config/feature-flags.ts index 6367e38a4d..72f9c22518 100644 --- a/config/feature-flags.ts +++ b/config/feature-flags.ts @@ -51,6 +51,8 @@ export function makeFeatureFlags(env: { customStatWeights: false, // On the Loadouts page, run Loadout Optimizer to find better tiers for loadouts. runLoInBackground: true, + // Whether to allow setting in-game loadout identifiers on DIM loadouts. + editInGameLoadoutIdentifiers: false, }; } diff --git a/src/app/loadout-drawer/LoadoutDrawer.tsx b/src/app/loadout-drawer/LoadoutDrawer.tsx index 2a3d451746..03239e8f73 100644 --- a/src/app/loadout-drawer/LoadoutDrawer.tsx +++ b/src/app/loadout-drawer/LoadoutDrawer.tsx @@ -175,7 +175,8 @@ export default function LoadoutDrawer({ setLoadout(setClassType(checked ? DestinyClass.Unknown : store.classType)); const showInGameLoadoutIdentifiers = - Boolean(loadout.parameters?.inGameIdentifiers) || loadout.items.length > 0; + $featureFlags.editInGameLoadoutIdentifiers && + (Boolean(loadout.parameters?.inGameIdentifiers) || loadout.items.length > 0); const header = (
    diff --git a/src/app/loadout/ingame/EditInGameLoadout.tsx b/src/app/loadout/ingame/EditInGameLoadout.tsx index 68f3b3ac75..b7a2e96c6b 100644 --- a/src/app/loadout/ingame/EditInGameLoadout.tsx +++ b/src/app/loadout/ingame/EditInGameLoadout.tsx @@ -16,7 +16,7 @@ import SelectInGameLoadoutIdentifiers, { import { editInGameLoadout, snapshotInGameLoadout } from './ingame-loadout-apply'; import { availableLoadoutSlotsSelector, inGameLoadoutsForCharacterSelector } from './selectors'; -/** An editor sheet for whatever we can edit with ingame loadouts. Name, color, icon. */ +/** An editor sheet for whatever we can edit with in-game loadouts. Name, color, icon. */ export default function EditInGameLoadout({ loadout, characterId, diff --git a/src/app/loadout/ingame/RadioButton.m.scss b/src/app/loadout/ingame/RadioButton.m.scss index 18c3eab260..6046a8c8ed 100644 --- a/src/app/loadout/ingame/RadioButton.m.scss +++ b/src/app/loadout/ingame/RadioButton.m.scss @@ -4,7 +4,7 @@ composes: dim-button from global; position: relative; - @include interactive($hover: true, $$active: true) { + @include interactive($hover: true, $active: true) { background-color: var(--theme-accent-primary); color: var(--theme-text-invert); > img { @@ -27,7 +27,7 @@ padding: 2px; } &.hasLoadout { - @include interactive($hover: true, $$active: true) { + @include interactive($hover: true, $active: true) { color: var(--theme-text) !important; } &.checked { diff --git a/src/app/loadout/ingame/SelectInGameLoadoutIdentifiers.tsx b/src/app/loadout/ingame/SelectInGameLoadoutIdentifiers.tsx index 9139616db1..86cace1722 100644 --- a/src/app/loadout/ingame/SelectInGameLoadoutIdentifiers.tsx +++ b/src/app/loadout/ingame/SelectInGameLoadoutIdentifiers.tsx @@ -7,7 +7,7 @@ import { useMemo } from 'react'; import { RadioButton } from './RadioButton'; import styles from './SelectInGameLoadoutIdentifiers.m.scss'; -/** Selection controls for choosing an ingame loadout identifier (name, color, +/** Selection controls for choosing an in-game loadout identifier (name, color, * icon). A controlled component. */ export default function SelectInGameLoadoutIdentifiers({ nameHash, From b08af1eb0ee171889f9bf05f942df2bd02ef99dc Mon Sep 17 00:00:00 2001 From: Ben Hollis Date: Mon, 2 Sep 2024 18:56:09 -0700 Subject: [PATCH 12/12] More feature flag --- src/app/loadout-drawer/loadout-events.ts | 1 + src/app/loadout/LoadoutView.tsx | 2 +- src/app/loadout/loadout-type-converters.ts | 5 +++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/app/loadout-drawer/loadout-events.ts b/src/app/loadout-drawer/loadout-events.ts index c8e5d27a30..a40b1aa331 100644 --- a/src/app/loadout-drawer/loadout-events.ts +++ b/src/app/loadout-drawer/loadout-events.ts @@ -27,6 +27,7 @@ export function editLoadout( { showClass = true, isNew = true, + /** Is this from an external source (e.g. a loadout share)? */ fromExternal = false, }: { showClass?: boolean; isNew?: boolean; fromExternal?: boolean } = {}, ) { diff --git a/src/app/loadout/LoadoutView.tsx b/src/app/loadout/LoadoutView.tsx index dd1dab6a38..e304c8a36a 100644 --- a/src/app/loadout/LoadoutView.tsx +++ b/src/app/loadout/LoadoutView.tsx @@ -139,7 +139,7 @@ export default function LoadoutView({ >

    - {loadout.parameters?.inGameIdentifiers && ( + {$featureFlags.editInGameLoadoutIdentifiers && loadout.parameters?.inGameIdentifiers && (