章节内容
历史介绍
开发环境
代码封装
函数式
获取小说命令行版(上)
获取小说命令行版(中)
获取小说命令行版(下)
嵌入到应用中
插件机制
文字转语音
设置与信号中断
插件商店
下载与删除(上)
下载与删除(下)
通知栏程序
播放功能
美化界面
数据可视化(上)
数据可视化(中)
数据可视化(下)
嵌入到应用中
打包应用

首先我们来看效果。

gif

 本章修改的内容比较多,因为牵扯了比较多的前端样式,可以运行的代码在这里 大家可以对照查漏补缺。

# 接受下载和删除信号

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

import { app, ipcMain } from 'electron'
import { resolve } from 'path'
import { readdirSync, ensureDirSync, writeFile, remove } from 'fs-extra'
import requireFoolWebpack from 'require-fool-webpack'
import { store } from './tray'
import { promisified } from 'phin'
import { on } from './helper'

const pluginsPath = store.get(
  'PLUGIN_PATH',
  resolve(app.getPath('home'), '.reader-app-scripts')
)
ensureDirSync(pluginsPath)

// https://github.com/webpack/webpack/issues/196
// https://github.com/sindresorhus/require-fool-webpack

const loadPlugins = () => {
  let files = readdirSync(pluginsPath)
  return [
    files,
    files.map(filename =>
      requireFoolWebpack(resolve(pluginsPath, filename))(requireFoolWebpack)
    )
  ]
}
const saveToSetting = (all?: any) => {
  const [files, _] = loadPlugins()
  store.set('LOACAL_PLUGINS', files)
  all && ipcMain.emit('get_local_plugins', all.event, all.args)
}

saveToSetting()

on('reload_local_plugins').subscribe(saveToSetting)
on('get_local_plugins').subscribe(({ event, args }) => {
  event.sender.send('local_plugins', store.get('LOACAL_PLUGINS', []))
})

const storeURL =
  'https://raw.githubusercontent.com/MiYogurt/reader-store/master/db.json'

const fetchAllPlugins = (event: any) => {
  promisified(storeURL).then((res: any) => {
    let db = []
    try {
      db = JSON.parse(res.body.toString())
    } catch (e) {}
    event.sender.send('all_plugins', db)
    store.set('ALL_PLUGINS', db)
  })
}

on('reload_all_plugins').subscribe(async ({ event, args }) => {
  await fetchAllPlugins(event)
  ipcMain.emit('get_all_plugins', event, args)
})

on('get_all_plugins').subscribe(({ event, args }) => {
  const db = store.get('ALL_PLUGINS', [])
  if (db.length > 0) {
    return event.sender.send('all_plugins', db)
  }
  fetchAllPlugins(event.sender.send)
})

// 下载插件
on('download_plugin').subscribe(async ({ event, args }) => {
  const { filename, url } = args
  await promisified(url)
    .then(({ body }) => body.toString())
    .then(data => (console.log(data), data)) // 查看一下数据内容
    .then(data => writeFile(pluginsPath + '/' + filename, data)) // 写入插件
  ipcMain.emit('reload_local_plugins', event, args) // 刷新
  event.sender.send('download_plugin_success', filename)
})

// 删除插件
on('delete_plugin').subscribe(async ({ event, args }) => {
  const { filename } = args
  await remove(pluginsPath + '/' + filename)
  ipcMain.emit('reload_local_plugins', event, args) // 刷新
  event.sender.send('delete_plugin_success', filename)
})

export default loadPlugins

# 前端优化

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

npm install --save svelte-transitions svelte-extras

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

"whiteListedModules": [
    "marked",
    "svelte-transitions",
    "svelte-extras"
],

# 自定义模板

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

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>App</title>
    <style>
        html {
            background: #fff;
            -webkit-app-region: drag
        }

        * {
            font-family: "PingFang SC";
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
    </style>
</head>

<body>
    <div id="app"></div>
</body>

</html>

# 添加全局消息组件

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

{#if $msg.content.length}
<p class={$msg.type} transition:slide>{$msg.content}</p>
{/if}

<script>
    import {
        slide
    } from 'svelte-transitions'
    export default {
        transitions: {
            slide
        },
    }
</script>
<style>
    .info {
        background: #6633FF;
    }

    .warring {
        background: #FF9900;
    }

    .success {
        background: #4BCB7C;
    }

    p {
        line-height: 60px;
        text-align: center;
        color: #fff;
        font-size: .9rem;
        letter-spacing: 5px;
        cursor: default;
        user-select: none;
        display: block;
        transition: background .5s;
    }

    @keyframes flash {
        0% {
            opacity: 0;
        }
        100% {
            opacity: 1;
        }
    }

    p::after,
    p::before {
        animation: flash .7s ease-in infinite alternate
    }

    p::after {
        content: '·'
    }

    p::before {
        content: '·'
    }
</style>

# 定义全局状态

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

function setMsg(type, content) {
  store.set({
    msg: {
      type,
      content
    }
  })
}

function resetMsg() {
  store.set({
    msg: {
      type: 'info',
      content: ''
    }
  })
}

function success(content, timer = 1000) {
  store.set({
    msg: {
      type: 'success',
      content
    }
  })
  setTimeout(resetMsg, timer)
}

store.setMsg = setMsg.bind(store)
store.success = success.bind(store)

# 修改 App.svelte

添加全局提示组件

<Tip/>
<svelte:component this="{$currentPage}" name="page" />

<script>
    export default {
        components: {
            Tip: './components/Tip.svelte'
        },
    };
</script>

# 添加回退按钮

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

<Link className="back" to="Main"><i class="back-icon"></i></Link>

<script>
    export default {
        components: {
            Link: './Link.svelte'
        }
    }
</script>

<style>
    .back-icon {
        border-left: 4px solid #fff;
        border-bottom: 4px solid #fff;
        display: inline-block;
        width: 15px;
        height: 15px;
        border-radius: 2px;
        transform: rotate(45deg);
        margin: 8px 8px 8px 0.8rem;
    }

     :global(.back) {
        width: 100%;
        height: 60px;
        background: #383A41;
        display: flex;
        align-items: center;
    }
</style>