mirror of
https://github.com/Kuingsmile/PicList.git
synced 2026-05-26 02:30:18 +08:00
✨ Feature(custom): refactor all main ipc event
This commit is contained in:
@@ -3,9 +3,10 @@ import {
|
||||
clipboard,
|
||||
dialog,
|
||||
Menu,
|
||||
MenuItem,
|
||||
MenuItemConstructorOptions,
|
||||
nativeTheme,
|
||||
Notification,
|
||||
screen,
|
||||
Tray
|
||||
} from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
@@ -29,23 +30,16 @@ import { configPaths } from '#/utils/configPaths'
|
||||
import { IPasteStyle, IWindowList } from '#/types/enum'
|
||||
|
||||
import pkg from 'root/package.json'
|
||||
import { hideMiniWindow, openMainWindow, openMiniWindow } from '~/utils/windowHelper'
|
||||
|
||||
let contextMenu: Menu | null
|
||||
|
||||
export function setDockMenu () {
|
||||
const isListeningClipboard = db.get(configPaths.settings.isListeningClipboard) || false
|
||||
const autoCloseMiniWindow = db.get(configPaths.settings.autoCloseMiniWindow) || false
|
||||
const dockMenu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: T('OPEN_MAIN_WINDOW'),
|
||||
click () {
|
||||
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)
|
||||
settingWindow!.show()
|
||||
settingWindow!.focus()
|
||||
if (windowManager.has(IWindowList.MINI_WINDOW) && autoCloseMiniWindow) {
|
||||
windowManager.get(IWindowList.MINI_WINDOW)!.hide()
|
||||
}
|
||||
}
|
||||
click: openMainWindow
|
||||
},
|
||||
{
|
||||
label: T('START_WATCH_CLIPBOARD'),
|
||||
@@ -58,7 +52,7 @@ export function setDockMenu () {
|
||||
})
|
||||
setDockMenu()
|
||||
},
|
||||
enabled: !isListeningClipboard
|
||||
visible: !isListeningClipboard
|
||||
},
|
||||
{
|
||||
label: T('STOP_WATCH_CLIPBOARD'),
|
||||
@@ -68,7 +62,7 @@ export function setDockMenu () {
|
||||
clipboardPoll.removeAllListeners()
|
||||
setDockMenu()
|
||||
},
|
||||
enabled: isListeningClipboard
|
||||
visible: isListeningClipboard
|
||||
}
|
||||
])
|
||||
app.dock.setMenu(dockMenu)
|
||||
@@ -80,59 +74,27 @@ export function createMenu () {
|
||||
{
|
||||
label: 'PicList',
|
||||
submenu: [
|
||||
{
|
||||
label: T('OPEN_MAIN_WINDOW'),
|
||||
click () {
|
||||
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)
|
||||
const autoCloseMiniWindow = db.get(configPaths.settings.autoCloseMiniWindow) || false
|
||||
settingWindow!.show()
|
||||
settingWindow!.focus()
|
||||
if (windowManager.has(IWindowList.MINI_WINDOW) && autoCloseMiniWindow) {
|
||||
windowManager.get(IWindowList.MINI_WINDOW)!.hide()
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: T('RELOAD_APP'),
|
||||
click () {
|
||||
app.relaunch()
|
||||
app.exit(0)
|
||||
}
|
||||
}
|
||||
{ label: T('OPEN_MAIN_WINDOW'), click: openMainWindow },
|
||||
{ label: T('RELOAD_APP'), click () { app.relaunch(); app.exit(0) } }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: T('CHOOSE_DEFAULT_PICBED'),
|
||||
type: 'submenu',
|
||||
// @ts-ignore
|
||||
submenu
|
||||
},
|
||||
{ label: T('CHOOSE_DEFAULT_PICBED'), type: 'submenu', submenu },
|
||||
{
|
||||
label: 'Edit',
|
||||
// @ts-ignore
|
||||
submenu: [
|
||||
// @ts-ignore
|
||||
{ label: 'Undo', accelerator: 'CmdOrCtrl+Z', selector: 'undo:' },
|
||||
// @ts-ignore
|
||||
{ label: 'Redo', accelerator: 'Shift+CmdOrCtrl+Z', selector: 'redo:' },
|
||||
{ label: 'Undo', accelerator: 'CmdOrCtrl+Z', role: 'undo' },
|
||||
{ label: 'Redo', accelerator: 'Shift+CmdOrCtrl+Z', role: 'redo' },
|
||||
{ type: 'separator' },
|
||||
// @ts-ignore
|
||||
{ label: 'Cut', accelerator: 'CmdOrCtrl+X', selector: 'cut:' },
|
||||
// @ts-ignore
|
||||
{ label: 'Copy', accelerator: 'CmdOrCtrl+C', selector: 'copy:' },
|
||||
// @ts-ignore
|
||||
{ label: 'Paste', accelerator: 'CmdOrCtrl+V', selector: 'paste:' },
|
||||
// @ts-ignore
|
||||
{ label: 'Select All', accelerator: 'CmdOrCtrl+A', selector: 'selectAll:' }
|
||||
{ label: 'Cut', accelerator: 'CmdOrCtrl+X', role: 'cut' },
|
||||
{ label: 'Copy', accelerator: 'CmdOrCtrl+C', role: 'copy' },
|
||||
{ label: 'Paste', accelerator: 'CmdOrCtrl+V', role: 'paste' },
|
||||
{ label: 'Select All', accelerator: 'CmdOrCtrl+A', role: 'selectAll' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: T('QUIT'),
|
||||
submenu: [
|
||||
{
|
||||
label: T('QUIT'),
|
||||
role: 'quit'
|
||||
}
|
||||
{ label: T('QUIT'), role: 'quit' }
|
||||
]
|
||||
}
|
||||
])
|
||||
@@ -142,94 +104,39 @@ export function createMenu () {
|
||||
export function createContextMenu () {
|
||||
const ClipboardWatcher = clipboardPoll
|
||||
const isListeningClipboard = db.get(configPaths.settings.isListeningClipboard) || false
|
||||
const isMiniWindowVisible = windowManager.has(IWindowList.MINI_WINDOW) && windowManager.get(IWindowList.MINI_WINDOW)!.isVisible()
|
||||
|
||||
const startWatchClipboard = () => {
|
||||
db.set(configPaths.settings.isListeningClipboard, true)
|
||||
ClipboardWatcher.startListening()
|
||||
ClipboardWatcher.on('change', () => {
|
||||
picgo.log.info('clipboard changed')
|
||||
uploadClipboardFiles()
|
||||
})
|
||||
createContextMenu()
|
||||
}
|
||||
|
||||
const stopWatchClipboard = () => {
|
||||
db.set(configPaths.settings.isListeningClipboard, false)
|
||||
ClipboardWatcher.stopListening()
|
||||
ClipboardWatcher.removeAllListeners()
|
||||
createContextMenu()
|
||||
}
|
||||
|
||||
if (process.platform === 'darwin' || process.platform === 'win32') {
|
||||
const submenu = buildPicBedListMenu()
|
||||
const template = [
|
||||
{
|
||||
label: T('OPEN_MAIN_WINDOW'),
|
||||
click () {
|
||||
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)
|
||||
const autoCloseMiniWindow = db.get(configPaths.settings.autoCloseMiniWindow) || false
|
||||
settingWindow!.show()
|
||||
settingWindow!.focus()
|
||||
if (windowManager.has(IWindowList.MINI_WINDOW) && autoCloseMiniWindow) {
|
||||
windowManager.get(IWindowList.MINI_WINDOW)!.hide()
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: T('CHOOSE_DEFAULT_PICBED'),
|
||||
type: 'submenu',
|
||||
// @ts-ignore
|
||||
submenu
|
||||
},
|
||||
{
|
||||
label: T('START_WATCH_CLIPBOARD'),
|
||||
click () {
|
||||
db.set(configPaths.settings.isListeningClipboard, true)
|
||||
ClipboardWatcher.startListening()
|
||||
ClipboardWatcher.on('change', () => {
|
||||
picgo.log.info('clipboard changed')
|
||||
uploadClipboardFiles()
|
||||
})
|
||||
createContextMenu()
|
||||
},
|
||||
enabled: !isListeningClipboard
|
||||
},
|
||||
{
|
||||
label: T('STOP_WATCH_CLIPBOARD'),
|
||||
click () {
|
||||
db.set(configPaths.settings.isListeningClipboard, false)
|
||||
ClipboardWatcher.stopListening()
|
||||
ClipboardWatcher.removeAllListeners()
|
||||
createContextMenu()
|
||||
},
|
||||
enabled: isListeningClipboard
|
||||
},
|
||||
{
|
||||
label: T('RELOAD_APP'),
|
||||
click () {
|
||||
app.relaunch()
|
||||
app.exit(0)
|
||||
}
|
||||
},
|
||||
// @ts-ignore
|
||||
{
|
||||
role: 'quit',
|
||||
label: T('QUIT')
|
||||
}
|
||||
] as any
|
||||
const template: Array<(MenuItemConstructorOptions) | (MenuItem)> = [
|
||||
{ label: T('OPEN_MAIN_WINDOW'), click: openMainWindow },
|
||||
{ label: T('CHOOSE_DEFAULT_PICBED'), type: 'submenu', submenu },
|
||||
{ label: T('START_WATCH_CLIPBOARD'), click: startWatchClipboard, visible: !isListeningClipboard },
|
||||
{ label: T('STOP_WATCH_CLIPBOARD'), click: stopWatchClipboard, visible: isListeningClipboard },
|
||||
{ label: T('RELOAD_APP'), click () { app.relaunch(); app.exit(0) } },
|
||||
{ label: T('QUIT'), role: 'quit' }
|
||||
]
|
||||
if (process.platform === 'win32') {
|
||||
template.splice(2, 0,
|
||||
{
|
||||
label: T('OPEN_MINI_WINDOW'),
|
||||
click () {
|
||||
const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)!
|
||||
miniWindow.removeAllListeners()
|
||||
if (db.get(configPaths.settings.miniWindowOntop)) {
|
||||
miniWindow.setAlwaysOnTop(true)
|
||||
}
|
||||
const { width, height } = screen.getPrimaryDisplay().workAreaSize
|
||||
const lastPosition = db.get(configPaths.settings.miniWindowPosition)
|
||||
if (lastPosition) {
|
||||
miniWindow.setPosition(lastPosition[0], lastPosition[1])
|
||||
} else {
|
||||
miniWindow.setPosition(width - 100, height - 100)
|
||||
}
|
||||
const setPositionFunc = () => {
|
||||
const position = miniWindow.getPosition()
|
||||
db.set(configPaths.settings.miniWindowPosition, position)
|
||||
}
|
||||
miniWindow.on('close', setPositionFunc)
|
||||
miniWindow.on('move', setPositionFunc)
|
||||
miniWindow.show()
|
||||
miniWindow.focus()
|
||||
const autoCloseMainWindow = db.get(configPaths.settings.autoCloseMainWindow) || false
|
||||
if (windowManager.has(IWindowList.SETTING_WINDOW) && autoCloseMainWindow) {
|
||||
windowManager.get(IWindowList.SETTING_WINDOW)!.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
{ label: T('OPEN_MINI_WINDOW'), click () { openMiniWindow(false) }, visible: !isMiniWindowVisible },
|
||||
{ label: T('HIDE_MINI_WINDOW'), click: hideMiniWindow, visible: isMiniWindowVisible }
|
||||
)
|
||||
}
|
||||
contextMenu = Menu.buildFromTemplate(template)
|
||||
@@ -242,70 +149,11 @@ export function createContextMenu () {
|
||||
// 目前的实现无法正常工作
|
||||
|
||||
contextMenu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: T('OPEN_MAIN_WINDOW'),
|
||||
click () {
|
||||
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)
|
||||
const autoCloseMiniWindow = db.get(configPaths.settings.autoCloseMiniWindow) || false
|
||||
settingWindow!.show()
|
||||
settingWindow!.focus()
|
||||
if (windowManager.has(IWindowList.MINI_WINDOW) && autoCloseMiniWindow) {
|
||||
windowManager.get(IWindowList.MINI_WINDOW)!.hide()
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: T('OPEN_MINI_WINDOW'),
|
||||
click () {
|
||||
const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)!
|
||||
miniWindow.removeAllListeners()
|
||||
if (db.get(configPaths.settings.miniWindowOntop)) {
|
||||
miniWindow.setAlwaysOnTop(true)
|
||||
}
|
||||
const { width, height } = screen.getPrimaryDisplay().workAreaSize
|
||||
const lastPosition = db.get(configPaths.settings.miniWindowPosition)
|
||||
if (lastPosition) {
|
||||
miniWindow.setPosition(lastPosition[0], lastPosition[1])
|
||||
} else {
|
||||
miniWindow.setPosition(width - 100, height - 100)
|
||||
}
|
||||
const setPositionFunc = () => {
|
||||
const position = miniWindow.getPosition()
|
||||
db.set(configPaths.settings.miniWindowPosition, position)
|
||||
}
|
||||
miniWindow.on('close', setPositionFunc)
|
||||
miniWindow.on('move', setPositionFunc)
|
||||
miniWindow.show()
|
||||
miniWindow.focus()
|
||||
const autoCloseMainWindow = db.get(configPaths.settings.autoCloseMainWindow) || false
|
||||
if (windowManager.has(IWindowList.SETTING_WINDOW) && autoCloseMainWindow) {
|
||||
windowManager.get(IWindowList.SETTING_WINDOW)!.hide()
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: T('START_WATCH_CLIPBOARD'),
|
||||
click () {
|
||||
db.set(configPaths.settings.isListeningClipboard, true)
|
||||
ClipboardWatcher.startListening()
|
||||
ClipboardWatcher.on('change', () => {
|
||||
picgo.log.info('clipboard changed')
|
||||
uploadClipboardFiles()
|
||||
})
|
||||
createContextMenu()
|
||||
},
|
||||
enabled: !isListeningClipboard
|
||||
},
|
||||
{
|
||||
label: T('STOP_WATCH_CLIPBOARD'),
|
||||
click () {
|
||||
db.set(configPaths.settings.isListeningClipboard, false)
|
||||
ClipboardWatcher.stopListening()
|
||||
ClipboardWatcher.removeAllListeners()
|
||||
createContextMenu()
|
||||
},
|
||||
enabled: isListeningClipboard
|
||||
},
|
||||
{ label: T('OPEN_MAIN_WINDOW'), click: openMainWindow },
|
||||
{ label: T('OPEN_MINI_WINDOW'), click () { openMiniWindow(false) }, visible: !isMiniWindowVisible },
|
||||
{ label: T('HIDE_MINI_WINDOW'), click: hideMiniWindow, visible: isMiniWindowVisible },
|
||||
{ label: T('START_WATCH_CLIPBOARD'), click: startWatchClipboard, visible: !isListeningClipboard },
|
||||
{ label: T('STOP_WATCH_CLIPBOARD'), click: stopWatchClipboard, visible: isListeningClipboard },
|
||||
{
|
||||
label: T('ABOUT'),
|
||||
click () {
|
||||
@@ -317,11 +165,7 @@ export function createContextMenu () {
|
||||
})
|
||||
}
|
||||
},
|
||||
// @ts-ignore
|
||||
{
|
||||
role: 'quit',
|
||||
label: T('QUIT')
|
||||
}
|
||||
{ label: T('QUIT'), role: 'quit' }
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,26 +4,18 @@ import {
|
||||
} from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import path from 'path'
|
||||
import { ISftpPlistConfig } from 'piclist'
|
||||
|
||||
import picgo from '@core/picgo'
|
||||
import db, { GalleryDB } from '@core/datastore'
|
||||
|
||||
import GuiApi from 'apis/gui'
|
||||
import uploader from 'apis/app/uploader'
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import { T } from '~/i18n/index'
|
||||
import { handleCopyUrl, handleUrlEncodeWithSetting } from '~/utils/common'
|
||||
import pasteTemplate from '~/utils/pasteTemplate'
|
||||
import SSHClient from '~/utils/sshClient'
|
||||
|
||||
import ALLApi from '@/apis/allApi'
|
||||
import { getRawData } from '@/utils/common'
|
||||
|
||||
import { IPasteStyle, IWindowList } from '#/types/enum'
|
||||
import { picBedsCanbeDeleted } from '#/utils/static'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
|
||||
const handleClipboardUploading = async (): Promise<false | ImgInfo[]> => {
|
||||
@@ -129,62 +121,3 @@ export const uploadChoosedFiles = async (webContents: WebContents, files: IFileW
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteSFTPFile (config: ISftpPlistConfig, fileName: string) {
|
||||
try {
|
||||
const client = SSHClient.instance
|
||||
await client.connect(config)
|
||||
const uploadPath = `/${(config.uploadPath || '')}/`.replace(/\/+/g, '/')
|
||||
const remote = path.join(uploadPath, fileName)
|
||||
const deleteResult = await client.deleteFileSFTP(config, remote)
|
||||
client.close()
|
||||
return deleteResult
|
||||
} catch (err: any) {
|
||||
picgo.log.error(err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export const deleteChoosedFiles = async (list: ImgInfo[]): Promise<boolean[]> => {
|
||||
const result = []
|
||||
for (const item of list) {
|
||||
if (item.id) {
|
||||
try {
|
||||
const dbStore = GalleryDB.getInstance()
|
||||
const file = await dbStore.getById(item.id)
|
||||
await dbStore.removeById(item.id)
|
||||
if (await picgo.getConfig(configPaths.settings.deleteCloudFile)) {
|
||||
if (item.type !== undefined && picBedsCanbeDeleted.includes(item.type)) {
|
||||
const noteFunc = (value: boolean) => {
|
||||
const notification = new Notification({
|
||||
title: T('MANAGE_BUCKET_BATCH_DELETE_ERROR_MSG_MSG2'),
|
||||
body: T(value ? 'GALLERY_SYNC_DELETE_NOTICE_SUCCEED' : 'GALLERY_SYNC_DELETE_NOTICE_FAILED')
|
||||
})
|
||||
notification.show()
|
||||
}
|
||||
if (item.type === 'sftpplist') {
|
||||
const { fileName, config } = item
|
||||
setTimeout(() => {
|
||||
deleteSFTPFile(getRawData(config), fileName || '').then(noteFunc)
|
||||
}, 0)
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
ALLApi.delete(item).then(noteFunc)
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
setTimeout(() => {
|
||||
picgo.emit('remove', [file], GuiApi.getInstance())
|
||||
}, 500)
|
||||
result.push(true)
|
||||
} catch (e) {
|
||||
result.push(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
|
||||
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents?.send('updateGallery')
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
Notification,
|
||||
WebContents
|
||||
} from 'electron'
|
||||
import fse from 'fs-extra'
|
||||
import fs from 'fs-extra'
|
||||
import util from 'util'
|
||||
import path from 'path'
|
||||
import { IPicGo } from 'piclist'
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
RENAME_FILE_NAME,
|
||||
TALKING_DATA_EVENT
|
||||
} from '#/events/constants'
|
||||
import { IWindowList } from '#/types/enum'
|
||||
import { ICOREBuildInEvent, IWindowList } from '#/types/enum'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
import { CLIPBOARD_IMAGE_FOLDER } from '#/utils/static'
|
||||
|
||||
@@ -61,21 +61,22 @@ const handleTalkingData = (webContents: WebContents, options: IAnalyticsData) =>
|
||||
|
||||
class Uploader {
|
||||
private webContents: WebContents | null = null
|
||||
// private uploading: boolean = false
|
||||
|
||||
constructor () {
|
||||
this.init()
|
||||
}
|
||||
|
||||
init () {
|
||||
picgo.on('notification', (message: Electron.NotificationConstructorOptions | undefined) => {
|
||||
picgo.on(ICOREBuildInEvent.NOTIFICATION, (message: Electron.NotificationConstructorOptions | undefined) => {
|
||||
const notification = new Notification(message)
|
||||
notification.show()
|
||||
})
|
||||
|
||||
picgo.on('uploadProgress', (progress: any) => {
|
||||
picgo.on(ICOREBuildInEvent.UPLOAD_PROGRESS, (progress: any) => {
|
||||
this.webContents?.send('uploadProgress', progress)
|
||||
})
|
||||
picgo.on('beforeTransform', () => {
|
||||
|
||||
picgo.on(ICOREBuildInEvent.BEFORE_TRANSFORM, () => {
|
||||
if (db.get(configPaths.settings.uploadNotification)) {
|
||||
const notification = new Notification({
|
||||
title: T('UPLOAD_PROGRESS'),
|
||||
@@ -84,6 +85,7 @@ class Uploader {
|
||||
notification.show()
|
||||
}
|
||||
})
|
||||
|
||||
picgo.helper.beforeUploadPlugins.register('renameFn', {
|
||||
handle: async (ctx: IPicGo) => {
|
||||
const rename = db.get(configPaths.settings.rename)
|
||||
@@ -150,7 +152,7 @@ class Uploader {
|
||||
return false
|
||||
} finally {
|
||||
if (filePath) {
|
||||
fse.remove(filePath)
|
||||
fs.remove(filePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
import bus from '@core/bus'
|
||||
import { CREATE_APP_MENU } from '@core/bus/constants'
|
||||
import db from '@core/datastore'
|
||||
import picgo from '@core/picgo'
|
||||
|
||||
import { T } from '~/i18n'
|
||||
|
||||
@@ -25,16 +24,14 @@ const windowList = new Map<IWindowList, IWindowListItem>()
|
||||
const handleWindowParams = (windowURL: string) => windowURL
|
||||
|
||||
const getDefaultWindowSizes = (): { width: number, height: number } => {
|
||||
const mainWindowWidth = picgo.getConfig<any>(configPaths.settings.mainWindowWidth)
|
||||
const mainWindowHeight = picgo.getConfig<any>(configPaths.settings.mainWindowHeight)
|
||||
const [mainWindowWidth, mainWindowHeight] = db.get([configPaths.settings.mainWindowWidth, configPaths.settings.mainWindowHeight])
|
||||
return {
|
||||
width: mainWindowWidth || 1200,
|
||||
height: mainWindowHeight || 800
|
||||
}
|
||||
}
|
||||
|
||||
const defaultWindowWidth = getDefaultWindowSizes().width
|
||||
const defaultWindowHeight = getDefaultWindowSizes().height
|
||||
const { width: defaultWindowWidth, height: defaultWindowHeight } = getDefaultWindowSizes()
|
||||
|
||||
const trayWindowOptions = {
|
||||
height: 350,
|
||||
|
||||
@@ -6,6 +6,7 @@ import { IWindowList } from '#/types/enum'
|
||||
class WindowManager implements IWindowManager {
|
||||
#windowMap: Map<IWindowList | string, BrowserWindow> = new Map()
|
||||
#windowIdMap: Map<number, IWindowList | string> = new Map()
|
||||
|
||||
create (name: IWindowList) {
|
||||
const windowConfig: IWindowListItem = windowList.get(name)!
|
||||
if (windowConfig.isValid) {
|
||||
|
||||
@@ -5,6 +5,8 @@ import { dbPathChecker, dbPathDir, getGalleryDBPath } from '@core/datastore/dbCh
|
||||
|
||||
import { T } from '~/i18n'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
import { IJSON } from '@picgo/store/dist/types'
|
||||
import { IConfig } from 'piclist'
|
||||
|
||||
const STORE_PATH = dbPathDir()
|
||||
|
||||
@@ -16,6 +18,7 @@ export const DB_PATH: string = getGalleryDBPath().dbPath
|
||||
|
||||
class ConfigStore {
|
||||
#db: JSONStore
|
||||
|
||||
constructor () {
|
||||
this.#db = new JSONStore(CONFIG_PATH)
|
||||
|
||||
@@ -40,34 +43,54 @@ class ConfigStore {
|
||||
this.read()
|
||||
}
|
||||
|
||||
flush () {
|
||||
this.#db = new JSONStore(CONFIG_PATH)
|
||||
read (flush?: boolean): IJSON {
|
||||
return this.#db.read(flush)
|
||||
}
|
||||
|
||||
read () {
|
||||
this.#db.read()
|
||||
return this.#db
|
||||
}
|
||||
|
||||
get (key = ''): any {
|
||||
getSingle (key = ''): any {
|
||||
if (key === '') {
|
||||
return this.#db.read()
|
||||
return this.#db.read(true)
|
||||
}
|
||||
this.read(true)
|
||||
return this.#db.get(key)
|
||||
}
|
||||
|
||||
get (key: string): any
|
||||
get (key: string[]): any[]
|
||||
get (key: string | string[] = ''): any {
|
||||
if (Array.isArray(key)) {
|
||||
return key.map(k => this.getSingle(k))
|
||||
}
|
||||
return this.getSingle(key)
|
||||
}
|
||||
|
||||
set (key: string, value: any): void {
|
||||
this.read(true)
|
||||
return this.#db.set(key, value)
|
||||
}
|
||||
|
||||
has (key: string) {
|
||||
this.read(true)
|
||||
return this.#db.has(key)
|
||||
}
|
||||
|
||||
unset (key: string, value: any): boolean {
|
||||
this.read(true)
|
||||
return this.#db.unset(key, value)
|
||||
}
|
||||
|
||||
saveConfig (config: Partial<IConfig>): void {
|
||||
Object.keys(config).forEach((name: string) => {
|
||||
this.set(name, config[name])
|
||||
})
|
||||
}
|
||||
|
||||
removeConfig (config: IConfig): void {
|
||||
Object.keys(config).forEach((name: string) => {
|
||||
this.unset(name, config[name])
|
||||
})
|
||||
}
|
||||
|
||||
getConfigPath () {
|
||||
return CONFIG_PATH
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ picgo.GUI_VERSION = global.PICGO_GUI_VERSION
|
||||
const originPicGoSaveConfig = picgo.saveConfig.bind(picgo)
|
||||
|
||||
function flushDB () {
|
||||
db.flush()
|
||||
db.read(true)
|
||||
}
|
||||
|
||||
const debounced = debounce(flushDB, 1000)
|
||||
|
||||
@@ -65,7 +65,7 @@ class GuiApi implements IGuiApi {
|
||||
await this.showSettingWindow()
|
||||
this.getWebcontentsByWindowId(this.settingWindowId)?.send(SHOW_INPUT_BOX, options)
|
||||
return new Promise<string>((resolve) => {
|
||||
ipcMain.once(SHOW_INPUT_BOX, (event: Event, value: string) => {
|
||||
ipcMain.once(SHOW_INPUT_BOX, (_: Event, value: string) => {
|
||||
resolve(value)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,388 +0,0 @@
|
||||
import {
|
||||
app,
|
||||
ipcMain,
|
||||
shell,
|
||||
Notification,
|
||||
IpcMainEvent,
|
||||
BrowserWindow,
|
||||
screen,
|
||||
IpcMainInvokeEvent
|
||||
} from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
import { ISftpPlistConfig } from 'piclist'
|
||||
|
||||
import bus from '@core/bus'
|
||||
import logger from '@core/picgo/logger'
|
||||
import db, { GalleryDB } from '@core/datastore'
|
||||
|
||||
import shortKeyHandler from 'apis/app/shortKey/shortKeyHandler'
|
||||
import uploader from 'apis/app/uploader'
|
||||
import {
|
||||
uploadClipboardFiles,
|
||||
uploadChoosedFiles
|
||||
} from 'apis/app/uploader/apis'
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import { T } from '~/i18n'
|
||||
import server from '~/server'
|
||||
import picgoCoreIPC from '~/events/picgoCoreIPC'
|
||||
import { buildMainPageMenu, buildMiniPageMenu, buildPluginPageMenu, buildPicBedListMenu } from '~/events/remotes/menu'
|
||||
import { handleCopyUrl, generateShortUrl, setTrayToolTip } from '~/utils/common'
|
||||
import { removeFileFromS3InMain, removeFileFromDogeInMain, removeFileFromHuaweiInMain } from '~/utils/deleteFunc'
|
||||
import getPicBeds from '~/utils/getPicBeds'
|
||||
import pasteTemplate from '~/utils/pasteTemplate'
|
||||
import SSHClient from '~/utils/sshClient'
|
||||
import { uploadFile, downloadFile } from '~/utils/syncSettings'
|
||||
import webServer from '~/server/webServer'
|
||||
|
||||
import {
|
||||
TOGGLE_SHORTKEY_MODIFIED_MODE,
|
||||
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,
|
||||
SET_MINI_WINDOW_POS,
|
||||
GET_PICBEDS,
|
||||
HIDE_DOCK
|
||||
} from '#/events/constants'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
import { ILogType, IPasteStyle, IWindowList } from '#/types/enum'
|
||||
|
||||
const STORE_PATH = app.getPath('userData')
|
||||
const commonConfigList = ['data.json', 'data.bak.json']
|
||||
const manageConfigList = ['manage.json', 'manage.bak.json']
|
||||
|
||||
export default {
|
||||
listen () {
|
||||
picgoCoreIPC.listen()
|
||||
// Upload Related IPC
|
||||
// from macOS tray
|
||||
ipcMain.on('uploadClipboardFiles', async () => {
|
||||
const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)!
|
||||
// macOS use builtin clipboard is OK
|
||||
const img = await uploader.setWebContents(trayWindow.webContents).uploadWithBuildInClipboard()
|
||||
if (img !== false) {
|
||||
const pasteStyle = db.get(configPaths.settings.pasteStyle) || IPasteStyle.MARKDOWN
|
||||
handleCopyUrl(await (pasteTemplate(pasteStyle, img[0], db.get(configPaths.settings.customLink))))
|
||||
const isShowResultNotification = db.get(configPaths.settings.uploadResultNotification) === undefined ? true : !!db.get(configPaths.settings.uploadResultNotification)
|
||||
if (isShowResultNotification) {
|
||||
const notification = new Notification({
|
||||
title: T('UPLOAD_SUCCEED'),
|
||||
body: img[0].imgUrl!
|
||||
// icon: file[0]
|
||||
// icon: img[0].imgUrl
|
||||
})
|
||||
notification.show()
|
||||
}
|
||||
await GalleryDB.getInstance().insert(img[0])
|
||||
trayWindow.webContents.send('clipboardFiles', [])
|
||||
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
|
||||
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('updateGallery')
|
||||
}
|
||||
}
|
||||
trayWindow.webContents.send('uploadFiles')
|
||||
})
|
||||
|
||||
ipcMain.on('uploadClipboardFilesFromUploadPage', () => {
|
||||
uploadClipboardFiles()
|
||||
})
|
||||
|
||||
ipcMain.on('uploadChoosedFiles', async (evt: IpcMainEvent, files: IFileWithPath[]) => {
|
||||
return uploadChoosedFiles(evt.sender, files)
|
||||
})
|
||||
|
||||
ipcMain.on('setTrayToolTip', (_: IpcMainEvent, title: string) => {
|
||||
setTrayToolTip(title)
|
||||
})
|
||||
|
||||
// ShortKey Related IPC
|
||||
ipcMain.on('updateShortKey', (evt: IpcMainEvent, item: IShortKeyConfig, oldKey: string, from: string) => {
|
||||
const result = shortKeyHandler.updateShortKey(item, oldKey, from)
|
||||
evt.sender.send('updateShortKeyResponse', result)
|
||||
if (result) {
|
||||
const notification = new Notification({
|
||||
title: T('OPERATION_SUCCEED'),
|
||||
body: T('TIPS_SHORTCUT_MODIFIED_SUCCEED')
|
||||
})
|
||||
notification.show()
|
||||
} else {
|
||||
const notification = new Notification({
|
||||
title: T('OPERATION_FAILED'),
|
||||
body: T('TIPS_SHORTCUT_MODIFIED_CONFLICT')
|
||||
})
|
||||
notification.show()
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('bindOrUnbindShortKey', (_: IpcMainEvent, item: IShortKeyConfig, from: string) => {
|
||||
const result = shortKeyHandler.bindOrUnbindShortKey(item, from)
|
||||
if (result) {
|
||||
const notification = new Notification({
|
||||
title: T('OPERATION_SUCCEED'),
|
||||
body: T('TIPS_SHORTCUT_MODIFIED_SUCCEED')
|
||||
})
|
||||
notification.show()
|
||||
} else {
|
||||
const notification = new Notification({
|
||||
title: T('OPERATION_FAILED'),
|
||||
body: T('TIPS_SHORTCUT_MODIFIED_CONFLICT')
|
||||
})
|
||||
notification.show()
|
||||
}
|
||||
})
|
||||
|
||||
// Gallery image cloud delete IPC
|
||||
|
||||
ipcMain.on('logDeleteMsg', async (_: IpcMainEvent, msg: string, logLevel: ILogType) => {
|
||||
logger[logLevel](msg)
|
||||
})
|
||||
|
||||
ipcMain.handle('delete-sftp-file', async (_: IpcMainInvokeEvent, config: ISftpPlistConfig, fileName: string) => {
|
||||
try {
|
||||
const client = SSHClient.instance
|
||||
await client.connect(config)
|
||||
const uploadPath = `/${(config.uploadPath || '')}/`.replace(/\/+/g, '/')
|
||||
const remote = path.join(uploadPath, fileName)
|
||||
const deleteResult = await client.deleteFileSFTP(config, remote)
|
||||
client.close()
|
||||
return deleteResult
|
||||
} catch (err: any) {
|
||||
logger.error(err)
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.handle('delete-aws-s3-file', async (_: IpcMainInvokeEvent, configMap: IStringKeyMap) => {
|
||||
const result = await removeFileFromS3InMain(configMap)
|
||||
return result
|
||||
})
|
||||
|
||||
ipcMain.handle('delete-doge-file', async (_: IpcMainInvokeEvent, configMap: IStringKeyMap) => {
|
||||
const result = await removeFileFromDogeInMain(configMap)
|
||||
return result
|
||||
})
|
||||
|
||||
ipcMain.handle('delete-huaweicloud-file', async (_: IpcMainInvokeEvent, configMap: IStringKeyMap) => {
|
||||
const result = await removeFileFromHuaweiInMain(configMap)
|
||||
return result
|
||||
})
|
||||
|
||||
// migrate from PicGo
|
||||
|
||||
ipcMain.handle('migrateFromPicGo', async () => {
|
||||
const picGoConfigPath = STORE_PATH.replace('piclist', 'picgo')
|
||||
const files = [
|
||||
'data.json',
|
||||
'data.bak.json',
|
||||
'picgo.db',
|
||||
'picgo.bak.db'
|
||||
]
|
||||
try {
|
||||
await Promise.all(files.map(async file => {
|
||||
const sourcePath = path.join(picGoConfigPath, file)
|
||||
const targetPath = path.join(STORE_PATH, file.replace('picgo', 'piclist'))
|
||||
await fs.remove(targetPath)
|
||||
await fs.copy(sourcePath, targetPath, { overwrite: true })
|
||||
}
|
||||
))
|
||||
return true
|
||||
} catch (err: any) {
|
||||
logger.error(err)
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
// PicList Setting page IPC
|
||||
|
||||
ipcMain.on('autoStart', (_: IpcMainEvent, val: boolean) => {
|
||||
app.setLoginItemSettings({
|
||||
openAtLogin: val
|
||||
})
|
||||
})
|
||||
|
||||
ipcMain.handle('getShortUrl', async (_: IpcMainInvokeEvent, url: string) => {
|
||||
const shortUrl = await generateShortUrl(url)
|
||||
return shortUrl
|
||||
})
|
||||
|
||||
ipcMain.handle('uploadCommonConfig', async () => {
|
||||
return await uploadFile(commonConfigList)
|
||||
})
|
||||
|
||||
ipcMain.handle('downloadCommonConfig', async () => {
|
||||
return await downloadFile(commonConfigList)
|
||||
})
|
||||
|
||||
ipcMain.handle('uploadManageConfig', async () => {
|
||||
return await uploadFile(manageConfigList)
|
||||
})
|
||||
|
||||
ipcMain.handle('downloadManageConfig', async () => {
|
||||
return await downloadFile(manageConfigList)
|
||||
})
|
||||
|
||||
ipcMain.handle('uploadAllConfig', async () => {
|
||||
return await uploadFile([...commonConfigList, ...manageConfigList])
|
||||
})
|
||||
|
||||
ipcMain.handle('downloadAllConfig', async () => {
|
||||
return await downloadFile([...commonConfigList, ...manageConfigList])
|
||||
})
|
||||
|
||||
ipcMain.on('toggleMainWindowAlwaysOnTop', () => {
|
||||
const mainWindow = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const isAlwaysOnTop = mainWindow.isAlwaysOnTop()
|
||||
mainWindow.setAlwaysOnTop(!isAlwaysOnTop)
|
||||
})
|
||||
|
||||
// Window operation API
|
||||
|
||||
ipcMain.on('openSettingWindow', () => {
|
||||
windowManager.get(IWindowList.SETTING_WINDOW)!.show()
|
||||
const autoCloseMiniWindow = db.get(configPaths.settings.autoCloseMiniWindow) || false
|
||||
if (autoCloseMiniWindow) {
|
||||
if (windowManager.has(IWindowList.MINI_WINDOW)) {
|
||||
windowManager.get(IWindowList.MINI_WINDOW)!.hide()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('openManualWindow', () => {
|
||||
windowManager.get(IWindowList.MANUAL_WINDOW)!.show()
|
||||
})
|
||||
|
||||
ipcMain.on('openMiniWindow', () => {
|
||||
const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)!
|
||||
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
miniWindow.removeAllListeners()
|
||||
if (db.get(configPaths.settings.miniWindowOntop)) {
|
||||
miniWindow.setAlwaysOnTop(true)
|
||||
}
|
||||
const { width, height } = screen.getPrimaryDisplay().workAreaSize
|
||||
const lastPosition = db.get(configPaths.settings.miniWindowPosition)
|
||||
if (lastPosition) {
|
||||
miniWindow.setPosition(lastPosition[0], lastPosition[1])
|
||||
} else {
|
||||
miniWindow.setPosition(width - 100, height - 100)
|
||||
}
|
||||
const setPositionFunc = () => {
|
||||
const position = miniWindow.getPosition()
|
||||
db.set(configPaths.settings.miniWindowPosition, position)
|
||||
}
|
||||
miniWindow.on('close', setPositionFunc)
|
||||
miniWindow.on('move', setPositionFunc)
|
||||
miniWindow.show()
|
||||
miniWindow.focus()
|
||||
settingWindow.hide()
|
||||
})
|
||||
|
||||
ipcMain.on('updateMiniIcon', (_: IpcMainEvent, iconPath: string) => {
|
||||
const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)!
|
||||
miniWindow.webContents.send('updateMiniIcon', iconPath)
|
||||
})
|
||||
|
||||
ipcMain.on('miniWindowOntop', (_: IpcMainEvent, val: boolean) => {
|
||||
const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)!
|
||||
miniWindow.setAlwaysOnTop(val)
|
||||
})
|
||||
|
||||
ipcMain.on('refreshSettingWindow', () => {
|
||||
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
settingWindow.webContents.reloadIgnoringCache()
|
||||
})
|
||||
|
||||
ipcMain.on(GET_PICBEDS, (evt: IpcMainEvent) => {
|
||||
const picBeds = getPicBeds()
|
||||
evt.sender.send(GET_PICBEDS, picBeds)
|
||||
// evt.returnValue = picBeds
|
||||
})
|
||||
|
||||
ipcMain.on(TOGGLE_SHORTKEY_MODIFIED_MODE, (_: IpcMainEvent, val: boolean) => {
|
||||
bus.emit(TOGGLE_SHORTKEY_MODIFIED_MODE, val)
|
||||
})
|
||||
|
||||
ipcMain.on('updateServer', () => {
|
||||
server.restart()
|
||||
})
|
||||
ipcMain.on('stopWebServer', () => {
|
||||
webServer.stop()
|
||||
})
|
||||
ipcMain.on('restartWebServer', () => {
|
||||
webServer.restart()
|
||||
})
|
||||
ipcMain.on(OPEN_DEVTOOLS, (event: IpcMainEvent) => {
|
||||
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(window)
|
||||
menu.popup({
|
||||
window
|
||||
})
|
||||
})
|
||||
ipcMain.on(SHOW_UPLOAD_PAGE_MENU, () => {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const menu = buildPicBedListMenu()
|
||||
menu.popup({
|
||||
window
|
||||
})
|
||||
})
|
||||
ipcMain.on(SHOW_PLUGIN_PAGE_MENU, (_: 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, (_: IpcMainEvent, filePath: string) => {
|
||||
const abFilePath = path.join(STORE_PATH, filePath)
|
||||
shell.openPath(abFilePath)
|
||||
})
|
||||
ipcMain.on(OPEN_URL, (_: IpcMainEvent, url: string) => {
|
||||
shell.openExternal(url)
|
||||
})
|
||||
ipcMain.on(RELOAD_APP, () => {
|
||||
app.relaunch()
|
||||
app.exit(0)
|
||||
})
|
||||
ipcMain.on(SET_MINI_WINDOW_POS, (_: IpcMainEvent, pos: IMiniWindowPos) => {
|
||||
const window = BrowserWindow.getFocusedWindow()
|
||||
window?.setBounds(pos)
|
||||
})
|
||||
ipcMain.on(HIDE_DOCK, (_: IpcMainEvent, val: boolean) => {
|
||||
if (val) {
|
||||
app.dock.hide()
|
||||
} else {
|
||||
app.dock.show()
|
||||
}
|
||||
})
|
||||
},
|
||||
dispose () {}
|
||||
}
|
||||
@@ -1,459 +0,0 @@
|
||||
import {
|
||||
dialog,
|
||||
shell,
|
||||
IpcMainEvent,
|
||||
ipcMain,
|
||||
clipboard
|
||||
} from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
import { IGuiMenuItem, PicGo as PicGoCore } from 'piclist'
|
||||
import { IObject, IFilter } from '@picgo/store/dist/types'
|
||||
|
||||
import picgo from '@core/picgo'
|
||||
import { GalleryDB } from '@core/datastore'
|
||||
import { dbPathChecker } from '@core/datastore/dbChecker'
|
||||
|
||||
import shortKeyHandler from 'apis/app/shortKey/shortKeyHandler'
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
import GuiApi from 'apis/gui'
|
||||
|
||||
import { showNotification } from '~/utils/common'
|
||||
import pasteTemplate from '~/utils/pasteTemplate'
|
||||
import { i18nManager, T } from '~/i18n'
|
||||
import { rpcServer } from '~/events/rpc'
|
||||
|
||||
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,
|
||||
PICGO_OPEN_DIRECTORY,
|
||||
PASTE_TEXT,
|
||||
OPEN_WINDOW,
|
||||
GET_LANGUAGE_LIST,
|
||||
SET_CURRENT_LANGUAGE,
|
||||
GET_CURRENT_LANGUAGE,
|
||||
PICGO_GET_CONFIG_SYNC
|
||||
} from '#/events/constants'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
import { IPasteStyle, IPicGoHelperType, IWindowList } from '#/types/enum'
|
||||
import { handleStreamlinePluginName, simpleClone } from '#/utils/common'
|
||||
|
||||
// eslint-disable-next-line
|
||||
const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require
|
||||
// const PluginHandler = requireFunc('picgo/lib/PluginHandler').default
|
||||
const STORE_PATH = path.dirname(dbPathChecker())
|
||||
// const CONFIG_PATH = path.join(STORE_PATH, '/data.json')
|
||||
|
||||
interface GuiMenuItem {
|
||||
label: string
|
||||
handle: (arg0: PicGoCore, arg1: GuiApi) => Promise<void>
|
||||
}
|
||||
|
||||
// get uploader or transformer config
|
||||
const getConfig = (name: string, type: IPicGoHelperType, ctx: PicGoCore) => {
|
||||
let config: any[] = []
|
||||
if (name === '') {
|
||||
return config
|
||||
} else {
|
||||
const handler = ctx.helper[type].get(name)
|
||||
if (handler) {
|
||||
if (handler.config) {
|
||||
config = handler.config(ctx)
|
||||
}
|
||||
}
|
||||
return config
|
||||
}
|
||||
}
|
||||
|
||||
const handleConfigWithFunction = (config: any[]) => {
|
||||
for (const i in config) {
|
||||
if (typeof config[i].default === 'function') {
|
||||
config[i].default = config[i].default()
|
||||
}
|
||||
if (typeof config[i].choices === 'function') {
|
||||
config[i].choices = config[i].choices()
|
||||
}
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
const getPluginList = (): IPicGoPlugin[] => {
|
||||
const pluginList = picgo.pluginLoader.getFullList()
|
||||
const list = []
|
||||
for (const i in pluginList) {
|
||||
const plugin = picgo.pluginLoader.getPlugin(pluginList[i])!
|
||||
const pluginPath = path.join(STORE_PATH, `/node_modules/${pluginList[i]}`)
|
||||
const pluginPKG = requireFunc(path.join(pluginPath, 'package.json'))
|
||||
const uploaderName = plugin.uploader || ''
|
||||
const transformerName = plugin.transformer || ''
|
||||
let menu: Omit<IGuiMenuItem, 'handle'>[] = []
|
||||
if (plugin.guiMenu) {
|
||||
menu = plugin.guiMenu(picgo).map(item => ({
|
||||
label: item.label
|
||||
}))
|
||||
}
|
||||
let gui = false
|
||||
if (pluginPKG.keywords && pluginPKG.keywords.length > 0) {
|
||||
if (pluginPKG.keywords.includes('picgo-gui-plugin')) {
|
||||
gui = true
|
||||
}
|
||||
}
|
||||
const obj: IPicGoPlugin = {
|
||||
name: handleStreamlinePluginName(pluginList[i]),
|
||||
fullName: pluginList[i],
|
||||
author: pluginPKG.author.name || pluginPKG.author,
|
||||
description: pluginPKG.description,
|
||||
logo: 'file://' + path.join(pluginPath, 'logo.png').split(path.sep).join('/'),
|
||||
version: pluginPKG.version,
|
||||
gui,
|
||||
config: {
|
||||
plugin: {
|
||||
fullName: pluginList[i],
|
||||
name: handleStreamlinePluginName(pluginList[i]),
|
||||
config: plugin.config ? handleConfigWithFunction(plugin.config(picgo)) : []
|
||||
},
|
||||
uploader: {
|
||||
name: uploaderName,
|
||||
config: handleConfigWithFunction(getConfig(uploaderName, IPicGoHelperType.uploader, picgo))
|
||||
},
|
||||
transformer: {
|
||||
name: transformerName,
|
||||
config: handleConfigWithFunction(getConfig(uploaderName, IPicGoHelperType.transformer, picgo))
|
||||
}
|
||||
},
|
||||
enabled: picgo.getConfig(`picgoPlugins.${pluginList[i]}`),
|
||||
homepage: pluginPKG.homepage ? pluginPKG.homepage : '',
|
||||
guiMenu: menu,
|
||||
ing: false
|
||||
}
|
||||
list.push(obj)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
const handleGetPluginList = () => {
|
||||
ipcMain.on('getPluginList', (event: IpcMainEvent) => {
|
||||
try {
|
||||
const list = simpleClone(getPluginList())
|
||||
// here can just send JS Object not function
|
||||
// or will cause [Failed to serialize arguments] error
|
||||
event.sender.send('pluginList', list)
|
||||
} catch (e: any) {
|
||||
event.sender.send('pluginList', [])
|
||||
showNotification({
|
||||
title: T('TIPS_GET_PLUGIN_LIST_FAILED'),
|
||||
body: e.message
|
||||
})
|
||||
picgo.log.error(e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handlePluginInstall = () => {
|
||||
ipcMain.on('installPlugin', async (event: IpcMainEvent, fullName: string) => {
|
||||
const dispose = handleNPMError()
|
||||
const res = await picgo.pluginHandler.install([fullName])
|
||||
event.sender.send('installPlugin', {
|
||||
success: res.success,
|
||||
body: fullName,
|
||||
errMsg: res.success ? '' : res.body
|
||||
})
|
||||
if (res.success) {
|
||||
shortKeyHandler.registerPluginShortKey(res.body[0])
|
||||
} else {
|
||||
showNotification({
|
||||
title: T('PLUGIN_INSTALL_FAILED'),
|
||||
body: res.body as string
|
||||
})
|
||||
}
|
||||
event.sender.send('hideLoading')
|
||||
dispose()
|
||||
})
|
||||
}
|
||||
|
||||
const handlePluginUninstall = async (fullName: string) => {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const dispose = handleNPMError()
|
||||
const res = await picgo.pluginHandler.uninstall([fullName])
|
||||
if (res.success) {
|
||||
window.webContents.send('uninstallSuccess', res.body[0])
|
||||
shortKeyHandler.unregisterPluginShortKey(res.body[0])
|
||||
} else {
|
||||
showNotification({
|
||||
title: T('PLUGIN_UNINSTALL_FAILED'),
|
||||
body: res.body as string
|
||||
})
|
||||
}
|
||||
window.webContents.send('hideLoading')
|
||||
dispose()
|
||||
}
|
||||
|
||||
const handlePluginUpdate = async (fullName: string | string[]) => {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const dispose = handleNPMError()
|
||||
const res = await picgo.pluginHandler.update(typeof fullName === 'string' ? [fullName] : fullName)
|
||||
if (res.success) {
|
||||
window.webContents.send('updateSuccess', res.body[0])
|
||||
} else {
|
||||
showNotification({
|
||||
title: T('PLUGIN_UPDATE_FAILED'),
|
||||
body: res.body as string
|
||||
})
|
||||
}
|
||||
window.webContents.send('hideLoading')
|
||||
dispose()
|
||||
}
|
||||
|
||||
const handleUpdateAllPlugin = () => {
|
||||
ipcMain.on('updateAllPlugin', async (_: IpcMainEvent, list: string[]) => {
|
||||
handlePluginUpdate(list)
|
||||
})
|
||||
}
|
||||
|
||||
const handleNPMError = (): IDispose => {
|
||||
const handler = (msg: string) => {
|
||||
if (msg === 'NPM is not installed') {
|
||||
dialog.showMessageBox({
|
||||
title: T('TIPS_ERROR'),
|
||||
message: T('TIPS_INSTALL_NODE_AND_RELOAD_PICGO'),
|
||||
buttons: ['Yes']
|
||||
}).then((res) => {
|
||||
if (res.response === 0) {
|
||||
shell.openExternal('https://nodejs.org/')
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
picgo.once('failed', handler)
|
||||
return () => picgo.off('failed', handler)
|
||||
}
|
||||
|
||||
const handleGetPicBedConfig = () => {
|
||||
ipcMain.on('getPicBedConfig', (event: IpcMainEvent, type: string) => {
|
||||
const name = picgo.helper.uploader.get(type)?.name || type
|
||||
if (picgo.helper.uploader.get(type)?.config) {
|
||||
const _config = picgo.helper.uploader.get(type)!.config!(picgo)
|
||||
const config = handleConfigWithFunction(_config)
|
||||
event.sender.send('getPicBedConfig', config, name)
|
||||
} else {
|
||||
event.sender.send('getPicBedConfig', [], name)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: remove it
|
||||
const handlePluginActions = () => {
|
||||
ipcMain.on('pluginActions', (_: IpcMainEvent, name: string, label: string) => {
|
||||
const plugin = picgo.pluginLoader.getPlugin(name)
|
||||
if (plugin?.guiMenu?.(picgo)?.length) {
|
||||
const menu: GuiMenuItem[] = plugin.guiMenu(picgo)
|
||||
menu.forEach(item => {
|
||||
if (item.label === label) {
|
||||
item.handle(picgo, GuiApi.getInstance())
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleRemoveFiles = () => {
|
||||
ipcMain.on('removeFiles', (_: IpcMainEvent, files: ImgInfo[]) => {
|
||||
setTimeout(() => {
|
||||
picgo.emit('remove', files, GuiApi.getInstance())
|
||||
}, 500)
|
||||
})
|
||||
}
|
||||
|
||||
const handlePicGoSaveConfig = () => {
|
||||
ipcMain.on(PICGO_SAVE_CONFIG, (_: IpcMainEvent, data: IObj) => {
|
||||
picgo.saveConfig(data)
|
||||
})
|
||||
}
|
||||
|
||||
const handlePicGoGetConfig = () => {
|
||||
ipcMain.handle(PICGO_GET_CONFIG, (_, key: string | undefined) => {
|
||||
return picgo.getConfig(key)
|
||||
})
|
||||
}
|
||||
|
||||
const handlePicGoGetConfigSync = () => {
|
||||
ipcMain.on(PICGO_GET_CONFIG_SYNC, (event: IpcMainEvent, key: string | undefined) => {
|
||||
const result = picgo.getConfig(key)
|
||||
event.returnValue = result
|
||||
})
|
||||
}
|
||||
|
||||
const handleImportLocalPlugin = () => {
|
||||
ipcMain.on('importLocalPlugin', async (event: IpcMainEvent) => {
|
||||
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const res = await dialog.showOpenDialog(settingWindow, {
|
||||
properties: ['openDirectory']
|
||||
})
|
||||
const filePaths = res.filePaths
|
||||
if (filePaths.length > 0) {
|
||||
const res = await picgo.pluginHandler.install(filePaths)
|
||||
if (res.success) {
|
||||
try {
|
||||
const list = simpleClone(getPluginList())
|
||||
event.sender.send('pluginList', list)
|
||||
} catch (e: any) {
|
||||
event.sender.send('pluginList', [])
|
||||
showNotification({
|
||||
title: T('TIPS_GET_PLUGIN_LIST_FAILED'),
|
||||
body: e.message
|
||||
})
|
||||
}
|
||||
showNotification({
|
||||
title: T('PLUGIN_IMPORT_SUCCEED'),
|
||||
body: ''
|
||||
})
|
||||
} else {
|
||||
showNotification({
|
||||
title: T('PLUGIN_IMPORT_FAILED'),
|
||||
body: res.body as string
|
||||
})
|
||||
}
|
||||
}
|
||||
event.sender.send('hideLoading')
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
ipcMain.handle(PASTE_TEXT, async (_, item: ImgInfo, copy = true) => {
|
||||
const pasteStyle = picgo.getConfig<IPasteStyle>(configPaths.settings.pasteStyle) || IPasteStyle.MARKDOWN
|
||||
const customLink = picgo.getConfig<string>(configPaths.settings.customLink)
|
||||
const txt = await pasteTemplate(pasteStyle, item, customLink)
|
||||
if (copy) {
|
||||
clipboard.writeText(txt)
|
||||
}
|
||||
return txt
|
||||
})
|
||||
}
|
||||
|
||||
const handleOpenFile = () => {
|
||||
ipcMain.on(PICGO_OPEN_FILE, (event: IpcMainEvent, fileName: string) => {
|
||||
const abFilePath = path.join(STORE_PATH, fileName)
|
||||
if (!fs.existsSync(abFilePath)) {
|
||||
fs.writeFileSync(abFilePath, '')
|
||||
}
|
||||
shell.openPath(abFilePath)
|
||||
})
|
||||
}
|
||||
|
||||
const handleOpenDirectory = () => {
|
||||
ipcMain.on(PICGO_OPEN_DIRECTORY, (event: IpcMainEvent, dirPath?: string, inStorePath: boolean = true) => {
|
||||
if (inStorePath) {
|
||||
dirPath = path.join(STORE_PATH, dirPath || '')
|
||||
}
|
||||
if (!dirPath || !fs.existsSync(dirPath)) {
|
||||
return
|
||||
}
|
||||
shell.openPath(dirPath)
|
||||
})
|
||||
}
|
||||
|
||||
const handleOpenWindow = () => {
|
||||
ipcMain.on(OPEN_WINDOW, (event: IpcMainEvent, windowName: IWindowList) => {
|
||||
const window = windowManager.get(windowName)
|
||||
if (window) {
|
||||
window.show()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleI18n = () => {
|
||||
ipcMain.on(GET_LANGUAGE_LIST, (event: IpcMainEvent) => {
|
||||
event.sender.send(GET_LANGUAGE_LIST, i18nManager.languageList)
|
||||
})
|
||||
ipcMain.on(SET_CURRENT_LANGUAGE, (event: IpcMainEvent, language: string) => {
|
||||
i18nManager.setCurrentLanguage(language)
|
||||
const { lang, locales } = i18nManager.getCurrentLocales()
|
||||
picgo.i18n.setLanguage(lang)
|
||||
if (process.platform === 'darwin') {
|
||||
const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)
|
||||
trayWindow?.webContents.send(SET_CURRENT_LANGUAGE, lang, locales)
|
||||
}
|
||||
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)
|
||||
settingWindow?.webContents.send(SET_CURRENT_LANGUAGE, lang, locales)
|
||||
if (windowManager.has(IWindowList.MINI_WINDOW)) {
|
||||
const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)
|
||||
miniWindow?.webContents.send(SET_CURRENT_LANGUAGE, lang, locales)
|
||||
}
|
||||
// event.sender.send(SET_CURRENT_LANGUAGE, lang, locales)
|
||||
})
|
||||
ipcMain.on(GET_CURRENT_LANGUAGE, (event: IpcMainEvent) => {
|
||||
const { lang, locales } = i18nManager.getCurrentLocales()
|
||||
event.sender.send(GET_CURRENT_LANGUAGE, lang, locales)
|
||||
})
|
||||
}
|
||||
|
||||
const handleRPCActions = () => {
|
||||
rpcServer.start()
|
||||
}
|
||||
|
||||
export default {
|
||||
listen () {
|
||||
handleGetPluginList()
|
||||
handlePluginInstall()
|
||||
handleGetPicBedConfig()
|
||||
handlePluginActions()
|
||||
handleRemoveFiles()
|
||||
handlePicGoSaveConfig()
|
||||
handlePicGoGetConfig()
|
||||
handlePicGoGetConfigSync()
|
||||
handlePicGoGalleryDB()
|
||||
handleImportLocalPlugin()
|
||||
handleUpdateAllPlugin()
|
||||
handleOpenFile()
|
||||
handleOpenDirectory()
|
||||
handleOpenWindow()
|
||||
handleI18n()
|
||||
handleRPCActions()
|
||||
},
|
||||
// TODO: separate to single file
|
||||
handlePluginUninstall,
|
||||
handlePluginUpdate
|
||||
}
|
||||
@@ -3,7 +3,9 @@ import {
|
||||
dialog,
|
||||
BrowserWindow,
|
||||
Menu,
|
||||
shell
|
||||
shell,
|
||||
MenuItemConstructorOptions,
|
||||
MenuItem
|
||||
} from 'electron'
|
||||
import { PicGo as PicGoCore } from 'piclist'
|
||||
|
||||
@@ -16,7 +18,7 @@ import {
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
import GuiApi from 'apis/gui'
|
||||
|
||||
import picgoCoreIPC from '~/events/picgoCoreIPC'
|
||||
import { handlePluginUninstall, handlePluginUpdate } from '~/events/rpc/routes/plugin/utils'
|
||||
import { T } from '~/i18n'
|
||||
import clipboardPoll from '~/utils/clipboardPoll'
|
||||
import { setTrayToolTip } from '~/utils/common'
|
||||
@@ -34,6 +36,7 @@ import { IWindowList } from '#/types/enum'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
|
||||
import pkg from 'root/package.json'
|
||||
import { openMainWindow } from '~/utils/windowHelper'
|
||||
|
||||
interface GuiMenuItem {
|
||||
label: string
|
||||
@@ -44,18 +47,10 @@ const buildMiniPageMenu = () => {
|
||||
const isListeningClipboard = db.get(configPaths.settings.isListeningClipboard) || false
|
||||
const ClipboardWatcher = clipboardPoll
|
||||
const submenu = buildPicBedListMenu()
|
||||
const template = [
|
||||
const template: Array<(MenuItemConstructorOptions) | (MenuItem)> = [
|
||||
{
|
||||
label: T('OPEN_MAIN_WINDOW'),
|
||||
click () {
|
||||
windowManager.get(IWindowList.SETTING_WINDOW)!.show()
|
||||
const autoCloseMiniWindow = db.get(configPaths.settings.autoCloseMiniWindow) || false
|
||||
if (autoCloseMiniWindow) {
|
||||
if (windowManager.has(IWindowList.MINI_WINDOW)) {
|
||||
windowManager.get(IWindowList.MINI_WINDOW)!.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
click: openMainWindow
|
||||
},
|
||||
{
|
||||
label: T('CHOOSE_DEFAULT_PICBED'),
|
||||
@@ -69,7 +64,7 @@ const buildMiniPageMenu = () => {
|
||||
}
|
||||
},
|
||||
{
|
||||
label: T('HIDE_WINDOW'),
|
||||
label: T('HIDE_MINI_WINDOW'),
|
||||
click () {
|
||||
BrowserWindow.getFocusedWindow()!.hide()
|
||||
}
|
||||
@@ -85,7 +80,7 @@ const buildMiniPageMenu = () => {
|
||||
})
|
||||
buildMiniPageMenu()
|
||||
},
|
||||
enabled: !isListeningClipboard
|
||||
visible: !isListeningClipboard
|
||||
},
|
||||
{
|
||||
label: T('STOP_WATCH_CLIPBOARD'),
|
||||
@@ -95,7 +90,7 @@ const buildMiniPageMenu = () => {
|
||||
ClipboardWatcher.removeAllListeners()
|
||||
buildMiniPageMenu()
|
||||
},
|
||||
enabled: isListeningClipboard
|
||||
visible: isListeningClipboard
|
||||
},
|
||||
{
|
||||
label: T('RELOAD_APP'),
|
||||
@@ -109,7 +104,6 @@ const buildMiniPageMenu = () => {
|
||||
label: T('QUIT')
|
||||
}
|
||||
]
|
||||
// @ts-ignore
|
||||
return Menu.buildFromTemplate(template)
|
||||
}
|
||||
|
||||
@@ -269,14 +263,14 @@ const buildPluginPageMenu = (plugin: IPicGoPlugin) => {
|
||||
click () {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
window.webContents.send(PICGO_HANDLE_PLUGIN_ING, plugin.fullName)
|
||||
picgoCoreIPC.handlePluginUninstall(plugin.fullName)
|
||||
handlePluginUninstall(plugin.fullName)
|
||||
}
|
||||
}, {
|
||||
label: T('UPDATE_PLUGIN'),
|
||||
click () {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
window.webContents.send(PICGO_HANDLE_PLUGIN_ING, plugin.fullName)
|
||||
picgoCoreIPC.handlePluginUpdate(plugin.fullName)
|
||||
handlePluginUpdate(plugin.fullName)
|
||||
}
|
||||
}]
|
||||
for (const i in plugin.config) {
|
||||
@@ -330,7 +324,6 @@ const buildPluginPageMenu = (plugin: IPicGoPlugin) => {
|
||||
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)
|
||||
|
||||
@@ -1,45 +1,62 @@
|
||||
import { ipcMain, IpcMainEvent } from 'electron'
|
||||
import { ipcMain, IpcMainEvent, IpcMainInvokeEvent } from 'electron'
|
||||
|
||||
import { configRouter } from '~/events/rpc/routes/config'
|
||||
import { toolboxRouter } from '~/events/rpc/routes/toolbox'
|
||||
import logger from '@core/picgo/logger'
|
||||
|
||||
import { galleryRouter } from '~/events/rpc/routes/gallery'
|
||||
import { picbedRouter } from '~/events/rpc/routes/picbed'
|
||||
import { pluginRouter } from '~/events/rpc/routes/plugin'
|
||||
import { settingRouter } from '~/events/rpc/routes/setting'
|
||||
import { systemRouter } from '~/events/rpc/routes/system'
|
||||
import { toolboxRouter } from '~/events/rpc/routes/toolbox'
|
||||
import { trayRouter } from '~/events/rpc/routes/tray'
|
||||
import { uploadRouter } from '~/events/rpc/routes/upload'
|
||||
|
||||
import { IRPCActionType } from '#/types/enum'
|
||||
import { RPC_ACTIONS } from '#/events/constants'
|
||||
import { IRPCActionType, IRPCType } from '#/types/enum'
|
||||
import { RPC_ACTIONS, RPC_ACTIONS_INVOKE } from '#/events/constants'
|
||||
|
||||
const isDevelopment = process.env.NODE_ENV !== 'production'
|
||||
|
||||
class RPCServer implements IRPCServer {
|
||||
private routes: IRPCRoutes = new Map()
|
||||
private routesWithResponse: IRPCRoutes = new Map()
|
||||
|
||||
private rpcEventHandler = async (event: IpcMainEvent, action: IRPCActionType, args: any[], callbackId: string) => {
|
||||
private rpcEventHandler = async (event: IpcMainEvent, action: IRPCActionType, args: any[]) => {
|
||||
try {
|
||||
const handler = this.routes.get(action)
|
||||
if (!handler) {
|
||||
return this.sendBack(event, action, null, callbackId)
|
||||
if (isDevelopment) {
|
||||
console.log(`action: ${action} args: ${JSON.stringify(args)}`)
|
||||
}
|
||||
const res = await handler?.(args, event)
|
||||
this.sendBack(event, action, res, callbackId)
|
||||
} catch (e) {
|
||||
this.sendBack(event, action, null, callbackId)
|
||||
const route = this.routes.get(action)
|
||||
await route?.handler?.(event, args)
|
||||
} catch (e: any) {
|
||||
logger.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* if sendback data is null, then it means that the action is not supported or error occurs
|
||||
* if there is no callbackId, then do not send back
|
||||
*/
|
||||
private sendBack (event: IpcMainEvent, action: IRPCActionType, data: any, callbackId: string) {
|
||||
if (callbackId) {
|
||||
event.sender.send(RPC_ACTIONS, data, action, callbackId)
|
||||
private rpcEventHandlerWithResponse = async (event: IpcMainInvokeEvent, action: IRPCActionType, args: any[]) => {
|
||||
try {
|
||||
if (isDevelopment) {
|
||||
console.log(`action: ${action} args: ${JSON.stringify(args)}`)
|
||||
}
|
||||
const route = this.routesWithResponse.get(action)
|
||||
return await route?.handler?.(event, args)
|
||||
} catch (e: any) {
|
||||
logger.error(e)
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
start () {
|
||||
ipcMain.on(RPC_ACTIONS, this.rpcEventHandler)
|
||||
ipcMain.handle(RPC_ACTIONS_INVOKE, this.rpcEventHandlerWithResponse)
|
||||
}
|
||||
|
||||
use (routes: IRPCRoutes) {
|
||||
for (const [action, handler] of routes) {
|
||||
this.routes.set(action, handler)
|
||||
for (const [action, route] of routes) {
|
||||
if (route.type === IRPCType.SEND) {
|
||||
this.routes.set(action, route)
|
||||
} else {
|
||||
this.routesWithResponse.set(action, route)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,9 +67,20 @@ class RPCServer implements IRPCServer {
|
||||
|
||||
const rpcServer = new RPCServer()
|
||||
|
||||
rpcServer.use(configRouter.routes())
|
||||
rpcServer.use(toolboxRouter.routes())
|
||||
rpcServer.use(systemRouter.routes())
|
||||
const routes = [
|
||||
galleryRouter.routes(),
|
||||
picbedRouter.routes(),
|
||||
pluginRouter.routes(),
|
||||
settingRouter.routes(),
|
||||
systemRouter.routes(),
|
||||
toolboxRouter.routes(),
|
||||
trayRouter.routes(),
|
||||
uploadRouter.routes()
|
||||
]
|
||||
|
||||
for (const route of routes) {
|
||||
rpcServer.use(route)
|
||||
}
|
||||
|
||||
export {
|
||||
rpcServer
|
||||
|
||||
@@ -1,9 +1,22 @@
|
||||
import { IRPCActionType } from '#/types/enum'
|
||||
import { IRPCType, IRPCActionType } from '#/types/enum'
|
||||
|
||||
interface IBatchAddParams {
|
||||
action: IRPCActionType
|
||||
handler: IRPCHandler<any>
|
||||
type?: IRPCType
|
||||
}
|
||||
|
||||
export class RPCRouter implements IRPCRouter {
|
||||
private routeMap: IRPCRoutes = new Map()
|
||||
add = <T>(action: IRPCActionType, handler: IRPCHandler<T>) => {
|
||||
this.routeMap.set(action, handler)
|
||||
add = <T>(action: IRPCActionType, handler: IRPCHandler<T>, type: IRPCType = IRPCType.SEND): this => {
|
||||
this.routeMap.set(action, { handler, type })
|
||||
return this
|
||||
}
|
||||
|
||||
addBatch = (params: IBatchAddParams[]): this => {
|
||||
for (const { action, handler, type = IRPCType.SEND } of params) {
|
||||
this.routeMap.set(action, { handler, type })
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
import { RPCRouter } from '~/events/rpc/router'
|
||||
import {
|
||||
deleteUploaderConfig,
|
||||
getUploaderConfigList,
|
||||
resetUploaderConfig,
|
||||
selectUploaderConfig,
|
||||
updateUploaderConfig
|
||||
} from '~/utils/handleUploaderConfig'
|
||||
import { IRPCActionType } from '#/types/enum'
|
||||
|
||||
const configRouter = new RPCRouter()
|
||||
|
||||
configRouter
|
||||
.add(IRPCActionType.GET_PICBED_CONFIG_LIST, async (args) => {
|
||||
const [type] = args as IGetUploaderConfigListArgs
|
||||
const config = getUploaderConfigList(type)
|
||||
return config
|
||||
})
|
||||
.add(IRPCActionType.DELETE_PICBED_CONFIG, async (args) => {
|
||||
const [type, id] = args as IDeleteUploaderConfigArgs
|
||||
const config = deleteUploaderConfig(type, id)
|
||||
return config
|
||||
})
|
||||
.add(IRPCActionType.SELECT_UPLOADER, async (args) => {
|
||||
const [type, id] = args as ISelectUploaderConfigArgs
|
||||
selectUploaderConfig(type, id)
|
||||
return true
|
||||
})
|
||||
.add(IRPCActionType.UPDATE_UPLOADER_CONFIG, async (args) => {
|
||||
const [type, id, config] = args as IUpdateUploaderConfigArgs
|
||||
updateUploaderConfig(type, id, config)
|
||||
return true
|
||||
})
|
||||
.add(IRPCActionType.RESET_UPLOADER_CONFIG, async (args) => {
|
||||
const [type, id] = args as IUpdateUploaderConfigArgs
|
||||
resetUploaderConfig(type, id)
|
||||
return true
|
||||
})
|
||||
|
||||
export {
|
||||
configRouter
|
||||
}
|
||||
133
src/main/events/rpc/routes/gallery/index.ts
Normal file
133
src/main/events/rpc/routes/gallery/index.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
|
||||
import { clipboard } from 'electron'
|
||||
|
||||
import { GalleryDB } from '@core/datastore'
|
||||
import picgo from '@core/picgo'
|
||||
import logger from '@core/picgo/logger'
|
||||
import { IFilter, IObject } from '@picgo/store/dist/types'
|
||||
import GuiApi from 'apis/gui'
|
||||
|
||||
import { RPCRouter } from '~/events/rpc/router'
|
||||
import { removeFileFromDogeInMain, removeFileFromHuaweiInMain, removeFileFromS3InMain, removeFileFromSFTPInMain } from '~/utils/deleteFunc'
|
||||
import pasteTemplate from '~/utils/pasteTemplate'
|
||||
|
||||
import { ICOREBuildInEvent, IPasteStyle, IRPCActionType, IRPCType } from '#/types/enum'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
|
||||
const galleryRouter = new RPCRouter()
|
||||
|
||||
const galleryRoutes = [
|
||||
{
|
||||
action: IRPCActionType.GALLERY_PASTE_TEXT,
|
||||
handler: async (_: IIPCEvent, args: [ item: ImgInfo, copy?: boolean]) => {
|
||||
const [item, copy = true] = args
|
||||
const pasteStyle = picgo.getConfig<IPasteStyle>(configPaths.settings.pasteStyle) || IPasteStyle.MARKDOWN
|
||||
const customLink = picgo.getConfig<string>(configPaths.settings.customLink)
|
||||
const txt = await pasteTemplate(pasteStyle, item, customLink)
|
||||
if (copy) {
|
||||
clipboard.writeText(txt)
|
||||
}
|
||||
return txt
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.GALLERY_REMOVE_FILES,
|
||||
handler: async (_: IIPCEvent, args: [files: ImgInfo[]]) => {
|
||||
setTimeout(() => {
|
||||
picgo.emit(ICOREBuildInEvent.REMOVE, args[0], GuiApi.getInstance())
|
||||
}, 500)
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.GALLERY_GET_DB,
|
||||
handler: async (_: IIPCEvent, args: [filter: IFilter]) => {
|
||||
const dbStore = GalleryDB.getInstance()
|
||||
return await dbStore.get(args[0])
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.GALLERY_GET_BY_ID_DB,
|
||||
handler: async (_: IIPCEvent, args: [id: string]) => {
|
||||
const dbStore = GalleryDB.getInstance()
|
||||
return await dbStore.getById(args[0])
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.GALLERY_UPDATE_BY_ID_DB,
|
||||
handler: async (_: IIPCEvent, args: [id: string, value: IObject]) => {
|
||||
const dbStore = GalleryDB.getInstance()
|
||||
return await dbStore.updateById(args[0], args[1])
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.GALLERY_REMOVE_BY_ID_DB,
|
||||
handler: async (_: IIPCEvent, args: [id: string]) => {
|
||||
const dbStore = GalleryDB.getInstance()
|
||||
return await dbStore.removeById(args[0])
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.GALLERY_INSERT_DB,
|
||||
handler: async (_: IIPCEvent, args: [value: IObject]) => {
|
||||
const dbStore = GalleryDB.getInstance()
|
||||
return await dbStore.insert(args[0])
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.GALLERY_INSERT_DB_BATCH,
|
||||
handler: async (_: IIPCEvent, args: [value: IObject[]]) => {
|
||||
const dbStore = GalleryDB.getInstance()
|
||||
return await dbStore.insertMany(args[0])
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.GALLERY_LOG_DELETE_MSG,
|
||||
handler: async (_: IIPCEvent, args: [msg: string, logLevel: ILogType]) => {
|
||||
const [msg, logLevel] = args
|
||||
console.log(msg, logLevel)
|
||||
logger[logLevel](msg)
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.GALLERY_DELETE_SFTP_FILE,
|
||||
handler: async (_: IIPCEvent, args: [config: ISftpPlistConfig, fileName: string]) => {
|
||||
const [config, fileName] = args
|
||||
return await removeFileFromSFTPInMain(config, fileName)
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.GALLERY_DELETE_AWS_S3_FILE,
|
||||
handler: async (_: IIPCEvent, args: [configMap: IStringKeyMap]) => {
|
||||
return await removeFileFromS3InMain(args[0])
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.GALLERY_DELETE_DOGE_FILE,
|
||||
handler: async (_: IIPCEvent, args: [configMap: IStringKeyMap]) => {
|
||||
return await removeFileFromDogeInMain(args[0])
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.GALLERY_DELETE_HUAWEI_OSS_FILE,
|
||||
handler: async (_: IIPCEvent, args: [configMap: IStringKeyMap]) => {
|
||||
return await removeFileFromHuaweiInMain(args[0])
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
}
|
||||
]
|
||||
|
||||
galleryRouter.addBatch(galleryRoutes)
|
||||
|
||||
export {
|
||||
galleryRouter
|
||||
}
|
||||
99
src/main/events/rpc/routes/picbed/index.ts
Normal file
99
src/main/events/rpc/routes/picbed/index.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import picgo from '@core/picgo'
|
||||
|
||||
import { RPCRouter } from '~/events/rpc/router'
|
||||
import {
|
||||
deleteUploaderConfig,
|
||||
getUploaderConfigList,
|
||||
resetUploaderConfig,
|
||||
selectUploaderConfig,
|
||||
updateUploaderConfig
|
||||
} from '~/utils/handleUploaderConfig'
|
||||
import { IRPCActionType, IRPCType } from '#/types/enum'
|
||||
|
||||
const picbedRouter = new RPCRouter()
|
||||
|
||||
const handleConfigWithFunction = (config: any[]) => {
|
||||
for (const i in config) {
|
||||
if (typeof config[i].default === 'function') {
|
||||
config[i].default = config[i].default()
|
||||
}
|
||||
if (typeof config[i].choices === 'function') {
|
||||
config[i].choices = config[i].choices()
|
||||
}
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
const picbedRoutes = [
|
||||
{
|
||||
action: IRPCActionType.PICBED_GET_CONFIG_LIST,
|
||||
handler: async (_: IIPCEvent, args: [type: string]) => {
|
||||
const config = getUploaderConfigList(args[0])
|
||||
return config
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.PICBED_DELETE_CONFIG,
|
||||
handler: async (_: IIPCEvent, args: [type: string, id: string]) => {
|
||||
const [type, id] = args
|
||||
const config = deleteUploaderConfig(type, id)
|
||||
return config
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.UPLOADER_SELECT,
|
||||
handler: async (_: IIPCEvent, args: [type: string, id: string]) => {
|
||||
const [type, id] = args
|
||||
selectUploaderConfig(type, id)
|
||||
return true
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.UPLOADER_UPDATE_CONFIG,
|
||||
handler: async (_: IIPCEvent, args: [type: string, id: string, config: IStringKeyMap]) => {
|
||||
const [type, id, config] = args
|
||||
updateUploaderConfig(type, id, config)
|
||||
return true
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.UPLOADER_RESET_CONFIG,
|
||||
handler: async (_: IIPCEvent, args: [type: string, id: string]) => {
|
||||
const [type, id] = args
|
||||
resetUploaderConfig(type, id)
|
||||
return true
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.PICBED_GET_PICBED_CONFIG,
|
||||
handler: async (_: IIPCEvent, args: [type: string]) => {
|
||||
const type = args[0]
|
||||
const name = picgo.helper.uploader.get(type)?.name || type
|
||||
if (picgo.helper.uploader.get(type)?.config) {
|
||||
const _config = picgo.helper.uploader.get(type)!.config!(picgo)
|
||||
const config = handleConfigWithFunction(_config)
|
||||
return {
|
||||
config,
|
||||
name
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
config: [],
|
||||
name
|
||||
}
|
||||
}
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
}
|
||||
]
|
||||
|
||||
picbedRouter.addBatch(picbedRoutes)
|
||||
|
||||
export {
|
||||
picbedRouter
|
||||
}
|
||||
37
src/main/events/rpc/routes/plugin/index.ts
Normal file
37
src/main/events/rpc/routes/plugin/index.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
|
||||
import { RPCRouter } from '~/events/rpc/router'
|
||||
import {
|
||||
pluginImportLocalFunc,
|
||||
pluginInstallFunc,
|
||||
pluginGetListFunc,
|
||||
pluginUpdateAllFunc
|
||||
} from '~/events/rpc/routes/plugin/utils'
|
||||
|
||||
import { IRPCActionType } from '#/types/enum'
|
||||
|
||||
const pluginRouter = new RPCRouter()
|
||||
|
||||
const pluginRoutes = [
|
||||
{
|
||||
action: IRPCActionType.PLUGIN_GET_LIST,
|
||||
handler: pluginGetListFunc
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.PLUGIN_INSTALL,
|
||||
handler: pluginInstallFunc
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.PLUGIN_IMPORT_LOCAL,
|
||||
handler: pluginImportLocalFunc
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.PLUGIN_UPDATE_ALL,
|
||||
handler: pluginUpdateAllFunc
|
||||
}
|
||||
]
|
||||
|
||||
pluginRouter.addBatch(pluginRoutes)
|
||||
|
||||
export {
|
||||
pluginRouter
|
||||
}
|
||||
227
src/main/events/rpc/routes/plugin/utils.ts
Normal file
227
src/main/events/rpc/routes/plugin/utils.ts
Normal file
@@ -0,0 +1,227 @@
|
||||
import { dialog, shell } from 'electron'
|
||||
import { IGuiMenuItem, PicGo as PicGoCore } from 'piclist'
|
||||
import path from 'path'
|
||||
|
||||
import { dbPathDir } from '@core/datastore/dbChecker'
|
||||
import picgo from '@core/picgo'
|
||||
|
||||
import shortKeyHandler from 'apis/app/shortKey/shortKeyHandler'
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import { T } from '~/i18n'
|
||||
import { showNotification } from '~/utils/common'
|
||||
|
||||
import { handleStreamlinePluginName, simpleClone } from '#/utils/common'
|
||||
import { ICOREBuildInEvent, IPicGoHelperType, IWindowList } from '#/types/enum'
|
||||
|
||||
const STORE_PATH = dbPathDir()
|
||||
|
||||
// eslint-disable-next-line
|
||||
const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require
|
||||
|
||||
// get uploader or transformer config
|
||||
const getConfig = (name: string, type: IPicGoHelperType, ctx: PicGoCore) => {
|
||||
let config: any[] = []
|
||||
if (name === '') {
|
||||
return config
|
||||
} else {
|
||||
const handler = ctx.helper[type].get(name)
|
||||
if (handler) {
|
||||
if (handler.config) {
|
||||
config = handler.config(ctx)
|
||||
}
|
||||
}
|
||||
return config
|
||||
}
|
||||
}
|
||||
|
||||
const handleConfigWithFunction = (config: any[]) => {
|
||||
for (const i in config) {
|
||||
if (typeof config[i].default === 'function') {
|
||||
config[i].default = config[i].default()
|
||||
}
|
||||
if (typeof config[i].choices === 'function') {
|
||||
config[i].choices = config[i].choices()
|
||||
}
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
const getPluginList = (): IPicGoPlugin[] => {
|
||||
const pluginList = picgo.pluginLoader.getFullList()
|
||||
const list = []
|
||||
for (const i in pluginList) {
|
||||
const plugin = picgo.pluginLoader.getPlugin(pluginList[i])!
|
||||
const pluginPath = path.join(STORE_PATH, `/node_modules/${pluginList[i]}`)
|
||||
const pluginPKG = requireFunc(path.join(pluginPath, 'package.json'))
|
||||
const uploaderName = plugin.uploader || ''
|
||||
const transformerName = plugin.transformer || ''
|
||||
let menu: Omit<IGuiMenuItem, 'handle'>[] = []
|
||||
if (plugin.guiMenu) {
|
||||
menu = plugin.guiMenu(picgo).map(item => ({
|
||||
label: item.label
|
||||
}))
|
||||
}
|
||||
let gui = false
|
||||
if (pluginPKG.keywords && pluginPKG.keywords.length > 0) {
|
||||
if (pluginPKG.keywords.includes('picgo-gui-plugin')) {
|
||||
gui = true
|
||||
}
|
||||
}
|
||||
const obj: IPicGoPlugin = {
|
||||
name: handleStreamlinePluginName(pluginList[i]),
|
||||
fullName: pluginList[i],
|
||||
author: pluginPKG.author.name || pluginPKG.author,
|
||||
description: pluginPKG.description,
|
||||
logo: 'file://' + path.join(pluginPath, 'logo.png').split(path.sep).join('/'),
|
||||
version: pluginPKG.version,
|
||||
gui,
|
||||
config: {
|
||||
plugin: {
|
||||
fullName: pluginList[i],
|
||||
name: handleStreamlinePluginName(pluginList[i]),
|
||||
config: plugin.config ? handleConfigWithFunction(plugin.config(picgo)) : []
|
||||
},
|
||||
uploader: {
|
||||
name: uploaderName,
|
||||
config: handleConfigWithFunction(getConfig(uploaderName, IPicGoHelperType.uploader, picgo))
|
||||
},
|
||||
transformer: {
|
||||
name: transformerName,
|
||||
config: handleConfigWithFunction(getConfig(uploaderName, IPicGoHelperType.transformer, picgo))
|
||||
}
|
||||
},
|
||||
enabled: picgo.getConfig(`picgoPlugins.${pluginList[i]}`),
|
||||
homepage: pluginPKG.homepage ? pluginPKG.homepage : '',
|
||||
guiMenu: menu,
|
||||
ing: false
|
||||
}
|
||||
list.push(obj)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
const handleNPMError = (): IDispose => {
|
||||
const handler = (msg: string) => {
|
||||
if (msg === 'NPM is not installed') {
|
||||
dialog.showMessageBox({
|
||||
title: T('TIPS_ERROR'),
|
||||
message: T('TIPS_INSTALL_NODE_AND_RELOAD_PICGO'),
|
||||
buttons: ['Yes']
|
||||
}).then((res) => {
|
||||
if (res.response === 0) {
|
||||
shell.openExternal('https://nodejs.org/')
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
picgo.once(ICOREBuildInEvent.FAILED, handler)
|
||||
return () => picgo.off(ICOREBuildInEvent.FAILED, handler)
|
||||
}
|
||||
|
||||
export const handlePluginUpdate = async (fullName: string | string[]) => {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const dispose = handleNPMError()
|
||||
const res = await picgo.pluginHandler.update(typeof fullName === 'string' ? [fullName] : fullName)
|
||||
if (res.success) {
|
||||
window.webContents.send('updateSuccess', res.body[0])
|
||||
} else {
|
||||
showNotification({
|
||||
title: T('PLUGIN_UPDATE_FAILED'),
|
||||
body: res.body as string
|
||||
})
|
||||
}
|
||||
window.webContents.send('hideLoading')
|
||||
dispose()
|
||||
}
|
||||
|
||||
export const handlePluginUninstall = async (fullName: string) => {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const dispose = handleNPMError()
|
||||
const res = await picgo.pluginHandler.uninstall([fullName])
|
||||
if (res.success) {
|
||||
window.webContents.send('uninstallSuccess', res.body[0])
|
||||
shortKeyHandler.unregisterPluginShortKey(res.body[0])
|
||||
} else {
|
||||
showNotification({
|
||||
title: T('PLUGIN_UNINSTALL_FAILED'),
|
||||
body: res.body as string
|
||||
})
|
||||
}
|
||||
window.webContents.send('hideLoading')
|
||||
dispose()
|
||||
}
|
||||
|
||||
export const pluginGetListFunc = async (event: IIPCEvent) => {
|
||||
try {
|
||||
const list = simpleClone(getPluginList())
|
||||
// here can just send JS Object not function
|
||||
// or will cause [Failed to serialize arguments] error
|
||||
event.sender.send('pluginList', list)
|
||||
} catch (e: any) {
|
||||
event.sender.send('pluginList', [])
|
||||
showNotification({
|
||||
title: T('TIPS_GET_PLUGIN_LIST_FAILED'),
|
||||
body: e.message
|
||||
})
|
||||
picgo.log.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
export const pluginInstallFunc = async (event: IIPCEvent, args: [fullName: string]) => {
|
||||
const fullName = args[0]
|
||||
const dispose = handleNPMError()
|
||||
const res = await picgo.pluginHandler.install([fullName])
|
||||
event.sender.send('installPlugin', {
|
||||
success: res.success,
|
||||
body: fullName,
|
||||
errMsg: res.success ? '' : res.body
|
||||
})
|
||||
if (res.success) {
|
||||
shortKeyHandler.registerPluginShortKey(res.body[0])
|
||||
} else {
|
||||
showNotification({
|
||||
title: T('PLUGIN_INSTALL_FAILED'),
|
||||
body: res.body as string
|
||||
})
|
||||
}
|
||||
event.sender.send('hideLoading')
|
||||
dispose()
|
||||
}
|
||||
|
||||
export const pluginImportLocalFunc = async (event: IIPCEvent) => {
|
||||
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const res = await dialog.showOpenDialog(settingWindow, {
|
||||
properties: ['openDirectory']
|
||||
})
|
||||
const filePaths = res.filePaths
|
||||
if (filePaths.length > 0) {
|
||||
const res = await picgo.pluginHandler.install(filePaths)
|
||||
if (res.success) {
|
||||
try {
|
||||
const list = simpleClone(getPluginList())
|
||||
event.sender.send('pluginList', list)
|
||||
} catch (e: any) {
|
||||
event.sender.send('pluginList', [])
|
||||
showNotification({
|
||||
title: T('TIPS_GET_PLUGIN_LIST_FAILED'),
|
||||
body: e.message
|
||||
})
|
||||
}
|
||||
showNotification({
|
||||
title: T('PLUGIN_IMPORT_SUCCEED'),
|
||||
body: ''
|
||||
})
|
||||
} else {
|
||||
showNotification({
|
||||
title: T('PLUGIN_IMPORT_FAILED'),
|
||||
body: res.body as string
|
||||
})
|
||||
}
|
||||
}
|
||||
event.sender.send('hideLoading')
|
||||
}
|
||||
|
||||
export const pluginUpdateAllFunc = async (_: IIPCEvent, args: [list: string[]]) => {
|
||||
handlePluginUpdate(args[0])
|
||||
}
|
||||
25
src/main/events/rpc/routes/setting/advanced.ts
Normal file
25
src/main/events/rpc/routes/setting/advanced.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
import { IRPCActionType } from '#/types/enum'
|
||||
import server from '~/server'
|
||||
import webServer from '~/server/webServer'
|
||||
|
||||
export default [
|
||||
{
|
||||
action: IRPCActionType.ADVANCED_UPDATE_SERVER,
|
||||
handler: async () => {
|
||||
server.restart()
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.ADVANCED_STOP_WEB_SERVER,
|
||||
handler: async () => {
|
||||
webServer.stop()
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.ADVANCED_RESTART_WEB_SERVER,
|
||||
handler: async () => {
|
||||
webServer.restart()
|
||||
}
|
||||
}
|
||||
]
|
||||
82
src/main/events/rpc/routes/setting/configure.ts
Normal file
82
src/main/events/rpc/routes/setting/configure.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import { app } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
|
||||
import logger from '@core/picgo/logger'
|
||||
import { downloadFile, uploadFile } from '~/utils/syncSettings'
|
||||
import { IRPCActionType, IRPCType } from '#/types/enum'
|
||||
|
||||
const STORE_PATH = app.getPath('userData')
|
||||
|
||||
const commonConfigList = ['data.json', 'data.bak.json']
|
||||
const manageConfigList = ['manage.json', 'manage.bak.json']
|
||||
|
||||
export default [
|
||||
{
|
||||
action: IRPCActionType.CONFIGURE_MIGRATE_FROM_PICGO,
|
||||
handler: async () => {
|
||||
const picGoConfigPath = STORE_PATH.replace('piclist', 'picgo')
|
||||
const files = [
|
||||
'data.json',
|
||||
'data.bak.json',
|
||||
'picgo.db',
|
||||
'picgo.bak.db'
|
||||
]
|
||||
try {
|
||||
await Promise.all(files.map(async file => {
|
||||
const sourcePath = path.join(picGoConfigPath, file)
|
||||
const targetPath = path.join(STORE_PATH, file.replace('picgo', 'piclist'))
|
||||
await fs.copy(sourcePath, targetPath, { overwrite: true })
|
||||
}
|
||||
))
|
||||
return true
|
||||
} catch (err: any) {
|
||||
logger.error(err)
|
||||
throw new Error('Migrate failed')
|
||||
}
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.CONFIGURE_UPLOAD_COMMON_CONFIG,
|
||||
handler: async () => {
|
||||
return await uploadFile(commonConfigList)
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.CONFIGURE_UPLOAD_MANAGE_CONFIG,
|
||||
handler: async () => {
|
||||
return await uploadFile(manageConfigList)
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.CONFIGURE_UPLOAD_ALL_CONFIG,
|
||||
handler: async () => {
|
||||
return await uploadFile([...commonConfigList, ...manageConfigList])
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.CONFIGURE_DOWNLOAD_COMMON_CONFIG,
|
||||
handler: async () => {
|
||||
return await downloadFile(commonConfigList)
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.CONFIGURE_DOWNLOAD_MANAGE_CONFIG,
|
||||
handler: async () => {
|
||||
return await downloadFile(manageConfigList)
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.CONFIGURE_DOWNLOAD_ALL_CONFIG,
|
||||
handler: async () => {
|
||||
return await downloadFile([...commonConfigList, ...manageConfigList])
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
}
|
||||
]
|
||||
21
src/main/events/rpc/routes/setting/index.ts
Normal file
21
src/main/events/rpc/routes/setting/index.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { RPCRouter } from '~/events/rpc/router'
|
||||
|
||||
import advancedRoutes from '~/events/rpc/routes/setting/advanced'
|
||||
import configureRoutes from '~/events/rpc/routes/setting/configure'
|
||||
import mainAppRoutes from '~/events/rpc/routes/setting/mainApp'
|
||||
import shortKeyRoutes from '~/events/rpc/routes/setting/shortKey'
|
||||
|
||||
const settingRouter = new RPCRouter()
|
||||
|
||||
const settingRoutes = [
|
||||
...advancedRoutes,
|
||||
...configureRoutes,
|
||||
...mainAppRoutes,
|
||||
...shortKeyRoutes
|
||||
]
|
||||
|
||||
settingRouter.addBatch(settingRoutes)
|
||||
|
||||
export {
|
||||
settingRouter
|
||||
}
|
||||
65
src/main/events/rpc/routes/setting/mainApp.ts
Normal file
65
src/main/events/rpc/routes/setting/mainApp.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { app, IpcMainEvent, shell } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
|
||||
import picgo from '@core/picgo'
|
||||
import { dbPathDir } from '@core/datastore/dbChecker'
|
||||
|
||||
import { IRPCActionType, IRPCType } from '#/types/enum'
|
||||
|
||||
const STORE_PATH = dbPathDir()
|
||||
|
||||
export default [
|
||||
{
|
||||
action: IRPCActionType.PICLIST_GET_CONFIG,
|
||||
handler: async (_: IIPCEvent, args: [key?: string]) => {
|
||||
return picgo.getConfig(args[0])
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.PICLIST_GET_CONFIG_SYNC,
|
||||
handler: async (event: IIPCEvent, args: [key?: string]) => {
|
||||
const result = picgo.getConfig(args[0])
|
||||
const eventInstance = event as IpcMainEvent
|
||||
eventInstance.returnValue = result
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.PICLIST_SAVE_CONFIG,
|
||||
handler: async (_: IIPCEvent, args: [data: IObj]) => {
|
||||
picgo.saveConfig(args[0])
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.PICLIST_OPEN_FILE,
|
||||
handler: async (_: IIPCEvent, args: [fileName: string]) => {
|
||||
const abFilePath = path.join(STORE_PATH, args[0])
|
||||
if (!fs.existsSync(abFilePath)) {
|
||||
fs.writeFileSync(abFilePath, '')
|
||||
}
|
||||
shell.openPath(abFilePath)
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.PICLIST_OPEN_DIRECTORY,
|
||||
handler: async (_: IIPCEvent, args: [dirPath?: string, inStorePath?: boolean]) => {
|
||||
let [dirPath, inStorePath = true] = args
|
||||
if (inStorePath) {
|
||||
dirPath = path.join(STORE_PATH, dirPath || '')
|
||||
}
|
||||
if (!dirPath || !fs.existsSync(dirPath)) {
|
||||
return
|
||||
}
|
||||
shell.openPath(dirPath)
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.PICLIST_AUTO_START,
|
||||
handler: async (_: IIPCEvent, args: [val: boolean]) => {
|
||||
app.setLoginItemSettings({
|
||||
openAtLogin: args[0]
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
46
src/main/events/rpc/routes/setting/shortKey.ts
Normal file
46
src/main/events/rpc/routes/setting/shortKey.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import {
|
||||
Notification
|
||||
} from 'electron'
|
||||
|
||||
import bus from '@core/bus'
|
||||
import shortKeyHandler from 'apis/app/shortKey/shortKeyHandler'
|
||||
import { T } from '~/i18n'
|
||||
|
||||
import { IRPCActionType, IRPCType } from '#/types/enum'
|
||||
import { TOGGLE_SHORTKEY_MODIFIED_MODE } from '#/events/constants'
|
||||
|
||||
const notificationFunc = (result: boolean) => {
|
||||
const notification = new Notification({
|
||||
title: T(`OPERATION_${result ? 'SUCCEED' : 'FAILED'}`),
|
||||
body: T(`TIPS_SHORTCUT_MODIFIED_${result ? 'SUCCEED' : 'CONFLICT'}`)
|
||||
})
|
||||
notification.show()
|
||||
}
|
||||
|
||||
export default [
|
||||
{
|
||||
action: IRPCActionType.SHORTKEY_UPDATE,
|
||||
handler: async (_: IIPCEvent, args: [item: IShortKeyConfig, oldKey: string, from: string]) => {
|
||||
const [item, oldKey, from] = args
|
||||
const result = shortKeyHandler.updateShortKey(item, oldKey, from)
|
||||
notificationFunc(result)
|
||||
return result
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.SHORTKEY_BIND_OR_UNBIND,
|
||||
handler: async (_: IIPCEvent, args: [item: IShortKeyConfig, from: string]) => {
|
||||
const [item, from] = args
|
||||
const result = shortKeyHandler.bindOrUnbindShortKey(item, from)
|
||||
notificationFunc(result)
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.SHORTKEY_TOGGLE_SHORTKEY_MODIFIED_MODE,
|
||||
handler: async (_: IIPCEvent, args: [status: boolean]) => {
|
||||
const [status] = args
|
||||
bus.emit(TOGGLE_SHORTKEY_MODIFIED_MODE, status)
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -1,24 +0,0 @@
|
||||
import { app, clipboard, shell } from 'electron'
|
||||
|
||||
import { RPCRouter } from '~/events/rpc/router'
|
||||
import { IRPCActionType } from '#/types/enum'
|
||||
|
||||
const systemRouter = new RPCRouter()
|
||||
|
||||
systemRouter
|
||||
.add(IRPCActionType.RELOAD_APP, async () => {
|
||||
app.relaunch()
|
||||
app.exit(0)
|
||||
})
|
||||
.add(IRPCActionType.OPEN_FILE, async (args) => {
|
||||
const [filePath] = args as IOpenFileArgs
|
||||
shell.openPath(filePath)
|
||||
})
|
||||
.add(IRPCActionType.COPY_TEXT, async (args) => {
|
||||
const [text] = args as ICopyTextArgs
|
||||
return clipboard.writeText(text)
|
||||
})
|
||||
|
||||
export {
|
||||
systemRouter
|
||||
}
|
||||
62
src/main/events/rpc/routes/system/app.ts
Normal file
62
src/main/events/rpc/routes/system/app.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { app, shell } from 'electron'
|
||||
|
||||
import picgo from '@core/picgo'
|
||||
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
import { i18nManager } from '~/i18n'
|
||||
|
||||
import { IRPCActionType, IWindowList } from '#/types/enum'
|
||||
import { GET_CURRENT_LANGUAGE, GET_LANGUAGE_LIST, SET_CURRENT_LANGUAGE } from '#/events/constants'
|
||||
|
||||
export default [
|
||||
{
|
||||
action: IRPCActionType.RELOAD_APP,
|
||||
handler: async () => {
|
||||
app.relaunch()
|
||||
app.exit(0)
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.OPEN_FILE,
|
||||
handler: async (_: IIPCEvent, args: [filePath: string]) => {
|
||||
shell.openPath(args[0])
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.OPEN_URL,
|
||||
handler: async (_: IIPCEvent, args: [url: string]) => {
|
||||
shell.openExternal(args[0])
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.GET_LANGUAGE_LIST,
|
||||
handler: async (event: IIPCEvent) => {
|
||||
event.sender.send(GET_LANGUAGE_LIST, i18nManager.languageList)
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.GET_CURRENT_LANGUAGE,
|
||||
handler: async (event: IIPCEvent) => {
|
||||
const { lang, locales } = i18nManager.getCurrentLocales()
|
||||
event.sender.send(GET_CURRENT_LANGUAGE, lang, locales)
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.SET_CURRENT_LANGUAGE,
|
||||
handler: async (_: IIPCEvent, args: [language: string]) => {
|
||||
i18nManager.setCurrentLanguage(args[0])
|
||||
const { lang, locales } = i18nManager.getCurrentLocales()
|
||||
picgo.i18n.setLanguage(lang)
|
||||
if (process.platform === 'darwin') {
|
||||
const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)
|
||||
trayWindow?.webContents.send(SET_CURRENT_LANGUAGE, lang, locales)
|
||||
}
|
||||
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)
|
||||
settingWindow?.webContents.send(SET_CURRENT_LANGUAGE, lang, locales)
|
||||
if (windowManager.has(IWindowList.MINI_WINDOW)) {
|
||||
const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)
|
||||
miniWindow?.webContents.send(SET_CURRENT_LANGUAGE, lang, locales)
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
16
src/main/events/rpc/routes/system/index.ts
Normal file
16
src/main/events/rpc/routes/system/index.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { RPCRouter } from '~/events/rpc/router'
|
||||
import appRoutes from '~/events/rpc/routes/system/app'
|
||||
import windowRoutes from '~/events/rpc/routes/system/window'
|
||||
|
||||
const systemRouter = new RPCRouter()
|
||||
|
||||
const systemRoutes = [
|
||||
...appRoutes,
|
||||
...windowRoutes
|
||||
]
|
||||
|
||||
systemRouter.addBatch(systemRoutes)
|
||||
|
||||
export {
|
||||
systemRouter
|
||||
}
|
||||
137
src/main/events/rpc/routes/system/window.ts
Normal file
137
src/main/events/rpc/routes/system/window.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
import { app, BrowserWindow } from 'electron'
|
||||
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import {
|
||||
buildMainPageMenu,
|
||||
buildMiniPageMenu,
|
||||
buildPicBedListMenu,
|
||||
buildPluginPageMenu
|
||||
} from '~/events/remotes/menu'
|
||||
import { openMiniWindow } from '~/utils/windowHelper'
|
||||
|
||||
import { IRPCActionType, IWindowList } from '#/types/enum'
|
||||
|
||||
export default [
|
||||
{
|
||||
action: IRPCActionType.HIDE_DOCK,
|
||||
handler: async (_: IIPCEvent, args: [value: boolean]) => {
|
||||
args[0] ? app.dock.hide() : app.dock.show()
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.OPEN_WINDOW,
|
||||
handler: async (_: IIPCEvent, args: [windowName: IWindowList]) => {
|
||||
const window = windowManager.get(args[0])
|
||||
if (window) {
|
||||
window.show()
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.OPEN_MANUAL_WINDOW,
|
||||
handler: async () => {
|
||||
windowManager.get(IWindowList.MANUAL_WINDOW)!.show()
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.OPEN_MINI_WINDOW,
|
||||
handler: async () => {
|
||||
openMiniWindow()
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.CLOSE_WINDOW,
|
||||
handler: async () => {
|
||||
const window = BrowserWindow.getFocusedWindow()
|
||||
if (process.platform === 'linux') {
|
||||
window?.hide()
|
||||
} else {
|
||||
window?.close()
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.MINIMIZE_WINDOW,
|
||||
handler: async () => {
|
||||
const window = BrowserWindow.getFocusedWindow()
|
||||
window?.minimize()
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.SHOW_MINI_PAGE_MENU,
|
||||
handler: async () => {
|
||||
const window = windowManager.get(IWindowList.MINI_WINDOW)!
|
||||
const menu = buildMiniPageMenu()
|
||||
menu.popup({
|
||||
window
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.SHOW_MAIN_PAGE_MENU,
|
||||
handler: async () => {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const menu = buildMainPageMenu(window)
|
||||
menu.popup({
|
||||
window
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.SHOW_UPLOAD_PAGE_MENU,
|
||||
handler: async () => {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const menu = buildPicBedListMenu()
|
||||
menu.popup({
|
||||
window
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.SHOW_PLUGIN_PAGE_MENU,
|
||||
handler: async (_: IIPCEvent, args: [plugin: IPicGoPlugin]) => {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const menu = buildPluginPageMenu(args[0])
|
||||
menu.popup({
|
||||
window
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.SET_MINI_WINDOW_POS,
|
||||
handler: async (_: IIPCEvent, args: [pos: IMiniWindowPos]) => {
|
||||
const window = BrowserWindow.getFocusedWindow()
|
||||
window?.setBounds(args[0])
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.MINI_WINDOW_ON_TOP,
|
||||
handler: async (_: IIPCEvent, args: [isOnTop: boolean]) => {
|
||||
const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)!
|
||||
miniWindow.setAlwaysOnTop(args[0])
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.MAIN_WINDOW_ON_TOP,
|
||||
handler: async () => {
|
||||
const mainWindow = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const isAlwaysOnTop = mainWindow.isAlwaysOnTop()
|
||||
mainWindow.setAlwaysOnTop(!isAlwaysOnTop)
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.UPDATE_MINI_WINDOW_ICON,
|
||||
handler: async (_: IIPCEvent, args: [iconPath: string]) => {
|
||||
const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)!
|
||||
miniWindow.webContents.send('updateMiniIcon', args[0])
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.REFRESH_SETTING_WINDOW,
|
||||
handler: async () => {
|
||||
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
settingWindow.webContents.reloadIgnoringCache()
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -11,7 +11,7 @@ import { T } from '~/i18n'
|
||||
|
||||
import { IToolboxItemCheckStatus, IToolboxItemType } from '#/types/enum'
|
||||
|
||||
const getProxy = (proxyStr: string): AxiosRequestConfig['proxy'] | false => {
|
||||
function getProxy (proxyStr: string): AxiosRequestConfig['proxy'] | null {
|
||||
if (proxyStr) {
|
||||
try {
|
||||
const proxyOptions = new URL(proxyStr)
|
||||
@@ -20,10 +20,9 @@ const getProxy = (proxyStr: string): AxiosRequestConfig['proxy'] | false => {
|
||||
port: parseInt(proxyOptions.port || '0', 10),
|
||||
protocol: proxyOptions.protocol
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
return false
|
||||
return null
|
||||
}
|
||||
|
||||
const sendToolboxRes = sendToolboxResWithType(IToolboxItemType.HAS_PROBLEM_WITH_PROXY)
|
||||
|
||||
@@ -3,7 +3,8 @@ import { checkFileMap, fixFileMap } from '~/events/rpc/routes/toolbox/checkFile'
|
||||
import { checkProxyMap } from '~/events/rpc/routes/toolbox/checkProxy'
|
||||
import { RPCRouter } from '~/events/rpc/router'
|
||||
|
||||
import { IRPCActionType, IToolboxItemType } from '#/types/enum'
|
||||
import { IRPCActionType, IRPCType, IToolboxItemType } from '#/types/enum'
|
||||
import { IpcMainEvent } from 'electron'
|
||||
|
||||
const toolboxRouter = new RPCRouter()
|
||||
|
||||
@@ -19,30 +20,37 @@ const toolboxFixMap: Partial<IToolboxFixMap<IToolboxItemType>> = {
|
||||
}
|
||||
|
||||
toolboxRouter
|
||||
.add(IRPCActionType.TOOLBOX_CHECK, async (args, event) => {
|
||||
const [type] = args as IToolboxCheckArgs
|
||||
if (type) {
|
||||
const handler = toolboxCheckMap[type]
|
||||
if (handler) {
|
||||
handler(event)
|
||||
}
|
||||
} else {
|
||||
// do check all
|
||||
for (const key in toolboxCheckMap) {
|
||||
const handler = toolboxCheckMap[key as IToolboxItemType]
|
||||
.add(
|
||||
IRPCActionType.TOOLBOX_CHECK,
|
||||
async (event, args) => {
|
||||
const [type] = args as IToolboxCheckArgs
|
||||
if (type) {
|
||||
const handler = toolboxCheckMap[type]
|
||||
if (handler) {
|
||||
handler(event)
|
||||
handler(event as IpcMainEvent)
|
||||
}
|
||||
} else {
|
||||
// do check all
|
||||
for (const key in toolboxCheckMap) {
|
||||
const handler = toolboxCheckMap[key as IToolboxItemType]
|
||||
if (handler) {
|
||||
handler(event as IpcMainEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.add(IRPCActionType.TOOLBOX_CHECK_FIX, async (args, event) => {
|
||||
const [type] = args as IToolboxCheckArgs
|
||||
const handler = toolboxFixMap[type]
|
||||
if (handler) {
|
||||
return await handler(event)
|
||||
}
|
||||
})
|
||||
},
|
||||
IRPCType.SEND
|
||||
)
|
||||
.add(
|
||||
IRPCActionType.TOOLBOX_CHECK_FIX, async (event, args) => {
|
||||
const [type] = args as IToolboxCheckArgs
|
||||
const handler = toolboxFixMap[type]
|
||||
if (handler) {
|
||||
return await handler(event as IpcMainEvent)
|
||||
}
|
||||
},
|
||||
IRPCType.INVOKE
|
||||
)
|
||||
|
||||
export {
|
||||
toolboxRouter
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { IpcMainEvent } from 'electron'
|
||||
import { IRPCActionType, IToolboxItemType } from '#/types/enum'
|
||||
|
||||
export const sendToolboxResWithType = (type: IToolboxItemType) => (event: IpcMainEvent, res?: Omit<IToolboxCheckRes, 'type'>) => {
|
||||
return event.sender.send(IRPCActionType.TOOLBOX_CHECK_RES, {
|
||||
...res,
|
||||
type
|
||||
})
|
||||
export function sendToolboxResWithType (type: IToolboxItemType) {
|
||||
return (event: IpcMainEvent, res?: Omit<IToolboxCheckRes, 'type'>) => {
|
||||
return event.sender.send(IRPCActionType.TOOLBOX_CHECK_RES, {
|
||||
...res,
|
||||
type
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
71
src/main/events/rpc/routes/tray/index.ts
Normal file
71
src/main/events/rpc/routes/tray/index.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import {
|
||||
Notification
|
||||
} from 'electron'
|
||||
|
||||
import { RPCRouter } from '~/events/rpc/router'
|
||||
import { generateShortUrl, setTrayToolTip, handleCopyUrl } from '~/utils/common'
|
||||
|
||||
import { IRPCActionType, IRPCType, IPasteStyle, IWindowList } from '#/types/enum'
|
||||
|
||||
import db, { GalleryDB } from '@core/datastore'
|
||||
|
||||
import uploader from 'apis/app/uploader'
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import { T } from '~/i18n'
|
||||
|
||||
import pasteTemplate from '~/utils/pasteTemplate'
|
||||
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
|
||||
const trayRouter = new RPCRouter()
|
||||
|
||||
const trayRoutes = [
|
||||
{
|
||||
action: IRPCActionType.TRAY_SET_TOOL_TIP,
|
||||
handler: async (_: IIPCEvent, args: [text: string]) => {
|
||||
setTrayToolTip(args[0])
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.TRAY_GET_SHORT_URL,
|
||||
handler: async (_: IIPCEvent, args: [url: string]) => {
|
||||
return await generateShortUrl(args[0])
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.TRAY_UPLOAD_CLIPBOARD_FILES,
|
||||
handler: async () => {
|
||||
const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)!
|
||||
// macOS use builtin clipboard is OK
|
||||
const img = await uploader.setWebContents(trayWindow.webContents).uploadWithBuildInClipboard()
|
||||
if (img !== false) {
|
||||
const pasteStyle = db.get(configPaths.settings.pasteStyle) || IPasteStyle.MARKDOWN
|
||||
handleCopyUrl(await (pasteTemplate(pasteStyle, img[0], db.get(configPaths.settings.customLink))))
|
||||
const isShowResultNotification = db.get(configPaths.settings.uploadResultNotification) === undefined ? true : !!db.get(configPaths.settings.uploadResultNotification)
|
||||
if (isShowResultNotification) {
|
||||
const notification = new Notification({
|
||||
title: T('UPLOAD_SUCCEED'),
|
||||
body: img[0].imgUrl!
|
||||
// icon: file[0]
|
||||
// icon: img[0].imgUrl
|
||||
})
|
||||
notification.show()
|
||||
}
|
||||
await GalleryDB.getInstance().insert(img[0])
|
||||
trayWindow.webContents.send('clipboardFiles', [])
|
||||
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
|
||||
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('updateGallery')
|
||||
}
|
||||
}
|
||||
trayWindow.webContents.send('uploadFiles')
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
trayRouter.addBatch(trayRoutes)
|
||||
|
||||
export {
|
||||
trayRouter
|
||||
}
|
||||
35
src/main/events/rpc/routes/upload/index.ts
Normal file
35
src/main/events/rpc/routes/upload/index.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { RPCRouter } from '~/events/rpc/router'
|
||||
import getPicBeds from '~/utils/getPicBeds'
|
||||
import { uploadChoosedFiles, uploadClipboardFiles } from '~/apis/app/uploader/apis'
|
||||
|
||||
import { IRPCActionType, IRPCType } from '#/types/enum'
|
||||
|
||||
const uploadRouter = new RPCRouter()
|
||||
|
||||
const uploadRoutes = [
|
||||
{
|
||||
action: IRPCActionType.MAIN_GET_PICBED,
|
||||
handler: async () => {
|
||||
return getPicBeds()
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.UPLOAD_CLIPBOARD_FILES_FROM_UPLOAD_PAGE,
|
||||
handler: async () => {
|
||||
uploadClipboardFiles()
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.UPLOAD_CHOOSED_FILES,
|
||||
handler: async (evt: IIPCEvent, args: [files: IFileWithPath[]]) => {
|
||||
return uploadChoosedFiles(evt.sender, args[0])
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
uploadRouter.addBatch(uploadRoutes)
|
||||
|
||||
export {
|
||||
uploadRouter
|
||||
}
|
||||
@@ -10,8 +10,10 @@ fs.ensureDirSync(imgFilePath)
|
||||
|
||||
const serverPort = 36699
|
||||
|
||||
let server: http.Server
|
||||
|
||||
export function startFileServer () {
|
||||
const server = http.createServer((req, res) => {
|
||||
server = http.createServer((req, res) => {
|
||||
const requestPath = req.url?.split('?')[0]
|
||||
const filePath = path.join(imgFilePath, decodeURIComponent(requestPath as string))
|
||||
|
||||
@@ -31,3 +33,9 @@ export function startFileServer () {
|
||||
logger.error(err)
|
||||
})
|
||||
}
|
||||
|
||||
export function stopFileServer () {
|
||||
server.close(() => {
|
||||
logger.info('File server is stopped')
|
||||
})
|
||||
}
|
||||
|
||||
@@ -32,8 +32,7 @@ import {
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import busEventList from '~/events/busEventList'
|
||||
import ipcList from '~/events/ipcList'
|
||||
import { startFileServer } from '~/fileServer'
|
||||
import { startFileServer, stopFileServer } from '~/fileServer'
|
||||
import { T } from '~/i18n'
|
||||
import '~/lifeCycle/errorHandler'
|
||||
import fixPath from '~/lifeCycle/fixPath'
|
||||
@@ -52,6 +51,7 @@ import updateChecker from '~/utils/updateChecker'
|
||||
import { II18nLanguage, IRemoteNoticeTriggerHook, ISartMode, IWindowList } from '#/types/enum'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
import { CLIPBOARD_IMAGE_FOLDER } from '#/utils/static'
|
||||
import { rpcServer } from '~/events/rpc'
|
||||
|
||||
const isDevelopment = process.env.NODE_ENV !== 'production'
|
||||
|
||||
@@ -146,7 +146,7 @@ class LifeCycle {
|
||||
fixPath()
|
||||
beforeOpen()
|
||||
initI18n()
|
||||
ipcList.listen()
|
||||
rpcServer.start()
|
||||
getManageApi()
|
||||
UpDownTaskQueue.getInstance()
|
||||
manageIpcList.listen()
|
||||
@@ -285,6 +285,8 @@ class LifeCycle {
|
||||
globalShortcut.unregisterAll()
|
||||
bus.removeAllListeners()
|
||||
server.shutdown()
|
||||
webServer.stop()
|
||||
stopFileServer()
|
||||
})
|
||||
// Exit cleanly on request from parent process in development mode.
|
||||
if (isDevelopment) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
IpcMainEvent,
|
||||
IpcMainInvokeEvent,
|
||||
ipcMain
|
||||
} from 'electron'
|
||||
|
||||
@@ -9,20 +10,19 @@ import { PICLIST_MANAGE_GET_CONFIG, PICLIST_MANAGE_SAVE_CONFIG, PICLIST_MANAGE_R
|
||||
const manageApi = getManageApi()
|
||||
|
||||
const handleManageGetConfig = () => {
|
||||
ipcMain.on(PICLIST_MANAGE_GET_CONFIG, (event: IpcMainEvent, key: string | undefined, callbackId: string) => {
|
||||
const result = manageApi.getConfig(key)
|
||||
event.sender.send(PICLIST_MANAGE_GET_CONFIG, result, callbackId)
|
||||
ipcMain.handle(PICLIST_MANAGE_GET_CONFIG, (_: IpcMainInvokeEvent, key: string | undefined) => {
|
||||
return manageApi.getConfig(key)
|
||||
})
|
||||
}
|
||||
|
||||
const handleManageSaveConfig = () => {
|
||||
ipcMain.on(PICLIST_MANAGE_SAVE_CONFIG, (_event: IpcMainEvent, data: any) => {
|
||||
ipcMain.on(PICLIST_MANAGE_SAVE_CONFIG, (_: IpcMainEvent, data: any) => {
|
||||
manageApi.saveConfig(data)
|
||||
})
|
||||
}
|
||||
|
||||
const handleManageRemoveConfig = () => {
|
||||
ipcMain.on(PICLIST_MANAGE_REMOVE_CONFIG, (_event: IpcMainEvent, key: string, propName: string) => {
|
||||
ipcMain.on(PICLIST_MANAGE_REMOVE_CONFIG, (_: IpcMainEvent, key: string, propName: string) => {
|
||||
manageApi.removeConfig(key, propName)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -11,12 +11,13 @@ import logger from '@core/picgo/logger'
|
||||
import { AESHelper } from '~/utils/aesHelper'
|
||||
import { changeCurrentUploader } from '~/utils/handleUploaderConfig'
|
||||
|
||||
import { uploadChoosedFiles, uploadClipboardFiles, deleteChoosedFiles } from 'apis/app/uploader/apis'
|
||||
import { uploadChoosedFiles, uploadClipboardFiles } from 'apis/app/uploader/apis'
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import { markdownContent } from '~/server/apiDoc'
|
||||
import router from '~/server/router'
|
||||
import {
|
||||
deleteChoosedFiles,
|
||||
handleResponse
|
||||
} from '~/server/utils'
|
||||
|
||||
|
||||
@@ -1,4 +1,20 @@
|
||||
import {
|
||||
Notification
|
||||
} from 'electron'
|
||||
|
||||
import picgo from '@core/picgo'
|
||||
import logger from '@core/picgo/logger'
|
||||
import db, { GalleryDB } from '@core/datastore'
|
||||
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import GuiApi from '~/apis/gui'
|
||||
import { T } from '~/i18n/index'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
import { picBedsCanbeDeleted } from '#/utils/static'
|
||||
import { ICOREBuildInEvent, IWindowList } from '#/types/enum'
|
||||
|
||||
import ALLApi from '@/apis/allApi'
|
||||
|
||||
export const handleResponse = ({
|
||||
response,
|
||||
@@ -31,3 +47,43 @@ export const ensureHTTPLink = (url: string): string => {
|
||||
? url
|
||||
: `http://${url}`
|
||||
}
|
||||
|
||||
export const deleteChoosedFiles = async (list: ImgInfo[]): Promise<boolean[]> => {
|
||||
const result = []
|
||||
for (const item of list) {
|
||||
if (item.id) {
|
||||
try {
|
||||
const dbStore = GalleryDB.getInstance()
|
||||
const file = await dbStore.getById(item.id)
|
||||
await dbStore.removeById(item.id)
|
||||
if (await db.get(configPaths.settings.deleteCloudFile)) {
|
||||
if (item.type !== undefined && picBedsCanbeDeleted.includes(item.type)) {
|
||||
const noteFunc = (value: boolean) => {
|
||||
const notification = new Notification({
|
||||
title: T('MANAGE_BUCKET_BATCH_DELETE_ERROR_MSG_MSG2'),
|
||||
body: T(value
|
||||
? 'GALLERY_SYNC_DELETE_NOTICE_SUCCEED'
|
||||
: 'GALLERY_SYNC_DELETE_NOTICE_FAILED'
|
||||
)
|
||||
})
|
||||
notification.show()
|
||||
}
|
||||
setTimeout(() => {
|
||||
ALLApi.delete(item).then(noteFunc)
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
setTimeout(() => {
|
||||
picgo.emit(ICOREBuildInEvent.REMOVE, [file], GuiApi.getInstance())
|
||||
}, 500)
|
||||
result.push(true)
|
||||
} catch (e) {
|
||||
result.push(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
|
||||
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents?.send('updateGallery')
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -2,10 +2,13 @@ import axios from 'axios'
|
||||
import crypto from 'crypto'
|
||||
import http, { AgentOptions } from 'http'
|
||||
import https from 'https'
|
||||
import path from 'path'
|
||||
import { ISftpPlistConfig } from 'piclist'
|
||||
import querystring from 'querystring'
|
||||
import { S3Client, DeleteObjectCommand, S3ClientConfig } from '@aws-sdk/client-s3'
|
||||
import { NodeHttpHandler } from '@smithy/node-http-handler'
|
||||
|
||||
import SSHClient from '~/utils/sshClient'
|
||||
import { getAgent } from '~/manage/utils/common'
|
||||
|
||||
interface DogecloudTokenFull {
|
||||
@@ -219,3 +222,18 @@ export async function removeFileFromHuaweiInMain (configMap: IStringKeyMap) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export async function removeFileFromSFTPInMain (config: ISftpPlistConfig, fileName: string) {
|
||||
try {
|
||||
const client = SSHClient.instance
|
||||
await client.connect(config)
|
||||
const uploadPath = `/${(config.uploadPath || '')}/`.replace(/\/+/g, '/')
|
||||
const remote = path.join(uploadPath, fileName)
|
||||
const deleteResult = await client.deleteFileSFTP(config, remote)
|
||||
client.close()
|
||||
return deleteResult
|
||||
} catch (err: any) {
|
||||
console.log(err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
56
src/main/utils/windowHelper.ts
Normal file
56
src/main/utils/windowHelper.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { screen } from 'electron'
|
||||
|
||||
import db from '@core/datastore'
|
||||
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import { IWindowList } from '#/types/enum'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
|
||||
export function openMiniWindow (hideSettingWindow:boolean = true) {
|
||||
const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)!
|
||||
miniWindow.removeAllListeners()
|
||||
if (db.get(configPaths.settings.miniWindowOntop)) {
|
||||
miniWindow.setAlwaysOnTop(true)
|
||||
}
|
||||
const { width, height } = screen.getPrimaryDisplay().workAreaSize
|
||||
const lastPosition = db.get(configPaths.settings.miniWindowPosition)
|
||||
if (lastPosition) {
|
||||
miniWindow.setPosition(lastPosition[0], lastPosition[1])
|
||||
} else {
|
||||
miniWindow.setPosition(width - 100, height - 100)
|
||||
}
|
||||
const setPositionFunc = () => {
|
||||
const position = miniWindow.getPosition()
|
||||
db.set(configPaths.settings.miniWindowPosition, position)
|
||||
}
|
||||
miniWindow.on('close', setPositionFunc)
|
||||
miniWindow.on('move', setPositionFunc)
|
||||
miniWindow.show()
|
||||
miniWindow.focus()
|
||||
if (hideSettingWindow) {
|
||||
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
settingWindow.hide()
|
||||
} else {
|
||||
const autoCloseMainWindow = db.get(configPaths.settings.autoCloseMainWindow) || false
|
||||
if (windowManager.has(IWindowList.SETTING_WINDOW) && autoCloseMainWindow) {
|
||||
windowManager.get(IWindowList.SETTING_WINDOW)!.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const openMainWindow = () => {
|
||||
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)
|
||||
const autoCloseMiniWindow = db.get(configPaths.settings.autoCloseMiniWindow) || false
|
||||
settingWindow!.show()
|
||||
settingWindow!.focus()
|
||||
if (windowManager.has(IWindowList.MINI_WINDOW) && autoCloseMiniWindow) {
|
||||
windowManager.get(IWindowList.MINI_WINDOW)!.hide()
|
||||
}
|
||||
}
|
||||
|
||||
export const hideMiniWindow = () => {
|
||||
if (windowManager.has(IWindowList.MINI_WINDOW)) {
|
||||
windowManager.get(IWindowList.MINI_WINDOW)!.hide()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user