<commit_before>
import {Watcher} from "./watcher";

import {Menu} from "electron";

import {map} from "underscore";
import {createSelector} from "reselect";

import * as clone from "clone";
import localizer from "../localizer";

import urls from "../constants/urls";
import * as actions from "../actions";

import os from "../util/os";
const macos = os.itchPlatform() === "osx";

import {IStore, IAppState, II18nState} from "../types";

type IMenuItem = Electron.MenuItemOptions;
type IMenuTemplate = IMenuItem[];

let refreshSelector: (state: IAppState) => void;
const makeRefreshSelector = (store: IStore) => createSelector(
  (state: IAppState) => state.system,
  (state: IAppState) => state.session.credentials,
  (system, credentials) => {
    setImmediate(() =>
      store.dispatch(actions.refreshMenu({system, credentials})),
    );
  },
);

let applySelector: (state: IAppState) => void;
const makeApplySelector = (store: IStore) => createSelector(
  (state: IAppState) => state.ui.menu.template,
  (state: IAppState) => state.i18n,
  (template, i18n) => {
    setImmediate(() => {
      // electron gotcha: buildFromTemplate mutates its argument
      const menu = Menu.buildFromTemplate(clone(fleshOutTemplate(template, i18n, store)));
      Menu.setApplicationMenu(menu);
    });
  },
);

interface IMenuItemPayload {
  role?: string;
  label?: string;
}

function convertMenuAction (payload: IMenuItemPayload) {
  const {role, label} = payload;

  switch (role) {
    case "about": return actions.openUrl({url: urls.appHomepage});
    default: // muffin
  }

  switch (label) {
    case "sidebar.new_tab": return actions.newTab({});
    case "menu.file.close_tab": return macos ? actions.closeTabOrAuxWindow({}) : actions.closeTab({id: null});
    case "menu.file.close_all_tabs": return actions.closeAllTabs({});
    case "menu.file.close_window": return actions.hideWindow({});
    case "menu.file.quit": return actions.quitWhenMain({});
    case "menu.file.preferences": return actions.navigate("preferences");
    case "menu.view.downloads": return actions.navigate("downloads");
    case "menu.view.history": return actions.navigate("history");
    case "menu.account.change_user": return actions.changeUser({});
    // TODO: change to proper about tab/window
    case "menu.help.about": return actions.openUrl({url: urls.appHomepage});
    case "menu.help.view_terms": return actions.openUrl({url: urls.termsOfService});
    case "menu.help.view_license": return actions.openUrl({url: `${urls.itchRepo}/blob/master/LICENSE`});
    case "menu.help.check_for_update": return actions.checkForSelfUpdate({});
    case "menu.help.report_issue": return actions.openUrl({url: `${urls.itchRepo}/issues/new`});
    case "menu.help.search_issue": return actions.openUrl({url: `${urls.itchRepo}/search?type=Issues`});
    case "menu.help.release_notes": return actions.openUrl({url: `${urls.itchRepo}/releases`});
    case "crash.test":
      (async function () { throw new Error("crash test!"); })();
    default:
      return null;
  }
}

