src/electron/main/helpers.js

Summary

Maintainability
C
1 day
Test Coverage
/* eslint no-console: 0 */
const { shell, app, Menu, ipcMain } = require('electron');
const { autoUpdater } = require('electron-updater');
const log = require('electron-log');
const { init } = require('@sentry/electron');
const { includes, startsWith, head } = require('lodash/fp');

const isMacOS = process.platform === 'darwin';
const packageJson = require('../package');

function initSentry() {
  init({
    dsn: 'https://9b448264479b47418f9e248c208632ae@sentry.io/1245680',
    release: app.getVersion(),
    environment: process.env.NODE_ENV
  });
}

function installDevToolsExtentions() {
  if (isMacOS) {
    const {
      default: installExtension,
      REACT_DEVELOPER_TOOLS,
      REDUX_DEVTOOLS,
      REACT_PERF
    } = require('electron-devtools-installer');

    installExtension([REACT_DEVELOPER_TOOLS.id, REDUX_DEVTOOLS.id, REACT_PERF.id])
      .then((extensionName) => console.log(`Added Extension:  ${extensionName}`))
      .catch((err) => console.log('An error occurred: ', err));

    console.log('\x1b[37m\x1b[41m', 'LOG ', '\x1b[0m', process.env.NODE_ENV);
  }
}

function sendStatusToWindow(text, info, targetWindow, channel = 'update-info') {
  targetWindow.webContents.send(channel, text, info);
}

function handleDownload(win) {
  win.webContents.session.on('will-download', (event, item, sender) => {
    const isUpdateUrl = includes(
      ['https://github.com/Gisto/Gisto/releases/download/'],
      head(item.getURLChain())
    );

    if (isUpdateUrl) {
      item.on('updated', (updateEvent, state) => {
        if (state === 'interrupted') {
          console.log('Download is interrupted but can be resumed');
        } else if (state === 'progressing') {
          if (item.isPaused()) {
            sender.send('update-info', 'Download paused');
          } else {
            const downloaded = item.getReceivedBytes() / 1048576;
            const total = item.getTotalBytes() / 1048576;

            sender.send('download-progress', null, {
              progress: { percent: (downloaded / total) * 100 }
            });
          }
        }
      });
      item.once('done', (doneEvent, state) => {
        if (state === 'completed') {
          sender.send('update-downloaded', 'New version downloaded, quit and run installer?', {
            success: true,
            path: item.getSavePath()
          });
        } else {
          sender.send('update-downloaded', null, { success: false });
        }
      });
    }
  });
}

function handleNavigate(win) {
  // Handled URL opening in default browser
  win.webContents.on('will-navigate', (event, url) => {
    const isUpdateUrl = includes(['https://github.com/Gisto/Gisto/releases/download/'], url);
    const isFileProtocol = startsWith('file:', url);

    if (isUpdateUrl || isFileProtocol) {
      return false;
    }

    event.preventDefault();
    shell.openExternal(url);

    return true;
  });
}

function setBadge(badge) {
  if (isMacOS) {
    app.dock.setBadge(badge);
  }
}

