Added: guiApi for plugins

This commit is contained in:
Molunerfinn
2019-01-11 17:22:33 +08:00
parent 250a014fc1
commit 927d913f3b
6 changed files with 195 additions and 28 deletions
+15 -19
View File
@@ -151,10 +151,11 @@ function createTray () {
tray.on('drop-files', async (event, files) => { tray.on('drop-files', async (event, files) => {
const pasteStyle = db.read().get('settings.pasteStyle').value() || 'markdown' const pasteStyle = db.read().get('settings.pasteStyle').value() || 'markdown'
const imgs = await new Uploader(files, 'imgFromPath', window.webContents).upload() const imgs = await new Uploader(files, window.webContents).upload()
if (imgs !== false) { if (imgs !== false) {
for (let i in imgs) { for (let i in imgs) {
clipboard.writeText(pasteTemplate(pasteStyle, imgs[i].imgUrl)) const url = imgs[i].url || imgs[i].imgUrl
clipboard.writeText(pasteTemplate(pasteStyle, url))
const notification = new Notification({ const notification = new Notification({
title: '上传成功', title: '上传成功',
body: imgs[i].imgUrl, body: imgs[i].imgUrl,
@@ -163,10 +164,8 @@ function createTray () {
setTimeout(() => { setTimeout(() => {
notification.show() notification.show()
}, i * 100) }, i * 100)
db.read().get('uploaded').insert(imgs[i]).write()
} }
imgs.forEach(item => {
db.read().get('uploaded').insert(item).write()
})
window.webContents.send('dragFiles', imgs) window.webContents.send('dragFiles', imgs)
} }
}) })
@@ -332,16 +331,15 @@ const uploadClipboardFiles = async () => {
if (img !== false) { if (img !== false) {
if (img.length > 0) { if (img.length > 0) {
const pasteStyle = db.read().get('settings.pasteStyle').value() || 'markdown' const pasteStyle = db.read().get('settings.pasteStyle').value() || 'markdown'
clipboard.writeText(pasteTemplate(pasteStyle, img[0].imgUrl)) const url = img[0].url || img[0].imgUrl
clipboard.writeText(pasteTemplate(pasteStyle, url))
const notification = new Notification({ const notification = new Notification({
title: '上传成功', title: '上传成功',
body: img[0].imgUrl, body: img[0].imgUrl,
icon: img[0].imgUrl icon: img[0].imgUrl
}) })
notification.show() notification.show()
img.forEach(item => { db.read().get('uploaded').insert(img[0]).write()
db.read().get('uploaded').insert(item).write()
})
window.webContents.send('clipboardFiles', []) window.webContents.send('clipboardFiles', [])
window.webContents.send('uploadFiles', img) window.webContents.send('uploadFiles', img)
if (settingWindow) { if (settingWindow) {
@@ -361,10 +359,11 @@ picgoCoreIPC(app, ipcMain)
// from macOS tray // from macOS tray
ipcMain.on('uploadClipboardFiles', async (evt, file) => { ipcMain.on('uploadClipboardFiles', async (evt, file) => {
const img = await new Uploader(file, 'imgFromClipboard', window.webContents).upload() const img = await new Uploader(file, window.webContents).upload()
if (img !== false) { if (img !== false) {
const pasteStyle = db.read().get('settings.pasteStyle').value() || 'markdown' const pasteStyle = db.read().get('settings.pasteStyle').value() || 'markdown'
clipboard.writeText(pasteTemplate(pasteStyle, img[0].imgUrl)) const url = img[0].url || img[0].imgUrl
clipboard.writeText(pasteTemplate(pasteStyle, url))
const notification = new Notification({ const notification = new Notification({
title: '上传成功', title: '上传成功',
body: img[0].imgUrl, body: img[0].imgUrl,
@@ -372,9 +371,7 @@ ipcMain.on('uploadClipboardFiles', async (evt, file) => {
icon: img[0].imgUrl icon: img[0].imgUrl
}) })
notification.show() notification.show()
img.forEach(item => { db.read().get('uploaded').insert(img[0]).write()
db.read().get('uploaded').insert(item).write()
})
window.webContents.send('clipboardFiles', []) window.webContents.send('clipboardFiles', [])
window.webContents.send('uploadFiles') window.webContents.send('uploadFiles')
if (settingWindow) { if (settingWindow) {
@@ -389,12 +386,13 @@ ipcMain.on('uploadClipboardFilesFromUploadPage', () => {
ipcMain.on('uploadChoosedFiles', async (evt, files) => { ipcMain.on('uploadChoosedFiles', async (evt, files) => {
const input = files.map(item => item.path) const input = files.map(item => item.path)
const imgs = await new Uploader(input, 'imgFromUploader', evt.sender).upload() const imgs = await new Uploader(input, evt.sender).upload()
if (imgs !== false) { if (imgs !== false) {
const pasteStyle = db.read().get('settings.pasteStyle').value() || 'markdown' const pasteStyle = db.read().get('settings.pasteStyle').value() || 'markdown'
let pasteText = '' let pasteText = ''
for (let i in imgs) { for (let i in imgs) {
pasteText += pasteTemplate(pasteStyle, imgs[i].imgUrl) + '\r\n' const url = imgs[i].url || imgs[i].imgUrl
pasteText += pasteTemplate(pasteStyle, url) + '\r\n'
const notification = new Notification({ const notification = new Notification({
title: '上传成功', title: '上传成功',
body: imgs[i].imgUrl, body: imgs[i].imgUrl,
@@ -403,11 +401,9 @@ ipcMain.on('uploadChoosedFiles', async (evt, files) => {
setTimeout(() => { setTimeout(() => {
notification.show() notification.show()
}, i * 100) }, i * 100)
db.read().get('uploaded').insert(imgs[i]).write()
} }
clipboard.writeText(pasteText) clipboard.writeText(pasteText)
imgs.forEach(item => {
db.read().get('uploaded').insert(item).write()
})
window.webContents.send('uploadFiles', imgs) window.webContents.send('uploadFiles', imgs)
if (settingWindow) { if (settingWindow) {
settingWindow.webContents.send('updateGallery') settingWindow.webContents.send('updateGallery')
+85
View File
@@ -0,0 +1,85 @@
import {
dialog,
BrowserWindow,
clipboard,
Notification
} from 'electron'
import db from '../../datastore'
import Uploader from './uploader'
import pasteTemplate from './pasteTemplate'
const WEBCONTENTS = Symbol('WEBCONTENTS')
const IPCMAIN = Symbol('IPCMAIN')
class GuiApi {
constructor (ipcMain, webcontents) {
this[WEBCONTENTS] = webcontents
this[IPCMAIN] = ipcMain
}
/**
* for plugin showInputBox
* @param {object} options
* return type is string or ''
*/
showInputBox (options) {
if (options === undefined) {
options = {
title: '',
placeholder: ''
}
}
this[WEBCONTENTS].send('showInputBox', options)
return new Promise((resolve, reject) => {
this[IPCMAIN].once('showInputBox', (event, value) => {
resolve(value)
})
})
}
/**
* for plugin show file explorer
* @param {object} options
*/
showFileExplorer (options) {
if (options === undefined) {
options = {}
}
return new Promise((resolve, reject) => {
dialog.showOpenDialog(BrowserWindow.fromWebContents(this[WEBCONTENTS]), options, filename => {
resolve(filename)
})
})
}
/**
* for plugin to upload file
* @param {array} input
*/
async upload (input) {
const imgs = await new Uploader(input, this[WEBCONTENTS]).upload()
if (imgs !== false) {
const pasteStyle = db.read().get('settings.pasteStyle').value() || 'markdown'
let pasteText = ''
for (let i in imgs) {
const url = imgs[i].url || imgs[i].imgUrl
pasteText += pasteTemplate(pasteStyle, url) + '\r\n'
const notification = new Notification({
title: '上传成功',
body: imgs[i].imgUrl,
icon: imgs[i].imgUrl
})
setTimeout(() => {
notification.show()
}, i * 100)
db.read().get('uploaded').insert(imgs[i]).write()
}
clipboard.writeText(pasteText)
this[WEBCONTENTS].send('uploadFiles', imgs)
this[WEBCONTENTS].send('updateGallery')
return imgs
}
return []
}
}
export default GuiApi
+15
View File
@@ -1,4 +1,5 @@
import path from 'path' import path from 'path'
import GuiApi from './guiApi'
// eslint-disable-next-line // eslint-disable-next-line
const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require
@@ -50,6 +51,7 @@ const handleGetPluginList = (ipcMain, STORE_PATH, CONFIG_PATH) => {
description: pluginPKG.description, description: pluginPKG.description,
logo: 'file://' + path.join(pluginPath, 'logo.png').split(path.sep).join('/'), logo: 'file://' + path.join(pluginPath, 'logo.png').split(path.sep).join('/'),
version: pluginPKG.version, version: pluginPKG.version,
gui: pluginPKG.gui || false,
config: { config: {
plugin: { plugin: {
name: pluginList[i].replace(/picgo-plugin-/, ''), name: pluginList[i].replace(/picgo-plugin-/, ''),
@@ -66,6 +68,7 @@ const handleGetPluginList = (ipcMain, STORE_PATH, CONFIG_PATH) => {
}, },
enabled: picgo.getConfig(`plugins.${pluginList[i]}`), enabled: picgo.getConfig(`plugins.${pluginList[i]}`),
homepage: pluginPKG.homepage ? pluginPKG.homepage : '', homepage: pluginPKG.homepage ? pluginPKG.homepage : '',
guiActions: typeof plugin.guiActions === 'function',
ing: false ing: false
} }
list.push(obj) list.push(obj)
@@ -125,6 +128,17 @@ const handleGetPicBedConfig = (ipcMain, CONFIG_PATH) => {
}) })
} }
const handlePluginActions = (ipcMain, CONFIG_PATH) => {
ipcMain.on('pluginActions', (event, name) => {
const picgo = new PicGo(CONFIG_PATH)
const plugin = picgo.pluginLoader.getPlugin(`picgo-plugin-${name}`)
const guiApi = new GuiApi(ipcMain, event.sender)
if (plugin.guiActions && typeof plugin.guiActions === 'function') {
plugin.guiActions(picgo, guiApi)
}
})
}
export default (app, ipcMain) => { export default (app, ipcMain) => {
const STORE_PATH = app.getPath('userData') const STORE_PATH = app.getPath('userData')
const CONFIG_PATH = path.join(STORE_PATH, '/data.json') const CONFIG_PATH = path.join(STORE_PATH, '/data.json')
@@ -133,4 +147,5 @@ export default (app, ipcMain) => {
handlePluginUninstall(ipcMain, CONFIG_PATH) handlePluginUninstall(ipcMain, CONFIG_PATH)
handlePluginUpdate(ipcMain, CONFIG_PATH) handlePluginUpdate(ipcMain, CONFIG_PATH)
handleGetPicBedConfig(ipcMain, CONFIG_PATH) handleGetPicBedConfig(ipcMain, CONFIG_PATH)
handlePluginActions(ipcMain, CONFIG_PATH)
} }
+6 -3
View File
@@ -75,9 +75,8 @@ const waitForRename = (window, id) => {
} }
class Uploader { class Uploader {
constructor (img, type, webContents) { constructor (img, webContents) {
this.img = img this.img = img
this.type = type
this.webContents = webContents this.webContents = webContents
} }
@@ -142,7 +141,11 @@ class Uploader {
} }
}) })
picgo.on('failed', ctx => { picgo.on('failed', ctx => {
console.log(ctx) const notification = new Notification({
title: '上传失败',
body: '请检查配置和上传的文件是否符合要求'
})
notification.show()
resolve(false) resolve(false)
}) })
}) })
+6 -3
View File
@@ -79,7 +79,7 @@
> >
</div> </div>
<div class="gallery-list__tool-panel"> <div class="gallery-list__tool-panel">
<i class="el-icon-document" @click="copy(item.imgUrl)"></i> <i class="el-icon-document" @click="copy(item)"></i>
<i class="el-icon-edit-outline" @click="openDialog(item)"></i> <i class="el-icon-edit-outline" @click="openDialog(item)"></i>
<i class="el-icon-delete" @click="remove(item.id)"></i> <i class="el-icon-delete" @click="remove(item.id)"></i>
<el-checkbox v-model="choosedList[item.id]" class="pull-right" @change=" handleBarActive = true"></el-checkbox> <el-checkbox v-model="choosedList[item.id]" class="pull-right" @change=" handleBarActive = true"></el-checkbox>
@@ -208,7 +208,8 @@ export default {
this.idx = null this.idx = null
this.changeZIndexForGallery(false) this.changeZIndexForGallery(false)
}, },
copy (url) { copy (item) {
const url = item.url || item.imgUrl
const style = this.$db.read().get('settings.pasteStyle').value() || 'markdown' const style = this.$db.read().get('settings.pasteStyle').value() || 'markdown'
const copyLink = pasteStyle(style, url) const copyLink = pasteStyle(style, url)
const obj = { const obj = {
@@ -313,7 +314,9 @@ export default {
// choosedList -> { [id]: true or false }; true means choosed. false means not choosed. // choosedList -> { [id]: true or false }; true means choosed. false means not choosed.
Object.keys(this.choosedList).forEach(key => { Object.keys(this.choosedList).forEach(key => {
if (this.choosedList[key]) { if (this.choosedList[key]) {
copyString += pasteStyle(style, this.$db.read().get('uploaded').getById(key).value().imgUrl) + '\n' const item = this.$db.read().get('uploaded').getById(key).value()
const url = item.url || item.imgUrl
copyString += pasteStyle(style, url) + '\n'
this.choosedList[key] = false this.choosedList[key] = false
} }
}) })
+68 -3
View File
@@ -15,6 +15,7 @@
<el-row :gutter="10" class="plugin-list" v-loading="loading"> <el-row :gutter="10" class="plugin-list" v-loading="loading">
<el-col :span="12" v-for="item in pluginList" :key="item.name"> <el-col :span="12" v-for="item in pluginList" :key="item.name">
<div class="plugin-item" :class="{ 'darwin': os === 'darwin' }"> <div class="plugin-item" :class="{ 'darwin': os === 'darwin' }">
<div class="cli-only-badge" v-if="!item.gui" title="CLI only">CLI</div>
<img class="plugin-item__logo" :src="item.logo" <img class="plugin-item__logo" :src="item.logo"
onerror="this.src='static/logo.png'" onerror="this.src='static/logo.png'"
> >
@@ -90,6 +91,20 @@
<el-button type="primary" @click="handleConfirmConfig" round>确定</el-button> <el-button type="primary" @click="handleConfirmConfig" round>确定</el-button>
</span> </span>
</el-dialog> </el-dialog>
<el-dialog
:title="inputBoxOptions.title || '输入框'"
:visible.sync="showInputBoxVisible"
:modal-append-to-body="false"
@close="handleInputBoxClose"
>
<el-input
v-model="inputBoxValue"
:placeholder="inputBoxOptions.placeholder"></el-input>
<span slot="footer">
<el-button @click="showInputBoxVisible = false" round>取消</el-button>
<el-button type="primary" @click="showInputBoxVisible = false" round>确定</el-button>
</span>
</el-dialog>
</div> </div>
</template> </template>
<script> <script>
@@ -113,7 +128,14 @@ export default {
loading: true, loading: true,
needReload: false, needReload: false,
id: '', id: '',
os: '' os: '',
// for showInputBox
showInputBoxVisible: false,
inputBoxValue: '',
inputBoxOptions: {
title: '',
placeholder: ''
}
} }
}, },
computed: { computed: {
@@ -183,6 +205,12 @@ export default {
this.getPluginList() this.getPluginList()
this.getSearchResult = debounce(this.getSearchResult, 50) this.getSearchResult = debounce(this.getSearchResult, 50)
this.needReload = this.$db.read().get('needReload').value() this.needReload = this.$db.read().get('needReload').value()
this.$electron.ipcRenderer.on('showInputBox', (evt, options) => {
this.inputBoxValue = ''
this.inputBoxOptions.title = options.title || ''
this.inputBoxOptions.placeholder = options.placeholder || ''
this.showInputBoxVisible = true
})
}, },
methods: { methods: {
buildContextMenu (plugin) { buildContextMenu (plugin) {
@@ -247,6 +275,16 @@ export default {
} }
menu.push(obj) menu.push(obj)
} }
if (plugin.guiActions) {
menu.push({
label: '运行Actions',
click () {
_this.$electron.ipcRenderer.send('pluginActions', plugin.name)
}
})
}
this.menu = this.$electron.remote.Menu.buildFromTemplate(menu) this.menu = this.$electron.remote.Menu.buildFromTemplate(menu)
this.menu.popup(this.$electron.remote.getCurrentWindow()) this.menu.popup(this.$electron.remote.getCurrentWindow())
}, },
@@ -257,8 +295,21 @@ export default {
this.$electron.ipcRenderer.send('getPicBeds') this.$electron.ipcRenderer.send('getPicBeds')
}, },
installPlugin (item) { installPlugin (item) {
item.ing = true if (!item.gui) {
this.$electron.ipcRenderer.send('installPlugin', item.name) this.$confirm('该插件未对可视化界面进行优化, 是否继续安装?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
item.ing = true
this.$electron.ipcRenderer.send('installPlugin', item.name)
}).catch(() => {
console.log('Install canceled')
})
} else {
item.ing = true
this.$electron.ipcRenderer.send('installPlugin', item.name)
}
}, },
uninstallPlugin (val) { uninstallPlugin (val) {
this.pluginList.forEach(item => { this.pluginList.forEach(item => {
@@ -350,6 +401,7 @@ export default {
homepage: item.package.links ? item.package.links.homepage : '', homepage: item.package.links ? item.package.links.homepage : '',
hasInstall: this.pluginNameList.some(plugin => plugin === item.package.name.replace(/picgo-plugin-/, '')), hasInstall: this.pluginNameList.some(plugin => plugin === item.package.name.replace(/picgo-plugin-/, '')),
version: item.package.version, version: item.package.version,
gui: item.package.gui || false,
ing: false // installing or uninstalling ing: false // installing or uninstalling
} }
}, },
@@ -372,6 +424,9 @@ export default {
if (url) { if (url) {
this.$electron.remote.shell.openExternal(url) this.$electron.remote.shell.openExternal(url)
} }
},
handleInputBoxClose () {
this.$electron.ipcRenderer.send('showInputBox', this.inputBoxValue)
} }
}, },
beforeDestroy () { beforeDestroy () {
@@ -379,6 +434,7 @@ export default {
this.$electron.ipcRenderer.removeAllListeners('installSuccess') this.$electron.ipcRenderer.removeAllListeners('installSuccess')
this.$electron.ipcRenderer.removeAllListeners('uninstallSuccess') this.$electron.ipcRenderer.removeAllListeners('uninstallSuccess')
this.$electron.ipcRenderer.removeAllListeners('updateSuccess') this.$electron.ipcRenderer.removeAllListeners('updateSuccess')
this.$electron.ipcRenderer.removeAllListeners('showInputBox')
} }
} }
</script> </script>
@@ -422,6 +478,15 @@ $darwinBg = #172426
user-select text user-select text
transition all .2s ease-in-out transition all .2s ease-in-out
margin-bottom 10px margin-bottom 10px
position relative
.cli-only-badge
position absolute
right 0px
top 0
font-size 12px
padding 3px 8px
background #49B1F5
color #eee
&.darwin &.darwin
background transparentify($darwinBg, #000, 0.75) background transparentify($darwinBg, #000, 0.75)
&:hover &:hover