<commit_before>import React from 'react';
import { t } from 'app/i18next-t';
import { AppIcon, tagIcon } from '../shell/icons';
import { faClone } from '@fortawesome/free-regular-svg-icons';
import { itemTags, getItemInfoSource, TagValue } from '../inventory/dim-item-info';
import { connect } from 'react-redux';
import { RootState } from '../store/reducers';
import { setSearchQuery } from '../shell/actions';
import _ from 'lodash';
import './search-filter.scss';
import { destinyVersionSelector, currentAccountSelector } from '../accounts/reducer';
import { SearchConfig, searchFilterSelector, searchConfigSelector } from './search-filters';
import { setItemState as d1SetItemState } from '../bungie-api/destiny1-api';
import { setLockState as d2SetLockState } from '../bungie-api/destiny2-api';
import { DestinyAccount } from '../accounts/destiny-account.service';
import { D2StoresService } from '../inventory/d2-stores.service';
import { D1StoresService } from '../inventory/d1-stores.service';
import { DimItem } from '../inventory/item-types';
import { StoreServiceType } from '../inventory/store-types';
import { loadingTracker } from '../shell/loading-tracker';
import SearchFilterInput from './SearchFilterInput';
import { showNotification } from '../notifications/notifications';
import NotificationButton from '../notifications/NotificationButton';
import { CompareService } from '../compare/compare.service';

const bulkItemTags = Array.from(itemTags) as any[];
bulkItemTags.shift();
bulkItemTags.unshift({ label: 'Tags.TagItems' });
bulkItemTags.push({ type: 'clear', label: 'Tags.ClearTag' });
bulkItemTags.push({ type: 'lock', label: 'Tags.LockAll' });
bulkItemTags.push({ type: 'unlock', label: 'Tags.UnlockAll' });

interface ProvidedProps {
  mobile?: boolean;
  onClear?(): void;
}

interface StoreProps {
  isPhonePortrait: boolean;
  destinyVersion: 1 | 2;
  account?: DestinyAccount;
  searchConfig: SearchConfig;
  searchFilter(item: DimItem): boolean;
}

const mapDispatchToProps = {
  setSearchQuery
};

type DispatchProps = typeof mapDispatchToProps;

type Props = ProvidedProps & StoreProps & DispatchProps;

interface State {
  showSelect: boolean;
}

function mapStateToProps(state: RootState): StoreProps {
  return {
    isPhonePortrait: state.shell.isPhonePortrait,
    destinyVersion: destinyVersionSelector(state),
    account: currentAccountSelector(state),
    searchConfig: searchConfigSelector(state),
    searchFilter: searchFilterSelector(state)
  };
}

class SearchFilter extends React.Component<Props, State> {
  state: State = { showSelect: false };
  private input = React.createRef<SearchFilterInput>();

