<commit_before>import React, { Component, createRef } from 'react';
import _ from 'lodash';
import Mousetrap from 'mousetrap';
import urbitOb from 'urbit-ob';
import { Sigil } from '~/logic/lib/sigil';
import { PatpNoSig, Path } from '~/types/noun';
import { Groups} from '~/types/group-update';
import { Rolodex, Contact } from '~/types/contact-update';
import { Associations } from '~/types/metadata-update';

export interface Invites {
  ships: PatpNoSig[];
  groups: string[][];
}

interface InviteSearchProps {
  groups: Groups;
  contacts: Rolodex;
  groupResults: boolean;
  shipResults: boolean;
  invites: Invites;
  setInvite: (inv: Invites) => void;
  disabled?: boolean;
	associations?: Associations;
}

interface InviteSearchState {
  groups: string[][];
  peers: PatpNoSig[];
  contacts: Map<PatpNoSig, string[]>;
  searchValue: string;
  searchResults: Invites;
  selected: PatpNoSig | Path | null;
  inviteError: boolean;
}

export class InviteSearch extends Component<
  InviteSearchProps,
  InviteSearchState
> {
  textarea: React.RefObject<HTMLTextAreaElement> = createRef();
  constructor(props) {
    super(props);
    this.state = {
      groups: [],
      peers: [],
      contacts: new Map(),
      searchValue: '',
      searchResults: {
        groups: [],
        ships: [],
      },
      selected: null,
      inviteError: false,
    };
    this.search = this.search.bind(this);
  }

  componentDidMount() {
    this.peerUpdate();
    this.bindShortcuts();
  }

  componentDidUpdate(prevProps) {
    if (prevProps !== this.props) {
      this.peerUpdate();
    }
  }

  peerUpdate() {
    const groups = Array.from(Object.keys(this.props.contacts))
      .filter((e) => !e.startsWith('/~/'))
      .map((e) => {
        const eachGroup: Path[] = [];
        eachGroup.push(e);
        if (this.props.associations) {
          let name = e;
          if (e in this.props.associations) {
            name =
              this.props.associations[e].metadata.title !== ''
                ? this.props.associations[e].metadata.title
                : e;
          }
          eachGroup.push(name);
        }
        return Array.from(eachGroup);
      });

    let peers: PatpNoSig[] = [];
    const peerSet = new Set<PatpNoSig>();
    const contacts = new Map();

    _.map(this.props.groups, (group, path) => {
      if (group.members.size > 0) {
        const groupEntries = group.members.values();
        for (const member of groupEntries) {
          peerSet.add(member);
        }
      }

	    const groupContacts = this.props.contacts[path];

      if (groupContacts) {
        const groupEntries = group.members.values();
        for (const member of groupEntries) {
          if (groupContacts[member]) {
            if (contacts.has(member)) {
              contacts
                .get(member)
                .push(groupContacts[member].nickname);
            } else {
              contacts.set(member, [
                groupContacts[member].nickname,
              ]);
            }
          }
        }
      }
    });
    peers = Array.from(peerSet);

    this.setState({ groups: groups, peers: peers, contacts: contacts });
  }

  search(event) {
    const searchTerm = event.target.value.toLowerCase().replace('~', '');
    const { state, props } = this;

    this.setState({ searchValue: event.target.value });

    if (searchTerm.length < 1) {
      this.setState({ searchResults: { groups: [], ships: [] } });
    }

    if (searchTerm.length > 0) {
      if (state.inviteError === true) {
        this.setState({ inviteError: false });
      }

      let groupMatches = !props.groupResults ? [] :
        state.groups.filter((e) => {
          return (
            e[0].includes(searchTerm) || e[1].toLowerCase().includes(searchTerm)
          );
        });

      let shipMatches = !props.shipResults ? []  :
         state.peers.filter((e) => {
          return (
            e.includes(searchTerm) && !props.invites.ships.includes(e)
          );
        });

        for (const contact of state.contacts.keys()) {
          const thisContact = state.contacts.get(contact) || [];
          const match = thisContact.filter((e) => {
            return e.toLowerCase().includes(searchTerm);
          });
          if (match.length > 0) {
            if (!(contact in shipMatches)) {
              shipMatches.push(contact);
            }
          }
        }

        let isValid = true;
        if (!urbitOb.isValidPatp('~' + searchTerm)) {
          isValid = false;
        }

        if (props.shipResults && isValid && shipMatches.findIndex((s) => s === searchTerm) < 0) {
          shipMatches.unshift(searchTerm);
        }

      const { selected } = state;
      const groupIdx = groupMatches.findIndex(([path]) => path === selected);
      const shipIdx = shipMatches.findIndex((ship) => ship === selected);
      const staleSelection = groupIdx < 0 && shipIdx < 0;
      if (!selected || staleSelection) {
        const newSelection = _.get(groupMatches, '[0][0]') || shipMatches[0];
        this.setState({ selected: newSelection });
      }

      if (searchTerm.length < 3) {
        groupMatches = groupMatches
          .filter(([, name]) =>
            name
              .toLowerCase()<commit_msg>all: exclude ships from invite search when shipResults is false<commit_after>import React, { Component, createRef } from 'react';
import _ from 'lodash';
import Mousetrap from 'mousetrap';
import urbitOb from 'urbit-ob';
import { Sigil } from '~/logic/lib/sigil';
import { PatpNoSig, Path } from '~/types/noun';
import { Groups} from '~/types/group-update';
import { Rolodex, Contact } from '~/types/contact-update';
import { Associations } from '~/types/metadata-update';

export interface Invites {
  ships: PatpNoSig[];
  groups: string[][];
}

interface InviteSearchProps {
  groups: Groups;
  contacts: Rolodex;
  groupResults: boolean;
  shipResults: boolean;
  invites: Invites;
  setInvite: (inv: Invites) => void;
  disabled?: boolean;
	associations?: Associations;
}

interface InviteSearchState {
  groups: string[][];
  peers: PatpNoSig[];
  contacts: Map<PatpNoSig, string[]>;
  searchValue: string;
  searchResults: Invites;
  selected: PatpNoSig | Path | null;
  inviteError: boolean;
}

export class InviteSearch extends Component<
  InviteSearchProps,
  InviteSearchState
> {
  textarea: React.RefObject<HTMLTextAreaElement> = createRef();
  constructor(props) {
    super(props);
    this.state = {
      groups: [],
      peers: [],
      contacts: new Map(),
      searchValue: '',
      searchResults: {
        groups: [],
        ships: [],
      },
      selected: null,
      inviteError: false,
    };
    this.search = this.search.bind(this);
  }

  componentDidMount() {
    this.peerUpdate();
    this.bindShortcuts();
  }

  componentDidUpdate(prevProps) {
    if (prevProps !== this.props) {
      this.peerUpdate();
    }
  }

  peerUpdate() {
    const groups = Array.from(Object.keys(this.props.contacts))
      .filter((e) => !e.startsWith('/~/'))
      .map((e) => {
        const eachGroup: Path[] = [];
        eachGroup.push(e);
        if (this.props.associations) {
          let name = e;
          if (e in this.props.associations) {
            name =
              this.props.associations[e].metadata.title !== ''
                ? this.props.associations[e].metadata.title
                : e;
          }
          eachGroup.push(name);
        }
        return Array.from(eachGroup);
      });

    let peers: PatpNoSig[] = [];
    const peerSet = new Set<PatpNoSig>();
    const contacts = new Map();

    _.map(this.props.groups, (group, path) => {
      if (group.members.size > 0) {
        const groupEntries = group.members.values();
        for (const member of groupEntries) {
          peerSet.add(member);
        }
      }

	    const groupContacts = this.props.contacts[path];

      if (groupContacts) {
        const groupEntries = group.members.values();
        for (const member of groupEntries) {
          if (groupContacts[member]) {
            if (contacts.has(member)) {
              contacts
                .get(member)
                .push(groupContacts[member].nickname);
            } else {
              contacts.set(member, [
                groupContacts[member].nickname,
              ]);
            }
          }
        }
      }
    });
    peers = Array.from(peerSet);

    this.setState({ groups: groups, peers: peers, contacts: contacts });
  }

  search(event) {
    const searchTerm = event.target.value.toLowerCase().replace('~', '');
    const { state, props } = this;

    this.setState({ searchValue: event.target.value });

    if (searchTerm.length < 1) {
      this.setState({ searchResults: { groups: [], ships: [] } });
    }

    if (searchTerm.length > 0) {
      if (state.inviteError === true) {
        this.setState({ inviteError: false });
      }

      let groupMatches = !props.groupResults ? [] :
        state.groups.filter((e) => {
          return (
            e[0].includes(searchTerm) || e[1].toLowerCase().includes(searchTerm)
          );
        });

      let shipMatches = !props.shipResults ? []  :
         state.peers.filter((e) => {
          return (
            e.includes(searchTerm) && !props.invites.ships.includes(e)
          );
        });

      for (const contact of state.contacts.keys()) {
        const thisContact = state.contacts.get(contact) || [];
        const match = thisContact.filter((e) => {
          return e.toLowerCase().includes(searchTerm);
        });
        if (match.length > 0) {
          if (!(contact in shipMatches) && props.shipResults) {
            shipMatches.push(contact);
          }
        }
      }

      let isValid = true;
      if (!urbitOb.isValidPatp('~' + searchTerm)) {
        isValid = false;
      }

      if (props.shipResults && isValid && shipMatches.findIndex((s) => s === searchTerm) < 0) {
        shipMatches.unshift(searchTerm);
      }

      const { selected } = state;
      const groupIdx = groupMatches.findIndex(([path]) => path === selected);
      const shipIdx = shipMatches.findIndex((ship) => ship === selected);
      const staleSelection = groupIdx < 0 && shipIdx < 0;
      if (!selected || staleSelection) {
        const newSelection = _.get(groupMatches, '[0][0]') || shipMatches[0];
        this.setState({ selected: newSelection });
      }

      if (searchTerm.length < 3) {
        groupMatches = groupMatches
          .filter(([, name]) =>
            name
              .toLowerCase()
{'fraction_non_alphanumeric': 0.14411347517730497, 'fraction_numerical': 0.002458628841607565, 'mean_word_length': 2.2116610993015486, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 39, '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': '1883216', 'n_tokens_mistral': 3190, 'n_tokens_neox': 3151, 'n_words': 655}