ronantakizawa/codereview-bench huggingface dataset:
Repository: hydralauncher/hydra
File: src/renderer/pages/game-details/hero-panel.tsx (TypeScript)

Code before:
import { Button } from "@renderer/components";
import { useDownload, useLibrary } from "@renderer/hooks";
import type { Game, ShopDetails } from "@types";

import * as styles from "./hero-panel.css";
import { formatDownloadProgress } from "@renderer/helpers";
import { BinaryNotFoundModal } from "../shared-modals/binary-not-found-modal";

export interface HeroPanelProps {
  game: Game | null;
  gameDetails: ShopDetails | null;
  color: string;
  openRepacksModal: () => void;
  getGame: () => void;
}

export function HeroPanel({
  game,
  gameDetails,
  color,
  openRepacksModal,
  getGame,
}: HeroPanelProps) {
  const { t } = useTranslation("game_details");

  const [showBinaryNotFoundModal, setShowBinaryNotFoundModal] = useState<boolean>(false);

  const {
    game: gameDownloading,
    isDownloading,
    progress,
    eta,
    numPeers,
    numSeeds,
    resumeDownload,
    pauseDownload,
    cancelDownload,
    deleteGame,
    removeGame,
    isGameDeleting,
  } = useDownload();
  const { updateLibrary } = useLibrary();

  const isGameDownloading = isDownloading && gameDownloading?.id === game?.id;

  const openGame = (gameId: number) =>
    window.electron.openGame(gameId).then(res => {
      if (!res) setShowBinaryNotFoundModal(true);
      updateLibrary();
    });


Review comment (suggestion):
No need tao pass `boolean` as a generic, since TypeScript can infer the type from the default value

Code after:
import { Button } from "@renderer/components";
import { useDownload, useLibrary } from "@renderer/hooks";
import type { Game, ShopDetails } from "@types";

import * as styles from "./hero-panel.css";
import { formatDownloadProgress } from "@renderer/helpers";
import { BinaryNotFoundModal } from "../shared-modals/binary-not-found-modal";

export interface HeroPanelProps {
  game: Game | null;
  gameDetails: ShopDetails | null;
  color: string;
  openRepacksModal: () => void;
  getGame: () => void;
}

export function HeroPanel({
  game,
  gameDetails,
  color,
  openRepacksModal,
  getGame,
}: HeroPanelProps) {
  const { t } = useTranslation("game_details");

  const [showBinaryNotFoundModal, setShowBinaryNotFoundModal] = useState(false);

  const {
    game: gameDownloading,
    isDownloading,
    progress,
    eta,
    numPeers,
    numSeeds,
    resumeDownload,
    pauseDownload,
    cancelDownload,
    deleteGame,
    removeGame,
    isGameDeleting,
  } = useDownload();
  const { updateLibrary } = useLibrary();

  const isGameDownloading = isDownloading && gameDownloading?.id === game?.id;

  const openGame = (gameId: number) =>
    window.electron.openGame(gameId).then(isBinaryInPath => {
      if (!isBinaryInPath) setShowBinaryNotFoundModal(true);
      updateLibrary();
    });
Repository: hydralauncher/hydra
File: src/main/services/steam-250.ts (TypeScript)

Code before:

      return Array.from(document.querySelectorAll(".appline .title a"))
        .map(($title: HTMLAnchorElement) => {
          const steamGameUrl = $title.href;
          if (!steamGameUrl) return null;

          return {
            title: $title.textContent,
            objectID: steamGameUrl.split("/").pop(),
          } as Steam250Game;
        })
        .filter((game) => game != null);
    })
    .catch((_) => [] as Steam250Game[]);
};

const steam250Paths = [
  "/hidden_gems",
  `/${new Date().getFullYear()}`,
  "/top250",
  "/most_played",
];

export const getSteam250List = async () => {
  const gamesPromises = steam250Paths.map((path) => requestSteam250(path));
  const gamesList = (await Promise.all(gamesPromises)).flat();

  const gamesMap: Map<string, Steam250Game> = gamesList.reduce((map, item) => {
    map.set(item.objectID, item);
    return map;
  }, new Map());

  return [...gamesMap.values()];
};


Review comment (suggestion):
Just turn these two lines into one:
```ts
await Promise.all(steam250Paths.map((path) => requestSteam250(path)));
```