  private bulkTag: React.ChangeEventHandler<HTMLSelectElement> = loadingTracker.trackPromise(
    async (e) => {
      this.setState({ showSelect: false });

      const selectedTag = e.currentTarget.value;

      if (selectedTag === 'lock' || selectedTag === 'unlock') {
        // Bulk locking/unlocking

        const state = selectedTag === 'lock';
        const lockables = this.getStoresService()
          .getAllItems()
          .filter((i) => i.lockable && this.props.searchFilter(i));
        try {
          for (const item of lockables) {
            const store =
              item.owner === 'vault'
                ? item.getStoresService().getActiveStore()!
                : item.getStoresService().getStore(item.owner)!;

            if (item.isDestiny2()) {
              await d2SetLockState(store, item, state);
            } else if (item.isDestiny1()) {
              await d1SetItemState(item, store, state, 'lock');
            }

            // TODO: Gotta do this differently in react land
            item.locked = state;
          }
          showNotification({
            type: 'success',
            title: state
              ? t('Filter.LockAllSuccess', { num: lockables.length })
              : t('Filter.UnlockAllSuccess', { num: lockables.length })
          });
        } catch (e) {
          showNotification({
            type: 'error',
            title: state ? t('Filter.LockAllFailed') : t('Filter.UnlockAllFailed'),
            body: e.message
          });
        } finally {
          // Touch the stores service to update state
          if (lockables.length) {
            lockables[0].getStoresService().touch();
          }
        }
      } else {
        // Bulk tagging
        const itemInfoService = await getItemInfoSource(this.props.account!);
        const selectedTagString = bulkItemTags.find(
          (tagInfo) => tagInfo.type && tagInfo.type === selectedTag
        ).label;
        const tagItems = this.getStoresService()
          .getAllItems()
          .filter((i) => i.taggable && this.props.searchFilter(i));
        // existing tags are later passed to buttonEffect so the notif button knows what to revert
        const previousState = tagItems.map((item) => {
          return { item, setTag: item.dimInfo.tag as TagValue | 'clear' | 'lock' | 'unlock' };
        });
        await itemInfoService.bulkSaveByKeys(
          tagItems.map((item) => ({
            key: item.id,
            tag: selectedTag === 'clear' ? undefined : (selectedTag as TagValue)
          }))
        );
        showNotification({
          type: 'success',
          duration: 30000,
          title: t('Header.BulkTag'),
          body: [
            t(selectedTagString === 'Tags.ClearTag' ? 'Filter.BulkClear' : 'Filter.BulkTag', {
              count: tagItems.length,
              tag: t(selectedTagString)
            }),
            <NotificationButton
              key="bulktaggingundobutton"
              type="undo"
              onClick={() => {<commit_msg>async i guess<commit_after>import React from 'react';
import { t } from 'app/i18next-t';
import { AppIcon, tagIcon } from '../shell/icons';
import { faClone } from '@fortawesome/free-regular-svg-icons';
import { itemTags, getItemInfoSource, TagValue } from '../inventory/dim-item-info';
import { connect } from 'react-redux';
import { RootState } from '../store/reducers';
import { setSearchQuery } from '../shell/actions';
import _ from 'lodash';
import './search-filter.scss';
import { destinyVersionSelector, currentAccountSelector } from '../accounts/reducer';
import { SearchConfig, searchFilterSelector, searchConfigSelector } from './search-filters';
import { setItemState as d1SetItemState } from '../bungie-api/destiny1-api';
import { setLockState as d2SetLockState } from '../bungie-api/destiny2-api';
import { DestinyAccount } from '../accounts/destiny-account.service';
import { D2StoresService } from '../inventory/d2-stores.service';
import { D1StoresService } from '../inventory/d1-stores.service';
import { DimItem } from '../inventory/item-types';
import { StoreServiceType } from '../inventory/store-types';
import { loadingTracker } from '../shell/loading-tracker';
import SearchFilterInput from './SearchFilterInput';
import { showNotification } from '../notifications/notifications';
import NotificationButton from '../notifications/NotificationButton';
import { CompareService } from '../compare/compare.service';

const bulkItemTags = Array.from(itemTags) as any[];
bulkItemTags.shift();
bulkItemTags.unshift({ label: 'Tags.TagItems' });
bulkItemTags.push({ type: 'clear', label: 'Tags.ClearTag' });
bulkItemTags.push({ type: 'lock', label: 'Tags.LockAll' });
bulkItemTags.push({ type: 'unlock', label: 'Tags.UnlockAll' });

interface ProvidedProps {
  mobile?: boolean;
  onClear?(): void;
}

interface StoreProps {
  isPhonePortrait: boolean;
  destinyVersion: 1 | 2;
  account?: DestinyAccount;
  searchConfig: SearchConfig;
  searchFilter(item: DimItem): boolean;
}

const mapDispatchToProps = {
  setSearchQuery
};

type DispatchProps = typeof mapDispatchToProps;

type Props = ProvidedProps & StoreProps & DispatchProps;

interface State {
  showSelect: boolean;
}

function mapStateToProps(state: RootState): StoreProps {
  return {
    isPhonePortrait: state.shell.isPhonePortrait,
    destinyVersion: destinyVersionSelector(state),
    account: currentAccountSelector(state),
    searchConfig: searchConfigSelector(state),
    searchFilter: searchFilterSelector(state)
  };
}

class SearchFilter extends React.Component<Props, State> {
  state: State = { showSelect: false };
  private input = React.createRef<SearchFilterInput>();

  private bulkTag: React.ChangeEventHandler<HTMLSelectElement> = loadingTracker.trackPromise(
    async (e) => {
      this.setState({ showSelect: false });

      const selectedTag = e.currentTarget.value;

      if (selectedTag === 'lock' || selectedTag === 'unlock') {
        // Bulk locking/unlocking

        const state = selectedTag === 'lock';
        const lockables = this.getStoresService()
          .getAllItems()
          .filter((i) => i.lockable && this.props.searchFilter(i));
        try {
          for (const item of lockables) {
            const store =
              item.owner === 'vault'
                ? item.getStoresService().getActiveStore()!
                : item.getStoresService().getStore(item.owner)!;

            if (item.isDestiny2()) {
              await d2SetLockState(store, item, state);
            } else if (item.isDestiny1()) {
              await d1SetItemState(item, store, state, 'lock');
            }

            // TODO: Gotta do this differently in react land
            item.locked = state;
          }
          showNotification({
            type: 'success',
            title: state
              ? t('Filter.LockAllSuccess', { num: lockables.length })
              : t('Filter.UnlockAllSuccess', { num: lockables.length })
          });
        } catch (e) {
          showNotification({
            type: 'error',
            title: state ? t('Filter.LockAllFailed') : t('Filter.UnlockAllFailed'),
            body: e.message
          });
        } finally {
          // Touch the stores service to update state
          if (lockables.length) {
            lockables[0].getStoresService().touch();
          }
        }
      } else {
        // Bulk tagging
        const itemInfoService = await getItemInfoSource(this.props.account!);
        const selectedTagString = bulkItemTags.find(
          (tagInfo) => tagInfo.type && tagInfo.type === selectedTag
        ).label;
        const tagItems = this.getStoresService()
          .getAllItems()
          .filter((i) => i.taggable && this.props.searchFilter(i));
        // existing tags are later passed to buttonEffect so the notif button knows what to revert
        const previousState = tagItems.map((item) => {
          return { item, setTag: item.dimInfo.tag as TagValue | 'clear' | 'lock' | 'unlock' };
        });
        await itemInfoService.bulkSaveByKeys(
          tagItems.map((item) => ({
            key: item.id,
            tag: selectedTag === 'clear' ? undefined : (selectedTag as TagValue)
          }))
        );
        showNotification({
          type: 'success',
          duration: 30000,
          title: t('Header.BulkTag'),
          body: [
            t(selectedTagString === 'Tags.ClearTag' ? 'Filter.BulkClear' : 'Filter.BulkTag', {
              count: tagItems.length,
              tag: t(selectedTagString)
            }),
            <NotificationButton
              key="bulktaggingundobutton"
              type="undo"
              onClick={async () => {
{'fraction_non_alphanumeric': 0.1306948109058927, 'fraction_numerical': 0.0038698328935795954, 'mean_word_length': 2.7528052805280527, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, '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': '5193614', 'n_tokens_mistral': 3203, 'n_tokens_neox': 3151, 'n_words': 744}