413 lines
11 KiB
TypeScript
413 lines
11 KiB
TypeScript
import { Tray, Menu, nativeImage } from 'electron'
|
|
import { isMac, isWin } from '@common/utils'
|
|
import path from 'node:path'
|
|
import {
|
|
hideWindow as hideMainWindow,
|
|
isExistWindow as isExistMainWindow,
|
|
isShowWindow as isShowMainWindow,
|
|
sendTaskbarButtonClick,
|
|
showWindow as showMainWindow,
|
|
} from './winMain'
|
|
import { quitApp } from '@main/app'
|
|
|
|
let tray: Electron.Tray | null
|
|
let isEnableTray: boolean = false
|
|
let themeId: number
|
|
let isShowStatusBarLyric: boolean = false
|
|
|
|
const playerState = {
|
|
empty: false,
|
|
collect: false,
|
|
play: false,
|
|
next: true,
|
|
prev: true,
|
|
}
|
|
|
|
const watchConfigKeys = [
|
|
'desktopLyric.enable',
|
|
'desktopLyric.isLock',
|
|
'desktopLyric.isAlwaysOnTop',
|
|
'tray.themeId',
|
|
'tray.enable',
|
|
'player.isShowStatusBarLyric',
|
|
'common.langId',
|
|
] satisfies Array<keyof LX.AppSetting>
|
|
|
|
const themeList = [
|
|
{
|
|
id: 0,
|
|
fileName: 'trayTemplate',
|
|
isNative: true,
|
|
},
|
|
{
|
|
id: 1,
|
|
fileName: 'tray_origin',
|
|
isNative: false,
|
|
},
|
|
{
|
|
id: 2,
|
|
fileName: 'tray_black',
|
|
isNative: false,
|
|
},
|
|
]
|
|
|
|
const messages = {
|
|
'en-us': {
|
|
collect: 'Collection',
|
|
uncollect: 'Cancel collection',
|
|
play: 'Play',
|
|
pause: 'Pause',
|
|
next: 'Next song',
|
|
prev: 'Previous song',
|
|
hide_win_main: 'Hide Main Window',
|
|
show_win_main: 'Show Main Window',
|
|
hide_win_lyric: 'Close desktop lyrics',
|
|
show_win_lyric: 'Open desktop lyrics',
|
|
lock_win_lyric: 'Lock desktop lyrics',
|
|
unlock_win_lyric: 'Unlock desktop lyrics',
|
|
top_win_lyric: 'Set top lyrics',
|
|
untop_win_lyric: 'Cancel top lyrics',
|
|
show_statusbar_lyric: 'Show statusbar lyric',
|
|
hide_statusbar_lyric: 'Hide statusbar lyric',
|
|
exit: 'Exit',
|
|
music_name: 'Name: ',
|
|
music_singer: 'Artist: ',
|
|
},
|
|
'zh-cn': {
|
|
collect: '收藏',
|
|
uncollect: '取消收藏',
|
|
play: '播放',
|
|
pause: '暂停',
|
|
next: '下一曲',
|
|
prev: '上一曲',
|
|
hide_win_main: '隐藏主界面',
|
|
show_win_main: '显示主界面',
|
|
hide_win_lyric: '关闭桌面歌词',
|
|
show_win_lyric: '开启桌面歌词',
|
|
lock_win_lyric: '锁定桌面歌词',
|
|
unlock_win_lyric: '解锁桌面歌词',
|
|
top_win_lyric: '置顶歌词',
|
|
untop_win_lyric: '取消置顶',
|
|
show_statusbar_lyric: '显示状态栏歌词',
|
|
hide_statusbar_lyric: '隐藏状态栏歌词',
|
|
exit: '退出',
|
|
music_name: '歌曲名: ',
|
|
music_singer: '艺术家: ',
|
|
},
|
|
'zh-tw': {
|
|
collect: '收藏',
|
|
uncollect: '取消收藏',
|
|
play: '播放',
|
|
pause: '暫停',
|
|
next: '下一曲',
|
|
prev: '上一曲',
|
|
hide_win_main: '隱藏主界面',
|
|
show_win_main: '顯示主界面',
|
|
hide_win_lyric: '關閉桌面歌詞',
|
|
show_win_lyric: '開啟桌面歌詞',
|
|
lock_win_lyric: '鎖定桌面歌詞',
|
|
unlock_win_lyric: '解鎖桌面歌詞',
|
|
top_win_lyric: '置頂歌詞',
|
|
untop_win_lyric: '取消置頂',
|
|
show_statusbar_lyric: '顯示狀態列歌詞',
|
|
hide_statusbar_lyric: '隱藏狀態列歌詞',
|
|
exit: '退出',
|
|
music_name: '歌曲名: ',
|
|
music_singer: '藝術家: ',
|
|
},
|
|
} as const
|
|
type Messages = typeof messages
|
|
type Langs = keyof Messages
|
|
const i18n = {
|
|
message: messages['zh-cn'] as Messages[Langs],
|
|
fallbackLocale: 'en-us' as 'en-us',
|
|
getMessage(key: keyof Messages[Langs]) {
|
|
return this.message[key]
|
|
},
|
|
setLang(lang?: Langs | null) {
|
|
this.message = lang
|
|
? messages[lang] ?? messages[this.fallbackLocale]
|
|
: messages[this.fallbackLocale]
|
|
},
|
|
}
|
|
|
|
const getIconPath = (id: number) => {
|
|
let theme = themeList.find(item => item.id === id) ?? themeList[0]
|
|
return path.join(global.staticPath, 'images/tray', theme.fileName + (isWin ? '.ico' : '.png'))
|
|
}
|
|
|
|
export const createTray = () => {
|
|
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
|
if ((tray && !tray.isDestroyed()) || !global.lx.appSetting['tray.enable']) return
|
|
|
|
// 托盘
|
|
tray = new Tray(nativeImage.createFromPath(getIconPath(global.lx.appSetting['tray.themeId'])))
|
|
|
|
// tray.setToolTip('LX Music')
|
|
// createMenu()
|
|
tray.setIgnoreDoubleClickEvents(true)
|
|
tray.on('click', () => {
|
|
showMainWindow()
|
|
})
|
|
}
|
|
|
|
export const destroyTray = () => {
|
|
if (!tray) return
|
|
tray.destroy()
|
|
isEnableTray = false
|
|
isShowStatusBarLyric = false
|
|
tray = null
|
|
}
|
|
|
|
const handleUpdateConfig = (setting: Partial<LX.AppSetting>) => {
|
|
global.lx.event_app.update_config(setting)
|
|
}
|
|
|
|
const createPlayerMenu = () => {
|
|
let menu: Electron.MenuItemConstructorOptions[] = []
|
|
menu.push(playerState.play ? {
|
|
label: i18n.getMessage('pause'),
|
|
click() {
|
|
sendTaskbarButtonClick('pause')
|
|
},
|
|
} : {
|
|
label: i18n.getMessage('play'),
|
|
click() {
|
|
sendTaskbarButtonClick('play')
|
|
},
|
|
})
|
|
menu.push({
|
|
label: i18n.getMessage('prev'),
|
|
click() {
|
|
sendTaskbarButtonClick('prev')
|
|
},
|
|
})
|
|
menu.push({
|
|
label: i18n.getMessage('next'),
|
|
click() {
|
|
sendTaskbarButtonClick('next')
|
|
},
|
|
})
|
|
menu.push(playerState.collect ? {
|
|
label: i18n.getMessage('uncollect'),
|
|
click() {
|
|
sendTaskbarButtonClick('unCollect')
|
|
},
|
|
} : {
|
|
label: i18n.getMessage('collect'),
|
|
click() {
|
|
sendTaskbarButtonClick('collect')
|
|
},
|
|
})
|
|
return menu
|
|
}
|
|
|
|
export const createMenu = () => {
|
|
if (!tray) return
|
|
let menu: Electron.MenuItemConstructorOptions[] = createPlayerMenu()
|
|
if (playerState.empty) for (const m of menu) m.enabled = false
|
|
menu.push({ type: 'separator' })
|
|
menu.push(global.lx.appSetting['desktopLyric.enable']
|
|
? {
|
|
label: i18n.getMessage('hide_win_lyric'),
|
|
click() {
|
|
handleUpdateConfig({ 'desktopLyric.enable': false })
|
|
},
|
|
}
|
|
: {
|
|
label: i18n.getMessage('show_win_lyric'),
|
|
click() {
|
|
handleUpdateConfig({ 'desktopLyric.enable': true })
|
|
},
|
|
})
|
|
menu.push(global.lx.appSetting['desktopLyric.isLock']
|
|
? {
|
|
label: i18n.getMessage('unlock_win_lyric'),
|
|
click() {
|
|
handleUpdateConfig({ 'desktopLyric.isLock': false })
|
|
},
|
|
}
|
|
: {
|
|
label: i18n.getMessage('lock_win_lyric'),
|
|
click() {
|
|
handleUpdateConfig({ 'desktopLyric.isLock': true })
|
|
},
|
|
})
|
|
menu.push(global.lx.appSetting['desktopLyric.isAlwaysOnTop']
|
|
? {
|
|
label: i18n.getMessage('untop_win_lyric'),
|
|
click() {
|
|
handleUpdateConfig({ 'desktopLyric.isAlwaysOnTop': false })
|
|
},
|
|
}
|
|
: {
|
|
label: i18n.getMessage('top_win_lyric'),
|
|
click() {
|
|
handleUpdateConfig({ 'desktopLyric.isAlwaysOnTop': true })
|
|
},
|
|
})
|
|
if (isMac) {
|
|
menu.push({ type: 'separator' })
|
|
menu.push(isShowStatusBarLyric
|
|
? {
|
|
label: i18n.getMessage('hide_statusbar_lyric'),
|
|
click() {
|
|
handleUpdateConfig({ 'player.isShowStatusBarLyric': false })
|
|
},
|
|
}
|
|
: {
|
|
label: i18n.getMessage('show_statusbar_lyric'),
|
|
click() {
|
|
handleUpdateConfig({ 'player.isShowStatusBarLyric': true })
|
|
},
|
|
})
|
|
}
|
|
menu.push({ type: 'separator' })
|
|
if (isExistMainWindow()) {
|
|
const isShow = isShowMainWindow()
|
|
menu.push(isShow
|
|
? {
|
|
label: i18n.getMessage('hide_win_main'),
|
|
click() {
|
|
hideMainWindow()
|
|
},
|
|
}
|
|
: {
|
|
label: i18n.getMessage('show_win_main'),
|
|
click() {
|
|
showMainWindow()
|
|
},
|
|
})
|
|
}
|
|
menu.push({
|
|
label: i18n.getMessage('exit'),
|
|
click() {
|
|
quitApp()
|
|
},
|
|
})
|
|
const contextMenu = Menu.buildFromTemplate(menu)
|
|
tray.setContextMenu(contextMenu)
|
|
}
|
|
|
|
export const setTrayImage = (themeId: number) => {
|
|
if (!tray) return
|
|
tray.setImage(nativeImage.createFromPath(getIconPath(themeId)))
|
|
}
|
|
|
|
const setLyric = (lyricLineText?: string) => {
|
|
if (isShowStatusBarLyric && tray && lyricLineText != null) {
|
|
tray.setTitle(lyricLineText)
|
|
}
|
|
}
|
|
|
|
const defaultTip = 'LX Music'
|
|
const setTip = () => {
|
|
if (!tray) return
|
|
|
|
let name = global.lx.player_status.name
|
|
let tip: string
|
|
if (name) {
|
|
if (name.length > 20) name = name.substring(0, 20) + '...'
|
|
let singer = global.lx.player_status.singer
|
|
if (singer?.length > 20) singer = singer.substring(0, 20) + '...'
|
|
|
|
tip = `${defaultTip}\n${i18n.getMessage('music_name')}${name}${singer ? `\n${i18n.getMessage('music_singer')}${singer}` : ''}`
|
|
} else tip = defaultTip
|
|
tray.setToolTip(tip)
|
|
}
|
|
|
|
const init = () => {
|
|
if (themeId != global.lx.appSetting['tray.themeId']) {
|
|
themeId = global.lx.appSetting['tray.themeId']
|
|
setTrayImage(themeId)
|
|
}
|
|
if (isEnableTray !== global.lx.appSetting['tray.enable']) {
|
|
isEnableTray = global.lx.appSetting['tray.enable']
|
|
global.lx.appSetting['tray.enable'] ? createTray() : destroyTray()
|
|
}
|
|
if (isShowStatusBarLyric !== global.lx.appSetting['player.isShowStatusBarLyric']) {
|
|
isShowStatusBarLyric = global.lx.appSetting['player.isShowStatusBarLyric']
|
|
if (isShowStatusBarLyric) {
|
|
setLyric(global.lx.player_status.lyricLineText)
|
|
} else {
|
|
tray?.setTitle('')
|
|
}
|
|
}
|
|
setTip()
|
|
createMenu()
|
|
}
|
|
|
|
export default () => {
|
|
global.lx.event_app.on('updated_config', (keys, setting) => {
|
|
if (!watchConfigKeys.some(key => keys.includes(key))) return
|
|
|
|
if (keys.includes('common.langId')) i18n.setLang(setting['common.langId'])
|
|
|
|
init()
|
|
})
|
|
|
|
global.lx.event_app.on('main_window_ready_to_show', () => {
|
|
createMenu()
|
|
})
|
|
global.lx.event_app.on('main_window_show', () => {
|
|
createMenu()
|
|
})
|
|
if (!isWin) {
|
|
global.lx.event_app.on('main_window_focus', () => {
|
|
createMenu()
|
|
})
|
|
global.lx.event_app.on('main_window_blur', () => {
|
|
createMenu()
|
|
})
|
|
}
|
|
global.lx.event_app.on('main_window_hide', () => {
|
|
createMenu()
|
|
})
|
|
global.lx.event_app.on('main_window_close', () => {
|
|
destroyTray()
|
|
})
|
|
|
|
global.lx.event_app.on('app_inited', () => {
|
|
i18n.setLang(global.lx.appSetting['common.langId'])
|
|
init()
|
|
})
|
|
|
|
global.lx.event_app.on('player_status', (status) => {
|
|
let updated = false
|
|
if (status.status) {
|
|
switch (status.status) {
|
|
case 'paused':
|
|
playerState.play = false
|
|
playerState.empty &&= false
|
|
setLyric('')
|
|
break
|
|
case 'error':
|
|
playerState.play = false
|
|
playerState.empty &&= false
|
|
setLyric('')
|
|
break
|
|
case 'playing':
|
|
playerState.play = true
|
|
playerState.empty &&= false
|
|
setLyric(global.lx.player_status.lyricLineText)
|
|
break
|
|
case 'stoped':
|
|
playerState.play &&= false
|
|
playerState.empty = true
|
|
setLyric('')
|
|
break
|
|
}
|
|
updated = true
|
|
} else {
|
|
setLyric(status.lyricLineText)
|
|
}
|
|
if (status.name != null) setTip()
|
|
if (status.singer != null) setTip()
|
|
if (status.collect != null) {
|
|
playerState.collect = status.collect
|
|
updated = true
|
|
}
|
|
if (updated) init()
|
|
})
|
|
}
|