10.《Electron 跨平台开发实战》- chapter10-menubar

项目代码

https://github.com/electron-in-action/clipmaster-9000/tree/completed-example

代码解析

electron9.x 代码更新

从源码下载的使用目前最新的版本 electron9.x、menubar 9.x ,
无法运行,原因是

  1. 无法识别Menubar类
  2. 得显式的让browserWindow集成node环境

需要做如下修改:

  • main.js
  • 旧版本的代码
const Menubar = require('menubar');
const { globalShortcut, Menu } = require('electron');

const menubar = Menubar({
  preloadWindow: true,
  index: `file://${__dirname}/index.html`,
});

  • electron9.x版本 修改为
const { menubar } = require('menubar');
const { globalShortcut, Menu } = require('electron');

const mb = menubar({
  preloadWindow: true,
  browserWindow: {
    webPreferences: {
      nodeIntegration: true
    }
  },
  index: `file://${__dirname}/index.html`,
});

request 代码要修改

旧版本代码

const publishClipping = (clippingText) => {
  request.post(toJSON(clippingText), (err, response, body) => {
  ... 
    const gistUrl = JSON.parse(body).html_url;
  ... 
  });
};

API接口可能已经修改,返回值估计也有变化,这里取body.documentation_ur作为跳转页面,打开默认浏览器
修改为:

let baseUrl = 'https://api.github.com/gists';
...
const publishClipping = (clippingText) => {

  request.post(baseUrl, toJSON(clippingText), (err, response, body) => {
 ...
    const gistUrl = JSON.parse(body).documentation_url; //   const gistUrl = JSON.parse(body).html_url;
...
  });

};

完整代码

index.html

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>Clipmaster 9000</title>
  <link rel="stylesheet" href="style.css" type="text/css">
</head>

<body>
  <div class="container">
    <section class="controls">
      <button id="copy-from-clipboard">Copy from Clipboard</button>
    </section>

    <section class="content">
      <div id="clippings-list" class="clippings-list"></div>
    </section>
  </div>
  <script>
    require('./renderer.js');
  </script>
</body>

</html>

main.js

const { menubar } = require('menubar');
const { globalShortcut, Menu } = require('electron');

const mb = menubar({
  preloadWindow: true,
  browserWindow: {
    webPreferences: {
      nodeIntegration: true
    }
  },
  index: `file://${__dirname}/index.html`,
});


mb.on('ready', () => {
  const secondaryMenu = Menu.buildFromTemplate([
    {
      label: 'Quit',
      click() { mb.app.quit(); },
      accelerator: 'CommandOrControl+Q'
    },
  ]);

  mb.tray.on('right-click', () => {
    mb.tray.popUpContextMenu(secondaryMenu);
  });

  const createClipping = globalShortcut.register('CommandOrControl+!', () => {
    mb.window.webContents.send('create-new-clipping');
  });

  const writeClipping = globalShortcut.register('CmdOrCtrl+Alt+@', () => {
    mb.window.webContents.send('write-to-clipboard');
  });

  const publishClipping = globalShortcut.register('CmdOrCtrl+Alt+#', () => {
    mb.window.webContents.send('publish-clipping');
  });

  if (!createClipping) { console.error('Registration failed', 'createClipping'); }
  if (!writeClipping) { console.error('Registration failed', 'writeClipping'); }
  if (!publishClipping) { console.error('Registration failed', 'publishClipping'); }
});

renderer.js

const { clipboard, ipcRenderer, shell } = require('electron');
let baseUrl = 'https://api.github.com/gists';

const request = require('request').defaults({
  url: baseUrl,
  headers: { 'User-Agent': 'Clipmaster 9000' }
});

const clippingsList = document.getElementById('clippings-list');
const copyFromClipboardButton = document.getElementById('copy-from-clipboard');

ipcRenderer.on('create-new-clipping', () => {
  addClippingToList();
  new Notification('Clipping Added', {
    body: `${clipboard.readText()}`
  });
});

ipcRenderer.on('write-to-clipboard', () => {
  const clipping = clippingsList.firstChild;
  writeToClipboard(getClippingText(clipping));
  new Notification('Clipping Copied', {
    body: `${clipboard.readText()}`
  });
});

ipcRenderer.on('publish-clipping', () => {
  const clipping = clippingsList.firstChild;
  publishClipping(getClippingText(clipping));
});

const createClippingElement = (clippingText) => {
  const clippingElement = document.createElement('article');

  clippingElement.classList.add('clippings-list-item');

  clippingElement.innerHTML = `
    <div class="clipping-text" disabled="true"></div>
    <div class="clipping-controls">
      <button class="copy-clipping">&rarr; Clipboard</button>
      <button class="publish-clipping">Publish</button>
      <button class="remove-clipping">Remove</button>
    </div>
  `;

  clippingElement.querySelector('.clipping-text').innerText = clippingText;

  return clippingElement;
};

