Compare commits

..
25 Commits
Author SHA1 Message Date
PiEgg 459953f391 🎉 Release: v2.3.0 2021-09-11 11:29:50 +08:00
PiEgg 75e3edcd87 Feature: add open devtool option 2021-08-29 19:04:43 +08:00
PiEgg 5895889059 🐛 Fix: shift key function in gallery page 2021-08-28 21:50:46 +08:00
PiEgg 58420c8c3b 📝 Docs: update FAQ 2021-08-28 17:36:14 +08:00
PiEgg 6c6f84779a 🐛 Fix: urlEncode bug when copy
ISSUES CLOSED: #731
2021-08-23 23:21:58 +08:00
PiEgg a676c083fe 🐛 Fix: some bugs
ISSUES CLOSED: #722
2021-08-21 10:42:52 +08:00
PiEgg 2d3e779e0b 🎉 Release: v2.3.0-beta.8 2021-08-13 20:38:50 +08:00
PiEgg 20d3cf987c 🐛 Fix: settings bug
ISSUES CLOSED: #710
2021-08-13 00:19:43 +08:00
PiEgg ae692632a5 🐛 Fix: upload clipboard images via http should return list
ISSUES CLOSED: #721
2021-08-12 22:17:14 +08:00
PiEgg 019efd354c 🎉 Release: v2.3.0-beta.7 2021-08-01 17:04:21 +08:00
PiEgg 7030f7a764 Feature: finish custom config path
ISSUES CLOSED: #255
2021-08-01 17:02:54 +08:00
PiEgg 96a63ea11a 🐛 Fix: bug of gallery db for plugin 2021-08-01 15:44:59 +08:00
PiEgg f1eb7f4d70 🐛 Fix: gallery db bug 2021-08-01 14:50:25 +08:00
PiEgg 6ddd660d89 Feature: add gallery db 2021-07-27 11:58:24 +08:00
PiEgg c70c3aff78 🚧 WIP: add gallery db 2021-07-27 00:15:11 +08:00
PiEgg 76964ff1a5 🚧 WIP: gallery db in progress 2021-07-25 23:25:36 +08:00
PiEgg 12cecc27e7 🐛 Fix: multiple uploading in the same time will cause rename failed 2021-07-14 23:57:09 +08:00
PiEgg bdf523a060 🐛 Fix: windows ia32 && x64 build options 2021-07-14 23:46:33 +08:00
PiEgg 49e5f343f4 🐛 Fix: enable plugin should reload
ISSUES CLOSED: #659
2021-07-10 19:38:05 +08:00
PiEgg 1657542144 Feature: add win32 support
ISSUES CLOSED: #632
2021-07-10 00:36:31 +08:00
PiEgg 8e5e9ec59a 🔨 Refactor: move guiApi to singleton 2021-07-10 00:25:34 +08:00
PiEgg 06b67e50b9 🐛 Fix: multiple uploading in the same time will cause output conflict
ISSUES CLOSED: #666
2021-05-09 10:52:14 +08:00
PiEgg cf895deb8d 🔨 Refactor: picgo server the way of uploading 2021-05-07 01:04:17 +08:00
PiEgg ab762ef465 🐛 Fix: uploader error in linux
ISSUES CLOSED: #627
2021-04-25 23:29:30 +08:00
PiEgg 92022a6e34 🐛 Fix: use uploader first 2021-04-24 18:16:13 +08:00
58 changed files with 1714 additions and 862 deletions
+54
View File
@@ -1,3 +1,57 @@
# :tada: 2.3.0 (2021-09-11)
### :sparkles: Features
* add open devtool option ([75e3edc](https://github.com/Molunerfinn/PicGo/commit/75e3edc))
### :bug: Bug Fixes
* shift key function in gallery page ([5895889](https://github.com/Molunerfinn/PicGo/commit/5895889))
* some bugs ([a676c08](https://github.com/Molunerfinn/PicGo/commit/a676c08)), closes [#722](https://github.com/Molunerfinn/PicGo/issues/722)
* urlEncode bug when copy ([6c6f847](https://github.com/Molunerfinn/PicGo/commit/6c6f847)), closes [#731](https://github.com/Molunerfinn/PicGo/issues/731)
### :pencil: Documentation
* update FAQ ([58420c8](https://github.com/Molunerfinn/PicGo/commit/58420c8))
# :tada: 2.3.0-beta.8 (2021-08-13)
### :bug: Bug Fixes
* settings bug ([20d3cf9](https://github.com/Molunerfinn/PicGo/commit/20d3cf9)), closes [#710](https://github.com/Molunerfinn/PicGo/issues/710)
* upload clipboard images via http should return list ([ae69263](https://github.com/Molunerfinn/PicGo/commit/ae69263)), closes [#721](https://github.com/Molunerfinn/PicGo/issues/721)
# :tada: 2.3.0-beta.7 (2021-08-01)
### :sparkles: Features
* add gallery db ([6ddd660](https://github.com/Molunerfinn/PicGo/commit/6ddd660))
* add win32 support ([1657542](https://github.com/Molunerfinn/PicGo/commit/1657542)), closes [#632](https://github.com/Molunerfinn/PicGo/issues/632)
* finish custom config path ([7030f7a](https://github.com/Molunerfinn/PicGo/commit/7030f7a)), closes [#255](https://github.com/Molunerfinn/PicGo/issues/255)
### :bug: Bug Fixes
* bug of gallery db for plugin ([96a63ea](https://github.com/Molunerfinn/PicGo/commit/96a63ea))
* enable plugin should reload ([49e5f34](https://github.com/Molunerfinn/PicGo/commit/49e5f34)), closes [#659](https://github.com/Molunerfinn/PicGo/issues/659)
* gallery db bug ([f1eb7f4](https://github.com/Molunerfinn/PicGo/commit/f1eb7f4))
* multiple uploading in the same time will cause output conflict ([06b67e5](https://github.com/Molunerfinn/PicGo/commit/06b67e5)), closes [#666](https://github.com/Molunerfinn/PicGo/issues/666)
* multiple uploading in the same time will cause rename failed ([12cecc2](https://github.com/Molunerfinn/PicGo/commit/12cecc2))
* uploader error in linux ([ab762ef](https://github.com/Molunerfinn/PicGo/commit/ab762ef)), closes [#627](https://github.com/Molunerfinn/PicGo/issues/627)
* use uploader first ([92022a6](https://github.com/Molunerfinn/PicGo/commit/92022a6))
* windows ia32 && x64 build options ([bdf523a](https://github.com/Molunerfinn/PicGo/commit/bdf523a))
# :tada: 2.3.0-beta.6 (2021-04-24)
+4
View File
@@ -57,3 +57,7 @@ PicGo 在 Mac 上是一个顶部栏应用,在 dock 栏是不会有图标的。
1. 先自行搜索 error 里的报错信息,往往你能百度或者谷歌出问题原因,不必开 issue。
2. 如果有带有 `401``403``40X` 状态码字样的,不用怀疑,就是你配置写错了,仔细检查配置,看看是否多了空格之类的。
3. 如果带有 `HttpError``RequestError``socket hang up` 等字样的说明这是网络问题,我无法帮你解决网络问题,请检查你自己的网络,是否有代理,DNS 设置是否正常等。
## 10. macOS版本安装完之后没有主界面
请找到PicGo在顶部栏的图标,然后右键(触摸板双指点按,或者鼠标右键),即可找到「打开详细窗口」的菜单。
+2 -1
View File
@@ -26,6 +26,7 @@ install:
build_script:
#- yarn test
- npm run release
- yarn build --win --ia32
- yarn release
test: off
+4 -2
View File
@@ -1,6 +1,6 @@
{
"name": "picgo",
"version": "2.3.0-beta.6",
"version": "2.3.0",
"private": true,
"scripts": {
"dev": "vue-cli-service electron:serve",
@@ -34,6 +34,7 @@
]
},
"dependencies": {
"@picgo/store": "^1.0.3",
"axios": "^0.19.0",
"core-js": "^3.3.2",
"element-ui": "^2.13.0",
@@ -42,8 +43,9 @@
"keycode": "^2.2.0",
"lodash-id": "^0.14.0",
"lowdb": "^1.0.0",
"picgo": "^1.4.19",
"picgo": "^1.4.24",
"qrcode.vue": "^1.7.0",
"uuidv4": "^6.2.11",
"vue": "^2.6.10",
"vue-gallery": "^2.0.1",
"vue-lazyload": "^1.2.6",
+18
View File
@@ -0,0 +1,18 @@
#!/bin/sh
# grab the paths
scriptPath=$(echo $0 | awk '{ print substr( $0, 1, length($0)-6 ) }')"windows10.ps1"
imagePath=$(echo $1 | awk '{ print substr( $0, 1, length($0)-18 ) }')
imageName=$(echo $1 | awk '{ print substr( $0, length($0)-17, length($0) ) }')
# run the powershell script
res=$(powershell.exe -noprofile -noninteractive -nologo -sta -executionpolicy unrestricted -file $(wslpath -w $scriptPath) $(wslpath -w $imagePath)"\\"$imageName)
# note that there is a return symbol in powershell result
noImage=$(echo "no image\r")
# check whether image exists
if [ "$res" = "$noImage" ] ;then
echo "no image"
else
echo $(wslpath -u $res)
fi
+2 -3
View File
@@ -1,7 +1,6 @@
import Vue from 'vue'
import App from './renderer/App.vue'
import router from './renderer/router'
import db from '#/datastore/index'
import ElementUI from 'element-ui'
import { webFrame } from 'electron'
import 'element-ui/lib/theme-chalk/index.css'
@@ -10,6 +9,7 @@ import axios from 'axios'
import mainMixin from './renderer/utils/mainMixin'
import bus from '@/utils/bus'
import { initTalkingData } from './renderer/utils/analytics'
import db from './renderer/utils/db'
webFrame.setVisualZoomLevelLimits(1, 1)
webFrame.setLayoutZoomLevelLimits(0, 0)
@@ -17,7 +17,6 @@ webFrame.setLayoutZoomLevelLimits(0, 0)
Vue.config.productionTip = false
Vue.prototype.$builtInPicBed = [
'smms',
'weibo',
'imgur',
'qiniu',
'tcyun',
@@ -25,7 +24,7 @@ Vue.prototype.$builtInPicBed = [
'aliyun',
'github'
]
Vue.prototype.$db = db
Vue.prototype.$$db = db
Vue.prototype.$http = axios
Vue.prototype.$bus = bus
@@ -4,7 +4,7 @@ import {
} from 'electron'
import logger from '@core/picgo/logger'
import GuiApi from '../../gui'
import db from '#/datastore'
import db from '~/main/apis/core/datastore'
import { TOGGLE_SHORTKEY_MODIFIED_MODE } from '#/events/constants'
import shortKeyService from './shortKeyService'
import picgo from '@core/picgo'
@@ -130,8 +130,7 @@ class ShortKeyHandler {
} else if (command.includes('picgo-plugin-')) {
const handler = shortKeyService.getShortKeyHandler(command)
if (handler) {
const guiApi = new GuiApi()
return handler(picgo, guiApi)
return handler(picgo, GuiApi.getInstance())
}
} else {
logger.warn(`can not find command: ${command}`)
+5 -3
View File
@@ -9,7 +9,7 @@ import {
} from 'electron'
import uploader from 'apis/app/uploader'
import getPicBeds from '~/main/utils/getPicBeds'
import db from '#/datastore'
import db, { GalleryDB } from '~/main/apis/core/datastore'
import windowManager from 'apis/app/window/windowManager'
import { IWindowList } from 'apis/app/window/constants'
import picgo from '@core/picgo'
@@ -201,6 +201,8 @@ export function createTray () {
tray!.setImage(`${__static}/menubar.png`)
})
// drop-files only be supported in macOS
// so the tray window must be available
tray.on('drop-files', async (event: Event, files: string[]) => {
const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)!
@@ -210,7 +212,7 @@ export function createTray () {
if (imgs !== false) {
const pasteText: string[] = []
for (let i = 0; i < imgs.length; i++) {
pasteText.push(pasteTemplate(pasteStyle, imgs[i]))
pasteText.push(pasteTemplate(pasteStyle, imgs[i], db.get('settings.customLink')))
const notification = new Notification({
title: '上传成功',
body: imgs[i].imgUrl!,
@@ -219,7 +221,7 @@ export function createTray () {
setTimeout(() => {
notification.show()
}, i * 100)
db.insert('uploaded', imgs[i])
await GalleryDB.getInstance().insert(imgs[i])
}
handleCopyUrl(pasteText.join('\n'))
trayWindow.webContents.send('dragFiles', imgs)
+11 -9
View File
@@ -6,7 +6,7 @@ import windowManager from 'apis/app/window/windowManager'
import { IWindowList } from 'apis/app/window/constants'
import uploader from '.'
import pasteTemplate from '#/utils/pasteTemplate'
import db from '#/datastore'
import db, { GalleryDB } from '~/main/apis/core/datastore'
import { handleCopyUrl } from '~/main/utils/common'
import { handleUrlEncode } from '#/utils/common'
export const uploadClipboardFiles = async (): Promise<string> => {
@@ -14,18 +14,19 @@ export const uploadClipboardFiles = async (): Promise<string> => {
let img = await uploader.setWebContents(win!.webContents).upload()
if (img !== false) {
if (img.length > 0) {
const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)!
const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)
const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
handleCopyUrl(pasteTemplate(pasteStyle, img[0]))
handleCopyUrl(pasteTemplate(pasteStyle, img[0], db.get('settings.customLink')))
const notification = new Notification({
title: '上传成功',
body: img[0].imgUrl!,
icon: img[0].imgUrl
})
notification.show()
db.insert('uploaded', img[0])
trayWindow.webContents.send('clipboardFiles', [])
trayWindow.webContents.send('uploadFiles', img)
await GalleryDB.getInstance().insert(img[0])
// trayWindow just be created in mac/windows, not in linux
trayWindow?.webContents?.send('clipboardFiles', [])
trayWindow?.webContents?.send('uploadFiles', img)
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('updateGallery')
}
@@ -51,7 +52,7 @@ export const uploadChoosedFiles = async (webContents: WebContents, files: IFileW
const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
const pasteText: string[] = []
for (let i = 0; i < imgs.length; i++) {
pasteText.push(pasteTemplate(pasteStyle, imgs[i]))
pasteText.push(pasteTemplate(pasteStyle, imgs[i], db.get('settings.customLink')))
const notification = new Notification({
title: '上传成功',
body: imgs[i].imgUrl!,
@@ -60,11 +61,12 @@ export const uploadChoosedFiles = async (webContents: WebContents, files: IFileW
setTimeout(() => {
notification.show()
}, i * 100)
db.insert('uploaded', imgs[i])
await GalleryDB.getInstance().insert(imgs[i])
result.push(handleUrlEncode(imgs[i].imgUrl!))
}
handleCopyUrl(pasteText.join('\n'))
windowManager.get(IWindowList.TRAY_WINDOW)!.webContents.send('uploadFiles', imgs)
// trayWindow just be created in mac/windows, not in linux
windowManager.get(IWindowList.TRAY_WINDOW)?.webContents?.send('uploadFiles', imgs)
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('updateGallery')
}
+6 -16
View File
@@ -6,7 +6,7 @@ import {
} from 'electron'
import dayjs from 'dayjs'
import picgo from '@core/picgo'
import db from '#/datastore'
import db from '~/main/apis/core/datastore'
import windowManager from 'apis/app/window/windowManager'
import { IWindowList } from 'apis/app/window/constants'
import util from 'util'
@@ -54,7 +54,7 @@ const handleTalkingData = (webContents: WebContents, options: IAnalyticsData) =>
class Uploader {
private webContents: WebContents | null = null
private uploading: boolean = false
// private uploading: boolean = false
constructor () {
this.init()
}
@@ -86,7 +86,7 @@ class Uploader {
let name: undefined | string | null
let fileName: string | undefined
if (autoRename) {
fileName = dayjs().add(index, 'second').format('YYYYMMDDHHmmss') + item.extname
fileName = dayjs().add(index, 'ms').format('YYYYMMDDHHmmSSS') + item.extname
} else {
fileName = item.fileName
}
@@ -109,33 +109,23 @@ class Uploader {
}
async upload (img?: IUploadOption): Promise<ImgInfo[]|false> {
if (this.uploading) {
showNotification({
title: '上传失败',
body: '前序上传还在继续,请稍后再试'
})
return Promise.resolve(false)
}
try {
const startTime = Date.now()
this.uploading = true
const output = await picgo.upload(img)
this.uploading = false
if (Array.isArray(output) && output.every((item: ImgInfo) => item.imgUrl)) {
if (Array.isArray(output) && output.some((item: ImgInfo) => item.imgUrl)) {
if (this.webContents) {
handleTalkingData(this.webContents, {
fromClipboard: !img,
type: db.get('picBed.current') || 'smms',
type: db.get('picBed.uploader') || db.get('picBed.current') || 'smms',
count: img ? img.length : 1,
duration: Date.now() - startTime
} as IAnalyticsData)
}
return output
return output.filter(item => item.imgUrl)
} else {
return false
}
} catch (e) {
this.uploading = false
logger.error(e)
setTimeout(() => {
showNotification({
+1 -1
View File
@@ -8,7 +8,7 @@ import {
import { IWindowListItem } from '#/types/electron'
import bus from '@core/bus'
import { CREATE_APP_MENU } from '@core/bus/constants'
import db from '#/datastore'
import db from '~/main/apis/core/datastore'
import { TOGGLE_SHORTKEY_MODIFIED_MODE } from '#/events/constants'
import { app } from 'electron'
+137
View File
@@ -0,0 +1,137 @@
import fs from 'fs-extra'
import path from 'path'
import { remote, app } from 'electron'
import dayjs from 'dayjs'
import { getLogger } from '@core/utils/localLogger'
const APP = process.type === 'renderer' ? remote.app : app
const STORE_PATH = APP.getPath('userData')
const configFilePath = path.join(STORE_PATH, 'data.json')
const configFileBackupPath = path.join(STORE_PATH, 'data.bak.json')
export const defaultConfigPath = configFilePath
let _configFilePath = ''
let hasCheckPath = false
const errorMsg = {
broken: 'PicGo 配置文件损坏,已经恢复为默认配置',
brokenButBackup: 'PicGo 配置文件损坏,已经恢复为备份配置'
}
/** ensure notification list */
if (!global.notificationList) global.notificationList = []
function dbChecker () {
if (process.type !== 'renderer') {
// db save bak
try {
const { dbPath, dbBackupPath } = getGalleryDBPath()
if (fs.existsSync(dbPath)) {
fs.copyFileSync(dbPath, dbBackupPath)
}
} catch (e) {
console.error(e)
}
const configFilePath = dbPathChecker()
if (!fs.existsSync(configFilePath)) {
return
}
let configFile: string = '{}'
let optionsTpl = {
title: '注意',
body: ''
}
// config save bak
try {
configFile = fs.readFileSync(configFilePath, { encoding: 'utf-8' })
JSON.parse(configFile)
} catch (e) {
fs.unlinkSync(configFilePath)
if (fs.existsSync(configFileBackupPath)) {
try {
configFile = fs.readFileSync(configFileBackupPath, { encoding: 'utf-8' })
JSON.parse(configFile)
fs.writeFileSync(configFilePath, configFile, { encoding: 'utf-8' })
const stats = fs.statSync(configFileBackupPath)
optionsTpl.body = `${errorMsg.brokenButBackup}\n备份文件版本:${dayjs(stats.mtime).format('YYYY-MM-DD HH:mm:ss')}`
global.notificationList?.push(optionsTpl)
return
} catch (e) {
optionsTpl.body = errorMsg.broken
global.notificationList?.push(optionsTpl)
return
}
}
optionsTpl.body = errorMsg.broken
global.notificationList?.push(optionsTpl)
return
}
fs.writeFileSync(configFileBackupPath, configFile, { encoding: 'utf-8' })
}
}
/**
* Get config path
*/
function dbPathChecker (): string {
if (_configFilePath) {
return _configFilePath
}
// defaultConfigPath
_configFilePath = defaultConfigPath
// if defaultConfig path is not exit
// do not parse the content of config
if (!fs.existsSync(defaultConfigPath)) {
return _configFilePath
}
try {
const configString = fs.readFileSync(defaultConfigPath, { encoding: 'utf-8' })
const config = JSON.parse(configString)
const userConfigPath: string = config.configPath || ''
if (userConfigPath) {
if (fs.existsSync(userConfigPath) && userConfigPath.endsWith('.json')) {
_configFilePath = userConfigPath
return _configFilePath
}
}
return _configFilePath
} catch (e) {
const picgoLogPath = path.join(defaultConfigPath, 'picgo.log')
const logger = getLogger(picgoLogPath)
if (!hasCheckPath) {
let optionsTpl = {
title: '注意',
body: '自定义文件解析出错,请检查路径内容是否正确'
}
global.notificationList?.push(optionsTpl)
hasCheckPath = true
}
logger('error', e)
console.error(e)
_configFilePath = defaultConfigPath
return _configFilePath
}
}
function dbPathDir () {
return path.dirname(dbPathChecker())
}
function getGalleryDBPath (): {
dbPath: string
dbBackupPath: string
} {
const configPath = dbPathChecker()
const dbPath = path.join(path.dirname(configPath), 'picgo.db')
const dbBackupPath = path.join(path.dirname(dbPath), 'picgo.bak.db')
return {
dbPath,
dbBackupPath
}
}
export {
dbChecker,
dbPathChecker,
dbPathDir,
getGalleryDBPath
}
@@ -2,33 +2,27 @@ import Datastore from 'lowdb'
// @ts-ignore
import LodashId from 'lodash-id'
import FileSync from 'lowdb/adapters/FileSync'
import path from 'path'
import fs from 'fs-extra'
import { remote, app } from 'electron'
import { dbChecker } from './dbChecker'
import { dbPathChecker, dbPathDir, getGalleryDBPath } from './dbChecker'
import { DBStore } from '@picgo/store'
const APP = process.type === 'renderer' ? remote.app : app
const STORE_PATH = APP.getPath('userData')
const STORE_PATH = dbPathDir()
if (process.type !== 'renderer') {
if (!fs.pathExistsSync(STORE_PATH)) {
fs.mkdirpSync(STORE_PATH)
}
dbChecker()
if (!fs.pathExistsSync(STORE_PATH)) {
fs.mkdirpSync(STORE_PATH)
}
const CONFIG_PATH: string = dbPathChecker()
const DB_PATH: string = getGalleryDBPath().dbPath
class DB {
// TODO: use JSONStore with @picgo/store
class ConfigStore {
private db: Datastore.LowdbSync<Datastore.AdapterSync>
constructor () {
const adapter = new FileSync(path.join(STORE_PATH, '/data.json'))
const adapter = new FileSync(CONFIG_PATH)
this.db = Datastore(adapter)
this.db._.mixin(LodashId)
if (!this.db.has('uploaded').value()) {
this.db.set('uploaded', []).write()
}
if (!this.db.has('picBed').value()) {
this.db.set('picBed', {
current: 'smms', // deprecated
@@ -75,6 +69,27 @@ class DB {
// @ts-ignore
return this.read().get(key).removeById(id).write()
}
getConfigPath () {
return CONFIG_PATH
}
}
export default new DB()
export default new ConfigStore()
// v2.3.0 add gallery db
class GalleryDB {
private static instance: DBStore
private constructor () {
console.log('init gallery db')
}
public static getInstance (): DBStore {
if (!GalleryDB.instance) {
GalleryDB.instance = new DBStore(DB_PATH, 'gallery')
}
return GalleryDB.instance
}
}
export {
GalleryDB
}
+6 -7
View File
@@ -1,14 +1,13 @@
import PicGoCore from '~/universal/types/picgo'
import {
app
} from 'electron'
import path from 'path'
import { dbChecker, dbPathChecker } from 'apis/core/datastore/dbChecker'
import pkg from 'root/package.json'
// eslint-disable-next-line
const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require
const PicGo = requireFunc('picgo') as typeof PicGoCore
const STORE_PATH = app.getPath('userData')
const CONFIG_PATH = path.join(STORE_PATH, '/data.json')
const CONFIG_PATH = dbPathChecker()
dbChecker()
const picgo = new PicGo(CONFIG_PATH)
picgo.saveConfig({
@@ -16,7 +15,7 @@ picgo.saveConfig({
PICGO_ENV: 'GUI'
})
// @ts-ignore
global.PICGO_GUI_VERSION = pkg.version
picgo.GUI_VERSION = global.PICGO_GUI_VERSION
export default picgo! as PicGoCore
+32
View File
@@ -0,0 +1,32 @@
import fs from 'fs-extra'
import dayjs from 'dayjs'
import util from 'util'
/**
* for local log before picgo inited
*/
const getLogger = (logPath: string) => {
if (!fs.existsSync(logPath)) {
fs.ensureFileSync(logPath)
}
return (type: string, ...msg: any[]) => {
let log = `${dayjs().format('YYYY-MM-DD HH:mm:ss')} [PicGo ${type.toUpperCase()}] `
msg.forEach((item: ILogArgvTypeWithError) => {
if (typeof item === 'object' && type === 'error') {
log += `\n------Error Stack Begin------\n${util.format(item.stack)}\n-------Error Stack End------- `
} else {
if (typeof item === 'object') {
item = JSON.stringify(item)
}
log += `${item} `
}
})
log += '\n'
// A synchronized approach to avoid log msg sequence errors
fs.appendFileSync(logPath, log)
}
}
export {
getLogger
}
+59 -3
View File
@@ -4,7 +4,9 @@ import {
Notification,
ipcMain
} from 'electron'
import db from '#/datastore'
import path from 'path'
import db, { GalleryDB } from 'apis/core/datastore'
import { dbPathChecker, defaultConfigPath, getGalleryDBPath } from 'apis/core/datastore/dbChecker'
import uploader from 'apis/app/uploader'
import pasteTemplate from '#/utils/pasteTemplate'
import { handleCopyUrl } from '~/main/utils/common'
@@ -15,11 +17,23 @@ import {
import {
SHOW_INPUT_BOX
} from '~/universal/events/constants'
import { DBStore } from '@picgo/store'
type PromiseResType<T> = T extends Promise<infer R> ? R : T
// Cross-process support may be required in the future
class GuiApi implements IGuiApi {
private static instance: GuiApi
private windowId: number = -1
private settingWindowId: number = -1
private constructor () {
console.log('init guiapi')
}
public static getInstance (): GuiApi {
if (!GuiApi.instance) {
GuiApi.instance = new GuiApi()
}
return GuiApi.instance
}
private async showSettingWindow () {
this.settingWindowId = await getSettingWindowId()
const settingWindow = BrowserWindow.fromId(this.settingWindowId)
@@ -69,7 +83,7 @@ class GuiApi implements IGuiApi {
const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
const pasteText: string[] = []
for (let i = 0; i < imgs.length; i++) {
pasteText.push(pasteTemplate(pasteStyle, imgs[i]))
pasteText.push(pasteTemplate(pasteStyle, imgs[i], db.get('settings.customLink')))
const notification = new Notification({
title: '上传成功',
body: imgs[i].imgUrl as string,
@@ -78,7 +92,7 @@ class GuiApi implements IGuiApi {
setTimeout(() => {
notification.show()
}, i * 100)
db.insert('uploaded', imgs[i])
await GalleryDB.getInstance().insert(imgs[i])
}
handleCopyUrl(pasteText.join('\n'))
webContents.send('uploadFiles', imgs)
@@ -118,6 +132,48 @@ class GuiApi implements IGuiApi {
})
})
}
/**
* get picgo config/data path
*/
async getConfigPath () {
const currentConfigPath = dbPathChecker()
const galleryDBPath = getGalleryDBPath().dbPath
return {
defaultConfigPath,
currentConfigPath,
galleryDBPath
}
}
get galleryDB (): DBStore {
return new Proxy<DBStore>(GalleryDB.getInstance(), {
get (target, prop: keyof DBStore) {
if (prop === 'removeById') {
return new Proxy(GalleryDB.getInstance().removeById, {
apply (target, ctx, args) {
return new Promise((resolve) => {
const guiApi = GuiApi.getInstance()
guiApi.showMessageBox({
title: '警告',
message: '有插件正在试图删除一些相册图片,是否继续',
type: 'info',
buttons: ['Yes', 'No']
}).then(res => {
if (res.result === 0) {
resolve(Reflect.apply(target, ctx, args))
} else {
resolve(undefined)
}
})
})
}
})
}
return Reflect.get(target, prop)
}
})
}
}
export default GuiApi
+8 -4
View File
@@ -8,13 +8,14 @@ import windowManager from 'apis/app/window/windowManager'
import { IWindowList } from 'apis/app/window/constants'
import uploader from 'apis/app/uploader'
import pasteTemplate from '#/utils/pasteTemplate'
import db from '#/datastore'
import db, { GalleryDB } from '~/main/apis/core/datastore'
import server from '~/main/server'
import getPicBeds from '~/main/utils/getPicBeds'
import shortKeyHandler from 'apis/app/shortKey/shortKeyHandler'
import bus from '@core/bus'
import {
TOGGLE_SHORTKEY_MODIFIED_MODE
TOGGLE_SHORTKEY_MODIFIED_MODE,
OPEN_DEVTOOLS
} from '#/events/constants'
import {
uploadClipboardFiles,
@@ -32,7 +33,7 @@ export default {
const img = await uploader.setWebContents(trayWindow.webContents).upload()
if (img !== false) {
const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
handleCopyUrl(pasteTemplate(pasteStyle, img[0]))
handleCopyUrl(pasteTemplate(pasteStyle, img[0], db.get('settings.customLink')))
const notification = new Notification({
title: '上传成功',
body: img[0].imgUrl!,
@@ -40,7 +41,7 @@ export default {
icon: img[0].imgUrl
})
notification.show()
db.insert('uploaded', img[0])
await GalleryDB.getInstance().insert(img[0])
trayWindow.webContents.send('clipboardFiles', [])
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('updateGallery')
@@ -141,6 +142,9 @@ export default {
ipcMain.on('updateServer', () => {
server.restart()
})
ipcMain.on(OPEN_DEVTOOLS, (event: IpcMainEvent) => {
event.sender.openDevTools()
})
},
dispose () {}
}
+77 -10
View File
@@ -4,8 +4,7 @@ import {
dialog,
shell,
IpcMainEvent,
ipcMain,
app
ipcMain
} from 'electron'
import PicGoCore from '~/universal/types/picgo'
import { IPicGoHelperType } from '#/types/enum'
@@ -16,11 +15,26 @@ import { IGuiMenuItem } from 'picgo/dist/src/types'
import windowManager from 'apis/app/window/windowManager'
import { IWindowList } from 'apis/app/window/constants'
import { showNotification } from '~/main/utils/common'
import { dbPathChecker } from 'apis/core/datastore/dbChecker'
import {
PICGO_SAVE_CONFIG,
PICGO_GET_CONFIG,
PICGO_GET_DB,
PICGO_INSERT_DB,
PICGO_INSERT_MANY_DB,
PICGO_UPDATE_BY_ID_DB,
PICGO_GET_BY_ID_DB,
PICGO_REMOVE_BY_ID_DB,
PICGO_OPEN_FILE
} from '#/events/constants'
import { GalleryDB } from 'apis/core/datastore'
import { IObject, IFilter } from '@picgo/store/dist/types'
// eslint-disable-next-line
const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require
// const PluginHandler = requireFunc('picgo/dist/lib/PluginHandler').default
const STORE_PATH = app.getPath('userData')
const STORE_PATH = path.dirname(dbPathChecker())
// const CONFIG_PATH = path.join(STORE_PATH, '/data.json')
type PicGoNotice = {
@@ -210,12 +224,11 @@ const handleGetPicBedConfig = () => {
const handlePluginActions = () => {
ipcMain.on('pluginActions', (event: IpcMainEvent, name: string, label: string) => {
const plugin = picgo.pluginLoader.getPlugin(name)
const guiApi = new GuiApi()
if (plugin?.guiMenu?.(picgo)?.length) {
const menu: GuiMenuItem[] = plugin.guiMenu(picgo)
menu.forEach(item => {
if (item.label === label) {
item.handle(picgo, guiApi)
item.handle(picgo, GuiApi.getInstance())
}
})
}
@@ -224,19 +237,25 @@ const handlePluginActions = () => {
const handleRemoveFiles = () => {
ipcMain.on('removeFiles', (event: IpcMainEvent, files: ImgInfo[]) => {
const guiApi = new GuiApi()
setTimeout(() => {
picgo.emit('remove', files, guiApi)
picgo.emit('remove', files, GuiApi.getInstance())
}, 500)
})
}
const handlePicGoSaveData = () => {
ipcMain.on('picgoSaveData', (event: IpcMainEvent, data: IObj) => {
const handlePicGoSaveConfig = () => {
ipcMain.on(PICGO_SAVE_CONFIG, (event: IpcMainEvent, data: IObj) => {
picgo.saveConfig(data)
})
}
const handlePicGoGetConfig = () => {
ipcMain.on(PICGO_GET_CONFIG, (event: IpcMainEvent, key: string | undefined, callbackId: string) => {
const result = picgo.getConfig(key)
event.sender.send(PICGO_GET_CONFIG, result, callbackId)
})
}
const handleImportLocalPlugin = () => {
ipcMain.on('importLocalPlugin', (event: IpcMainEvent) => {
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)!
@@ -264,6 +283,51 @@ const handleImportLocalPlugin = () => {
})
}
const handlePicGoGalleryDB = () => {
ipcMain.on(PICGO_GET_DB, async (event: IpcMainEvent, filter: IFilter, callbackId: string) => {
const dbStore = GalleryDB.getInstance()
const res = await dbStore.get(filter)
event.sender.send(PICGO_GET_DB, res, callbackId)
})
ipcMain.on(PICGO_INSERT_DB, async (event: IpcMainEvent, value: IObject, callbackId: string) => {
const dbStore = GalleryDB.getInstance()
const res = await dbStore.insert(value)
event.sender.send(PICGO_INSERT_DB, res, callbackId)
})
ipcMain.on(PICGO_INSERT_MANY_DB, async (event: IpcMainEvent, value: IObject[], callbackId: string) => {
const dbStore = GalleryDB.getInstance()
const res = await dbStore.insertMany(value)
event.sender.send(PICGO_INSERT_MANY_DB, res, callbackId)
})
ipcMain.on(PICGO_UPDATE_BY_ID_DB, async (event: IpcMainEvent, id: string, value: IObject[], callbackId: string) => {
const dbStore = GalleryDB.getInstance()
const res = await dbStore.updateById(id, value)
event.sender.send(PICGO_UPDATE_BY_ID_DB, res, callbackId)
})
ipcMain.on(PICGO_GET_BY_ID_DB, async (event: IpcMainEvent, id: string, callbackId: string) => {
const dbStore = GalleryDB.getInstance()
const res = await dbStore.getById(id)
event.sender.send(PICGO_GET_BY_ID_DB, res, callbackId)
})
ipcMain.on(PICGO_REMOVE_BY_ID_DB, async (event: IpcMainEvent, id: string, callbackId: string) => {
const dbStore = GalleryDB.getInstance()
const res = await dbStore.removeById(id)
event.sender.send(PICGO_REMOVE_BY_ID_DB, res, callbackId)
})
}
const handleOpenFile = () => {
ipcMain.on(PICGO_OPEN_FILE, (event: IpcMainEvent, fileName: string) => {
const abFilePath = path.join(STORE_PATH, fileName)
shell.openItem(abFilePath)
})
}
export default {
listen () {
handleGetPluginList()
@@ -273,7 +337,10 @@ export default {
handleGetPicBedConfig()
handlePluginActions()
handleRemoveFiles()
handlePicGoSaveData()
handlePicGoSaveConfig()
handlePicGoGetConfig()
handlePicGoGalleryDB()
handleImportLocalPlugin()
handleOpenFile()
}
}
+3 -3
View File
@@ -1,14 +1,14 @@
import { app } from 'electron'
import fse from 'fs-extra'
import path from 'path'
import dayjs from 'dayjs'
import util from 'util'
const STORE_PATH = app.getPath('userData')
import { dbPathDir } from 'apis/core/datastore/dbChecker'
const STORE_PATH = dbPathDir()
const LOG_PATH = path.join(STORE_PATH, '/picgo.log')
// since the error may occur in picgo-core
// so we can't use the log from picgo
const loggerWriter = (error: Error) => {
export const loggerWriter = (error: Error) => {
let log = `${dayjs().format('YYYY-MM-DD HH:mm:ss')} [PicGo ERROR] startup error`
if (error?.stack) {
log += `\n------Error Stack Begin------\n${util.format(error.stack)}\n-------Error Stack End-------\n`
+18 -9
View File
@@ -16,7 +16,8 @@ import busEventList from '~/main/events/busEventList'
import { IWindowList } from 'apis/app/window/constants'
import windowManager from 'apis/app/window/windowManager'
import {
updateShortKeyFromVersion212
updateShortKeyFromVersion212,
migrateGalleryFromVersion230
} from '~/main/migrate'
import {
uploadChoosedFiles,
@@ -29,10 +30,11 @@ import server from '~/main/server/index'
import updateChecker from '~/main/utils/updateChecker'
import shortKeyHandler from 'apis/app/shortKey/shortKeyHandler'
import { getUploadFiles } from '~/main/utils/handleArgv'
import db from '#/datastore'
import db, { GalleryDB } from '~/main/apis/core/datastore'
import bus from '@core/bus'
import { privacyManager } from '~/main/utils/privacyManager'
import logger from 'apis/core/picgo/logger'
import picgo from 'apis/core/picgo'
const isDevelopment = process.env.NODE_ENV !== 'production'
@@ -54,7 +56,7 @@ const handleStartUpFiles = (argv: string[], cwd: string) => {
}
class LifeCycle {
private beforeReady () {
private async beforeReady () {
protocol.registerSchemesAsPrivileged([{ scheme: 'picgo', privileges: { secure: true, standard: true } }])
// fix the $PATH in macOS
fixPath()
@@ -62,9 +64,11 @@ class LifeCycle {
ipcList.listen()
busEventList.listen()
updateShortKeyFromVersion212(db, db.get('settings.shortKey'))
await migrateGalleryFromVersion230(db, GalleryDB.getInstance(), picgo)
}
private onReady () {
app.on('ready', async () => {
const readyFunction = async () => {
console.log('on ready')
createProtocol('picgo')
if (isDevelopment && !process.env.IS_TEST) {
// Install Vue Devtools
@@ -92,14 +96,19 @@ class LifeCycle {
handleStartUpFiles(process.argv, process.cwd())
}
if (global.notificationList?.length > 0) {
while (global.notificationList.length) {
if (global.notificationList && global.notificationList?.length > 0) {
while (global.notificationList?.length) {
const option = global.notificationList.pop()
const notice = new Notification(option!)
notice.show()
}
}
})
}
if (!app.isReady()) {
app.on('ready', readyFunction)
} else {
readyFunction()
}
}
private onRunning () {
app.on('second-instance', (event, commandLine, workingDirectory) => {
@@ -162,12 +171,12 @@ class LifeCycle {
}
}
}
launchApp () {
async launchApp () {
const gotTheLock = app.requestSingleInstanceLock()
if (!gotTheLock) {
app.quit()
} else {
this.beforeReady()
await this.beforeReady()
this.onReady()
this.onRunning()
this.onQuit()
+24 -3
View File
@@ -1,6 +1,10 @@
import DB from '#/datastore'
import { DBStore } from '@picgo/store'
import ConfigStore from '~/main/apis/core/datastore'
import path from 'path'
import fse from 'fs-extra'
import PicGoCore from '#/types/picgo'
// from v2.1.2
const updateShortKeyFromVersion212 = (db: typeof DB, shortKeyConfig: IShortKeyConfigs | IOldShortKeyConfigs) => {
const updateShortKeyFromVersion212 = (db: typeof ConfigStore, shortKeyConfig: IShortKeyConfigs | IOldShortKeyConfigs) => {
// #557 极端情况可能会出现配置不存在,需要重新写入
if (shortKeyConfig === undefined) {
const defaultShortKeyConfig = {
@@ -28,6 +32,23 @@ const updateShortKeyFromVersion212 = (db: typeof DB, shortKeyConfig: IShortKeyCo
return false
}
const migrateGalleryFromVersion230 = async (configDB: typeof ConfigStore, galleryDB: DBStore, picgo: PicGoCore) => {
const originGallery: ImgInfo[] = configDB.get('uploaded')
const configPath = configDB.getConfigPath()
const configBakPath = path.join(path.dirname(configPath), 'config.bak.json')
// migrate gallery from config to gallery db
if (originGallery && Array.isArray(originGallery) && originGallery?.length > 0) {
if (fse.existsSync(configBakPath)) {
fse.copyFileSync(configPath, configBakPath)
}
await galleryDB.insertMany(originGallery)
picgo.saveConfig({
uploaded: []
})
}
}
export {
updateShortKeyFromVersion212
updateShortKeyFromVersion212,
migrateGalleryFromVersion230
}
+2 -1
View File
@@ -67,7 +67,7 @@ class Server {
}
})
}
logger.info(`[PicGo Server] get the request`)
logger.info(`[PicGo Server] get the request`, body)
const handler = routers.getHandler(request.url!)
handler!({
...postObj,
@@ -103,6 +103,7 @@ class Server {
})
}
startup () {
console.log('startup', this.config.enable)
if (this.config.enable) {
this.listen(this.config.port)
}
+18 -13
View File
@@ -1,12 +1,16 @@
import router from './router'
import {
uploadWithClipboardFiles,
uploadWithFiles
} from '@core/bus/apis'
import {
handleResponse
} from './utils'
import logger from '@core/picgo/logger'
import windowManager from 'apis/app/window/windowManager'
import { uploadChoosedFiles, uploadClipboardFiles } from 'apis/app/uploader/apis'
import path from 'path'
import { dbPathDir } from 'apis/core/datastore/dbChecker'
const STORE_PATH = dbPathDir()
const LOG_PATH = path.join(STORE_PATH, 'picgo.log')
const errorMessage = `upload error. see ${LOG_PATH} for more detail.`
router.post('/upload', async ({
response,
@@ -19,13 +23,13 @@ router.post('/upload', async ({
if (list.length === 0) {
// upload with clipboard
logger.info('[PicGo Server] upload clipboard file')
const res = await uploadWithClipboardFiles()
if (res.success) {
const res = await uploadClipboardFiles()
if (res) {
handleResponse({
response,
body: {
success: true,
result: res.result
result: [res]
}
})
} else {
@@ -33,7 +37,7 @@ router.post('/upload', async ({
response,
body: {
success: false,
message: 'upload error'
message: errorMessage
}
})
}
@@ -45,13 +49,14 @@ router.post('/upload', async ({
path: item
}
})
const res = await uploadWithFiles(pathList)
if (res.success) {
const win = windowManager.getAvailableWindow()
const res = await uploadChoosedFiles(win.webContents, pathList)
if (res.length) {
handleResponse({
response,
body: {
success: true,
result: res.result
result: res
}
})
} else {
@@ -59,7 +64,7 @@ router.post('/upload', async ({
response,
body: {
success: false,
message: 'upload error'
message: errorMessage
}
})
}
@@ -70,7 +75,7 @@ router.post('/upload', async ({
response,
body: {
success: false,
message: err
message: errorMessage
}
})
}
+15 -16
View File
@@ -1,19 +1,12 @@
import fs from 'fs-extra'
import path from 'path'
import os from 'os'
import { remote, app } from 'electron'
import pkg from 'root/package.json'
import { dbPathChecker } from 'apis/core/datastore/dbChecker'
const APP = process.type === 'renderer' ? remote.app : app
const STORE_PATH = APP.getPath('userData')
function injectPicGoVersion () {
global.PICGO_GUI_VERSION = pkg.version
global.PICGO_CORE_VERSION = pkg.dependencies.picgo.replace('^', '')
}
const configPath = dbPathChecker()
const CONFIG_DIR = path.dirname(configPath)
function beforeOpen () {
injectPicGoVersion()
if (process.platform === 'darwin') {
resolveMacWorkFlow()
}
@@ -41,7 +34,7 @@ function resolveMacWorkFlow () {
*/
function resolveClipboardImageGenerator () {
let clipboardFiles = getClipboardFiles()
if (!fs.pathExistsSync(path.join(STORE_PATH, 'windows10.ps1'))) {
if (!fs.pathExistsSync(path.join(CONFIG_DIR, 'windows10.ps1'))) {
clipboardFiles.forEach(item => {
fs.copyFileSync(item.origin, item.dest)
})
@@ -52,10 +45,15 @@ function resolveClipboardImageGenerator () {
}
function diffFilesAndUpdate (filePath1: string, filePath2: string) {
let file1 = fs.readFileSync(filePath1)
let file2 = fs.readFileSync(filePath2)
try {
let file1 = fs.existsSync(filePath1) && fs.readFileSync(filePath1)
let file2 = fs.existsSync(filePath1) && fs.readFileSync(filePath2)
if (!file1.equals(file2)) {
if (!file1 || !file2 || !file1.equals(file2)) {
fs.copyFileSync(filePath1, filePath2)
}
} catch (e) {
console.error(e)
fs.copyFileSync(filePath1, filePath2)
}
}
@@ -65,13 +63,14 @@ function resolveClipboardImageGenerator () {
'/linux.sh',
'/mac.applescript',
'/windows.ps1',
'/windows10.ps1'
'/windows10.ps1',
'/wsl.sh'
]
return files.map(item => {
return {
origin: path.join(__static, item),
dest: path.join(STORE_PATH, item)
dest: path.join(CONFIG_DIR, item)
}
})
}
+1 -1
View File
@@ -1,4 +1,4 @@
import db from '#/datastore'
import db from '~/main/apis/core/datastore'
import { clipboard, Notification, dialog } from 'electron'
export const handleCopyUrl = (str: string): void => {
+1 -1
View File
@@ -1,4 +1,4 @@
import db from '#/datastore'
import db from '~/main/apis/core/datastore'
import { ipcMain } from 'electron'
import { showMessageBox } from '~/main/utils/common'
import { SHOW_PRIVACY_MESSAGE } from '~/universal/events/constants'
+1 -1
View File
@@ -1,5 +1,5 @@
import { dialog, shell } from 'electron'
import db from '#/datastore'
import db from '~/main/apis/core/datastore'
import axios from 'axios'
import pkg from 'root/package.json'
import { lt } from 'semver'
+19 -23
View File
@@ -8,31 +8,27 @@
</el-switch>
</div>
</template>
<script>
export default {
name: 'choose-pic-bed',
props: {
type: String,
label: String
},
data () {
return {
value: false
}
},
created () {
if (this.type === this.$db.get('picBed.current')) {
<script lang="ts">
import { Component, Vue, Prop } from 'vue-property-decorator'
@Component({
name: 'choose-pic-bed'
})
export default class extends Vue {
value = false
@Prop() type!: string
@Prop() label!: string
async created () {
const current = await this.getConfig<string>('picBed.current')
if (this.type === current) {
this.value = true
}
},
methods: {
choosePicBed (val) {
this.letPicGoSaveData({
'picBed.current': this.type,
'picBed.uploader': this.type
})
this.$emit('update:choosed', this.type)
}
}
choosePicBed (val: string) {
this.saveConfig({
'picBed.current': this.type,
'picBed.uploader': this.type
})
this.$emit('update:choosed', this.type)
}
}
</script>
+35 -15
View File
@@ -72,7 +72,7 @@ import { cloneDeep, union } from 'lodash'
})
export default class extends Vue {
@Prop() private config!: any[]
@Prop() readonly type!: string
@Prop() readonly type!: 'uploader' | 'transformer' | 'plugin'
@Prop() readonly id!: string
configList = []
ruleForm = {}
@@ -81,8 +81,41 @@ export default class extends Vue {
immediate: true
})
handleConfigChange (val: any) {
this.handleConfig(val)
}
async validate () {
return new Promise((resolve, reject) => {
// @ts-ignore
this.$refs.form.validate((valid: boolean) => {
if (valid) {
resolve(this.ruleForm)
} else {
resolve(false)
return false
}
})
})
}
getConfigType () {
switch (this.type) {
case 'plugin': {
return this.id
}
case 'uploader': {
return `picBed.${this.id}`
}
case 'transformer': {
return `transformer.${this.id}`
}
default:
return `unknown`
}
}
async handleConfig (val: any) {
this.ruleForm = Object.assign({}, {})
const config = this.$db.get(`picBed.${this.id}`)
const config = await this.getConfig<IPicGoPluginConfig>(this.getConfigType())
if (val.length > 0) {
this.configList = cloneDeep(val).map((item: any) => {
let defaultValue = item.default !== undefined
@@ -102,19 +135,6 @@ export default class extends Vue {
})
}
}
async validate () {
return new Promise((resolve, reject) => {
// @ts-ignore
this.$refs.form.validate((valid: boolean) => {
if (valid) {
resolve(this.ruleForm)
} else {
resolve(false)
return false
}
})
})
}
}
</script>
<style lang='stylus'>
+18 -68
View File
@@ -17,6 +17,7 @@
:default-active="defaultActive"
@select="handleSelect"
:unique-opened="true"
@open="handleGetPicPeds"
>
<el-menu-item index="upload">
<i class="el-icon-upload"></i>
@@ -91,35 +92,6 @@
</el-col>
</el-row>
</el-dialog>
<el-dialog
title="自定义链接格式"
:visible.sync="customLinkVisible"
>
<el-form
label-position="top"
:model="customLink"
ref="customLink"
:rules="rules"
>
<el-form-item
label="用占位符$url来表示url的位置"
prop="value"
>
<el-input
class="align-center"
v-model="customLink.value"
:autofocus="true"
></el-input>
</el-form-item>
</el-form>
<div>
[]($url)
</div>
<span slot="footer">
<el-button @click="cancelCustomLink">取消</el-button>
<el-button type="primary" @click="confirmCustomLink">确定</el-button>
</span>
</el-dialog>
<el-dialog
class="qrcode-dialog"
top="3vh"
@@ -183,12 +155,14 @@ import {
IpcRendererEvent,
clipboard
} from 'electron'
import db from '#/datastore'
// import db from '#/datastore'
import mixin from '@/utils/mixin'
import InputBoxDialog from '@/components/InputBoxDialog.vue'
import {
SHOW_PRIVACY_MESSAGE
SHOW_PRIVACY_MESSAGE,
OPEN_DEVTOOLS
} 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)) {
@@ -212,18 +186,7 @@ export default class extends Vue {
visible = false
keyBindingVisible = false
customLinkVisible = false
customLink = {
value: db.get('customLink') || '$url'
}
rules = {
value: [
{ validator: customLinkRule, trigger: 'blur' }
]
}
os = ''
shortKey: IShortKeyMap = {
upload: db.get('shortKey.upload')
}
picBed: IPicBedType[] = []
qrcodeVisible = false
picBedConfigString = ''
@@ -231,21 +194,25 @@ export default class extends Vue {
created () {
this.os = process.platform
this.buildMenu()
ipcRenderer.send('getPicBeds')
ipcRenderer.on('getPicBeds', this.getPicBeds)
this.handleGetPicPeds()
}
@Watch('choosedPicBedForQRCode')
choosedPicBedForQRCodeChange (val: string[], oldVal: string[]) {
if (val.length > 0) {
this.$nextTick(() => {
const picBedConfig = db.get('picBed')
this.$nextTick(async () => {
const picBedConfig = await this.getConfig('picBed')
const config = pick(picBedConfig, ...this.choosedPicBedForQRCode)
this.picBedConfigString = JSON.stringify(config)
})
}
}
handleGetPicPeds = () => {
ipcRenderer.send('getPicBeds')
}
handleSelect (index: string) {
const type = index.match(/picbeds-/)
if (type === null) {
@@ -310,6 +277,12 @@ export default class extends Vue {
click () {
ipcRenderer.send(SHOW_PRIVACY_MESSAGE)
}
},
{
label: '打开调试器',
click () {
ipcRenderer.send(OPEN_DEVTOOLS)
}
}
]
this.menu = Menu.buildFromTemplate(template)
@@ -318,29 +291,6 @@ export default class extends Vue {
// this.menu!.popup(remote.getCurrentWindow())
this.menu!.popup()
}
keyDetect (type: string, event: KeyboardEvent) {
this.shortKey[type] = keyDetect(event).join('+')
}
cancelKeyBinding () {
this.keyBindingVisible = false
this.shortKey = db.get('shortKey')
}
cancelCustomLink () {
this.customLinkVisible = false
this.customLink.value = db.get('customLink') || '$url'
}
confirmCustomLink () {
// @ts-ignore
this.$refs.customLink.validate((valid: boolean) => {
if (valid) {
db.set('customLink', this.customLink.value)
this.customLinkVisible = false
ipcRenderer.send('updateCustomLink')
} else {
return false
}
})
}
openMiniWindow () {
ipcRenderer.send('openMiniWindow')
}
+79 -72
View File
@@ -71,12 +71,12 @@
<el-col :span="20" :offset="2">
<el-row :gutter="16">
<gallerys
:images="images"
:images="filterList"
:index="idx"
@close="handleClose"
:options="options"
></gallerys>
<el-col :span="6" v-for="(item, index) in images" :key="item.id" class="gallery-list__img">
<el-col :span="6" v-for="(item, index) in filterList" :key="item.id" class="gallery-list__img">
<div
class="gallery-list__item"
@click="zoomImage(index)"
@@ -111,7 +111,9 @@
// @ts-ignore
import gallerys from 'vue-gallery'
import pasteStyle from '#/utils/pasteTemplate'
import { IPasteStyle } from '#/types/enum'
import { Component, Vue, Watch } from 'vue-property-decorator'
import { IResult } from '@picgo/store/dist/types'
import {
ipcRenderer,
clipboard,
@@ -153,34 +155,40 @@ export default class extends Vue {
picBed: IPicBedType[] = []
@Watch('$route')
handleRouteUpdate (to: any, from: any) {
console.log(to, from)
if (from.name === 'gallery') {
this.clearChoosedList()
}
if (to.name === 'gallery') {
this.updateGallery()
}
}
created () {
async created () {
ipcRenderer.on('updateGallery', (event: IpcRendererEvent) => {
this.$nextTick(() => {
this.filterList = this.getGallery()
this.$nextTick(async () => {
this.updateGallery()
})
})
ipcRenderer.send('getPicBeds')
ipcRenderer.on('getPicBeds', this.getPicBeds)
this.updateGallery()
}
mounted () {
document.addEventListener('keydown', this.handleDetectShiftKey)
document.addEventListener('keyup', this.handleDetectShiftKey)
}
handleDetectShiftKey (event: KeyboardEvent) {
if (event.keyCode === 16) {
this.isShiftKeyPress = !this.isShiftKeyPress
if (event.key === 'Shift') {
if (event.type === 'keydown') {
this.isShiftKeyPress = true
} else if (event.type === 'keyup') {
this.isShiftKeyPress = false
}
}
}
get filterList () {
return this.getGallery()
}
set filterList (val) {
this.images = val
}
get isAllSelected () {
const values = Object.values(this.choosedList)
if (values.length === 0) {
@@ -194,37 +202,28 @@ export default class extends Vue {
getPicBeds (event: IpcRendererEvent, picBeds: IPicBedType[]) {
this.picBed = picBeds
}
getGallery () {
if (this.choosedPicBed.length > 0) {
let arr: ImgInfo[] = []
this.choosedPicBed.forEach(item => {
let obj: IObj = {
type: item
}
if (this.searchText) {
obj.fileName = this.searchText
}
// @ts-ignore
arr = arr.concat(this.$db.read().get('uploaded').filter(obj => {
return obj.fileName.indexOf(this.searchText) !== -1 && obj.type === item
}).reverse().value())
})
this.images = arr
getGallery (): ImgInfo[] {
if (this.searchText || this.choosedPicBed.length > 0) {
return this.images
.filter(item => {
let isInChoosedPicBed = true
let isIncludesSearchText = true
if (this.choosedPicBed.length > 0) {
isInChoosedPicBed = this.choosedPicBed.some(type => type === item.type)
}
if (this.searchText) {
isIncludesSearchText = item.fileName?.includes(this.searchText) || false
}
return isIncludesSearchText && isInChoosedPicBed
})
} else {
if (this.searchText) {
let data = this.$db.read().get('uploaded')
// @ts-ignore
.filter(item => {
return item.fileName.indexOf(this.searchText) !== -1
}).reverse().value()
this.images = data
} else {
// @ts-ignore
this.images = this.$db.read().get('uploaded').slice().reverse().value()
}
return this.images
}
return this.images
}
async updateGallery () {
this.images = (await this.$$db.get({ orderBy: 'desc' })).data
}
@Watch('filterList')
handleFilterListChange () {
this.clearChoosedList()
@@ -267,9 +266,10 @@ export default class extends Vue {
this.idx = null
this.changeZIndexForGallery(false)
}
copy (item: ImgInfo) {
const style = this.$db.get('settings.pasteStyle') || 'markdown'
const copyLink = pasteStyle(style, item)
async copy (item: ImgInfo) {
const style = await this.getConfig<IPasteStyle>('settings.pasteStyle') || IPasteStyle.MARKDOWN
const customLink = await this.getConfig<string>('settings.customLink')
const copyLink = pasteStyle(style, item, customLink)
const obj = {
title: '复制链接成功',
body: copyLink,
@@ -286,9 +286,9 @@ export default class extends Vue {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const file = this.$db.getById('uploaded', id)
this.$db.removeById('uploaded', id)
}).then(async () => {
const file = await this.$$db.getById(id)
await this.$$db.removeById(id)
ipcRenderer.send('removeFiles', [file])
const obj = {
title: '操作结果',
@@ -298,7 +298,7 @@ export default class extends Vue {
myNotification.onclick = () => {
return true
}
this.getGallery()
this.updateGallery()
}).catch((e) => {
console.log(e)
return true
@@ -309,12 +309,10 @@ export default class extends Vue {
this.imgInfo.imgUrl = item.imgUrl as string
this.dialogVisible = true
}
confirmModify () {
this.$db.read().get('uploaded')
// @ts-ignore
.getById(this.imgInfo.id)
.assign({ imgUrl: this.imgInfo.imgUrl })
.write()
async confirmModify () {
await this.$$db.updateById(this.imgInfo.id, {
imgUrl: this.imgInfo.imgUrl
})
const obj = {
title: '修改图片URL成功',
body: this.imgInfo.imgUrl,
@@ -325,7 +323,7 @@ export default class extends Vue {
return true
}
this.dialogVisible = false
this.getGallery()
this.updateGallery()
}
choosePicBed (type: string) {
let idx = this.choosedPicBed.indexOf(type)
@@ -355,18 +353,21 @@ export default class extends Vue {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
let files: ImgInfo[] = []
Object.keys(this.choosedList).forEach(key => {
}).then(async () => {
let files: IResult<ImgInfo>[] = []
const imageIDList = Object.keys(this.choosedList)
for (let i = 0; i < imageIDList.length; i++) {
const key = imageIDList[i]
if (this.choosedList[key]) {
const file = this.$db.getById('uploaded', key)
files.push(file)
this.$db.removeById('uploaded', key)
const file = await this.$$db.getById<ImgInfo>(key)
if (file) {
files.push(file)
await this.$$db.removeById(key)
}
}
})
}
this.clearChoosedList()
this.choosedList = {} // 只有删除才能将这个置空
this.getGallery()
const obj = {
title: '操作结果',
body: '删除成功'
@@ -376,23 +377,29 @@ export default class extends Vue {
myNotification.onclick = () => {
return true
}
this.updateGallery()
}).catch(() => {
return true
})
}
}
multiCopy () {
async multiCopy () {
if (Object.values(this.choosedList).some(item => item)) {
const copyString: string[] = []
const style = this.$db.get('settings.pasteStyle') || 'markdown'
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.
Object.keys(this.choosedList).forEach(key => {
const imageIDList = Object.keys(this.choosedList)
for (let i = 0; i < imageIDList.length; i++) {
const key = imageIDList[i]
if (this.choosedList[key]) {
const item = this.$db.getById('uploaded', key)
copyString.push(pasteStyle(style, item))
this.choosedList[key] = false
const item = await this.$$db.getById<ImgInfo>(key)
if (item) {
copyString.push(pasteStyle(style, item, customLink))
this.choosedList[key] = false
}
}
})
}
const obj = {
title: '批量复制链接成功',
body: copyString.join('\n')
@@ -407,11 +414,11 @@ export default class extends Vue {
toggleHandleBar () {
this.handleBarActive = !this.handleBarActive
}
getPasteStyle () {
this.pasteStyle = this.$db.get('settings.pasteStyle') || 'markdown'
}
handlePasteStyleChange (val: string) {
this.$db.set('settings.pasteStyle', val)
// getPasteStyle () {
// this.pasteStyle = this.$db.get('settings.pasteStyle') || 'markdown'
// }
async handlePasteStyleChange (val: string) {
this.saveConfig('settings.pasteStyle', val)
this.pasteStyle = val
}
beforeDestroy () {
+11 -4
View File
@@ -23,7 +23,7 @@ import {
IpcRendererEvent,
remote
} from 'electron'
import { SHOW_PRIVACY_MESSAGE } from '~/universal/events/constants'
import { SHOW_PRIVACY_MESSAGE, OPEN_DEVTOOLS } from '~/universal/events/constants'
@Component({
name: 'mini-page',
mixins: [mixin]
@@ -136,15 +136,16 @@ export default class extends Vue {
openContextMenu () {
this.menu!.popup()
}
buildMenu () {
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: this.$db.get('picBed.current') === item.type,
checked: current === item.type,
click () {
_this.letPicGoSaveData({
_this.saveConfig({
'picBed.current': item.type,
'picBed.uploader': item.type
})
@@ -189,6 +190,12 @@ export default class extends Vue {
remote.app.exit(0)
}
},
{
label: '打开调试器',
click () {
ipcRenderer.send(OPEN_DEVTOOLS)
}
},
{
role: 'quit',
label: '退出'
+83 -50
View File
@@ -338,13 +338,14 @@
<script lang="ts">
import keyDetect from '@/utils/key-binding'
import pkg from 'root/package.json'
import path from 'path'
import { IConfig } from 'picgo/dist/src/types/index'
import { PICGO_OPEN_FILE } from '#/events/constants'
import {
ipcRenderer,
remote
} from 'electron'
import { Component, Vue } from 'vue-property-decorator'
import db from '#/datastore'
// import db from '#/datastore'
const releaseUrl = 'https://api.github.com/repos/Molunerfinn/PicGo/releases/latest'
const releaseUrlBackup = 'https://cdn.jsdelivr.net/gh/Molunerfinn/PicGo@latest/package.json'
const downloadUrl = 'https://github.com/Molunerfinn/PicGo/releases/latest'
@@ -355,30 +356,22 @@ const customLinkRule = (rule: string, value: string, callback: (arg0?: Error) =>
return callback()
}
}
let logLevel = db.get('settings.logLevel')
if (!Array.isArray(logLevel)) {
if (logLevel && logLevel.length > 0) {
logLevel = [logLevel]
} else {
logLevel = ['all']
}
}
@Component({
name: 'picgo-setting'
})
export default class extends Vue {
form: ISettingForm = {
updateHelper: db.get('settings.showUpdateTip'),
updateHelper: false,
showPicBedList: [],
autoStart: db.get('settings.autoStart') || false,
rename: db.get('settings.rename') || false,
autoRename: db.get('settings.autoRename') || false,
uploadNotification: db.get('settings.uploadNotification') || false,
miniWindowOntop: db.get('settings.miniWindowOntop') || false,
logLevel,
autoCopyUrl: db.get('settings.autoCopy') === undefined ? true : db.get('settings.autoCopy'),
checkBetaUpdate: db.get('settings.checkBetaUpdate') === undefined ? true : db.get('settings.checkBetaUpdate')
autoStart: false,
rename: false,
autoRename: false,
uploadNotification: false,
miniWindowOntop: false,
logLevel: ['all'],
autoCopyUrl: true,
checkBetaUpdate: true
}
picBed: IPicBedType[] = []
logFileVisible = false
@@ -388,14 +381,14 @@ export default class extends Vue {
serverVisible = false
proxyVisible = false
customLink = {
value: db.get('settings.customLink') || '$url'
value: '$url'
}
shortKey: IShortKeyMap = {
upload: db.get('settings.shortKey.upload')
upload: ''
}
proxy = db.get('picBed.proxy') || ''
npmRegistry = db.get('settings.registry') || ''
npmProxy = db.get('settings.proxy') || ''
proxy = ''
npmRegistry = ''
npmProxy = ''
rules = {
value: [
{ validator: customLinkRule, trigger: 'blur' }
@@ -409,7 +402,7 @@ export default class extends Vue {
warn: '提醒-Warn',
none: '不记录日志-None'
}
server = db.get('settings.server') || {
server = {
port: 36677,
host: '127.0.0.1',
enable: true
@@ -429,7 +422,47 @@ export default class extends Vue {
this.os = process.platform
ipcRenderer.send('getPicBeds')
ipcRenderer.on('getPicBeds', this.getPicBeds)
this.initData()
}
async initData () {
const config = (await this.getConfig<IConfig>())!
if (config !== undefined) {
const settings = config.settings || {}
const picBed = config.picBed
this.form.updateHelper = settings.showUpdateTip || false
this.form.autoStart = settings.autoStart || false
this.form.rename = settings.rename || false
this.form.autoRename = settings.autoRename || false
this.form.uploadNotification = settings.uploadNotification || false
this.form.miniWindowOntop = settings.miniWindowOntop || false
this.form.logLevel = this.initLogLevel(settings.logLevel || [])
this.form.autoCopyUrl = settings.autoCopy === undefined ? true : settings.autoCopy
this.form.checkBetaUpdate = settings.checkBetaUpdate === undefined ? true : settings.checkBetaUpdate
this.customLink.value = settings.customLink || '$url'
this.shortKey.upload = settings.shortKey.upload
this.proxy = picBed.proxy || ''
this.npmRegistry = settings.registry || ''
this.npmProxy = settings.proxy || ''
this.server = settings.server || {
port: 36677,
host: '127.0.0.1',
enable: true
}
}
}
initLogLevel (logLevel: string | string[]) {
if (!Array.isArray(logLevel)) {
if (logLevel && logLevel.length > 0) {
logLevel = [logLevel]
} else {
logLevel = ['all']
}
}
return logLevel
}
getPicBeds (event: Event, picBeds: IPicBedType[]) {
this.picBed = picBeds
this.form.showPicBedList = this.picBed.map(item => {
@@ -439,10 +472,7 @@ export default class extends Vue {
}) as string[]
}
openFile (file: string) {
const { app, shell } = remote
const STORE_PATH = app.getPath('userData')
const FILE = path.join(STORE_PATH, `/${file}`)
shell.openItem(FILE)
ipcRenderer.send(PICGO_OPEN_FILE, file)
}
openLogSetting () {
this.logFileVisible = true
@@ -450,15 +480,15 @@ export default class extends Vue {
keyDetect (type: string, event: KeyboardEvent) {
this.shortKey[type] = keyDetect(event).join('+')
}
cancelCustomLink () {
async cancelCustomLink () {
this.customLinkVisible = false
this.customLink.value = db.get('settings.customLink') || '$url'
this.customLink.value = await this.getConfig<string>('settings.customLink') || '$url'
}
confirmCustomLink () {
// @ts-ignore
this.$refs.customLink.validate((valid: boolean) => {
if (valid) {
db.set('settings.customLink', this.customLink.value)
this.saveConfig('settings.customLink', this.customLink.value)
this.customLinkVisible = false
ipcRenderer.send('updateCustomLink')
} else {
@@ -466,13 +496,13 @@ export default class extends Vue {
}
})
}
cancelProxy () {
async cancelProxy () {
this.proxyVisible = false
this.proxy = db.get('picBed.proxy') || undefined
this.proxy = await this.getConfig<string>('picBed.proxy') || ''
}
confirmProxy () {
this.proxyVisible = false
this.letPicGoSaveData({
this.saveConfig({
'picBed.proxy': this.proxy,
'settings.proxy': this.npmProxy,
'settings.registry': this.npmRegistry
@@ -485,10 +515,10 @@ export default class extends Vue {
}
}
updateHelperChange (val: boolean) {
db.set('settings.showUpdateTip', val)
this.saveConfig('settings.showUpdateTip', val)
}
checkBetaUpdateChange (val: boolean) {
db.set('settings.checkBetaUpdate', val)
this.saveConfig('settings.checkBetaUpdate', val)
}
handleShowPicBedListChange (val: string[]) {
const list = this.picBed.map(item => {
@@ -499,22 +529,22 @@ export default class extends Vue {
}
return item
})
this.letPicGoSaveData({
this.saveConfig({
'picBed.list': list
})
ipcRenderer.send('getPicBeds')
}
handleAutoStartChange (val: boolean) {
db.set('settings.autoStart', val)
this.saveConfig('settings.autoStart', val)
ipcRenderer.send('autoStart', val)
}
handleRename (val: boolean) {
this.letPicGoSaveData({
this.saveConfig({
'settings.rename': val
})
}
handleAutoRename (val: boolean) {
this.letPicGoSaveData({
this.saveConfig({
'settings.autoRename': val
})
}
@@ -556,14 +586,16 @@ export default class extends Vue {
this.checkUpdateVisible = false
}
handleUploadNotification (val: boolean) {
db.set('settings.uploadNotification', val)
this.saveConfig({
'settings.uploadNotification': val
})
}
handleMiniWindowOntop (val: boolean) {
db.set('settings.miniWindowOntop', val)
this.saveConfig('settings.miniWindowOntop', val)
this.$message.info('需要重启生效')
}
handleAutoCopyUrl (val: boolean) {
db.set('settings.autoCopy', val)
this.saveConfig('settings.autoCopy', val)
const successNotification = new Notification('设置自动复制链接', {
body: '设置成功'
})
@@ -575,7 +607,7 @@ export default class extends Vue {
if (this.form.logLevel.length === 0) {
return this.$message.error('请选择日志记录等级')
}
this.letPicGoSaveData({
this.saveConfig({
'settings.logLevel': this.form.logLevel
})
const successNotification = new Notification('设置日志', {
@@ -586,9 +618,9 @@ export default class extends Vue {
}
this.logFileVisible = false
}
cancelLogLevelSetting () {
async cancelLogLevelSetting () {
this.logFileVisible = false
let logLevel = db.get('settings.logLevel')
let logLevel = await this.getConfig<string | string[]>('settings.logLevel')
if (!Array.isArray(logLevel)) {
if (logLevel && logLevel.length > 0) {
logLevel = [logLevel]
@@ -599,8 +631,9 @@ export default class extends Vue {
this.form.logLevel = logLevel
}
confirmServerSetting () {
// @ts-ignore
this.server.port = parseInt(this.server.port, 10)
this.letPicGoSaveData({
this.saveConfig({
'settings.server': this.server
})
const successNotification = new Notification('设置PicGo-Server', {
@@ -612,9 +645,9 @@ export default class extends Vue {
this.serverVisible = false
ipcRenderer.send('updateServer')
}
cancelServerSetting () {
async cancelServerSetting () {
this.serverVisible = false
this.server = db.get('settings.server') || {
this.server = await this.getConfig('settings.server') || {
port: 36677,
host: '127.0.0.1',
enable: true
+25 -24
View File
@@ -164,7 +164,7 @@ export default class extends Vue {
document.querySelector('.main-content.el-row').style.zIndex = 10
}
}
created () {
async created () {
this.os = process.platform
ipcRenderer.on('hideLoading', () => {
this.loading = false
@@ -216,25 +216,26 @@ export default class extends Vue {
})
this.getPluginList()
this.getSearchResult = debounce(this.getSearchResult, 50)
this.needReload = this.$db.get('needReload')
this.needReload = await this.getConfig<boolean>('needReload') || false
}
buildContextMenu (plugin: IPicGoPlugin) {
async buildContextMenu (plugin: IPicGoPlugin) {
const _this = this
let menu = [{
label: '启用插件',
enabled: !plugin.enabled,
click () {
_this.letPicGoSaveData({
_this.saveConfig({
[`picgoPlugins.${plugin.fullName}`]: true
})
plugin.enabled = true
_this.getPicBeds()
_this.needReload = true
}
}, {
label: '禁用插件',
enabled: plugin.enabled,
click () {
_this.letPicGoSaveData({
_this.saveConfig({
[`picgoPlugins.${plugin.fullName}`]: false
})
plugin.enabled = false
@@ -267,7 +268,8 @@ export default class extends Vue {
_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)
}
@@ -275,7 +277,7 @@ export default class extends Vue {
// handle transformer
if (plugin.config.transformer.name) {
let currentTransformer = this.$db.get('picBed.transformer') || 'path'
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}`,
@@ -350,8 +352,10 @@ export default class extends Vue {
remote.app.relaunch()
remote.app.exit(0)
}
handleReload () {
this.$db.set('needReload', true)
async handleReload () {
this.saveConfig({
needReload: true
})
this.needReload = true
const successNotification = new Notification('更新成功', {
body: '请点击此通知重启应用以生效'
@@ -363,14 +367,14 @@ export default class extends Vue {
cleanSearch () {
this.searchText = ''
}
toggleTransformer (transformer: string) {
let currentTransformer = this.$db.get('picBed.transformer') || 'path'
async toggleTransformer (transformer: string) {
let currentTransformer = await this.getConfig<string>('picBed.transformer') || 'path'
if (currentTransformer === transformer) {
this.letPicGoSaveData({
this.saveConfig({
'picBed.transformer': 'path'
})
} else {
this.letPicGoSaveData({
this.saveConfig({
'picBed.transformer': transformer
})
}
@@ -381,17 +385,17 @@ export default class extends Vue {
if (result !== false) {
switch (this.currentType) {
case 'plugin':
this.letPicGoSaveData({
this.saveConfig({
[`${this.configName}`]: result
})
break
case 'uploader':
this.letPicGoSaveData({
this.saveConfig({
[`picBed.${this.configName}`]: result
})
break
case 'transformer':
this.letPicGoSaveData({
this.saveConfig({
[`transformer.${this.configName}`]: result
})
break
@@ -447,20 +451,20 @@ export default class extends Vue {
}
}
// restore Uploader & Transformer
handleRestoreState (item: string, name: string) {
async handleRestoreState (item: string, name: string) {
if (item === 'uploader') {
const current = this.$db.get('picBed.current')
const current = await this.getConfig('picBed.current')
if (current === name) {
this.letPicGoSaveData({
this.saveConfig({
'picBed.current': 'smms',
'picBed.uploader': 'smms'
})
}
}
if (item === 'transformer') {
const current = this.$db.get('picBed.transformer')
const current = await this.getConfig('picBed.transformer')
if (current === name) {
this.letPicGoSaveData({
this.saveConfig({
'picBed.transformer': 'path'
})
}
@@ -474,9 +478,6 @@ export default class extends Vue {
goAwesomeList () {
remote.shell.openExternal('https://github.com/PicGo/Awesome-PicGo')
}
letPicGoSaveData (data: IObj) {
ipcRenderer.send('picgoSaveData', data)
}
handleImportLocalPlugin () {
ipcRenderer.send('importLocalPlugin')
this.loading = true
+8 -11
View File
@@ -109,8 +109,8 @@ export default class extends Vue {
command = ''
shortKey = ''
currentIndex = 0
created () {
const shortKeyConfig = this.$db.get('settings.shortKey') as IShortKeyConfigs
async created () {
const shortKeyConfig = (await this.getConfig<IShortKeyConfigs>('settings.shortKey'))!
this.list = Object.keys(shortKeyConfig).map(item => {
return {
...shortKeyConfig[item],
@@ -132,26 +132,23 @@ export default class extends Vue {
toggleEnable (item: IShortKeyConfig) {
const status = !item.enable
item.enable = status
// this.$db.set(`settings.shortKey.${item.name}.enable`, status)
ipcRenderer.send('bindOrUnbindShortKey', item, item.from)
}
keyDetect (event: KeyboardEvent) {
this.shortKey = keyDetect(event).join('+')
}
openKeyBindingDialog (config: IShortKeyConfig, index: number) {
async openKeyBindingDialog (config: IShortKeyConfig, index: number) {
this.command = `${config.from}:${config.name}`
this.shortKey = this.$db.get(`settings.shortKey.${this.command}.key`)
this.shortKey = await this.getConfig(`settings.shortKey.${this.command}.key`) || ''
this.currentIndex = index
this.keyBindingVisible = true
}
cancelKeyBinding () {
async cancelKeyBinding () {
this.keyBindingVisible = false
this.shortKey = this.$db.get(`settings.shortKey.${this.command}.key`)
this.shortKey = await this.getConfig<string>(`settings.shortKey.${this.command}.key`) || ''
}
confirmKeyBinding () {
const oldKey = this.$db.get(`settings.shortKey.${this.command}.key`)
// this.$db.set(`settings.shortKey.${this.command}.key`, this.shortKey)
// const newKey = this.$db.get(`settings.shortKey.${this.command}`)
async confirmKeyBinding () {
const oldKey = await this.getConfig<string>(`settings.shortKey.${this.command}.key`)
const config = Object.assign({}, this.list[this.currentIndex])
config.key = this.shortKey
ipcRenderer.send('updateShortKey', config, oldKey, config.from)
+18 -16
View File
@@ -30,12 +30,15 @@ import { Component, Vue } from 'vue-property-decorator'
import mixin from '@/utils/mixin'
import pasteTemplate from '#/utils/pasteTemplate'
import { ipcRenderer, clipboard } from 'electron'
import { IPasteStyle } from '#/types/enum'
import { IResult } from '@picgo/store/dist/types'
@Component({
name: 'tray-page',
mixins: [mixin]
})
export default class extends Vue {
files = []
files: IResult<ImgInfo>[] = []
notification = {
title: '复制链接成功',
body: '',
@@ -46,16 +49,16 @@ export default class extends Vue {
get reverseList () {
return this.files.slice().reverse()
}
getData () {
// @ts-ignore
this.files = this.$db.read().get('uploaded').slice().reverse().slice(0, 5).value()
async getData () {
this.files = (await this.$$db.get<ImgInfo>({ orderBy: 'desc', limit: 5 })).data
}
copyTheLink (item: ImgInfo) {
async copyTheLink (item: ImgInfo) {
this.notification.body = item.imgUrl!
this.notification.icon = item.imgUrl!
const myNotification = new Notification(this.notification.title, this.notification)
const pasteStyle = this.$db.get('settings.pasteStyle') || 'markdown'
clipboard.writeText(pasteTemplate(pasteStyle, item))
const pasteStyle = await this.getConfig<IPasteStyle>('settings.pasteStyle') || IPasteStyle.MARKDOWN
const customLink = await this.getConfig<string>('settings.customLink')
clipboard.writeText(pasteTemplate(pasteStyle, item, customLink))
myNotification.onclick = () => {
return true
}
@@ -83,19 +86,18 @@ export default class extends Vue {
mounted () {
this.disableDragFile()
this.getData()
ipcRenderer.on('dragFiles', (event: Event, files: string[]) => {
files.forEach(item => {
this.$db.insert('uploaded', item)
})
// @ts-ignore
this.files = this.$db.read().get('uploaded').slice().reverse().slice(0, 5).value()
ipcRenderer.on('dragFiles', async (event: Event, files: string[]) => {
for (let i = 0; i < files.length; i++) {
const item = files[i]
await this.$$db.insert(item)
}
this.files = (await this.$$db.get<ImgInfo>({ orderBy: 'desc', limit: 5 })).data
})
ipcRenderer.on('clipboardFiles', (event: Event, files: ImgInfo[]) => {
this.clipboardFiles = files
})
ipcRenderer.on('uploadFiles', (event: Event) => {
// @ts-ignore
this.files = this.$db.read().get('uploaded').slice().reverse().slice(0, 5).value()
ipcRenderer.on('uploadFiles', async (event: Event) => {
this.files = (await this.$$db.get<ImgInfo>({ orderBy: 'desc', limit: 5 })).data
console.log(this.files)
this.uploadFlag = false
})
+14 -11
View File
@@ -169,11 +169,13 @@ export default class extends Vue {
})
ipcRenderer.send('uploadChoosedFiles', sendFiles)
}
getPasteStyle () {
this.pasteStyle = this.$db.get('settings.pasteStyle') || 'markdown'
async getPasteStyle () {
this.pasteStyle = await this.getConfig('settings.pasteStyle') || 'markdown'
}
handlePasteStyleChange (val: string) {
this.$db.set('settings.pasteStyle', val)
this.saveConfig({
'settings.pasteStyle': val
})
}
uploadClipboardFiles () {
ipcRenderer.send('uploadClipboardFilesFromUploadPage')
@@ -196,10 +198,10 @@ export default class extends Vue {
this.$message.error('请输入合法的URL')
}
}
getDefaultPicBed () {
const current: string = this.$db.get('picBed.current')
async getDefaultPicBed () {
const currentPicBed = await this.getConfig<string>('picBed.current')
this.picBed.forEach(item => {
if (item.type === current) {
if (item.type === currentPicBed) {
this.picBedName = item.name
}
})
@@ -208,20 +210,21 @@ export default class extends Vue {
this.picBed = picBeds
this.getDefaultPicBed()
}
handleChangePicBed () {
this.buildMenu()
async handleChangePicBed () {
await this.buildMenu()
// this.menu.popup(remote.getCurrentWindow())
this.menu!.popup()
}
buildMenu () {
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: this.$db.get('picBed.current') === item.type,
checked: currentPicBed === item.type,
click () {
_this.letPicGoSaveData({
_this.saveConfig({
'picBed.current': item.type,
'picBed.uploader': item.type
})
+3 -3
View File
@@ -86,8 +86,8 @@ export default class extends Vue {
customUrl: '',
options: ''
}
created () {
const config = this.$db.get('picBed.aliyun') as IAliYunConfig
async created () {
const config = await this.getConfig<IAliYunConfig>('picBed.aliyun')
if (config) {
this.form = Object.assign({}, config)
}
@@ -96,7 +96,7 @@ export default class extends Vue {
// @ts-ignore
this.$refs.aliyun.validate((valid) => {
if (valid) {
this.letPicGoSaveData({
this.saveConfig({
'picBed.aliyun': this.form
})
const successNotification = new window.Notification('设置结果', {
+3 -3
View File
@@ -71,8 +71,8 @@ export default class extends Vue {
customUrl: '',
branch: ''
}
created () {
const config = this.$db.get('picBed.github') as IGitHubConfig
async created () {
const config = await this.getConfig<IGitHubConfig>('picBed.github')
if (config) {
this.form = Object.assign({}, config)
}
@@ -81,7 +81,7 @@ export default class extends Vue {
// @ts-ignore
this.$refs.github.validate((valid) => {
if (valid) {
this.letPicGoSaveData({
this.saveConfig({
'picBed.github': this.form
})
const successNotification = new Notification('设置结果', {
+3 -3
View File
@@ -48,8 +48,8 @@ export default class extends Vue {
clientId: '',
proxy: ''
}
created () {
const config = this.$db.get('picBed.imgur') as IImgurConfig
async created () {
const config = await this.getConfig<IImgurConfig>('picBed.imgur')
if (config) {
this.form = Object.assign({}, config)
}
@@ -58,7 +58,7 @@ export default class extends Vue {
// @ts-ignore
this.$refs.imgur.validate((valid) => {
if (valid) {
this.letPicGoSaveData({
this.saveConfig({
'picBed.imgur': this.form
})
const successNotification = new Notification('设置结果', {
+2 -2
View File
@@ -56,7 +56,7 @@ export default class extends Vue {
// @ts-ignore
const result = await this.$refs.configForm.validate()
if (result !== false) {
this.letPicGoSaveData({
this.saveConfig({
[`picBed.${this.type}`]: result
})
const successNotification = new Notification('设置结果', {
@@ -68,7 +68,7 @@ export default class extends Vue {
}
}
setDefaultPicBed (type: string) {
this.letPicGoSaveData({
this.saveConfig({
'picBed.current': type,
'picBed.uploader': type
})
+4 -4
View File
@@ -17,7 +17,7 @@
:rules="{
required: true, message: 'AccessKey不能为空', trigger: 'blur'
}">
<el-input v-model="form.accessKey" placeholder="AccessKey" @keyup.native.enter="confirm('weiboForm')"></el-input>
<el-input v-model="form.accessKey" placeholder="AccessKey" @keyup.native.enter="confirm"></el-input>
</el-form-item>
<el-form-item
label="设定SecretKey"
@@ -88,8 +88,8 @@ export default class extends Vue {
options: '',
path: ''
}
created () {
const config = this.$db.get('picBed.qiniu') as IQiniuConfig
async created () {
const config = await this.getConfig<IQiniuConfig>('picBed.qiniu')
if (config) {
this.form = Object.assign({}, config)
}
@@ -98,7 +98,7 @@ export default class extends Vue {
// @ts-ignore
this.$refs.qiniu.validate((valid) => {
if (valid) {
this.letPicGoSaveData({
this.saveConfig({
'picBed.qiniu': this.form
})
const successNotification = new Notification('设置结果', {
+4 -4
View File
@@ -41,17 +41,17 @@ export default class extends Vue {
form: ISMMSConfig = {
token: ''
}
created () {
const config = this.$db.get('picBed.smms.token') as (string | boolean)
async created () {
const config = await this.getConfig<string | boolean>('picBed.smms.token')
if (typeof config !== 'boolean') {
this.form.token = config
this.form.token = config || ''
}
}
confirm () {
// @ts-ignore
this.$refs.smms.validate((valid) => {
if (valid) {
this.letPicGoSaveData({
this.saveConfig({
'picBed.smms': this.form
})
const successNotification = new window.Notification('设置结果', {
+4 -4
View File
@@ -30,7 +30,7 @@
:rules="{
required: true, message: 'SecretId不能为空', trigger: 'blur'
}">
<el-input v-model="form.secretId" placeholder="SecretId" @keyup.native.enter="confirm('weiboForm')"></el-input>
<el-input v-model="form.secretId" placeholder="SecretId" @keyup.native.enter="confirm"></el-input>
</el-form-item>
<el-form-item
label="设定SecretKey"
@@ -104,8 +104,8 @@ export default class extends Vue {
customUrl: '',
version: 'v4'
}
created () {
const config = this.$db.get('picBed.tcyun') as ITcYunConfig
async created () {
const config = await this.getConfig<ITcYunConfig>('picBed.tcyun')
if (config) {
this.form = Object.assign({}, config)
}
@@ -114,7 +114,7 @@ export default class extends Vue {
// @ts-ignore
this.$refs.tcyun.validate((valid) => {
if (valid) {
this.letPicGoSaveData({
this.saveConfig({
'picBed.tcyun': this.form
})
const successNotification = new window.Notification('设置结果', {
+3 -3
View File
@@ -79,8 +79,8 @@ export default class extends Vue {
options: '',
path: ''
}
created () {
const config = this.$db.get('picBed.upyun') as IUpYunConfig
async created () {
const config = await this.getConfig<IUpYunConfig>('picBed.upyun')
if (config) {
this.form = Object.assign({}, config)
}
@@ -89,7 +89,7 @@ export default class extends Vue {
// @ts-ignore
this.$refs.tcyun.validate((valid) => {
if (valid) {
this.letPicGoSaveData({
this.saveConfig({
'picBed.upyun': this.form
})
const successNotification = new Notification('设置结果', {
-153
View File
@@ -1,153 +0,0 @@
<template>
<div id="weibo-view">
<el-row :gutter="16">
<el-col :span="16" :offset="4">
<div class="view-title">
微博图床设置[已停止支持]
</div>
<el-form
ref="weiboForm"
label-position="right"
label-width="120px"
size="small"
:model="form">
<el-form-item
label="设定用户名"
prop="username"
:rules="{
required: !chooseCookie, message: '用户名不能为空', trigger: 'blur'
}">
<el-input v-model="form.username" placeholder="用户名" @keyup.native.enter="confirm('weiboForm')" :disabled="chooseCookie"></el-input>
</el-form-item>
<el-form-item
label="设定密码"
prop="password"
:rules="{required: !chooseCookie,messsage: '密码不能为空',trigger: 'blur'}">
<el-input v-model="form.password" type="password" @keyup.native.enter="confirm('weiboForm')" placeholder="密码" :disabled="chooseCookie"></el-input>
</el-form-item>
<el-form-item
label="使用Cookie上传"
>
<el-switch
v-model="chooseCookie"
active-text="cookie模式"
@change="handleSwitchChange"
></el-switch>
<i class="el-icon-question" @click="openWiki"></i>
</el-form-item>
<el-form-item
label="设定Cookie"
prop="cookie"
:rules="{
required: chooseCookie, message: '密码不能为空', trigger: 'blur'
}">
<el-input v-model="form.cookie" @keyup.native.enter="confirm('weiboForm')" placeholder="Cookie" :disabled="!chooseCookie"></el-input>
</el-form-item>
<el-form-item label="* 图片质量">
<el-radio-group v-model="quality">
<el-radio label="thumbnail">缩略图</el-radio>
<el-radio label="mw690">中等尺寸</el-radio>
<el-radio label="large">原图</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item>
<el-button-group>
<el-button type="primary" @click="confirm('weiboForm')" round>确定</el-button>
<el-button type="success" @click="setDefaultPicBed('weibo')" round :disabled="defaultPicBed === 'weibo'">设为默认图床</el-button>
</el-button-group>
</el-form-item>
</el-form>
</el-col>
</el-row>
</div>
</template>
<script>
import mixin from '@/utils/ConfirmButtonMixin'
export default {
name: 'weibo',
mixins: [mixin],
data () {
return {
form: {
username: '',
password: '',
cookie: ''
},
chooseCookie: false,
quality: 'large'
}
},
created () {
const config = this.$db.get('picBed.weibo')
if (config) {
this.form.username = config.username
this.form.password = config.password
this.quality = config.quality || 'large'
this.form.cookie = config.cookie
this.chooseCookie = config.chooseCookie
}
},
methods: {
confirm (formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.letPicGoSaveData({
'picBed.weibo': {
username: this.form.username,
password: this.form.password,
quality: this.quality,
cookie: this.form.cookie,
chooseCookie: this.chooseCookie
}
})
const successNotification = new window.Notification('设置结果', {
body: '设置成功'
})
successNotification.onclick = () => {
return true
}
} else {
return false
}
})
},
handleSwitchChange () {
this.$refs['weiboForm'].resetFields()
},
openWiki () {
this.$electron.remote.shell.openExternal('https://picgo.github.io/PicGo-Doc/zh/guide/config.html#微博图床')
}
}
}
</script>
<style lang='stylus'>
.el-message
left 60%
#weibo-view
.el-form
label
line-height 22px
padding-bottom 0
color #eee
.el-button-group
width 100%
.el-button
width 50%
.el-input__inner
border-radius 19px
.el-radio-group
margin-left 25px
.el-switch__label
color #eee
&.is-active
color #409EFF
.el-icon-question
font-size 20px
float right
margin-top 9px
color #eee
cursor pointer
transition .2s color ease-in-out
&:hover
color #409EFF
</style>
-5
View File
@@ -31,11 +31,6 @@ export default new Router({
component: () => import(/* webpackChunkName: "Upload" */ '@/pages/Upload.vue'),
name: 'upload'
},
{
path: 'weibo',
component: () => import(/* webpackChunkName: "Weibo" */ '@/pages/picbeds/Weibo.vue'),
name: 'weibo'
},
{
path: 'qiniu',
component: () => import(/* webpackChunkName: "Qiniu" */ '@/pages/picbeds/Qiniu.vue'),
+9 -2
View File
@@ -1,10 +1,17 @@
import { Component, Vue } from 'vue-property-decorator'
import { ipcRenderer } from 'electron'
import { IConfig } from 'picgo/dist/src/types'
@Component
export default class extends Vue {
defaultPicBed = this.$db.get('picBed.uploader') || this.$db.get('picBed.current') || 'smms'
defaultPicBed = 'smms'
async created () {
const config = await this.getConfig<IConfig>()
if (config) {
this.defaultPicBed = config?.picBed?.uploader || config?.picBed?.current || 'smms'
}
}
setDefaultPicBed (type: string) {
this.letPicGoSaveData({
this.saveConfig({
'picBed.current': type,
'picBed.uploader': type
})
+53
View File
@@ -0,0 +1,53 @@
import { IObject, IResult, IGetResult, IFilter } from '@picgo/store/dist/types'
import { ipcRenderer, IpcRendererEvent } from 'electron'
import { uuid } from 'uuidv4'
import {
PICGO_GET_DB,
PICGO_INSERT_DB,
PICGO_INSERT_MANY_DB,
PICGO_UPDATE_BY_ID_DB,
PICGO_GET_BY_ID_DB,
PICGO_REMOVE_BY_ID_DB
} from '#/events/constants'
import { IGalleryDB } from '#/types/extra-vue'
export class GalleryDB implements IGalleryDB {
async get<T> (filter?: IFilter): Promise<IGetResult<T>> {
const res = await this.msgHandler<IGetResult<T>>(PICGO_GET_DB, filter)
return res
}
async insert<T> (value: T): Promise<IResult<T>> {
const res = await this.msgHandler<IResult<T>>(PICGO_INSERT_DB, value)
return res
}
async insertMany<T> (value: T[]): Promise<IResult<T>[]> {
const res = await this.msgHandler<IResult<T>[]>(PICGO_INSERT_MANY_DB, value)
return res
}
async updateById (id: string, value: IObject): Promise<boolean> {
const res = await this.msgHandler<boolean>(PICGO_UPDATE_BY_ID_DB, id, value)
return res
}
async getById<T> (id: string): Promise<IResult<T> | undefined> {
const res = await this.msgHandler<IResult<T> | undefined>(PICGO_GET_BY_ID_DB, id)
return res
}
async removeById (id: string): Promise<void> {
const res = await this.msgHandler<void>(PICGO_REMOVE_BY_ID_DB, id)
return res
}
private msgHandler<T> (method: string, ...args: any[]): Promise<T> {
return new Promise((resolve) => {
const callbackId = uuid()
const callback = (event: IpcRendererEvent, data: T, returnCallbackId: string) => {
if (returnCallbackId === callbackId) {
resolve(data)
ipcRenderer.removeListener(method, callback)
}
}
ipcRenderer.on(method, callback)
ipcRenderer.send(method, ...args, callbackId)
})
}
}
export default new GalleryDB()
+24 -3
View File
@@ -1,8 +1,29 @@
import { Component, Vue } from 'vue-property-decorator'
import { ipcRenderer } from 'electron'
import { ipcRenderer, IpcRendererEvent } from 'electron'
import { PICGO_SAVE_CONFIG, PICGO_GET_CONFIG } from '#/events/constants'
import { uuid } from 'uuidv4'
@Component
export default class extends Vue {
letPicGoSaveData (data: IObj) {
ipcRenderer.send('picgoSaveData', data)
// support string key + value or object config
saveConfig (config: IObj | string, value?: any) {
if (typeof config === 'string') {
config = {
[config]: value
}
}
ipcRenderer.send(PICGO_SAVE_CONFIG, config)
}
getConfig<T> (key?: string): Promise<T | undefined> {
return new Promise((resolve) => {
const callbackId = uuid()
const callback = (event: IpcRendererEvent, config: T | undefined, returnCallbackId: string) => {
if (returnCallbackId === callbackId) {
resolve(config)
ipcRenderer.removeListener(PICGO_GET_CONFIG, callback)
}
}
ipcRenderer.on(PICGO_GET_CONFIG, callback)
ipcRenderer.send(PICGO_GET_CONFIG, key, callbackId)
})
}
}
-55
View File
@@ -1,55 +0,0 @@
import fs from 'fs-extra'
import path from 'path'
import { app } from 'electron'
import dayjs from 'dayjs'
const errorMsg = {
broken: 'PicGo 配置文件损坏,已经恢复为默认配置',
brokenButBackup: 'PicGo 配置文件损坏,已经恢复为备份配置'
}
function dbChecker () {
if (process.type !== 'renderer') {
if (!global.notificationList) global.notificationList = []
const STORE_PATH = app.getPath('userData')
const configFilePath = path.join(STORE_PATH, 'data.json')
const configFileBackupPath = path.join(STORE_PATH, 'data.bak.json')
if (!fs.existsSync(configFilePath)) {
return
}
let configFile: string = '{}'
let optionsTpl = {
title: '注意',
body: ''
}
try {
configFile = fs.readFileSync(configFilePath, { encoding: 'utf-8' })
JSON.parse(configFile)
} catch (e) {
fs.unlinkSync(configFilePath)
if (fs.existsSync(configFileBackupPath)) {
try {
configFile = fs.readFileSync(configFileBackupPath, { encoding: 'utf-8' })
JSON.parse(configFile)
fs.writeFileSync(configFilePath, configFile, { encoding: 'utf-8' })
const stats = fs.statSync(configFileBackupPath)
optionsTpl.body = `${errorMsg.brokenButBackup}\n备份文件版本:${dayjs(stats.mtime).format('YYYY-MM-DD HH:mm:ss')}`
global.notificationList.push(optionsTpl)
return
} catch (e) {
optionsTpl.body = errorMsg.broken
global.notificationList.push(optionsTpl)
return
}
}
optionsTpl.body = errorMsg.broken
global.notificationList.push(optionsTpl)
return
}
fs.writeFileSync(configFileBackupPath, configFile, { encoding: 'utf-8' })
}
}
export {
dbChecker
}
+10
View File
@@ -4,3 +4,13 @@ export const TOGGLE_SHORTKEY_MODIFIED_MODE = 'TOGGLE_SHORTKEY_MODIFIED_MODE'
export const TALKING_DATA_APPID = '7E6832BCE3F1438696579E541DFEBFDA'
export const TALKING_DATA_EVENT = 'TALKING_DATA_EVENT'
export const SHOW_PRIVACY_MESSAGE = 'SHOW_PRIVACY_MESSAGE'
export const PICGO_SAVE_CONFIG = 'PICGO_SAVE_CONFIG'
export const PICGO_GET_CONFIG = 'PICGO_GET_CONFIG'
export const PICGO_GET_DB = 'PICGO_GET_DB'
export const PICGO_INSERT_DB = 'PICGO_INSERT_DB'
export const PICGO_INSERT_MANY_DB = 'PICGO_INSERT_MANY_DB'
export const PICGO_UPDATE_BY_ID_DB = 'PICGO_UPDATE_BY_ID_DB'
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_OPEN_FILE = 'PICGO_OPEN_FILE'
export const OPEN_DEVTOOLS = 'OPEN_DEVTOOLS'
+1 -1
View File
@@ -27,7 +27,7 @@ declare global {
interface Global {
PICGO_GUI_VERSION: string
PICGO_CORE_VERSION: string
notificationList: IAppNotification[]
notificationList?: IAppNotification[]
}
}
}
+14 -3
View File
@@ -1,14 +1,25 @@
import VueRouter, { Route } from 'vue-router'
import db from '#/datastore'
import axios from 'axios'
import { IObject, IResult, IGetResult, IFilter } from '@picgo/store/dist/types'
interface IGalleryDB {
get<T>(filter?: IFilter): Promise<IGetResult<T>>
insert<T> (value: T): Promise<IResult<T>>
insertMany<T> (value: T[]): Promise<IResult<T>[]>
updateById (id: string, value: IObject): Promise<boolean>
getById<T> (id: string): Promise<IResult<T> | undefined>
removeById (id: string): Promise<void>
}
declare module 'vue/types/vue' {
interface Vue {
$router: VueRouter,
$route: Route,
$db: typeof db
$http: typeof axios
$builtInPicBed: string[]
$bus: Vue
letPicGoSaveData(data: IObj): void
$$db: IGalleryDB
saveConfig(data: IObj | string, value?: any): void
getConfig<T>(key?: string): Promise<T | undefined>
}
}
+13
View File
@@ -45,6 +45,7 @@ interface ImgInfo {
extname?: string
imgUrl?: string
id?: string
type?: string
[propName: string]: any
}
@@ -144,6 +145,14 @@ interface IPicGoPlugin {
hasInstall?: boolean
}
interface IPicGoPluginConfig {
name: string
type: string
required: boolean
default?: any
[propName: string]: any
}
interface IPluginMenuConfig {
name: string
fullName?: string
@@ -312,3 +321,7 @@ interface IAnalyticsData {
interface IStringKeyMap {
[propName: string]: any
}
type ILogArgvType = string | number
type ILogArgvTypeWithError = ILogArgvType | Error
+5 -7
View File
@@ -1,10 +1,8 @@
import db from '#/datastore'
import { IPasteStyle } from '#/types/enum'
import { handleUrlEncode } from './common'
const formatCustomLink = (customLink: string, item: ImgInfo) => {
let fileName = item.fileName!.replace(new RegExp(`\\${item.extname}$`), '')
const url = handleUrlEncode(item.url || item.imgUrl)
const url = item.url || item.imgUrl
const formatObj = {
url,
fileName
@@ -19,15 +17,15 @@ const formatCustomLink = (customLink: string, item: ImgInfo) => {
return customLink
}
export default (style: IPasteStyle, item: ImgInfo) => {
const url = handleUrlEncode(item.url || item.imgUrl)
const customLink = db.get('settings.customLink') || '$url'
export default (style: IPasteStyle, item: ImgInfo, customLink: string | undefined) => {
const url = item.url || item.imgUrl
const _customLink = customLink || '$url'
const tpl = {
'markdown': `![](${url})`,
'HTML': `<img src="${url}"/>`,
'URL': url,
'UBB': `[IMG]${url}[/IMG]`,
'Custom': formatCustomLink(customLink, item)
'Custom': formatCustomLink(_customLink, item)
}
return tpl[style]
}
+11 -2
View File
@@ -3,6 +3,8 @@ function resolve (dir) {
return path.join(__dirname, dir)
}
const arch = process.argv.includes('--ia32') ? 'ia32' : 'x64'
const config = {
configureWebpack: {
devtool: 'nosources-source-map'
@@ -66,7 +68,14 @@ const config = {
},
win: {
icon: 'build/icons/icon.ico',
target: 'nsis'
// eslint-disable-next-line no-template-curly-in-string
artifactName: `PicGo Setup \${version}-${arch}.exe`,
target: [{
target: 'nsis',
arch: [
arch
]
}]
},
nsis: {
shortcutName: 'PicGo',
@@ -86,7 +95,7 @@ const config = {
if (process.env.NODE_ENV === 'development') {
config.configureWebpack = {
devtool: 'eval-source-map'
devtool: 'source-map'
}
// for dev main process hot reload
config.pluginOptions.electronBuilder.mainProcessWatch = ['src/main/**/*']
+695 -184
View File
File diff suppressed because it is too large Load Diff