diff --git a/build-config/main/webpack.config.base.js b/build-config/main/webpack.config.base.js index 01a2d03d..fc5be7d5 100644 --- a/build-config/main/webpack.config.base.js +++ b/build-config/main/webpack.config.base.js @@ -9,6 +9,8 @@ module.exports = { }, resolve: { alias: { + '@': path.join(__dirname, '../../src/main'), + events: path.join(__dirname, '../../src/main/events'), common: path.join(__dirname, '../../src/common'), }, extensions: ['*', '.js', '.json', '.node'], diff --git a/package-lock.json b/package-lock.json index b70b4b86..854ef381 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "lx-music-desktop", - "version": "0.18.0", + "version": "0.17.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/src/main/events/_name.js b/src/main/events/_name.js new file mode 100644 index 00000000..95d4d37c --- /dev/null +++ b/src/main/events/_name.js @@ -0,0 +1,5 @@ +exports.tray = { + create: 'create', + destroy: 'destroy', +} + diff --git a/src/main/events/index.js b/src/main/events/index.js index 0ad3cfed..15015415 100644 --- a/src/main/events/index.js +++ b/src/main/events/index.js @@ -1,13 +1,5 @@ +global.lx_event = {} -require('./request') -// require('./appName') -require('./progressBar') -require('./trafficLight') -require('./musicMeta') -require('./selectDir') -require('./setWindowSize') -require('./showSaveDialog') -require('./clearCache') -require('./getCacheSize') -require('./setIgnoreMouseEvent') -require('./getEnvParams') +const Tray = require('./tray') + +if (!global.lx_event.setting) global.lx_event.tray = new Tray() diff --git a/src/main/events/tray.js b/src/main/events/tray.js new file mode 100644 index 00000000..39867f43 --- /dev/null +++ b/src/main/events/tray.js @@ -0,0 +1,14 @@ +const { EventEmitter } = require('events') +const { tray: TRAY_EVENT_NAME } = require('./_name') + +class Tray extends EventEmitter { + create() { + this.emit(TRAY_EVENT_NAME.create) + } + + destroy() { + this.emit(TRAY_EVENT_NAME.destroy) + } +} + +module.exports = Tray diff --git a/src/main/index.js b/src/main/index.js index 744324e6..177d58d3 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -1,4 +1,4 @@ -const { app, BrowserWindow, Menu, shell } = require('electron') +const { app, BrowserWindow, shell } = require('electron') const path = require('path') // 单例应用程序 @@ -15,7 +15,7 @@ app.on('second-instance', (event, argv, cwd) => { } }) -const isDev = process.env.NODE_ENV !== 'production' +const isDev = global.isDev = process.env.NODE_ENV !== 'production' app.on('web-contents-created', (event, contents) => { contents.on('will-navigate', (event, navigationUrl) => { @@ -34,23 +34,23 @@ app.on('web-contents-created', (event, contents) => { // https://github.com/electron/electron/issues/18397 app.allowRendererProcessReuse = !isDev -const { getWindowSizeInfo, parseEnv } = require('./utils') +const { getAppSetting, parseEnv, getWindowSizeInfo } = require('./utils') global.envParams = parseEnv() require('../common/error') require('./events') -const winEvent = require('./events/winEvent') +require('./rendererEvents') +const winEvent = require('./rendererEvents/winEvent') const autoUpdate = require('./utils/autoUpdate') -const { isLinux, isMac } = require('../common/utils') - +const { isMac, isLinux } = require('../common/utils') let mainWindow let winURL -let isFirstCheckedUpdate = true if (isDev) { - global.__static = path.join(__dirname, '../static') + // eslint-disable-next-line no-undef + global.__static = __static winURL = 'http://localhost:9080' } else { global.__static = path.join(__dirname, '/static') @@ -58,7 +58,7 @@ if (isDev) { } function createWindow() { - let windowSizeInfo = getWindowSizeInfo() + const windowSizeInfo = getWindowSizeInfo(global.appSetting) /** * Initial window options */ @@ -86,61 +86,27 @@ function createWindow() { winEvent(mainWindow) // mainWindow.webContents.openDevTools() - if (!isDev) { - autoUpdate(isFirstCheckedUpdate) - isFirstCheckedUpdate = false - } + if (!isDev) autoUpdate() } -if (isMac) { - const template = [ - { - label: app.getName(), - submenu: [ - { label: '关于洛雪音乐', role: 'about' }, - { type: 'separator' }, - { label: '隐藏', role: 'hide' }, - { label: '显示其他', role: 'hideothers' }, - { label: '显示全部', role: 'unhide' }, - { type: 'separator' }, - { label: '退出', accelerator: 'Command+Q', click: () => app.quit() }, - ], - }, - { - label: '窗口', - role: 'window', - submenu: [ - { label: '最小化', role: 'minimize' }, - { label: '关闭', role: 'close' }, - ], - }, - { - label: '编辑', - submenu: [ - { label: '撤销', accelerator: 'CmdOrCtrl+Z', role: 'undo' }, - { label: '恢复', accelerator: 'Shift+CmdOrCtrl+Z', role: 'redo' }, - { type: 'separator' }, - { label: '剪切', accelerator: 'CmdOrCtrl+X', role: 'cut' }, - { label: '复制', accelerator: 'CmdOrCtrl+C', role: 'copy' }, - { label: '粘贴', accelerator: 'CmdOrCtrl+V', role: 'paste' }, - { label: '选择全部', accelerator: 'CmdOrCtrl+A', role: 'selectAll' }, - ], - }, - ] - - Menu.setApplicationMenu(Menu.buildFromTemplate(template)) -} else { - Menu.setApplicationMenu(null) +function init() { + global.appSetting = getAppSetting() + createWindow() + global.lx_event.tray.create() } -app.on('ready', createWindow) - -app.on('window-all-closed', () => { - if (!isMac) app.quit() -}) +app.on('ready', init) app.on('activate', () => { - if (mainWindow === null) { - createWindow() + if (mainWindow === null) return init() +}) + +app.on('window-all-closed', () => { + if (isMac) { + global.lx_event.tray.destroy() + } else { + app.quit() } }) + +require('./modules') diff --git a/src/main/modules/appMenu.js b/src/main/modules/appMenu.js new file mode 100644 index 00000000..89bfd837 --- /dev/null +++ b/src/main/modules/appMenu.js @@ -0,0 +1,43 @@ +const { app, Menu } = require('electron') +const { isMac } = require('../../common/utils') + +if (isMac) { + const template = [ + { + label: app.getName(), + submenu: [ + { label: '关于洛雪音乐', role: 'about' }, + { type: 'separator' }, + { label: '隐藏', role: 'hide' }, + { label: '显示其他', role: 'hideothers' }, + { label: '显示全部', role: 'unhide' }, + { type: 'separator' }, + { label: '退出', accelerator: 'Command+Q', click: () => app.quit() }, + ], + }, + { + label: '窗口', + role: 'window', + submenu: [ + { label: '最小化', role: 'minimize' }, + { label: '关闭', role: 'close' }, + ], + }, + { + label: '编辑', + submenu: [ + { label: '撤销', accelerator: 'CmdOrCtrl+Z', role: 'undo' }, + { label: '恢复', accelerator: 'Shift+CmdOrCtrl+Z', role: 'redo' }, + { type: 'separator' }, + { label: '剪切', accelerator: 'CmdOrCtrl+X', role: 'cut' }, + { label: '复制', accelerator: 'CmdOrCtrl+C', role: 'copy' }, + { label: '粘贴', accelerator: 'CmdOrCtrl+V', role: 'paste' }, + { label: '选择全部', accelerator: 'CmdOrCtrl+A', role: 'selectAll' }, + ], + }, + ] + + Menu.setApplicationMenu(Menu.buildFromTemplate(template)) +} else { + Menu.setApplicationMenu(null) +} diff --git a/src/main/modules/index.js b/src/main/modules/index.js new file mode 100644 index 00000000..bd79f0da --- /dev/null +++ b/src/main/modules/index.js @@ -0,0 +1,2 @@ +require('./appMenu') +require('./tray') diff --git a/src/main/modules/tray.js b/src/main/modules/tray.js new file mode 100644 index 00000000..5337c21c --- /dev/null +++ b/src/main/modules/tray.js @@ -0,0 +1,42 @@ +const { Tray, Menu } = require('electron') +const { isMac, isWin } = require('../../common/utils') +const { tray: TRAY_EVENT_NAME } = require('../events/_name') +const path = require('path') +global.lx_event.tray.on(TRAY_EVENT_NAME.create, () => { + createTray() +}) +global.lx_event.tray.on(TRAY_EVENT_NAME.destroy, () => { + destroyTray() +}) + +let tray +function createTray() { + if (tray && !tray.isDestroyed() && global.appSetting.tray && global.appSetting.tray.isShow) return + + const iconPath = path.join(global.__static, 'images/tray', isWin ? '256x256.ico' : isMac ? '512x512.icns' : '512x512.png') + + // 托盘 + tray = new Tray(iconPath) + + const contextMenu = Menu.buildFromTemplate([ + { + label: '退出', + role: 'quit', + }, + ]) + tray.setToolTip('洛雪音乐助手') + tray.setContextMenu(contextMenu) + tray.on('click', () => { + const mainWindow = global.mainWindow + if (!mainWindow) return + mainWindow.isVisible() + ? mainWindow.focus() + : mainWindow.show() + }) +} + +function destroyTray() { + if (!tray) return + tray.destroy() + tray = null +} diff --git a/src/main/events/appName.js b/src/main/rendererEvents/appName.js similarity index 100% rename from src/main/events/appName.js rename to src/main/rendererEvents/appName.js diff --git a/src/main/events/clearCache.js b/src/main/rendererEvents/clearCache.js similarity index 100% rename from src/main/events/clearCache.js rename to src/main/rendererEvents/clearCache.js diff --git a/src/main/events/getCacheSize.js b/src/main/rendererEvents/getCacheSize.js similarity index 100% rename from src/main/events/getCacheSize.js rename to src/main/rendererEvents/getCacheSize.js diff --git a/src/main/events/getEnvParams.js b/src/main/rendererEvents/getEnvParams.js similarity index 100% rename from src/main/events/getEnvParams.js rename to src/main/rendererEvents/getEnvParams.js diff --git a/src/main/rendererEvents/index.js b/src/main/rendererEvents/index.js new file mode 100644 index 00000000..a555a5bd --- /dev/null +++ b/src/main/rendererEvents/index.js @@ -0,0 +1,14 @@ + +require('./request') +// require('./appName') +require('./progressBar') +require('./trafficLight') +require('./musicMeta') +require('./selectDir') +require('./setWindowSize') +require('./showSaveDialog') +require('./clearCache') +require('./getCacheSize') +require('./setIgnoreMouseEvent') +require('./getEnvParams') +require('./tray') diff --git a/src/main/events/musicMeta.js b/src/main/rendererEvents/musicMeta.js similarity index 100% rename from src/main/events/musicMeta.js rename to src/main/rendererEvents/musicMeta.js diff --git a/src/main/events/progressBar.js b/src/main/rendererEvents/progressBar.js similarity index 100% rename from src/main/events/progressBar.js rename to src/main/rendererEvents/progressBar.js diff --git a/src/main/events/request.js b/src/main/rendererEvents/request.js similarity index 100% rename from src/main/events/request.js rename to src/main/rendererEvents/request.js diff --git a/src/main/events/restartWindow.js b/src/main/rendererEvents/restartWindow.js similarity index 100% rename from src/main/events/restartWindow.js rename to src/main/rendererEvents/restartWindow.js diff --git a/src/main/events/selectDir.js b/src/main/rendererEvents/selectDir.js similarity index 100% rename from src/main/events/selectDir.js rename to src/main/rendererEvents/selectDir.js diff --git a/src/main/events/setIgnoreMouseEvent.js b/src/main/rendererEvents/setIgnoreMouseEvent.js similarity index 100% rename from src/main/events/setIgnoreMouseEvent.js rename to src/main/rendererEvents/setIgnoreMouseEvent.js diff --git a/src/main/events/setWindowSize.js b/src/main/rendererEvents/setWindowSize.js similarity index 100% rename from src/main/events/setWindowSize.js rename to src/main/rendererEvents/setWindowSize.js diff --git a/src/main/events/showSaveDialog.js b/src/main/rendererEvents/showSaveDialog.js similarity index 100% rename from src/main/events/showSaveDialog.js rename to src/main/rendererEvents/showSaveDialog.js diff --git a/src/main/events/trafficLight.js b/src/main/rendererEvents/trafficLight.js similarity index 73% rename from src/main/events/trafficLight.js rename to src/main/rendererEvents/trafficLight.js index 2be309f5..d6e02b29 100644 --- a/src/main/events/trafficLight.js +++ b/src/main/rendererEvents/trafficLight.js @@ -1,7 +1,6 @@ // const { app } = require('electron') const { mainOn } = require('../../common/ipc') - mainOn('min', event => { if (global.mainWindow) { global.mainWindow.minimize() @@ -12,10 +11,11 @@ mainOn('max', event => { global.mainWindow.maximize() } }) -mainOn('close', event => { +mainOn('close', (event, params) => { if (global.mainWindow) { + // console.log('close', params) + if (params && params.isToTray) return global.mainWindow.hide() // global.mainWindowdow.destroy() - // console.log('close') // app.quit() global.mainWindow.close() } diff --git a/src/main/rendererEvents/tray.js b/src/main/rendererEvents/tray.js new file mode 100644 index 00000000..127abf4f --- /dev/null +++ b/src/main/rendererEvents/tray.js @@ -0,0 +1,17 @@ +// const { app } = require('electron') +const { mainOn } = require('../../common/ipc') + +mainOn('changeTray', (event, params) => { + switch (params.action) { + case 'create': + global.lx_event.tray.create() + break + case 'destroy': + global.lx_event.tray.destroy() + break + + default: + break + } +}) + diff --git a/src/main/events/winEvent.js b/src/main/rendererEvents/winEvent.js similarity index 100% rename from src/main/events/winEvent.js rename to src/main/rendererEvents/winEvent.js diff --git a/src/main/utils/autoUpdate.js b/src/main/utils/autoUpdate.js index 84ddd8d8..2e2cc857 100644 --- a/src/main/utils/autoUpdate.js +++ b/src/main/utils/autoUpdate.js @@ -5,6 +5,9 @@ const { mainOn } = require('../../common/ipc') autoUpdater.logger = log // autoUpdater.autoDownload = false autoUpdater.logger.transports.file.level = 'info' + +let isFirstCheckedUpdate = true + log.info('App starting...') @@ -67,7 +70,7 @@ const handleSendEvent = action => { } } -module.exports = isFirstCheckedUpdate => { +module.exports = () => { if (!isFirstCheckedUpdate) { if (waitEvent.length) { waitEvent.forEach((event, index) => { @@ -79,6 +82,8 @@ module.exports = isFirstCheckedUpdate => { } return } + isFirstCheckedUpdate = false + autoUpdater.on('checking-for-update', () => { sendStatusToWindow('Checking for update...') }) diff --git a/src/main/utils/index.js b/src/main/utils/index.js index 4f328a6a..1f932106 100644 --- a/src/main/utils/index.js +++ b/src/main/utils/index.js @@ -1,12 +1,22 @@ const Store = require('electron-store') const { windowSizeList } = require('../../common/config') -exports.getWindowSizeInfo = () => { - let electronStore = new Store() - const { windowSizeId = 1 } = electronStore.get('setting') || {} +exports.getWindowSizeInfo = ({ windowSizeId = 1 } = {}) => { return windowSizeList.find(i => i.id === windowSizeId) || windowSizeList[0] } +exports.getAppSetting = () => { + let electronStore = new Store() + const defaultSetting = { + windowSizeId: 1, + tray: { + isShow: false, + isToTray: false, + }, + } + return Object.assign(defaultSetting, electronStore.get('setting') || {}) +} + exports.parseEnv = () => { const params = {} const rx = /^-\w+/ diff --git a/src/renderer/components/core/Toolbar.vue b/src/renderer/components/core/Toolbar.vue index f778c611..15a3242f 100644 --- a/src/renderer/components/core/Toolbar.vue +++ b/src/renderer/components/core/Toolbar.vue @@ -119,7 +119,9 @@ export default { rendererSend('max') }, close() { - rendererSend('close') + rendererSend('close', { + isToTray: this.setting.tray.isToTray, + }) }, }, } diff --git a/src/renderer/lang/cns/view/setting.json b/src/renderer/lang/cns/view/setting.json index e0a7156d..83e05415 100644 --- a/src/renderer/lang/cns/view/setting.json +++ b/src/renderer/lang/cns/view/setting.json @@ -18,6 +18,8 @@ "basic_window_size_medium": "中", "basic_window_size_big": "大", "basic_window_size_larger": "较大", + "basic_to_tray_title": "关闭时不退出软件将其最小化到系统托盘", + "basic_to_tray": "关闭时最小化到系统托盘", "basic_lang_title": "软件显示的语言", "basic_lang": "语言", diff --git a/src/renderer/lang/cnt/view/setting.json b/src/renderer/lang/cnt/view/setting.json index 0aeead05..c2e08fb5 100644 --- a/src/renderer/lang/cnt/view/setting.json +++ b/src/renderer/lang/cnt/view/setting.json @@ -18,6 +18,8 @@ "basic_window_size_medium": "中", "basic_window_size_big": "大", "basic_window_size_larger": "較大", + "basic_to_tray_title": "關閉時不退出軟件將其最小化到系統托盤", + "basic_to_tray": "關閉時最小化到系統托盤", "basic_lang_title": "軟件顯示的語言", "basic_lang": "語言", "play": "播放設置", diff --git a/src/renderer/lang/en/view/setting.json b/src/renderer/lang/en/view/setting.json index 10f2ab94..1219f1eb 100644 --- a/src/renderer/lang/en/view/setting.json +++ b/src/renderer/lang/en/view/setting.json @@ -18,6 +18,8 @@ "basic_window_size_medium": "Medium", "basic_window_size_big": "Large", "basic_window_size_larger": "Larger", + "basic_to_tray_title": "Minimize it to the system tray without closing the software when closing", + "basic_to_tray": "Minimize to system tray when closing", "basic_lang_title": "The language displayed in the software", "basic_lang": "Language", diff --git a/src/renderer/utils/index.js b/src/renderer/utils/index.js index f52eacbf..c47a558a 100644 --- a/src/renderer/utils/index.js +++ b/src/renderer/utils/index.js @@ -175,7 +175,7 @@ export const objectDeepMerge = (target, source) => { * @param {*} setting */ export const updateSetting = (setting, version) => { - const defaultVersion = '1.0.20' + const defaultVersion = '1.0.22' if (!version) { if (setting) { version = setting.version @@ -237,6 +237,10 @@ export const updateSetting = (setting, version) => { password: '', }, }, + tray: { + isShow: false, + isToTray: false, + }, windowSizeId: 2, themeId: 0, langId: 'cns', @@ -263,6 +267,7 @@ export const updateSetting = (setting, version) => { setting = defaultSetting } if (setting.apiSource != 'temp') setting.apiSource = 'test' // 强制设置回 test 接口源 + return { setting, version: defaultVersion } } diff --git a/src/renderer/views/Setting.vue b/src/renderer/views/Setting.vue index 0454c30c..13d875eb 100644 --- a/src/renderer/views/Setting.vue +++ b/src/renderer/views/Setting.vue @@ -28,6 +28,11 @@ div.scroll(:class="$style.setting") material-checkbox(v-for="(item, index) in windowSizeList" :id="`setting_window_size_${item.id}`" name="setting_window_size" @change="handleWindowSizeChange" :class="$style.gapLeft" need v-model="current_setting.windowSizeId" :value="item.id" :label="$t('view.setting.basic_window_size_' + item.name)" :key="item.id") + dd(:title="$t('view.setting.basic_to_tray_title')") + h3 {{$t('view.setting.basic_to_tray')}} + div + material-checkbox(id="setting_to_tray" v-model="current_setting.tray.isToTray" @change="handleToTrayChange" :label="$t('view.setting.is_enable')") + dd(:title="$t('view.setting.basic_lang_title')") h3 {{$t('view.setting.basic_lang')}} div @@ -366,7 +371,11 @@ export default { themeId: 0, sourceId: 0, randomAnimate: true, - apiSource: 'messoer', + tray: { + isShow: false, + isToTray: false, + }, + apiSource: 'temp', }, languageList, cacheSize: '0 B', @@ -617,6 +626,10 @@ export default { handleMediaDeviceChange(audioDevice) { this.setMediaDeviceId(audioDevice.deviceId) }, + handleToTrayChange(isToTray) { + this.current_setting.tray.isShow = isToTray + rendererSend('changeTray', { action: isToTray ? 'create' : 'destroy' }) + }, }, } diff --git a/src/static/images/tray/256x256.ico b/src/static/images/tray/256x256.ico new file mode 100644 index 00000000..a961683d Binary files /dev/null and b/src/static/images/tray/256x256.ico differ diff --git a/src/static/images/tray/512x512.icns b/src/static/images/tray/512x512.icns new file mode 100644 index 00000000..8bb1977e Binary files /dev/null and b/src/static/images/tray/512x512.icns differ diff --git a/src/static/images/tray/512x512.png b/src/static/images/tray/512x512.png new file mode 100644 index 00000000..6f6e3c39 Binary files /dev/null and b/src/static/images/tray/512x512.png differ