Skip to content

Commit

Permalink
Merge pull request #9760 from DestinyItemManager/ingame-identifiers
Browse files Browse the repository at this point in the history
Edit in-game identifiers for DIM loadouts
  • Loading branch information
bhollis committed Sep 3, 2024
2 parents 7c35043 + b08af1e commit 755bc1a
Show file tree
Hide file tree
Showing 39 changed files with 580 additions and 252 deletions.
2 changes: 1 addition & 1 deletion babel.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ module.exports = function (api) {
if (isProduction) {
plugins.push(
'@babel/plugin-transform-react-constant-elements',
'@babel/plugin-transform-react-inline-elements'
'@babel/plugin-transform-react-inline-elements',
);
} else {
if (!isTest) {
Expand Down
2 changes: 2 additions & 0 deletions config/feature-flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
}

Expand Down
4 changes: 4 additions & 0 deletions config/i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,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"
Expand Down Expand Up @@ -702,6 +703,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",
Expand Down
16 changes: 8 additions & 8 deletions icons/build_icons.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ for (const VERSION of ['release', 'beta', 'dev', 'pr']) {

for (const size of [16, 32, 96, 48]) {
execSync(
`rsvg-convert -w ${size} -h ${size} -o "${VERSION}/favicon-${size}x${size}.png" "favicon-${VERSION}.svg"`
`rsvg-convert -w ${size} -h ${size} -o "${VERSION}/favicon-${size}x${size}.png" "favicon-${VERSION}.svg"`,
);
}

Expand All @@ -27,22 +27,22 @@ for (const VERSION of ['release', 'beta', 'dev', 'pr']) {
}[VERSION];

execSync(
`rsvg-convert -w 180 -h 180 -o "${VERSION}/apple-touch-icon.png" "apple-touch-icon-${VERSION}.svg"`
`rsvg-convert -w 180 -h 180 -o "${VERSION}/apple-touch-icon.png" "apple-touch-icon-${VERSION}.svg"`,
);
execSync(
`rsvg-convert -w 180 -h 180 -o "${VERSION}/apple-touch-icon-${CACHEBREAKER}.png" "apple-touch-icon-${VERSION}.svg"`
`rsvg-convert -w 180 -h 180 -o "${VERSION}/apple-touch-icon-${CACHEBREAKER}.png" "apple-touch-icon-${VERSION}.svg"`,
);
execSync(
`rsvg-convert -w 192 -h 192 -o "${VERSION}/android-chrome-192x192-${CACHEBREAKER}.png" "android-icon-${VERSION}.svg"`
`rsvg-convert -w 192 -h 192 -o "${VERSION}/android-chrome-192x192-${CACHEBREAKER}.png" "android-icon-${VERSION}.svg"`,
);
execSync(
`rsvg-convert -w 512 -h 512 -o "${VERSION}/android-chrome-512x512-${CACHEBREAKER}.png" "android-icon-${VERSION}.svg"`
`rsvg-convert -w 512 -h 512 -o "${VERSION}/android-chrome-512x512-${CACHEBREAKER}.png" "android-icon-${VERSION}.svg"`,
);
execSync(
`rsvg-convert -w 512 -h 512 -b "${color}" -o "${VERSION}/android-chrome-mask-512x512-${CACHEBREAKER}.png" "android-icon-${VERSION}.svg"`
`rsvg-convert -w 512 -h 512 -b "${color}" -o "${VERSION}/android-chrome-mask-512x512-${CACHEBREAKER}.png" "android-icon-${VERSION}.svg"`,
);
execSync(
`convert ${VERSION}/favicon-48x48.png -define icon:auto-resize=48,32,16 ${VERSION}/favicon.ico`
`convert ${VERSION}/favicon-48x48.png -define icon:auto-resize=48,32,16 ${VERSION}/favicon.ico`,
);
rimraf.sync(`${VERSION}/favicon-48x48.png`);
}
Expand All @@ -54,6 +54,6 @@ fs.mkdirSync('splash');
for (const [_a, _b, _c, _d, w, h] of splash) {
execSync(`rsvg-convert -w ${w} -h ${h} -a -o "splash/splash-${w}x${h}.png" "splash.svg"`);
execSync(
`convert splash/splash-${w}x${h}.png -background "#313233" -gravity center -extent ${w}x${h} splash/splash-${w}x${h}.png`
`convert splash/splash-${w}x${h}.png -background "#313233" -gravity center -extent ${w}x${h} splash/splash-${w}x${h}.png`,
);
}
2 changes: 1 addition & 1 deletion src/app/dim-ui/Sheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);

// The sheet is dismissed if it's flicked at a velocity above dismissVelocity,
// or dragged down more than dismissAmount times the height of the sheet.
Expand Down
23 changes: 22 additions & 1 deletion src/app/loadout-drawer/LoadoutDrawer.m.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
}