const addClippingToList = () => {
  const clippingText = clipboard.readText();
  const clippingElement = createClippingElement(clippingText);
  clippingsList.prepend(clippingElement);
};

copyFromClipboardButton.addEventListener('click', addClippingToList);

clippingsList.addEventListener('click', (event) => {
  const hasClass = className => event.target.classList.contains(className);

  const clippingListItem = getButtonParent(event);

  if (hasClass('remove-clipping')) removeClipping(clippingListItem);
  if (hasClass('copy-clipping')) writeToClipboard(getClippingText(clippingListItem));
  if (hasClass('publish-clipping')) publishClipping(getClippingText(clippingListItem));
});

const removeClipping = (target) => {
  target.remove();
};

const writeToClipboard = (clippingText) => {
  clipboard.writeText(clippingText);
};

const publishClipping = (clippingText) => {

  request.post(baseUrl, toJSON(clippingText), (err, response, body) => {

    if (err) {
      return new Notification('Error Publishing Your Clipping', {
        body: JSON.parse(err).message
      });
    }

    const gistUrl = JSON.parse(body).documentation_url; //   const gistUrl = JSON.parse(body).html_url;
    const notification = new Notification('Your Clipping Has Been Published', {
      body: `Click to open ${gistUrl} in your browser.`
    });

    notification.onclick = () => { shell.openExternal(gistUrl); };

    clipboard.writeText(gistUrl);
  });

};

const getButtonParent = ({ target }) => {
  return target.parentNode.parentNode;
};

const getClippingText = (clippingListItem) => {
  return clippingListItem.querySelector('.clipping-text').innerText;
};

const toJSON = (clippingText) => {
  return {
    body: JSON.stringify({
      description: 'Created with Clipmaster 9000',
      public: 'true',
      files: {
        'clipping.txt': { content: clippingText }
      }
    })
  };
};


style.css

html {
  box-sizing: border-box;
}

html, body {
  height: 100%;
  width: 100%;
  overflow: hidden;
  font-size: 12px;
}

*, *:before, *:after {
  box-sizing: inherit;
}

body {
  margin: 0;
  padding: 0;
}

body, input {
  font: menu;
  font-size: 12px;
}

body > div {
  height: 100%;
  overflow: scroll;
  -webkit-overflow-scrolling: touch;
}

.container {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  overflow: auto;
}

textarea, input, div, button { outline: none; }

.controls {
  background-color: rgb(217, 241, 238);
  padding: 1em;
  top: 0;
  position: fixed;
  width: 100%;
}

.controls button {
  background-color: rgb(181, 220, 216);
  border: none;
  padding: 0.5em 1em;
}

.controls button:hover {
  background-color: rgb(156, 198, 192);
}

.controls button:active {
  background-color: rgb(144, 182, 177);
}

.controls button:disabled {
  background-color: rgb(196, 204, 202);
}

.content {
  height: 100%;
}

.clippings-list {
  margin-top: 65px;
  padding: 0 10px;
}

.clippings-list-item {
  border: 1px solid rgb(178, 193, 191);
  box-shadow: 1px 1px 1px 1px rgba(205, 228, 224, 0.78);
  padding: 0.5em;
  margin-bottom: 1em;
}

.clipping-text {
  background-color: rgb(228, 248, 245);
  padding: 0.5em;
  min-width: 100%;
  max-height: 10em;
  overflow: scroll;
}

.clipping-text::-webkit-scrollbar {
  display: none;
}

.clipping-controls {
  margin-top: 0.5em;
}

button {
  background-color: rgb(181, 220, 216);
  border: none;
  font-size: 0.8em;
  padding: 0.5em 1em;
}

button:hover {
  background-color: rgb(156, 198, 192);
}

button:active {
  background-color: rgb(144, 182, 177);
}

button:disabled {
  background-color: rgb(196, 204, 202);
}

button.remove-clipping {
  display: none;
  float: right;
  color: white;
  background-color: rgb(208, 69, 55);
}

button.remove-clipping:hover {
  background-color: rgb(208, 41, 29);
}

button.remove-clipping:active {
  background-color: rgb(236, 0, 6);
}

button.remove-clipping:disabled {
  background-color: rgb(152, 73, 64);
}

.clippings-list-item:hover button.remove-clipping {
  display: inline-block;
}

package.json

{
  "name": "clipmaster-9000",
  "version": "1.0.0",
  "description": "A menubar application with a rich UI.",
  "main": "app/main.js",
  "scripts": {
    "start": "electron .",
    "test": "echo \"Error:no test specified\" && exit 1"
  },
  "author": "weikai",
  "license": "MIT",
  "dependencies": {
    "electron": "^9.0.3",
    "menubar": "^9.0.1",
    "request": "^2.88.2"
  }
}
posted @ 2020-06-18 19:35  easy5  阅读(407)  评论(0编辑  收藏  举报