<commit_before>import { Fetcher } from "./types";
import getByIds from "../helpers/get-by-ids";

import { IGameSet } from "../types";

import normalize from "../api/normalize";
import { collection, game, arrayOf } from "../api/schemas";

import { sortAndFilter } from "./sort-and-filter";
import { fromJSONField } from "../db/json-field";

import { indexBy, pluck } from "underscore";

import { pathToId } from "../util/navigation";

const emptyArr = [];

export default class CollectionFetcher extends Fetcher {
  async work(): Promise<void> {
    const { db } = this.ctx;

    if (this.hasGames() && !this.warrantsRemote(this.reason)) {
      const { games, gameIds, gamesCount } = this.tabData();
      await this.pushGames(games, gameIds, gamesCount);
      return;
    }

    const path = this.tabData().path;
    const collectionId = +pathToId(path);

    let localCollection = db.collections.findOneById(collectionId);

    if (localCollection) {
      this.push({
        collections: {
          [collectionId]: localCollection,
        },
      });

      let gameIds: number[] = emptyArr;
      if (localCollection && localCollection.gameIds) {
        const localIds = fromJSONField<number[]>(localCollection.gameIds);
        if (localIds) {
          gameIds = localIds;
        }
      }

      const localGames = db.games.all(k => k.select().whereIn("id", gameIds));
      await this.pushGames(
        indexBy(localGames, "id"),
        gameIds,
        localCollection.gamesCount,
      );
    }

    let normalized;
    const collResponse = await this.withApi(async api => {
      return await api.collection(collectionId);
    });

    normalized = normalize(collResponse, {
      collection: collection,
    });
    let remoteCollection = normalized.entities.collections[collectionId];

    this.push({
      collections: {
        [collectionId]: remoteCollection,
      },
    });

    const gamesResponse = await this.withApi(async api => {
      return await api.collectionGames(collectionId);
    });

    normalized = normalize(gamesResponse, {
      games: arrayOf(game),
    });

    const remoteGames = normalized.entities.games;
    const remoteGameIds: number[] = normalized.result.gameIds;

    this.pushGames(remoteGames, remoteGameIds, remoteCollection.gamesCount);
  }

  hasGames(): boolean {
    const gameIds = this.tabData().gameIds || emptyArr;
    return gameIds.length > 0;
  }

  async pushGames(games: IGameSet, gameIds: number[], gamesCount: number) {
    const sortedGames = sortAndFilter(
      getByIds(games, gameIds),
      this.tabId,
      this.ctx.store,
    );

    this.push({
      games: games,
      gameIds: pluck(sortedGames, "id"),
      gamesOffset: 0,
      gamesCount: gamesCount,
    });
  }
}<commit_msg>Don't display ghost game when filtering a collection<commit_after>import { Fetcher } from "./types";
import getByIds from "../helpers/get-by-ids";

import { IGameSet } from "../types";

import normalize from "../api/normalize";
import { collection, game, arrayOf } from "../api/schemas";

import { sortAndFilter } from "./sort-and-filter";
import { fromJSONField } from "../db/json-field";

import { indexBy, pluck } from "underscore";

import { pathToId } from "../util/navigation";

const emptyArr = [];

export default class CollectionFetcher extends Fetcher {
  async work(): Promise<void> {
    const { db } = this.ctx;

    if (this.hasGames() && !this.warrantsRemote(this.reason)) {
      const { games, gameIds, gamesCount } = this.tabData();
      await this.pushGames(games, gameIds, gamesCount);
      return;
    }

    const path = this.tabData().path;
    const collectionId = +pathToId(path);

    let localCollection = db.collections.findOneById(collectionId);

    if (localCollection) {
      this.push({
        collections: {
          [collectionId]: localCollection,
        },
      });

      let gameIds: number[] = emptyArr;
      if (localCollection && localCollection.gameIds) {
        const localIds = fromJSONField<number[]>(localCollection.gameIds);
        if (localIds) {
          gameIds = localIds;
        }
      }

      const localGames = db.games.all(k => k.select().whereIn("id", gameIds));
      await this.pushGames(
        indexBy(localGames, "id"),
        gameIds,
        localCollection.gamesCount,
      );
    }

    let normalized;
    const collResponse = await this.withApi(async api => {
      return await api.collection(collectionId);
    });

    normalized = normalize(collResponse, {
      collection: collection,
    });
    let remoteCollection = normalized.entities.collections[collectionId];

    this.push({
      collections: {
        [collectionId]: remoteCollection,
      },
    });

    const gamesResponse = await this.withApi(async api => {
      return await api.collectionGames(collectionId);
    });

    normalized = normalize(gamesResponse, {
      games: arrayOf(game),
    });

    const remoteGames = normalized.entities.games;
    const remoteGameIds: number[] = normalized.result.gameIds;

    this.pushGames(remoteGames, remoteGameIds, remoteCollection.gamesCount);
  }

  hasGames(): boolean {
    const gameIds = this.tabData().gameIds || emptyArr;
    return gameIds.length > 0;
  }

  async pushGames(games: IGameSet, gameIds: number[], gamesCount: number) {
    const sortedGames = sortAndFilter(
      getByIds(games, gameIds),
      this.tabId,
      this.ctx.store,
    );

    this.push({
      games: games,
      gameIds: pluck(sortedGames, "id"),
      gamesOffset: 0,
      gamesCount: sortedGames.length,
    });
  }
}
{'fraction_non_alphanumeric': 0.13025585972445874, 'fraction_numerical': 0.0007156915369475756, 'mean_word_length': 2.873873873873874, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 15, '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': '5305812', 'n_tokens_mistral': 1692, 'n_tokens_neox': 1648, 'n_words': 371}