Code after:

      return Array.from(document.querySelectorAll(".appline .title a"))
        .map(($title: HTMLAnchorElement) => {
          const steamGameUrl = $title.href;
          if (!steamGameUrl) return null;

          return {
            title: $title.textContent,
            objectID: steamGameUrl.split("/").pop(),
          } as Steam250Game;
        })
        .filter((game) => game != null);
    })
    .catch((_) => [] as Steam250Game[]);
};

const steam250Paths = [
  "/hidden_gems",
  `/${new Date().getFullYear()}`,
  "/top250",
  "/most_played",
];

export const getSteam250List = async () => {
  const gamesList = (
    await Promise.all(steam250Paths.map((path) => requestSteam250(path)))
  ).flat();

  const gamesMap: Map<string, Steam250Game> = gamesList.reduce((map, item) => {
    map.set(item.objectID, item);
    return map;
  }, new Map());

  return [...gamesMap.values()];
};
Repository: hydralauncher/hydra
File: src/main/events/catalogue/get-random-game.ts (TypeScript)

Code before:
import shuffle from "lodash/shuffle";

import { Steam250Game, getSteam250List } from "@main/services";

import { registerEvent } from "../register-event";
import { searchGames, searchRepacks } from "../helpers/search-games";

let gamesList: Steam250Game[] = [];
let nextGameIndex = 0;

