Skip to content

Commit

Permalink
feat(ui): PluginApi: notify
Browse files Browse the repository at this point in the history
  • Loading branch information
Akryum committed Jun 10, 2018
1 parent dbef5e9 commit e35ee25
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 20 deletions.
17 changes: 17 additions & 0 deletions docs/dev-guide/ui-plugin-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
32 changes: 32 additions & 0 deletions packages/@vue/cli-service/ui.js
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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
}
}
}
}
}
}
Expand Down Expand Up @@ -107,6 +137,8 @@ module.exports = api => {
// Data
resetSharedData('serve')
removeSharedData('serve-url')
firstRun = true
hadFailed = false
},
onRun: () => {
api.ipcOn(onWebpackMessage)
Expand Down
20 changes: 20 additions & 0 deletions packages/@vue/cli-ui/src/graphql-api/api/PluginApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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 */

/**
Expand Down
11 changes: 11 additions & 0 deletions packages/@vue/cli-ui/src/graphql-api/api/notify.js
Original file line number Diff line number Diff line change
@@ -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)
}
22 changes: 11 additions & 11 deletions packages/@vue/cli-ui/src/graphql-api/connectors/plugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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'
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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'
})
}

Expand All @@ -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
Expand Down
6 changes: 3 additions & 3 deletions packages/@vue/cli-ui/src/graphql-api/connectors/projects.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand All @@ -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'

Expand Down Expand Up @@ -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({
Expand Down
11 changes: 5 additions & 6 deletions packages/@vue/cli-ui/src/graphql-api/connectors/tasks.js
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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'
Expand Down Expand Up @@ -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({
Expand All @@ -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'
})
}
}
Expand Down
15 changes: 15 additions & 0 deletions packages/@vue/cli-ui/src/graphql-api/utils/notification.js
Original file line number Diff line number Diff line change
@@ -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
})
}

0 comments on commit e35ee25

Please sign in to comment.