.notes {
margin-top: 8px;
font-size: 14px;
margin-top: 4px;

summary {
font-weight: bold;
Expand All @@ -29,3 +29,24 @@
align-items: center;
gap: 4px;
}

.header {
composes: flexRow from '../dim-ui/common.m.scss';
gap: 0 12px;
}

.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;
}
}
3 changes: 3 additions & 0 deletions src/app/loadout-drawer/LoadoutDrawer.m.scss.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

49 changes: 34 additions & 15 deletions src/app/loadout-drawer/LoadoutDrawer.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
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';
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/items/item-search-filter';
import { AppIcon, addIcon, faRandom } from 'app/shell/icons';
Expand Down Expand Up @@ -56,6 +58,7 @@ export default function LoadoutDrawer({
initialLoadout,
storeId,
isNew,
fromExternal,
onClose,
}: {
initialLoadout: Loadout;
Expand All @@ -66,6 +69,7 @@ export default function LoadoutDrawer({
*/
storeId: string;
isNew: boolean;
fromExternal: boolean;
onClose: () => void;
}) {
const dispatch = useThunkDispatch();
Expand All @@ -88,7 +92,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)),
Expand Down Expand Up @@ -170,21 +174,36 @@ export default function LoadoutDrawer({
const toggleAnyClass = (checked: boolean) =>
setLoadout(setClassType(checked ? DestinyClass.Unknown : store.classType));

const showInGameLoadoutIdentifiers =
$featureFlags.editInGameLoadoutIdentifiers &&
(Boolean(loadout.parameters?.inGameIdentifiers) || loadout.items.length > 0);

const header = (
<div>
<LoadoutDrawerHeader loadout={loadout} onNameChanged={handleNameChanged} />
<details className={styles.notes} open={Boolean(loadout.notes?.length)}>
<summary>{t('MovePopup.Notes')}</summary>
<WithSymbolsPicker input={ref} setValue={(val) => setLoadout(setNotes(val))}>
<TextareaAutosize
onChange={handleNotesChanged}
ref={ref}
value={loadout.notes}
maxLength={2048}
placeholder={t('Loadouts.NotesPlaceholder')}
/>
</WithSymbolsPicker>
</details>
<div className={styles.header}>
{showInGameLoadoutIdentifiers && (
<InGameLoadoutIdentifiersSelectButton loadout={loadout} setLoadout={setLoadout} />
)}
<div className={styles.headerDetails}>
{fromExternal && (
<div className={styles.classType}>
<ClassIcon classType={loadout.classType} />
{t('Loadouts.ClassType', { className: store.className, context: store.genderName })}
</div>
)}
<LoadoutDrawerHeader loadout={loadout} onNameChanged={handleNameChanged} />
<details className={styles.notes} open={Boolean(loadout.notes?.length)}>
<summary>{t('MovePopup.Notes')}</summary>
<WithSymbolsPicker input={ref} setValue={(val) => setLoadout(setNotes(val))}>
<TextareaAutosize
onChange={handleNotesChanged}
ref={ref}
value={loadout.notes}
maxLength={2048}
placeholder={t('Loadouts.NotesPlaceholder')}
/>
</WithSymbolsPicker>
</details>
</div>
</div>
);

Expand Down
23 changes: 8 additions & 15 deletions src/app/loadout-drawer/LoadoutDrawerContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 { Loadout } from '../loadout/loadout-types';
import { addItem$, editLoadout$ } from './loadout-events';
import { EditLoadoutState, addItem$, editLoadout$ } from './loadout-events';
import { convertToLoadoutItem, newLoadout, pickBackingStore } from './loadout-utils';

const LoadoutDrawer = lazy(
Expand Down Expand Up @@ -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<EditLoadoutState>();

const handleDrawerClose = useCallback(() => {
setInitialLoadout(undefined);
Expand All @@ -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 =
Expand All @@ -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],
),
Expand Down Expand Up @@ -113,6 +103,7 @@ export default function LoadoutDrawerContainer({ account }: { account: DestinyAc
storeId: owner.id,
isNew: true,
showClass: true,
fromExternal: true,
});
}
},
Expand Down Expand Up @@ -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 });
Expand Down Expand Up @@ -173,6 +165,7 @@ export default function LoadoutDrawerContainer({ account }: { account: DestinyAc
storeId={initialLoadout.storeId}
isNew={initialLoadout.isNew}
onClose={handleDrawerClose}
fromExternal={initialLoadout.fromExternal}
/>
) : (
<D1LoadoutDrawer
Expand Down
13 changes: 0 additions & 13 deletions src/app/loadout-drawer/LoadoutDrawerHeader.m.scss
Original file line number Diff line number Diff line change
@@ -1,18 +1,5 @@
@use '../variables.scss' as *;

.loadoutName {
font-size: 18px;
display: flex;
flex-flow: row nowrap;
align-items: center;
max-width: 50em;

svg,
:global(.app-icon) {
margin-right: 8px;
}
}

.dimInput {
composes: flexRow from '../dim-ui/common.m.scss';
padding: 2px 25px 2px 5px;
Expand Down
1 change: 0 additions & 1 deletion src/app/loadout-drawer/LoadoutDrawerHeader.m.scss.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 14 additions & 18 deletions src/app/loadout-drawer/LoadoutDrawerHeader.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -22,22 +21,19 @@ export default function LoadoutDrawerHeader({
useAutocomplete(inputRef, tags);

return (
<div className={styles.loadoutName}>
<ClassIcon classType={loadout.classType} />
<WithSymbolsPicker className={styles.dimInput} input={inputRef} setValue={onNameChanged}>
<input
name="name"
ref={inputRef}
onChange={setName}
minLength={1}
maxLength={50}
required={true}
autoComplete="off"
type="text"
value={loadout.name}
placeholder={t('Loadouts.LoadoutName')}
/>
</WithSymbolsPicker>
</div>
<WithSymbolsPicker className={styles.dimInput} input={inputRef} setValue={onNameChanged}>
<input
name="name"
ref={inputRef}
onChange={setName}
minLength={1}
maxLength={50}
required={true}
autoComplete="off"
type="text"
value={loadout.name}
placeholder={t('Loadouts.LoadoutName')}
/>
</WithSymbolsPicker>
);
}
9 changes: 8 additions & 1 deletion src/app/loadout-drawer/loadout-drawer-reducer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { LoadoutParameters } from '@destinyitemmanager/dim-api-types';
import { InGameLoadoutIdentifiers, LoadoutParameters } from '@destinyitemmanager/dim-api-types';
import { D1Categories } from 'app/destiny1/d1-bucket-categories';
import { D1ManifestDefinitions } from 'app/destiny1/d1-definitions';
import { D2Categories } from 'app/destiny2/d2-bucket-categories';
Expand Down Expand Up @@ -851,3 +851,10 @@ export function randomizeLoadoutMods(
})(loadout);
});
}

/**
* Set the name/icon/color of this loadout.
*/
export function setInGameLoadoutIdentifiers(identifiers: InGameLoadoutIdentifiers | undefined) {
return setLoadoutParameters({ inGameIdentifiers: identifiers });
}
Loading

0 comments on commit 755bc1a

Please sign in to comment.