diff --git a/.gitignore b/.gitignore index ecdf97c3..e42d6c24 100644 --- a/.gitignore +++ b/.gitignore @@ -37,5 +37,5 @@ src/Bot/conf.js log.txt \.idea/ secrets -/public/macos-wallpaper +/public/assets /public/*.exe diff --git a/scripts/preBuild.js b/scripts/preBuild.js index 6ff196aa..4c51201c 100644 --- a/scripts/preBuild.js +++ b/scripts/preBuild.js @@ -1,11 +1,13 @@ const { join } = require('path') -const { copyFileSync } = require('fs'); +const { ensureDirSync, copySync } = require('fs-extra') // Copy wallpaper binaries to public folder on build const sources = [ - '../src/electron/helpers/wallpaper/win-wallpaper.exe' + '../src/electron/helpers/wallpaper/assets', + '../src/electron/helpers/assets', ] -const destFolder = join(__dirname, '..', 'public'); +const destFolder = join(__dirname, '..', 'public', 'assets'); +ensureDirSync(destFolder); -sources.forEach(src => copyFileSync(join(__dirname, src), join(destFolder, src.split('/').pop()))) \ No newline at end of file +sources.forEach(src => copySync(join(__dirname, src), destFolder)) \ No newline at end of file diff --git a/src/App/Providers/AudioPlayer/Audioplayer.re b/src/App/Providers/AudioPlayer/Audioplayer.re index ed63f38d..10ec2f27 100644 --- a/src/App/Providers/AudioPlayer/Audioplayer.re +++ b/src/App/Providers/AudioPlayer/Audioplayer.re @@ -75,13 +75,6 @@ let make = (~children) => { setPlaylist(~beatmapPlaylist=[||], ~playlistID="", ()); }; - React.useEffect0(() => { - MediaSession.setActionHandler(`play, Some(_play)); - MediaSession.setActionHandler(`pause, Some(pause)); - MediaSession.setActionHandler(`stop, Some(_stop)); - None; - }); - let playFromPlaylist = (playlistindex: int) => { let nextSong = playlist[playlistindex]; Audio.setSrc(audio, nextSong.path); @@ -102,8 +95,51 @@ let make = (~children) => { ); }; + let playNext = () => { + switch (_canPlayNextSong()) { + | Some(nextSong) => playFromPlaylist(nextSong) + | None => () + }; + }; + + let playPrevious = () => { + switch (_canPlayPrevSong()) { + | Some(prevSong) => playFromPlaylist(prevSong) + | None => () + }; + }; + + React.useEffect3( + () => { + IPCRenderer.send( + UPDATE_THUMB_BAR({ + isPlaying: playingState.isPlaying, + canPlayNext: playingState.hasNext, + canPlayPrev: playingState.hasPrev, + }), + ); + IPCRenderer.on("EXEC_PREV", playPrevious); + IPCRenderer.on("EXEC_NEXT", playNext); + Some( + () => { + IPCRenderer.removeListener("EXEC_PREV", playPrevious); + IPCRenderer.removeListener("EXEC_NEXT", playNext); + }, + ); + }, + (playingState.isPlaying, playingState.hasNext, playingState.hasPrev), + ); + + React.useEffect0(() => { + MediaSession.setActionHandler(`play, Some(_play)); + MediaSession.setActionHandler(`pause, Some(pause)); + MediaSession.setActionHandler(`stop, Some(_stop)); + IPCRenderer.on("EXEC_PLAY_PAUSE", togglePlayPause); + None; + }); + let updateMediaHandlers = () => { - ( + let next = switch (_canPlayNextSong()) { | Some(nextSongindex) => setPlayingState(prevState => {...prevState, hasNext: true}); @@ -111,21 +147,21 @@ let make = (~children) => { | None => setPlayingState(prevState => {...prevState, hasNext: false}); None; - } - ) - |> MediaSession.setActionHandler(`nexttrack); - ( + }; + + MediaSession.setActionHandler(`nexttrack, next); + + let previous = switch (_canPlayPrevSong()) { | Some(prevSongindex) => setPlayingState(prevState => {...prevState, hasPrev: true}); Some(() => playFromPlaylist(prevSongindex)); | None => setPlayingState(prevState => {...prevState, hasPrev: false}); - None; - } - ) - |> MediaSession.setActionHandler(`previoustrack); + }; + + MediaSession.setActionHandler(`previoustrack, previous); }; React.useEffect2( @@ -136,20 +172,6 @@ let make = (~children) => { (playingState.beatmapSetId, playlist), ); - let playNext = () => { - switch (_canPlayNextSong()) { - | Some(nextSong) => playFromPlaylist(nextSong) - | None => () - }; - }; - - let playPrevious = () => { - switch (_canPlayPrevSong()) { - | Some(prevSong) => playFromPlaylist(prevSong) - | None => () - }; - }; - let setAudio = ( ~song: song, diff --git a/src/electron/MainWindow.js b/src/electron/MainWindow.js index 1c9313f8..1008691e 100644 --- a/src/electron/MainWindow.js +++ b/src/electron/MainWindow.js @@ -4,6 +4,7 @@ const { autoUpdater } = require('electron-updater'); const { join } = require('path'); const isDev = require('electron-is-dev'); const beatmapDownloader = require('./BeatmapDownloader'); +const taskBar = require('./helpers/windowsTaskBar'); const makeMainWindowSettings = () => { const mainWindowState = windowStateKeeper({ @@ -45,6 +46,7 @@ const makeMainWindow = ({ content, ...options }) => { mainWindow.show(); beatmapDownloader.register(mainWindow); mainWindowState.manage(mainWindow); + taskBar.register(mainWindow); if (isDev) mainWindow.webContents.openDevTools(); }) .on('show', () => { diff --git a/src/electron/helpers/assets/next.png b/src/electron/helpers/assets/next.png new file mode 100644 index 00000000..45398843 Binary files /dev/null and b/src/electron/helpers/assets/next.png differ diff --git a/src/electron/helpers/assets/pause.png b/src/electron/helpers/assets/pause.png new file mode 100644 index 00000000..c50c6be6 Binary files /dev/null and b/src/electron/helpers/assets/pause.png differ diff --git a/src/electron/helpers/assets/play.png b/src/electron/helpers/assets/play.png new file mode 100644 index 00000000..1bc23824 Binary files /dev/null and b/src/electron/helpers/assets/play.png differ diff --git a/src/electron/helpers/assets/previous.png b/src/electron/helpers/assets/previous.png new file mode 100644 index 00000000..62f225ea Binary files /dev/null and b/src/electron/helpers/assets/previous.png differ diff --git a/src/electron/helpers/wallpaper/win-wallpaper.exe b/src/electron/helpers/wallpaper/assets/win-wallpaper.exe similarity index 100% rename from src/electron/helpers/wallpaper/win-wallpaper.exe rename to src/electron/helpers/wallpaper/assets/win-wallpaper.exe diff --git a/src/electron/helpers/wallpaper/win.js b/src/electron/helpers/wallpaper/win.js index 27576639..51851b5b 100644 --- a/src/electron/helpers/wallpaper/win.js +++ b/src/electron/helpers/wallpaper/win.js @@ -5,7 +5,7 @@ const childProcess = require('child_process'); const execFile = promisify(childProcess.execFile); // Binary source → https://github.com/sindresorhus/win-wallpaper -const binary = path.join(__dirname, 'win-wallpaper.exe'); +const binary = path.join(__dirname, 'assets', 'win-wallpaper.exe'); exports.get = async () => { const { stdout } = await execFile(binary); diff --git a/src/electron/helpers/windowsTaskBar.js b/src/electron/helpers/windowsTaskBar.js new file mode 100644 index 00000000..dbe52f60 --- /dev/null +++ b/src/electron/helpers/windowsTaskBar.js @@ -0,0 +1,66 @@ +/* eslint-disable no-underscore-dangle */ +const { ipcMain, nativeImage } = require('electron'); +const path = require('path'); + +class WindowsTaskBar { + static IPC_CHANNELS = Object.freeze({ + EXEC_PLAY_PAUSE: 'EXEC_PLAY_PAUSE', + EXEC_PREV: 'EXEC_PREV', + EXEC_NEXT: 'EXEC_NEXT', + UPDATE_THUMB_BAR: 'UPDATE_THUMB_BAR', + UPDATE_PLAY_STATE: 'UPDATE_PLAY_STATE', + }); + + constructor() { + this.browserWindow = null; + this.icons = { + next: nativeImage.createFromPath(path.join(__dirname, 'assets', 'next.png')), + prev: nativeImage.createFromPath(path.join(__dirname, 'assets', 'previous.png')), + play: nativeImage.createFromPath(path.join(__dirname, 'assets', 'play.png')), + pause: nativeImage.createFromPath(path.join(__dirname, 'assets', 'pause.png')), + }; + } + + _sendNext() { + this.browserWindow.webContents.send(WindowsTaskBar.IPC_CHANNELS.EXEC_NEXT); + } + + _sendPlayPause() { + this.browserWindow.webContents.send(WindowsTaskBar.IPC_CHANNELS.EXEC_PLAY_PAUSE); + } + + _sendPrevious() { + this.browserWindow.webContents.send(WindowsTaskBar.IPC_CHANNELS.EXEC_PREV); + } + + _updateThumbarButtons(isPlaying, canPlayNext, canPlayPrev) { + this.browserWindow.setThumbarButtons([ + { + icon: this.icons.prev, + flags: ['nobackground', ...(canPlayPrev ? [] : ['disabled', 'hidden'])], + click: this._sendPrevious.bind(this), + }, + { + icon: isPlaying ? this.icons.pause : this.icons.play, + flags: ['nobackground'], + click: this._sendPlayPause.bind(this), + }, + { + icon: this.icons.next, + flags: ['nobackground', ...(canPlayNext ? [] : ['disabled', 'hidden'])], + click: this._sendNext.bind(this), + }, + ]); + } + + register(browserWindow) { + this.browserWindow = browserWindow; + ipcMain.on(WindowsTaskBar.IPC_CHANNELS.UPDATE_THUMB_BAR, (event, { isPlaying, canPlayNext, canPlayPrev }) => { + this._updateThumbarButtons(isPlaying, canPlayNext, canPlayPrev); + }); + } +} + +const windowsTaskBar = new WindowsTaskBar(); + +module.exports = windowsTaskBar; diff --git a/src/electron/reason/IPCRenderer.re b/src/electron/reason/IPCRenderer.re new file mode 100644 index 00000000..d6183153 --- /dev/null +++ b/src/electron/reason/IPCRenderer.re @@ -0,0 +1,29 @@ +type t; +[@bs.module "electron"] external remote: t = "ipcRenderer"; + +[@bs.send] external send: (t, string, 'a) => unit = "send"; +[@bs.send] external on: (t, string, 'a => unit) => unit = "on"; +[@bs.send] +external removeListener: (t, string, 'a => unit) => unit = "removeListener"; + +type updateThumbBarData = { + isPlaying: bool, + canPlayNext: bool, + canPlayPrev: bool, +}; + +type channel = + | UPDATE_THUMB_BAR(updateThumbBarData) + | UPDATE_PLAY_STATE(bool); + +let send = channel => { + switch (channel) { + | UPDATE_THUMB_BAR(data) => send(remote, "UPDATE_THUMB_BAR", data) + | UPDATE_PLAY_STATE(isPlaying) => + send(remote, "UPDATE_PLAY_STATE", isPlaying) + }; +}; + +let on = (channel, callback) => on(remote, channel, callback); +let removeListener = (channel, callback) => + removeListener(remote, channel, callback);