const getRandomGame = async (_event: Electron.IpcMainInvokeEvent) => {
  if (gamesList.length == 0) {
    const steam250List = await getSteam250List();

    const filteredSteam250List = steam250List.filter((game) => {
      const repacks = searchRepacks(game.title);
      const catalogue = searchGames({ query: game.title });

      return repacks.length && catalogue.length;
    });

    gamesList = shuffle(filteredSteam250List);
  }

  if (gamesList.length == 0) {
    return "";
  }

  const resultObjectId = gamesList[nextGameIndex].objectID;

  nextGameIndex += 1;

  if (nextGameIndex == gamesList.length) {

Review comment (suggestion):
Consider using an object instead:
```ts
const state = { games: [], index: 0 };
```

I think it would be clearer, and you wouldn't use variables

Code after:
import shuffle from "lodash/shuffle";

import { Steam250Game, getSteam250List } from "@main/services";

import { registerEvent } from "../register-event";
import { searchGames, searchRepacks } from "../helpers/search-games";

const state = { games: Array<Steam250Game>(), index: 0 };

const getRandomGame = async (_event: Electron.IpcMainInvokeEvent) => {
  if (state.games.length == 0) {
    const steam250List = await getSteam250List();

    const filteredSteam250List = steam250List.filter((game) => {
      const repacks = searchRepacks(game.title);
      const catalogue = searchGames({ query: game.title });

      return repacks.length && catalogue.length;
    });

    state.games = shuffle(filteredSteam250List);
  }

  if (state.games.length == 0) {
    return "";
  }

  const resultObjectId = state.games[state.index].objectID;

  state.index += 1;

  if (state.index == state.games.length) {
    state.index = 0;
Repository: hydralauncher/hydra
File: src/renderer/src/pages/settings/settings-general.tsx (TypeScript)

Code before:
    }
  }

  return (
    <>
      <div className={styles.downloadsPathField}>
        <TextField
          label={t("downloads_path")}
          value={form.downloadsPath}
          readOnly
          disabled
        />

        <Button
          style={{ alignSelf: "flex-end" }}
          theme="outline"
          onClick={handleChooseDownloadsPath}
        >
          {t("change")}
        </Button>
      </div>

      <h3>{t("language")}</h3>
      <>
        <Select value={form.language} onChange={handleLanguageChange}>
          {Object.keys(languageResources).map((language) => (
            <option key={language} value={language}>
              {ISO6391.getNativeName(language)}
            </option>
          ))}
        </Select>
      </>

      <h3>{t("notifications")}</h3>
      <>
        <CheckboxField
          label={t("enable_download_notifications")}
          checked={form.downloadNotificationsEnabled}
          onChange={() =>
            handleChange({
              downloadNotificationsEnabled: !form.downloadNotificationsEnabled,
            })
          }
        />

        <CheckboxField
          label={t("enable_repack_list_notifications")}
          checked={form.repackUpdatesNotificationsEnabled}
          onChange={() =>
            handleChange({
              repackUpdatesNotificationsEnabled:

Review comment (refactor):
Could you move this to a useState? This way you can also sort the languageOptions. Something like this:

![image](https://github.com/hydralauncher/hydra/assets/167933696/075c69b9-5dda-424b-8140-5ee7392ab5d0)


Code after:
      defaultPath: form.downloadsPath,
      properties: ["openDirectory"],
    });

    if (filePaths && filePaths.length > 0) {
      const path = filePaths[0];
      handleChange({ downloadsPath: path });
    }
  };

  function updateFormWithUserPreferences() {
    if (userPreferences) {
      const parsedLanguage = userPreferences.language.split("-")[0];

      setForm((prev) => ({
        ...prev,
        downloadsPath: userPreferences.downloadsPath ?? defaultDownloadsPath,
        downloadNotificationsEnabled:
          userPreferences.downloadNotificationsEnabled,
        repackUpdatesNotificationsEnabled:
          userPreferences.repackUpdatesNotificationsEnabled,
        language: parsedLanguage,
      }));
    }
  }

  return (
    <>
      <div className={styles.downloadsPathField}>
        <TextField
          label={t("downloads_path")}
          value={form.downloadsPath}
          readOnly
          disabled
        />

        <Button
          style={{ alignSelf: "flex-end" }}
          theme="outline"
          onClick={handleChooseDownloadsPath}
        >
          {t("change")}
        </Button>
      </div>

      <h3>{t("language")}</h3>
      <>
        <Select
          value={form.language}
          onChange={handleLanguageChange}
          options={languageOptions.map((language) => ({
Repository: hydralauncher/hydra
File: src/renderer/src/pages/settings/settings-general.tsx (TypeScript)

Code before:
    }
  }

  return (
    <>
      <div className={styles.downloadsPathField}>
        <TextField
          label={t("downloads_path")}
          value={form.downloadsPath}
          readOnly
          disabled
        />

        <Button
          style={{ alignSelf: "flex-end" }}
          theme="outline"
          onClick={handleChooseDownloadsPath}
        >
          {t("change")}
        </Button>
      </div>

      <h3>{t("language")}</h3>
      <>
        <Select value={form.language} onChange={handleLanguageChange}>
          {languageOptions.map((language) => (
            <option key={language.option} value={language.option}>
              {language.nativeName}
            </option>
          ))}
        </Select>
      </>

      <h3>{t("notifications")}</h3>
      <>
        <CheckboxField
          label={t("enable_download_notifications")}
          checked={form.downloadNotificationsEnabled}
          onChange={() =>
            handleChange({
              downloadNotificationsEnabled: !form.downloadNotificationsEnabled,
            })
          }
        />

        <CheckboxField
          label={t("enable_repack_list_notifications")}
          checked={form.repackUpdatesNotificationsEnabled}
          onChange={() =>
            handleChange({
              repackUpdatesNotificationsEnabled:

Review comment (suggestion):
You can potentially convert this into an `options` prop, since the `Select` children will always be `<option />`
For instance:
```tsx
<Select options={[ { value: "en", label: "English" } ]} />
```

Code after:
        language: parsedLanguage,
      }));
    }
  }

  return (
    <>
      <div className={styles.downloadsPathField}>
        <TextField
          label={t("downloads_path")}
          value={form.downloadsPath}
          readOnly
          disabled
        />

        <Button
          style={{ alignSelf: "flex-end" }}
          theme="outline"
          onClick={handleChooseDownloadsPath}
        >
          {t("change")}
        </Button>
      </div>

      <h3>{t("language")}</h3>
      <>
        <Select
          value={form.language}
          onChange={handleLanguageChange}
          options={languageOptions.map((language) => ({
            key: language.option,
            value: language.option,
            label: language.nativeName,
          }))}
        />
      </>

      <h3>{t("notifications")}</h3>
      <>
        <CheckboxField
          label={t("enable_download_notifications")}
          checked={form.downloadNotificationsEnabled}
          onChange={() =>
            handleChange({
              downloadNotificationsEnabled: !form.downloadNotificationsEnabled,
            })
          }
        />

        <CheckboxField
          label={t("enable_repack_list_notifications")}
Repository: hydralauncher/hydra
File: src/renderer/src/pages/game-details/modals/game-options-modal.tsx (TypeScript)

Code before:
import { debounce } from "lodash-es";

export interface GameOptionsModalProps {
  visible: boolean;
  game: Game;
  onClose: () => void;
}

export function GameOptionsModal({
  visible,
  game,
  onClose,
}: GameOptionsModalProps) {
  const { t } = useTranslation("game_details");

  const { showSuccessToast, showErrorToast } = useToast();

  const { updateGame, setShowRepacksModal, repacks, selectGameExecutable } =
    useContext(gameDetailsContext);

  const [showDeleteModal, setShowDeleteModal] = useState(false);
  const [showRemoveGameModal, setShowRemoveGameModal] = useState(false);
  const [launchOptions, setLaunchOptions] = useState(game.launchOptions ?? "");
  const [showResetAchievementsModal, setShowResetAchievementsModal] =
    useState(false);
  const [isDeleting, setIsDeleting] = useState(false);

  const {
    removeGameInstaller,
    removeGameFromLibrary,
    isGameDeleting,
    cancelDownload,
  } = useDownload();

  const deleting = isGameDeleting(game.id);

  const { lastPacket } = useDownload();

  const isGameDownloading =
    game.status === "active" && lastPacket?.game.id === game.id;

  const debounceUpdateLaunchOptions = useRef(
    debounce(async (value: string) => {
      await window.electron.updateLaunchOptions(game.id, value);
      updateGame();
    }, 1000)
  ).current;

  const handleRemoveGameFromLibrary = async () => {
    if (isGameDownloading) {
      await cancelDownload(game.id);

Review comment (suggestion):
```suggestion
  const [isResettingAchievements, setIsResettingAchievements] = useState(false);
```

Code after:
import { debounce } from "lodash-es";

export interface GameOptionsModalProps {
  visible: boolean;
  game: Game;
  onClose: () => void;
}

export function GameOptionsModal({
  visible,
  game,
  onClose,
}: GameOptionsModalProps) {
  const { t } = useTranslation("game_details");

  const { showSuccessToast, showErrorToast } = useToast();

  const {
    updateGame,
    setShowRepacksModal,
    repacks,
    selectGameExecutable,
    achievements,
  } = useContext(gameDetailsContext);

  const [showDeleteModal, setShowDeleteModal] = useState(false);
  const [showRemoveGameModal, setShowRemoveGameModal] = useState(false);
  const [launchOptions, setLaunchOptions] = useState(game.launchOptions ?? "");
  const [showResetAchievementsModal, setShowResetAchievementsModal] =
    useState(false);
  const [isDeletingAchievements, setIsDeletingAchievements] = useState(false);

  const {
    removeGameInstaller,
    removeGameFromLibrary,
    isGameDeleting,
    cancelDownload,
  } = useDownload();

  const { userDetails } = useUserDetails();

  const hasAchievements =
    (achievements?.filter((achievement) => achievement.unlocked).length ?? 0) >
    0;

  const deleting = isGameDeleting(game.id);

  const { lastPacket } = useDownload();

  const isGameDownloading =
    game.status === "active" && lastPacket?.game.id === game.id;
Repository: hydralauncher/hydra
File: src/main/events/library/open-game.ts (TypeScript)

Code before:
import { gameRepository } from "@main/repository";

import { registerEvent } from "../register-event";
import { shell } from "electron";
import { spawn } from "child_process";
import { parseExecutablePath } from "../helpers/parse-executable-path";
import { parseLaunchOptions } from "../helpers/parse-launch-options";

const openGame = async (
  _event: Electron.IpcMainInvokeEvent,
  gameId: number,
  executablePath: string,
  launchOptions: string | null
) => {
  const parsedPath = parseExecutablePath(executablePath);
  const parsedParams = parseLaunchOptions(launchOptions);

  await gameRepository.update({ id: gameId }, { executablePath: parsedPath, launchOptions });

  if (process.platform === "linux" || process.platform === "darwin") {
    shell.openPath(parsedPath);
  }

  if (process.platform === "win32") {
    spawn(parsedPath, parsedParams, { shell: false });
  }
};

registerEvent("openGame", openGame);


Review comment (suggestion):
```suggestion
    spawn(parsedPath, parsedParams, { shell: false, detached: true });
```
this should be enough

Code after:
import { gameRepository } from "@main/repository";

import { registerEvent } from "../register-event";
import { shell } from "electron";
import { spawn } from "child_process";
import { parseExecutablePath } from "../helpers/parse-executable-path";
import { parseLaunchOptions } from "../helpers/parse-launch-options";

const openGame = async (
  _event: Electron.IpcMainInvokeEvent,
  gameId: number,
  executablePath: string,
  launchOptions: string | null
) => {
  const parsedPath = parseExecutablePath(executablePath);
  const parsedParams = parseLaunchOptions(launchOptions);

  await gameRepository.update(
    { id: gameId },
    { executablePath: parsedPath, launchOptions }
  );

  if (process.platform === "linux" || process.platform === "darwin") {
    shell.openPath(parsedPath);
  }

  if (process.platform === "win32") {
    spawn(parsedPath, parsedParams, { shell: false, detached: true });
  }
};

registerEvent("openGame", openGame);
Repository: hydralauncher/hydra
File: src/main/services/hosters/datanodes.ts (TypeScript)

Code before:
import axios, { AxiosResponse } from "axios";

export class DatanodesApi {
  private static session = axios.create({});

  public static async getDownloadUrl(downloadUrl: string): Promise<string> {
    const parsedUrl = new URL(downloadUrl);
    const pathSegments = parsedUrl.pathname.split("/");

    const fileCode = decodeURIComponent(pathSegments[1]);
    const fileName = decodeURIComponent(pathSegments[pathSegments.length - 1]);

    const headers: Record<string, string> = {
      "Content-Type": "application/x-www-form-urlencoded",
      Cookie: `lang=english; file_name=${fileName}; file_code=${fileCode};`,
      Host: "datanodes.to",
      Origin: "https://datanodes.to",
      Referer: "https://datanodes.to/download",
      "User-Agent":
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
    };

    const payload = new URLSearchParams({
      op: "download2",
      id: fileCode,
      rand: "",
      referer: "https://datanodes.to/download",
      method_free: "Free Download >>",
      method_premium: "",
      adblock_detected: "",
    });

    const config = {
      headers,
      maxRedirects: 0,
      validateStatus: (status: number) => status === 302 || status < 400,
    };


Review comment (performance):
I think it would be more efficient if you just passed the whole config object directly into the function call on line `39`. This way, you wouldn't have to write any types + you would inherit the types directly from `axios`.

Code after:
import axios, { AxiosResponse } from "axios";

export class DatanodesApi {
  private static readonly session = axios.create({});

  public static async getDownloadUrl(downloadUrl: string): Promise<string> {
    const parsedUrl = new URL(downloadUrl);
    const pathSegments = parsedUrl.pathname.split("/");

    const fileCode = decodeURIComponent(pathSegments[1]);
    const fileName = decodeURIComponent(pathSegments[pathSegments.length - 1]);

    const payload = new URLSearchParams({
      op: "download2",
      id: fileCode,
      rand: "",
      referer: "https://datanodes.to/download",
      method_free: "Free Download >>",
      method_premium: "",
      adblock_detected: "",
    });

    const response: AxiosResponse = await this.session.post(
      "https://datanodes.to/download",
      payload,
      {
        headers: {
          "Content-Type": "application/x-www-form-urlencoded",
          Cookie: `lang=english; file_name=${fileName}; file_code=${fileCode};`,
          Host: "datanodes.to",
          Origin: "https://datanodes.to",
          Referer: "https://datanodes.to/download",
          "User-Agent":
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
        },
        maxRedirects: 0,        validateStatus: (status: number) => status === 302 || status < 400,
      }
    );
Repository: hydralauncher/hydra
File: src/main/events/autoupdater/restart-and-install-update.ts (TypeScript)

Code before:
import { app } from "electron";
import { registerEvent } from "../register-event";
import updater from "electron-updater";

const { autoUpdater } = updater;

export const restartAndInstallUpdate = () => {
  autoUpdater.removeAllListeners();
  if (app.isPackaged) {
    autoUpdater.quitAndInstall(false);
  }
};

const restartAndInstallUpdateEvent = async (
  _event: Electron.IpcMainInvokeEvent
) => {
  restartAndInstallUpdate();
};

registerEvent("restartAndInstallUpdate", restartAndInstallUpdateEvent);


Review comment (suggestion):
Aqui também não precisa de um escopo novo, você pode retornar o método diretamente
`=> restartAndInstallUpdate()`

Code after:
import { app } from "electron";
import { registerEvent } from "../register-event";
import updater from "electron-updater";

const { autoUpdater } = updater;

export const restartAndInstallUpdate = () => {
  autoUpdater.removeAllListeners();
  if (app.isPackaged) {
    autoUpdater.quitAndInstall(false);
  }
};

const restartAndInstallUpdateEvent = async (
  _event: Electron.IpcMainInvokeEvent
) => restartAndInstallUpdate();

registerEvent("restartAndInstallUpdate", restartAndInstallUpdateEvent);