<commit_before>import {
  DestinyFactionProgression,
  DestinyMilestone,
  DestinyVendorComponent
} from 'bungie-api-ts/destiny2';
import { t } from 'app/i18next-t';
import React from 'react';
import _ from 'lodash';
import { DestinyAccount } from '../accounts/destiny-account.service';
import { characterIsCurrent } from './CharacterTile';
import { Faction } from './Faction';
import { Milestone } from './Milestone';
import { Raid } from './Raid';
import './progress.scss';
import { ProgressProfile, reloadProgress, getProgressStream } from './progress.service';
import WellRestedPerkIcon from './WellRestedPerkIcon';
import { CrucibleRank } from './CrucibleRank';
import ErrorBoundary from '../dim-ui/ErrorBoundary';
import { Loading } from '../dim-ui/Loading';
import { connect } from 'react-redux';
import { RootState } from '../store/reducers';
import { chainComparator, compareBy } from '../comparators';
import { Subscriptions } from '../rx-utils';
import { refresh$ } from '../shell/refresh';
import CollapsibleTitle from '../dim-ui/CollapsibleTitle';
import PresentationNodeRoot from '../collections/PresentationNodeRoot';
import { InventoryBuckets } from '../inventory/inventory-buckets';
import { D2ManifestDefinitions, getDefinitions } from '../destiny2/d2-definitions.service';
import PageWithMenu from 'app/dim-ui/PageWithMenu';
import { DimStore } from 'app/inventory/store-types';
import { sortedStoresSelector } from 'app/inventory/reducer';
import { D2StoresService } from 'app/inventory/d2-stores.service';
import CharacterSelect from 'app/character-select/CharacterSelect';
import { DimItem } from 'app/inventory/item-types';
import { AppIcon } from 'app/shell/icons';
import { faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons';
import Pursuit, { showPursuitAsExpired } from './Pursuit';
import { queueAction } from 'app/inventory/action-queue';
import destinySetsLogo from '../../images/destinySetsLogo.svg';
import braytechLogo from '../../images/braytechLogo.svg';

const factionOrder = [
  611314723, // Vanguard,
  3231773039, // Vanguard Research,
  697030790, // Crucible,
  1021210278, // Gunsmith,

  4235119312, // EDZ Deadzone Scout,
  4196149087, // Titan Field Commander,
  1660497607, // Nessus AI,
  828982195, // Io Researcher,
  3859807381, // Voice of Rasputin,
  2677528157, // Follower of Osiris,
  24856709, // Leviathan,

  469305170, // The Nine,
  1761642340, // Iron Banner,

  2105209711, // New Monarchy,
  1714509342, // Future War Cult,
  3398051042 // Dead Orbit
];

// unfortunately the API's raid .order attribute is odd
const raidOrder = [
  3660836525, // levi
  2986584050, // eow
  2683538554, // sos
  3181387331, // wish
  1342567285, // scourge
  2590427074 // crown
];

interface ProvidedProps {
  account: DestinyAccount;
}

interface StoreProps {
  isPhonePortrait: boolean;
  buckets?: InventoryBuckets;
  defs?: D2ManifestDefinitions;
  stores: DimStore[];
}

type Props = ProvidedProps & StoreProps;

interface State {
  progress?: ProgressProfile;
  selectedStoreId?: string;
}

function mapStateToProps(state: RootState): StoreProps {
  return {
    isPhonePortrait: state.shell.isPhonePortrait,
    stores: sortedStoresSelector(state),
    defs: state.manifest.d2Manifest,
    buckets: state.inventory.buckets
  };
}

const sortQuests = chainComparator(
  compareBy(showPursuitAsExpired),
  compareBy((item: DimItem) => !item.tracked),
  compareBy((item: DimItem) => {
    const objectives = item.objectives;
    const percentComplete = objectives
      ? _.sumBy(objectives, (objective) => {
          if (objective.completionValue) {
            return (
              Math.min(1, (objective.progress || 0) / objective.completionValue) / objectives.length
            );
          } else {
            return 0;
          }
        })
      : 0;
    return percentComplete >= 1;
  }),
  compareBy((item) => {
    return item.isDestiny2() && item.quest && item.quest.expirationDate
      ? item.quest.expirationDate
      : new Date(8640000000000000);
  }),
  compareBy((item) => item.typeName),
  compareBy((item) => {
    return item.icon;
  }),
  compareBy((item) => item.name)
);

const pursuitsOrder = ['Bounties', 'Quests', 'Items'];

class Progress extends React.Component<Props, State> {
  private subscriptions = new Subscriptions();

  constructor(props) {
    super(props);
    this.state = {};
  }

  componentDidMount() {
    if (!this.props.defs) {
      getDefinitions();
    }
<commit_msg>Simplify progress sorting<commit_after>import {
  DestinyFactionProgression,
  DestinyMilestone,
  DestinyVendorComponent
} from 'bungie-api-ts/destiny2';
import { t } from 'app/i18next-t';
import React from 'react';
import _ from 'lodash';
import { DestinyAccount } from '../accounts/destiny-account.service';
import { characterIsCurrent } from './CharacterTile';
import { Faction } from './Faction';
import { Milestone } from './Milestone';
import { Raid } from './Raid';
import './progress.scss';
import { ProgressProfile, reloadProgress, getProgressStream } from './progress.service';
import WellRestedPerkIcon from './WellRestedPerkIcon';
import { CrucibleRank } from './CrucibleRank';
import ErrorBoundary from '../dim-ui/ErrorBoundary';
import { Loading } from '../dim-ui/Loading';
import { connect } from 'react-redux';
import { RootState } from '../store/reducers';
import { chainComparator, compareBy } from '../comparators';
import { Subscriptions } from '../rx-utils';
import { refresh$ } from '../shell/refresh';
import CollapsibleTitle from '../dim-ui/CollapsibleTitle';
import PresentationNodeRoot from '../collections/PresentationNodeRoot';
import { InventoryBuckets } from '../inventory/inventory-buckets';
import { D2ManifestDefinitions, getDefinitions } from '../destiny2/d2-definitions.service';
import PageWithMenu from 'app/dim-ui/PageWithMenu';
import { DimStore } from 'app/inventory/store-types';
import { sortedStoresSelector } from 'app/inventory/reducer';
import { D2StoresService } from 'app/inventory/d2-stores.service';
import CharacterSelect from 'app/character-select/CharacterSelect';
import { DimItem } from 'app/inventory/item-types';
import { AppIcon } from 'app/shell/icons';
import { faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons';
import Pursuit, { showPursuitAsExpired } from './Pursuit';
import { queueAction } from 'app/inventory/action-queue';
import destinySetsLogo from '../../images/destinySetsLogo.svg';
import braytechLogo from '../../images/braytechLogo.svg';

const factionOrder = [
  611314723, // Vanguard,
  3231773039, // Vanguard Research,
  697030790, // Crucible,
  1021210278, // Gunsmith,

  4235119312, // EDZ Deadzone Scout,
  4196149087, // Titan Field Commander,
  1660497607, // Nessus AI,
  828982195, // Io Researcher,
  3859807381, // Voice of Rasputin,
  2677528157, // Follower of Osiris,
  24856709, // Leviathan,

  469305170, // The Nine,
  1761642340, // Iron Banner,

  2105209711, // New Monarchy,
  1714509342, // Future War Cult,
  3398051042 // Dead Orbit
];

// unfortunately the API's raid .order attribute is odd
const raidOrder = [
  3660836525, // levi
  2986584050, // eow
  2683538554, // sos
  3181387331, // wish
  1342567285, // scourge
  2590427074 // crown
];

interface ProvidedProps {
  account: DestinyAccount;
}

interface StoreProps {
  isPhonePortrait: boolean;
  buckets?: InventoryBuckets;
  defs?: D2ManifestDefinitions;
  stores: DimStore[];
}

type Props = ProvidedProps & StoreProps;

interface State {
  progress?: ProgressProfile;
  selectedStoreId?: string;
}

function mapStateToProps(state: RootState): StoreProps {
  return {
    isPhonePortrait: state.shell.isPhonePortrait,
    stores: sortedStoresSelector(state),
    defs: state.manifest.d2Manifest,
    buckets: state.inventory.buckets
  };
}

const sortQuests = chainComparator(
  compareBy(showPursuitAsExpired),
  compareBy((item) => !item.tracked),
  compareBy((item) => item.complete),
  compareBy((item) => {
    return item.isDestiny2() && item.quest && item.quest.expirationDate
      ? item.quest.expirationDate
      : new Date(8640000000000000);
  }),
  compareBy((item) => item.typeName),
  compareBy((item) => item.icon),
  compareBy((item) => item.name)
);

const pursuitsOrder = ['Bounties', 'Quests', 'Items'];

class Progress extends React.Component<Props, State> {
  private subscriptions = new Subscriptions();

  constructor(props) {
    super(props);
    this.state = {};
  }

  componentDidMount() {
    if (!this.props.defs) {
      getDefinitions();
    }

{'fraction_non_alphanumeric': 0.1352996254681648, 'fraction_numerical': 0.056999063670411985, 'mean_word_length': 4.0, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3841370', 'n_tokens_mistral': 3039, 'n_tokens_neox': 2701, 'n_words': 629}