From 750e6accffc1dcfe779e4f59823da713ba6bb4b0 Mon Sep 17 00:00:00 2001 From: DEMARET Arnaut Date: Fri, 27 May 2022 11:46:03 +0200 Subject: [PATCH] chore(App): bump bs-platform + use rescript (#421) --- .vscode/extensions.json | 2 +- bsconfig.json | 9 +- package.json | 13 +- scripts/build.js | 8 +- scripts/start.js | 8 +- ...yerProvider.re => AudioPlayerProvider.res} | 66 ++--- src/App/Providers/AudioPlayer/Audioplayer.re | 270 ----------------- src/App/Providers/AudioPlayer/Audioplayer.res | 232 +++++++++++++++ src/App/Providers/TaskProvider.re | 109 ------- src/App/Providers/TaskProvider.res | 96 +++++++ src/App/helpers/DiscordRPC.re | 5 - src/App/helpers/DiscordRPC.res | 4 + src/App/helpers/Icon.re | 29 -- src/App/helpers/Icon.res | 23 ++ src/App/modules/Sections.re | 2 - src/App/modules/Sections.res | 2 + src/App/modules/common/Badge.re | 138 --------- src/App/modules/common/Badge.res | 110 +++++++ src/App/modules/common/Checkbox.re | 27 -- src/App/modules/common/Checkbox.res | 18 ++ .../{ProgressRing.re => ProgressRing.res} | 27 +- src/App/modules/common/TitleBar.re | 66 ----- src/App/modules/common/TitleBar.res | 60 ++++ .../{OsuDbParser.re => OsuDbParser.res} | 31 +- src/shared/PpyHelpers.re | 45 --- src/shared/PpyHelpers.res | 40 +++ src/shared/dom/Audio.re | 35 --- src/shared/dom/Audio.res | 34 +++ src/shared/dom/Image.re | 7 - src/shared/dom/Image.res | 7 + .../{MediaMetadata.re => MediaMetadata.res} | 12 +- src/shared/dom/MediaSession.re | 20 -- src/shared/dom/MediaSession.res | 19 ++ src/shared/electron/BrowserWindow.re | 8 - src/shared/electron/BrowserWindow.res | 8 + src/shared/electron/IPCRenderer.re | 29 -- src/shared/electron/IPCRenderer.res | 26 ++ src/shared/electron/Remote.re | 5 - src/shared/electron/Remote.res | 5 + yarn.lock | 272 ++++++++++-------- 40 files changed, 917 insertions(+), 1010 deletions(-) rename src/App/Providers/AudioPlayer/{AudioPlayerProvider.re => AudioPlayerProvider.res} (53%) delete mode 100644 src/App/Providers/AudioPlayer/Audioplayer.re create mode 100644 src/App/Providers/AudioPlayer/Audioplayer.res delete mode 100644 src/App/Providers/TaskProvider.re create mode 100644 src/App/Providers/TaskProvider.res delete mode 100644 src/App/helpers/DiscordRPC.re create mode 100644 src/App/helpers/DiscordRPC.res delete mode 100644 src/App/helpers/Icon.re create mode 100644 src/App/helpers/Icon.res delete mode 100644 src/App/modules/Sections.re create mode 100644 src/App/modules/Sections.res delete mode 100644 src/App/modules/common/Badge.re create mode 100644 src/App/modules/common/Badge.res delete mode 100644 src/App/modules/common/Checkbox.re create mode 100644 src/App/modules/common/Checkbox.res rename src/App/modules/common/{ProgressRing.re => ProgressRing.res} (50%) delete mode 100644 src/App/modules/common/TitleBar.re create mode 100644 src/App/modules/common/TitleBar.res rename src/electron/helpers/{OsuDbParser.re => OsuDbParser.res} (76%) delete mode 100644 src/shared/PpyHelpers.re create mode 100644 src/shared/PpyHelpers.res delete mode 100644 src/shared/dom/Audio.re create mode 100644 src/shared/dom/Audio.res delete mode 100644 src/shared/dom/Image.re create mode 100644 src/shared/dom/Image.res rename src/shared/dom/{MediaMetadata.re => MediaMetadata.res} (70%) delete mode 100644 src/shared/dom/MediaSession.re create mode 100644 src/shared/dom/MediaSession.res delete mode 100644 src/shared/electron/BrowserWindow.re create mode 100644 src/shared/electron/BrowserWindow.res delete mode 100644 src/shared/electron/IPCRenderer.re create mode 100644 src/shared/electron/IPCRenderer.res delete mode 100644 src/shared/electron/Remote.re create mode 100644 src/shared/electron/Remote.res diff --git a/.vscode/extensions.json b/.vscode/extensions.json index f708eaa..78e752f 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -5,6 +5,6 @@ // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp "esbenp.prettier-vscode", "dbaeumer.vscode-eslint", - "jaredly.reason-vscode" + "chenglou92.rescript-vscode" ] } diff --git a/bsconfig.json b/bsconfig.json index 7e9792b..4720f22 100644 --- a/bsconfig.json +++ b/bsconfig.json @@ -17,6 +17,9 @@ ], "suffix": ".bs.js", "namespace": true, - "bs-dependencies": ["reason-react", "bs-css"], - "refmt": 3 -} + "bs-dependencies": [ + "@rescript/react", + "bs-css", + "bs-css-emotion" + ] +} \ No newline at end of file diff --git a/package.json b/package.json index c684337..9a16255 100644 --- a/package.json +++ b/package.json @@ -18,9 +18,9 @@ "lint:fix:prettier": "prettier --write '**/*.{json,jsx,tsx,mjs,js,ts}'", "lint:es": "eslint '**/*.{js,jsx,mjs,ts,tsx}'", "lint:fix:es": "eslint --fix '**/*.{js,jsx,mjs,ts,tsx}'", - "bsb": "bsb -make-world", - "bsb:watch": "bsb -make-world -w", - "bsb:clean": "bsb -clean-world", + "res": "rescript build", + "res:watch": "rescript build -w", + "res:clean": "rescript clean", "dist": "yarn install && yarn build && electron-builder build --win --linux --mac --ia32 --x64", "dist:win": "yarn install && yarn build && electron-builder build --win --x64", "dist:linux": "yarn install && yarn build && electron-builder build --linux --x64", @@ -131,6 +131,7 @@ "devDependencies": { "@babel/core": "7.13.13", "@babel/preset-env": "^7.13.12", + "@rescript/react": "0.10.3", "@svgr/webpack": "5.5.0", "acorn-dynamic-import": "^4.0.0", "babel-eslint": "10.1.0", @@ -138,8 +139,8 @@ "babel-loader": "8.2.2", "babel-plugin-named-asset-import": "^0.3.7", "babel-preset-react-app": "^10.0.0", - "bs-css": "^11.0.0", - "bs-platform": "8.4.2", + "bs-css": "^15.3.0", + "bs-css-emotion": "^4.3.0", "camelcase": "^5.2.0", "case-sensitive-paths-webpack-plugin": "2.3.0", "conventional-changelog-cli": "^2.1.1", @@ -170,7 +171,7 @@ "pnp-webpack-plugin": "1.2.1", "prettier": "^1.18.2", "react-dev-utils": "^9.0.1", - "reason-react": "^0.7.1", + "rescript": "9.1.4", "resolve": "1.17.0", "semver": "6.0.0", "string-replace-loader": "^2.3.0", diff --git a/scripts/build.js b/scripts/build.js index 0669a82..e288a59 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -51,15 +51,15 @@ const config = configFactory('production'); const { checkBrowsers } = require('react-dev-utils/browsersHelper'); checkBrowsers(paths.appPath, isInteractive) .then(() => new Promise((resolve, reject) => { - const bsb = exec('npm run bsb', + const res = exec('npm run res', (err) => { if (err) { - console.log("Couldn't build reason using bsb"); + console.log("Couldn't build rescript using res"); reject(err) } }); - bsb.stdout.pipe(process.stdout) - bsb.on('exit', resolve) + res.stdout.pipe(process.stdout) + res.on('exit', resolve) })) .then(() => { // First, read the current file sizes in build directory. diff --git a/scripts/start.js b/scripts/start.js index 6f25d85..e4c93c5 100644 --- a/scripts/start.js +++ b/scripts/start.js @@ -64,15 +64,15 @@ if (process.env.HOST) { const { checkBrowsers } = require('react-dev-utils/browsersHelper'); checkBrowsers(paths.appPath, isInteractive) .then(() => new Promise((resolve, reject) => { - const bsb = exec('npm run bsb', + const res = exec('npm run res', (err) => { if (err) { - console.log("Couldn't build reason using bsb"); + console.log("Couldn't build rescript using rescript"); reject(err) } }); - bsb.stdout.pipe(process.stdout) - bsb.on('exit', resolve) + res.stdout.pipe(process.stdout) + res.on('exit', resolve) })) .then(() => { // We attempt to use the default port but if it is busy, we offer the user to diff --git a/src/App/Providers/AudioPlayer/AudioPlayerProvider.re b/src/App/Providers/AudioPlayer/AudioPlayerProvider.res similarity index 53% rename from src/App/Providers/AudioPlayer/AudioPlayerProvider.re rename to src/App/Providers/AudioPlayer/AudioPlayerProvider.res index b2aa7fe..d76dfa5 100644 --- a/src/App/Providers/AudioPlayer/AudioPlayerProvider.re +++ b/src/App/Providers/AudioPlayer/AudioPlayerProvider.res @@ -7,43 +7,36 @@ type playingState = { muted: bool, hasNext: bool, hasPrev: bool, -}; +} type song = { id: int, artist: string, title: string, -}; +} type playlistItem = { id: int, path: string, title: string, artist: string, -}; +} -type playlist = array(playlistItem); +type playlist = array type value = { - playingState, - playlist, + playingState: playingState, + playlist: playlist, playlistID: string, - setPlaylist: - (~beatmapPlaylist: playlist, ~playlistID: string=?, unit) => unit, - setAudio: - ( - ~song: song, - ~audioFilePath: option(string), - ~previewOffset: option(int) - ) => - unit, + setPlaylist: (~beatmapPlaylist: playlist, ~playlistID: string=?, unit) => unit, + setAudio: (~song: song, ~audioFilePath: option, ~previewOffset: option) => unit, setVolume: float => unit, pause: unit => unit, togglePlayPause: unit => unit, setMuted: bool => unit, playNext: unit => unit, playPrevious: unit => unit, -}; +} let initialState: playingState = { artist: "", @@ -54,36 +47,35 @@ let initialState: playingState = { muted: false, hasNext: false, hasPrev: false, -}; +} module Provider = { let value = { playingState: initialState, - playlist: [||], + playlist: [], playlistID: "", - setPlaylist: (~beatmapPlaylist: playlist, ~playlistID=?, unit) => unit, - setAudio: - ( - ~song: song, - ~audioFilePath: option(string), - ~previewOffset: option(int), - ) => - (), - setVolume: (vol: float) => (), + setPlaylist: (~beatmapPlaylist as _: playlist, ~playlistID as _=?, unit) => unit, + setAudio: ( + ~song as _: song, + ~audioFilePath as _: option, + ~previewOffset as _: option, + ) => (), + setVolume: (_vol: float) => (), pause: () => (), togglePlayPause: () => (), - setMuted: (muted: bool) => (), + setMuted: (_muted: bool) => (), playNext: () => (), playPrevious: () => (), - }; - let audioPlayerContext = React.createContext(value); + } + let audioPlayerContext = React.createContext(value) - let makeProps = (~value, ~children, ()) => { - "value": value, - "children": children, - }; + let makeProps = (~value, ~children, ()) => + { + "value": value, + "children": children, + } - let make = React.Context.provider(audioPlayerContext); -}; + let make = React.Context.provider(audioPlayerContext) +} -let useAudioPlayer = () => React.useContext(Provider.audioPlayerContext); +let useAudioPlayer = () => React.useContext(Provider.audioPlayerContext) diff --git a/src/App/Providers/AudioPlayer/Audioplayer.re b/src/App/Providers/AudioPlayer/Audioplayer.re deleted file mode 100644 index 981a8d6..0000000 --- a/src/App/Providers/AudioPlayer/Audioplayer.re +++ /dev/null @@ -1,270 +0,0 @@ -open AudioPlayerProvider; - -[@bs.val] external alert: string => unit = "alert"; - -let audio = Audio.make(); - -let _play = () => { - Audio.play(audio); -}; - -let _updateMetadata = (song: song) => { - MediaMetadata.make({ - title: song.title, - artist: song.artist, - album: "Beatconnect", - artwork: [|MediaMetadata.makeArtwork(song.id)|], - }) - ->MediaSession.setMediaSessionMetadata; -}; - -let _setPreviewAudio = (beatmapSetId: int) => { - Audio.setSrc(audio, {j|https://b.ppy.sh/preview/$beatmapSetId.mp3|j}); -}; - -let _setAudioSrc = (~audioFilePath, ~previewOffset=?, ()) => { - Audio.setSrc(audio, audioFilePath); - switch (previewOffset) { - | Some(offset) => Audio.setCurrentTime(audio, offset) - | None => () - }; -}; - -let pause = () => { - Audio.pause(audio); -}; - -let togglePlayPause = () => Audio.paused(audio) ? _play() : pause(); - -let setVolume = Audio.setVolume(audio); - -[@react.component] -let make = (~children) => { - let playlistErrorCount = React.useRef(0); - let (playingState, setPlayingState) = React.useState(() => initialState); - let (playlist: playlist, setPlaylist) = React.useState(() => [||]); - let (playlistID: string, setPlaylistID) = React.useState(() => ""); - - let _canPlay = (offset: int) => - if (playlist->Belt_Array.length > 0) { - let currentSongIndex = - playlist->Belt_Array.getIndexBy(item => - item.id == playingState.beatmapSetId - ); - switch (currentSongIndex) { - | None => None - | Some(index) => - index + offset < playlist->Js_array.length && index + offset > (-1) - ? Some(index + offset) : None - }; - } else { - None; - }; - - let _canPlayNextSong = () => _canPlay(1); - let _canPlayPrevSong = () => _canPlay(-1); - - let setPlaylist = (~beatmapPlaylist: playlist, ~playlistID=?, ()) => { - setPlaylist(_ => beatmapPlaylist); - switch (playlistID) { - | Some(id) => setPlaylistID(_ => id) - | None => () - }; - }; - - let _stop = () => { - pause(); - setPlaylist(~beatmapPlaylist=[||], ~playlistID="", ()); - }; - - let playFromPlaylist = (playlistindex: int) => { - let nextSong = playlist[playlistindex]; - Audio.setSrc(audio, nextSong.path); - _updateMetadata({ - id: nextSong.id, - title: nextSong.title, - artist: nextSong.artist, - }); - _play(); - setPlayingState(oldState => - { - ...oldState, - isPlaying: false, - beatmapSetId: nextSong.id, - title: nextSong.title, - artist: nextSong.artist, - } - ); - }; - - 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}); - Some(() => playFromPlaylist(nextSongindex)); - | None => - setPlayingState(prevState => {...prevState, hasNext: false}); - None; - }; - - 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, previous); - }; - - React.useEffect2( - () => { - updateMediaHandlers(); - None; - }, - (playingState.beatmapSetId, playlist), - ); - - let setAudio = - ( - ~song: song, - ~audioFilePath: option(string), - ~previewOffset: option(int), - ) => { - setPlaylist(~beatmapPlaylist=[||], ~playlistID="", ()); - _updateMetadata(song); - - switch (audioFilePath, previewOffset) { - | (None, None) => _setPreviewAudio(song.id) - | (None, Some(_)) => _setPreviewAudio(song.id) - | (Some(audioFilePath), None) => _setAudioSrc(~audioFilePath, ()) - | (Some(audioFilePath), Some(previewOffset)) => - _setAudioSrc(~audioFilePath, ~previewOffset, ()) - }; - - _play(); - setPlayingState(oldState => - { - ...oldState, - isPlaying: false, - beatmapSetId: song.id, - artist: song.artist, - title: song.title, - } - ); - }; - - let setMuted = muted => { - Audio.setMuted(audio, muted); - setPlayingState(oldState => {...oldState, muted}); - }; - - Audio.onended(audio, _e => { - switch (_canPlayNextSong()) { - | Some(nextSongindex) => playFromPlaylist(nextSongindex) - | None => DiscordRPC.clearActivity() - } - }); - - Audio.onpause( - audio, - _e => { - Js.log("PAUSEEEE"); - setPlayingState(oldState => {...oldState, isPlaying: false}); - }, - ); - - Audio.onplay(audio, _e => { - setPlayingState(oldState => {...oldState, isPlaying: true}) - }); - - Audio.oncanplay(audio, _e => - setPlayingState(oldState => {...oldState, isPlaying: true}) - ); - - Audio.onvolumechange(audio, e => { - setPlayingState(oldState => {...oldState, volume: e.target.volume}) - }); - - Audio.onerror( - audio, - _e => { - setPlayingState(oldState => {...oldState, isPlaying: false}); - let skipCount = playlistErrorCount->React.Ref.current; - if (skipCount > 12 || skipCount >= playlist->Js_array.length - 1) { - playlistErrorCount->React.Ref.setCurrent(0); - setPlaylist(~beatmapPlaylist=[||], ~playlistID="", ()); - alert( - "Failed to play a song one or multiple times, please check your osu songs folder setting in the Settings section", - ); - } else { - playNext(); - playlistErrorCount->React.Ref.setCurrent(skipCount + 1); - }; - }, - ); - - let value = { - playingState, - pause, - setAudio, - setPlaylist, - setVolume, - togglePlayPause, - setMuted, - playlist, - playNext, - playPrevious, - playlistID, - }; - children ; -}; diff --git a/src/App/Providers/AudioPlayer/Audioplayer.res b/src/App/Providers/AudioPlayer/Audioplayer.res new file mode 100644 index 0000000..4c9cff3 --- /dev/null +++ b/src/App/Providers/AudioPlayer/Audioplayer.res @@ -0,0 +1,232 @@ +open AudioPlayerProvider + +@val external alert: string => unit = "alert" + +let audio = Audio.make() + +let _play = () => Audio.play(audio) + +let _updateMetadata = (song: song) => + MediaMetadata.make({ + title: song.title, + artist: song.artist, + album: "Beatconnect", + artwork: [MediaMetadata.makeArtwork(song.id->Js.Int.toString)], + })->MediaSession.setMediaSessionMetadata + +let _setPreviewAudio = (beatmapSetId: int) => + Audio.setSrc(audio, `https://b.ppy.sh/preview/${beatmapSetId->Js.Int.toString}.mp3`) + +let _setAudioSrc = (~audioFilePath, ~previewOffset=?, ()) => { + Audio.setSrc(audio, audioFilePath) + switch previewOffset { + | Some(offset) => Audio.setCurrentTime(audio, offset) + | None => () + } +} + +let pause = () => Audio.pause(audio) + +let togglePlayPause = () => Audio.paused(audio) ? _play() : pause() + +let setVolume = Audio.setVolume(audio) + +@react.component +let make = (~children) => { + let playlistErrorCount = React.useRef(0) + let (playingState, setPlayingState) = React.useState(() => initialState) + let (playlist: playlist, setPlaylist) = React.useState(() => []) + let (playlistID: string, setPlaylistID) = React.useState(() => "") + + let _canPlay = (offset: int) => + if playlist->Belt_Array.length > 0 { + let currentSongIndex = + playlist->Belt_Array.getIndexBy(item => item.id == playingState.beatmapSetId) + switch currentSongIndex { + | None => None + | Some(index) => + index + offset < playlist->Js_array.length && index + offset > -1 + ? Some(index + offset) + : None + } + } else { + None + } + + let _canPlayNextSong = () => _canPlay(1) + let _canPlayPrevSong = () => _canPlay(-1) + + let setPlaylist = (~beatmapPlaylist: playlist, ~playlistID=?, ()) => { + setPlaylist(_ => beatmapPlaylist) + switch playlistID { + | Some(id) => setPlaylistID(_ => id) + | None => () + } + } + + let _stop = () => { + pause() + setPlaylist(~beatmapPlaylist=[], ~playlistID="", ()) + } + + let playFromPlaylist = (playlistindex: int) => { + let nextSong = playlist[playlistindex] + Audio.setSrc(audio, nextSong.path) + _updateMetadata({ + id: nextSong.id, + title: nextSong.title, + artist: nextSong.artist, + }) + _play() + setPlayingState(oldState => { + ...oldState, + isPlaying: false, + beatmapSetId: nextSong.id, + title: nextSong.title, + artist: nextSong.artist, + }) + } + + 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}) + Some(() => playFromPlaylist(nextSongindex)) + | None => + setPlayingState(prevState => {...prevState, hasNext: false}) + None + } + + 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, previous) + } + + React.useEffect2(() => { + updateMediaHandlers() + None + }, (playingState.beatmapSetId, playlist)) + + let setAudio = (~song: song, ~audioFilePath: option, ~previewOffset: option) => { + setPlaylist(~beatmapPlaylist=[], ~playlistID="", ()) + _updateMetadata(song) + + switch (audioFilePath, previewOffset) { + | (None, None) => _setPreviewAudio(song.id) + | (None, Some(_)) => _setPreviewAudio(song.id) + | (Some(audioFilePath), None) => _setAudioSrc(~audioFilePath, ()) + | (Some(audioFilePath), Some(previewOffset)) => _setAudioSrc(~audioFilePath, ~previewOffset, ()) + } + + _play() + setPlayingState(oldState => { + ...oldState, + isPlaying: false, + beatmapSetId: song.id, + artist: song.artist, + title: song.title, + }) + } + + let setMuted = muted => { + Audio.setMuted(audio, muted) + setPlayingState(oldState => {...oldState, muted: muted}) + } + + Audio.onended(audio, _e => + switch _canPlayNextSong() { + | Some(nextSongindex) => playFromPlaylist(nextSongindex) + | None => DiscordRPC.clearActivity() + } + ) + + Audio.onpause(audio, _e => { + Js.log("PAUSEEEE") + setPlayingState(oldState => {...oldState, isPlaying: false}) + }) + + Audio.onplay(audio, _e => setPlayingState(oldState => {...oldState, isPlaying: true})) + + Audio.oncanplay(audio, _e => setPlayingState(oldState => {...oldState, isPlaying: true})) + + Audio.onvolumechange(audio, e => + setPlayingState(oldState => {...oldState, volume: e.target.volume}) + ) + + Audio.onerror(audio, _e => { + setPlayingState(oldState => {...oldState, isPlaying: false}) + let skipCount = playlistErrorCount.current + if skipCount > 12 || skipCount >= playlist->Js_array.length - 1 { + playlistErrorCount.current = 0 + setPlaylist(~beatmapPlaylist=[], ~playlistID="", ()) + alert( + "Failed to play a song one or multiple times, please check your osu songs folder setting in the Settings section", + ) + } else { + playNext() + playlistErrorCount.current = skipCount + 1 + } + }) + + let value = { + playingState: playingState, + pause: pause, + setAudio: setAudio, + setPlaylist: setPlaylist, + setVolume: setVolume, + togglePlayPause: togglePlayPause, + setMuted: setMuted, + playlist: playlist, + playNext: playNext, + playPrevious: playPrevious, + playlistID: playlistID, + } + children +} diff --git a/src/App/Providers/TaskProvider.re b/src/App/Providers/TaskProvider.re deleted file mode 100644 index 525dc0c..0000000 --- a/src/App/Providers/TaskProvider.re +++ /dev/null @@ -1,109 +0,0 @@ -[@bs.deriving {jsConverter: newType}] -type status = [ | `running | `terminated | `error]; - -module Task = { - type t = { - name: string, - description: string, - status, - section: Sections.t, - }; - - module Setters = { - let setName = (name, t) => {...t, name}; - let setDescription = (description, t) => {...t, description}; - let setStatus = (status, t) => {...t, status}; - let setSection = (section, t) => {...t, section}; - }; -}; - -type partialTask = { - name: string, - description: option(string), - status: option(status), -}; - -type value = { - add: Task.t => unit, - update: partialTask => unit, - terminate: string => unit, - tasks: Js.Array.t(Task.t), -}; - -module Provider = { - let value = { - add: task => Js.log(task), - update: partialTask => Js.log(partialTask), - terminate: taskName => Js.log(taskName), - tasks: [||], - }; - let taskContext = React.createContext(value); - - let makeProps = (~value, ~children, ()) => { - "value": value, - "children": children, - }; - - let make = React.Context.provider(taskContext); - - let updateTaskAt = (index, tasks, task) => - tasks->Belt.Array.mapWithIndex((i, currentTask) => - i === index ? task : currentTask - ); -}; - -let useTasks = () => React.useContext(Provider.taskContext); - -[@react.component] -let make = (~children) => { - let (tasks, setTasks) = React.useState(() => [||]); - let tasksRef = React.useRef(tasks); - React.useEffect1( - () => { - React.Ref.setCurrent(tasksRef, tasks); - None; - }, - [|tasks|], - ); - let add = (task: Task.t) => - setTasks(oldTasks => Js.Array.concat([|task|], oldTasks)); - let update = (partialTask: partialTask) => { - let tasks = React.Ref.current(tasksRef); - let taskIndex = - Js.Array.findIndex( - (task: Task.t) => task.name == partialTask.name, - tasks, - ); - if (taskIndex != (-1)) { - let {description, status} = partialTask; - switch (description, status) { - | (Some(description), Some(status)) => - setTasks(tasks => - tasks[taskIndex] - |> Task.Setters.setDescription(description) - |> Task.Setters.setStatus(status) - |> Provider.updateTaskAt(taskIndex, tasks) - ) - | (Some(description), None) => - setTasks(tasks => - tasks[taskIndex] - |> Task.Setters.setDescription(description) - |> Provider.updateTaskAt(taskIndex, tasks) - ) - | (None, Some(status)) => - setTasks(tasks => - tasks[taskIndex] - |> Task.Setters.setStatus(status) - |> Provider.updateTaskAt(taskIndex, tasks) - ) - | (None, None) => () - }; - }; - }; - let terminate = (taskName: string) => - setTasks(oldTasks => - Js.Array.filter((task: Task.t) => !(task.name == taskName), oldTasks) - ); - let value = {add, terminate, update, tasks}; - children ; -}; diff --git a/src/App/Providers/TaskProvider.res b/src/App/Providers/TaskProvider.res new file mode 100644 index 0000000..720a605 --- /dev/null +++ b/src/App/Providers/TaskProvider.res @@ -0,0 +1,96 @@ +@deriving({jsConverter: newType}) +type status = [#running | #terminated | #error] + +module Task = { + type t = { + name: string, + description: string, + status: status, + section: Sections.t, + } + + module Setters = { + let setName = (t, name) => {...t, name: name} + let setDescription = (t, description) => {...t, description: description} + let setStatus = (t, status) => {...t, status: status} + let setSection = (t, section) => {...t, section: section} + } +} + +type partialTask = { + name: string, + description: option, + status: option, +} + +type value = { + add: Task.t => unit, + update: partialTask => unit, + terminate: string => unit, + tasks: Js.Array.t, +} + +module Provider = { + let value = { + add: task => Js.log(task), + update: partialTask => Js.log(partialTask), + terminate: taskName => Js.log(taskName), + tasks: [], + } + let taskContext = React.createContext(value) + + let makeProps = (~value, ~children, ()) => + { + "value": value, + "children": children, + } + + let make = React.Context.provider(taskContext) + + let updateTaskAt = (task, index, tasks) => + tasks->Belt.Array.mapWithIndex((i, currentTask) => i === index ? task : currentTask) +} + +let useTasks = () => React.useContext(Provider.taskContext) + +@react.component +let make = (~children) => { + let (tasks, setTasks) = React.useState(() => []) + let tasksRef = React.useRef(tasks) + React.useEffect1(() => { + tasksRef.current = tasks + None + }, [tasks]) + let add = (task: Task.t) => setTasks(oldTasks => Js.Array.concat([task], oldTasks)) + let update = (partialTask: partialTask) => { + let tasks = tasksRef.current + let taskIndex = Js.Array.findIndex((task: Task.t) => task.name == partialTask.name, tasks) + if taskIndex != -1 { + let {description, status} = partialTask + switch (description, status) { + | (Some(description), Some(status)) => + setTasks(tasks => + tasks[taskIndex] + ->Task.Setters.setDescription(description) + ->Task.Setters.setStatus(status) + ->Provider.updateTaskAt(taskIndex, tasks) + ) + | (Some(description), None) => + setTasks(tasks => + tasks[taskIndex] + ->Task.Setters.setDescription(description) + ->Provider.updateTaskAt(taskIndex, tasks) + ) + | (None, Some(status)) => + setTasks(tasks => + tasks[taskIndex]->Task.Setters.setStatus(status)->Provider.updateTaskAt(taskIndex, tasks) + ) + | (None, None) => () + } + } + } + let terminate = (taskName: string) => + setTasks(oldTasks => Js.Array.filter((task: Task.t) => !(task.name == taskName), oldTasks)) + let value = {add: add, terminate: terminate, update: update, tasks: tasks} + children +} diff --git a/src/App/helpers/DiscordRPC.re b/src/App/helpers/DiscordRPC.re deleted file mode 100644 index d594012..0000000 --- a/src/App/helpers/DiscordRPC.re +++ /dev/null @@ -1,5 +0,0 @@ -[@bs.module "./discordRPC.js"] -external setPlayingSongPresence: (string, string) => unit = - "setPlayingSongPresence"; -[@bs.module "./discordRPC.js"] -external clearActivity: unit => unit = "clearActivity"; diff --git a/src/App/helpers/DiscordRPC.res b/src/App/helpers/DiscordRPC.res new file mode 100644 index 0000000..37f8502 --- /dev/null +++ b/src/App/helpers/DiscordRPC.res @@ -0,0 +1,4 @@ +@module("./discordRPC.js") +external setPlayingSongPresence: (string, string) => unit = "setPlayingSongPresence" +@module("./discordRPC.js") +external clearActivity: unit => unit = "clearActivity" diff --git a/src/App/helpers/Icon.re b/src/App/helpers/Icon.re deleted file mode 100644 index 4e1f181..0000000 --- a/src/App/helpers/Icon.re +++ /dev/null @@ -1,29 +0,0 @@ -[@bs.deriving jsConverter] -type jsRenderIconsArgs = { - name: string, - color: option(string), - width: option(int), - height: option(int), -}; - -[@bs.module "./renderIcons"] -external jsRenderIcons: - { - . - "color": option(string), - "height": option(int), - "name": string, - "width": option(int), - } => - React.element = - "default"; - -let make = - ( - ~name: string, - ~color: option(string)=?, - ~width: option(int)=?, - ~height: option(int)=?, - (), - ) => - {name, color, width, height}->jsRenderIconsArgsToJs->jsRenderIcons; \ No newline at end of file diff --git a/src/App/helpers/Icon.res b/src/App/helpers/Icon.res new file mode 100644 index 0000000..b5bdb48 --- /dev/null +++ b/src/App/helpers/Icon.res @@ -0,0 +1,23 @@ +@deriving(jsConverter) +type jsRenderIconsArgs = { + name: string, + color: option, + width: option, + height: option, +} + +@module("./renderIcons") +external jsRenderIcons: { + "color": option, + "height": option, + "name": string, + "width": option, +} => React.element = "default" + +let make = ( + ~name: string, + ~color: option=?, + ~width: option=?, + ~height: option=?, + (), +) => {name: name, color: color, width: width, height: height}->jsRenderIconsArgsToJs->jsRenderIcons diff --git a/src/App/modules/Sections.re b/src/App/modules/Sections.re deleted file mode 100644 index eceae60..0000000 --- a/src/App/modules/Sections.re +++ /dev/null @@ -1,2 +0,0 @@ -[@bs.deriving {jsConverter: newType}] -type t = [ | `Beatmaps | `Packs | `Downloads | `Bot | `Settings]; \ No newline at end of file diff --git a/src/App/modules/Sections.res b/src/App/modules/Sections.res new file mode 100644 index 0000000..8ed9b9a --- /dev/null +++ b/src/App/modules/Sections.res @@ -0,0 +1,2 @@ +@deriving({jsConverter: newType}) +type t = [#Beatmaps | #Packs | #Downloads | #Bot | #Settings] diff --git a/src/App/modules/common/Badge.re b/src/App/modules/common/Badge.re deleted file mode 100644 index c99aa86..0000000 --- a/src/App/modules/common/Badge.re +++ /dev/null @@ -1,138 +0,0 @@ -open Css; - -let makeBaseStyle = () => - style([ - border(px(2), solid, hex("2c3e50")), - borderRadius(rem(0.25)), - fontSize(rem(0.8)), - padding3(~top=rem(0.1), ~h=rem(0.45), ~bottom=rem(0.2)), - display(inlineFlex), - alignItems(center), - whiteSpace(nowrap), - ]); - -let makeStatusStyle = (status, difficulty) => - switch (status) { - | "info" => style([backgroundColor(hex("3498db"))]) - | "pending" => - style([ - backgroundColor( - difficulty->Belt.Option.mapWithDefault( - rgba(241, 196, 15), - PpyHelpers.getBeatmapDifficultyColorRGBA, - 0.5, - ), - ), - borderColor( - difficulty->Belt.Option.mapWithDefault( - rgba(241, 196, 15), - PpyHelpers.getBeatmapDifficultyColorRGBA, - 0.8, - ), - ), - hover([ - backgroundColor(rgba(241, 196, 15, 0.7)), - borderColor(rgb(241, 196, 15)), - ]), - ]) - | "qualified" => - style([ - backgroundColor(rgba(241, 196, 15, 0.50)), - borderColor(rgba(241, 196, 15, 0.8)), - hover([ - backgroundColor(rgba(241, 196, 15, 0.7)), - borderColor(rgb(241, 196, 15)), - ]), - ]) - | "graveyard" => - style([ - backgroundColor(rgba(231, 76, 60, 0.50)), - borderColor(rgba(231, 76, 60, 0.8)), - hover([ - backgroundColor(rgba(231, 76, 60, 0.7)), - borderColor(rgb(231, 76, 60)), - ]), - ]) - | "WIP" => - style([ - backgroundColor(rgba(231, 76, 60, 0.50)), - borderColor(rgba(231, 76, 60, 0.8)), - hover([ - backgroundColor(rgba(231, 76, 60, 0.7)), - borderColor(rgb(231, 76, 60)), - ]), - ]) - | "loved" => - style([ - backgroundColor(rgba(222, 90, 148, 0.50)), - borderColor(rgba(222, 90, 148, 0.8)), - hover([ - backgroundColor(rgba(222, 90, 148, 0.7)), - borderColor(rgb(222, 90, 148)), - ]), - ]) - | "ranked" => - style([ - backgroundColor( - difficulty->Belt.Option.mapWithDefault( - rgba(46, 204, 113), - PpyHelpers.getBeatmapDifficultyColorRGBA, - 0.5, - ), - ), - borderColor( - difficulty->Belt.Option.mapWithDefault( - rgba(46, 204, 113), - PpyHelpers.getBeatmapDifficultyColorRGBA, - 0.8, - ), - ), - hover([ - backgroundColor(rgba(46, 204, 113, 0.7)), - borderColor(rgb(46, 204, 113)), - ]), - ]) - | _ => style([backgroundColor(hex("2c3e50"))]) - }; - -let makeStyle = (status, difficulty) => - [makeBaseStyle(), makeStatusStyle(status, difficulty)]->merge; - -[@react.component] -let make = - ( - ~status, - ~difficulty: option(float), - ~difficultyText: option(string), - ~style=?, - ) => { - let (text, setText) = React.useState(() => status); - - React.useEffect1( - () => { - setText(_ => - switch (difficultyText) { - | Some(difficultyText) => difficultyText - | None => status - } - ); - None; - }, - [|difficultyText|], - ); - - setText(_ => status)} - onMouseLeave={_ => - setText(_ => - switch (difficultyText) { - | Some(difficultyText) => difficultyText - | None => status - } - ) - }> - text->React.string - ; -}; diff --git a/src/App/modules/common/Badge.res b/src/App/modules/common/Badge.res new file mode 100644 index 0000000..3cfb824 --- /dev/null +++ b/src/App/modules/common/Badge.res @@ -0,0 +1,110 @@ +open CssJs + +let makeBaseStyle = () => + style(. [ + border(px(2), solid, hex("2c3e50")), + borderRadius(rem(0.25)), + fontSize(rem(0.8)), + padding3(~top=rem(0.1), ~h=rem(0.45), ~bottom=rem(0.2)), + display(inlineFlex), + alignItems(center), + whiteSpace(nowrap), + ]) + +let makeStatusStyle = (status, difficulty) => + switch status { + | "info" => style(. [backgroundColor(hex("3498db"))]) + | "pending" => + style(. [ + backgroundColor( + difficulty->Belt.Option.mapWithDefault( + rgba(241, 196, 15), + PpyHelpers.getBeatmapDifficultyColorRGBA, + #num(0.5), + ), + ), + borderColor( + difficulty->Belt.Option.mapWithDefault( + rgba(241, 196, 15), + PpyHelpers.getBeatmapDifficultyColorRGBA, + #num(0.8), + ), + ), + hover([backgroundColor(rgba(241, 196, 15, #num(0.7))), borderColor(rgb(241, 196, 15))]), + ]) + | "qualified" => + style(. [ + backgroundColor(rgba(241, 196, 15, #num(0.50))), + borderColor(rgba(241, 196, 15, #num(0.8))), + hover([backgroundColor(rgba(241, 196, 15, #num(0.7))), borderColor(rgb(241, 196, 15))]), + ]) + | "graveyard" => + style(. [ + backgroundColor(rgba(231, 76, 60, #num(0.50))), + borderColor(rgba(231, 76, 60, #num(0.8))), + hover([backgroundColor(rgba(231, 76, 60, #num(0.7))), borderColor(rgb(231, 76, 60))]), + ]) + | "WIP" => + style(. [ + backgroundColor(rgba(231, 76, 60, #num(0.50))), + borderColor(rgba(231, 76, 60, #num(0.8))), + hover([backgroundColor(rgba(231, 76, 60, #num(0.7))), borderColor(rgb(231, 76, 60))]), + ]) + | "loved" => + style(. [ + backgroundColor(rgba(222, 90, 148, #num(0.50))), + borderColor(rgba(222, 90, 148, #num(0.8))), + hover([backgroundColor(rgba(222, 90, 148, #num(0.7))), borderColor(rgb(222, 90, 148))]), + ]) + | "ranked" => + style(. [ + backgroundColor( + difficulty->Belt.Option.mapWithDefault( + rgba(46, 204, 113), + PpyHelpers.getBeatmapDifficultyColorRGBA, + #num(0.5), + ), + ), + borderColor( + difficulty->Belt.Option.mapWithDefault( + rgba(46, 204, 113), + PpyHelpers.getBeatmapDifficultyColorRGBA, + #num(0.8), + ), + ), + hover([backgroundColor(rgba(46, 204, 113, #num(0.7))), borderColor(rgb(46, 204, 113))]), + ]) + | _ => style(. [backgroundColor(hex("2c3e50"))]) + } + +let makeStyle = (status, difficulty) => + [makeBaseStyle(), makeStatusStyle(status, difficulty)]->merge(. _) + +@react.component +let make = (~status, ~difficulty: option, ~difficultyText: option, ~style=?) => { + let (text, setText) = React.useState(() => status) + + React.useEffect1(() => { + setText(_ => + switch difficultyText { + | Some(difficultyText) => difficultyText + | None => status + } + ) + None + }, [difficultyText]) + + setText(_ => status)} + onMouseLeave={_ => + setText(_ => + switch difficultyText { + | Some(difficultyText) => difficultyText + | None => status + } + )}> + {text->React.string} + +} diff --git a/src/App/modules/common/Checkbox.re b/src/App/modules/common/Checkbox.re deleted file mode 100644 index 24128c8..0000000 --- a/src/App/modules/common/Checkbox.re +++ /dev/null @@ -1,27 +0,0 @@ -open Css; - -let checkBoxStyle = (~color, ~activeColor) => - style([ - unsafe("-webkit-appearance", "none"), - margin2(~v=auto, ~h=rem(1.)), - width(px(20)), - height(px(20)), - border(px(1), solid, hex(color)), - borderRadius(px(2)), - verticalAlign(middle), - backgroundColor(transparent), - selector( - "&:checked", - [borderColor(hex(activeColor)), backgroundColor(hex(activeColor))], - ), - ]); - -[@react.component] -let make = (~checked, ~disabled, ~color, ~activeColor) => { - ; -}; diff --git a/src/App/modules/common/Checkbox.res b/src/App/modules/common/Checkbox.res new file mode 100644 index 0000000..59dec1d --- /dev/null +++ b/src/App/modules/common/Checkbox.res @@ -0,0 +1,18 @@ +open CssJs + +let checkBoxStyle = (~color, ~activeColor) => + style(. [ + unsafe("-webkit-appearance", "none"), + margin2(~v=auto, ~h=rem(1.)), + width(px(20)), + height(px(20)), + border(px(1), solid, hex(color)), + borderRadius(px(2)), + verticalAlign(middle), + backgroundColor(transparent), + selector(. "&:checked", [borderColor(hex(activeColor)), backgroundColor(hex(activeColor))]), + ]) + +@react.component +let make = (~checked, ~disabled, ~color, ~activeColor) => + diff --git a/src/App/modules/common/ProgressRing.re b/src/App/modules/common/ProgressRing.res similarity index 50% rename from src/App/modules/common/ProgressRing.re rename to src/App/modules/common/ProgressRing.res index 63ed521..4b960ae 100644 --- a/src/App/modules/common/ProgressRing.re +++ b/src/App/modules/common/ProgressRing.res @@ -1,33 +1,32 @@ -open Css; +open CssJs let makeStyle = strokeDashoffset => - style([ + style(. [ transition(~duration=350, "stroke-dashoffset"), transform(rotate(deg(-90.))), transformOrigin(pct(50.), pct(50.)), unsafe("stroke-dashoffset", strokeDashoffset), - ]); + ]) -let floatToString = Js.Float.toString; +let floatToString = Js.Float.toString -[@react.component] +@react.component let make = (~radius, ~stroke, ~progress) => { - let normalizedRadius = radius -. stroke *. 2.; - let circumference = normalizedRadius *. 2. *. Js.Math._PI; - let strokeDashoffset = - (circumference -. progress /. 100. *. circumference)->floatToString; - let size = (radius *. 2.)->floatToString; + let normalizedRadius = radius -. stroke *. 2. + let circumference = normalizedRadius *. 2. *. Js.Math._PI + let strokeDashoffset = (circumference -. progress /. 100. *. circumference)->floatToString + let size = (radius *. 2.)->floatToString - + floatToString} - strokeDasharray={j|$circumference $circumference|j} + strokeDasharray={`${circumference->Js.Float.toString} ${circumference->Js.Float.toString}`} r={normalizedRadius->floatToString} cx={radius->floatToString} cy={radius->floatToString} /> - ; -}; + +} diff --git a/src/App/modules/common/TitleBar.re b/src/App/modules/common/TitleBar.re deleted file mode 100644 index f2f3879..0000000 --- a/src/App/modules/common/TitleBar.re +++ /dev/null @@ -1,66 +0,0 @@ -open Css; -let makeWrapperStyle = () => - style([ - zIndex(500), - position(fixed), - right(zero), - display(`flex), - alignItems(center), - ]); - -let makeControlStyle = (~bgColor=?, ~spacer=false, ()) => - style([ - unsafe("WebkitAppRegion", "no-drag"), - padding2(~v=zero, ~h=px(7)), - selector( - "&:hover", - [ - backgroundColor( - switch (bgColor) { - | None => rgba(255, 255, 255, 0.25) - | Some(c) => c - }, - ), - ], - ), - marginLeft(spacer ? `auto : `zero), - ]); - -[@react.component] -let make = (~height: int) => { - let {AudioPlayerProvider.playingState} = - AudioPlayerProvider.useAudioPlayer(); - let artist = playingState.artist; - let title = playingState.title; - let songTitle = {j|$artist - $title|j}; - let title = - playingState.isPlaying - ? {j|Beatconnect \u23F5 $songTitle|j} : "Beatconnect"; - // let window = Remote.getCurrentWindow(Remote.remote); - // window->BrowserWindow.setTitle(title); - - // let onMinimizeClick = _e => BrowserWindow.minimize(window); - // let onCloseClick = _e => BrowserWindow.close(window); - // let onMaximizeClick = _e => - // BrowserWindow.isMaximized(window) - // ? BrowserWindow.unmaximize(window) : BrowserWindow.maximize(window); - - let onMinimizeClick = _ => (); - let onCloseClick = _ => (); - let onMaximizeClick = _ => (); - -
-
- {Icon.make(~name="Dash", ~width=height, ~height, ())} -
-
- {Icon.make(~name="Square", ~width=height, ~height, ())} -
-
- {Icon.make(~name="Cancel", ~width=height, ~height, ())} -
-
; -}; - -let default = make; diff --git a/src/App/modules/common/TitleBar.res b/src/App/modules/common/TitleBar.res new file mode 100644 index 0000000..09fc7d1 --- /dev/null +++ b/src/App/modules/common/TitleBar.res @@ -0,0 +1,60 @@ +open CssJs + +let makeWrapperStyle = () => + style(. [zIndex(500), position(fixed), right(zero), display(#flex), alignItems(center)]) + +let makeControlStyle = (~bgColor=?, ~spacer=false, ()) => + style(. [ + unsafe("WebkitAppRegion", "no-drag"), + padding2(~v=zero, ~h=px(7)), + selector(. + "&:hover", + [ + backgroundColor( + switch bgColor { + | None => rgba(255, 255, 255, #num(0.25)) + | Some(c) => c + }, + ), + ], + ), + marginLeft(spacer ? #auto : #zero), + ]) + +@react.component +let make = (~height: int) => { + let {AudioPlayerProvider.playingState: playingState} = AudioPlayerProvider.useAudioPlayer() + let artist = playingState.artist + let title = playingState.title + let songTitle = `${artist} - ${title}` + + let _title = playingState.isPlaying ? `Beatconnect \\u23F5 ${songTitle}bot` : "Beatconnect" + + // let window = Remote.getCurrentWindow(Remote.remote) + // window->BrowserWindow.setTitle(title) + + // let onMinimizeClick = _e => BrowserWindow.minimize(window) + // let onCloseClick = _e => BrowserWindow.close(window) + // let onMaximizeClick = _e => + // BrowserWindow.isMaximized(window) + // ? BrowserWindow.unmaximize(window) + // : BrowserWindow.maximize(window) + + let onMinimizeClick = _ => () + let onCloseClick = _ => () + let onMaximizeClick = _ => () + +
+
+ {Icon.make(~name="Dash", ~width=height, ~height, ())} +
+
+ {Icon.make(~name="Square", ~width=height, ~height, ())} +
+
+ {Icon.make(~name="Cancel", ~width=height, ~height, ())} +
+
+} + +let default = make diff --git a/src/electron/helpers/OsuDbParser.re b/src/electron/helpers/OsuDbParser.res similarity index 76% rename from src/electron/helpers/OsuDbParser.re rename to src/electron/helpers/OsuDbParser.res index 61375ce..af55a19 100644 --- a/src/electron/helpers/OsuDbParser.re +++ b/src/electron/helpers/OsuDbParser.res @@ -51,24 +51,23 @@ type beatmap = { visual_override: bool, last_modification_time_2: float, mania_scroll_speed: int, -}; +} type osuDBData = { - beatmaps: list(beatmap), + beatmaps: list, userperms: int, isLocked: bool, -}; -type t; -[@bs.deriving {jsConverter: newType}] -type osuDatabase = [ | `osudb | `collection]; +} +type t +@deriving({jsConverter: newType}) +type osuDatabase = [#osudb | #collection] type osuDbParserInstance = { - . - [@bs.meth] "getOsuDBData": unit => osuDBData, - [@bs.meth] "setBuffer": (abs_osuDatabase, Buffer.t) => unit, -}; -[@bs.new] [@bs.module] -external make: unit => osuDbParserInstance = "osu-db-parser"; -let parser = make(); + getOsuDBData: unit => osuDBData, + setBuffer: (abs_osuDatabase, Buffer.t) => unit, +} +@new @module +external make: unit => osuDbParserInstance = "osu-db-parser" +let parser = make() let read = (buffer: Buffer.t) => { - parser##setBuffer(osuDatabaseToJs(`osudb), buffer); - parser##getOsuDBData(); -}; \ No newline at end of file + parser.setBuffer(osuDatabaseToJs(#osudb), buffer) + parser.getOsuDBData() +} diff --git a/src/shared/PpyHelpers.re b/src/shared/PpyHelpers.re deleted file mode 100644 index b3ed7b7..0000000 --- a/src/shared/PpyHelpers.re +++ /dev/null @@ -1,45 +0,0 @@ -let getThumbUrl = beatmapId => {j|https://b.ppy.sh/thumb/$beatmapId.jpg|j}; - -let getListCoverUrl = beatmapId => {j|https://assets.ppy.sh/beatmaps/$beatmapId/covers/list@2x.jpg|j}; - -let resolveThumbnail = (beatmapId, osuPath, fallbackUrl) => { - let localPath = - "file://" - ++ osuPath - ++ "/Data/bt/" - ++ beatmapId - ++ "l.jpg" - |> Js_global.encodeURI - |> Js_string.replaceByRe([%re "/\\/g/"], "/"); - - let thumbanil = Image.make(); - thumbanil->Image.setSrc(localPath); - thumbanil->Image.decode - |> Js_promise.then_(() => Js_promise.resolve(localPath)) - |> Js_promise.catch(_ => Js_promise.resolve(fallbackUrl)); -}; - -let resolveThumbURL = (beatmapId, osuPath) => - resolveThumbnail(beatmapId, osuPath, getListCoverUrl(beatmapId)); - -let getBeatmapDifficultyColorHex = (difficulty: float) => { - switch (difficulty) { - | difficulty when difficulty >= 6.5 => "#000" - | difficulty when difficulty >= 5.3 => "#8866ee" - | difficulty when difficulty >= 4.0 => "#ff66aa" - | difficulty when difficulty >= 2.7 => "#ffcc22" - | difficulty when difficulty >= 2.0 => "#66ccff" - | _ => "#88b300" - }; -}; - -let getBeatmapDifficultyColorRGBA = (difficulty: float) => { - switch (difficulty) { - | difficulty when difficulty >= 6.5 => Css.rgba(0, 0, 0) - | difficulty when difficulty >= 5.3 => Css.rgba(136, 102, 238) - | difficulty when difficulty >= 4.0 => Css.rgba(255, 102, 170) - | difficulty when difficulty >= 2.7 => Css.rgba(255, 204, 34) - | difficulty when difficulty >= 2.0 => Css.rgba(102, 204, 255) - | _ => Css.rgba(136, 179, 0) - }; -}; diff --git a/src/shared/PpyHelpers.res b/src/shared/PpyHelpers.res new file mode 100644 index 0000000..c078947 --- /dev/null +++ b/src/shared/PpyHelpers.res @@ -0,0 +1,40 @@ +let getThumbUrl = beatmapId => `https://b.ppy.sh/thumb/${beatmapId}.jpg` + +let getListCoverUrl = beatmapId => `https://assets.ppy.sh/beatmaps/${beatmapId}/covers/list@2x.jpg` + +let resolveThumbnail = (beatmapId, osuPath, fallbackUrl) => { + let localPath = + "file://" ++ + (osuPath ++ ("/Data/bt/" ++ (beatmapId ++ "l.jpg"))) + ->Js.Global.encodeURI + ->Js.String2.replaceByRe(%re("/\\/g/"), "/") + + let thumbanil = Image.make() + thumbanil->Image.setSrc(localPath) + thumbanil->Image.decode + |> Js.Promise.then_(() => Js_promise.resolve(localPath)) + |> Js.Promise.catch(_ => Js_promise.resolve(fallbackUrl)) +} + +let resolveThumbURL = (beatmapId, osuPath) => + resolveThumbnail(beatmapId, osuPath, getListCoverUrl(beatmapId)) + +let getBeatmapDifficultyColorHex = (difficulty: float) => + switch difficulty { + | difficulty if difficulty >= 6.5 => "#000" + | difficulty if difficulty >= 5.3 => "#8866ee" + | difficulty if difficulty >= 4.0 => "#ff66aa" + | difficulty if difficulty >= 2.7 => "#ffcc22" + | difficulty if difficulty >= 2.0 => "#66ccff" + | _ => "#88b300" + } + +let getBeatmapDifficultyColorRGBA = (difficulty: float) => + switch difficulty { + | difficulty if difficulty >= 6.5 => Css.rgba(0, 0, 0) + | difficulty if difficulty >= 5.3 => Css.rgba(136, 102, 238) + | difficulty if difficulty >= 4.0 => Css.rgba(255, 102, 170) + | difficulty if difficulty >= 2.7 => Css.rgba(255, 204, 34) + | difficulty if difficulty >= 2.0 => Css.rgba(102, 204, 255) + | _ => Css.rgba(136, 179, 0) + } diff --git a/src/shared/dom/Audio.re b/src/shared/dom/Audio.re deleted file mode 100644 index 4e986b1..0000000 --- a/src/shared/dom/Audio.re +++ /dev/null @@ -1,35 +0,0 @@ -type t; -type callback('a) = 'a => unit; - -type target = { - readyState: int, - volume: int, -}; - -type partialAudioEvent = {target}; - -[@bs.new] external make: unit => t = "Audio"; -[@bs.new] external makeWithSrc: (~src: string) => t = "Audio"; - -[@bs.get] external paused: t => bool = "paused"; -[@bs.get] external getMuted: t => bool = "muted"; -[@bs.get] external getVolume: t => int = "volume"; -[@bs.set] external setSrc: (t, string) => unit = "src"; -[@bs.set] external setVolume: (t, float) => unit = "volume"; -[@bs.set] external setCurrentTime: (t, int) => unit = "currentTime"; -[@bs.set] external setMuted: (t, bool) => unit = "muted"; -[@bs.send] external pause: t => unit = "pause"; -[@bs.send] external play: t => unit = "play"; -[@bs.set] -external onended: (t, callback(partialAudioEvent)) => unit = "onended"; -[@bs.set] -external onerror: (t, callback(partialAudioEvent)) => unit = "onerror"; -[@bs.set] -external onpause: (t, callback(partialAudioEvent)) => unit = "onpause"; -[@bs.set] -external onplay: (t, callback(partialAudioEvent)) => unit = "onplay"; -[@bs.set] -external oncanplay: (t, callback(partialAudioEvent)) => unit = "oncanplay"; -[@bs.set] -external onvolumechange: (t, callback(partialAudioEvent)) => unit = - "onvolumechange"; diff --git a/src/shared/dom/Audio.res b/src/shared/dom/Audio.res new file mode 100644 index 0000000..59b4bbf --- /dev/null +++ b/src/shared/dom/Audio.res @@ -0,0 +1,34 @@ +type t +type callback<'a> = 'a => unit + +type target = { + readyState: int, + volume: int, +} + +type partialAudioEvent = {target: target} + +@new external make: unit => t = "Audio" +@new external makeWithSrc: (~src: string) => t = "Audio" + +@get external paused: t => bool = "paused" +@get external getMuted: t => bool = "muted" +@get external getVolume: t => int = "volume" +@set external setSrc: (t, string) => unit = "src" +@set external setVolume: (t, float) => unit = "volume" +@set external setCurrentTime: (t, int) => unit = "currentTime" +@set external setMuted: (t, bool) => unit = "muted" +@send external pause: t => unit = "pause" +@send external play: t => unit = "play" +@set +external onended: (t, callback) => unit = "onended" +@set +external onerror: (t, callback) => unit = "onerror" +@set +external onpause: (t, callback) => unit = "onpause" +@set +external onplay: (t, callback) => unit = "onplay" +@set +external oncanplay: (t, callback) => unit = "oncanplay" +@set +external onvolumechange: (t, callback) => unit = "onvolumechange" diff --git a/src/shared/dom/Image.re b/src/shared/dom/Image.re deleted file mode 100644 index 09e66c3..0000000 --- a/src/shared/dom/Image.re +++ /dev/null @@ -1,7 +0,0 @@ -type t; - -[@bs.new] external make: unit => t = "Image"; -[@bs.new] external makeWithSize: (~width: int, ~height: int) => t = "Image"; - -[@bs.send] external decode: t => Js.Promise.t(unit) = "decode"; -[@bs.set] external setSrc: (t, string) => unit = "src"; diff --git a/src/shared/dom/Image.res b/src/shared/dom/Image.res new file mode 100644 index 0000000..7771d77 --- /dev/null +++ b/src/shared/dom/Image.res @@ -0,0 +1,7 @@ +type t + +@new external make: unit => t = "Image" +@new external makeWithSize: (~width: int, ~height: int) => t = "Image" + +@send external decode: t => Js.Promise.t = "decode" +@set external setSrc: (t, string) => unit = "src" diff --git a/src/shared/dom/MediaMetadata.re b/src/shared/dom/MediaMetadata.res similarity index 70% rename from src/shared/dom/MediaMetadata.re rename to src/shared/dom/MediaMetadata.res index 5e1abbb..8505474 100644 --- a/src/shared/dom/MediaMetadata.re +++ b/src/shared/dom/MediaMetadata.res @@ -1,22 +1,22 @@ -type t; +type t type artwork = { src: string, sizes: string, type_: string, -}; +} type metadata = { title: string, artist: string, album: string, - artwork: array(artwork), -}; + artwork: array, +} -[@bs.new] external make: metadata => t = "MediaMetadata"; +@new external make: metadata => t = "MediaMetadata" let makeArtwork = id => { src: PpyHelpers.getListCoverUrl(id), sizes: "160x100", type_: "image/jpg", -}; +} diff --git a/src/shared/dom/MediaSession.re b/src/shared/dom/MediaSession.re deleted file mode 100644 index 9cb31f0..0000000 --- a/src/shared/dom/MediaSession.re +++ /dev/null @@ -1,20 +0,0 @@ -type t; -type navigator; - -[@bs.deriving {jsConverter: newType}] -type actionType = [ | `play | `pause | `previoustrack | `nexttrack | `stop]; - -[@bs.val] external mediaSession: t = "navigator.mediaSession"; - -[@bs.val] external navigator: navigator = "navigator"; - -[@bs.send] -external setActionHandler: (t, actionType, option(unit => unit)) => unit = - "setActionHandler"; - -let setActionHandler = setActionHandler(mediaSession); - -[@bs.set] -external setMediaSessionMetadata: (t, MediaMetadata.t) => unit = "metadata"; - -let setMediaSessionMetadata = setMediaSessionMetadata(mediaSession); diff --git a/src/shared/dom/MediaSession.res b/src/shared/dom/MediaSession.res new file mode 100644 index 0000000..1fa2e4a --- /dev/null +++ b/src/shared/dom/MediaSession.res @@ -0,0 +1,19 @@ +type t +type navigator + +@deriving({jsConverter: newType}) +type actionType = [#play | #pause | #previoustrack | #nexttrack | #stop] + +@val external mediaSession: t = "navigator.mediaSession" + +@val external navigator: navigator = "navigator" + +@send +external setActionHandler: (t, actionType, option unit>) => unit = "setActionHandler" + +let setActionHandler = setActionHandler(mediaSession) + +@set +external setMediaSessionMetadata: (t, MediaMetadata.t) => unit = "metadata" + +let setMediaSessionMetadata = setMediaSessionMetadata(mediaSession) diff --git a/src/shared/electron/BrowserWindow.re b/src/shared/electron/BrowserWindow.re deleted file mode 100644 index f4cf909..0000000 --- a/src/shared/electron/BrowserWindow.re +++ /dev/null @@ -1,8 +0,0 @@ -type t; - -[@bs.send] external setTitle: (t, string) => unit = "setTitle"; -[@bs.send] external isMaximized: t => bool = "isMaximized"; -[@bs.send] external unmaximize: t => unit = "unmaximize"; -[@bs.send] external maximize: t => unit = "maximize"; -[@bs.send] external close: t => unit = "close"; -[@bs.send] external minimize: t => unit = "minimize"; \ No newline at end of file diff --git a/src/shared/electron/BrowserWindow.res b/src/shared/electron/BrowserWindow.res new file mode 100644 index 0000000..6f3d59c --- /dev/null +++ b/src/shared/electron/BrowserWindow.res @@ -0,0 +1,8 @@ +type t + +@send external setTitle: (t, string) => unit = "setTitle" +@send external isMaximized: t => bool = "isMaximized" +@send external unmaximize: t => unit = "unmaximize" +@send external maximize: t => unit = "maximize" +@send external close: t => unit = "close" +@send external minimize: t => unit = "minimize" diff --git a/src/shared/electron/IPCRenderer.re b/src/shared/electron/IPCRenderer.re deleted file mode 100644 index d618315..0000000 --- a/src/shared/electron/IPCRenderer.re +++ /dev/null @@ -1,29 +0,0 @@ -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); diff --git a/src/shared/electron/IPCRenderer.res b/src/shared/electron/IPCRenderer.res new file mode 100644 index 0000000..448d933 --- /dev/null +++ b/src/shared/electron/IPCRenderer.res @@ -0,0 +1,26 @@ +type t +@module("electron") external remote: t = "ipcRenderer" + +@send external send: (t, string, 'a) => unit = "send" +@send external on: (t, string, 'a => unit) => unit = "on" +@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) diff --git a/src/shared/electron/Remote.re b/src/shared/electron/Remote.re deleted file mode 100644 index 958a7e1..0000000 --- a/src/shared/electron/Remote.re +++ /dev/null @@ -1,5 +0,0 @@ -type t; - -[@bs.module "electron"] external remote: t = "remote"; -[@bs.send] -external getCurrentWindow: t => BrowserWindow.t = "getCurrentWindow"; \ No newline at end of file diff --git a/src/shared/electron/Remote.res b/src/shared/electron/Remote.res new file mode 100644 index 0000000..a096d7f --- /dev/null +++ b/src/shared/electron/Remote.res @@ -0,0 +1,5 @@ +type t + +@module("electron") external remote: t = "remote" +@send +external getCurrentWindow: t => BrowserWindow.t = "getCurrentWindow" diff --git a/yarn.lock b/yarn.lock index 21ea5cd..62e66ca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -249,7 +249,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-imports@npm:^7.0.0, @babel/helper-module-imports@npm:^7.12.13, @babel/helper-module-imports@npm:^7.16.7": +"@babel/helper-module-imports@npm:^7.12.13, @babel/helper-module-imports@npm:^7.16.7": version: 7.16.7 resolution: "@babel/helper-module-imports@npm:7.16.7" dependencies: @@ -290,6 +290,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-plugin-utils@npm:^7.17.12": + version: 7.17.12 + resolution: "@babel/helper-plugin-utils@npm:7.17.12" + checksum: 300da74658e2b958bb488d4ca27757e676b14f9978501dfb3a2fc505d4322d58fbd4f7b79470299828f61cf880dff8fce83bbe942f0a00e0b86a8fdc21f6cc8d + languageName: node + linkType: hard + "@babel/helper-remap-async-to-generator@npm:^7.16.8": version: 7.16.8 resolution: "@babel/helper-remap-async-to-generator@npm:7.16.8" @@ -735,6 +742,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-syntax-jsx@npm:^7.12.13": + version: 7.17.12 + resolution: "@babel/plugin-syntax-jsx@npm:7.17.12" + dependencies: + "@babel/helper-plugin-utils": ^7.17.12 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: a5dde0b0fc6493a4b1f0341a7b68dcfa0c1ba1553e8aa21e995320cb4267df619c7c20c0840f14f13c302f4bef4759a92cde3f69c0be1ff489dcc2835bc4de53 + languageName: node + linkType: hard + "@babel/plugin-syntax-jsx@npm:^7.16.7": version: 7.16.7 resolution: "@babel/plugin-syntax-jsx@npm:7.16.7" @@ -1471,6 +1489,15 @@ __metadata: languageName: node linkType: hard +"@babel/runtime@npm:^7.13.10": + version: 7.18.3 + resolution: "@babel/runtime@npm:7.18.3" + dependencies: + regenerator-runtime: ^0.13.4 + checksum: 70aa471de7452e9ad6d1f7ef880b76558b102a97d81aaf3251a6d70b90de7388ac0e7f5a9f5b5266ee046df92451cd7e1b310c043a5f131704949138d4790643 + languageName: node + linkType: hard + "@babel/template@npm:^7.12.13, @babel/template@npm:^7.16.7, @babel/template@npm:^7.3.3, @babel/template@npm:^7.4.0": version: 7.16.7 resolution: "@babel/template@npm:7.16.7" @@ -1576,19 +1603,60 @@ __metadata: languageName: node linkType: hard -"@emotion/cache@npm:^10.0.27": - version: 10.0.29 - resolution: "@emotion/cache@npm:10.0.29" +"@emotion/babel-plugin@npm:^11.7.1": + version: 11.9.2 + resolution: "@emotion/babel-plugin@npm:11.9.2" dependencies: - "@emotion/sheet": 0.9.4 - "@emotion/stylis": 0.8.5 - "@emotion/utils": 0.11.3 - "@emotion/weak-memoize": 0.2.5 - checksum: a2bdf4dd21b63d5e88999bfefe841e5a7c64783d321f94bb930d28d18e8d15c80997c84a21996856d9356ef36d70e1ea1209f23de0c2f9e65c8b86e7d3a2f192 + "@babel/helper-module-imports": ^7.12.13 + "@babel/plugin-syntax-jsx": ^7.12.13 + "@babel/runtime": ^7.13.10 + "@emotion/hash": ^0.8.0 + "@emotion/memoize": ^0.7.5 + "@emotion/serialize": ^1.0.2 + babel-plugin-macros: ^2.6.1 + convert-source-map: ^1.5.0 + escape-string-regexp: ^4.0.0 + find-root: ^1.1.0 + source-map: ^0.5.7 + stylis: 4.0.13 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 5956bc9eefe556c95323b198aa337abdcca30bc0624c3cae93378f262bded7992582a7e7d2f86868dbd3a831f7ae78b79714e9a36a124e1f6763d8706d4716ad languageName: node linkType: hard -"@emotion/hash@npm:0.8.0": +"@emotion/cache@npm:^11.0.0, @emotion/cache@npm:^11.7.1": + version: 11.7.1 + resolution: "@emotion/cache@npm:11.7.1" + dependencies: + "@emotion/memoize": ^0.7.4 + "@emotion/sheet": ^1.1.0 + "@emotion/utils": ^1.0.0 + "@emotion/weak-memoize": ^0.2.5 + stylis: 4.0.13 + checksum: 60b5ce3fcc9caafa4d6a3c72079f1e4051bdfd50c44321f6263555cf5f6acc07706da5328615c1b0b52fa0dd7cf4a41bcf2ee1aae45b7aa25ad73d867f720189 + languageName: node + linkType: hard + +"@emotion/css@npm:^11.0.0": + version: 11.9.0 + resolution: "@emotion/css@npm:11.9.0" + dependencies: + "@emotion/babel-plugin": ^11.7.1 + "@emotion/cache": ^11.7.1 + "@emotion/serialize": ^1.0.3 + "@emotion/sheet": ^1.0.3 + "@emotion/utils": ^1.0.0 + peerDependencies: + "@babel/core": ^7.0.0 + peerDependenciesMeta: + "@babel/core": + optional: true + checksum: 4c835e473e9dff1b9017fb6e782a356bb5b7593ef7f4c11f5827288f25ecdb970456f258c65bfa3cc7a48e6de0eea8287df605d1d8cda4b476fd5da7388e1789 + languageName: node + linkType: hard + +"@emotion/hash@npm:^0.8.0": version: 0.8.0 resolution: "@emotion/hash@npm:0.8.0" checksum: 8fd781e18428745d6c7121bebf3965cad12c61f3bd5fb773e46f16f1d7b7ae1346770df438fcfe8bc73ecf6762a54baef7cf259a694575d4f06cabb79ebcf7c0 @@ -1611,55 +1679,48 @@ __metadata: languageName: node linkType: hard -"@emotion/memoize@npm:0.7.4": - version: 0.7.4 - resolution: "@emotion/memoize@npm:0.7.4" - checksum: 874123a94c89963dda3438d1ea7f7c17fa670d965610eefaa49e0dbf47cccee6f6108e04175867d7e485d2c04096a98bba5a4bef2606b3bf2070637327ebe3ff +"@emotion/memoize@npm:^0.7.4, @emotion/memoize@npm:^0.7.5": + version: 0.7.5 + resolution: "@emotion/memoize@npm:0.7.5" + checksum: 09f5290283c50ae32f0808bebacf7eaac7ecdbd799a27698479bd03821f3acbd2ac512adcf0ae43394fed804007d6cd18de0c49475a14cd020d5f18fc7ab72bf languageName: node linkType: hard -"@emotion/serialize@npm:^0.11.15, @emotion/serialize@npm:^0.11.16": - version: 0.11.16 - resolution: "@emotion/serialize@npm:0.11.16" +"@emotion/serialize@npm:^1.0.2, @emotion/serialize@npm:^1.0.3": + version: 1.0.3 + resolution: "@emotion/serialize@npm:1.0.3" dependencies: - "@emotion/hash": 0.8.0 - "@emotion/memoize": 0.7.4 - "@emotion/unitless": 0.7.5 - "@emotion/utils": 0.11.3 - csstype: ^2.5.7 - checksum: b7d9a94a039ea5e86b0daccc29f0c40fbbda7004b1fde9ee14be38463c3aaabdd027b38c5c0e580f66883fb6e3b49160292843b0e14efe49e662a520c38f4c15 + "@emotion/hash": ^0.8.0 + "@emotion/memoize": ^0.7.4 + "@emotion/unitless": ^0.7.5 + "@emotion/utils": ^1.0.0 + csstype: ^3.0.2 + checksum: 4a02aaa3594cd2821f887212eb989459aea90bc47b166a1337838b76392267408dffb49b90e4d9dcb5a2b80e34ec71f0c9aea401a830604f93b7e2358c78ce2c languageName: node linkType: hard -"@emotion/sheet@npm:0.9.4": - version: 0.9.4 - resolution: "@emotion/sheet@npm:0.9.4" - checksum: 48baa183e56d78df56ad474d833476fc26c0d563e931a82245e38975fe47ed338155d73dd5769c675aa821feef2e0480f4285c0584fc5271026f276316a299c1 +"@emotion/sheet@npm:^1.0.3, @emotion/sheet@npm:^1.1.0": + version: 1.1.0 + resolution: "@emotion/sheet@npm:1.1.0" + checksum: 9ffd9df2a86d0bf2b23b489adaebb38ec763be91772a25547164ed5058910270f59dc8fb8c181f2da000231deb35fc4b9cf31c00f70e15fb1ce89c76053f7f5f languageName: node linkType: hard -"@emotion/stylis@npm:0.8.5": - version: 0.8.5 - resolution: "@emotion/stylis@npm:0.8.5" - checksum: bb43a77f784cce86f7a625519544aab56b8f341117957f7dd15315398780289784bd2ec0ba1bc1b19ac639bdb304a4ed08b1f8e3e4c13e8063b9824e551b3994 - languageName: node - linkType: hard - -"@emotion/unitless@npm:0.7.5": +"@emotion/unitless@npm:^0.7.5": version: 0.7.5 resolution: "@emotion/unitless@npm:0.7.5" checksum: 0be366ef09860037ef08aed0450cb5510f4be25886005e2f120f8e8b7385de6b41ac47df5b9bd55781e5153853e9ed5f49aa517dcbad34cc23bd8afb0201932a languageName: node linkType: hard -"@emotion/utils@npm:0.11.3": - version: 0.11.3 - resolution: "@emotion/utils@npm:0.11.3" - checksum: b5c3a22204a878eafa8deb362493b48c3f3a7f7ec3e5a18634d14aa8d5c9d8274db0a9d0206b3c124fa170640880fbcda987971699a1cf69355f2cec994da487 +"@emotion/utils@npm:^1.0.0": + version: 1.1.0 + resolution: "@emotion/utils@npm:1.1.0" + checksum: ab04aafa16c7c3e0b98a68f10ca388edd4af0ca299861dc700c1b02f44e0a355f9af6732250a2772e572c607100abe6f09eec19a9a9631f50d5866c11750830a languageName: node linkType: hard -"@emotion/weak-memoize@npm:0.2.5": +"@emotion/weak-memoize@npm:^0.2.5": version: 0.2.5 resolution: "@emotion/weak-memoize@npm:0.2.5" checksum: 9fe31f0c9d761468d7868be2faf924ddef3506160c72a1979ced8f72cec5d90499403a29c904af570496ef06803e484495f84d4c311bd0787259c89dba4119ed @@ -2147,6 +2208,16 @@ __metadata: languageName: node linkType: hard +"@rescript/react@npm:0.10.3": + version: 0.10.3 + resolution: "@rescript/react@npm:0.10.3" + peerDependencies: + react: ">=16.8.1" + react-dom: ">=16.8.1" + checksum: 8b3ab98ed0b9122b878a4d3bf01ac2657059ad89642204948057bd822b0ff01c4631e113d57e9bf75b01fbe63a2f8dec149121643c9c3a47d9532d853fd07835 + languageName: node + linkType: hard + "@sindresorhus/is@npm:^0.14.0": version: 0.14.0 resolution: "@sindresorhus/is@npm:0.14.0" @@ -3639,24 +3710,6 @@ __metadata: languageName: node linkType: hard -"babel-plugin-emotion@npm:^10.0.27": - version: 10.2.2 - resolution: "babel-plugin-emotion@npm:10.2.2" - dependencies: - "@babel/helper-module-imports": ^7.0.0 - "@emotion/hash": 0.8.0 - "@emotion/memoize": 0.7.4 - "@emotion/serialize": ^0.11.16 - babel-plugin-macros: ^2.0.0 - babel-plugin-syntax-jsx: ^6.18.0 - convert-source-map: ^1.5.0 - escape-string-regexp: ^1.0.5 - find-root: ^1.1.0 - source-map: ^0.5.7 - checksum: 6def612598c8dbb74942271b8cb406eccad0d1a6b4ec1180f8205f5e92362b3e200bc566fdec58e37076a6a602e95c226fc9d28ed6a558fff748637cb214e3c9 - languageName: node - linkType: hard - "babel-plugin-istanbul@npm:^5.1.0": version: 5.2.0 resolution: "babel-plugin-istanbul@npm:5.2.0" @@ -3694,7 +3747,7 @@ __metadata: languageName: node linkType: hard -"babel-plugin-macros@npm:^2.0.0": +"babel-plugin-macros@npm:^2.6.1": version: 2.8.0 resolution: "babel-plugin-macros@npm:2.8.0" dependencies: @@ -3761,13 +3814,6 @@ __metadata: languageName: node linkType: hard -"babel-plugin-syntax-jsx@npm:^6.18.0": - version: 6.18.0 - resolution: "babel-plugin-syntax-jsx@npm:6.18.0" - checksum: a5c8174ad6165d5f541f9f31cf4b6338ccfb7d586cec111537fa567f13b5fbdcf54f7928db44429d4610aa1be9d07bb03d017b22ba521ff819a6a2090b694797 - languageName: node - linkType: hard - "babel-plugin-transform-react-remove-prop-types@npm:^0.4.24": version: 0.4.24 resolution: "babel-plugin-transform-react-remove-prop-types@npm:0.4.24" @@ -3884,6 +3930,7 @@ __metadata: dependencies: "@babel/core": 7.13.13 "@babel/preset-env": ^7.13.12 + "@rescript/react": 0.10.3 "@svgr/webpack": 5.5.0 acorn-dynamic-import: ^4.0.0 babel-eslint: 10.1.0 @@ -3891,8 +3938,8 @@ __metadata: babel-loader: 8.2.2 babel-plugin-named-asset-import: ^0.3.7 babel-preset-react-app: ^10.0.0 - bs-css: ^11.0.0 - bs-platform: 8.4.2 + bs-css: ^15.3.0 + bs-css-emotion: ^4.3.0 camelcase: ^5.2.0 case-sensitive-paths-webpack-plugin: 2.3.0 conventional-changelog-cli: ^2.1.1 @@ -3939,8 +3986,8 @@ __metadata: react-jss: ^10.6.0 react-redux: ^7.2.3 react-window: ^1.8.6 - reason-react: ^0.7.1 redux: ^4.0.5 + rescript: 9.1.4 resolve: 1.17.0 semver: 6.0.0 string-replace-loader: ^2.3.0 @@ -4230,24 +4277,21 @@ __metadata: languageName: node linkType: hard -"bs-css@npm:^11.0.0": - version: 11.0.0 - resolution: "bs-css@npm:11.0.0" +"bs-css-emotion@npm:^4.3.0": + version: 4.3.0 + resolution: "bs-css-emotion@npm:4.3.0" dependencies: - emotion: ^10.0.7 - checksum: 299817a12c9d21d91b43d9309c738ff8e269eaa1f89fe725fc36dbd570072d65dfcad8a086994594b7e5ce4fc3a136ec1a36a16de612032a4cb2fc5184b6d26d + "@emotion/cache": ^11.0.0 + "@emotion/css": ^11.0.0 + bs-css: 15.3.0 + checksum: df84803a9a8716010778999daf9283de29268ca1fcbaa558a95b312ba811f7c701845230bd2f50f6c19424528e8c3484c344d2ec702542687f32bdc9ab5f3c11 languageName: node linkType: hard -"bs-platform@npm:8.4.2": - version: 8.4.2 - resolution: "bs-platform@npm:8.4.2" - bin: - bsb: bsb - bsc: bsc - bsrefmt: bsrefmt - bstracing: lib/bstracing - checksum: 60f84c4c63809031d2e8609f90ef877174b9461d8ca8e3697c921548648a7187ddc977ce210dd2df91568c5ffc46a8ffe16dcf8836ddb6637d6dcbaaef306a08 +"bs-css@npm:15.3.0, bs-css@npm:^15.3.0": + version: 15.3.0 + resolution: "bs-css@npm:15.3.0" + checksum: 0b9b954541dd48d5b0d3accaad222c110d905725c7356f3dfd695296d75582ae976904fb80dedddbe1864803ef9cf6e221fbb6b4508e7740134da8bc0e4d5392 languageName: node linkType: hard @@ -5465,18 +5509,6 @@ __metadata: languageName: node linkType: hard -"create-emotion@npm:^10.0.27": - version: 10.0.27 - resolution: "create-emotion@npm:10.0.27" - dependencies: - "@emotion/cache": ^10.0.27 - "@emotion/serialize": ^0.11.15 - "@emotion/sheet": 0.9.4 - "@emotion/utils": 0.11.3 - checksum: 59581a54fe4f6da6bcbae502df852f0cf0dc6f002aaf416701266a4d944e63d9e33ac0d733b35d8a6265f4052dfdba67222ae341e8f9382afb2e40ed03831053 - languageName: node - linkType: hard - "create-hash@npm:^1.1.0, create-hash@npm:^1.1.2, create-hash@npm:^1.2.0": version: 1.2.0 resolution: "create-hash@npm:1.2.0" @@ -5694,13 +5726,6 @@ __metadata: languageName: node linkType: hard -"csstype@npm:^2.5.7": - version: 2.6.20 - resolution: "csstype@npm:2.6.20" - checksum: 6f7ec09b01460f266d32b6963c386c0412b29775db5a1137986c85daf5df8db395d4b04d5e10b57f358e838fec278c9361263be7a675c67efa4fcb873fa073b3 - languageName: node - linkType: hard - "csstype@npm:^3.0.2": version: 3.0.11 resolution: "csstype@npm:3.0.11" @@ -6507,16 +6532,6 @@ __metadata: languageName: node linkType: hard -"emotion@npm:^10.0.7": - version: 10.0.27 - resolution: "emotion@npm:10.0.27" - dependencies: - babel-plugin-emotion: ^10.0.27 - create-emotion: ^10.0.27 - checksum: cc542b2df96732b0c60683a60c94e42d226ac37854c73d33c6e32f596dce52261a4c07a841edf621f44166a4b5a146e4cb18d294ae6759a5522f80e2dba5cd44 - languageName: node - linkType: hard - "encodeurl@npm:^1.0.2, encodeurl@npm:~1.0.2": version: 1.0.2 resolution: "encodeurl@npm:1.0.2" @@ -13082,7 +13097,7 @@ fsevents@^1.2.7: languageName: node linkType: hard -"react-dom@npm:>=16.8.1, react-dom@npm:^17.0.2": +"react-dom@npm:^17.0.2": version: 17.0.2 resolution: "react-dom@npm:17.0.2" dependencies: @@ -13171,7 +13186,7 @@ fsevents@^1.2.7: languageName: node linkType: hard -"react@npm:>=16.8.1, react@npm:^17.0.2": +"react@npm:^17.0.2": version: 17.0.2 resolution: "react@npm:17.0.2" dependencies: @@ -13303,16 +13318,6 @@ fsevents@^1.2.7: languageName: node linkType: hard -"reason-react@npm:^0.7.1": - version: 0.7.1 - resolution: "reason-react@npm:0.7.1" - dependencies: - react: ">=16.8.1" - react-dom: ">=16.8.1" - checksum: 66f0aa4817ad90db10e61dd18e7e6c5ee517dd54f6ad39c69cfdcfba03aecc2e330a3299fd735a3e7c781ae8f0b7b818c1eabcd42b38abae4eb23db3ddbece16 - languageName: node - linkType: hard - "recursive-readdir@npm:2.2.2": version: 2.2.2 resolution: "recursive-readdir@npm:2.2.2" @@ -13571,6 +13576,18 @@ fsevents@^1.2.7: languageName: node linkType: hard +"rescript@npm:9.1.4": + version: 9.1.4 + resolution: "rescript@npm:9.1.4" + bin: + bsc: bsc + bsrefmt: bsrefmt + bstracing: lib/bstracing + rescript: rescript + checksum: 96638bedf42159713b67dde4cd7c2008775f1f310ba41f65d879521983a011bcfbfb7ab0b404af9084ff25ccedc14f2289de8805002fe95154a11e75cbe87145 + languageName: node + linkType: hard + "resolve-cwd@npm:^2.0.0": version: 2.0.0 resolution: "resolve-cwd@npm:2.0.0" @@ -14859,6 +14876,13 @@ resolve@^2.0.0-next.3: languageName: node linkType: hard +"stylis@npm:4.0.13": + version: 4.0.13 + resolution: "stylis@npm:4.0.13" + checksum: 2c91ba8bd0de9c210d7a8ef94a0fbbef954f3e64af7185e21f6aa003d27d56f5073a7fb09075857e6ea056ca6c56feb8e55e8e9ddf44cea0f58d76e3b07fc3a8 + languageName: node + linkType: hard + "sumchecker@npm:^3.0.1": version: 3.0.1 resolution: "sumchecker@npm:3.0.1"