Skip to content

Commit

Permalink
Sorts items by Completion %. (#9643)
Browse files Browse the repository at this point in the history
  • Loading branch information
grySal committed Jul 29, 2023
1 parent 44db38b commit 3a51a5d
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 13 deletions.
1 change: 1 addition & 0 deletions config/i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -1273,6 +1273,7 @@
"Triumphs": {
"HideCompleted": "Hide completed triumphs",
"RevealRedacted": "Reveal redacted triumphs",
"SortRecords": "Sort triumphs by completion",
"GildingTriumph": "Gilding Triumph"
},
"Vendors": {
Expand Down
1 change: 1 addition & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## Next

* You can now search for Emotes and Ghost Projections on the Records page.
* Added button to sort triumphs by completion.
* Greatly expanded the "Randomize Loadout" feature. You can now randomize a Loadout's subclass and its configuration, weapons, armor, cosmetics, and armor mods.
* Randomize them individually through the three dots in a Loadout section.
* Randomize the entire Loadout using the "Randomize" button at the bottom of the Loadout drawer.
Expand Down
2 changes: 2 additions & 0 deletions src/app/records/PresentationNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export default function PresentationNode({
const defs = useD2Definitions()!;
const completedRecordsHidden = useSelector(settingSelector('completedRecordsHidden'));
const redactedRecordsRevealed = useSelector(settingSelector('redactedRecordsRevealed'));
const sortRecordProgression = useSelector(settingSelector('sortRecordProgression'));
const presentationNodeHash = node.hash;
const headerRef = useScrollNodeIntoView(path, presentationNodeHash);

Expand Down Expand Up @@ -129,6 +130,7 @@ export default function PresentationNode({
ownedItemHashes={ownedItemHashes}
completedRecordsHidden={completedRecordsHidden}
redactedRecordsRevealed={redactedRecordsRevealed}
sortRecordProgression={sortRecordProgression}
/>
)}
</div>
Expand Down
83 changes: 73 additions & 10 deletions src/app/records/PresentationNodeLeaf.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import { VendorItemDisplay } from 'app/vendors/VendorItemComponent';
import { DestinyCollectibleState, DestinyRecordState } from 'bungie-api-ts/destiny2';
import { sortBy } from 'lodash';
import Collectible from './Collectible';
import CollectiblesGrid from './CollectiblesGrid';
import Craftable from './Craftable';
import Metrics from './Metrics';
import { RecordGrid } from './Record';
import { DimPresentationNodeLeaf } from './presentation-nodes';
import {
DimCollectible,
DimMetric,
DimPresentationNodeLeaf,
DimRecord,
} from './presentation-nodes';

/**
* Displays "leaf node" contents for presentation nodes (collectibles, triumphs, metrics)
Expand All @@ -14,35 +21,41 @@ export default function PresentationNodeLeaf({
ownedItemHashes,
completedRecordsHidden,
redactedRecordsRevealed,
sortRecordProgression,
}: {
node: DimPresentationNodeLeaf;
ownedItemHashes?: Set<number>;
completedRecordsHidden: boolean;
redactedRecordsRevealed: boolean;
sortRecordProgression: boolean;
}) {
return (
<>
{node.collectibles && node.collectibles.length > 0 && (
<CollectiblesGrid>
{node.collectibles.map((collectible) => (
<Collectible
key={collectible.key}
collectible={collectible}
owned={Boolean(ownedItemHashes?.has(collectible.item.hash))}
/>
))}
{(sortRecordProgression ? sortCollectibles(node.collectibles) : node.collectibles).map(
(collectible) => (
<Collectible
key={collectible.key}
collectible={collectible}
owned={Boolean(ownedItemHashes?.has(collectible.item.hash))}
/>
)
)}
</CollectiblesGrid>
)}

{node.records && node.records.length > 0 && (
<RecordGrid
records={node.records}
records={sortRecordProgression ? sortRecords(node.records) : node.records}
completedRecordsHidden={completedRecordsHidden}
redactedRecordsRevealed={redactedRecordsRevealed}
/>
)}

{node.metrics && node.metrics.length > 0 && <Metrics metrics={node.metrics} />}
{node.metrics && node.metrics.length > 0 && (
<Metrics metrics={sortRecordProgression ? sortMetrics(node.metrics) : node.metrics} />
)}

{node.craftables && node.craftables.length > 0 && (
<CollectiblesGrid>
Expand All @@ -62,3 +75,53 @@ export default function PresentationNodeLeaf({
</>
);
}

function sortRecords(records: DimRecord[]): DimRecord[] {
return sortBy(records, (record) => {
// Triumph is already completed so move it to back of list.
if (
record.recordComponent.state & DestinyRecordState.RecordRedeemed ||
record.recordComponent.state & DestinyRecordState.CanEquipTitle ||
!record.recordComponent.state
) {
return -1;
}

// check which key is used to track progress
let objectives;
if (record.recordComponent.intervalObjectives) {
objectives = record.recordComponent.intervalObjectives;
} else if (record.recordComponent.objectives) {
objectives = record.recordComponent.objectives;
} else {
// its a legacy triumph so it has no objectives and is not completed
return 0;
}

// Sum up the progress
let totalProgress = 0;
for (const x of objectives) {
totalProgress += Math.min(1, x.progress! / x.completionValue);
}
return totalProgress / objectives.length;
}).reverse();
}

function sortCollectibles(collectibles: DimCollectible[]): DimCollectible[] {
return sortBy(collectibles, (collectible) => {
if (collectible.state & DestinyCollectibleState.NotAcquired) {
return -1;
}
return 0;
});
}

function sortMetrics(metrics: DimMetric[]): DimMetric[] {
return sortBy(metrics, (metric) => {
const objectives = metric.metricComponent.objectiveProgress;
if (objectives.complete) {
return -1;
}
return objectives.progress! / objectives.completionValue;
}).reverse();
}
4 changes: 3 additions & 1 deletion src/app/records/PresentationNodeSearchResults.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default function PresentationNodeSearchResults({
// TODO: make each node in path linkable
const completedRecordsHidden = useSelector(settingSelector('completedRecordsHidden'));
const redactedRecordsRevealed = useSelector(settingSelector('redactedRecordsRevealed'));

const sortRecordProgression = useSelector(settingSelector('sortRecordProgression'));
return (
<div>
{searchResults.map((sr) => (
Expand Down Expand Up @@ -46,6 +46,7 @@ export default function PresentationNodeSearchResults({
ownedItemHashes={ownedItemHashes}
completedRecordsHidden={completedRecordsHidden}
redactedRecordsRevealed={redactedRecordsRevealed}
sortRecordProgression={sortRecordProgression}
/>
);
})()}
Expand All @@ -54,6 +55,7 @@ export default function PresentationNodeSearchResults({
ownedItemHashes={ownedItemHashes}
completedRecordsHidden={completedRecordsHidden}
redactedRecordsRevealed={redactedRecordsRevealed}
sortRecordProgression={sortRecordProgression}
/>
</div>
</div>
Expand Down
10 changes: 9 additions & 1 deletion src/app/records/Records.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export default function Records({ account }: Props) {
const [completedRecordsHidden, setCompletedRecordsHidden] = useSetting('completedRecordsHidden');
const [redactedRecordsRevealed, setRedactedRecordsRevealed] =
useSetting('redactedRecordsRevealed');
const [sortRecordProgression, setSortRecordProgression] = useSetting('sortRecordProgression');

const defs = useD2Definitions();

Expand Down Expand Up @@ -107,7 +108,7 @@ export default function Records({ account }: Props) {

const onToggleCompletedRecordsHidden = (checked: boolean) => setCompletedRecordsHidden(checked);
const onToggleRedactedRecordsRevealed = (checked: boolean) => setRedactedRecordsRevealed(checked);

const onToggleSortRecordProgression = (checked: boolean) => setSortRecordProgression(checked);
return (
<PageWithMenu className="d2-vendors">
<PageWithMenu.Menu>
Expand Down Expand Up @@ -135,6 +136,13 @@ export default function Records({ account }: Props) {
>
{t('Triumphs.RevealRedacted')}
</CheckButton>
<CheckButton
name="sort-progression"
checked={sortRecordProgression}
onChange={onToggleSortRecordProgression}
>
{t('Triumphs.SortRecords')}
</CheckButton>
</div>
</PageWithMenu.Menu>

Expand Down
2 changes: 2 additions & 0 deletions src/app/settings/initial-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ export interface Settings extends DimApiSettings {
language: DimLanguage;
loIncludeVendorItems: boolean;
theme: string;
sortRecordProgression: boolean;
}

export const initialSettingsState: Settings = {
...defaultSettings,
loIncludeVendorItems: false,
language: defaultLanguage(),
theme: 'default',
sortRecordProgression: false,
};
3 changes: 2 additions & 1 deletion src/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1262,7 +1262,8 @@
"Triumphs": {
"GildingTriumph": "Gilding Triumph",
"HideCompleted": "Hide completed triumphs",
"RevealRedacted": "Reveal redacted triumphs"
"RevealRedacted": "Reveal redacted triumphs",
"SortRecords": "Sort triumphs by completion"
},
"Vendors": {
"Collections": "Collections",
Expand Down

0 comments on commit 3a51a5d

Please sign in to comment.