首先我们来看效果。

gif

 本章修改的内容比较多,因为牵扯了比较多的前端样式,可以运行的代码在这里下载与删除(上) - 图2 大家可以对照查漏补缺。

接受下载和删除信号

首先对 plugins.ts 进行更新,添加相应的处理逻辑,为了避免错漏,笔者把所有代码都贴了出来。这里对之前的一些方法做了简单的更新。对于监听的事件可以通过 emit 触发,因为继承 EventEmitter 类。当我们刷新或者下载的时候我们都需要更新缓存。

  1. import { app, ipcMain } from 'electron'
  2. import { resolve } from 'path'
  3. import { readdirSync, ensureDirSync, writeFile, remove } from 'fs-extra'
  4. import requireFoolWebpack from 'require-fool-webpack'
  5. import { store } from './tray'
  6. import { promisified } from 'phin'
  7. import { on } from './helper'
  8. const pluginsPath = store.get(
  9. 'PLUGIN_PATH',
  10. resolve(app.getPath('home'), '.reader-app-scripts')
  11. )
  12. ensureDirSync(pluginsPath)
  13. // https://github.com/webpack/webpack/issues/196
  14. // https://github.com/sindresorhus/require-fool-webpack
  15. const loadPlugins = () => {
  16. let files = readdirSync(pluginsPath)
  17. return [
  18. files,
  19. files.map(filename =>
  20. requireFoolWebpack(resolve(pluginsPath, filename))(requireFoolWebpack)
  21. )
  22. ]
  23. }
  24. const saveToSetting = (all?: any) => {
  25. const [files, _] = loadPlugins()
  26. store.set('LOACAL_PLUGINS', files)
  27. all && ipcMain.emit('get_local_plugins', all.event, all.args)
  28. }
  29. saveToSetting()
  30. on('reload_local_plugins').subscribe(saveToSetting)
  31. on('get_local_plugins').subscribe(({ event, args }) => {
  32. event.sender.send('local_plugins', store.get('LOACAL_PLUGINS', []))
  33. })
  34. const storeURL =
  35. 'https://raw.githubusercontent.com/MiYogurt/reader-store/master/db.json'
  36. const fetchAllPlugins = (event: any) => {
  37. promisified(storeURL).then((res: any) => {
  38. let db = []
  39. try {
  40. db = JSON.parse(res.body.toString())
  41. } catch (e) {}
  42. event.sender.send('all_plugins', db)
  43. store.set('ALL_PLUGINS', db)
  44. })
  45. }
  46. on('reload_all_plugins').subscribe(async ({ event, args }) => {
  47. await fetchAllPlugins(event)
  48. ipcMain.emit('get_all_plugins', event, args)
  49. })
  50. on('get_all_plugins').subscribe(({ event, args }) => {
  51. const db = store.get('ALL_PLUGINS', [])
  52. if (db.length > 0) {
  53. return event.sender.send('all_plugins', db)
  54. }
  55. fetchAllPlugins(event.sender.send)
  56. })
  57. // 下载插件
  58. on('download_plugin').subscribe(async ({ event, args }) => {
  59. const { filename, url } = args
  60. await promisified(url)
  61. .then(({ body }) => body.toString())
  62. .then(data => (console.log(data), data)) // 查看一下数据内容
  63. .then(data => writeFile(pluginsPath + '/' + filename, data)) // 写入插件
  64. ipcMain.emit('reload_local_plugins', event, args) // 刷新
  65. event.sender.send('download_plugin_success', filename)
  66. })
  67. // 删除插件
  68. on('delete_plugin').subscribe(async ({ event, args }) => {
  69. const { filename } = args
  70. await remove(pluginsPath + '/' + filename)
  71. ipcMain.emit('reload_local_plugins', event, args) // 刷新
  72. event.sender.send('delete_plugin_success', filename)
  73. })
  74. export default loadPlugins

前端优化

首先我们安装一下依赖,transitions 里面是动画指令,extras 里面是帮助方法,比如数组的 push。

  1. npm install --save svelte-transitions svelte-extras

