feat(ui): PluginApi: notify

This commit is contained in:
Guillaume Chau
2018-06-10 15:03:39 +02:00
parent dbef5e9fed
commit e35ee2500d
8 changed files with 114 additions and 20 deletions

View File

@@ -966,6 +966,23 @@ api.db.get('posts')
const { storageGet, storageSet } = api.namespace('my-plugin.')
```
### Notification
You can display notifications using the user OS notification system:
```js
api.notify({
title: 'Some title',
message: 'Some message',
icon: 'path-to-icon.png'
})
```
There are some builtin icons:
- `'done'`
- `'error'`
### Localization
You can put locale files compatible with [vue-i18n](https://github.com/kazupon/vue-i18n) in a `locales` folder at the root of your plugin. They will be automatically loaded into the client when the project is opened. You can then use `$t` to translate strings in your components and other vue-i18n helpers. Also, the strings used in the UI API (like `describeTask`) will go through vue-i18n as well to you can localize them.

View File

@@ -1,6 +1,9 @@
module.exports = api => {
const { setSharedData, removeSharedData } = api.namespace('webpack-dashboard-')
let firstRun = true
let hadFailed = false
function resetSharedData (key) {
setSharedData(`${key}-status`, null)
setSharedData(`${key}-progress`, 0)
@@ -15,6 +18,33 @@ module.exports = api => {
const type = message.webpackDashboardData.type
for (const data of message.webpackDashboardData.value) {
setSharedData(`${type}-${data.type}`, data.value)
if (type === 'serve' && data.type === 'status') {
if (data.value === 'Failed') {
api.notify({
title: 'Build failed',
message: 'The build has errors.',
icon: 'error'
})
hadFailed = true
} else if (data.value === 'Success') {
if (hadFailed) {
api.notify({
title: 'Build fixed',
message: 'The build succeeded.',
icon: 'done'
})
hadFailed = false
} else if (firstRun) {
api.notify({
title: 'App ready',
message: 'The build succeeded.',
icon: 'done'
})
firstRun = false
}
}
}
}
}
}
@@ -107,6 +137,8 @@ module.exports = api => {
// Data
resetSharedData('serve')
removeSharedData('serve-url')
firstRun = true
hadFailed = false
},
onRun: () => {
api.ipcOn(onWebpackMessage)

View File

@@ -4,11 +4,13 @@ const sharedData = require('../connectors/shared-data')
const views = require('../connectors/views')
// Utils
const ipc = require('../utils/ipc')
const { notify } = require('../utils/notification')
// Validators
const { validateConfiguration } = require('./configuration')
const { validateDescribeTask, validateAddTask } = require('./task')
const { validateClientAddon } = require('./client-addon')
const { validateView, validateBadge } = require('./view')
const { validateNotify } = require('./notify')
class PluginApi {
constructor (context) {
@@ -264,6 +266,24 @@ class PluginApi {
return this.context.db
}
/**
* Display a notification in the user OS
* @param {object} options Notification options
*/
notify (options) {
try {
validateNotify(options)
notify(options)
} catch (e) {
logs.add({
type: 'error',
tag: 'PluginApi',
message: `(${this.pluginId || 'unknown plugin'}) 'notify' options are invalid\n${e.message}`
}, this.context)
console.error(new Error(`Invalid options: ${e.message}`))
}
}
/* Namespaced */
/**

View File

@@ -0,0 +1,11 @@
const { createSchema, validateSync } = require('@vue/cli-shared-utils')
const schema = createSchema(joi => ({
title: joi.string().required(),
message: joi.string().required(),
icon: joi.string()
}))
exports.validateNotify = (options) => {
validateSync(options, schema)
}

View File

@@ -16,7 +16,6 @@ const {
updatePackage
} = require('@vue/cli/lib/util/installDeps')
const invoke = require('@vue/cli/lib/invoke')
const notifier = require('node-notifier')
// Subs
const channels = require('../channels')
// Connectors
@@ -36,6 +35,7 @@ const { getCommand } = require('../utils/command')
const { resolveModuleRoot } = require('../utils/resolve-path')
const ipc = require('../utils/ipc')
const { log } = require('../utils/logger')
const { notify } = require('../utils/notification')
const PROGRESS_ID = 'plugin-installation'
const CLI_SERVICE = '@vue/cli-service'
@@ -264,10 +264,10 @@ function install (id, context) {
await initPrompts(id, context)
installationStep = 'config'
notifier.notify({
notify({
title: `Plugin installed`,
message: `Plugin ${id} installed, next step is configuration`,
icon: path.resolve(__dirname, '../../assets/done.png')
icon: 'done'
})
return getInstallation(context)
@@ -297,10 +297,10 @@ function uninstall (id, context) {
currentPluginId = null
installationStep = null
notifier.notify({
notify({
title: `Plugin uninstalled`,
message: `Plugin ${id} uninstalled`,
icon: path.resolve(__dirname, '../../assets/done.png')
icon: 'done'
})
return getInstallation(context)
@@ -329,10 +329,10 @@ function runInvoke (id, context) {
runPluginApi(id, context)
installationStep = 'diff'
notifier.notify({
notify({
title: `Plugin invoke sucess`,
message: `Plugin ${id} invoked successfully`,
icon: path.resolve(__dirname, '../../assets/done.png')
icon: 'done'
})
return getInstallation(context)
@@ -377,10 +377,10 @@ function update (id, context, notify = true) {
}, context)
if (notify) {
notifier.notify({
notify({
title: `Plugin updated`,
message: `Plugin ${id} was successfully updated`,
icon: path.resolve(__dirname, '../../assets/done.png')
icon: 'done'
})
}
@@ -399,10 +399,10 @@ async function updateAll (context) {
}
}
notifier.notify({
notify({
title: `Plugins updated`,
message: `${updatedPlugins.length} plugin(s) were successfully updated`,
icon: path.resolve(__dirname, '../../assets/done.png')
icon: 'done'
})
return updatedPlugins

View File

@@ -7,7 +7,6 @@ const { getFeatures } = require('@vue/cli/lib/util/features')
const { defaults } = require('@vue/cli/lib/options')
const { toShortPluginId } = require('@vue/cli-shared-utils')
const { progress: installProgress } = require('@vue/cli/lib/util/installDeps')
const notifier = require('node-notifier')
// Connectors
const progress = require('./progress')
const cwd = require('./cwd')
@@ -19,6 +18,7 @@ const locales = require('./locales')
const getContext = require('../context')
// Utils
const { log } = require('../utils/logger')
const { notify } = require('../utils/notification')
const PROGRESS_ID = 'project-create'
@@ -309,10 +309,10 @@ async function create (input, context) {
await creator.create({ git: true }, preset)
removeCreator()
notifier.notify({
notify({
title: `Project created`,
message: `Project ${cwd.get()} created`,
icon: path.resolve(__dirname, '../../assets/done.png')
icon: 'done'
})
return importProject({

View File

@@ -1,7 +1,5 @@
const path = require('path')
const execa = require('execa')
const terminate = require('terminate')
const notifier = require('node-notifier')
// Subs
const channels = require('../channels')
// Connectors
@@ -14,6 +12,7 @@ const views = require('./views')
// Utils
const { getCommand } = require('../utils/command')
const { log } = require('../utils/logger')
const { notify } = require('../utils/notification')
const MAX_LOGS = 2000
const VIEW_ID = 'vue-project-tasks'
@@ -329,10 +328,10 @@ async function run (id, context) {
message: `Task ${task.id} ended with error code ${code}`,
type: 'error'
}, context)
notifier.notify({
notify({
title: `Task error`,
message: `Task ${task.id} ended with error code ${code}`,
icon: path.resolve(__dirname, '../../assets/error.png')
icon: 'error'
})
} else {
updateOne({
@@ -343,10 +342,10 @@ async function run (id, context) {
message: `Task ${task.id} completed`,
type: 'done'
}, context)
notifier.notify({
notify({
title: `Task completed`,
message: `Task ${task.id} completed`,
icon: path.resolve(__dirname, '../../assets/done.png')
icon: 'done'
})
}
}

View File

@@ -0,0 +1,15 @@
const path = require('path')
const notifier = require('node-notifier')
const builtinIcons = {
'done': path.resolve(__dirname, '../../assets/done.png'),
'error': path.resolve(__dirname, '../../assets/error.png')
}
exports.notify = ({ title, message, icon }) => {
notifier.notify({
title,
message,
icon: builtinIcons[icon] || icon
})
}