• 12.《Electron 跨平台开发实战》- chapter12 IndexedDB


    IndexedDB

    IndexedDB 是由Chromeinu提供的NoSQL数据库,它的键和值可以是任何类型的JavaScript类型

    第三方访问 IndexedDB

    与IndexedDB 的所有交互都是异步进行的,直接使用事件来实现异步处理,这导致代码难以阅读和理解,

    idb库

    github:https://github.com/jakearchibald/idb
    idb库封装了IndexedDB的内部细节,将IndexedDb基于事件的API封装成了一个基于Promise的API

    Dexie.js

    github:https://github.com/dfahlander/Dexie.js
    electron中使用示例: https://github.com/dfahlander/Dexie.js/tree/master/samples/electron
    API简单使用:https://dexie.org/docs/API-Reference

    Clipsmater-IndexedDB

    项目:Clipsmater-IndexedDB

    使用IndexedDB数据库存储剪切板内容,使用第三方库 Dexie.js 操作IndexedDB数据库

    完整代码

    • 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": {
        "dexie": "^3.0.1",
        "electron": "9.0.3",
        "menubar": "^9.0.1",
        "request": "^2.88.2"
      }
    }
    
    

    安装第三方库dexie

      cnpm install dexie --save
    

    自定义数据库模块 dbdexie.js

    • 创建dbdexie.js文件,内容如下:
    const Dexie = require('dexie');
    const db = new Dexie('clipsmarter_database');
    
    db.version(1).stores({
        clips: '++id, value, data'
    });
    
    module.exports = db;
    

    创建一个名为clipsmarter_database的数据,
    创建数据库表clips, 其中:

    NOTE: Don’t declare all columns like in SQL. You only declare properties you want to index, that is properties you want to use in a where(…) query.
    Schema Syntax

    1. ++ Auto-incremented primary key
    2. & Unique
      • Multi-entry index
        
    3. [A+B] Compound index.

    使用 dexie

    引用 自定模块 dbdexie

    const db = require('./dbdexie');
    

    查询

    const initClippingElement = () => {
      /*
      db.clips
        //.reverse() //按主键降序排列
        .each(clip => {
          let clippingElement = createClippingElement(clip.id, clip.value);
          clippingsList.prepend(clippingElement);
        });
    */
      db.clips
        .orderBy('id')
        //.reverse()  //按主键降序排列
        .toArray((clips) => {
          for (var clip of clips) {
            let clippingElement = createClippingElement(clip.id, clip.value);
            clippingsList.prepend(clippingElement);
          }
        });
    }
    
    

    html中设置clipId

    const createClippingElement = (clipId, 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" data-clipId='${clipId}'>Remove</button>
        </div>
      `;
    
      clippingElement.querySelector('.clipping-text').innerText = clippingText;
    
      return clippingElement;
    };
    

    添加

    const addClippingToList = () => {
      const clippingText = clipboard.readText();
      db.clips.add({ value: clippingText, data: Date.now() })
        .then(id => {
          return db.clips.get(id);
        }).then(item => {
          const clippingElement = createClippingElement(item.id, item.value);
          clippingsList.prepend(clippingElement);
        }).catch(err => {
          alert("Error: " + (err.stack || err));
        });
    };
    

    删除

    删除时,获取id: let clipId = parseInt(event.target.getAttribute("data-clipId"));
    
    clippingsList.addEventListener('click', (event) => {
      const hasClass = className => event.target.classList.contains(className);
      let clipId = parseInt(event.target.getAttribute("data-clipId")); //删除时,获取id
      const clippingListItem = getButtonParent(event);
    
      if (hasClass('remove-clipping')) removeClipping(clipId, clippingListItem);
      if (hasClass('copy-clipping')) writeToClipboard(getClippingText(clippingListItem));
      if (hasClass('publish-clipping')) publishClipping(getClippingText(clippingListItem));
    });
    
    const removeClipping = (clipId, target) => {
      db.clips.delete(clipId).then(result => {
        //Promise that resolves successfully with an undefined result, no matter if a record was deleted or not. 
        //不管删没删,都是返回:undefined
        target.remove();
      });
    };
    
    • renderer.js 完整代码
    const { clipboard, ipcRenderer, shell } = require('electron');
    const db = require('./dbdexie');
    
    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 initClippingElement = () => {
      /*
      db.clips
        //.reverse() //按主键降序排列
        .each(clip => {
          let clippingElement = createClippingElement(clip.id, clip.value);
          clippingsList.prepend(clippingElement);
        });
    */
      db.clips
        .orderBy('id')
        //.reverse()  //按主键降序排列
        .toArray((clips) => {
          for (var clip of clips) {
            let clippingElement = createClippingElement(clip.id, clip.value);
            clippingsList.prepend(clippingElement);
          }
        });
    }
    
    const createClippingElement = (clipId, 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" data-clipId='${clipId}'>Remove</button>
        </div>
      `;
    
      clippingElement.querySelector('.clipping-text').innerText = clippingText;
    
      return clippingElement;
    };
    
    const addClippingToList = () => {
      const clippingText = clipboard.readText();
      db.clips.add({ value: clippingText, data: Date.now() })
        .then(id => {
          return db.clips.get(id);
        }).then(item => {
          const clippingElement = createClippingElement(item.id, item.value);
          clippingsList.prepend(clippingElement);
        }).catch(err => {
          alert("Error: " + (err.stack || err));
        });
    };
    
    copyFromClipboardButton.addEventListener('click', addClippingToList);
    
    clippingsList.addEventListener('click', (event) => {
      const hasClass = className => event.target.classList.contains(className);
      let clipId = parseInt(event.target.getAttribute("data-clipId"));
      const clippingListItem = getButtonParent(event);
    
      if (hasClass('remove-clipping')) removeClipping(clipId, clippingListItem);
      if (hasClass('copy-clipping')) writeToClipboard(getClippingText(clippingListItem));
      if (hasClass('publish-clipping')) publishClipping(getClippingText(clippingListItem));
    });
    
    const removeClipping = (clipId, target) => {
      db.clips.delete(clipId).then(result => {
        //Promise that resolves successfully with an undefined result, no matter if a record was deleted or not. 
        //不管删没删,都是返回:undefined
        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 }
          }
        })
      };
    };
    
    initClippingElement();
    
    • main.html
      main.html 没做任何变化,沿用charp-10的内容
    <!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'); }
    
    });
    
    
    • style.css
    html {
      box-sizing: border-box;
    }
    
    html, body {
      height: 100%;
       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;
       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- 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;
    }
    
    
  • 相关阅读:
    Mac开发者常用的工具
    10个常见的缓存使用误区
    果壳摘选
    WPF 无缝图片滚动
    WinForm 子线程修改主线程(UI线程)
    如何在WinForm中发送HTTP请求
    新闻接口调用之新浪滚动新闻
    使用json-lib进行Java和JSON之间的转换
    转载 一位程序员的妻子讲述她老公教给她了什么
    sae 上传文件 java实现
  • 原文地址:https://www.cnblogs.com/easy5weikai/p/13167807.html
Copyright © 2020-2023  润新知