<commit_before>import app from '../../forum/app';
import PaginatedListState, { Page } from '../../common/states/PaginatedListState';
import Discussion from '../../common/models/Discussion';

export default class DiscussionListState extends PaginatedListState<Discussion> {
  protected extraDiscussions: Discussion[] = [];

  constructor(params: any, page: number = 1) {
    super(params, page, 20);
  }

  get type(): string {
    return 'discussions';
  }

  requestParams() {
    const params: any = { include: ['user', 'lastPostedUser'], filter: this.params.filter || {} };

    params.sort = this.sortMap()[this.params.sort];

    if (this.params.q) {
      params.filter.q = this.params.q;

      params.include.push('mostRelevantPost', 'mostRelevantPost.user');
    }
    return params;
  }

  protected loadPage(page: number = 1): any {
    const preloadedDiscussions = app.preloadedApiDocument();

    if (preloadedDiscussions) {
      this.initialLoading = false;

      return Promise.resolve(preloadedDiscussions);
    }

    return super.loadPage(page);
  }

  clear() {
    super.clear();

    this.extraDiscussions = [];
  }

  /**
   * Get a map of sort keys (which appear in the URL, and are used for
   * translation) to the API sort value that they represent.
   */
  sortMap() {
    const map: any = {};

    if (this.params.q) {
      map.relevance = '';
    }
    map.latest = '-lastPostedAt';
    map.top = '-commentCount';
    map.newest = '-createdAt';
    map.oldest = 'createdAt';

    return map;
  }

  /**
   * In the last request, has the user searched for a discussion?
   */
  isSearchResults() {
    return !!this.params.q;
  }

  removeDiscussion(discussion: Discussion) {
    for (const page of this.pages) {
      const index = page.items.indexOf(discussion);

      if (index !== -1) {
        page.items.splice(index, 1);
        break;
      }
    }

    const index = this.extraDiscussions.indexOf(discussion);

    if (index !== -1) {
      this.extraDiscussions.splice(index);
    }

    m.redraw();
  }

  /**
   * Add a discussion to the top of the list.
   */
  addDiscussion(discussion: Discussion) {
    this.removeDiscussion(discussion);
    this.extraDiscussions.unshift(discussion);

    m.redraw();
  }

  protected getAllItems(): Discussion[] {
    return this.extraDiscussions.concat(super.getAllItems());
  }

  public getPages(): Page<Discussion>[] {
    const pages = super.getPages();

    if (this.extraDiscussions.length) {
      return [
        {
          number: -1,
          items: this.extraDiscussions,
        },
        ...pages,
      ];
    }

    return pages;
  }
}<commit_msg>chore: better typings for DiscussionListState (#3132)<commit_after>import app from '../../forum/app';
import PaginatedListState, { Page } from '../../common/states/PaginatedListState';
import Discussion from '../../common/models/Discussion';

export interface IRequestParams {
  include: string[];
  filter: Record<string, string>;
  sort?: string;
}

export default class DiscussionListState extends PaginatedListState<Discussion> {
  protected extraDiscussions: Discussion[] = [];

  constructor(params: any, page: number = 1) {
    super(params, page, 20);
  }

  get type(): string {
    return 'discussions';
  }

  requestParams(): IRequestParams {
    const params: IRequestParams = {
      include: ['user', 'lastPostedUser'],
      filter: this.params.filter || {},
    };

    params.sort = this.sortMap()[this.params.sort];

    if (this.params.q) {
      params.filter.q = this.params.q;
      params.include.push('mostRelevantPost', 'mostRelevantPost.user');
    }

    return params;
  }

  protected loadPage(page: number = 1): Promise<Discussion[]> {
    const preloadedDiscussions = app.preloadedApiDocument() as Discussion[] | null;

    if (preloadedDiscussions) {
      this.initialLoading = false;

      return Promise.resolve(preloadedDiscussions);
    }

    return super.loadPage(page);
  }

  clear(): void {
    super.clear();

    this.extraDiscussions = [];
  }

  /**
   * Get a map of sort keys (which appear in the URL, and are used for
   * translation) to the API sort value that they represent.
   */
  sortMap() {
    const map: any = {};

    if (this.params.q) {
      map.relevance = '';
    }
    map.latest = '-lastPostedAt';
    map.top = '-commentCount';
    map.newest = '-createdAt';
    map.oldest = 'createdAt';

    return map;
  }

  /**
   * In the last request, has the user searched for a discussion?
   */
  isSearchResults(): boolean {
    return !!this.params.q;
  }

  removeDiscussion(discussion: Discussion): void {
    for (const page of this.pages) {
      const index = page.items.indexOf(discussion);

      if (index !== -1) {
        page.items.splice(index, 1);
        break;
      }
    }

    const index = this.extraDiscussions.indexOf(discussion);

    if (index !== -1) {
      this.extraDiscussions.splice(index);
    }

    m.redraw();
  }

  /**
   * Add a discussion to the top of the list.
   */
  addDiscussion(discussion: Discussion): void {
    this.removeDiscussion(discussion);
    this.extraDiscussions.unshift(discussion);

    m.redraw();
  }

  protected getAllItems(): Discussion[] {
    return this.extraDiscussions.concat(super.getAllItems());
  }

  public getPages(): Page<Discussion>[] {
    const pages = super.getPages();

    if (this.extraDiscussions.length) {
      return [
        {
          number: -1,
          items: this.extraDiscussions,
        },
        ...pages,
      ];
    }

    return pages;
  }
}
{'fraction_non_alphanumeric': 0.13936342384463227, 'fraction_numerical': 0.0035964754540550262, 'mean_word_length': 2.6834437086092717, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 9, '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': '2878175', 'n_tokens_mistral': 1797, 'n_tokens_neox': 1685, 'n_words': 397}