Code Study: How Menu Event Works in Github Desktop

Understanding Menu Event

menu-event.ts defines all possible menu event names.

export type MenuEvent =
  | 'push'
  | 'pull'
  | 'show-changes'
  | 'show-history'
  | 'add-local-repository'
  | 'create-branch'
  | 'show-branches'
  | 'remove-repository'
  | 'create-repository'
  | 'rename-branch'
  | 'delete-branch'
  | 'show-preferences'
  | 'choose-repository'
  | 'open-working-directory'
  | 'update-branch'
  | 'compare-to-branch'
  | 'merge-branch'
  | 'show-repository-settings'
  | 'open-in-shell'
  | 'compare-on-github'
  | 'view-repository-on-github'
  | 'clone-repository'
  | 'show-about'
  | 'boomtown'
  | 'open-pull-request'
  | 'install-cli'
  | 'open-external-editor'
  | 'select-all'

Triggering Menu Event

Each menu event name is bound an Electron menu item’s click event handler in build-default-menu.ts:

template.push({
  label: 'GitHub Desktop',
  submenu: [
    {
      label: 'About GitHub Desktop',
      click: emit('show-about'),
      id: 'about',
    },

When the Electron menu item gets clicked, it sends the ‘menu-event’ message together with the menu event name bound to the Electron menu item. The message is sent to the renderer process if a BrowserWindow is available, or emitted to the main process if not.

function emit(name: MenuEvent): ClickHandler {
  return (menuItem, window) => {
    if (window) {
      window.webContents.send('menu-event', { name })
    } else {
      ipcMain.emit('menu-event', { name })
    }
  }
}

If the main process receives this in main.ts, it will try to pass the event to the main window.

ipcMain.on('menu-event', (event: Electron.IpcMessageEvent, args: any[]) => {
  const { name }: { name: MenuEvent } = event as any
  if (mainWindow) {
    mainWindow.sendMenuEvent(name)
  }
})

Handling Menu Event

The renderer process listens to the ‘menu-event’ and act on it based on the event name.

ipcRenderer.on(
  'menu-event',
  (event: Electron.IpcMessageEvent, { name }: { name: MenuEvent }) => {
    this.onMenuEvent(name)
  }
)

private onMenuEvent(name: MenuEvent): any {
  // Don't react to menu events when an error dialog is shown.
  if (this.state.errors.length) {
    return
  }
  switch (name) {
    case 'push':
      return this.push()
    case 'pull':
      return this.pull()
    case 'show-changes':
      return this.showChanges()
    case 'show-history':
      ...
tags: GitHub Desktop - Electron