function fleshOutTemplate (template: IMenuTemplate, i18n: II18nState, store: IStore) {
  const t = localizer.getT(i18n.strings, i18n.lang);

  const visitNode = (input: IMenuItem) => {
    if (input.type === "separator") {
      return input;
    }

    const {label, role = null, enabled = true} = input;
    const node = clone(input);

    node.label = t(label);
    const menuAction = convertMenuAction({label, role});
    if (enabled && menuAction) {
      node.click = (e) => {
        store.dispatch(menuAction);
      };
    }

    if (node.submenu) {
      node.submenu = map(node.submenu as IMenuItem[], visitNode);
    }<commit_msg>Don't attempt to localize null labels, avoid 'undefined {}' on macOS, closes #1186<commit_after>
import {Watcher} from "./watcher";

import {Menu} from "electron";

import {map} from "underscore";
import {createSelector} from "reselect";

import * as clone from "clone";
import localizer from "../localizer";

import urls from "../constants/urls";
import * as actions from "../actions";

import os from "../util/os";
const macos = os.itchPlatform() === "osx";

import {IStore, IAppState, II18nState} from "../types";

type IMenuItem = Electron.MenuItemOptions;
type IMenuTemplate = IMenuItem[];

let refreshSelector: (state: IAppState) => void;
const makeRefreshSelector = (store: IStore) => createSelector(
  (state: IAppState) => state.system,
  (state: IAppState) => state.session.credentials,
  (system, credentials) => {
    setImmediate(() =>
      store.dispatch(actions.refreshMenu({system, credentials})),
    );
  },
);

let applySelector: (state: IAppState) => void;
const makeApplySelector = (store: IStore) => createSelector(
  (state: IAppState) => state.ui.menu.template,
  (state: IAppState) => state.i18n,
  (template, i18n) => {
    setImmediate(() => {
      // electron gotcha: buildFromTemplate mutates its argument
      const menu = Menu.buildFromTemplate(clone(fleshOutTemplate(template, i18n, store)));
      Menu.setApplicationMenu(menu);
    });
  },
);

interface IMenuItemPayload {
  role?: string;
  label?: string;
}

function convertMenuAction (payload: IMenuItemPayload) {
  const {role, label} = payload;

  switch (role) {
    case "about": return actions.openUrl({url: urls.appHomepage});
    default: // muffin
  }

  switch (label) {
    case "sidebar.new_tab": return actions.newTab({});
    case "menu.file.close_tab": return macos ? actions.closeTabOrAuxWindow({}) : actions.closeTab({id: null});
    case "menu.file.close_all_tabs": return actions.closeAllTabs({});
    case "menu.file.close_window": return actions.hideWindow({});
    case "menu.file.quit": return actions.quitWhenMain({});
    case "menu.file.preferences": return actions.navigate("preferences");
    case "menu.view.downloads": return actions.navigate("downloads");
    case "menu.view.history": return actions.navigate("history");
    case "menu.account.change_user": return actions.changeUser({});
    // TODO: change to proper about tab/window
    case "menu.help.about": return actions.openUrl({url: urls.appHomepage});
    case "menu.help.view_terms": return actions.openUrl({url: urls.termsOfService});
    case "menu.help.view_license": return actions.openUrl({url: `${urls.itchRepo}/blob/master/LICENSE`});
    case "menu.help.check_for_update": return actions.checkForSelfUpdate({});
    case "menu.help.report_issue": return actions.openUrl({url: `${urls.itchRepo}/issues/new`});
    case "menu.help.search_issue": return actions.openUrl({url: `${urls.itchRepo}/search?type=Issues`});
    case "menu.help.release_notes": return actions.openUrl({url: `${urls.itchRepo}/releases`});
    case "crash.test":
      (async function () { throw new Error("crash test!"); })();
    default:
      return null;
  }
}

function fleshOutTemplate (template: IMenuTemplate, i18n: II18nState, store: IStore) {
  const t = localizer.getT(i18n.strings, i18n.lang);

  const visitNode = (input: IMenuItem) => {
    if (input.type === "separator") {
      return input;
    }

    const {label, role = null, enabled = true} = input;
    const node = clone(input);

    if (label) {
      node.label = t(label);
    }
    const menuAction = convertMenuAction({label, role});
    if (enabled && menuAction) {
      node.click = (e) => {
        store.dispatch(menuAction);
      };
    }

    if (node.submenu) {
      node.submenu = map(node.submenu as IMenuItem[], visitNode);
    }
{'fraction_non_alphanumeric': 0.15837346174424827, 'fraction_numerical': 0.004815409309791332, 'mean_word_length': 4.131777625257378, 'pattern_counts': {'":': 36, '<': 3, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7022123', 'n_tokens_mistral': 2366, 'n_tokens_neox': 2354, 'n_words': 557}