📦 Chore: update electron from v6 -> v16

This commit is contained in:
PiEgg 2022-01-04 23:40:28 +08:00
parent 459953f391
commit ea20d3b971
61 changed files with 6928 additions and 5582 deletions

View File

@ -6,20 +6,32 @@ module.exports = {
env: { env: {
node: true node: true
}, },
parser: "vue-eslint-parser", parser: 'vue-eslint-parser',
'extends': [ extends: [
'plugin:vue/essential', 'plugin:vue/essential',
'@vue/standard', '@vue/standard',
'@vue/typescript' '@vue/typescript'
], ],
'plugins': ['@typescript-eslint'], plugins: ['@typescript-eslint'],
rules: { rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'off' : 'off', 'no-console': process.env.NODE_ENV === 'production' ? 'off' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
"indent": "off", indent: 'off',
"@typescript-eslint/indent": ["error", 2] 'no-async-promise-executor': 'off',
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': 'error',
'@typescript-eslint/indent': ['error', 2]
}, },
parserOptions: { parserOptions: {
parser: '@typescript-eslint/parser' parser: '@typescript-eslint/parser'
} },
overrides: [
{
files: ['*.ts', '*.vue'],
rules: {
'no-undef': 'off' // https://typescript-eslint.io/docs/linting/troubleshooting/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors
}
}
],
ignorePatterns: ['src/**/*.d.ts']
} }

View File

@ -1,5 +1,6 @@
module.exports = { module.exports = {
presets: [ presets: [
'@vue/cli-plugin-babel/preset' '@vue/cli-plugin-babel/preset'
] ],
plugins: ['@babel/plugin-proposal-optional-chaining']
} }

View File

@ -22,7 +22,7 @@
.col-xs-10.col-xs-offset-1.col-md-7.col-md-offset-0 .col-xs-10.col-xs-offset-1.col-md-7.col-md-offset-0
img(:src="item.url") img(:src="item.url")
.col-xs-10.col-xs-offset-1.col-md-5.col-md-offset-0.display-list__content .col-xs-10.col-xs-offset-1.col-md-5.col-md-offset-0.display-list__content
.display-list__title {{ item.title }} .display-list__title {{ item.title }}
.display-list__desc {{ item.desc }} .display-list__desc {{ item.desc }}
.row.ex-width.info .row.ex-width.info
.col-xs-10.col-xs-offset-1 .col-xs-10.col-xs-offset-1
@ -94,7 +94,7 @@ html,
h1 h1
margin 0 margin 0
padding 0 padding 0
font-family "Source Sans Pro","Helvetica Neue","PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif font-family "Source Sans Pro","Helvetica Neue","PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif
#app #app
position relative position relative
.mask .mask
@ -213,4 +213,4 @@ h1
font-size 25px font-size 25px
&__desc &__desc
margin-top 12px margin-top 12px
</style> </style>

View File

@ -6,6 +6,7 @@
"dev": "vue-cli-service electron:serve", "dev": "vue-cli-service electron:serve",
"build": "vue-cli-service electron:build", "build": "vue-cli-service electron:build",
"lint": "vue-cli-service lint", "lint": "vue-cli-service lint",
"lint:fix": "eslint --fix --ext .js,.jsx,.ts,.tsx,.vue src/",
"electron:build": "vue-cli-service electron:build", "electron:build": "vue-cli-service electron:build",
"electron:serve": "vue-cli-service electron:serve", "electron:serve": "vue-cli-service electron:serve",
"postinstall": "electron-builder install-app-deps", "postinstall": "electron-builder install-app-deps",
@ -39,7 +40,7 @@
"core-js": "^3.3.2", "core-js": "^3.3.2",
"element-ui": "^2.13.0", "element-ui": "^2.13.0",
"fix-path": "^2.1.0", "fix-path": "^2.1.0",
"fs-extra": "^8.1.0", "fs-extra": "^10.0.0",
"keycode": "^2.2.0", "keycode": "^2.2.0",
"lodash-id": "^0.14.0", "lodash-id": "^0.14.0",
"lowdb": "^1.0.0", "lowdb": "^1.0.0",
@ -52,36 +53,42 @@
"vue-router": "^3.1.3" "vue-router": "^3.1.3"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^8.2.0", "@babel/plugin-proposal-optional-chaining": "^7.16.7",
"@picgo/bump-version": "^1.0.3", "@picgo/bump-version": "^1.1.2",
"@types/fs-extra": "^8.0.1", "@types/fs-extra": "^9.0.13",
"@types/inquirer": "^6.5.0", "@types/inquirer": "^6.5.0",
"@types/lowdb": "^1.0.9", "@types/lowdb": "^1.0.9",
"@types/node": "10.17.6", "@types/node": "^16.10.2",
"@types/request-promise-native": "^1.0.17", "@types/request-promise-native": "^1.0.17",
"@types/semver": "^7.3.8",
"@typescript-eslint/eslint-plugin": "^4.33.0",
"@typescript-eslint/parser": "^4.33.0",
"@vue/cli-plugin-babel": "^4.0.0", "@vue/cli-plugin-babel": "^4.0.0",
"@vue/cli-plugin-eslint": "^4.0.0", "@vue/cli-plugin-eslint": "^4.0.0",
"@vue/cli-plugin-router": "^4.0.0", "@vue/cli-plugin-router": "^4.0.0",
"@vue/cli-plugin-typescript": "^4.0.0", "@vue/cli-plugin-typescript": "^4.5.13",
"@vue/cli-service": "^4.0.0", "@vue/cli-service": "^4.0.0",
"@vue/eslint-config-standard": "^4.0.0", "@vue/eslint-config-standard": "^6.1.0",
"@vue/eslint-config-typescript": "^4.0.0", "@vue/eslint-config-typescript": "^7.0.0",
"commitizen": "^4.0.3",
"conventional-changelog": "^3.1.18", "conventional-changelog": "^3.1.18",
"cz-customizable": "^6.2.0", "cz-customizable": "^6.2.0",
"electron": "^6.0.0", "electron": "^16.0.6",
"electron-devtools-installer": "^3.2.0", "electron-devtools-installer": "^3.2.0",
"eslint": "^5.16.0", "eslint": "^7.32.0",
"eslint-plugin-vue": "^5.0.0", "eslint-config-standard": ">=16.0.0",
"eslint-plugin-import": "^2.24.2",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^5.1.0",
"eslint-plugin-vue": "^7.0.0",
"husky": "^3.1.0", "husky": "^3.1.0",
"stylus": "^0.54.7", "stylus": "^0.54.7",
"stylus-loader": "^3.0.2", "stylus-loader": "^3.0.2",
"typescript": "~3.7.3", "typescript": "^4.4.3",
"vue-cli-plugin-electron-builder": "^1.4.2", "vue-cli-plugin-electron-builder": "^2.1.1",
"vue-property-decorator": "^8.3.0", "vue-property-decorator": "^8.3.0",
"vue-template-compiler": "^2.6.10" "vue-template-compiler": "^2.6.10"
}, },
"resolutions": { "resolutions": {
"@types/node": "12.0.2" "@types/node": "^16.10.2"
} }
} }

View File