修改 package.json,添加打包白名单。

  1. "whiteListedModules": [
  2. "marked",
  3. "svelte-transitions",
  4. "svelte-extras"
  5. ],

自定义模板

新建 src/index.ejs ,文档里面没有说明如何自定义模板,但是源码里面有处理逻辑。-webkit-app-region 是为了让无边框的 App 可以支持拖动。

  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <title>App</title>
  8. <style>
  9. html {
  10. background: #fff;
  11. -webkit-app-region: drag
  12. }
  13. * {
  14. font-family: "PingFang SC";
  15. margin: 0;
  16. padding: 0;
  17. box-sizing: border-box;
  18. }
  19. </style>
  20. </head>
  21. <body>
  22. <div id="app"></div>
  23. </body>
  24. </html>

添加全局消息组件

新建 compoennts/Tip.svelte,这个用于全局消息提示。

  1. {#if $msg.content.length}
  2. <p class={$msg.type} transition:slide>{$msg.content}</p>
  3. {/if}
  4. <script>
  5. import {
  6. slide
  7. } from 'svelte-transitions'
  8. export default {
  9. transitions: {
  10. slide
  11. },
  12. }
  13. </script>
  14. <style>
  15. .info {
  16. background: #6633FF;
  17. }
  18. .warring {
  19. background: #FF9900;
  20. }
  21. .success {
  22. background: #4BCB7C;
  23. }
  24. p {
  25. line-height: 60px;
  26. text-align: center;
  27. color: #fff;
  28. font-size: .9rem;
  29. letter-spacing: 5px;
  30. cursor: default;
  31. user-select: none;
  32. display: block;
  33. transition: background .5s;
  34. }
  35. @keyframes flash {
  36. 0% {
  37. opacity: 0;
  38. }
  39. 100% {
  40. opacity: 1;
  41. }
  42. }
  43. p::after,
  44. p::before {
  45. animation: flash .7s ease-in infinite alternate
  46. }
  47. p::after {
  48. content: '·'
  49. }
  50. p::before {
  51. content: '·'
  52. }
  53. </style>

定义全局状态

添加好这些方法,当然别忘记在 store 里面初始化 msg,这些都是提示消息的帮助方法。

  1. function setMsg(type, content) {
  2. store.set({
  3. msg: {
  4. type,
  5. content
  6. }
  7. })
  8. }
  9. function resetMsg() {
  10. store.set({
  11. msg: {
  12. type: 'info',
  13. content: ''
  14. }
  15. })
  16. }
  17. function success(content, timer = 1000) {
  18. store.set({
  19. msg: {
  20. type: 'success',
  21. content
  22. }
  23. })
  24. setTimeout(resetMsg, timer)
  25. }
  26. store.setMsg = setMsg.bind(store)
  27. store.success = success.bind(store)

修改 App.svelte

添加全局提示组件

  1. <Tip/>
  2. <svelte:component this="{$currentPage}" name="page" />
  3. <script>
  4. export default {
  5. components: {
  6. Tip: './components/Tip.svelte'
  7. },
  8. };
  9. </script>

添加回退按钮

就像手机端回退的按钮一样,新建 components/Back.svelte,这里的按钮我们通过 css 画出来。

  1. <Link className="back" to="Main"><i class="back-icon"></i></Link>
  2. <script>
  3. export default {
  4. components: {
  5. Link: './Link.svelte'
  6. }
  7. }
  8. </script>
  9. <style>
  10. .back-icon {
  11. border-left: 4px solid #fff;
  12. border-bottom: 4px solid #fff;
  13. display: inline-block;
  14. width: 15px;
  15. height: 15px;
  16. border-radius: 2px;
  17. transform: rotate(45deg);
  18. margin: 8px 8px 8px 0.8rem;
  19. }
  20. :global(.back) {
  21. width: 100%;
  22. height: 60px;
  23. background: #383A41;
  24. display: flex;
  25. align-items: center;
  26. }
  27. </style>