function buildMenu(mainWindow) {
  const template = [];
  const name = app.getName();

  template.unshift(
    {
      label: name,
      submenu: [
        {
          label: `About ${name}`,
          role: 'about'
        },
        {
          label: 'Services',
          role: 'services',
          submenu: [],
          visible: !isMacOS
        },
        {
          label: 'Preferences',
          accelerator: 'CmdOrCtrl+,',
          click: () => mainWindow.webContents.send('routeTo', '/settings')
        },
        {
          label: 'Console',
          click: () => mainWindow.webContents.openDevTools(),
          visible: false
        },
        {
          label: 'Reload',
          accelerator: 'CmdOrCtrl+R',
          click: () => mainWindow.webContents.reload()
        },
        {
          label: 'Check for updates',
          click: () => {
            autoUpdater.autoDownload = false;
            autoUpdater.checkForUpdates();
          },
          visible: !isMacOS
        },
        {
          label: 'Quit',
          accelerator: 'CmdOrCtrl+Q',
          click: () => app.quit()
        }
      ]
    },
    {
      label: 'Edit',
      submenu: [
        { label: 'Undo', accelerator: 'CmdOrCtrl+Z', selector: 'undo:' },
        { label: 'Redo', accelerator: 'Shift+CmdOrCtrl+Z', selector: 'redo:' },
        { type: 'separator' },
        { label: 'Cut', accelerator: 'CmdOrCtrl+X', selector: 'cut:' },
        { label: 'Copy', accelerator: 'CmdOrCtrl+C', selector: 'copy:' },
        { label: 'Paste', accelerator: 'CmdOrCtrl+V', selector: 'paste:' },
        { label: 'Select All', accelerator: 'CmdOrCtrl+A', selector: 'selectAll:' }
      ]
    },
    {
      label: 'View',
      submenu: [
        {
          label: 'Toggle Full Screen',
          accelerator: 'Ctrl+Command+F',
          click: (item, focusedWindow) => {
            if (focusedWindow) {
              focusedWindow.setFullScreen(!focusedWindow.isFullScreen());
            }
          }
        }
      ]
    },
    {
      label: 'Help',
      submenu: [
        {
          label: `Learn More about ${name}`,
          click() {
            shell.openExternal('https://www.gistoapp.com');
          }
        },
        {
          label: 'Documentation',
          click() {
            shell.openExternal('https://www.gistoapp.com/documentation/');
          }
        },
        {
          label: 'Announcements',
          click() {
            shell.openExternal('https://www.gistoapp.com/blog/');
          }
        },
        {
          label: 'Search Issues',
          click() {
            shell.openExternal('https://github.com/gisto/gisto/issues');
          }
        }
      ]
    }
  );

  Menu.setApplicationMenu(Menu.buildFromTemplate(template));
}

function handleMacOSUpdates(mainWindow) {
  if (isMacOS) {
    const LATEST_RELEASED_VERSION_URL = 'https://api.github.com/repos/Gisto/Gisto/releases/latest';
    const request = require('superagent');
    const semver = require('semver');

    sendStatusToWindow('Gisto checking for new version...', {}, mainWindow, 'update-info');
    request
      .get(LATEST_RELEASED_VERSION_URL)
      .set(
        'User-Agent',
        `Gisto app v${packageJson.version} (https://www.gistoapp.com) Snippets made awesome`
      )
      .end((error, result) => {
        let shouldUpdate = false;

        if (result.body) {
          const serverVersion = result.body.name;
          const serverAssets = result.body.assets;

          if (serverVersion && packageJson.version) {
            shouldUpdate = semver.lt(packageJson.version, serverVersion);
          }

          if (shouldUpdate) {
            const dmgUrl = serverAssets.filter(
              (asset) => asset.name === `Gisto-${serverVersion}.dmg`
            );

            sendStatusToWindow(
              `Update from ${packageJson.version} to ${serverVersion} available`,
              { url: dmgUrl },
              mainWindow,
              'update-info'
            );
          } else {
            sendStatusToWindow('No updates available at the moment', {}, mainWindow, 'no-updates');
          }
        }
        if (error) {
          sendStatusToWindow(
            'No new version information at the moment',
            {},
            mainWindow,
            'update-info'
          );
        }
      });
  }
}

function updateChecker(mainWindow) {
  ipcMain.on('downloadUpdate', () => autoUpdater.downloadUpdate());
  ipcMain.on('quitAndInstall', () => autoUpdater.quitAndInstall(true, true));

  autoUpdater.logger = log;
  autoUpdater.logger.transports.file.level = 'debug';

  autoUpdater.autoDownload = false;
  autoUpdater.checkForUpdates();

  autoUpdater.on('update-available', (info) => {
    sendStatusToWindow(
      `Update from ${packageJson.version} to ${info.version} available`,
      info,
      mainWindow,
      'update-available'
    );
  });

  autoUpdater.on('update-downloaded', (info) => {
    sendStatusToWindow(
      'New version downloaded, quit and Install?',
      info,
      mainWindow,
      'update-downloaded'
    );
  });

  autoUpdater.on('download-progress', (progress, bytesPerSecond, percent, total, transferred) => {
    sendStatusToWindow(
      'Downloaded: ',
      {
        progress,
        bytesPerSecond,
        percent,
        total,
        transferred
      },
      mainWindow,
      'download-progress'
    );
  });
}

function handleCmdFlags(win, flags) {
  if (flags.token) {
    win.webContents.send('token', flags.token);
  }
}

module.exports = {
  initSentry,
  installDevToolsExtentions,
  handleDownload,
  handleNavigate,
  setBadge,
  buildMenu,
  updateChecker,
  handleMacOSUpdates,
  handleCmdFlags
};