@ -12,7 +12,6 @@ import { initTalkingData } from './renderer/utils/analytics'
import db from './renderer/utils/db' import db from './renderer/utils/db'
webFrame.setVisualZoomLevelLimits(1, 1) webFrame.setVisualZoomLevelLimits(1, 1)
webFrame.setLayoutZoomLevelLimits(0, 0)
Vue.config.productionTip = false Vue.config.productionTip = false
Vue.prototype.$builtInPicBed = [ Vue.prototype.$builtInPicBed = [

View File

@ -6,7 +6,7 @@ The lowest level APIs that are not dependent on each other. The upper APIs depen
## app ## app
Provide key API interfaces for PicGo application, including uploader, window management, shortcut key system, etc Provide key API interfaces for PicGo application, including uploader, window management, shortcut key system, remotes handler, etc
## gui ## gui

View File

@ -16,10 +16,12 @@ class ShortKeyHandler {
this.isInModifiedMode = flag this.isInModifiedMode = flag
}) })
} }
init () { init () {
this.initBuiltInShortKey() this.initBuiltInShortKey()
this.initPluginsShortKey() this.initPluginsShortKey()
} }
private initBuiltInShortKey () { private initBuiltInShortKey () {
const commands = db.get('settings.shortKey') as IShortKeyConfigs const commands = db.get('settings.shortKey') as IShortKeyConfigs
Object.keys(commands) Object.keys(commands)
@ -34,10 +36,11 @@ class ShortKeyHandler {
} }
}) })
} }
private initPluginsShortKey () { private initPluginsShortKey () {
// get enabled plugin // get enabled plugin
const pluginList = picgo.pluginLoader.getList() const pluginList = picgo.pluginLoader.getList()
for (let item of pluginList) { for (const item of pluginList) {
const plugin = picgo.pluginLoader.getPlugin(item) const plugin = picgo.pluginLoader.getPlugin(item)
// if a plugin has commands // if a plugin has commands
if (plugin && plugin.commands) { if (plugin && plugin.commands) {
@ -46,7 +49,7 @@ class ShortKeyHandler {
continue continue
} }
const commands = plugin.commands(picgo) as IPluginShortKeyConfig[] const commands = plugin.commands(picgo) as IPluginShortKeyConfig[]
for (let cmd of commands) { for (const cmd of commands) {
const command = `${item}:${cmd.name}` const command = `${item}:${cmd.name}`
if (db.has(`settings.shortKey[${command}]`)) { if (db.has(`settings.shortKey[${command}]`)) {
const commandConfig = db.get(`settings.shortKey.${command}`) as IShortKeyConfig const commandConfig = db.get(`settings.shortKey.${command}`) as IShortKeyConfig
@ -63,6 +66,7 @@ class ShortKeyHandler {
} }
} }
} }
private registerShortKey (config: IShortKeyConfig | IPluginShortKeyConfig, command: string, handler: IShortKeyHandler, writeFlag: boolean) { private registerShortKey (config: IShortKeyConfig | IPluginShortKeyConfig, command: string, handler: IShortKeyHandler, writeFlag: boolean) {
shortKeyService.registerCommand(command, handler) shortKeyService.registerCommand(command, handler)
if (config.key) { if (config.key) {
@ -85,6 +89,7 @@ class ShortKeyHandler {
}) })
} }
} }
// enable or disable shortKey // enable or disable shortKey
bindOrUnbindShortKey (item: IShortKeyConfig, from: string): boolean { bindOrUnbindShortKey (item: IShortKeyConfig, from: string): boolean {
const command = `${from}:${item.name}` const command = `${from}:${item.name}`
@ -108,6 +113,7 @@ class ShortKeyHandler {
} }
} }
} }
// update shortKey bindings // update shortKey bindings
updateShortKey (item: IShortKeyConfig, oldKey: string, from: string): boolean { updateShortKey (item: IShortKeyConfig, oldKey: string, from: string): boolean {
const command = `${from}:${item.name}` const command = `${from}:${item.name}`
@ -121,6 +127,7 @@ class ShortKeyHandler {
}) })
return true return true
} }
private async handler (command: string) { private async handler (command: string) {
if (this.isInModifiedMode) { if (this.isInModifiedMode) {
return return
@ -136,6 +143,7 @@ class ShortKeyHandler {
logger.warn(`can not find command: ${command}`) logger.warn(`can not find command: ${command}`)
} }
} }
registerPluginShortKey (pluginName: string) { registerPluginShortKey (pluginName: string) {
const plugin = picgo.pluginLoader.getPlugin(pluginName) const plugin = picgo.pluginLoader.getPlugin(pluginName)
if (plugin && plugin.commands) { if (plugin && plugin.commands) {
@ -144,7 +152,7 @@ class ShortKeyHandler {
return return
} }
const commands = plugin.commands(picgo) as IPluginShortKeyConfig[] const commands = plugin.commands(picgo) as IPluginShortKeyConfig[]
for (let cmd of commands) { for (const cmd of commands) {
const command = `${pluginName}:${cmd.name}` const command = `${pluginName}:${cmd.name}`
if (db.has(`settings.shortKey[${command}]`)) { if (db.has(`settings.shortKey[${command}]`)) {
const commandConfig = db.get(`settings.shortKey[${command}]`) as IShortKeyConfig const commandConfig = db.get(`settings.shortKey[${command}]`) as IShortKeyConfig
@ -155,6 +163,7 @@ class ShortKeyHandler {
} }
} }
} }
unregisterPluginShortKey (pluginName: string) { unregisterPluginShortKey (pluginName: string) {
const commands = db.get('settings.shortKey') as IShortKeyConfigs const commands = db.get('settings.shortKey') as IShortKeyConfigs
const keyList = Object.keys(commands) const keyList = Object.keys(commands)

View File

@ -4,15 +4,18 @@ class ShortKeyService {
registerCommand (command: string, handler: IShortKeyHandler) { registerCommand (command: string, handler: IShortKeyHandler) {
this.commandList.set(command, handler) this.commandList.set(command, handler)
} }
unregisterCommand (command: string) { unregisterCommand (command: string) {
this.commandList.delete(command) this.commandList.delete(command)
} }
getShortKeyHandler (command: string): IShortKeyHandler | null { getShortKeyHandler (command: string): IShortKeyHandler | null {
const handler = this.commandList.get(command) const handler = this.commandList.get(command)
if (handler) return handler if (handler) return handler
logger.warn(`cannot find command: ${command}`) logger.warn(`cannot find command: ${command}`)
return null return null
} }
getCommandList () { getCommandList () {
return [...this.commandList.keys()] return [...this.commandList.keys()]
} }

View File

@ -13,7 +13,7 @@ import db, { GalleryDB } from '~/main/apis/core/datastore'
import windowManager from 'apis/app/window/windowManager' import windowManager from 'apis/app/window/windowManager'
import { IWindowList } from 'apis/app/window/constants' import { IWindowList } from 'apis/app/window/constants'
import picgo from '@core/picgo' import picgo from '@core/picgo'
import pasteTemplate from '#/utils/pasteTemplate' import pasteTemplate from '~/main/utils/pasteTemplate'
import pkg from 'root/package.json' import pkg from 'root/package.json'
import { handleCopyUrl } from '~/main/utils/common' import { handleCopyUrl } from '~/main/utils/common'
import { privacyManager } from '~/main/utils/privacyManager' import { privacyManager } from '~/main/utils/privacyManager'
@ -162,8 +162,8 @@ export function createTray () {
if (process.platform === 'darwin') { if (process.platform === 'darwin') {
toggleWindow(bounds) toggleWindow(bounds)
setTimeout(() => { setTimeout(() => {
let img = clipboard.readImage() const img = clipboard.readImage()
let obj: ImgInfo[] = [] const obj: ImgInfo[] = []
if (!img.isEmpty()) { if (!img.isEmpty()) {
// 从剪贴板来的图片默认转为png // 从剪贴板来的图片默认转为png
// @ts-ignore // @ts-ignore

View File

@ -5,13 +5,13 @@ import {
import windowManager from 'apis/app/window/windowManager' import windowManager from 'apis/app/window/windowManager'
import { IWindowList } from 'apis/app/window/constants' import { IWindowList } from 'apis/app/window/constants'
import uploader from '.' import uploader from '.'
import pasteTemplate from '#/utils/pasteTemplate' import pasteTemplate from '~/main/utils/pasteTemplate'
import db, { GalleryDB } from '~/main/apis/core/datastore' import db, { GalleryDB } from '~/main/apis/core/datastore'
import { handleCopyUrl } from '~/main/utils/common' import { handleCopyUrl } from '~/main/utils/common'
import { handleUrlEncode } from '#/utils/common' import { handleUrlEncode } from '#/utils/common'
export const uploadClipboardFiles = async (): Promise<string> => { export const uploadClipboardFiles = async (): Promise<string> => {
const win = windowManager.getAvailableWindow() const win = windowManager.getAvailableWindow()
let img = await uploader.setWebContents(win!.webContents).upload() const img = await uploader.setWebContents(win!.webContents).upload()
if (img !== false) { if (img !== false) {
if (img.length > 0) { if (img.length > 0) {
const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW) const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)

View File

@ -16,7 +16,7 @@ import { TALKING_DATA_EVENT } from '~/universal/events/constants'
import logger from '@core/picgo/logger' import logger from '@core/picgo/logger'
const waitForShow = (webcontent: WebContents) => { const waitForShow = (webcontent: WebContents) => {
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve) => {
webcontent.on('did-finish-load', () => { webcontent.on('did-finish-load', () => {
resolve() resolve()
}) })
@ -24,7 +24,7 @@ const waitForShow = (webcontent: WebContents) => {
} }
const waitForRename = (window: BrowserWindow, id: number): Promise<string|null> => { const waitForRename = (window: BrowserWindow, id: number): Promise<string|null> => {
return new Promise((resolve, reject) => { return new Promise((resolve) => {
const windowId = window.id const windowId = window.id
ipcMain.once(`rename${id}`, (evt: Event, newName: string) => { ipcMain.once(`rename${id}`, (evt: Event, newName: string) => {
resolve(newName) resolve(newName)
@ -68,7 +68,7 @@ class Uploader {
picgo.on('uploadProgress', progress => { picgo.on('uploadProgress', progress => {
this.webContents?.send('uploadProgress', progress) this.webContents?.send('uploadProgress', progress)
}) })
picgo.on('beforeTransform', ctx => { picgo.on('beforeTransform', () => {
if (db.get('settings.uploadNotification')) { if (db.get('settings.uploadNotification')) {
const notification = new Notification({ const notification = new Notification({
title: '上传进度', title: '上传进度',
@ -125,7 +125,7 @@ class Uploader {
} else { } else {
return false return false
} }
} catch (e) { } catch (e: any) {
logger.error(e) logger.error(e)
setTimeout(() => { setTimeout(() => {
showNotification({ showNotification({

View File

@ -9,16 +9,16 @@ const isDevelopment = process.env.NODE_ENV !== 'production'
export const TRAY_WINDOW_URL = isDevelopment export const TRAY_WINDOW_URL = isDevelopment
? (process.env.WEBPACK_DEV_SERVER_URL as string) ? (process.env.WEBPACK_DEV_SERVER_URL as string)
: `picgo://./index.html` : 'picgo://./index.html'
export const SETTING_WINDOW_URL = isDevelopment export const SETTING_WINDOW_URL = isDevelopment
? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#main-page/upload` ? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#main-page/upload`
: `picgo://./index.html#main-page/upload` : 'picgo://./index.html#main-page/upload'
export const MINI_WINDOW_URL = isDevelopment export const MINI_WINDOW_URL = isDevelopment
? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#mini-page` ? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#mini-page`
: `picgo://./index.html#mini-page` : 'picgo://./index.html#mini-page'
export const RENAME_WINDOW_URL = process.env.NODE_ENV === 'development' export const RENAME_WINDOW_URL = process.env.NODE_ENV === 'development'
? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#rename-page` ? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#rename-page`
: `picgo://./index.html#rename-page` : 'picgo://./index.html#rename-page'

View File

@ -28,7 +28,8 @@ windowList.set(IWindowList.TRAY_WINDOW, {
transparent: true, transparent: true,
vibrancy: 'ultra-dark', vibrancy: 'ultra-dark',
webPreferences: { webPreferences: {
nodeIntegration: true, nodeIntegration: !!process.env.ELECTRON_NODE_INTEGRATION,
contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION,
nodeIntegrationInWorker: true, nodeIntegrationInWorker: true,
backgroundThrottling: false backgroundThrottling: false
} }
@ -60,7 +61,8 @@ windowList.set(IWindowList.SETTING_WINDOW, {
titleBarStyle: 'hidden', titleBarStyle: 'hidden',
webPreferences: { webPreferences: {
backgroundThrottling: false, backgroundThrottling: false,
nodeIntegration: true, nodeIntegration: !!process.env.ELECTRON_NODE_INTEGRATION,
contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION,
nodeIntegrationInWorker: true, nodeIntegrationInWorker: true,
webSecurity: false webSecurity: false
} }
@ -93,7 +95,7 @@ windowList.set(IWindowList.MINI_WINDOW, {
isValid: process.platform !== 'darwin', isValid: process.platform !== 'darwin',
multiple: false, multiple: false,
options () { options () {
let obj: IBrowserWindowOptions = { const obj: IBrowserWindowOptions = {
height: 64, height: 64,
width: 64, width: 64,
show: process.platform === 'linux', show: process.platform === 'linux',
@ -105,7 +107,8 @@ windowList.set(IWindowList.MINI_WINDOW, {
icon: `${__static}/logo.png`, icon: `${__static}/logo.png`,
webPreferences: { webPreferences: {
backgroundThrottling: false, backgroundThrottling: false,
nodeIntegration: true, nodeIntegration: !!process.env.ELECTRON_NODE_INTEGRATION,
contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION,
nodeIntegrationInWorker: true nodeIntegrationInWorker: true
} }
} }
@ -124,7 +127,7 @@ windowList.set(IWindowList.RENAME_WINDOW, {
isValid: true, isValid: true,
multiple: true, multiple: true,
options () { options () {
let options: IBrowserWindowOptions = { const options: IBrowserWindowOptions = {
height: 175, height: 175,
width: 300, width: 300,
show: true, show: true,
@ -132,7 +135,8 @@ windowList.set(IWindowList.RENAME_WINDOW, {
resizable: false, resizable: false,
vibrancy: 'ultra-dark', vibrancy: 'ultra-dark',
webPreferences: { webPreferences: {
nodeIntegration: true, nodeIntegration: !!process.env.ELECTRON_NODE_INTEGRATION,
contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION,
nodeIntegrationInWorker: true, nodeIntegrationInWorker: true,
backgroundThrottling: false backgroundThrottling: false
} }

View File

@ -34,6 +34,7 @@ class WindowManager implements IWindowManager {
return null return null
} }
} }
get (name: IWindowList) { get (name: IWindowList) {
if (this.has(name)) { if (this.has(name)) {
return this.windowMap.get(name)! return this.windowMap.get(name)!
@ -42,9 +43,11 @@ class WindowManager implements IWindowManager {
return window return window
} }
} }
has (name: IWindowList) { has (name: IWindowList) {
return this.windowMap.has(name) return this.windowMap.has(name)
} }
// useless // useless
// delete (name: IWindowList) { // delete (name: IWindowList) {
// const window = this.windowMap.get(name) // const window = this.windowMap.get(name)
@ -60,6 +63,7 @@ class WindowManager implements IWindowManager {
this.windowIdMap.delete(id) this.windowIdMap.delete(id)
} }
} }
getAvailableWindow () { getAvailableWindow () {
const miniWindow = this.windowMap.get(IWindowList.MINI_WINDOW) const miniWindow = this.windowMap.get(IWindowList.MINI_WINDOW)
if (miniWindow && miniWindow.isVisible()) { if (miniWindow && miniWindow.isVisible()) {

View File

@ -14,7 +14,7 @@ export const uploadWithClipboardFiles = (): Promise<{
success: boolean, success: boolean,
result?: string[] result?: string[]
}> => { }> => {
return new Promise((resolve, reject) => { return new Promise((resolve) => {
bus.once(UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE, (result: string) => { bus.once(UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE, (result: string) => {
if (result) { if (result) {
return resolve({ return resolve({
@ -35,7 +35,7 @@ export const uploadWithFiles = (pathList: IFileWithPath[]): Promise<{
success: boolean, success: boolean,
result?: string[] result?: string[]
}> => { }> => {
return new Promise((resolve, reject) => { return new Promise((resolve) => {
bus.once(UPLOAD_WITH_FILES_RESPONSE, (result: string[]) => { bus.once(UPLOAD_WITH_FILES_RESPONSE, (result: string[]) => {
if (result.length) { if (result.length) {
return resolve({ return resolve({
@ -55,7 +55,7 @@ export const uploadWithFiles = (pathList: IFileWithPath[]): Promise<{
// get available window id: // get available window id:
// miniWindow or settingWindow or trayWindow // miniWindow or settingWindow or trayWindow
export const getWindowId = (): Promise<number> => { export const getWindowId = (): Promise<number> => {
return new Promise((resolve, reject) => { return new Promise((resolve) => {
bus.once(GET_WINDOW_ID_REPONSE, (id: number) => { bus.once(GET_WINDOW_ID_REPONSE, (id: number) => {
resolve(id) resolve(id)
}) })
@ -65,7 +65,7 @@ export const getWindowId = (): Promise<number> => {
// get settingWindow id: // get settingWindow id:
export const getSettingWindowId = (): Promise<number> => { export const getSettingWindowId = (): Promise<number> => {
return new Promise((resolve, reject) => { return new Promise((resolve) => {
bus.once(GET_SETTING_WINDOW_ID_RESPONSE, (id: number) => { bus.once(GET_SETTING_WINDOW_ID_RESPONSE, (id: number) => {
resolve(id) resolve(id)
}) })

View File

@ -1,9 +1,8 @@
import fs from 'fs-extra' import fs from 'fs-extra'
import path from 'path' import path from 'path'
import { remote, app } from 'electron' import { app as APP } from 'electron'
import dayjs from 'dayjs'
import { getLogger } from '@core/utils/localLogger' import { getLogger } from '@core/utils/localLogger'
const APP = process.type === 'renderer' ? remote.app : app import dayjs from 'dayjs'
const STORE_PATH = APP.getPath('userData') const STORE_PATH = APP.getPath('userData')
const configFilePath = path.join(STORE_PATH, 'data.json') const configFilePath = path.join(STORE_PATH, 'data.json')
const configFileBackupPath = path.join(STORE_PATH, 'data.bak.json') const configFileBackupPath = path.join(STORE_PATH, 'data.bak.json')
@ -36,7 +35,7 @@ function dbChecker () {
return return
} }
let configFile: string = '{}' let configFile: string = '{}'
let optionsTpl = { const optionsTpl = {
title: '注意', title: '注意',
body: '' body: ''
} }
@ -98,7 +97,7 @@ function dbPathChecker (): string {
const picgoLogPath = path.join(defaultConfigPath, 'picgo.log') const picgoLogPath = path.join(defaultConfigPath, 'picgo.log')
const logger = getLogger(picgoLogPath) const logger = getLogger(picgoLogPath)
if (!hasCheckPath) { if (!hasCheckPath) {
let optionsTpl = { const optionsTpl = {
title: '注意', title: '注意',
body: '自定义文件解析出错,请检查路径内容是否正确' body: '自定义文件解析出错,请检查路径内容是否正确'
} }

View File

@ -42,33 +42,42 @@ class ConfigStore {
}).write() }).write()
} }
} }
read () { read () {
return this.db.read() return this.db.read()
} }
get (key = '') { get (key = '') {
return this.read().get(key).value() return this.read().get(key).value()
} }
set (key: string, value: any) { set (key: string, value: any) {
return this.read().set(key, value).write() return this.read().set(key, value).write()
} }
has (key: string) { has (key: string) {
return this.read().has(key).value() return this.read().has(key).value()
} }
insert (key: string, value: any): void { insert (key: string, value: any): void {
// @ts-ignore // @ts-ignore
return this.read().get(key).insert(value).write() return this.read().get(key).insert(value).write()
} }
unset (key: string, value: any): boolean { unset (key: string, value: any): boolean {
return this.read().get(key).unset(value).value() return this.read().get(key).unset(value).value()
} }
getById (key: string, id: string) { getById (key: string, id: string) {
// @ts-ignore // @ts-ignore
return this.read().get(key).getById(id).value() return this.read().get(key).getById(id).value()
} }
removeById (key: string, id: string) { removeById (key: string, id: string) {
// @ts-ignore // @ts-ignore
return this.read().get(key).removeById(id).write() return this.read().get(key).removeById(id).write()
} }
getConfigPath () { getConfigPath () {
return CONFIG_PATH return CONFIG_PATH
} }
@ -82,6 +91,7 @@ class GalleryDB {
private constructor () { private constructor () {
console.log('init gallery db') console.log('init gallery db')
} }
public static getInstance (): DBStore { public static getInstance (): DBStore {
if (!GalleryDB.instance) { if (!GalleryDB.instance) {
GalleryDB.instance = new DBStore(DB_PATH, 'gallery') GalleryDB.instance = new DBStore(DB_PATH, 'gallery')

View File

@ -8,7 +8,7 @@ import path from 'path'
import db, { GalleryDB } from 'apis/core/datastore' import db, { GalleryDB } from 'apis/core/datastore'
import { dbPathChecker, defaultConfigPath, getGalleryDBPath } from 'apis/core/datastore/dbChecker' import { dbPathChecker, defaultConfigPath, getGalleryDBPath } from 'apis/core/datastore/dbChecker'
import uploader from 'apis/app/uploader' import uploader from 'apis/app/uploader'
import pasteTemplate from '#/utils/pasteTemplate' import pasteTemplate from '~/main/utils/pasteTemplate'
import { handleCopyUrl } from '~/main/utils/common' import { handleCopyUrl } from '~/main/utils/common'
import { import {
getWindowId, getWindowId,
@ -28,20 +28,22 @@ class GuiApi implements IGuiApi {
private constructor () { private constructor () {
console.log('init guiapi') console.log('init guiapi')
} }
public static getInstance (): GuiApi { public static getInstance (): GuiApi {
if (!GuiApi.instance) { if (!GuiApi.instance) {
GuiApi.instance = new GuiApi() GuiApi.instance = new GuiApi()
} }
return GuiApi.instance return GuiApi.instance
} }
private async showSettingWindow () { private async showSettingWindow () {
this.settingWindowId = await getSettingWindowId() this.settingWindowId = await getSettingWindowId()
const settingWindow = BrowserWindow.fromId(this.settingWindowId) const settingWindow = BrowserWindow.fromId(this.settingWindowId)
if (settingWindow.isVisible()) { if (settingWindow?.isVisible()) {
return true return true
} }
settingWindow.show() settingWindow?.show()
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve) => {
setTimeout(() => { setTimeout(() => {
resolve() resolve()
}, 1000) // TODO: a better way to wait page loaded. }, 1000) // TODO: a better way to wait page loaded.
@ -49,7 +51,7 @@ class GuiApi implements IGuiApi {
} }
private getWebcontentsByWindowId (id: number) { private getWebcontentsByWindowId (id: number) {
return BrowserWindow.fromId(id).webContents return BrowserWindow.fromId(id)?.webContents
} }
async showInputBox (options: IShowInputBoxOption = { async showInputBox (options: IShowInputBoxOption = {
@ -57,28 +59,24 @@ class GuiApi implements IGuiApi {
placeholder: '' placeholder: ''
}) { }) {
await this.showSettingWindow() await this.showSettingWindow()
this.getWebcontentsByWindowId(this.settingWindowId) this.getWebcontentsByWindowId(this.settingWindowId)?.send(SHOW_INPUT_BOX, options)
.send(SHOW_INPUT_BOX, options) return new Promise<string>((resolve) => {
return new Promise<string>((resolve, reject) => {
ipcMain.once(SHOW_INPUT_BOX, (event: Event, value: string) => { ipcMain.once(SHOW_INPUT_BOX, (event: Event, value: string) => {
resolve(value) resolve(value)
}) })
}) })
} }
showFileExplorer (options: IShowFileExplorerOption = {}) { async showFileExplorer (options: IShowFileExplorerOption = {}) {
return new Promise<string>(async (resolve, reject) => { this.windowId = await getWindowId()
this.windowId = await getWindowId() const res = await dialog.showOpenDialog(BrowserWindow.fromId(this.windowId)!, options)
dialog.showOpenDialog(BrowserWindow.fromId(this.windowId), options, (filename: string) => { return res.filePaths?.[0]
resolve(filename)
})
})
} }
async upload (input: IUploadOption) { async upload (input: IUploadOption) {
this.windowId = await getWindowId() this.windowId = await getWindowId()
const webContents = this.getWebcontentsByWindowId(this.windowId) const webContents = this.getWebcontentsByWindowId(this.windowId)
const imgs = await uploader.setWebContents(webContents).upload(input) const imgs = await uploader.setWebContents(webContents!).upload(input)
if (imgs !== false) { if (imgs !== false) {
const pasteStyle = db.get('settings.pasteStyle') || 'markdown' const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
const pasteText: string[] = [] const pasteText: string[] = []
@ -95,8 +93,8 @@ class GuiApi implements IGuiApi {
await GalleryDB.getInstance().insert(imgs[i]) await GalleryDB.getInstance().insert(imgs[i])
} }
handleCopyUrl(pasteText.join('\n')) handleCopyUrl(pasteText.join('\n'))
webContents.send('uploadFiles', imgs) webContents?.send('uploadFiles', imgs)
webContents.send('updateGallery') webContents?.send('updateGallery')
return imgs return imgs
} }
return [] return []
@ -119,10 +117,10 @@ class GuiApi implements IGuiApi {
type: 'info', type: 'info',
buttons: ['Yes', 'No'] buttons: ['Yes', 'No']
}) { }) {
return new Promise<IShowMessageBoxResult>(async (resolve, reject) => { return new Promise<IShowMessageBoxResult>(async (resolve) => {
this.windowId = await getWindowId() this.windowId = await getWindowId()
dialog.showMessageBox( dialog.showMessageBox(
BrowserWindow.fromId(this.windowId), BrowserWindow.fromId(this.windowId)!,
options options
).then((res) => { ).then((res) => {
resolve({ resolve({

View File

@ -28,7 +28,7 @@ function initEventCenter () {
[GET_SETTING_WINDOW_ID]: busCallGetSettingWindowId, [GET_SETTING_WINDOW_ID]: busCallGetSettingWindowId,
[CREATE_APP_MENU]: createMenu [CREATE_APP_MENU]: createMenu
} }
for (let i in eventList) { for (const i in eventList) {
bus.on(i, eventList[i]) bus.on(i, eventList[i])
} }
} }

View File

@ -1,13 +1,15 @@
import { import {
app, app,
ipcMain, ipcMain,
shell,
Notification, Notification,
IpcMainEvent IpcMainEvent,
BrowserWindow
} from 'electron' } from 'electron'
import windowManager from 'apis/app/window/windowManager' import windowManager from 'apis/app/window/windowManager'
import { IWindowList } from 'apis/app/window/constants' import { IWindowList } from 'apis/app/window/constants'
import uploader from 'apis/app/uploader' import uploader from 'apis/app/uploader'
import pasteTemplate from '#/utils/pasteTemplate' import pasteTemplate from '~/main/utils/pasteTemplate'
import db, { GalleryDB } from '~/main/apis/core/datastore' import db, { GalleryDB } from '~/main/apis/core/datastore'
import server from '~/main/server' import server from '~/main/server'
import getPicBeds from '~/main/utils/getPicBeds' import getPicBeds from '~/main/utils/getPicBeds'
@ -15,7 +17,16 @@ import shortKeyHandler from 'apis/app/shortKey/shortKeyHandler'
import bus from '@core/bus' import bus from '@core/bus'
import { import {
TOGGLE_SHORTKEY_MODIFIED_MODE, TOGGLE_SHORTKEY_MODIFIED_MODE,
OPEN_DEVTOOLS OPEN_DEVTOOLS,
SHOW_MINI_PAGE_MENU,
MINIMIZE_WINDOW,
CLOSE_WINDOW,
SHOW_MAIN_PAGE_MENU,
SHOW_UPLOAD_PAGE_MENU,
OPEN_USER_STORE_FILE,
OPEN_URL,
RELOAD_APP,
SHOW_PLUGIN_PAGE_MENU
} from '#/events/constants' } from '#/events/constants'
import { import {
uploadClipboardFiles, uploadClipboardFiles,
@ -23,6 +34,10 @@ import {
} from '~/main/apis/app/uploader/apis' } from '~/main/apis/app/uploader/apis'
import picgoCoreIPC from './picgoCoreIPC' import picgoCoreIPC from './picgoCoreIPC'
import { handleCopyUrl } from '~/main/utils/common' import { handleCopyUrl } from '~/main/utils/common'
import { buildMainPageMenu, buildMiniPageMenu, buildPluginPageMenu, buildUploadPageMenu } from './remotes/menu'
import path from 'path'
const STORE_PATH = app.getPath('userData')
export default { export default {
listen () { listen () {
@ -145,6 +160,58 @@ export default {
ipcMain.on(OPEN_DEVTOOLS, (event: IpcMainEvent) => { ipcMain.on(OPEN_DEVTOOLS, (event: IpcMainEvent) => {
event.sender.openDevTools() event.sender.openDevTools()
}) })
// menu & window methods
ipcMain.on(SHOW_MINI_PAGE_MENU, () => {
const window = windowManager.get(IWindowList.MINI_WINDOW)!
const menu = buildMiniPageMenu()
menu.popup({
window
})
})
ipcMain.on(SHOW_MAIN_PAGE_MENU, () => {
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
const menu = buildMainPageMenu()
menu.popup({
window
})
})
ipcMain.on(SHOW_UPLOAD_PAGE_MENU, () => {
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
const menu = buildUploadPageMenu()
menu.popup({
window
})
})
ipcMain.on(SHOW_PLUGIN_PAGE_MENU, (evt: IpcMainEvent, plugin: IPicGoPlugin) => {
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
const menu = buildPluginPageMenu(plugin)
menu.popup({
window
})
})
ipcMain.on(MINIMIZE_WINDOW, () => {
const window = BrowserWindow.getFocusedWindow()
window?.minimize()
})
ipcMain.on(CLOSE_WINDOW, () => {
const window = BrowserWindow.getFocusedWindow()
if (process.platform === 'linux') {
window?.hide()
} else {
window?.close()
}
})
ipcMain.on(OPEN_USER_STORE_FILE, (evt: IpcMainEvent, filePath: string) => {
const abFilePath = path.join(STORE_PATH, filePath)
shell.openPath(abFilePath)
})
ipcMain.on(OPEN_URL, (evt: IpcMainEvent, url: string) => {
shell.openExternal(url)
})
ipcMain.on(RELOAD_APP, () => {
app.relaunch()
app.exit(0)
})
}, },
dispose () {} dispose () {}
} }

View File

@ -4,10 +4,11 @@ import {
dialog, dialog,
shell, shell,
IpcMainEvent, IpcMainEvent,
ipcMain ipcMain,
clipboard
} from 'electron' } from 'electron'
import PicGoCore from '~/universal/types/picgo' import PicGoCore from '~/universal/types/picgo'
import { IPicGoHelperType } from '#/types/enum' import { IPasteStyle, IPicGoHelperType } from '#/types/enum'
import shortKeyHandler from 'apis/app/shortKey/shortKeyHandler' import shortKeyHandler from 'apis/app/shortKey/shortKeyHandler'
import picgo from '@core/picgo' import picgo from '@core/picgo'
import { handleStreamlinePluginName } from '~/universal/utils/common' import { handleStreamlinePluginName } from '~/universal/utils/common'
@ -25,11 +26,13 @@ import {
PICGO_UPDATE_BY_ID_DB, PICGO_UPDATE_BY_ID_DB,
PICGO_GET_BY_ID_DB, PICGO_GET_BY_ID_DB,
PICGO_REMOVE_BY_ID_DB, PICGO_REMOVE_BY_ID_DB,
PICGO_OPEN_FILE PICGO_OPEN_FILE,
PASTE_TEXT
} from '#/events/constants' } from '#/events/constants'
import { GalleryDB } from 'apis/core/datastore' import { GalleryDB } from 'apis/core/datastore'
import { IObject, IFilter } from '@picgo/store/dist/types' import { IObject, IFilter } from '@picgo/store/dist/types'
import pasteTemplate from '../utils/pasteTemplate'
// 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
@ -37,11 +40,6 @@ const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_re
const STORE_PATH = path.dirname(dbPathChecker()) const STORE_PATH = path.dirname(dbPathChecker())
// const CONFIG_PATH = path.join(STORE_PATH, '/data.json') // const CONFIG_PATH = path.join(STORE_PATH, '/data.json')
type PicGoNotice = {
title: string,
body: string[]
}
interface GuiMenuItem { interface GuiMenuItem {
label: string label: string
handle: (arg0: PicGoCore, arg1: GuiApi) => Promise<void> handle: (arg0: PicGoCore, arg1: GuiApi) => Promise<void>
@ -64,7 +62,7 @@ const getConfig = (name: string, type: IPicGoHelperType, ctx: PicGoCore) => {
} }
const handleConfigWithFunction = (config: any[]) => { const handleConfigWithFunction = (config: any[]) => {
for (let i in config) { for (const i in config) {
if (typeof config[i].default === 'function') { if (typeof config[i].default === 'function') {
config[i].default = config[i].default() config[i].default = config[i].default()
} }
@ -78,7 +76,7 @@ const handleConfigWithFunction = (config: any[]) => {
const getPluginList = (): IPicGoPlugin[] => { const getPluginList = (): IPicGoPlugin[] => {
const pluginList = picgo.pluginLoader.getFullList() const pluginList = picgo.pluginLoader.getFullList()
const list = [] const list = []
for (let i in pluginList) { for (const i in pluginList) {
const plugin = picgo.pluginLoader.getPlugin(pluginList[i])! const plugin = picgo.pluginLoader.getPlugin(pluginList[i])!
const pluginPath = path.join(STORE_PATH, `/node_modules/${pluginList[i]}`) const pluginPath = path.join(STORE_PATH, `/node_modules/${pluginList[i]}`)
const pluginPKG = requireFunc(path.join(pluginPath, 'package.json')) const pluginPKG = requireFunc(path.join(pluginPath, 'package.json'))
@ -156,39 +154,37 @@ const handlePluginInstall = () => {
}) })
} }
const handlePluginUninstall = () => { const handlePluginUninstall = async (fullName: string) => {
ipcMain.on('uninstallPlugin', async (event: IpcMainEvent, msg: string) => { const window = windowManager.get(IWindowList.SETTING_WINDOW)!
const dispose = handleNPMError() const dispose = handleNPMError()
const res = await picgo.pluginHandler.uninstall([msg]) const res = await picgo.pluginHandler.uninstall([fullName])
if (res.success) { if (res.success) {
event.sender.send('uninstallSuccess', res.body[0]) window.webContents.send('uninstallSuccess', res.body[0])
shortKeyHandler.unregisterPluginShortKey(res.body[0]) shortKeyHandler.unregisterPluginShortKey(res.body[0])
} else { } else {
showNotification({ showNotification({
title: '插件卸载失败', title: '插件卸载失败',
body: res.body as string body: res.body as string
}) })
} }
event.sender.send('hideLoading') window.webContents.send('hideLoading')
dispose() dispose()
})
} }
const handlePluginUpdate = () => { const handlePluginUpdate = async (fullName: string) => {
ipcMain.on('updatePlugin', async (event: IpcMainEvent, msg: string) => { const window = windowManager.get(IWindowList.SETTING_WINDOW)!
const dispose = handleNPMError() const dispose = handleNPMError()
const res = await picgo.pluginHandler.update([msg]) const res = await picgo.pluginHandler.update([fullName])
if (res.success) { if (res.success) {
event.sender.send('updateSuccess', res.body[0]) window.webContents.send('updateSuccess', res.body[0])
} else { } else {
showNotification({ showNotification({
title: '插件更新失败', title: '插件更新失败',
body: res.body as string body: res.body as string
}) })
} }
event.sender.send('hideLoading') window.webContents.send('hideLoading')
dispose() dispose()
})
} }
const handleNPMError = (): IDispose => { const handleNPMError = (): IDispose => {
@ -221,6 +217,7 @@ const handleGetPicBedConfig = () => {
}) })
} }
// TODO: remove it
const handlePluginActions = () => { const handlePluginActions = () => {
ipcMain.on('pluginActions', (event: IpcMainEvent, name: string, label: string) => { ipcMain.on('pluginActions', (event: IpcMainEvent, name: string, label: string) => {
const plugin = picgo.pluginLoader.getPlugin(name) const plugin = picgo.pluginLoader.getPlugin(name)
@ -257,29 +254,29 @@ const handlePicGoGetConfig = () => {
} }
const handleImportLocalPlugin = () => { const handleImportLocalPlugin = () => {
ipcMain.on('importLocalPlugin', (event: IpcMainEvent) => { ipcMain.on('importLocalPlugin', async (event: IpcMainEvent) => {
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)! const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)!
dialog.showOpenDialog(settingWindow, { const res = await dialog.showOpenDialog(settingWindow, {
properties: ['openDirectory'] properties: ['openDirectory']
}, async (filePath: string[]) => {
if (filePath.length > 0) {
const res = await picgo.pluginHandler.install(filePath)
if (res.success) {
const list = getPluginList()
event.sender.send('pluginList', list)
showNotification({
title: '导入插件成功',
body: ''
})
} else {
showNotification({
title: '导入插件失败',
body: res.body as string
})
}
}
event.sender.send('hideLoading')
}) })
const filePaths = res.filePaths
if (filePaths.length > 0) {
const res = await picgo.pluginHandler.install(filePaths)
if (res.success) {
const list = getPluginList()
event.sender.send('pluginList', list)
showNotification({
title: '导入插件成功',
body: ''
})
} else {
showNotification({
title: '导入插件失败',
body: res.body as string
})
}
}
event.sender.send('hideLoading')
}) })
} }
@ -319,12 +316,22 @@ const handlePicGoGalleryDB = () => {
const res = await dbStore.removeById(id) const res = await dbStore.removeById(id)
event.sender.send(PICGO_REMOVE_BY_ID_DB, res, callbackId) event.sender.send(PICGO_REMOVE_BY_ID_DB, res, callbackId)
}) })
ipcMain.handle(PASTE_TEXT, async (item: ImgInfo, copy = true) => {
const pasteStyle = picgo.getConfig<IPasteStyle>('settings.pasteStyle') || IPasteStyle.MARKDOWN
const customLink = picgo.getConfig<string>('settings.customLink')
const txt = pasteTemplate(pasteStyle, item, customLink)
if (copy) {
clipboard.writeText(txt)
}
return txt
})
} }
const handleOpenFile = () => { const handleOpenFile = () => {
ipcMain.on(PICGO_OPEN_FILE, (event: IpcMainEvent, fileName: string) => { ipcMain.on(PICGO_OPEN_FILE, (event: IpcMainEvent, fileName: string) => {
const abFilePath = path.join(STORE_PATH, fileName) const abFilePath = path.join(STORE_PATH, fileName)
shell.openItem(abFilePath) shell.openPath(abFilePath)
}) })
} }
@ -332,8 +339,6 @@ export default {
listen () { listen () {
handleGetPluginList() handleGetPluginList()
handlePluginInstall() handlePluginInstall()
handlePluginUninstall()
handlePluginUpdate()
handleGetPicBedConfig() handleGetPicBedConfig()
handlePluginActions() handlePluginActions()
handleRemoveFiles() handleRemoveFiles()
@ -342,5 +347,8 @@ export default {
handlePicGoGalleryDB() handlePicGoGalleryDB()
handleImportLocalPlugin() handleImportLocalPlugin()
handleOpenFile() handleOpenFile()
} },
// TODO: separate to single file
handlePluginUninstall,
handlePluginUpdate
} }

View File

@ -0,0 +1,286 @@
import windowManager from 'apis/app/window/windowManager'
import { IWindowList } from 'apis/app/window/constants'
import { Menu, BrowserWindow, app, dialog } from 'electron'
import getPicBeds from '~/main/utils/getPicBeds'
import picgo from '@core/picgo'
import {
uploadClipboardFiles
} from '~/main/apis/app/uploader/apis'
import { privacyManager } from '~/main/utils/privacyManager'
import pkg from 'root/package.json'
import GuiApi from 'apis/gui'
import PicGoCore from '~/universal/types/picgo'
import { PICGO_CONFIG_PLUGIN, PICGO_HANDLE_PLUGIN_ING, PICGO_TOGGLE_PLUGIN } from '~/universal/events/constants'
import picgoCoreIPC from '~/main/events/picgoCoreIPC'
interface GuiMenuItem {
label: string
handle: (arg0: PicGoCore, arg1: GuiApi) => Promise<void>
}
const buildMiniPageMenu = () => {
const picBeds = getPicBeds()
const current = picgo.getConfig('picBed.uploader')
const submenu = picBeds.filter(item => item.visible).map(item => {
return {
label: item.name,
type: 'radio',
checked: current === item.type,
click () {
picgo.saveConfig({
'picBed.current': item.type,
'picBed.uploader': item.type
})
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('syncPicBed')
}
}
}
})
const template = [
{
label: '打开详细窗口',
click () {
windowManager.get(IWindowList.SETTING_WINDOW)!.show()
if (windowManager.has(IWindowList.MINI_WINDOW)) {
windowManager.get(IWindowList.MINI_WINDOW)!.hide()
}
}
},
{
label: '选择默认图床',
type: 'submenu',
submenu
},
{
label: '剪贴板图片上传',
click () {
uploadClipboardFiles()
}
},
{
label: '隐藏窗口',
click () {
BrowserWindow.getFocusedWindow()!.hide()
}
},
{
label: '隐私协议',
click () {
privacyManager.show(false)
}
},
{
label: '重启应用',
click () {
app.relaunch()
app.exit(0)
}
},
{
role: 'quit',
label: '退出'
}
]
// @ts-ignore
return Menu.buildFromTemplate(template)
}
const buildMainPageMenu = () => {
const template = [
{
label: '关于',
click () {
dialog.showMessageBox({
title: 'PicGo',
message: 'PicGo',
detail: `Version: ${pkg.version}\nAuthor: Molunerfinn\nGithub: https://github.com/Molunerfinn/PicGo`
})
}
},
{
label: '赞助PicGo',
click () {
// TODO: show donation
}
},
{
label: '生成图床配置二维码',
click () {
// TODO: qrcode
// _this.qrcodeVisible = true
}
},
{
label: '隐私协议',
click () {
privacyManager.show(false)
}
}
]
// @ts-ignore
return Menu.buildFromTemplate(template)
}
const buildUploadPageMenu = () => {
const picBeds = getPicBeds()
const currentPicBed = picgo.getConfig('picBed.uploader')
const submenu = picBeds.filter(item => item.visible).map(item => {
return {
label: item.name,
type: 'radio',
checked: currentPicBed === item.type,
click () {
picgo.saveConfig({
'picBed.current': item.type,
'picBed.uploader': item.type
})
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('syncPicBed')
}
}
}
})
// @ts-ignore
return Menu.buildFromTemplate(submenu)
}
// TODO: separate to single file
const handleRestoreState = (item: string, name: string): void => {
if (item === 'uploader') {
const current = picgo.getConfig('picBed.current')
if (current === name) {
picgo.saveConfig({
'picBed.current': 'smms',
'picBed.uploader': 'smms'
})
}
}
if (item === 'transformer') {
const current = picgo.getConfig('picBed.transformer')
if (current === name) {
picgo.saveConfig({
'picBed.transformer': 'path'
})
}
}
}
const buildPluginPageMenu = (plugin: IPicGoPlugin) => {
const menu = [{
label: '启用插件',
enabled: !plugin.enabled,
click () {
picgo.saveConfig({
[`picgoPlugins.${plugin.fullName}`]: true
})
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
window.webContents.send(PICGO_TOGGLE_PLUGIN, plugin.fullName, true)
}
}, {
label: '禁用插件',
enabled: plugin.enabled,
click () {
picgo.saveConfig({
[`picgoPlugins.${plugin.fullName}`]: false
})
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
window.webContents.send(PICGO_HANDLE_PLUGIN_ING, plugin.fullName)
window.webContents.send(PICGO_TOGGLE_PLUGIN, plugin.fullName, false)
if (plugin.config.transformer.name) {
handleRestoreState('transformer', plugin.config.transformer.name)
}
if (plugin.config.uploader.name) {
handleRestoreState('uploader', plugin.config.uploader.name)
}
}
}, {
label: '卸载插件',
click () {
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
window.webContents.send(PICGO_HANDLE_PLUGIN_ING, plugin.fullName)
picgoCoreIPC.handlePluginUninstall(plugin.fullName)
}
}, {
label: '更新插件',
click () {
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
window.webContents.send(PICGO_HANDLE_PLUGIN_ING, plugin.fullName)
picgoCoreIPC.handlePluginUpdate(plugin.fullName)
}
}]
for (const i in plugin.config) {
if (plugin.config[i].config.length > 0) {
const obj = {
label: `配置${i} - ${plugin.config[i].fullName || plugin.config[i].name}`,
click () {
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
const currentType = i
const configName = plugin.config[i].fullName || plugin.config[i].name
const config = plugin.config[i].config
window.webContents.send(PICGO_CONFIG_PLUGIN, currentType, configName, config)
}
}
menu.push(obj)
}
}
// handle transformer
if (plugin.config.transformer.name) {
const currentTransformer = picgo.getConfig<string>('picBed.transformer') || 'path'
const pluginTransformer = plugin.config.transformer.name
const obj = {
label: `${currentTransformer === pluginTransformer ? '禁用' : '启用'}transformer - ${plugin.config.transformer.name}`,
click () {
const transformer = plugin.config.transformer.name
const currentTransformer = picgo.getConfig<string>('picBed.transformer') || 'path'
if (currentTransformer === transformer) {
picgo.saveConfig({
'picBed.transformer': 'path'
})
} else {
picgo.saveConfig({
'picBed.transformer': transformer
})
}
}
}
menu.push(obj)
}
// plugin custom menus
if (plugin.guiMenu) {
menu.push({
// @ts-ignore
type: 'separator'
})
for (const i of plugin.guiMenu) {
menu.push({
label: i.label,
click () {
// ipcRenderer.send('pluginActions', plugin.fullName, i.label)
const picgPlugin = picgo.pluginLoader.getPlugin(plugin.fullName)
if (picgPlugin?.guiMenu?.(picgo)?.length) {
const menu: GuiMenuItem[] = picgPlugin.guiMenu(picgo)
menu.forEach(item => {
if (item.label === i.label) {
item.handle(picgo, GuiApi.getInstance())
}
})
}
}
})
}
}
// @ts-ignore
return Menu.buildFromTemplate(menu)
}
export {
buildMiniPageMenu,
buildMainPageMenu,
buildUploadPageMenu,
buildPluginPageMenu
}

View File

@ -66,6 +66,7 @@ class LifeCycle {
updateShortKeyFromVersion212(db, db.get('settings.shortKey')) updateShortKeyFromVersion212(db, db.get('settings.shortKey'))
await migrateGalleryFromVersion230(db, GalleryDB.getInstance(), picgo) await migrateGalleryFromVersion230(db, GalleryDB.getInstance(), picgo)
} }
private onReady () { private onReady () {
const readyFunction = async () => { const readyFunction = async () => {
console.log('on ready') console.log('on ready')
@ -74,7 +75,7 @@ class LifeCycle {
// Install Vue Devtools // Install Vue Devtools
try { try {
await installExtension(VUEJS_DEVTOOLS) await installExtension(VUEJS_DEVTOOLS)
} catch (e) { } catch (e: any) {
console.error('Vue Devtools failed to install:', e.toString()) console.error('Vue Devtools failed to install:', e.toString())
} }
} }
@ -110,6 +111,7 @@ class LifeCycle {
readyFunction() readyFunction()
} }
} }
private onRunning () { private onRunning () {
app.on('second-instance', (event, commandLine, workingDirectory) => { app.on('second-instance', (event, commandLine, workingDirectory) => {
logger.info('detect second instance') logger.info('detect second instance')
@ -144,6 +146,7 @@ class LifeCycle {
process.env.XDG_CURRENT_DESKTOP = 'Unity' process.env.XDG_CURRENT_DESKTOP = 'Unity'
} }
} }
private onQuit () { private onQuit () {
app.on('window-all-closed', () => { app.on('window-all-closed', () => {
if (process.platform !== 'darwin') { if (process.platform !== 'darwin') {
@ -171,6 +174,7 @@ class LifeCycle {
} }
} }
} }
async launchApp () { async launchApp () {
const gotTheLock = app.requestSingleInstanceLock() const gotTheLock = app.requestSingleInstanceLock()
if (!gotTheLock) { if (!gotTheLock) {

View File

@ -29,6 +29,7 @@ class Server {
} }
this.httpServer = http.createServer(this.handleRequest) this.httpServer = http.createServer(this.handleRequest)
} }
private checkIfConfigIsValid (config: IObj | undefined) { private checkIfConfigIsValid (config: IObj | undefined) {
if (config && config.port && config.host && (config.enable !== undefined)) { if (config && config.port && config.host && (config.enable !== undefined)) {
return true return true
@ -36,6 +37,7 @@ class Server {
return false return false
} }
} }
private handleRequest = (request: http.IncomingMessage, response: http.ServerResponse) => { private handleRequest = (request: http.IncomingMessage, response: http.ServerResponse) => {
if (request.method === 'POST') { if (request.method === 'POST') {
if (!routers.getHandler(request.url!)) { if (!routers.getHandler(request.url!)) {
@ -57,8 +59,8 @@ class Server {
request.on('end', () => { request.on('end', () => {
try { try {
postObj = (body === '') ? {} : JSON.parse(body) postObj = (body === '') ? {} : JSON.parse(body)
} catch (err) { } catch (err: any) {
logger.error(`[PicGo Server]`, err) logger.error('[PicGo Server]', err)
return handleResponse({ return handleResponse({
response, response,
body: { body: {
@ -67,7 +69,7 @@ class Server {
} }
}) })
} }
logger.info(`[PicGo Server] get the request`, body) logger.info('[PicGo Server] get the request', body)
const handler = routers.getHandler(request.url!) const handler = routers.getHandler(request.url!)
handler!({ handler!({
...postObj, ...postObj,
@ -81,6 +83,7 @@ class Server {
response.end() response.end()
} }
} }
// port as string is a bug // port as string is a bug
private listen = (port: number | string) => { private listen = (port: number | string) => {
logger.info(`[PicGo Server] is listening at ${port}`) logger.info(`[PicGo Server] is listening at ${port}`)
@ -102,18 +105,21 @@ class Server {
} }
}) })
} }
startup () { startup () {
console.log('startup', this.config.enable) console.log('startup', this.config.enable)
if (this.config.enable) { if (this.config.enable) {
this.listen(this.config.port) this.listen(this.config.port)
} }
} }
shutdown (hasStarted?: boolean) { shutdown (hasStarted?: boolean) {
this.httpServer.close() this.httpServer.close()
if (!hasStarted) { if (!hasStarted) {
logger.info('[PicGo Server] shutdown') logger.info('[PicGo Server] shutdown')
} }
} }
restart () { restart () {
this.config = picgo.getConfig('settings.server') this.config = picgo.getConfig('settings.server')
this.shutdown() this.shutdown()

View File

@ -4,6 +4,7 @@ class Router {
get (url: string, callback: routeHandler): void { get (url: string, callback: routeHandler): void {
this.router.set(url, callback) this.router.set(url, callback)
} }
post (url: string, callback: routeHandler): void { post (url: string, callback: routeHandler): void {
this.router.set(url, callback) this.router.set(url, callback)
} }

View File

@ -69,7 +69,7 @@ router.post('/upload', async ({
}) })
} }
} }
} catch (err) { } catch (err: any) {
logger.error(err) logger.error(err)
handleResponse({ handleResponse({
response, response,

View File

@ -33,7 +33,7 @@ function resolveMacWorkFlow () {
* *
*/ */
function resolveClipboardImageGenerator () { function resolveClipboardImageGenerator () {
let clipboardFiles = getClipboardFiles() const clipboardFiles = getClipboardFiles()
if (!fs.pathExistsSync(path.join(CONFIG_DIR, 'windows10.ps1'))) { if (!fs.pathExistsSync(path.join(CONFIG_DIR, 'windows10.ps1'))) {
clipboardFiles.forEach(item => { clipboardFiles.forEach(item => {
fs.copyFileSync(item.origin, item.dest) fs.copyFileSync(item.origin, item.dest)
@ -46,8 +46,8 @@ function resolveClipboardImageGenerator () {
function diffFilesAndUpdate (filePath1: string, filePath2: string) { function diffFilesAndUpdate (filePath1: string, filePath2: string) {
try { try {
let file1 = fs.existsSync(filePath1) && fs.readFileSync(filePath1) const file1 = fs.existsSync(filePath1) && fs.readFileSync(filePath1)
let file2 = fs.existsSync(filePath1) && fs.readFileSync(filePath2) const file2 = fs.existsSync(filePath1) && fs.readFileSync(filePath2)
if (!file1 || !file2 || !file1.equals(file2)) { if (!file1 || !file2 || !file1.equals(file2)) {
fs.copyFileSync(filePath1, filePath2) fs.copyFileSync(filePath1, filePath2)
@ -59,7 +59,7 @@ function resolveClipboardImageGenerator () {
} }
function getClipboardFiles () { function getClipboardFiles () {
let files = [ const files = [
'/linux.sh', '/linux.sh',
'/mac.applescript', '/mac.applescript',
'/windows.ps1', '/windows.ps1',

View File

@ -34,7 +34,7 @@ export const showNotification = (options: IPrivateShowNotificationOption = {
} }
export const showMessageBox = (options: any) => { export const showMessageBox = (options: any) => {
return new Promise<IShowMessageBoxResult>(async (resolve, reject) => { return new Promise<IShowMessageBoxResult>(async (resolve) => {
dialog.showMessageBox( dialog.showMessageBox(
options options
).then((res) => { ).then((res) => {

View File

@ -39,7 +39,7 @@ const getUploadFiles = (argv = process.argv, cwd = process.cwd(), logger: Logger
path: item path: item
} }
} else { } else {
let tempPath = path.join(cwd, item) const tempPath = path.join(cwd, item)
if (fs.existsSync(tempPath)) { if (fs.existsSync(tempPath)) {
return { return {
path: tempPath path: tempPath

View File

@ -1,7 +1,7 @@
import { IPasteStyle } from '#/types/enum' import { IPasteStyle } from '#/types/enum'
const formatCustomLink = (customLink: string, item: ImgInfo) => { const formatCustomLink = (customLink: string, item: ImgInfo) => {
let fileName = item.fileName!.replace(new RegExp(`\\${item.extname}$`), '') const fileName = item.fileName!.replace(new RegExp(`\\${item.extname}$`), '')
const url = item.url || item.imgUrl const url = item.url || item.imgUrl
const formatObj = { const formatObj = {
url, url,
@ -10,7 +10,7 @@ const formatCustomLink = (customLink: string, item: ImgInfo) => {
const keys = Object.keys(formatObj) as ['url', 'fileName'] const keys = Object.keys(formatObj) as ['url', 'fileName']
keys.forEach(item => { keys.forEach(item => {
if (customLink.indexOf(`$${item}`) !== -1) { if (customLink.indexOf(`$${item}`) !== -1) {
let reg = new RegExp(`\\$${item}`, 'g') const reg = new RegExp(`\\$${item}`, 'g')
customLink = customLink.replace(reg, formatObj[item]) customLink = customLink.replace(reg, formatObj[item])
} }
}) })
@ -21,11 +21,11 @@ export default (style: IPasteStyle, item: ImgInfo, customLink: string | undefine
const url = item.url || item.imgUrl const url = item.url || item.imgUrl
const _customLink = customLink || '$url' const _customLink = customLink || '$url'
const tpl = { const tpl = {
'markdown': `![](${url})`, markdown: `![](${url})`,
'HTML': `<img src="${url}"/>`, HTML: `<img src="${url}"/>`,
'URL': url, URL: url,
'UBB': `[IMG]${url}[/IMG]`, UBB: `[IMG]${url}[/IMG]`,
'Custom': formatCustomLink(_customLink, item) Custom: formatCustomLink(_customLink, item)
} }
return tpl[style] return tpl[style]
} }

View File

@ -23,7 +23,8 @@ export default class extends Vue {
this.value = true this.value = true
} }
} }
choosePicBed (val: string) {
choosePicBed () {
this.saveConfig({ this.saveConfig({
'picBed.current': this.type, 'picBed.current': this.type,
'picBed.uploader': this.type 'picBed.uploader': this.type

View File

@ -83,8 +83,9 @@ export default class extends Vue {
handleConfigChange (val: any) { handleConfigChange (val: any) {
this.handleConfig(val) this.handleConfig(val)
} }
async validate () { async validate () {
return new Promise((resolve, reject) => { return new Promise((resolve) => {
// @ts-ignore // @ts-ignore
this.$refs.form.validate((valid: boolean) => { this.$refs.form.validate((valid: boolean) => {
if (valid) { if (valid) {
@ -109,7 +110,7 @@ export default class extends Vue {
return `transformer.${this.id}` return `transformer.${this.id}`
} }
default: default:
return `unknown` return 'unknown'
} }
} }
@ -119,8 +120,10 @@ export default class extends Vue {
if (val.length > 0) { if (val.length > 0) {
this.configList = cloneDeep(val).map((item: any) => { this.configList = cloneDeep(val).map((item: any) => {
let defaultValue = item.default !== undefined let defaultValue = item.default !== undefined
? item.default : item.type === 'checkbox' ? item.default
? [] : null : item.type === 'checkbox'
? []
: null
if (item.type === 'checkbox') { if (item.type === 'checkbox') {
const defaults = item.choices.filter((i: any) => { const defaults = item.choices.filter((i: any) => {
return i.checked return i.checked

View File

@ -15,7 +15,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Vue } from 'vue-property-decorator' import { Component, Vue } from 'vue-property-decorator'
import { remote, ipcRenderer, IpcRendererEvent } from 'electron' import { ipcRenderer, IpcRendererEvent } from 'electron'
import { import {
SHOW_INPUT_BOX, SHOW_INPUT_BOX,
SHOW_INPUT_BOX_RESPONSE SHOW_INPUT_BOX_RESPONSE
@ -30,30 +30,36 @@ export default class extends Vue {
title: '', title: '',
placeholder: '' placeholder: ''
} }
created () { created () {
ipcRenderer.on(SHOW_INPUT_BOX, this.ipcEventHandler) ipcRenderer.on(SHOW_INPUT_BOX, this.ipcEventHandler)
this.$bus.$on(SHOW_INPUT_BOX, this.initInputBoxValue) this.$bus.$on(SHOW_INPUT_BOX, this.initInputBoxValue)
} }
ipcEventHandler (evt: IpcRendererEvent, options: IShowInputBoxOption) { ipcEventHandler (evt: IpcRendererEvent, options: IShowInputBoxOption) {
this.initInputBoxValue(options) this.initInputBoxValue(options)
} }
initInputBoxValue (options: IShowInputBoxOption) { initInputBoxValue (options: IShowInputBoxOption) {
this.inputBoxValue = options.value || '' this.inputBoxValue = options.value || ''
this.inputBoxOptions.title = options.title || '' this.inputBoxOptions.title = options.title || ''
this.inputBoxOptions.placeholder = options.placeholder || '' this.inputBoxOptions.placeholder = options.placeholder || ''
this.showInputBoxVisible = true this.showInputBoxVisible = true
} }
handleInputBoxCancel () { handleInputBoxCancel () {
// TODO: RPCServer // TODO: RPCServer
this.showInputBoxVisible = false this.showInputBoxVisible = false
ipcRenderer.send(SHOW_INPUT_BOX, '') ipcRenderer.send(SHOW_INPUT_BOX, '')
this.$bus.$emit(SHOW_INPUT_BOX_RESPONSE, '') this.$bus.$emit(SHOW_INPUT_BOX_RESPONSE, '')
} }
handleInputBoxConfirm () { handleInputBoxConfirm () {
this.showInputBoxVisible = false this.showInputBoxVisible = false
ipcRenderer.send(SHOW_INPUT_BOX, this.inputBoxValue) ipcRenderer.send(SHOW_INPUT_BOX, this.inputBoxValue)
this.$bus.$emit(SHOW_INPUT_BOX_RESPONSE, this.inputBoxValue) this.$bus.$emit(SHOW_INPUT_BOX_RESPONSE, this.inputBoxValue)
} }
beforeDestroy () { beforeDestroy () {
ipcRenderer.removeListener(SHOW_INPUT_BOX, this.ipcEventHandler) ipcRenderer.removeListener(SHOW_INPUT_BOX, this.ipcEventHandler)
this.$bus.$off(SHOW_INPUT_BOX) this.$bus.$off(SHOW_INPUT_BOX)

View File

@ -148,29 +148,18 @@ import { Component, Vue, Watch } from 'vue-property-decorator'
import QrcodeVue from 'qrcode.vue' import QrcodeVue from 'qrcode.vue'
import pick from 'lodash/pick' import pick from 'lodash/pick'
import pkg from 'root/package.json' import pkg from 'root/package.json'
import keyDetect from '@/utils/key-binding'
import { import {
remote,
ipcRenderer, ipcRenderer,
IpcRendererEvent, IpcRendererEvent,
clipboard clipboard
} from 'electron' } from 'electron'
// import db from '#/datastore'
import mixin from '@/utils/mixin' import mixin from '@/utils/mixin'
import InputBoxDialog from '@/components/InputBoxDialog.vue' import InputBoxDialog from '@/components/InputBoxDialog.vue'
import { import {
SHOW_PRIVACY_MESSAGE, MINIMIZE_WINDOW,
OPEN_DEVTOOLS CLOSE_WINDOW,
SHOW_MAIN_PAGE_MENU
} from '~/universal/events/constants' } from '~/universal/events/constants'
import { IConfig } from 'picgo/dist/src/types/index'
const { Menu, dialog, BrowserWindow } = remote
const customLinkRule = (rule: string, value: string, callback: (arg0?: Error) => void) => {
if (!/\$url/.test(value)) {
return callback(new Error('必须含有$url'))
} else {
return callback()
}
}
@Component({ @Component({
name: 'main-page', name: 'main-page',
mixins: [mixin], mixins: [mixin],
@ -182,7 +171,6 @@ const customLinkRule = (rule: string, value: string, callback: (arg0?: Error) =>
export default class extends Vue { export default class extends Vue {
version = process.env.NODE_ENV === 'production' ? pkg.version : 'Dev' version = process.env.NODE_ENV === 'production' ? pkg.version : 'Dev'
defaultActive = 'upload' defaultActive = 'upload'
menu: Electron.Menu | null = null
visible = false visible = false
keyBindingVisible = false keyBindingVisible = false
customLinkVisible = false customLinkVisible = false
@ -193,13 +181,13 @@ export default class extends Vue {
choosedPicBedForQRCode: string[] = [] choosedPicBedForQRCode: string[] = []
created () { created () {
this.os = process.platform this.os = process.platform
this.buildMenu() ipcRenderer.send('getPicBeds')
ipcRenderer.on('getPicBeds', this.getPicBeds) ipcRenderer.on('getPicBeds', this.getPicBeds)
this.handleGetPicPeds() this.handleGetPicPeds()
} }
@Watch('choosedPicBedForQRCode') @Watch('choosedPicBedForQRCode')
choosedPicBedForQRCodeChange (val: string[], oldVal: string[]) { choosedPicBedForQRCodeChange (val: string[]) {
if (val.length > 0) { if (val.length > 0) {
this.$nextTick(async () => { this.$nextTick(async () => {
const picBedConfig = await this.getConfig('picBed') const picBedConfig = await this.getConfig('picBed')
@ -235,77 +223,38 @@ export default class extends Vue {
} }
} }
} }
minimizeWindow () { minimizeWindow () {
const window = BrowserWindow.getFocusedWindow() ipcRenderer.send(MINIMIZE_WINDOW)
window!.minimize()
} }
closeWindow () { closeWindow () {
const window = BrowserWindow.getFocusedWindow() ipcRenderer.send(CLOSE_WINDOW)
if (process.platform === 'linux') {
window!.hide()
} else {
window!.close()
}
}
buildMenu () {
const _this = this
const template = [
{
label: '关于',
click () {
dialog.showMessageBox({
title: 'PicGo',
message: 'PicGo',
detail: `Version: ${pkg.version}\nAuthor: Molunerfinn\nGithub: https://github.com/Molunerfinn/PicGo`
})
}
},
{
label: '赞助PicGo',
click () {
_this.visible = true
}
},
{
label: '生成图床配置二维码',
click () {
_this.qrcodeVisible = true
}
},
{
label: '隐私协议',
click () {
ipcRenderer.send(SHOW_PRIVACY_MESSAGE)
}
},
{
label: '打开调试器',
click () {
ipcRenderer.send(OPEN_DEVTOOLS)
}
}
]
this.menu = Menu.buildFromTemplate(template)
} }
openDialog () { openDialog () {
// this.menu!.popup(remote.getCurrentWindow()) ipcRenderer.send(SHOW_MAIN_PAGE_MENU)
this.menu!.popup()
} }
openMiniWindow () { openMiniWindow () {
ipcRenderer.send('openMiniWindow') ipcRenderer.send('openMiniWindow')
} }
handleCopyPicBedConfig () { handleCopyPicBedConfig () {
clipboard.writeText(this.picBedConfigString) clipboard.writeText(this.picBedConfigString)
this.$message.success('图床配置复制成功') this.$message.success('图床配置复制成功')
} }
getPicBeds (event: IpcRendererEvent, picBeds: IPicBedType[]) { getPicBeds (event: IpcRendererEvent, picBeds: IPicBedType[]) {
this.picBed = picBeds this.picBed = picBeds
} }
beforeRouteEnter (to: any, from: any, next: any) {
beforeRouteEnter (to: any, next: any) {
next((vm: this) => { next((vm: this) => {
vm.defaultActive = to.name vm.defaultActive = to.name
}) })
} }
beforeDestroy () { beforeDestroy () {
ipcRenderer.removeListener('getPicBeds', this.getPicBeds) ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
} }

View File

@ -110,10 +110,9 @@
<script lang="ts"> <script lang="ts">
// @ts-ignore // @ts-ignore
import gallerys from 'vue-gallery' import gallerys from 'vue-gallery'
import pasteStyle from '#/utils/pasteTemplate'
import { IPasteStyle } from '#/types/enum'
import { Component, Vue, Watch } from 'vue-property-decorator' import { Component, Vue, Watch } from 'vue-property-decorator'
import { IResult } from '@picgo/store/dist/types' import { IResult } from '@picgo/store/dist/types'
import { PASTE_TEXT } from '#/events/constants'
import { import {
ipcRenderer, ipcRenderer,
clipboard, clipboard,
@ -133,11 +132,13 @@ export default class extends Vue {
urlProperty: 'imgUrl', urlProperty: 'imgUrl',
closeOnSlideClick: true closeOnSlideClick: true
} }
dialogVisible = false dialogVisible = false
imgInfo = { imgInfo = {
id: '', id: '',
imgUrl: '' imgUrl: ''
} }
choosedList: IObjT<boolean> = {} choosedList: IObjT<boolean> = {}
choosedPicBed: string[] = [] choosedPicBed: string[] = []
lastChoosed: number = -1 lastChoosed: number = -1
@ -152,6 +153,7 @@ export default class extends Vue {
UBB: 'UBB', UBB: 'UBB',
Custom: 'Custom' Custom: 'Custom'
} }
picBed: IPicBedType[] = [] picBed: IPicBedType[] = []
@Watch('$route') @Watch('$route')
handleRouteUpdate (to: any, from: any) { handleRouteUpdate (to: any, from: any) {
@ -163,8 +165,9 @@ export default class extends Vue {
this.updateGallery() this.updateGallery()
} }
} }
async created () { async created () {
ipcRenderer.on('updateGallery', (event: IpcRendererEvent) => { ipcRenderer.on('updateGallery', () => {
this.$nextTick(async () => { this.$nextTick(async () => {
this.updateGallery() this.updateGallery()
}) })
@ -173,10 +176,12 @@ export default class extends Vue {
ipcRenderer.on('getPicBeds', this.getPicBeds) ipcRenderer.on('getPicBeds', this.getPicBeds)
this.updateGallery() this.updateGallery()
} }
mounted () { mounted () {
document.addEventListener('keydown', this.handleDetectShiftKey) document.addEventListener('keydown', this.handleDetectShiftKey)
document.addEventListener('keyup', this.handleDetectShiftKey) document.addEventListener('keyup', this.handleDetectShiftKey)
} }
handleDetectShiftKey (event: KeyboardEvent) { handleDetectShiftKey (event: KeyboardEvent) {
if (event.key === 'Shift') { if (event.key === 'Shift') {
if (event.type === 'keydown') { if (event.type === 'keydown') {
@ -186,9 +191,11 @@ export default class extends Vue {
} }
} }
} }
get filterList () { get filterList () {
return this.getGallery() return this.getGallery()
} }
get isAllSelected () { get isAllSelected () {
const values = Object.values(this.choosedList) const values = Object.values(this.choosedList)
if (values.length === 0) { if (values.length === 0) {
@ -199,9 +206,11 @@ export default class extends Vue {
}) })
} }
} }
getPicBeds (event: IpcRendererEvent, picBeds: IPicBedType[]) { getPicBeds (event: IpcRendererEvent, picBeds: IPicBedType[]) {
this.picBed = picBeds this.picBed = picBeds
} }
getGallery (): ImgInfo[] { getGallery (): ImgInfo[] {
if (this.searchText || this.choosedPicBed.length > 0) { if (this.searchText || this.choosedPicBed.length > 0) {
return this.images return this.images
@ -220,6 +229,7 @@ export default class extends Vue {
return this.images return this.images
} }
} }
async updateGallery () { async updateGallery () {
this.images = (await this.$$db.get({ orderBy: 'desc' })).data this.images = (await this.$$db.get({ orderBy: 'desc' })).data
} }
@ -228,12 +238,13 @@ export default class extends Vue {
handleFilterListChange () { handleFilterListChange () {
this.clearChoosedList() this.clearChoosedList()
} }
handleChooseImage (val: boolean, index: number) { handleChooseImage (val: boolean, index: number) {
if (val === true) { if (val === true) {
this.handleBarActive = true this.handleBarActive = true
if (this.lastChoosed !== -1 && this.isShiftKeyPress) { if (this.lastChoosed !== -1 && this.isShiftKeyPress) {
let min = Math.min(this.lastChoosed, index) const min = Math.min(this.lastChoosed, index)
let max = Math.max(this.lastChoosed, index) const max = Math.max(this.lastChoosed, index)
for (let i = min + 1; i < max; i++) { for (let i = min + 1; i < max; i++) {
const id = this.filterList[i].id! const id = this.filterList[i].id!
this.$set(this.choosedList, id, true) this.$set(this.choosedList, id, true)
@ -242,6 +253,7 @@ export default class extends Vue {
this.lastChoosed = index this.lastChoosed = index
} }
} }
clearChoosedList () { clearChoosedList () {
this.isShiftKeyPress = false this.isShiftKeyPress = false
Object.keys(this.choosedList).forEach(key => { Object.keys(this.choosedList).forEach(key => {
@ -249,10 +261,12 @@ export default class extends Vue {
}) })
this.lastChoosed = -1 this.lastChoosed = -1
} }
zoomImage (index: number) { zoomImage (index: number) {
this.idx = index this.idx = index
this.changeZIndexForGallery(true) this.changeZIndexForGallery(true)
} }
changeZIndexForGallery (isOpen: boolean) { changeZIndexForGallery (isOpen: boolean) {
if (isOpen) { if (isOpen) {
// @ts-ignore // @ts-ignore
@ -262,25 +276,25 @@ export default class extends Vue {
document.querySelector('.main-content.el-row').style.zIndex = 10 document.querySelector('.main-content.el-row').style.zIndex = 10
} }
} }
handleClose () { handleClose () {
this.idx = null this.idx = null
this.changeZIndexForGallery(false) this.changeZIndexForGallery(false)
} }
async copy (item: ImgInfo) { async copy (item: ImgInfo) {
const style = await this.getConfig<IPasteStyle>('settings.pasteStyle') || IPasteStyle.MARKDOWN const copyLink = await ipcRenderer.invoke(PASTE_TEXT, item)
const customLink = await this.getConfig<string>('settings.customLink')
const copyLink = pasteStyle(style, item, customLink)
const obj = { const obj = {
title: '复制链接成功', title: '复制链接成功',
body: copyLink, body: copyLink,
icon: item.url || item.imgUrl icon: item.url || item.imgUrl
} }
const myNotification = new Notification(obj.title, obj) const myNotification = new Notification(obj.title, obj)
clipboard.writeText(copyLink)
myNotification.onclick = () => { myNotification.onclick = () => {
return true return true
} }
} }
remove (id: string) { remove (id: string) {
this.$confirm('此操作将把该图片移出相册, 是否继续?', '提示', { this.$confirm('此操作将把该图片移出相册, 是否继续?', '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
@ -304,11 +318,13 @@ export default class extends Vue {
return true return true
}) })
} }
openDialog (item: ImgInfo) { openDialog (item: ImgInfo) {
this.imgInfo.id = item.id! this.imgInfo.id = item.id!
this.imgInfo.imgUrl = item.imgUrl as string this.imgInfo.imgUrl = item.imgUrl as string
this.dialogVisible = true this.dialogVisible = true
} }
async confirmModify () { async confirmModify () {
await this.$$db.updateById(this.imgInfo.id, { await this.$$db.updateById(this.imgInfo.id, {
imgUrl: this.imgInfo.imgUrl imgUrl: this.imgInfo.imgUrl
@ -325,26 +341,31 @@ export default class extends Vue {
this.dialogVisible = false this.dialogVisible = false
this.updateGallery() this.updateGallery()
} }
choosePicBed (type: string) { choosePicBed (type: string) {
let idx = this.choosedPicBed.indexOf(type) const idx = this.choosedPicBed.indexOf(type)
if (idx !== -1) { if (idx !== -1) {
this.choosedPicBed.splice(idx, 1) this.choosedPicBed.splice(idx, 1)
} else { } else {
this.choosedPicBed.push(type) this.choosedPicBed.push(type)
} }
} }
cleanSearch () { cleanSearch () {
this.searchText = '' this.searchText = ''
} }
isMultiple (obj: IObj) { isMultiple (obj: IObj) {
return Object.values(obj).some(item => item) return Object.values(obj).some(item => item)
} }
toggleSelectAll () { toggleSelectAll () {
const result = !this.isAllSelected const result = !this.isAllSelected
this.filterList.forEach(item => { this.filterList.forEach(item => {
this.$set(this.choosedList, item.id!, result) this.$set(this.choosedList, item.id!, result)
}) })
} }
multiRemove () { multiRemove () {
// choosedList -> { [id]: true or false }; true means choosed. false means not choosed. // choosedList -> { [id]: true or false }; true means choosed. false means not choosed.
const multiRemoveNumber = Object.values(this.choosedList).filter(item => item).length const multiRemoveNumber = Object.values(this.choosedList).filter(item => item).length
@ -354,7 +375,7 @@ export default class extends Vue {
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
}).then(async () => { }).then(async () => {
let files: IResult<ImgInfo>[] = [] const files: IResult<ImgInfo>[] = []
const imageIDList = Object.keys(this.choosedList) const imageIDList = Object.keys(this.choosedList)
for (let i = 0; i < imageIDList.length; i++) { for (let i = 0; i < imageIDList.length; i++) {
const key = imageIDList[i] const key = imageIDList[i]
@ -383,11 +404,10 @@ export default class extends Vue {
}) })
} }
} }
async multiCopy () { async multiCopy () {
if (Object.values(this.choosedList).some(item => item)) { if (Object.values(this.choosedList).some(item => item)) {
const copyString: string[] = [] const copyString: string[] = []
const style = await this.getConfig<IPasteStyle>('settings.pasteStyle') || IPasteStyle.MARKDOWN
const customLink = await this.getConfig<string>('settings.customLink')
// choosedList -> { [id]: true or false }; true means choosed. false means not choosed. // choosedList -> { [id]: true or false }; true means choosed. false means not choosed.
const imageIDList = Object.keys(this.choosedList) const imageIDList = Object.keys(this.choosedList)
for (let i = 0; i < imageIDList.length; i++) { for (let i = 0; i < imageIDList.length; i++) {
@ -395,7 +415,8 @@ export default class extends Vue {
if (this.choosedList[key]) { if (this.choosedList[key]) {
const item = await this.$$db.getById<ImgInfo>(key) const item = await this.$$db.getById<ImgInfo>(key)
if (item) { if (item) {
copyString.push(pasteStyle(style, item, customLink)) const txt = await ipcRenderer.invoke(PASTE_TEXT, item)
copyString.push(txt)
this.choosedList[key] = false this.choosedList[key] = false
} }
} }
@ -411,9 +432,11 @@ export default class extends Vue {
} }
} }
} }
toggleHandleBar () { toggleHandleBar () {
this.handleBarActive = !this.handleBarActive this.handleBarActive = !this.handleBarActive
} }
// getPasteStyle () { // getPasteStyle () {
// this.pasteStyle = this.$db.get('settings.pasteStyle') || 'markdown' // this.pasteStyle = this.$db.get('settings.pasteStyle') || 'markdown'
// } // }
@ -421,6 +444,7 @@ export default class extends Vue {
this.saveConfig('settings.pasteStyle', val) this.saveConfig('settings.pasteStyle', val)
this.pasteStyle = val this.pasteStyle = val
} }
beforeDestroy () { beforeDestroy () {
ipcRenderer.removeAllListeners('updateGallery') ipcRenderer.removeAllListeners('updateGallery')
ipcRenderer.removeListener('getPicBeds', this.getPicBeds) ipcRenderer.removeListener('getPicBeds', this.getPicBeds)

View File

@ -20,10 +20,9 @@ import mixin from '@/utils/mixin'
import { Component, Vue, Watch } from 'vue-property-decorator' import { Component, Vue, Watch } from 'vue-property-decorator'
import { import {
ipcRenderer, ipcRenderer,
IpcRendererEvent, IpcRendererEvent
remote
} from 'electron' } from 'electron'
import { SHOW_PRIVACY_MESSAGE, OPEN_DEVTOOLS } from '~/universal/events/constants' import { SHOW_MINI_PAGE_MENU } from '~/universal/events/constants'
@Component({ @Component({
name: 'mini-page', name: 'mini-page',
mixins: [mixin] mixins: [mixin]
@ -55,6 +54,7 @@ export default class extends Vue {
}) })
this.getPicBeds() this.getPicBeds()
} }
mounted () { mounted () {
window.addEventListener('mousedown', this.handleMouseDown, false) window.addEventListener('mousedown', this.handleMouseDown, false)
window.addEventListener('mousemove', this.handleMouseMove, false) window.addEventListener('mousemove', this.handleMouseMove, false)
@ -73,27 +73,31 @@ export default class extends Vue {
}, 1200) }, 1200)
} }
} }
getPicBeds () { getPicBeds () {
this.picBed = ipcRenderer.sendSync('getPicBeds') this.picBed = ipcRenderer.sendSync('getPicBeds')
this.buildMenu()
} }
onDrop (e: DragEvent) { onDrop (e: DragEvent) {
this.dragover = false this.dragover = false
this.ipcSendFiles(e.dataTransfer!.files) this.ipcSendFiles(e.dataTransfer!.files)
} }
openUploadWindow () { openUploadWindow () {
// @ts-ignore // @ts-ignore
document.getElementById('file-uploader').click() document.getElementById('file-uploader').click()
} }
onChange (e: any) { onChange (e: any) {
this.ipcSendFiles(e.target.files) this.ipcSendFiles(e.target.files)
// @ts-ignore // @ts-ignore
document.getElementById('file-uploader').value = '' document.getElementById('file-uploader').value = ''
} }
ipcSendFiles (files: FileList) { ipcSendFiles (files: FileList) {
let sendFiles: IFileWithPath[] = [] const sendFiles: IFileWithPath[] = []
Array.from(files).forEach((item, index) => { Array.from(files).forEach((item) => {
let obj = { const obj = {
name: item.name, name: item.name,
path: item.path path: item.path
} }
@ -101,6 +105,7 @@ export default class extends Vue {
}) })
ipcRenderer.send('uploadChoosedFiles', sendFiles) ipcRenderer.send('uploadChoosedFiles', sendFiles)
} }
handleMouseDown (e: MouseEvent) { handleMouseDown (e: MouseEvent) {
this.dragging = true this.dragging = true
this.wX = e.pageX this.wX = e.pageX
@ -108,20 +113,23 @@ export default class extends Vue {
this.screenX = e.screenX this.screenX = e.screenX
this.screenY = e.screenY this.screenY = e.screenY
} }
handleMouseMove (e: MouseEvent) { handleMouseMove (e: MouseEvent) {
e.preventDefault() e.preventDefault()
e.stopPropagation() e.stopPropagation()
if (this.dragging) { if (this.dragging) {
const xLoc = e.screenX - this.wX // const xLoc = e.screenX - this.wX
const yLoc = e.screenY - this.wY // const yLoc = e.screenY - this.wY
remote.BrowserWindow.getFocusedWindow()!.setBounds({ // FIXME: drag
x: xLoc, // remote.BrowserWindow.getFocusedWindow()!.setBounds({
y: yLoc, // x: xLoc,
width: 64, // y: yLoc,
height: 64 // width: 64,
}) // height: 64
// })
} }
} }
handleMouseUp (e: MouseEvent) { handleMouseUp (e: MouseEvent) {
this.dragging = false this.dragging = false
if (this.screenX === e.screenX && this.screenY === e.screenY) { if (this.screenX === e.screenX && this.screenY === e.screenY) {
@ -133,77 +141,11 @@ export default class extends Vue {
} }
} }
} }
openContextMenu () { openContextMenu () {
this.menu!.popup() ipcRenderer.send(SHOW_MINI_PAGE_MENU)
}
async buildMenu () {
const _this = this
const current = await this.getConfig('picBed.current')
const submenu = this.picBed.filter(item => item.visible).map(item => {
return {
label: item.name,
type: 'radio',
checked: current === item.type,
click () {
_this.saveConfig({
'picBed.current': item.type,
'picBed.uploader': item.type
})
ipcRenderer.send('syncPicBed')
}
}
})
const template = [
{
label: '打开详细窗口',
click () {
ipcRenderer.send('openSettingWindow')
}
},
{
label: '选择默认图床',
type: 'submenu',
submenu
},
{
label: '剪贴板图片上传',
click () {
ipcRenderer.send('uploadClipboardFilesFromUploadPage')
}
},
{
label: '隐藏窗口',
click () {
remote.BrowserWindow.getFocusedWindow()!.hide()
}
},
{
label: '隐私协议',
click () {
ipcRenderer.send(SHOW_PRIVACY_MESSAGE)
}
},
{
label: '重启应用',
click () {
remote.app.relaunch()
remote.app.exit(0)
}
},
{
label: '打开调试器',
click () {
ipcRenderer.send(OPEN_DEVTOOLS)
}
},
{
role: 'quit',
label: '退出'
}
]
// @ts-ignore
this.menu = remote.Menu.buildFromTemplate(template)
} }
beforeDestroy () { beforeDestroy () {
ipcRenderer.removeAllListeners('uploadProgress') ipcRenderer.removeAllListeners('uploadProgress')
ipcRenderer.removeListener('getPicBeds', this.getPicBeds) ipcRenderer.removeListener('getPicBeds', this.getPicBeds)

View File

@ -339,10 +339,9 @@
import keyDetect from '@/utils/key-binding' import keyDetect from '@/utils/key-binding'
import pkg from 'root/package.json' import pkg from 'root/package.json'
import { IConfig } from 'picgo/dist/src/types/index' import { IConfig } from 'picgo/dist/src/types/index'
import { PICGO_OPEN_FILE } from '#/events/constants' import { PICGO_OPEN_FILE, OPEN_URL } from '#/events/constants'
import { import {
ipcRenderer, ipcRenderer
remote
} from 'electron' } from 'electron'
import { Component, Vue } from 'vue-property-decorator' import { Component, Vue } from 'vue-property-decorator'
// import db from '#/datastore' // import db from '#/datastore'
@ -373,6 +372,7 @@ export default class extends Vue {
autoCopyUrl: true, autoCopyUrl: true,
checkBetaUpdate: true checkBetaUpdate: true
} }
picBed: IPicBedType[] = [] picBed: IPicBedType[] = []
logFileVisible = false logFileVisible = false
keyBindingVisible = false keyBindingVisible = false
@ -383,9 +383,11 @@ export default class extends Vue {
customLink = { customLink = {
value: '$url' value: '$url'
} }
shortKey: IShortKeyMap = { shortKey: IShortKeyMap = {
upload: '' upload: ''
} }
proxy = '' proxy = ''
npmRegistry = '' npmRegistry = ''
npmProxy = '' npmProxy = ''
@ -394,6 +396,7 @@ export default class extends Vue {
{ validator: customLinkRule, trigger: 'blur' } { validator: customLinkRule, trigger: 'blur' }
] ]
} }
logLevel = { logLevel = {
all: '全部-All', all: '全部-All',
success: '成功-Success', success: '成功-Success',
@ -402,11 +405,13 @@ export default class extends Vue {
warn: '提醒-Warn', warn: '提醒-Warn',
none: '不记录日志-None' none: '不记录日志-None'
} }
server = { server = {
port: 36677, port: 36677,
host: '127.0.0.1', host: '127.0.0.1',
enable: true enable: true
} }
version = pkg.version version = pkg.version
latestVersion = '' latestVersion = ''
os = '' os = ''
@ -418,12 +423,14 @@ export default class extends Vue {
return false return false
} }
} }
created () { created () {
this.os = process.platform this.os = process.platform
ipcRenderer.send('getPicBeds') ipcRenderer.send('getPicBeds')
ipcRenderer.on('getPicBeds', this.getPicBeds) ipcRenderer.on('getPicBeds', this.getPicBeds)
this.initData() this.initData()
} }
async initData () { async initData () {
const config = (await this.getConfig<IConfig>())! const config = (await this.getConfig<IConfig>())!
if (config !== undefined) { if (config !== undefined) {
@ -469,21 +476,27 @@ export default class extends Vue {
if (item.visible) { if (item.visible) {
return item.name return item.name
} }
}) as string[] return null
}).filter(item => item) as string[]
} }
openFile (file: string) { openFile (file: string) {
ipcRenderer.send(PICGO_OPEN_FILE, file) ipcRenderer.send(PICGO_OPEN_FILE, file)
} }
openLogSetting () { openLogSetting () {
this.logFileVisible = true this.logFileVisible = true
} }
keyDetect (type: string, event: KeyboardEvent) { keyDetect (type: string, event: KeyboardEvent) {
this.shortKey[type] = keyDetect(event).join('+') this.shortKey[type] = keyDetect(event).join('+')
} }
async cancelCustomLink () { async cancelCustomLink () {
this.customLinkVisible = false this.customLinkVisible = false
this.customLink.value = await this.getConfig<string>('settings.customLink') || '$url' this.customLink.value = await this.getConfig<string>('settings.customLink') || '$url'
} }
confirmCustomLink () { confirmCustomLink () {
// @ts-ignore // @ts-ignore
this.$refs.customLink.validate((valid: boolean) => { this.$refs.customLink.validate((valid: boolean) => {
@ -496,10 +509,12 @@ export default class extends Vue {
} }
}) })
} }
async cancelProxy () { async cancelProxy () {
this.proxyVisible = false this.proxyVisible = false
this.proxy = await this.getConfig<string>('picBed.proxy') || '' this.proxy = await this.getConfig<string>('picBed.proxy') || ''
} }
confirmProxy () { confirmProxy () {
this.proxyVisible = false this.proxyVisible = false
this.saveConfig({ this.saveConfig({
@ -514,12 +529,15 @@ export default class extends Vue {
return true return true
} }
} }
updateHelperChange (val: boolean) { updateHelperChange (val: boolean) {
this.saveConfig('settings.showUpdateTip', val) this.saveConfig('settings.showUpdateTip', val)
} }
checkBetaUpdateChange (val: boolean) { checkBetaUpdateChange (val: boolean) {
this.saveConfig('settings.checkBetaUpdate', val) this.saveConfig('settings.checkBetaUpdate', val)
} }
handleShowPicBedListChange (val: string[]) { handleShowPicBedListChange (val: string[]) {
const list = this.picBed.map(item => { const list = this.picBed.map(item => {
if (!val.includes(item.name)) { if (!val.includes(item.name)) {
@ -534,20 +552,24 @@ export default class extends Vue {
}) })
ipcRenderer.send('getPicBeds') ipcRenderer.send('getPicBeds')
} }
handleAutoStartChange (val: boolean) { handleAutoStartChange (val: boolean) {
this.saveConfig('settings.autoStart', val) this.saveConfig('settings.autoStart', val)
ipcRenderer.send('autoStart', val) ipcRenderer.send('autoStart', val)
} }
handleRename (val: boolean) { handleRename (val: boolean) {
this.saveConfig({ this.saveConfig({
'settings.rename': val 'settings.rename': val
}) })
} }
handleAutoRename (val: boolean) { handleAutoRename (val: boolean) {
this.saveConfig({ this.saveConfig({
'settings.autoRename': val 'settings.autoRename': val
}) })
} }
compareVersion2Update (current: string, latest: string) { compareVersion2Update (current: string, latest: string) {
const currentVersion = current.split('.').map(item => parseInt(item)) const currentVersion = current.split('.').map(item => parseInt(item))
const latestVersion = latest.split('.').map(item => parseInt(item)) const latestVersion = latest.split('.').map(item => parseInt(item))
@ -562,6 +584,7 @@ export default class extends Vue {
} }
return false return false
} }
checkUpdate () { checkUpdate () {
this.checkUpdateVisible = true this.checkUpdateVisible = true
this.$http.get(releaseUrl) this.$http.get(releaseUrl)
@ -576,24 +599,29 @@ export default class extends Vue {
}) })
}) })
} }
confirmCheckVersion () { confirmCheckVersion () {
if (this.needUpdate) { if (this.needUpdate) {
remote.shell.openExternal(downloadUrl) ipcRenderer.send(OPEN_URL, downloadUrl)
} }
this.checkUpdateVisible = false this.checkUpdateVisible = false
} }
cancelCheckVersion () { cancelCheckVersion () {
this.checkUpdateVisible = false this.checkUpdateVisible = false
} }
handleUploadNotification (val: boolean) { handleUploadNotification (val: boolean) {
this.saveConfig({ this.saveConfig({
'settings.uploadNotification': val 'settings.uploadNotification': val
}) })
} }
handleMiniWindowOntop (val: boolean) { handleMiniWindowOntop (val: boolean) {
this.saveConfig('settings.miniWindowOntop', val) this.saveConfig('settings.miniWindowOntop', val)
this.$message.info('需要重启生效') this.$message.info('需要重启生效')
} }
handleAutoCopyUrl (val: boolean) { handleAutoCopyUrl (val: boolean) {
this.saveConfig('settings.autoCopy', val) this.saveConfig('settings.autoCopy', val)
const successNotification = new Notification('设置自动复制链接', { const successNotification = new Notification('设置自动复制链接', {
@ -603,6 +631,7 @@ export default class extends Vue {
return true return true
} }
} }
confirmLogLevelSetting () { confirmLogLevelSetting () {
if (this.form.logLevel.length === 0) { if (this.form.logLevel.length === 0) {
return this.$message.error('请选择日志记录等级') return this.$message.error('请选择日志记录等级')
@ -618,6 +647,7 @@ export default class extends Vue {
} }
this.logFileVisible = false this.logFileVisible = false
} }
async cancelLogLevelSetting () { async cancelLogLevelSetting () {
this.logFileVisible = false this.logFileVisible = false
let logLevel = await this.getConfig<string | string[]>('settings.logLevel') let logLevel = await this.getConfig<string | string[]>('settings.logLevel')
@ -630,6 +660,7 @@ export default class extends Vue {
} }
this.form.logLevel = logLevel this.form.logLevel = logLevel
} }
confirmServerSetting () { confirmServerSetting () {
// @ts-ignore // @ts-ignore
this.server.port = parseInt(this.server.port, 10) this.server.port = parseInt(this.server.port, 10)
@ -645,6 +676,7 @@ export default class extends Vue {
this.serverVisible = false this.serverVisible = false
ipcRenderer.send('updateServer') ipcRenderer.send('updateServer')
} }
async cancelServerSetting () { async cancelServerSetting () {
this.serverVisible = false this.serverVisible = false
this.server = await this.getConfig('settings.server') || { this.server = await this.getConfig('settings.server') || {
@ -653,10 +685,11 @@ export default class extends Vue {
enable: true enable: true
} }
} }
handleLevelDisabled (val: string) { handleLevelDisabled (val: string) {
let currentLevel = val const currentLevel = val
let flagLevel let flagLevel
let result = this.form.logLevel.some(item => { const result = this.form.logLevel.some(item => {
if (item === 'all' || item === 'none') { if (item === 'all' || item === 'none') {
flagLevel = item flagLevel = item
} }
@ -673,12 +706,15 @@ export default class extends Vue {
} }
return false return false
} }
goConfigPage () { goConfigPage () {
remote.shell.openExternal('https://picgo.github.io/PicGo-Doc/zh/guide/config.html#picgo设置') ipcRenderer.send(OPEN_URL, 'https://picgo.github.io/PicGo-Doc/zh/guide/config.html#picgo设置')
} }
goShortCutPage () { goShortCutPage () {
this.$router.push('shortKey') this.$router.push('shortKey')
} }
beforeDestroy () { beforeDestroy () {
ipcRenderer.removeListener('getPicBeds', this.getPicBeds) ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
} }

View File

@ -109,11 +109,17 @@ import ConfigForm from '@/components/ConfigForm.vue'
import { debounce } from 'lodash' import { debounce } from 'lodash'
import { import {
ipcRenderer, ipcRenderer,
remote,
IpcRendererEvent IpcRendererEvent
} from 'electron' } from 'electron'
import { handleStreamlinePluginName } from '~/universal/utils/common' import { handleStreamlinePluginName } from '~/universal/utils/common'
const { Menu } = remote import {
OPEN_URL,
RELOAD_APP,
PICGO_CONFIG_PLUGIN,
PICGO_HANDLE_PLUGIN_ING,
PICGO_TOGGLE_PLUGIN,
SHOW_PLUGIN_PAGE_MENU
} from '#/events/constants'
@Component({ @Component({
name: 'plugin', name: 'plugin',
@ -144,6 +150,7 @@ export default class extends Vue {
? `picgo-plugin-${this.searchText}` ? `picgo-plugin-${this.searchText}`
: this.searchText : this.searchText
} }
@Watch('npmSearchText') @Watch('npmSearchText')
onNpmSearchTextChange (val: string) { onNpmSearchTextChange (val: string) {
if (val) { if (val) {
@ -154,6 +161,7 @@ export default class extends Vue {
this.getPluginList() this.getPluginList()
} }
} }
@Watch('dialogVisible') @Watch('dialogVisible')
onDialogVisible (val: boolean) { onDialogVisible (val: boolean) {
if (val) { if (val) {
@ -164,6 +172,7 @@ export default class extends Vue {
document.querySelector('.main-content.el-row').style.zIndex = 10 document.querySelector('.main-content.el-row').style.zIndex = 10
} }
} }
async created () { async created () {
this.os = process.platform this.os = process.platform
ipcRenderer.on('hideLoading', () => { ipcRenderer.on('hideLoading', () => {
@ -214,105 +223,45 @@ export default class extends Vue {
}) })
this.pluginNameList = this.pluginNameList.filter(item => item !== plugin) this.pluginNameList = this.pluginNameList.filter(item => item !== plugin)
}) })
ipcRenderer.on(PICGO_CONFIG_PLUGIN, (evt: IpcRendererEvent, currentType: string, configName: string, config: any) => {
this.currentType = currentType
this.configName = configName
this.dialogVisible = true
this.config = config
})
ipcRenderer.on(PICGO_HANDLE_PLUGIN_ING, (evt: IpcRendererEvent, fullName: string) => {
this.pluginList.forEach(item => {
if (item.fullName === fullName || (item.name === fullName)) {
item.ing = true
}
})
this.loading = true
})
ipcRenderer.on(PICGO_TOGGLE_PLUGIN, (evt: IpcRendererEvent, fullName: string, enabled: boolean) => {
const plugin = this.pluginList.find(item => item.fullName === fullName)
if (plugin) {
plugin.enabled = enabled
this.getPicBeds()
this.needReload = true
}
})
this.getPluginList() this.getPluginList()
this.getSearchResult = debounce(this.getSearchResult, 50) this.getSearchResult = debounce(this.getSearchResult, 50)
this.needReload = await this.getConfig<boolean>('needReload') || false this.needReload = await this.getConfig<boolean>('needReload') || false
} }
async buildContextMenu (plugin: IPicGoPlugin) { async buildContextMenu (plugin: IPicGoPlugin) {
const _this = this ipcRenderer.send(SHOW_PLUGIN_PAGE_MENU, plugin)
let menu = [{
label: '启用插件',
enabled: !plugin.enabled,
click () {
_this.saveConfig({
[`picgoPlugins.${plugin.fullName}`]: true
})
plugin.enabled = true
_this.getPicBeds()
_this.needReload = true
}
}, {
label: '禁用插件',
enabled: plugin.enabled,
click () {
_this.saveConfig({
[`picgoPlugins.${plugin.fullName}`]: false
})
plugin.enabled = false
_this.getPicBeds()
if (plugin.config.transformer.name) {
_this.handleRestoreState('transformer', plugin.config.transformer.name)
}
if (plugin.config.uploader.name) {
_this.handleRestoreState('uploader', plugin.config.uploader.name)
}
_this.needReload = true
}
}, {
label: '卸载插件',
click () {
_this.uninstallPlugin(plugin.fullName)
}
}, {
label: '更新插件',
click () {
_this.updatePlugin(plugin.fullName)
}
}]
for (let i in plugin.config) {
if (plugin.config[i].config.length > 0) {
const obj = {
label: `配置${i} - ${plugin.config[i].fullName || plugin.config[i].name}`,
click () {
_this.currentType = i
_this.configName = plugin.config[i].fullName || plugin.config[i].name
_this.dialogVisible = true
_this.config = plugin.config[i].config
},
enabled: plugin.enabled
}
menu.push(obj)
}
}
// handle transformer
if (plugin.config.transformer.name) {
let currentTransformer = await this.getConfig<string>('picBed.transformer') || 'path'
let pluginTransformer = plugin.config.transformer.name
const obj = {
label: `${currentTransformer === pluginTransformer ? '禁用' : '启用'}transformer - ${plugin.config.transformer.name}`,
click () {
_this.toggleTransformer(plugin.config.transformer.name)
}
}
menu.push(obj)
}
// plugin custom menus
if (plugin.guiMenu) {
menu.push({
// @ts-ignore
type: 'separator'
})
for (let i of plugin.guiMenu) {
menu.push({
label: i.label,
click () {
ipcRenderer.send('pluginActions', plugin.fullName, i.label)
}
})
}
}
this.menu = Menu.buildFromTemplate(menu)
this.menu.popup()
} }
getPluginList () { getPluginList () {
ipcRenderer.send('getPluginList') ipcRenderer.send('getPluginList')
} }
getPicBeds () { getPicBeds () {
ipcRenderer.send('getPicBeds') ipcRenderer.send('getPicBeds')
} }
installPlugin (item: IPicGoPlugin) { installPlugin (item: IPicGoPlugin) {
if (!item.gui) { if (!item.gui) {
this.$confirm('该插件未对可视化界面进行优化, 是否继续安装?', '提示', { this.$confirm('该插件未对可视化界面进行优化, 是否继续安装?', '提示', {
@ -330,6 +279,7 @@ export default class extends Vue {
ipcRenderer.send('installPlugin', item.fullName) ipcRenderer.send('installPlugin', item.fullName)
} }
} }
uninstallPlugin (val: string) { uninstallPlugin (val: string) {
this.pluginList.forEach(item => { this.pluginList.forEach(item => {
if (item.name === val) { if (item.name === val) {
@ -339,6 +289,7 @@ export default class extends Vue {
this.loading = true this.loading = true
ipcRenderer.send('uninstallPlugin', val) ipcRenderer.send('uninstallPlugin', val)
} }
updatePlugin (val: string) { updatePlugin (val: string) {
this.pluginList.forEach(item => { this.pluginList.forEach(item => {
if (item.fullName === val) { if (item.fullName === val) {
@ -348,10 +299,11 @@ export default class extends Vue {
this.loading = true this.loading = true
ipcRenderer.send('updatePlugin', val) ipcRenderer.send('updatePlugin', val)
} }
reloadApp () { reloadApp () {
remote.app.relaunch() ipcRenderer.send(RELOAD_APP)
remote.app.exit(0)
} }
async handleReload () { async handleReload () {
this.saveConfig({ this.saveConfig({
needReload: true needReload: true
@ -364,21 +316,11 @@ export default class extends Vue {
this.reloadApp() this.reloadApp()
} }
} }
cleanSearch () { cleanSearch () {
this.searchText = '' this.searchText = ''
} }
async toggleTransformer (transformer: string) {
let currentTransformer = await this.getConfig<string>('picBed.transformer') || 'path'
if (currentTransformer === transformer) {
this.saveConfig({
'picBed.transformer': 'path'
})
} else {
this.saveConfig({
'picBed.transformer': transformer
})
}
}
async handleConfirmConfig () { async handleConfirmConfig () {
// @ts-ignore // @ts-ignore
const result = await this.$refs.configForm.validate() const result = await this.$refs.configForm.validate()
@ -410,6 +352,7 @@ export default class extends Vue {
this.getPluginList() this.getPluginList()
} }
} }
getSearchResult (val: string) { getSearchResult (val: string) {
// this.$http.get(`https://api.npms.io/v2/search?q=${val}`) // this.$http.get(`https://api.npms.io/v2/search?q=${val}`)
this.$http.get(`https://registry.npmjs.com/-/v1/search?text=${val}`) this.$http.get(`https://registry.npmjs.com/-/v1/search?text=${val}`)
@ -428,6 +371,7 @@ export default class extends Vue {
this.loading = false this.loading = false
}) })
} }
handleSearchResult (item: INPMSearchResultObject) { handleSearchResult (item: INPMSearchResultObject) {
const name = handleStreamlinePluginName(item.package.name) const name = handleStreamlinePluginName(item.package.name)
let gui = false let gui = false
@ -450,6 +394,7 @@ export default class extends Vue {
ing: false // installing or uninstalling ing: false // installing or uninstalling
} }
} }
// restore Uploader & Transformer // restore Uploader & Transformer
async handleRestoreState (item: string, name: string) { async handleRestoreState (item: string, name: string) {
if (item === 'uploader') { if (item === 'uploader') {
@ -470,18 +415,26 @@ export default class extends Vue {
} }
} }
} }
openHomepage (url: string) { openHomepage (url: string) {
if (url) { if (url) {
remote.shell.openExternal(url) ipcRenderer.send(OPEN_URL, url)
} }
} }
goAwesomeList () { goAwesomeList () {
remote.shell.openExternal('https://github.com/PicGo/Awesome-PicGo') ipcRenderer.send(OPEN_URL, 'https://github.com/PicGo/Awesome-PicGo')
} }
saveConfig (data: IObj) {
ipcRenderer.send('picgoSaveData', data)
}
handleImportLocalPlugin () { handleImportLocalPlugin () {
ipcRenderer.send('importLocalPlugin') ipcRenderer.send('importLocalPlugin')
this.loading = true this.loading = true
} }
beforeDestroy () { beforeDestroy () {
ipcRenderer.removeAllListeners('pluginList') ipcRenderer.removeAllListeners('pluginList')
ipcRenderer.removeAllListeners('installPlugin') ipcRenderer.removeAllListeners('installPlugin')

View File

@ -41,12 +41,15 @@ export default class extends Vue {
this.id = id this.id = id
}) })
} }
confirmName () { confirmName () {
ipcRenderer.send(`rename${this.id}`, this.fileName) ipcRenderer.send(`rename${this.id}`, this.fileName)
} }
cancel () { cancel () {
ipcRenderer.send(`rename${this.id}`, null) ipcRenderer.send(`rename${this.id}`, null)
} }
beforeDestroy () { beforeDestroy () {
ipcRenderer.removeAllListeners('rename') ipcRenderer.removeAllListeners('rename')
} }

View File

@ -118,35 +118,43 @@ export default class extends Vue {
} }
}) })
} }
@Watch('keyBindingVisible') @Watch('keyBindingVisible')
onKeyBindingVisibleChange (val: boolean) { onKeyBindingVisibleChange (val: boolean) {
ipcRenderer.send(TOGGLE_SHORTKEY_MODIFIED_MODE, val) ipcRenderer.send(TOGGLE_SHORTKEY_MODIFIED_MODE, val)
} }
calcOrigin (item: string) { calcOrigin (item: string) {
const [origin] = item.split(':') const [origin] = item.split(':')
return origin return origin
} }
calcOriginShowName (item: string) { calcOriginShowName (item: string) {
return item.replace('picgo-plugin-', '') return item.replace('picgo-plugin-', '')
} }
toggleEnable (item: IShortKeyConfig) { toggleEnable (item: IShortKeyConfig) {
const status = !item.enable const status = !item.enable
item.enable = status item.enable = status
ipcRenderer.send('bindOrUnbindShortKey', item, item.from) ipcRenderer.send('bindOrUnbindShortKey', item, item.from)
} }
keyDetect (event: KeyboardEvent) { keyDetect (event: KeyboardEvent) {
this.shortKey = keyDetect(event).join('+') this.shortKey = keyDetect(event).join('+')
} }
async openKeyBindingDialog (config: IShortKeyConfig, index: number) { async openKeyBindingDialog (config: IShortKeyConfig, index: number) {
this.command = `${config.from}:${config.name}` this.command = `${config.from}:${config.name}`
this.shortKey = await this.getConfig(`settings.shortKey.${this.command}.key`) || '' this.shortKey = await this.getConfig(`settings.shortKey.${this.command}.key`) || ''
this.currentIndex = index this.currentIndex = index
this.keyBindingVisible = true this.keyBindingVisible = true
} }
async cancelKeyBinding () { async cancelKeyBinding () {
this.keyBindingVisible = false this.keyBindingVisible = false
this.shortKey = await this.getConfig<string>(`settings.shortKey.${this.command}.key`) || '' this.shortKey = await this.getConfig<string>(`settings.shortKey.${this.command}.key`) || ''
} }
async confirmKeyBinding () { async confirmKeyBinding () {
const oldKey = await this.getConfig<string>(`settings.shortKey.${this.command}.key`) const oldKey = await this.getConfig<string>(`settings.shortKey.${this.command}.key`)
const config = Object.assign({}, this.list[this.currentIndex]) const config = Object.assign({}, this.list[this.currentIndex])
@ -159,6 +167,7 @@ export default class extends Vue {
} }
}) })
} }
beforeDestroy () { beforeDestroy () {
ipcRenderer.send(TOGGLE_SHORTKEY_MODIFIED_MODE, false) ipcRenderer.send(TOGGLE_SHORTKEY_MODIFIED_MODE, false)
} }

View File

@ -28,10 +28,9 @@
<script lang="ts"> <script lang="ts">
import { Component, Vue } from 'vue-property-decorator' import { Component, Vue } from 'vue-property-decorator'
import mixin from '@/utils/mixin' import mixin from '@/utils/mixin'
import pasteTemplate from '#/utils/pasteTemplate' import { ipcRenderer } from 'electron'
import { ipcRenderer, clipboard } from 'electron'
import { IPasteStyle } from '#/types/enum'
import { IResult } from '@picgo/store/dist/types' import { IResult } from '@picgo/store/dist/types'
import { PASTE_TEXT } from '#/events/constants'
@Component({ @Component({
name: 'tray-page', name: 'tray-page',
@ -44,28 +43,31 @@ export default class extends Vue {
body: '', body: '',
icon: '' icon: ''
} }
clipboardFiles: ImgInfo[] = [] clipboardFiles: ImgInfo[] = []
uploadFlag = false uploadFlag = false
get reverseList () { get reverseList () {
return this.files.slice().reverse() return this.files.slice().reverse()
} }
async getData () { async getData () {
this.files = (await this.$$db.get<ImgInfo>({ orderBy: 'desc', limit: 5 })).data this.files = (await this.$$db.get<ImgInfo>({ orderBy: 'desc', limit: 5 })).data
} }
async copyTheLink (item: ImgInfo) { async copyTheLink (item: ImgInfo) {
this.notification.body = item.imgUrl! this.notification.body = item.imgUrl!
this.notification.icon = item.imgUrl! this.notification.icon = item.imgUrl!
const myNotification = new Notification(this.notification.title, this.notification) const myNotification = new Notification(this.notification.title, this.notification)
const pasteStyle = await this.getConfig<IPasteStyle>('settings.pasteStyle') || IPasteStyle.MARKDOWN ipcRenderer.invoke(PASTE_TEXT, item)
const customLink = await this.getConfig<string>('settings.customLink')
clipboard.writeText(pasteTemplate(pasteStyle, item, customLink))
myNotification.onclick = () => { myNotification.onclick = () => {
return true return true
} }
} }
calcHeight (width: number, height: number): number { calcHeight (width: number, height: number): number {
return height * 160 / width return height * 160 / width
} }
disableDragFile () { disableDragFile () {
window.addEventListener('dragover', (e) => { window.addEventListener('dragover', (e) => {
e = e || event e = e || event
@ -76,6 +78,7 @@ export default class extends Vue {
e.preventDefault() e.preventDefault()
}, false) }, false)
} }
uploadClipboardFiles () { uploadClipboardFiles () {
if (this.uploadFlag) { if (this.uploadFlag) {
return return
@ -83,6 +86,7 @@ export default class extends Vue {
this.uploadFlag = true this.uploadFlag = true
ipcRenderer.send('uploadClipboardFiles') ipcRenderer.send('uploadClipboardFiles')
} }
mounted () { mounted () {
this.disableDragFile() this.disableDragFile()
this.getData() this.getData()
@ -96,15 +100,16 @@ export default class extends Vue {
ipcRenderer.on('clipboardFiles', (event: Event, files: ImgInfo[]) => { ipcRenderer.on('clipboardFiles', (event: Event, files: ImgInfo[]) => {
this.clipboardFiles = files this.clipboardFiles = files
}) })
ipcRenderer.on('uploadFiles', async (event: Event) => { ipcRenderer.on('uploadFiles', async () => {
this.files = (await this.$$db.get<ImgInfo>({ orderBy: 'desc', limit: 5 })).data this.files = (await this.$$db.get<ImgInfo>({ orderBy: 'desc', limit: 5 })).data
console.log(this.files) console.log(this.files)
this.uploadFlag = false this.uploadFlag = false
}) })
ipcRenderer.on('updateFiles', (event: Event) => { ipcRenderer.on('updateFiles', () => {
this.getData() this.getData()
}) })
} }
beforeDestroy () { beforeDestroy () {
ipcRenderer.removeAllListeners('dragFiles') ipcRenderer.removeAllListeners('dragFiles')
ipcRenderer.removeAllListeners('clipboardFiles') ipcRenderer.removeAllListeners('clipboardFiles')

View File

@ -60,17 +60,16 @@
import { Component, Vue, Watch } from 'vue-property-decorator' import { Component, Vue, Watch } from 'vue-property-decorator'
import { import {
ipcRenderer, ipcRenderer,
IpcRendererEvent, IpcRendererEvent
remote
} from 'electron' } from 'electron'
import { import {
SHOW_INPUT_BOX, SHOW_INPUT_BOX,
SHOW_INPUT_BOX_RESPONSE SHOW_INPUT_BOX_RESPONSE,
SHOW_UPLOAD_PAGE_MENU
} from '~/universal/events/constants' } from '~/universal/events/constants'
import { import {
isUrl isUrl
} from '~/universal/utils/common' } from '~/universal/utils/common'
const { Menu } = remote
@Component({ @Component({
name: 'upload' name: 'upload'
}) })
@ -82,7 +81,6 @@ export default class extends Vue {
pasteStyle = '' pasteStyle = ''
picBed: IPicBedType[] = [] picBed: IPicBedType[] = []
picBedName = '' picBedName = ''
menu: Electron.Menu | null= null
mounted () { mounted () {
ipcRenderer.on('uploadProgress', (event: IpcRendererEvent, progress: number) => { ipcRenderer.on('uploadProgress', (event: IpcRendererEvent, progress: number) => {
if (progress !== -1) { if (progress !== -1) {
@ -102,6 +100,7 @@ export default class extends Vue {
ipcRenderer.on('getPicBeds', this.getPicBeds) ipcRenderer.on('getPicBeds', this.getPicBeds)
this.$bus.$on(SHOW_INPUT_BOX_RESPONSE, this.handleInputBoxValue) this.$bus.$on(SHOW_INPUT_BOX_RESPONSE, this.handleInputBoxValue)
} }
@Watch('progress') @Watch('progress')
onProgressChange (val: number) { onProgressChange (val: number) {
if (val === 100) { if (val === 100) {
@ -114,12 +113,14 @@ export default class extends Vue {
}, 1200) }, 1200)
} }
} }
beforeDestroy () { beforeDestroy () {
this.$bus.$off(SHOW_INPUT_BOX_RESPONSE) this.$bus.$off(SHOW_INPUT_BOX_RESPONSE)
ipcRenderer.removeAllListeners('uploadProgress') ipcRenderer.removeAllListeners('uploadProgress')
ipcRenderer.removeAllListeners('syncPicBed') ipcRenderer.removeAllListeners('syncPicBed')
ipcRenderer.removeListener('getPicBeds', this.getPicBeds) ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
} }
onDrop (e: DragEvent) { onDrop (e: DragEvent) {
this.dragover = false this.dragover = false
const items = e.dataTransfer!.items const items = e.dataTransfer!.items
@ -136,6 +137,7 @@ export default class extends Vue {
this.ipcSendFiles(e.dataTransfer!.files) this.ipcSendFiles(e.dataTransfer!.files)
} }
} }
handleURLDrag (items: DataTransferItemList, dataTransfer: DataTransfer) { handleURLDrag (items: DataTransferItemList, dataTransfer: DataTransfer) {
// text/html // text/html
// Use this data to get a more precise URL // Use this data to get a more precise URL
@ -151,17 +153,20 @@ export default class extends Vue {
this.$message.error('请拖入合法的图片文件或者图片URL地址') this.$message.error('请拖入合法的图片文件或者图片URL地址')
} }
} }
openUplodWindow () { openUplodWindow () {
document.getElementById('file-uploader')!.click() document.getElementById('file-uploader')!.click()
} }
onChange (e: any) { onChange (e: any) {
this.ipcSendFiles(e.target.files); this.ipcSendFiles(e.target.files);
(document.getElementById('file-uploader') as HTMLInputElement).value = '' (document.getElementById('file-uploader') as HTMLInputElement).value = ''
} }
ipcSendFiles (files: FileList) { ipcSendFiles (files: FileList) {
let sendFiles: IFileWithPath[] = [] const sendFiles: IFileWithPath[] = []
Array.from(files).forEach((item, index) => { Array.from(files).forEach((item) => {
let obj = { const obj = {
name: item.name, name: item.name,
path: item.path path: item.path
} }
@ -169,17 +174,21 @@ export default class extends Vue {
}) })
ipcRenderer.send('uploadChoosedFiles', sendFiles) ipcRenderer.send('uploadChoosedFiles', sendFiles)
} }
async getPasteStyle () { async getPasteStyle () {
this.pasteStyle = await this.getConfig('settings.pasteStyle') || 'markdown' this.pasteStyle = await this.getConfig('settings.pasteStyle') || 'markdown'
} }
handlePasteStyleChange (val: string) { handlePasteStyleChange (val: string) {
this.saveConfig({ this.saveConfig({
'settings.pasteStyle': val 'settings.pasteStyle': val
}) })
} }
uploadClipboardFiles () { uploadClipboardFiles () {
ipcRenderer.send('uploadClipboardFilesFromUploadPage') ipcRenderer.send('uploadClipboardFilesFromUploadPage')
} }
async uploadURLFiles () { async uploadURLFiles () {
const str = await navigator.clipboard.readText() const str = await navigator.clipboard.readText()
this.$bus.$emit(SHOW_INPUT_BOX, { this.$bus.$emit(SHOW_INPUT_BOX, {
@ -188,6 +197,7 @@ export default class extends Vue {
placeholder: 'http://或者https://开头' placeholder: 'http://或者https://开头'
}) })
} }
handleInputBoxValue (val: string) { handleInputBoxValue (val: string) {
if (val === '') return false if (val === '') return false
if (isUrl(val)) { if (isUrl(val)) {
@ -198,6 +208,7 @@ export default class extends Vue {
this.$message.error('请输入合法的URL') this.$message.error('请输入合法的URL')
} }
} }
async getDefaultPicBed () { async getDefaultPicBed () {
const currentPicBed = await this.getConfig<string>('picBed.current') const currentPicBed = await this.getConfig<string>('picBed.current')
this.picBed.forEach(item => { this.picBed.forEach(item => {
@ -206,34 +217,14 @@ export default class extends Vue {
} }
}) })
} }
getPicBeds (event: Event, picBeds: IPicBedType[]) { getPicBeds (event: Event, picBeds: IPicBedType[]) {
this.picBed = picBeds this.picBed = picBeds
this.getDefaultPicBed() this.getDefaultPicBed()
} }
async handleChangePicBed () { async handleChangePicBed () {
await this.buildMenu() ipcRenderer.send(SHOW_UPLOAD_PAGE_MENU)
// this.menu.popup(remote.getCurrentWindow())
this.menu!.popup()
}
async buildMenu () {
const _this = this
const currentPicBed = await this.getConfig<string>('picBed.current')
const submenu = this.picBed.filter(item => item.visible).map(item => {
return {
label: item.name,
type: 'radio',
checked: currentPicBed === item.type,
click () {
_this.saveConfig({
'picBed.current': item.type,
'picBed.uploader': item.type
})
ipcRenderer.send('syncPicBed')
}
}
})
// @ts-ignore
this.menu = Menu.buildFromTemplate(submenu)
} }
} }
</script> </script>

View File

@ -86,12 +86,14 @@ export default class extends Vue {
customUrl: '', customUrl: '',
options: '' options: ''
} }
async created () { async created () {
const config = await this.getConfig<IAliYunConfig>('picBed.aliyun') const config = await this.getConfig<IAliYunConfig>('picBed.aliyun')
if (config) { if (config) {
this.form = Object.assign({}, config) this.form = Object.assign({}, config)
} }
} }
confirm () { confirm () {
// @ts-ignore // @ts-ignore
this.$refs.aliyun.validate((valid) => { this.$refs.aliyun.validate((valid) => {

View File

@ -71,12 +71,14 @@ export default class extends Vue {
customUrl: '', customUrl: '',
branch: '' branch: ''
} }
async created () { async created () {
const config = await this.getConfig<IGitHubConfig>('picBed.github') const config = await this.getConfig<IGitHubConfig>('picBed.github')
if (config) { if (config) {
this.form = Object.assign({}, config) this.form = Object.assign({}, config)
} }
} }
confirm () { confirm () {
// @ts-ignore // @ts-ignore
this.$refs.github.validate((valid) => { this.$refs.github.validate((valid) => {

View File

@ -48,12 +48,14 @@ export default class extends Vue {
clientId: '', clientId: '',
proxy: '' proxy: ''
} }
async created () { async created () {
const config = await this.getConfig<IImgurConfig>('picBed.imgur') const config = await this.getConfig<IImgurConfig>('picBed.imgur')
if (config) { if (config) {
this.form = Object.assign({}, config) this.form = Object.assign({}, config)
} }
} }
confirm () { confirm () {
// @ts-ignore // @ts-ignore
this.$refs.imgur.validate((valid) => { this.$refs.imgur.validate((valid) => {

View File

@ -52,6 +52,7 @@ export default class extends Vue {
ipcRenderer.send('getPicBedConfig', this.$route.params.type) ipcRenderer.send('getPicBedConfig', this.$route.params.type)
ipcRenderer.on('getPicBedConfig', this.getPicBeds) ipcRenderer.on('getPicBedConfig', this.getPicBeds)
} }
async handleConfirm () { async handleConfirm () {
// @ts-ignore // @ts-ignore
const result = await this.$refs.configForm.validate() const result = await this.$refs.configForm.validate()
@ -67,6 +68,7 @@ export default class extends Vue {
} }
} }
} }
setDefaultPicBed (type: string) { setDefaultPicBed (type: string) {
this.saveConfig({ this.saveConfig({
'picBed.current': type, 'picBed.current': type,
@ -81,10 +83,12 @@ export default class extends Vue {
return true return true
} }
} }
getPicBeds (event: IpcRendererEvent, config: any[], name: string) { getPicBeds (event: IpcRendererEvent, config: any[], name: string) {
this.config = config this.config = config
this.picBedName = name this.picBedName = name
} }
beforeDestroy () { beforeDestroy () {
ipcRenderer.removeListener('getPicBedConfig', this.getPicBeds) ipcRenderer.removeListener('getPicBedConfig', this.getPicBeds)
} }

View File

@ -88,12 +88,14 @@ export default class extends Vue {
options: '', options: '',
path: '' path: ''
} }
async created () { async created () {
const config = await this.getConfig<IQiniuConfig>('picBed.qiniu') const config = await this.getConfig<IQiniuConfig>('picBed.qiniu')
if (config) { if (config) {
this.form = Object.assign({}, config) this.form = Object.assign({}, config)
} }
} }
confirm () { confirm () {
// @ts-ignore // @ts-ignore
this.$refs.qiniu.validate((valid) => { this.$refs.qiniu.validate((valid) => {

View File

@ -41,12 +41,14 @@ export default class extends Vue {
form: ISMMSConfig = { form: ISMMSConfig = {
token: '' token: ''
} }
async created () { async created () {
const config = await this.getConfig<string | boolean>('picBed.smms.token') const config = await this.getConfig<string | boolean>('picBed.smms.token')
if (typeof config !== 'boolean') { if (typeof config !== 'boolean') {
this.form.token = config || '' this.form.token = config || ''
} }
} }
confirm () { confirm () {
// @ts-ignore // @ts-ignore
this.$refs.smms.validate((valid) => { this.$refs.smms.validate((valid) => {

View File

@ -86,9 +86,10 @@
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { ipcRenderer } from 'electron'
import { Component, Vue } from 'vue-property-decorator' import { Component, Vue } from 'vue-property-decorator'
import mixin from '@/utils/ConfirmButtonMixin' import mixin from '@/utils/ConfirmButtonMixin'
import { remote } from 'electron' import { OPEN_URL } from '#/events/constants'
@Component({ @Component({
name: 'tcyun', name: 'tcyun',
mixins: [mixin] mixins: [mixin]
@ -104,12 +105,14 @@ export default class extends Vue {
customUrl: '', customUrl: '',
version: 'v4' version: 'v4'
} }
async created () { async created () {
const config = await this.getConfig<ITcYunConfig>('picBed.tcyun') const config = await this.getConfig<ITcYunConfig>('picBed.tcyun')
if (config) { if (config) {
this.form = Object.assign({}, config) this.form = Object.assign({}, config)
} }
} }
confirm () { confirm () {
// @ts-ignore // @ts-ignore
this.$refs.tcyun.validate((valid) => { this.$refs.tcyun.validate((valid) => {
@ -128,8 +131,9 @@ export default class extends Vue {
} }
}) })
} }
openWiki () { openWiki () {
remote.shell.openExternal('https://github.com/Molunerfinn/PicGo/wiki/%E8%AF%A6%E7%BB%86%E7%AA%97%E5%8F%A3%E7%9A%84%E4%BD%BF%E7%94%A8#腾讯云cos') ipcRenderer.send(OPEN_URL, 'https://picgo.github.io/PicGo-Doc/zh/guide/config.html#%E8%85%BE%E8%AE%AF%E4%BA%91cos')
} }
} }
</script> </script>

View File

@ -79,12 +79,14 @@ export default class extends Vue {
options: '', options: '',
path: '' path: ''
} }
async created () { async created () {
const config = await this.getConfig<IUpYunConfig>('picBed.upyun') const config = await this.getConfig<IUpYunConfig>('picBed.upyun')
if (config) { if (config) {
this.form = Object.assign({}, config) this.form = Object.assign({}, config)
} }
} }
confirm () { confirm () {
// @ts-ignore // @ts-ignore
this.$refs.tcyun.validate((valid) => { this.$refs.tcyun.validate((valid) => {

View File

@ -1,5 +1,4 @@
import { Component, Vue } from 'vue-property-decorator' import { Component, Vue } from 'vue-property-decorator'
import { ipcRenderer } from 'electron'
import { IConfig } from 'picgo/dist/src/types' import { IConfig } from 'picgo/dist/src/types'
@Component @Component
export default class extends Vue { export default class extends Vue {
@ -10,6 +9,7 @@ export default class extends Vue {
this.defaultPicBed = config?.picBed?.uploader || config?.picBed?.current || 'smms' this.defaultPicBed = config?.picBed?.uploader || config?.picBed?.current || 'smms'
} }
} }
setDefaultPicBed (type: string) { setDefaultPicBed (type: string) {
this.saveConfig({ this.saveConfig({
'picBed.current': type, 'picBed.current': type,

View File

@ -15,26 +15,32 @@ export class GalleryDB implements IGalleryDB {
const res = await this.msgHandler<IGetResult<T>>(PICGO_GET_DB, filter) const res = await this.msgHandler<IGetResult<T>>(PICGO_GET_DB, filter)
return res return res
} }
async insert<T> (value: T): Promise<IResult<T>> { async insert<T> (value: T): Promise<IResult<T>> {
const res = await this.msgHandler<IResult<T>>(PICGO_INSERT_DB, value) const res = await this.msgHandler<IResult<T>>(PICGO_INSERT_DB, value)
return res return res
} }
async insertMany<T> (value: T[]): Promise<IResult<T>[]> { async insertMany<T> (value: T[]): Promise<IResult<T>[]> {
const res = await this.msgHandler<IResult<T>[]>(PICGO_INSERT_MANY_DB, value) const res = await this.msgHandler<IResult<T>[]>(PICGO_INSERT_MANY_DB, value)
return res return res
} }
async updateById (id: string, value: IObject): Promise<boolean> { async updateById (id: string, value: IObject): Promise<boolean> {
const res = await this.msgHandler<boolean>(PICGO_UPDATE_BY_ID_DB, id, value) const res = await this.msgHandler<boolean>(PICGO_UPDATE_BY_ID_DB, id, value)
return res return res
} }
async getById<T> (id: string): Promise<IResult<T> | undefined> { async getById<T> (id: string): Promise<IResult<T> | undefined> {
const res = await this.msgHandler<IResult<T> | undefined>(PICGO_GET_BY_ID_DB, id) const res = await this.msgHandler<IResult<T> | undefined>(PICGO_GET_BY_ID_DB, id)
return res return res
} }
async removeById (id: string): Promise<void> { async removeById (id: string): Promise<void> {
const res = await this.msgHandler<void>(PICGO_REMOVE_BY_ID_DB, id) const res = await this.msgHandler<void>(PICGO_REMOVE_BY_ID_DB, id)
return res return res
} }
private msgHandler<T> (method: string, ...args: any[]): Promise<T> { private msgHandler<T> (method: string, ...args: any[]): Promise<T> {
return new Promise((resolve) => { return new Promise((resolve) => {
const callbackId = uuid() const callbackId = uuid()

View File

@ -14,16 +14,16 @@ const isSpecialKey = (keyCode: number) => {
const keyDetect = (event: KeyboardEvent) => { const keyDetect = (event: KeyboardEvent) => {
const meta = process.platform === 'darwin' ? 'Cmd' : 'Super' const meta = process.platform === 'darwin' ? 'Cmd' : 'Super'
let specialKey = { const specialKey = {
Ctrl: event.ctrlKey, Ctrl: event.ctrlKey,
Shift: event.shiftKey, Shift: event.shiftKey,
Alt: event.altKey, Alt: event.altKey,
[meta]: event.metaKey [meta]: event.metaKey
} }
let pressKey = [] const pressKey = []
for (let i in specialKey) { for (const i in specialKey) {
if (specialKey[i]) { if (specialKey[i]) {
pressKey.push(i) pressKey.push(i)
} }

View File

@ -13,6 +13,7 @@ export default class extends Vue {
} }
ipcRenderer.send(PICGO_SAVE_CONFIG, config) ipcRenderer.send(PICGO_SAVE_CONFIG, config)
} }
getConfig<T> (key?: string): Promise<T | undefined> { getConfig<T> (key?: string): Promise<T | undefined> {
return new Promise((resolve) => { return new Promise((resolve) => {
const callbackId = uuid() const callbackId = uuid()

View File

@ -4,11 +4,13 @@ export default class extends Vue {
mounted () { mounted () {
this.disableDragEvent() this.disableDragEvent()
} }
disableDragEvent () { disableDragEvent () {
window.addEventListener('dragenter', this.disableDrag, false) window.addEventListener('dragenter', this.disableDrag, false)
window.addEventListener('dragover', this.disableDrag) window.addEventListener('dragover', this.disableDrag)
window.addEventListener('drop', this.disableDrag) window.addEventListener('drop', this.disableDrag)
} }
disableDrag (e: DragEvent) { disableDrag (e: DragEvent) {
const dropzone = document.getElementById('upload-area') const dropzone = document.getElementById('upload-area')
if (dropzone === null || !dropzone.contains(<Node>e.target)) { if (dropzone === null || !dropzone.contains(<Node>e.target)) {
@ -17,6 +19,7 @@ export default class extends Vue {
e.dataTransfer!.dropEffect = 'none' e.dataTransfer!.dropEffect = 'none'
} }
} }
beforeDestroy () { beforeDestroy () {
window.removeEventListener('dragenter', this.disableDrag, false) window.removeEventListener('dragenter', this.disableDrag, false)
window.removeEventListener('dragover', this.disableDrag) window.removeEventListener('dragover', this.disableDrag)

View File

@ -14,3 +14,16 @@ export const PICGO_GET_BY_ID_DB = 'PICGO_GET_BY_ID_DB'
export const PICGO_REMOVE_BY_ID_DB = 'PICGO_REMOVE_BY_ID_DB' export const PICGO_REMOVE_BY_ID_DB = 'PICGO_REMOVE_BY_ID_DB'
export const PICGO_OPEN_FILE = 'PICGO_OPEN_FILE' export const PICGO_OPEN_FILE = 'PICGO_OPEN_FILE'
export const OPEN_DEVTOOLS = 'OPEN_DEVTOOLS' export const OPEN_DEVTOOLS = 'OPEN_DEVTOOLS'
export const SHOW_MINI_PAGE_MENU = 'SHOW_MINI_PAGE_MENU'
export const SHOW_MAIN_PAGE_MENU = 'SHOW_MAIN_PAGE_MENU'
export const SHOW_UPLOAD_PAGE_MENU = 'SHOW_UPLOAD_PAGE_MENU'
export const SHOW_PLUGIN_PAGE_MENU = 'SHOW_PLUGIN_PAGE_MENU'
export const MINIMIZE_WINDOW = 'MINIMIZE_WINDOW'
export const CLOSE_WINDOW = 'CLOSE_WINDOW'
export const OPEN_USER_STORE_FILE = 'OPEN_USER_STORE_FILE'
export const OPEN_URL = 'OPEN_URL'
export const RELOAD_APP = 'RELOAD_APP'
export const PICGO_CONFIG_PLUGIN = 'PICGO_CONFIG_PLUGIN'
export const PICGO_HANDLE_PLUGIN_ING = 'PICGO_HANDLE_PLUGIN_ING'
export const PICGO_TOGGLE_PLUGIN = 'PICGO_TOGGLE_PLUGIN'
export const PASTE_TEXT = 'PASTE_TEXT'

View File

@ -23,11 +23,7 @@ declare interface IWindowManager {
// https://stackoverflow.com/questions/35074713/extending-typescript-global-object-in-node-js/44387594#44387594 // https://stackoverflow.com/questions/35074713/extending-typescript-global-object-in-node-js/44387594#44387594
declare global { declare global {
namespace NodeJS { var PICGO_GUI_VERSION: string
interface Global { var PICGO_CORE_VERSION: string
PICGO_GUI_VERSION: string var notificationList: IAppNotification[]
PICGO_CORE_VERSION: string
notificationList?: IAppNotification[]
}
}
} }

View File

@ -15,7 +15,7 @@ declare interface ErrnoException extends Error {
stack?: string; stack?: string;
} }
declare var __static: string declare let __static: string
declare type ILogType = 'success' | 'info' | 'warn' | 'error' declare type ILogType = 'success' | 'info' | 'warn' | 'error'
@ -94,6 +94,7 @@ interface IBrowserWindowOptions {
webPreferences: { webPreferences: {
nodeIntegration: boolean, nodeIntegration: boolean,
nodeIntegrationInWorker: boolean, nodeIntegrationInWorker: boolean,
contextIsolation: boolean,
backgroundThrottling: boolean backgroundThrottling: boolean
webSecurity?: boolean webSecurity?: boolean
}, },

View File

@ -1,6 +1,6 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "es2020", "target": "es2019", // https://github.com/TypeStrong/ts-loader/issues/1061
"module": "esnext", "module": "esnext",
"strict": true, "strict": true,
"jsx": "preserve", "jsx": "preserve",

View File

@ -23,6 +23,7 @@ const config = {
}, },
pluginOptions: { pluginOptions: {
electronBuilder: { electronBuilder: {
nodeIntegration: true, // will remove in the future
customFileProtocol: 'picgo://./', customFileProtocol: 'picgo://./',
externals: ['picgo'], externals: ['picgo'],
chainWebpackMainProcess: config => { chainWebpackMainProcess: config => {

11121
yarn.lock

File diff suppressed because it is too large Load Diff