mirror of
https://github.com/Zagrios/bs-manager.git
synced 2026-07-03 14:08:25 +02:00
Compare commits
126 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 571a4f23ea | |||
| 2b9829d57a | |||
| 2f6ea39f79 | |||
| 8e33214fd0 | |||
| 310dfb94c2 | |||
| 39e2a54e1e | |||
| 829cace7b9 | |||
| e82afa7e22 | |||
| 5e64ce16f4 | |||
| 2154d90759 | |||
| d889c85b4a | |||
| cc4d033db2 | |||
| 1cfef9f607 | |||
| bbd26d1c12 | |||
| 72f0c22e88 | |||
| 26765c45c7 | |||
| 2afcaa46f9 | |||
| 3c5b2a2eed | |||
| 0ff8e865aa | |||
| 2d60acce51 | |||
| 53fb197585 | |||
| 3c11f5a0a1 | |||
| bef27b4ac5 | |||
| 0d0cfa1b4e | |||
| 286f4aa68d | |||
| 58f563ed61 | |||
| 523575e43c | |||
| 414017930e | |||
| 01550557fb | |||
| cdfb957203 | |||
| c647099f60 | |||
| 8899017a37 | |||
| c8b983fd76 | |||
| 24ad6b6a1d | |||
| ee9e23ef2c | |||
| 3125009e80 | |||
| b23e211e11 | |||
| 2cc9b342b7 | |||
| 085a74a6b3 | |||
| f6159a863c | |||
| 4430138e77 | |||
| 04eca2cbc0 | |||
| c8d47f8e67 | |||
| f7da6fdb63 | |||
| 2466bb6d4c | |||
| 5f293e38b6 | |||
| 08ce0b1da3 | |||
| e12aa7e695 | |||
| c92a32a503 | |||
| 56fdfdca49 | |||
| b2510645e5 | |||
| b5a08584a0 | |||
| 78568cd235 | |||
| 8e86d4a7c5 | |||
| 6322ff2dd5 | |||
| 89c3b93ba6 | |||
| 580dea6b44 | |||
| b203adc66f | |||
| 7a2639f772 | |||
| 535430d0c5 | |||
| a0c9c88e0e | |||
| 4b7610f39f | |||
| a06e903c5c | |||
| f79f46269f | |||
| b82ddc1c59 | |||
| f4dc6ab838 | |||
| 62dde63206 | |||
| 5c034013fe | |||
| 23717c15d1 | |||
| 09acc3ccaa | |||
| 7c3f4f6a64 | |||
| 8dc48b20cb | |||
| 165b17e034 | |||
| 4255e2ad85 | |||
| 16b7c1ed76 | |||
| d179bde348 | |||
| e2234b19ea | |||
| 45259f304b | |||
| 220c7940a8 | |||
| 9665dbcb4e | |||
| 423a17070c | |||
| 0422e7fee8 | |||
| 7fdf165d57 | |||
| 31d1b8536d | |||
| 20ac0ef7da | |||
| a72c849693 | |||
| 48f3bf53ca | |||
| e78a1189b0 | |||
| 1209d8e32a | |||
| b89da81989 | |||
| b0a5cbc74e | |||
| a6d8bedde2 | |||
| e9824e86f4 | |||
| 220837d2fb | |||
| 1bfdcb732a | |||
| 9865f91b6d | |||
| 838b1b3b93 | |||
| 0f7a30992d | |||
| 1c2c997b6b | |||
| bbbaeb86d0 | |||
| 34921b166e | |||
| f9b10a1174 | |||
| 6b20fa8f7b | |||
| bc5fb017cf | |||
| ce82f53568 | |||
| 4f5ef38d1b | |||
| 26e2ed5bbf | |||
| a5d42f3687 | |||
| 811e49327a | |||
| a8bb34c2d8 | |||
| 13b426eca6 | |||
| dceb09ec36 | |||
| e89199fec3 | |||
| 1b2136ffb9 | |||
| 416cf41841 | |||
| 3940a0a06c | |||
| 6a987126c8 | |||
| a4f68ebc8b | |||
| b7adfacfd1 | |||
| ee03a90f02 | |||
| 383d917e25 | |||
| e15d4c45d4 | |||
| 6077e51203 | |||
| 079dc32429 | |||
| 01ef77832d | |||
| d29e6d39f5 |
@@ -1,76 +1,243 @@
|
||||
<p align="center">
|
||||
<a href="https://github.com/Zagrios/bs-manager">
|
||||
<img src="https://raw.githubusercontent.com/Zagrios/bs-manager/master/resources/readme/icon.svg" alt="logo" width="250px"/>
|
||||
</a>
|
||||
<h3 align="center">BSManager</h3>
|
||||
<p align="center">Simple and fancy tools that alow you to download, launch and manage BeatSaber versions, Maps and Mods easily and quickly.</p>
|
||||
</p>
|
||||
<a name="readme-top"></a>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://github.com/Zagrios/bs-manager/blob/master/resources/readme/beat-running.png?raw=true" width="130"/>
|
||||
</p>
|
||||
<!-- PROJECT SHIELDS -->
|
||||
<!--
|
||||
*** I'm using markdown "reference style" links for readability.
|
||||
*** Reference links are enclosed in brackets [ ] instead of parentheses ( ).
|
||||
*** See the bottom of this document for the declaration of the reference variables
|
||||
*** for contributors-url, forks-url, etc. This is an optional, concise syntax you may use.
|
||||
*** https://www.markdownguide.org/basic-syntax/#reference-style-links
|
||||
-->
|
||||
|
||||
<details open="open">
|
||||
<summary>
|
||||
<h2>Table of Contents</h2>
|
||||
</summary>
|
||||
<ol>
|
||||
<li>
|
||||
<a href="#about-the-project">About The Project</a>
|
||||
<ul><li><a href="#built-with">Built With</a></li></ul>
|
||||
</li>
|
||||
<li><a href="#how-to-use">How To Use</a></li>
|
||||
<li><a href="#having-problems">Having problems?</a></li>
|
||||
<li><a href="#acknowledgements">Acknowledgements</a></li>
|
||||
</ol>
|
||||
</details>
|
||||
|
||||
<h2 id="about-the-project">About The Project</h2>
|
||||
<p>
|
||||
Have you ever wanted to switch between multiple BeatSaber version? To have a specific version for you and your friends, or for personal use and Streaming use? Have you ever wanted to manage easily <b>Maps</b> and <b>Mods</b> between versions? All of that in one place? <b>Then this program is made for you!</b>
|
||||
<br/><br/>
|
||||
BSManager is designed to download any BeatSaber version from Steam's depots, and launch it within the same program!<br>
|
||||
- You can install as many versions as you want, customize them to quickly differentiate them, clone them to separate the additional contents of the same version!<br>
|
||||
- You can clone and launch your Steam version from the program!<br/><br/>
|
||||
<b>In the near future</b><br/>
|
||||
- You can Create a master Maps folder and link it across all versions!<br/>
|
||||
- You can download Maps with "OneClick"!<br/>
|
||||
- You can download Maps from BSManager!<br/>
|
||||
- You can download and manage Mods for each versions!<br/>
|
||||
- And even more!
|
||||
</p>
|
||||
<h3 id="built-with">Built With</h3>
|
||||
<!-- TITLE -->
|
||||
<div align="center">
|
||||
<a href="https://github.com/Zagrios/bs-manager">
|
||||
<img src="resources/readme/icon.svg" alt="Logo" width="250" height="250">
|
||||
</a>
|
||||
<br><br>
|
||||
<h2><b>BSManager</b></h2>
|
||||
<i>(Beat Saber Manager)</i>
|
||||
<br><br>
|
||||
<span>
|
||||
Download, manage and customize your versions of Beat Saber with a simple click!
|
||||
</span>
|
||||
<br>
|
||||
<span>
|
||||
<a href="https://github.com/Zagrios/bs-manager/issues/new?assignees=Zagrios&labels=bug&template=-bug--bug-report.md&title=%5BBUG%5D+%3A+">Report Bug</a>
|
||||
·
|
||||
<a href="https://github.com/Zagrios/bs-manager/issues/new?assignees=Zagrios&labels=enhancement&template=-feat---feature-request.md&title=%5BFEAT.%5D+%3A+">Request Feature</a>
|
||||
·
|
||||
<a href="https://github.com/Zagrios/bs-manager/security/policy">Report a security vulnerability</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- BADGES -->
|
||||
---
|
||||
<br>
|
||||
<div align="center" >
|
||||
<a href="https://github.com/Zagrios/bs-manager/stargazers"><img src ="https://img.shields.io/github/stars/Zagrios/bs-manager?style=for-the-badge" alt="Stargazers"/></a>
|
||||
<a href="https://github.com/Zagrios/bs-manager/network/members"><img src ="https://img.shields.io/github/forks/Zagrios/bs-manager?style=for-the-badge" alt="Forks"/></a>
|
||||
<a href="https://github.com/Zagrios/bs-manager/blob/master/LICENSE"><img src ="https://img.shields.io/github/license/Zagrios/bs-manager?style=for-the-badge" alt="License"/></a>
|
||||
<a href="https://github.com/Zagrios/bs-manager/graphs/contributors"><img src ="https://img.shields.io/github/contributors/Zagrios/bs-manager?style=for-the-badge" alt="Contributors"/></a>
|
||||
<a href="https://github.com/Zagrios/bs-manager/issues"><img alt="GitHub issues" src="https://img.shields.io/github/issues/Zagrios/bs-manager?style=for-the-badge"></a>
|
||||
<br>
|
||||
<a href="https://discord.gg/uSqbHVpKdV"><img src ="https://img.shields.io/badge/-DISCORD-5865f2?style=for-the-badge&logo=discord&logoColor=ffffff" alt="discord"/></a>
|
||||
<a href="https://mee6.xyz/fr/m/bsmanager"><img src ="https://img.shields.io/badge/-🥰%20Support%20BSM-EC4546?style=for-the-badge" alt="Donation"/></a>
|
||||
<a href="https://twitter.com/BSManager_"><img src ="https://img.shields.io/badge/-Twitter-F5F8FA?style=for-the-badge&logo=Twitter" alt="Twitter"/></a>
|
||||
<br>
|
||||
[<a href="#readme-bot">Go to bottom</a>]
|
||||
</div>
|
||||
|
||||
---
|
||||
<!-- TABLE OF CONTENTS -->
|
||||
<b>Table of Contents</b>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://github.com/electron-react-boilerplate/electron-react-boilerplate">Electron React Boilerplate</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/SteamRE/DepotDownloader">DepotDownloader</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#about-the-project">About The Project</a>
|
||||
<ul>
|
||||
<li><a href="#built-with">Built With</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#usage">Usage</a></li>
|
||||
<ul>
|
||||
<li><a href="#how-to-install">How to install?</a></li>
|
||||
<li><a href="#manage-versions">Manage Versions</a></li>
|
||||
<li><a href="#customize-your-versions">Customize your versions</a></li>
|
||||
<li><a href="#install-mods">Install Mods</a></li>
|
||||
<li><a href="#install-maps">Install Maps</a></li>
|
||||
<li><a href="#one-click">One Click</a></li>
|
||||
<li><a href="#having-problems">Having problems?</a></li>
|
||||
</ul>
|
||||
<li><a href="#credits">Credits</a></li>
|
||||
<li><a href="#contributing">Contributing</a></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="how-to-use">How To Use</h2>
|
||||
<ul>
|
||||
<li>
|
||||
Download the latest release from <a href="https://github.com/Zagrios/bs-manager/releases">Releases</a>.
|
||||
</li>
|
||||
<li>Execute the installer and BSManager will start itself.</li>
|
||||
<li>Once its done if you want to download a version, select a version and download it!</li>
|
||||
<li>From the version page you can choose launch options, launch the version, and more from the dropdown menu!</li>
|
||||
</ul>
|
||||
<!-- ABOUT THE PROJECT -->
|
||||
<div align="center">
|
||||
<h1> </h1>
|
||||
<h1><b>About The Project</b></h1>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://github.com/Zagrios/bs-manager">BSManager</a> was created to unify and improve the two most used Apps in the community (<a href="https://github.com/RiskiVR/BSLegacyLauncher">BS legacy launcher</a> and <a href="https://github.com/Assistant/ModAssistant">Mod Assistant</a>).
|
||||
<br><br>
|
||||
<h4><a href="https://github.com/Zagrios/bs-manager">BSManager</a> will allow you to:</h4>
|
||||
<ul>
|
||||
<li><b>Manage multiple BeatSaber versions</b>, designed for people who need to play on different versions or have multiple profiles for the same version.</li>
|
||||
<li><b>Download your maps in the same way</b>, in addition to that, depending on your choice, maps are not version dependent. so you will be able to find your maps whatever version you are using without having to transfer them.</li>
|
||||
<li><b>Install your mods in one click</b>, just like <a href="https://github.com/Assistant/ModAssistant">Mod Assistant</a>, <a href="https://github.com/Zagrios/bs-manager">BSManager</a> will allow you to install your mods quickly and securely depending on the version you choose.</li>
|
||||
<li><b>Customize your interface</b>, we know that some of you like to customize your interfaces to the maximum. That's why <a href="https://github.com/Zagrios/bs-manager">BSManager</a> brings you peace of mind on this point, you can change the name of the versions, their color, and you can even change the color of the logo! Of course, a dark theme has been added to protect your eyes at night.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<br>
|
||||
<div align="center">
|
||||
<img src="resources/readme/preview.png" alt="preview">
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<h2 id="having-problems">Having problems?</h2>
|
||||
<p>
|
||||
See the <a href="https://github.com/Zagrios/bs-manager/issues">open issues</a> for a list of known issues, from there you can create a new issue for the problem you have!
|
||||
Otherwise you can add on to existing issues if one matches what you're encountering.
|
||||
</p>
|
||||
<h2 id="acknowledgements">Acknowledgement</h2>
|
||||
<ul>
|
||||
<li><a href="https://github.com/Zagrios">Zagrios</a> - Founder, Lead Developer & Project Lead</li>
|
||||
<li><a href="https://github.com/Iluhadesu">Iluhadesu</a> - Custom DepotDownloader Developer</li>
|
||||
<li><a href="https://github.com/GaetanGrd">Gaëtan Gerard</a> - Main Alpha Tester</li>
|
||||
</ul>
|
||||
<p align="center">
|
||||
<img src="https://github.com/Zagrios/bs-manager/blob/master/resources/readme/beat-conflict.png?raw=true" width="130"/>
|
||||
</p>
|
||||
<!--BUILT WITH-->
|
||||
<div>
|
||||
<h2><b>Built With</b></h2>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://electron-react-boilerplate.js.org/"><img src="https://img.shields.io/badge/-Electron%20React%20Boilerplate-black?style=for-the-badge&logo=Electron" alt="Electron React Boilerplate"></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/SteamRE/DepotDownloader"><img src="https://img.shields.io/badge/-Depot%20Downloader-2a475e?style=for-the-badge&logo=steam" alt="Depot Downloader"></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://rxjs.dev/"><img src="https://img.shields.io/badge/-RxJs-purple?style=for-the-badge&logo=ReactiveX" alt="RxJs"></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://tailwindcss.com/"><img src="https://img.shields.io/badge/-Tailwind%20CSS-white?style=for-the-badge&logo=Tailwind%20CSS" alt="TailWind CSS"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<!--USAGE-->
|
||||
<div align="center">
|
||||
<h1> </h1>
|
||||
<h1><b>Usage</b></h1>
|
||||
</div>
|
||||
|
||||
<!--HOW TO INSTALL?-->
|
||||
<div>
|
||||
<h2><b>How to install?</b></h2>
|
||||
<ul>
|
||||
<li>Download the <a href="https://github.com/Zagrios/bs-manager/releases/latest">latest release</a> from <a href="https://github.com/Zagrios/bs-manager/releases">Releases</a>.</li>
|
||||
<li>Execute the installer and <a href="../..">BSManager</a> will start itself.</li>
|
||||
<li>Once its done if you want to download a version, select a version and download it! </li>
|
||||
<li>From the version page you can choose options, launch the version, and more!</li>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<!--MANAGE VERSIONS-->
|
||||
<div>
|
||||
<h2><b>Manage Versions</b></h2>
|
||||
<span>
|
||||
<a href="https://github.com/Zagrios/bs-manager">BSManager</a> allows you to manage your versions of Beat Saber. It gives you the ability to download any version of Beat Saber as long as you have a Steam account that already owns the game. Using <a href="https://github.com/Zagrios/bs-manager">BSManager</a>, you can easily manage and update your collection of Beat Saber versions.
|
||||
</span>
|
||||
</div>
|
||||
<br>
|
||||
<div align="center">
|
||||
|
||||
https://user-images.githubusercontent.com/40648115/213008824-25cfdda3-6350-42dc-aa53-2132b4b8e838.mov
|
||||
</div>
|
||||
|
||||
<!--CUSTOM UR VERSIONS-->
|
||||
<div>
|
||||
<h2><b>Customize your versions</b></h2>
|
||||
<span>
|
||||
<a href="https://github.com/Zagrios/bs-manager">BSManager</a> will allow you to completely customize each of your downloaded versions, you can change the color of the icon, choose a name for the version and finally install your mods,maps and other contents according to the Beat Saber version!
|
||||
</span>
|
||||
</div>
|
||||
<br>
|
||||
<div align="center">
|
||||
|
||||
https://user-images.githubusercontent.com/40648115/213296794-74a1e20b-12dc-44ec-a67f-e25d7010bd3d.mov
|
||||
</div>
|
||||
|
||||
<!--INSTALL MODS-->
|
||||
<div>
|
||||
<h2><b>Install Mods</b></h2>
|
||||
<span>
|
||||
<a href="https://github.com/Zagrios/bs-manager">BSManager</a> allows you to manage Beat Saber mods in a quick and easy way. It uses the <a href="https://beatmods.com/#/">BeatMod API</a> to allow you to install, update or uninstall mods for each version of your collection. So the app can help you manage them efficiently.
|
||||
</span>
|
||||
</div>
|
||||
<br>
|
||||
<div align="center">
|
||||
|
||||
https://user-images.githubusercontent.com/40648115/213296816-bdaafc90-da76-4835-8d83-9ac6a4e1c549.mov
|
||||
</div>
|
||||
|
||||
<!--INSTALL MAPS-->
|
||||
<div>
|
||||
<h2><b>Install Maps</b></h2>
|
||||
<span>
|
||||
In addition to managing mods, <a href="https://github.com/Zagrios/bs-manager">BSManager</a> allows you to manage and organize custom beatmaps existing on <a href="https://beatsaver.com">Beat Saver</a>, a popular site for sharing custom beatmaps for beat Saber. Moreover the app allow you to share your maps across all your versions.
|
||||
</span>
|
||||
</div>
|
||||
<br>
|
||||
<div align="center">
|
||||
|
||||
https://user-images.githubusercontent.com/40648115/213296838-f775fe83-866d-4764-a7d0-648dbb8ec983.mov
|
||||
</div>
|
||||
|
||||
<!--ONE CLICK-->
|
||||
<div>
|
||||
<h2><b>One Click</b></h2>
|
||||
<span>
|
||||
The "One-Click" feature of <a href="https://github.com/Zagrios/bs-manager">BSManager</a> is an incredibly convenient feature for Beat Saber users. It allows easy and quick installation of models, maps and playlists for all versions of Beat Saber you own.
|
||||
</span>
|
||||
</div>
|
||||
<br>
|
||||
<div align="center">
|
||||
|
||||
https://user-images.githubusercontent.com/40648115/213435051-7feb815d-f32b-4135-b095-ef49726a836c.mov
|
||||
</div>
|
||||
|
||||
<!--HAVING PROBLEMS?-->
|
||||
<div>
|
||||
<h2><b>Having problems?</b></h2>
|
||||
<span>
|
||||
See the <a href="https://github.com/Zagrios/bs-manager/issues">open issues</a> to show the list of known issues, from there you can create a new issue for your problem!
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<br><br><br>
|
||||
|
||||
<!-- Credits -->
|
||||
<div>
|
||||
<h1><b>Credits</b></h1>
|
||||
<ul>
|
||||
<li><a href="https://github.com/Zagrios">Zagrios</a> - Founder & Lead Developer</li>
|
||||
<li><a href="https://github.com/Iluhadesu">Iluhadesu</a> - Project Team & Developpement help</li>
|
||||
<li><a href="https://github.com/GaetanGrd">GaetanGrd</a> - Project Team & Developpement help</li>
|
||||
<li><a href="https://github.com/cheddZy">cheddZy</a> - Icon creator</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Contributing -->
|
||||
<br>
|
||||
<!--CONTRIBUTING-->
|
||||
<div>
|
||||
<h1><b>Contributing</b></h1>
|
||||
<p>
|
||||
If you'd like to contribute, check out the <a href="https://github.com/Zagrios/bs-manager/blob/master/CONTRIBUTING.md">contributing guide</a>
|
||||
</p>
|
||||
<p>Thank you to all the people who already contributed to BSManager!</p>
|
||||
<div>
|
||||
<a href="https://github.com/Zagrios/bs-manager/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=Zagrios/bs-manager" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<div align="right">
|
||||
[<a href="#readme-top">Return to top</a>]
|
||||
</div>
|
||||
|
||||
<h1> </h1>
|
||||
<a name="readme-bot"></a>
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# Reporting Security Issues
|
||||
|
||||
If you discover a security issue in BSManager, please report it by sending an
|
||||
email to [contact@bsmanager.io](mailto:contact@bsmanager.io).
|
||||
|
||||
This will allow us to assess the risk, and make a fix available before we add a
|
||||
bug report to the GitHub repository.
|
||||
|
||||
Thanks for helping make BSManager safe for everyone.
|
||||
@@ -13,12 +13,19 @@
|
||||
"refuse": "Refuse",
|
||||
"apply": "Apply"
|
||||
},
|
||||
"nav-bar":{
|
||||
"add-version": "Add a version",
|
||||
"settings": "Settings"
|
||||
},
|
||||
"pages": {
|
||||
"version-viewer": {
|
||||
"launch-mods": {
|
||||
"oculus": "Oculus Mod",
|
||||
"oculus-description": "If you are running Beat Saber through Steam, this allows you to play the game on an Oculus headset.",
|
||||
"desktop": "Desktop Mod",
|
||||
"debug": "Debug Mod"
|
||||
"desktop-description": "This allows you to use WASD and the mouse to navigate around the menu in game. This makes testing much easier, because you don't have to put on your headset!",
|
||||
"debug": "Debug Mod",
|
||||
"debug-description": "Enables the output log window for IPA. This will show the debug console that mods use."
|
||||
},
|
||||
"maps": {
|
||||
"search-bar": {
|
||||
@@ -78,7 +85,11 @@
|
||||
},
|
||||
"available-versions": {
|
||||
"title": "Select a version",
|
||||
"steam-release": "Release page"
|
||||
"steam-release": "Release page",
|
||||
"dropdown": {
|
||||
"refresh": "Refresh list",
|
||||
"import-version" :"Import a version"
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"steam": {
|
||||
@@ -226,6 +237,22 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"bs-import-version":{
|
||||
"success":{
|
||||
"start-import": {
|
||||
"title": "Import in progress 👌",
|
||||
"desc": "The import may take several minutes depending on your configuration."
|
||||
},
|
||||
"imported": {
|
||||
"title": "Imported version 🎉"
|
||||
}
|
||||
},
|
||||
"errors":{
|
||||
"import-error":{
|
||||
"desc": "Check that the selected folder is a BeatSaber installation."
|
||||
}
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"move-folder": {
|
||||
"success": {
|
||||
@@ -420,6 +447,13 @@
|
||||
"submit": "Login"
|
||||
}
|
||||
},
|
||||
"bs-import-version":{
|
||||
"title": "Import a version",
|
||||
"description": "Imports a version of BeatSaber to use with BSManager. The import will copy the selected BeatSaber installation folder to the BSManager versions folder.",
|
||||
"buttons":{
|
||||
"submit": "Import a version"
|
||||
}
|
||||
},
|
||||
"bs-uninstall": {
|
||||
"title": "Uninstall",
|
||||
"description": "Are you sure you want to uninstall BeatSaber {version} ? You will have to download it again to play it.",
|
||||
|
||||
@@ -13,12 +13,19 @@
|
||||
"refuse": "Rechazar",
|
||||
"apply": "Aplicar"
|
||||
},
|
||||
"nav-bar":{
|
||||
"add-version": "Agregar una versión",
|
||||
"settings": "Ajustes"
|
||||
},
|
||||
"pages": {
|
||||
"version-viewer": {
|
||||
"launch-mods": {
|
||||
"oculus": "Oculus Mod",
|
||||
"oculus-description": "Si estás ejecutando Beat Saber a través de Steam, esto te permite jugar en un auricular Oculus.",
|
||||
"desktop": "Desktop Mod",
|
||||
"debug": "Debug Mod"
|
||||
"desktop-description": "Esto te permite usar WASD y el mouse para navegar por el menú en juego. Esto hace que las pruebas sean mucho más fáciles, ¡porque no tienes que ponerte el auricular!",
|
||||
"debug": "Debug Mod",
|
||||
"debug-description": "Habilita la ventana de registro de salida para IPA. Esto mostrará la consola de depuración que usan los mods."
|
||||
},
|
||||
"maps": {
|
||||
"search-bar": {
|
||||
@@ -78,7 +85,11 @@
|
||||
},
|
||||
"available-versions": {
|
||||
"title": "Seleccione una versión",
|
||||
"steam-release": "Publicación Página"
|
||||
"steam-release": "Publicación Página",
|
||||
"dropdown": {
|
||||
"refresh": "Actualizar la lista",
|
||||
"import-version" :"Importar una versión"
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"steam": {
|
||||
@@ -226,6 +237,22 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"bs-import-version":{
|
||||
"success":{
|
||||
"start-import": {
|
||||
"title": "Importación en curso 👌",
|
||||
"desc": "La importación puede tardar varios minutos dependiendo de su configuración."
|
||||
},
|
||||
"imported": {
|
||||
"title": "Versión importada 🎉"
|
||||
}
|
||||
},
|
||||
"errors":{
|
||||
"import-error":{
|
||||
"desc": "Compruebe que la carpeta seleccionada es una instalación de BeatSaber."
|
||||
}
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"move-folder": {
|
||||
"success": {
|
||||
@@ -420,6 +447,13 @@
|
||||
"submit": "Inicio de sesión"
|
||||
}
|
||||
},
|
||||
"bs-import-version":{
|
||||
"title": "Importar una versión",
|
||||
"description": "Importa una versión de BeatSaber para usar con BSManager. La importación copiará la carpeta de instalación seleccionada de BeatSaber en la carpeta de versiones de BSManager.",
|
||||
"buttons":{
|
||||
"submit": "Importar una versión"
|
||||
}
|
||||
},
|
||||
"bs-uninstall": {
|
||||
"title": "Desinstalar",
|
||||
"description": "¿Estás seguro de que quieres desinstalar BeatSaber {version}? Tendrás que descargarlo de nuevo para poder jugar.",
|
||||
|
||||
@@ -13,12 +13,19 @@
|
||||
"refuse": "Refuser",
|
||||
"apply": "Appliquer"
|
||||
},
|
||||
"nav-bar":{
|
||||
"add-version": "Ajouter une version",
|
||||
"settings": "Paramètres"
|
||||
},
|
||||
"pages": {
|
||||
"version-viewer": {
|
||||
"launch-mods": {
|
||||
"oculus": "Mode Oculus",
|
||||
"oculus-description": "Si vous exécutez Beat Saber via Steam, cela vous permet de jouer au jeu sur un casque Oculus.",
|
||||
"desktop": "Mode Bureau",
|
||||
"debug": "Mode Debug"
|
||||
"desktop-description": "Cela vous permet d'utiliser WASD et la souris pour naviguer dans le menu en jeu. Cela rend les tests beaucoup plus faciles, car vous n'avez pas à mettre votre casque!",
|
||||
"debug": "Mode Debug",
|
||||
"debug-description": "Active la fenêtre de log pour IPA. Cela affichera la console de débogage utilisée par les mods."
|
||||
},
|
||||
"maps": {
|
||||
"search-bar": {
|
||||
@@ -78,7 +85,11 @@
|
||||
},
|
||||
"available-versions": {
|
||||
"title": "Choisi une version",
|
||||
"steam-release": "Notes de version"
|
||||
"steam-release": "Notes de version",
|
||||
"dropdown": {
|
||||
"refresh": "Rafraichir la liste",
|
||||
"import-version" :"Importer une version"
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"steam": {
|
||||
@@ -226,6 +237,22 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"bs-import-version":{
|
||||
"success":{
|
||||
"start-import": {
|
||||
"title": "Importation en cours 👌",
|
||||
"desc": "L'importation peut durer plusieurs minutes selon votre configuration."
|
||||
},
|
||||
"imported": {
|
||||
"title": "Version importée 🎉"
|
||||
}
|
||||
},
|
||||
"errors":{
|
||||
"import-error":{
|
||||
"desc": "Vérifier que le dossier sélectionné est une installation de BeatSaber."
|
||||
}
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"move-folder": {
|
||||
"success": {
|
||||
@@ -420,6 +447,13 @@
|
||||
"submit": "Se connecter"
|
||||
}
|
||||
},
|
||||
"bs-import-version":{
|
||||
"title": "Importer une version",
|
||||
"description": "Importe une version de BeatSaber pour profiter des fonctionnalités de BSManager. L'importation copiera le dossier d'installation de BeatSaber sélectionné dans le dossier de versions de BSManager.",
|
||||
"buttons":{
|
||||
"submit": "Importer une version"
|
||||
}
|
||||
},
|
||||
"bs-uninstall": {
|
||||
"title": "Désinstaller",
|
||||
"description": "Est-tu sûr de vouloir désinstaller BeatSaber {version} ? Tu vas devoir la retélécharger pour y jouer.",
|
||||
|
||||
+2
-1
@@ -74,7 +74,8 @@
|
||||
},
|
||||
"win": {
|
||||
"target": [
|
||||
"nsis"
|
||||
"nsis",
|
||||
"nsis-web"
|
||||
],
|
||||
"icon": "assets/favicon.ico",
|
||||
"certificateSha1": "f3363a89b084f8f6b616babea3c91f9d79e15000"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "bs-manager",
|
||||
"version": "1.0.0-alpha.4",
|
||||
"version": "1.0.0",
|
||||
"description": "A foundation for scalable desktop apps",
|
||||
"main": "./dist/main/main.js",
|
||||
"author": {
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 965 KiB |
@@ -1,6 +1,5 @@
|
||||
import { ipcMain } from 'electron';
|
||||
import { BSVersion } from 'shared/bs-version.interface';
|
||||
import { BSInstallerService, DownloadEventType } from '../services/bs-installer.service';
|
||||
import { BSInstallerService, DownloadEventType, DownloadInfo } from '../services/bs-installer.service';
|
||||
import { IpcRequest } from 'shared/models/ipc';
|
||||
import { InstallationLocationService } from '../services/installation-location.service';
|
||||
import { UtilsService } from '../services/utils.service';
|
||||
@@ -18,13 +17,6 @@ export interface InitDownloadInfoInterface {
|
||||
stay: boolean
|
||||
}
|
||||
|
||||
export interface DownloadInfo {
|
||||
bsVersion: BSVersion,
|
||||
username: string,
|
||||
password?: string,
|
||||
stay?: boolean
|
||||
}
|
||||
|
||||
ipcMain.on('is-dotnet-6-installed', async (event, request: IpcRequest<void>) => {
|
||||
const installer = BSInstallerService.getInstance();
|
||||
const utils = UtilsService.getInstance();
|
||||
@@ -65,4 +57,14 @@ ipcMain.on('bs-download.set-installation-folder', (event, request: IpcRequest<st
|
||||
});
|
||||
})
|
||||
|
||||
ipcMain.on('bs-download.import-version', (event, request: IpcRequest<string>) => {
|
||||
const utils = UtilsService.getInstance();
|
||||
const installer = BSInstallerService.getInstance();
|
||||
installer.importVersion(request.args).catch(e => {
|
||||
utils.ipcSend(request.responceChannel, {success: false, error: e});
|
||||
}).then(() => {
|
||||
utils.ipcSend(request.responceChannel, {success: true});
|
||||
});
|
||||
})
|
||||
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ ipcMain.on('new-window', async (event, request: IpcRequest<string>) => {
|
||||
});
|
||||
|
||||
ipcMain.on('choose-folder', async (event, request: IpcRequest<void>) => {
|
||||
dialog.showOpenDialog({properties: ['openDirectory']}).then(res => {
|
||||
dialog.showOpenDialog({properties: ['openDirectory'],}).then(res => {
|
||||
UtilsService.getInstance().ipcSend(request.responceChannel, {success: true, data: res});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { BS_APP_ID, BS_DEPOT } from "../constants";
|
||||
import path from "path";
|
||||
import { BSVersion } from 'shared/bs-version.interface';
|
||||
import { BSVersion, PartialBSVersion } from 'shared/bs-version.interface';
|
||||
import { UtilsService } from "./utils.service";
|
||||
import { ChildProcessWithoutNullStreams, spawn, spawnSync } from "child_process";
|
||||
import log from "electron-log";
|
||||
@@ -9,6 +9,7 @@ import { ctrlc } from "ctrlc-windows";
|
||||
import { BSLocalVersionService } from "./bs-local-version.service";
|
||||
import isOnline from 'is-online';
|
||||
import { WindowManagerService } from "./window-manager.service";
|
||||
import { copy, copySync } from "fs-extra";
|
||||
|
||||
export class BSInstallerService{
|
||||
|
||||
@@ -85,6 +86,13 @@ export class BSInstallerService{
|
||||
if(!(await isOnline({timeout: 1500}))){ throw "no-internet"; }
|
||||
|
||||
this.utils.createFolderIfNotExist(this.installLocationService.versionsDirectory);
|
||||
|
||||
const versionPath = await this.localVersionService.getVersionPath(bsVersion)
|
||||
|
||||
const dest = !downloadInfos.isVerification ? this.getPathNotAleardyExist(versionPath) : versionPath;
|
||||
|
||||
const downloadVersion: BSVersion = {...downloadInfos.bsVersion, ...(path.basename(dest) !== downloadInfos.bsVersion.BSVersion && {name: path.basename(dest)})}
|
||||
|
||||
this.downloadProcess = spawn(
|
||||
this.getDepotDownloaderExePath(),
|
||||
[
|
||||
@@ -92,12 +100,14 @@ export class BSInstallerService{
|
||||
`-depot ${BS_DEPOT}`,
|
||||
`-manifest ${bsVersion.BSManifest}`,
|
||||
`-username ${downloadInfos.username}`,
|
||||
`-dir \"${this.localVersionService.getVersionFolder(bsVersion)}\"`,
|
||||
`-dir \"${this.localVersionService.getVersionFolder(downloadVersion)}\"`,
|
||||
(downloadInfos.stay || !downloadInfos.password) && "-remember-password"
|
||||
],
|
||||
{shell: true, cwd: this.installLocationService.versionsDirectory}
|
||||
);
|
||||
|
||||
this.utils.ipcSend("start-download-version", {success: true, data: downloadVersion});
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
this.downloadProcess.stdout.on('data', data => {
|
||||
@@ -159,6 +169,34 @@ export class BSInstallerService{
|
||||
})
|
||||
}
|
||||
|
||||
private getPathNotAleardyExist(path: string): string{
|
||||
let destPath = path;
|
||||
let folderExist = this.utils.pathExist(destPath);
|
||||
let i = 0;
|
||||
|
||||
while(folderExist){
|
||||
i++;
|
||||
destPath = `${path} (${i})`;
|
||||
folderExist = this.utils.pathExist(destPath);
|
||||
}
|
||||
|
||||
return destPath
|
||||
}
|
||||
|
||||
public async importVersion(path: string): Promise<PartialBSVersion>{
|
||||
|
||||
const rawBsVersion = await this.localVersionService.getVersionOfBSFolder(path);
|
||||
|
||||
if(!rawBsVersion){ throw new Error("NOT_BS_FOLDER"); }
|
||||
|
||||
const destPath = this.getPathNotAleardyExist(await this.localVersionService.getVersionPath(rawBsVersion));
|
||||
|
||||
await copy(path, destPath, {dereference: true});
|
||||
|
||||
return rawBsVersion;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -168,12 +206,13 @@ export interface DownloadInfo {
|
||||
bsVersion: BSVersion,
|
||||
username: string,
|
||||
password?: string,
|
||||
stay?: boolean
|
||||
stay?: boolean,
|
||||
isVerification: boolean
|
||||
}
|
||||
|
||||
export interface DownloadEvent{
|
||||
type: DownloadEventType,
|
||||
data?: unknown,
|
||||
data?: unknown
|
||||
}
|
||||
|
||||
export type DownloadEventType = "[Password]" | "[Guard]" | "[2FA]" | "[Progress]" | "[Validated]" | "[Finished]" | "[AlreadyDownloading]" | "[Error]" | "[Warning]" | "[SteamID]" | "[Exit]" | "[NoInternet]";
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { BSVersionLibService } from "./bs-version-lib.service";
|
||||
import { BSVersion } from 'shared/bs-version.interface';
|
||||
import { BSVersion, PartialBSVersion } from 'shared/bs-version.interface';
|
||||
import { InstallationLocationService } from "./installation-location.service";
|
||||
import { SteamService } from "./steam.service";
|
||||
import { UtilsService } from "./utils.service";
|
||||
import { BS_APP_ID, OCULUS_BS_DIR } from "../constants";
|
||||
import path from "path";
|
||||
import { createInterface } from "readline";
|
||||
import { createReadStream } from "fs";
|
||||
import fs from "fs-extra";
|
||||
import { ConfigurationService } from "./configuration.service";
|
||||
@@ -14,6 +13,7 @@ import { BsmException } from "shared/models/bsm-exception.model";
|
||||
import log from "electron-log";
|
||||
import { OculusService } from "./oculus.service";
|
||||
import { DownloadLinkType } from "shared/models/mods";
|
||||
import sanitize from "sanitize-filename";
|
||||
|
||||
export class BSLocalVersionService{
|
||||
|
||||
@@ -33,35 +33,46 @@ export class BSLocalVersionService{
|
||||
return BSLocalVersionService.instance;
|
||||
}
|
||||
|
||||
private constructor(){
|
||||
this.installLocationService = InstallationLocationService.getInstance();
|
||||
this.utilsService = UtilsService.getInstance();
|
||||
this.steamService = SteamService.getInstance();
|
||||
this.oculusService = OculusService.getInstance();
|
||||
this.remoteVersionService = BSVersionLibService.getInstance();
|
||||
this.configService = ConfigurationService.getInstance();
|
||||
}
|
||||
private constructor(){
|
||||
this.installLocationService = InstallationLocationService.getInstance();
|
||||
this.utilsService = UtilsService.getInstance();
|
||||
this.steamService = SteamService.getInstance();
|
||||
this.oculusService = OculusService.getInstance();
|
||||
this.remoteVersionService = BSVersionLibService.getInstance();
|
||||
this.configService = ConfigurationService.getInstance();
|
||||
}
|
||||
|
||||
private async getVersionOfBSFolder(bsPath: string): Promise<string>{
|
||||
|
||||
|
||||
public async getVersionOfBSFolder(bsPath: string): Promise<PartialBSVersion>{
|
||||
const versionFilePath = path.join(bsPath, 'Beat Saber_Data', 'globalgamemanagers');
|
||||
if(!this.utilsService.pathExist(versionFilePath)){ return null; }
|
||||
const versionsAvailable = await this.remoteVersionService.getAvailableVersions();
|
||||
return new Promise<string>(resolve => {
|
||||
const readLine = createInterface({ input: createReadStream(versionFilePath) });
|
||||
let findVersion: string = null;
|
||||
readLine.on('line', (line) => {
|
||||
return new Promise<PartialBSVersion>(resolve => {
|
||||
const stream = createReadStream(versionFilePath);
|
||||
let findVersion: PartialBSVersion = null;
|
||||
stream.on('data', (line) => {
|
||||
line = line.toString();
|
||||
for(const version of versionsAvailable){
|
||||
if(line.includes(version.BSVersion)){
|
||||
findVersion = version.BSVersion;
|
||||
readLine.close();
|
||||
findVersion = {BSVersion: version.BSVersion};
|
||||
if(findVersion.BSVersion !== path.basename(bsPath)){
|
||||
findVersion.name = path.basename(bsPath);
|
||||
}
|
||||
stream.close();
|
||||
stream.destroy();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
readLine.on('close', () => {
|
||||
stream.on('end', () => {
|
||||
if(findVersion){ resolve(findVersion) }
|
||||
resolve(findVersion);
|
||||
});
|
||||
stream.on('close', () => {
|
||||
if(findVersion){ resolve(findVersion) }
|
||||
resolve(findVersion);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -92,11 +103,11 @@ export class BSLocalVersionService{
|
||||
}
|
||||
|
||||
private removeSpecialChar(seq: string): string{
|
||||
return seq.replace( /[<>:"\/\\|?*]+/g, '' );
|
||||
return sanitize(seq);
|
||||
}
|
||||
|
||||
public getVersionFolder(version: BSVersion){
|
||||
return version.name ? `${version.BSVersion}-${version.name}` : version.BSVersion;
|
||||
public getVersionFolder(version: BSVersion): string{
|
||||
return version.name ?? version.BSVersion;
|
||||
}
|
||||
|
||||
public getVersionType(version: BSVersion): DownloadLinkType{
|
||||
@@ -107,10 +118,12 @@ export class BSLocalVersionService{
|
||||
|
||||
private async getSteamVersion(): Promise<BSVersion>{
|
||||
const steamBsFolder = await this.steamService.getGameFolder(BS_APP_ID, "Beat Saber");
|
||||
|
||||
if(!steamBsFolder || !this.utilsService.pathExist(steamBsFolder)){ return null; }
|
||||
const steamBsVersion = await this.getVersionOfBSFolder(steamBsFolder);
|
||||
|
||||
if(!steamBsVersion){ return null; }
|
||||
const version = await this.remoteVersionService.getVersionDetails(steamBsVersion);
|
||||
const version = await this.remoteVersionService.getVersionDetails(steamBsVersion.BSVersion);
|
||||
if(!version){ return null; }
|
||||
return {...version, steam: true};
|
||||
}
|
||||
@@ -120,37 +133,44 @@ export class BSLocalVersionService{
|
||||
if(!oculusBsFolder){ return null; }
|
||||
const oculusBsVersion = await this.getVersionOfBSFolder(oculusBsFolder);
|
||||
if(!oculusBsVersion){ return null; }
|
||||
const version = await this.remoteVersionService.getVersionDetails(oculusBsVersion);
|
||||
const version = await this.remoteVersionService.getVersionDetails(oculusBsVersion.BSVersion);
|
||||
if(!version){ return null; }
|
||||
return {...version, oculus: true};
|
||||
}
|
||||
|
||||
public async getInstalledVersions(): Promise<BSVersion[]>{
|
||||
|
||||
const versions: BSVersion[] = [];
|
||||
const steamVersion = await this.getSteamVersion();
|
||||
if(steamVersion){ versions.push(steamVersion); }
|
||||
const oculusVersion = await this.getOculusVersion();
|
||||
if(oculusVersion){ versions.push(oculusVersion); }
|
||||
const versions: BSVersion[] = [];
|
||||
|
||||
if(!this.utilsService.pathExist(this.installLocationService.versionsDirectory)){ return versions }
|
||||
const steamVersion = await this.getSteamVersion();
|
||||
if(steamVersion){ versions.push(steamVersion); }
|
||||
|
||||
const folderInInstallation = this.utilsService.listDirsInDir(this.installLocationService.versionsDirectory);
|
||||
for(const f of folderInInstallation){
|
||||
log.info("try get version from folder", f);
|
||||
let version = await this.remoteVersionService.getVersionDetails(f);
|
||||
if(version){ version = this.getCustomVersions().find(v => v.BSVersion === version.BSVersion && v.name === version.name) ?? version; }
|
||||
else { version = this.getCustomVersions().find(v => {
|
||||
const [version, ...rest] = f.split("-");
|
||||
if(rest.length < 1){ return false; }
|
||||
const name = rest.join("-");
|
||||
return name === v.name && version === v.BSVersion;
|
||||
})
|
||||
}
|
||||
version && versions.push(version);
|
||||
};
|
||||
this.setCustomVersions(versions.filter(v => !!v.name || !!v.color));
|
||||
return versions;
|
||||
const oculusVersion = await this.getOculusVersion();
|
||||
if(oculusVersion){ versions.push(oculusVersion); }
|
||||
|
||||
if(!this.utilsService.pathExist(this.installLocationService.versionsDirectory)){ return versions }
|
||||
|
||||
const folderInInstallation = this.utilsService.listDirsInDir(this.installLocationService.versionsDirectory, true);
|
||||
|
||||
for(const f of folderInInstallation){
|
||||
log.info("try get version from folder", f);
|
||||
const rawVersion = await this.getVersionOfBSFolder(f);
|
||||
if(!rawVersion){ continue; }
|
||||
const bsVersion = {...await this.remoteVersionService.getVersionDetails(rawVersion.BSVersion)};
|
||||
if(!bsVersion){ continue; }
|
||||
bsVersion.name = path.basename(f) !== bsVersion.BSVersion ? path.basename(f) : undefined;
|
||||
|
||||
const customVersion = this.getCustomVersions().find(custom => custom.BSVersion === bsVersion.BSVersion && custom.name === bsVersion.name);
|
||||
|
||||
if(customVersion){
|
||||
bsVersion.color = customVersion.color;
|
||||
}
|
||||
|
||||
versions.push(bsVersion);
|
||||
};
|
||||
this.setCustomVersions(versions.filter(v => !!v.color));
|
||||
|
||||
return versions;
|
||||
}
|
||||
|
||||
public async deleteVersion(version: BSVersion): Promise<boolean>{
|
||||
@@ -177,13 +197,14 @@ export class BSLocalVersionService{
|
||||
return editedVersion;
|
||||
}
|
||||
|
||||
if(this.utilsService.pathExist(newPath)){ throw {title: "VersionAlreadExist"} as BsmException; }
|
||||
if(this.utilsService.pathExist(newPath) && newPath === oldPath){ throw {title: "VersionAlreadExist"} as BsmException; }
|
||||
|
||||
return rename(oldPath, newPath).then(() => {
|
||||
this.deleteCustomVersion(version);
|
||||
this.addCustomVersion(editedVersion);
|
||||
return editedVersion;
|
||||
}).catch((err: Error) => {
|
||||
log.error("edit version error", err, version, name, color);
|
||||
throw {title: "CantRename", error: err} as BsmException;
|
||||
});
|
||||
}
|
||||
@@ -198,7 +219,6 @@ export class BSLocalVersionService{
|
||||
if(originPath === newPath){
|
||||
this.deleteCustomVersion(version);
|
||||
this.addCustomVersion(cloneVersion);
|
||||
return cloneVersion;
|
||||
}
|
||||
|
||||
if(this.utilsService.pathExist(newPath)){ throw {title: "VersionAlreadExist"} as BsmException; }
|
||||
@@ -207,7 +227,7 @@ export class BSLocalVersionService{
|
||||
this.addCustomVersion(cloneVersion);
|
||||
return cloneVersion;
|
||||
}).catch((err: Error) => {
|
||||
log.error("CLONE", err, version);
|
||||
log.error("clone version error", err, version, name, color);
|
||||
throw {title: "CantClone", error: err} as BsmException
|
||||
})
|
||||
}
|
||||
|
||||
@@ -50,6 +50,9 @@ export class OculusService {
|
||||
public async getGameFolder(gameFolder: string): Promise<string>{
|
||||
|
||||
const libsFolders = await this.getOculusLibsPath();
|
||||
|
||||
if(!libsFolders){ return null; }
|
||||
|
||||
const rootLibDir = "Software"
|
||||
|
||||
for(const lib of libsFolders){
|
||||
|
||||
@@ -8,6 +8,8 @@ import { BsmImage } from "../shared/bsm-image.component";
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
import { BsmIcon } from "../svgs/bsm-icon.component";
|
||||
import { LinkOpenerService } from 'renderer/services/link-opener.service';
|
||||
import { motion } from 'framer-motion';
|
||||
import { GlowEffect } from '../shared/glow-effect.component';
|
||||
|
||||
export const AvailableVersionItem = memo(function AvailableVersionItem(props: {version: BSVersion}) {
|
||||
|
||||
@@ -15,6 +17,7 @@ export const AvailableVersionItem = memo(function AvailableVersionItem(props: {v
|
||||
const linkOpener = LinkOpenerService.getInstance();
|
||||
|
||||
const [selected, setSelected] = useState(false);
|
||||
const [hovered, setHovered] = useState(false);
|
||||
const t = useTranslation();
|
||||
|
||||
const formatedDate = (() => { return dateFormat(+props.version.ReleaseDate*1000, "ddd. d mmm yyyy"); })()
|
||||
@@ -38,8 +41,8 @@ export const AvailableVersionItem = memo(function AvailableVersionItem(props: {v
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<li className="group relative w-72 h-60 transition-transform active:scale-[.98]" onClick={toggleSelect}>
|
||||
<span className={`absolute glow-on-hover group-hover:opacity-100 ${selected && "!opacity-100"}`}/>
|
||||
<motion.li className="group relative w-72 h-60 transition-transform active:scale-[.98]" onClick={toggleSelect} onHoverStart={() => setHovered(true)} onHoverEnd={() => setHovered(false)}>
|
||||
<GlowEffect visible={hovered || selected} className="absolute"/>
|
||||
<div className={`relative flex flex-col overflow-hidden rounded-md w-72 h-60 cursor-pointer group-hover:shadow-none duration-300 bg-light-main-color-2 dark:bg-main-color-2 ${!selected && "shadow-lg shadow-gray-900"}`}>
|
||||
<BsmImage image={props.version.ReleaseImg ? props.version.ReleaseImg : defaultImage} errorImage={defaultImage} placeholder={defaultImage} className="absolute top-0 right-0 w-full h-full opacity-40 blur-xl object-cover" loading="lazy"/>
|
||||
<BsmImage image={props.version.ReleaseImg ? props.version.ReleaseImg : defaultImage} errorImage={defaultImage} placeholder={defaultImage} className="bg-black w-full h-3/4 object-cover" loading="lazy"/>
|
||||
@@ -56,6 +59,6 @@ export const AvailableVersionItem = memo(function AvailableVersionItem(props: {v
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</motion.li>
|
||||
)
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { BSVersion } from 'shared/bs-version.interface';
|
||||
import { useEffect, useState } from "react"
|
||||
import { BSVersionManagerService } from "renderer/services/bs-version-manager.service";
|
||||
import { filter, take } from "rxjs/operators";
|
||||
import { filter } from "rxjs/operators";
|
||||
import { AvailableVersionItem } from "./available-version-item.component";
|
||||
|
||||
export function AvailableVersionsSlide(props: {year: string}) {
|
||||
@@ -11,7 +11,7 @@ export function AvailableVersionsSlide(props: {year: string}) {
|
||||
const versionsService = BSVersionManagerService.getInstance();
|
||||
|
||||
useEffect(() => {
|
||||
versionsService.availableVersions$.pipe(filter(versions => !!versions?.length), take(1)).subscribe(() => {
|
||||
versionsService.availableVersions$.pipe(filter(versions => !!versions?.length)).subscribe(() => {
|
||||
setAvailableVersions(versionsService.getAvaibleVersionsOfYear(props.year));
|
||||
});
|
||||
}, [])
|
||||
|
||||
+14
-14
@@ -1,29 +1,29 @@
|
||||
import { useEffect, useState } from "react"
|
||||
import { BSVersionManagerService } from "../../services/bs-version-manager.service";
|
||||
import { AvailableVersionsSlide } from "./available-versions-slide.component";
|
||||
import { filter, take } from "rxjs/operators";
|
||||
import { filter} from "rxjs/operators";
|
||||
import { TabNavBar } from "../shared/tab-nav-bar.component";
|
||||
|
||||
export function AvailableVersionsSlider() {
|
||||
|
||||
const [availableYears, setAvailableYears] = useState([]);
|
||||
const [yearIndex, setYearIndex] = useState(0);
|
||||
const versionManagerService = BSVersionManagerService.getInstance();
|
||||
|
||||
const versionManagerService = BSVersionManagerService.getInstance();
|
||||
const [availableYears, setAvailableYears] = useState([]);
|
||||
const [yearIndex, setYearIndex] = useState(0);
|
||||
|
||||
const setSelectedYear = (index: number) => {
|
||||
setYearIndex(index);
|
||||
}
|
||||
const setSelectedYear = (index: number) => {
|
||||
setYearIndex(index);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
versionManagerService.availableVersions$.pipe(filter(versions => !!versions?.length), take(1)).subscribe(() => {
|
||||
setAvailableYears(versionManagerService.getAvailableYears());
|
||||
});
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
versionManagerService.availableVersions$.pipe(filter(versions => !!versions?.length)).subscribe(() => {
|
||||
setAvailableYears(versionManagerService.getAvailableYears());
|
||||
});
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="w-full h-fit max-h-full flex flex-col items-center grow min-h-0">
|
||||
<TabNavBar className="mb-3" tabIndex={yearIndex} tabsText={availableYears} onTabChange={setSelectedYear}/>
|
||||
<div className="w-full h-fit max-h-full flex flex-col items-center grow min-h-0 gap-3">
|
||||
<TabNavBar tabIndex={yearIndex} tabsText={availableYears} onTabChange={setSelectedYear}/>
|
||||
<ol className="w-full min-h-0 flex transition-transform duration-300" style={{transform: `translate(${-(yearIndex * 100)}%, 0)`}}>
|
||||
{ availableYears.map(year =>
|
||||
<AvailableVersionsSlide key={year} year={year}/>
|
||||
|
||||
@@ -14,6 +14,7 @@ import { MAP_DIFFICULTIES_COLORS } from "renderer/partials/maps/map-difficulties
|
||||
import { BsmButton } from "../shared/bsm-button.component"
|
||||
import equal from "fast-deep-equal/es6";
|
||||
import clone from "rfdc";
|
||||
import { GlowEffect } from "../shared/glow-effect.component"
|
||||
|
||||
export type Props = {
|
||||
className?: string,
|
||||
@@ -214,7 +215,7 @@ export function FilterPanel({className, ref, playlist = false, filter, onChange,
|
||||
{onApply && (
|
||||
<div className="inline float-right relative w-fit h-fit mt-2">
|
||||
<BsmButton className="inline float-right rounded-md font-bold px-1 py-0.5 text-sm" text="Appliquer" typeColor="primary" withBar={false} onClick={handleApply}/>
|
||||
{haveChanged && <div className="glow-on-hover !opacity-100"></div>}
|
||||
<GlowEffect visible={haveChanged}/>
|
||||
</div>
|
||||
)}
|
||||
</motion.div>
|
||||
|
||||
+15
-7
@@ -11,6 +11,7 @@ import { BsmImage } from "../shared/bsm-image.component"
|
||||
import BeatConflict from "../../../../assets/images/apngs/beat-conflict.png"
|
||||
import { BsmButton } from "../shared/bsm-button.component"
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook"
|
||||
import BeatWaitingImg from "../../../../assets/images/apngs/beat-waiting.png"
|
||||
|
||||
type Props = {
|
||||
version: BSVersion,
|
||||
@@ -26,7 +27,7 @@ export const LocalMapsListPanel = forwardRef(({version, className, filter, searc
|
||||
|
||||
const ref = useRef(null)
|
||||
const isVisible = useInView(ref, {once: true});
|
||||
const [maps, setMaps] = useState<BsmLocalMap[]>([]);
|
||||
const [maps, setMaps] = useState<BsmLocalMap[]>(null);
|
||||
const [subs] = useState<Subscription[]>([]);
|
||||
const [selectedMaps, setSelectedMaps] = useState([]);
|
||||
const t = useTranslation();
|
||||
@@ -51,7 +52,7 @@ export const LocalMapsListPanel = forwardRef(({version, className, filter, searc
|
||||
}
|
||||
|
||||
return () => {
|
||||
setMaps(() => []);
|
||||
setMaps(() => null);
|
||||
subs.forEach(s => s.unsubscribe());
|
||||
mapsDownloader.removeOnMapDownloadedListene(loadMaps);
|
||||
}
|
||||
@@ -248,11 +249,18 @@ export const LocalMapsListPanel = forwardRef(({version, className, filter, searc
|
||||
<div ref={ref} className={className}>
|
||||
<ul className="p-3 w-full grow flex flex-wrap justify-center content-start gap-2 overflow-y-scroll scrollbar-thin scrollbar-thumb-rounded-full scrollbar-thumb-neutral-900">
|
||||
{maps?.length ? renderMaps() : (
|
||||
<div className="h-full flex flex-col items-center justify-center flex-wrap gap-1">
|
||||
<BsmImage className="h-32" image={BeatConflict}/>
|
||||
<span className="font-bold">{t("pages.version-viewer.maps.tabs.maps.empty-maps.text")}</span>
|
||||
<BsmButton className="font-bold rounded-md p-2" text="pages.version-viewer.maps.tabs.maps.empty-maps.button" typeColor="primary" withBar={false} onClick={e => {e.preventDefault(); mapsDownloader.openDownloadMapModal(version)}}/>
|
||||
</div>
|
||||
maps === null ? (
|
||||
<div className="h-full flex flex-col items-center justify-center flex-wrap gap-1">
|
||||
<img className="w-32 h-32 spin-loading" src={BeatWaitingImg} alt=" "/>
|
||||
<span className="font-bold">{t("modals.download-maps.loading-maps")}</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="h-full flex flex-col items-center justify-center flex-wrap gap-1">
|
||||
<BsmImage className="h-32" image={BeatConflict}/>
|
||||
<span className="font-bold">{t("pages.version-viewer.maps.tabs.maps.empty-maps.text")}</span>
|
||||
<BsmButton className="font-bold rounded-md p-2" text="pages.version-viewer.maps.tabs.maps.empty-maps.button" typeColor="primary" withBar={false} onClick={e => {e.preventDefault(); mapsDownloader.openDownloadMapModal(version)}}/>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -20,6 +20,7 @@ import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
import { MAP_DIFFICULTIES } from "renderer/partials/maps/map-difficulties/map-difficulties";
|
||||
import { MAP_DIFFICULTIES_COLORS } from "renderer/partials/maps/map-difficulties/map-difficulties-colors"
|
||||
import useDoubleClick from "use-double-click";
|
||||
import { GlowEffect } from "../shared/glow-effect.component";
|
||||
|
||||
export type ParsedMapDiff = {type: BsvMapDifficultyType, name: string, stars: number}
|
||||
|
||||
@@ -148,7 +149,7 @@ export const MapItem = memo(({hash, title, autor, songAutor, coverUrl, songUrl,
|
||||
|
||||
return (
|
||||
<motion.li className="relative h-[100px] min-w-[370px] shrink-0 grow basis-0 text-white group cursor-pointer" onHoverStart={() => setHovered(true)} onHoverEnd={() => setHovered(false)} style={{zIndex: hovered && 5, transform: "translateZ(0) scale(1.0, 1.0)", backfaceVisibility: "hidden"}}>
|
||||
{(hovered || selected) && onSelected && <motion.span className="glow-on-hover !transition-none" animate={{opacity: 1}} transition={{duration: .2, ease: "linear"}}/>}
|
||||
<GlowEffect visible={hovered || selected && !!onSelected}/>
|
||||
<AnimatePresence>
|
||||
{(diffsPanelHovered || bottomBarHovered) && (
|
||||
<motion.ul key={hash} className="absolute top-[calc(100%-10px)] w-full h-fit max-h-[200%] pt-4 pb-2 px-2 overflow-y-scroll bg-light-main-color-3 dark:bg-main-color-3 text-main-color-1 dark:text-current brightness-125 rounded-md flex flex-col gap-3 scrollbar-thin scrollbar-thumb-rounded-full scrollbar-thumb-neutral-900 shadow-sm shadow-black" initial={{opacity: 0}} animate={{opacity: 1}} exit={{opacity: 0}} transition={{duration: .15}} onHoverStart={diffsPanelHoverStart} onHoverEnd={diffsPanelHoverEnd}>
|
||||
|
||||
+1
-1
@@ -120,7 +120,7 @@ export function MapsPlaylistsPanel({version}: Props) {
|
||||
<div className="w-full h-full flex flex-col items-center justify-center">
|
||||
<nav className="w-full shrink-0 flex h-9 justify-center px-40 gap-2 mb-3 text-main-color-1 dark:text-white">
|
||||
<div className="h-full rounded-full bg-light-main-color-2 dark:bg-main-color-2 grow p-[6px]">
|
||||
<input type="text" className="h-full w-full bg-light-main-color-1 dark:bg-main-color-1 rounded-full px-2" placeholder={t("pages.version-viewer.maps.search-bar.search-placeholder")} value={tabIndex === 0 ? mapSearch : playlistSearch} onChange={e => handleSearch(e.target.value)}/>
|
||||
<input type="text" className="h-full w-full bg-light-main-color-1 dark:bg-main-color-1 rounded-full px-2" placeholder={t("pages.version-viewer.maps.search-bar.search-placeholder")} value={tabIndex === 0 ? mapSearch : playlistSearch} onChange={e => handleSearch(e.target.value)} tabIndex={-1}/>
|
||||
</div>
|
||||
<BsmDropdownButton className="h-full relative z-[1] flex justify-center" buttonClassName="flex items-center justify-center h-full rounded-full px-2 py-1" icon="filter" text="pages.version-viewer.maps.search-bar.filters-btn" withBar={false}>
|
||||
<FilterPanel className="absolute top-[calc(100%+3px)] origin-top w-[500px] h-fit p-2 rounded-md shadow-md shadow-black" filter={mapFilter} onChange={setMapFilter}/>
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { BsmButton } from 'renderer/components/shared/bsm-button.component'
|
||||
import { BsmImage } from 'renderer/components/shared/bsm-image.component'
|
||||
import { useTranslation } from 'renderer/hooks/use-translation.hook'
|
||||
import { ModalComponent, ModalExitCode } from 'renderer/services/modale.service'
|
||||
import BeatImpatient from "../../../../../assets/images/apngs/beat-impatient.png"
|
||||
|
||||
export const ImportVersionModal: ModalComponent<void> = ({resolver}) => {
|
||||
|
||||
const t = useTranslation();
|
||||
|
||||
return (
|
||||
<form>
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center text-gray-800 dark:text-gray-200">{t("modals.bs-import-version.title")}</h1>
|
||||
<BsmImage className="mx-auto h-20" image={BeatImpatient}/>
|
||||
<p className="max-w-sm text-gray-800 dark:text-gray-200">{t("modals.bs-import-version.description")}</p>
|
||||
<div className="grid grid-flow-col grid-cols-2 gap-4 mt-4">
|
||||
<BsmButton typeColor="cancel" className="rounded-md text-center transition-all" onClick={() => resolver({exitCode: ModalExitCode.CANCELED})} withBar={false} text="misc.cancel"/>
|
||||
<BsmButton typeColor="primary" className="rounded-md text-center transition-all" onClick={() => resolver({exitCode: ModalExitCode.COMPLETED})} withBar={false} text="modals.bs-import-version.buttons.submit"/>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import { motion, Variants } from "framer-motion";
|
||||
import { useObservable } from "renderer/hooks/use-observable.hook";
|
||||
import { AudioPlayerService } from "renderer/services/audio-player.service";
|
||||
|
||||
// Thanks to cheddZy for the icon : https://github.com/cheddZy
|
||||
|
||||
export const BsManagerIcon = memo(({className}: {className?: string}) => {
|
||||
|
||||
const audioPlayer = AudioPlayerService.getInstance();
|
||||
@@ -34,7 +36,7 @@ export const BsManagerIcon = memo(({className}: {className?: string}) => {
|
||||
<path d="M626.75,485.91l20.82,351.94a35.21,35.21,0,0,1-15.49,31.36L487.37,965.35l-.06,0A35.56,35.56,0,0,1,476,970.12a34.85,34.85,0,0,1-23.51-2.87L136.58,809.9a115.45,115.45,0,0,1-63.75-96.49L52,361.46a35.26,35.26,0,0,1,15.23-31.21l145.05-96.36a35.72,35.72,0,0,1,11.27-4.7,34.87,34.87,0,0,1,23.51,2.87L563,389.42a115.41,115.41,0,0,1,63.75,96.49Z"/>
|
||||
<path filter="brightness(30%)" d="M613.78,486.67l20.82,352a22.34,22.34,0,0,1-32.26,21.31L286.4,702.57A102.43,102.43,0,0,1,229.83,617L209,265a22.35,22.35,0,0,1,32.26-21.32L557.2,401.05A102.42,102.42,0,0,1,613.78,486.67Z"/>
|
||||
</g>
|
||||
<g fill="#fff">
|
||||
<g className="text-white stroke-black stroke-[2px] dark:stroke-0" fill="currentColor">
|
||||
<path d="M718.29,776.48,700,804.18c-40.63,39.5-87.06,67.25-133.81,90.75-87.32,43.9-203.67,77.19-249,90.34a48,48,0,0,1-26.17.16c-64-17.72-188.62-72-236.28-125.49a4.23,4.23,0,0,1,5.3-6.45c35.64,20.87,111.22,57.64,156.37,74.22,49.18,18.06,81.29,8.74,92.28-3.26a4.77,4.77,0,0,1,7.24.21c19.51,24,107.26,4.6,166.64-13C546.91,892.66,668.42,835.1,718.29,776.48Z"/>
|
||||
<path d="M522.09,933.81,374.64,989.49a3.92,3.92,0,0,0,1.44,7.58l54.62-.77a35.63,35.63,0,0,0,19-5.79l76-49.55A4,4,0,0,0,522.09,933.81Z"/>
|
||||
<path d="M218.92,195.56c-21.14.07-33.55,35.86-33.16,89.08.2,28,2.58,92.74,4.79,147.73.23,5.71-8.18,6.48-9,.81L143.13,162.47a4.38,4.38,0,0,1,7.12-4,63.63,63.63,0,0,0,21.93,11.85,4.61,4.61,0,0,0,5.9-3.72c4.2-27.7,16.63-58.74,37.35-88.74a4.36,4.36,0,0,1,7.56.69c10.86,23.87,49,103.8,93.34,152.79a4.65,4.65,0,0,1-5.62,7.22C274.14,219.18,227.7,195.54,218.92,195.56Z"/>
|
||||
|
||||
@@ -41,11 +41,12 @@ export function BsVersionItem(props: {version: BSVersion}) {
|
||||
)
|
||||
}
|
||||
|
||||
const cancel = () => {
|
||||
const versionDownload = downloaderService.currentBsVersionDownload$.value;
|
||||
downloaderService.cancelDownload().then(async res => {
|
||||
if(!res.success){ return; }
|
||||
if(!downloaderService.isVerification){
|
||||
const cancel = () => {
|
||||
const versionDownload = downloaderService.currentBsVersionDownload$.value;
|
||||
const wasVerification = downloaderService.isVerification;
|
||||
downloaderService.cancelDownload().then(async res => {
|
||||
if(!res.success){ return; }
|
||||
if(!wasVerification){
|
||||
bsUninstallerService.uninstall(versionDownload).then(res => res && verionManagerService.askInstalledVersions());
|
||||
}
|
||||
});
|
||||
@@ -81,11 +82,9 @@ export function BsVersionItem(props: {version: BSVersion}) {
|
||||
const renderVersionText = () => {
|
||||
if(props.version.name){
|
||||
return (
|
||||
<Tippy content={props.version.BSVersion} placement="right-end" arrow={false} className="font-bold !bg-main-color-3" duration={[200, 0]}>
|
||||
<div className="h-8 flex items-center dark:text-gray-200 text-gray-800 overflow-hidden">
|
||||
<div ref={ref} className='whitespace-nowrap font-bold tracking-wide w-full text-center' style={{fontSize}}>{props.version.name}</div>
|
||||
</div>
|
||||
</Tippy>
|
||||
<div className="h-8 flex items-center dark:text-gray-200 text-gray-800 overflow-hidden">
|
||||
<div ref={ref} className='whitespace-nowrap font-bold tracking-wide w-full text-center' style={{fontSize}}>{props.version.name}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
@@ -98,10 +97,20 @@ export function BsVersionItem(props: {version: BSVersion}) {
|
||||
|
||||
return (
|
||||
<NavBarItem onCancel={cancel} progress={downloading ? downloadPercent : 0} isActive={isActive() && !downloading} isDownloading={downloading}>
|
||||
<Link onDoubleClick={handleDoubleClick} to={`/bs-version/${props.version.BSVersion}`} state={props.version} className="w-full flex items-center justify-start content-center max-w-full">
|
||||
{renderIcon()}
|
||||
{renderVersionText()}
|
||||
</Link>
|
||||
{props.version.name ? (
|
||||
<Tippy content={props.version.BSVersion} placement="right-end" arrow={false} className="font-bold !bg-main-color-3" duration={[100, 0]} animation="shift-away-subtle">
|
||||
<Link onDoubleClick={handleDoubleClick} to={`/bs-version/${props.version.BSVersion}`} state={props.version} className="w-full flex items-center justify-start content-center max-w-full">
|
||||
{renderIcon()}
|
||||
{renderVersionText()}
|
||||
</Link>
|
||||
</Tippy>
|
||||
) : (
|
||||
<Link onDoubleClick={handleDoubleClick} to={`/bs-version/${props.version.BSVersion}`} state={props.version} className="w-full flex items-center justify-start content-center max-w-full">
|
||||
{renderIcon()}
|
||||
{renderVersionText()}
|
||||
</Link>
|
||||
)}
|
||||
|
||||
</NavBarItem>
|
||||
)
|
||||
}
|
||||
@@ -7,12 +7,20 @@ import { useObservable } from 'renderer/hooks/use-observable.hook';
|
||||
import { BsManagerIcon } from './bsmanager-icon.component';
|
||||
import { MapsNavBarItem } from './nav-bar-items/maps-nav-bar-item.component';
|
||||
import { NavBarSpliter } from './nav-bar-spliter.component';
|
||||
import { useThemeColor } from 'renderer/hooks/use-theme-color.hook';
|
||||
import Tippy from '@tippyjs/react';
|
||||
import { useTranslation } from 'renderer/hooks/use-translation.hook';
|
||||
import { I18nService } from 'renderer/services/i18n.service';
|
||||
|
||||
export function NavBar() {
|
||||
|
||||
const bsVersionServoce = BSVersionManagerService.getInstance();
|
||||
const bsVersionServoce = BSVersionManagerService.getInstance();
|
||||
|
||||
const installedVersions = useObservable(bsVersionServoce.installedVersions$);
|
||||
const installedVersions = useObservable(bsVersionServoce.installedVersions$);
|
||||
const color = useThemeColor("first-color");
|
||||
const t = useTranslation();
|
||||
|
||||
useObservable(I18nService.getInstance().currentLanguage$)
|
||||
|
||||
return (
|
||||
<nav id='nav-bar' className='z-10 flex flex-col h-full max-h-full items-center p-1 bg-light-main-color-1 dark:bg-main-color-1'>
|
||||
@@ -23,13 +31,17 @@ export function NavBar() {
|
||||
{installedVersions && installedVersions.map((version) => <BsVersionItem key={JSON.stringify(version)} version={version}/>)}
|
||||
</ol>
|
||||
<NavBarSpliter/>
|
||||
<div className='w-full pb-2 flex flex-col items-center content-center justify-start gap-2'>
|
||||
<Link to="blah">
|
||||
<BsmIcon icon='add' className='text-blue-500 h-[34px]'/>
|
||||
</Link>
|
||||
<Link to="settings">
|
||||
<BsmIcon icon='settings' className='text-blue-500 h-7'/>
|
||||
</Link>
|
||||
<div className='w-full pb-2 flex flex-col items-center content-center justify-start gap-1'>
|
||||
<Tippy placement="right" content={t("nav-bar.add-version")}>
|
||||
<Link className='rounded-md w-9 h-9 flex justify-center items-center hover:bg-light-main-color-3 dark:hover:bg-main-color-3' to="blah">
|
||||
<BsmIcon icon='add' className='text-blue-500 h-[34px]' style={{color}}/>
|
||||
</Link>
|
||||
</Tippy>
|
||||
<Tippy placement="right" content={t("nav-bar.settings")}>
|
||||
<Link className='rounded-md w-9 h-9 flex justify-center items-center hover:bg-light-main-color-3 dark:hover:bg-main-color-3' to="settings">
|
||||
<BsmIcon icon='settings' className='text-blue-500 h-7' style={{color}}/>
|
||||
</Link>
|
||||
</Tippy>
|
||||
</div>
|
||||
</nav>
|
||||
)
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
import { BsmIcon } from "../svgs/bsm-icon.component"
|
||||
import { useThemeColor } from "renderer/hooks/use-theme-color.hook";
|
||||
import { getCorrectTextColor } from "renderer/helpers/correct-text-color"
|
||||
import { GlowEffect } from "./glow-effect.component";
|
||||
import { motion } from "framer-motion";
|
||||
import { useState } from "react";
|
||||
|
||||
type Props = {className?:string, checked?: boolean, onChange?: (val: boolean) => void, disabled?: boolean}
|
||||
|
||||
export function BsmCheckbox({className, checked, onChange, disabled} : Props) {
|
||||
|
||||
const [hovered, setHovered] = useState(false);
|
||||
const checkedColor = useThemeColor("first-color");
|
||||
const iconColor = getCorrectTextColor(checkedColor);
|
||||
|
||||
@@ -15,12 +19,12 @@ export function BsmCheckbox({className, checked, onChange, disabled} : Props) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`${className} group-one`}>
|
||||
{!disabled && <span className="glow-on-hover !w-[calc(100%+6px)] !h-[calc(100%+6px)] !-top-[3px] !-left-[3px] group-one-hover:opacity-100"/>}
|
||||
<motion.div className={`${className} group-one`} onHoverStart={() => setHovered(true)} onHoverEnd={() => setHovered(false)}>
|
||||
<GlowEffect visible={!disabled && hovered} className="!w-[calc(100%+6px)] !h-[calc(100%+6px)] !-top-[3px] !-left-[3px]"/>
|
||||
|
||||
<span className={`w-full h-full flex items-center justify-center rounded-md border-2 border-current overflow-hidden bg-light-main-color-3 dark:bg-main-color-1 ${disabled ? "opacity-30 cursor-not-allowed" : "cursor-pointer"}`} onClickCapture={e => {e.stopPropagation(); handleClick()}}>
|
||||
<BsmIcon className="w-full h-full" icon="check" style={{color: iconColor, backgroundColor: checkedColor, visibility: checked ? "visible" : "hidden"}}/>
|
||||
</span>
|
||||
</div>
|
||||
</motion.div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { AnimatePresence, motion } from "framer-motion"
|
||||
|
||||
type Props = {
|
||||
visible?: boolean,
|
||||
className?: string
|
||||
}
|
||||
|
||||
export function GlowEffect({visible, className}: Props) {
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{visible && <motion.div transition={{duration: .1}} initial={{opacity: 0}} animate={{opacity: 1}} exit={{opacity: 0}} className={`${className} glow-on-hover`}/>}
|
||||
</AnimatePresence>
|
||||
)
|
||||
}
|
||||
@@ -46,10 +46,12 @@ import { FilterIcon } from "./icons/filter-icon.component";
|
||||
import DiscordIcon from "./icons/discord-icon.component";
|
||||
import { TwitterIcon } from "./icons/twitter-icon.component";
|
||||
import { Mee6Icon } from "./icons/mee6-icon.component";
|
||||
import { InfoIcon } from "./icons/info-icon.component";
|
||||
import { SyncIcon } from "./icons/sync-icon.component";
|
||||
|
||||
export type BsmIconType = BsvMapCharacteristic | (
|
||||
"settings"|"trash"|"favorite"|"folder"|"bsNote"|"check"|"three-dots"|"twitch"|"eye"|"play"|"checkCircleIcon"|"discord"|
|
||||
"terminal"|"desktop"|"oculus"|"add"|"cross"|"task"|"github"|"close"|"thumbUpFill"|"timerFill"|"pause"|"twitter"|
|
||||
"settings"|"trash"|"favorite"|"folder"|"bsNote"|"check"|"three-dots"|"twitch"|"eye"|"play"|"checkCircleIcon"|"discord"|"info"|
|
||||
"terminal"|"desktop"|"oculus"|"add"|"cross"|"task"|"github"|"close"|"thumbUpFill"|"timerFill"|"pause"|"twitter"|"sync"|
|
||||
"copy"|"steam"|"edit"|"export"|"patreon"|"search"|"bsMapDifficulty"|"link"|"unlink"|"download"|"filter"|"mee6"|
|
||||
"fr-FR-flag"|"es-ES-flag"|"en-US-flag"|"en-EN-flag"
|
||||
);
|
||||
@@ -103,6 +105,8 @@ export const BsmIcon = memo(({className, icon, style}: {className?: string, icon
|
||||
if(icon === "discord"){ return <DiscordIcon className={className} style={style}/> }
|
||||
if(icon === "twitter"){ return <TwitterIcon className={className} style={style}/> }
|
||||
if(icon === "mee6"){ return <Mee6Icon className={className} style={style}/> }
|
||||
if(icon === "info"){ return <InfoIcon className={className} style={style}/> }
|
||||
if(icon === "sync"){ return <SyncIcon className={className} style={style}/> }
|
||||
return <TrashIcon className={className} style={style}/>
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { CSSProperties } from "react";
|
||||
|
||||
export function InfoIcon(props: {className?: string, style?: CSSProperties}) {
|
||||
return (
|
||||
<svg className={props.className} style={props.style} viewBox="0 0 400 400">
|
||||
<path stroke="none" fill="currentColor" fillRule="evenodd" d="M225 1.3c-34.8 9.2-48.5 49.6-25.4 75 30.8 34 89 14.6 88.8-29.8 0-31-31.9-53.7-63.4-45.3m-45 134.4c-13.6 2.1-31.1 7.3-52.3 15.5l-10.9 4.2L114 167c-1.5 6.4-2.7 11.7-2.6 11.9.1.1 4-1 8.7-2.5 23.6-7.6 42.2-5.7 47.6 5 6.3 12.3 4.2 25.2-14.4 91.4-6 21.2-11.7 42.8-12.7 48-10 51.7 11.2 78.7 62.1 78.9 23 0 26-.7 61-14.1L278 380l2.8-11.7c3.3-13.6 3.5-12.5-1.8-10.5-24.3 9.3-48.5 6.9-53.6-5.3-5.2-12.4-3-25.6 14.5-87.8 13.3-47 12.5-44.1 13.8-52.7 6.5-42.6-6-66.5-38.8-74.6-6.5-1.6-28.5-2.8-34.7-1.9"/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import { CSSProperties } from "react";
|
||||
|
||||
export function SettingIcon(props: {className?: string, style?: CSSProperties}) {
|
||||
return (
|
||||
<svg className={props.className} style={props.style} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40" height="40" width="40">
|
||||
<svg className={props.className} style={props.style} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40">
|
||||
<path fill="currentColor" d="M16.5 35.833 15.792 30.792Q15 30.542 14.104 30.042Q13.208 29.542 12.542 28.958L7.875 31.042L4.375 24.875L8.542 21.792Q8.458 21.375 8.438 20.896Q8.417 20.417 8.417 20Q8.417 19.625 8.438 19.146Q8.458 18.667 8.542 18.208L4.375 15.125L7.875 9L12.542 11.042Q13.25 10.458 14.125 9.979Q15 9.5 15.792 9.208L16.5 4.167H23.5L24.208 9.208Q25.042 9.542 25.875 10Q26.708 10.458 27.375 11.042L32.125 9L35.625 15.125L31.375 18.208Q31.5 18.667 31.521 19.125Q31.542 19.583 31.542 20Q31.542 20.417 31.5 20.854Q31.458 21.292 31.375 21.792L35.583 24.875L32.083 31.042L27.375 28.917Q26.667 29.542 25.854 30.042Q25.042 30.542 24.208 30.792L23.5 35.833ZM19.958 24.792Q21.958 24.792 23.375 23.396Q24.792 22 24.792 20Q24.792 18 23.375 16.604Q21.958 15.208 19.958 15.208Q17.958 15.208 16.562 16.604Q15.167 18 15.167 20Q15.167 22 16.562 23.396Q17.958 24.792 19.958 24.792Z"/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { CSSProperties } from "react";
|
||||
|
||||
export function SyncIcon(props: {className?: string, style?: CSSProperties}) {
|
||||
return (
|
||||
<svg className={props.className} style={props.style} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40">
|
||||
<path fill="currentColor" d="M25.625 32.542q-.917.416-1.667-.084t-.75-1.625q0-.5.354-1 .355-.5.855-.75 2.666-1.25 4.187-3.75 1.521-2.5 1.521-5.458 0-1.833-.854-3.708-.854-1.875-2.313-3.375l-.791-.709v2.75q0 .667-.459 1.105-.458.437-1.083.437-.667 0-1.104-.437-.438-.438-.438-1.105V8.125q0-.792.542-1.312.542-.521 1.292-.521h6.75q.666 0 1.104.437.437.438.437 1.104 0 .667-.437 1.084-.438.416-1.104.416h-3l.375.417q2.416 2.292 3.604 4.938 1.187 2.645 1.187 5.187 0 4.208-2.229 7.583t-5.979 5.084ZM8.417 33.708q-.667 0-1.084-.437-.416-.438-.416-1.104 0-.667.416-1.084.417-.416 1.084-.416h2.958L11 30.333q-2.5-2.208-3.667-4.729-1.166-2.521-1.166-5.479 0-4.208 2.271-7.604 2.27-3.396 5.979-5.063.916-.416 1.645.105.73.52.73 1.604 0 .5-.334 1-.333.5-.833.75-2.625 1.25-4.187 3.75-1.563 2.5-1.563 5.458 0 2.25.854 4.042.854 1.791 2.396 3.208l.792.542v-2.75q0-.667.458-1.105.458-.437 1.125-.437.625 0 1.062.437.438.438.438 1.105v6.708q0 .792-.542 1.313-.541.52-1.291.52Z"/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
+21
-6
@@ -1,18 +1,33 @@
|
||||
import Tippy from "@tippyjs/react";
|
||||
import { GlowEffect } from "renderer/components/shared/glow-effect.component";
|
||||
import { BsmIcon, BsmIconType } from "renderer/components/svgs/bsm-icon.component"
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
|
||||
type props = {onClick: (active: boolean) => void, active: boolean, text: string, icon: BsmIconType}
|
||||
type props = {
|
||||
onClick: (active: boolean) => void,
|
||||
active: boolean,
|
||||
text: string,
|
||||
icon: BsmIconType,
|
||||
infoText?: string,
|
||||
}
|
||||
|
||||
export function LaunchModToogle({onClick, active, text, icon}: props) {
|
||||
export function LaunchModToogle({onClick, active, text, icon, infoText}: props) {
|
||||
|
||||
const t = useTranslation();
|
||||
|
||||
return (
|
||||
<div className={`relative rounded-full cursor-pointer group active:scale-95 transition-transform ${!active && "shadow-md shadow-black"}`} onClick={() => onClick(!active)}>
|
||||
<div className={`absolute glow-on-hover !rounded-full ${active && "!opacity-100 blur-[2px]"}`}/>
|
||||
<div className='w-full h-full px-6 flex justify-center items-center bg-light-main-color-2 dark:bg-main-color-2 p-3 rounded-full text-gray-800 dark:text-white group-hover:bg-light-main-color-1 dark:group-hover:bg-main-color-1'>
|
||||
<BsmIcon icon={icon} className='mr-1 h-7 text-gray-800 dark:text-white'/>
|
||||
<span className='w-fit min-w-fit h-full text-lg font-bold uppercase tracking-wide italic'>{t(text)}</span>
|
||||
<GlowEffect visible={active} className="absolute !rounded-full blur-[2px]"/>
|
||||
<div className='w-full h-full px-6 flex gap-1.5 justify-center items-center bg-light-main-color-2 dark:bg-main-color-2 p-3 rounded-full text-gray-800 dark:text-white group-hover:bg-light-main-color-1 dark:group-hover:bg-main-color-1'>
|
||||
<BsmIcon icon={icon} className='h-7 text-gray-800 dark:text-white'/>
|
||||
<span className='w-fit min-w-fit text-lg font-bold uppercase tracking-wide italic '>{t(text)}</span>
|
||||
{infoText && (
|
||||
<Tippy content={t(infoText)} placement="bottom" delay={[400, 0]}>
|
||||
<div className="h-[25px] w-[25px] p-1.5 rounded-full cursor-help bg-light-main-color-1 dark:bg-main-color-3 hover:brightness-110">
|
||||
<BsmIcon className="w-full h-full" icon="info"/>
|
||||
</div>
|
||||
</Tippy>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -39,9 +39,9 @@ export function LaunchSlide({version}: Props) {
|
||||
return (
|
||||
<div className="w-full shrink-0 items-center relative flex flex-col justify-start">
|
||||
<div className='grid grid-flow-col gap-6'>
|
||||
{!version.oculus && <LaunchModToogle icon='oculus' onClick={() => setMode(LaunchMods.OCULUS_MOD, !oculusMode)} active={oculusMode} text="pages.version-viewer.launch-mods.oculus"/>}
|
||||
<LaunchModToogle icon='desktop' onClick={() => setMode(LaunchMods.DESKTOP_MOD, !desktopMode)} active={desktopMode} text="pages.version-viewer.launch-mods.desktop"/>
|
||||
<LaunchModToogle icon='terminal' onClick={() => setMode(LaunchMods.DEBUG_MOD, !debugMode)} active={debugMode} text="pages.version-viewer.launch-mods.debug"/>
|
||||
{!version.oculus && <LaunchModToogle infoText="pages.version-viewer.launch-mods.oculus-description" icon='oculus' onClick={() => setMode(LaunchMods.OCULUS_MOD, !oculusMode)} active={oculusMode} text="pages.version-viewer.launch-mods.oculus"/>}
|
||||
<LaunchModToogle infoText="pages.version-viewer.launch-mods.desktop-description" icon='desktop' onClick={() => setMode(LaunchMods.DESKTOP_MOD, !desktopMode)} active={desktopMode} text="pages.version-viewer.launch-mods.desktop"/>
|
||||
<LaunchModToogle infoText="pages.version-viewer.launch-mods.debug-description" icon='terminal' onClick={() => setMode(LaunchMods.DEBUG_MOD, !debugMode)} active={debugMode} text="pages.version-viewer.launch-mods.debug"/>
|
||||
</div>
|
||||
<div className='grow flex justify-center items-center'>
|
||||
<BsmButton onClick={launch} active={JSON.stringify(version) === JSON.stringify(launchState)} className='relative text-5xl text-gray-800 dark:text-gray-200 font-bold tracking-wide pt-1 pb-3 px-7 rounded-lg shadow-md italic shadow-black active:scale-90 transition-transform' text="misc.launch"/>
|
||||
|
||||
@@ -50,8 +50,6 @@
|
||||
width: calc(100% + 8px);
|
||||
height: calc(100% + 8px);
|
||||
animation: glowing 20s linear infinite;
|
||||
opacity: 0;
|
||||
transition: opacity .2s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes glowing {
|
||||
|
||||
@@ -7,45 +7,99 @@ import { BsmButton } from "renderer/components/shared/bsm-button.component";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
import { BSVersionManagerService } from "renderer/services/bs-version-manager.service";
|
||||
import { useObservable } from "renderer/hooks/use-observable.hook";
|
||||
import { BsmDropdownButton } from "renderer/components/shared/bsm-dropdown-button.component";
|
||||
import { ProgressBarService } from "renderer/services/progress-bar.service";
|
||||
import { ModalExitCode, ModalService } from "renderer/services/modale.service";
|
||||
import { ImportVersionModal } from "renderer/components/modal/modal-types/import-version-modal.component";
|
||||
import { IpcService } from "renderer/services/ipc.service";
|
||||
import { NotificationService } from "renderer/services/notification.service";
|
||||
import { timer } from "rxjs";
|
||||
|
||||
export function AvailableVersionsList() {
|
||||
|
||||
const bsDownloaderService: BsDownloaderService = BsDownloaderService.getInstance();
|
||||
const versionManagerService: BSVersionManagerService = BSVersionManagerService.getInstance();
|
||||
const bsDownloaderService = BsDownloaderService.getInstance();
|
||||
const versionManagerService = BSVersionManagerService.getInstance();
|
||||
const progressBar = ProgressBarService.getInstance();
|
||||
const modal = ModalService.getInsance();
|
||||
const ipc = IpcService.getInstance();
|
||||
const installer = BsDownloaderService.getInstance();
|
||||
const notification = NotificationService.getInstance();
|
||||
|
||||
const [versionSelected, setVersionSelected] = useState(null as BSVersion);
|
||||
const currentVersionDownloading = useObservable(bsDownloaderService.currentBsVersionDownload$);
|
||||
const [downloading, setDownloading] = useState(false);
|
||||
const t = useTranslation();
|
||||
|
||||
const startDownload = () => {
|
||||
bsDownloaderService.download(versionSelected, versionManagerService.isVersionInstalled(versionSelected));
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const versionSelectedSub = bsDownloaderService.selectedBsVersion$.subscribe(version => {
|
||||
setVersionSelected(version);
|
||||
});
|
||||
|
||||
return () => {
|
||||
versionSelectedSub.unsubscribe();
|
||||
const startDownload = () => {
|
||||
setDownloading(() => true)
|
||||
bsDownloaderService.download(versionSelected).finally(() => {
|
||||
setDownloading(() => false);
|
||||
});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const versionSelectedSub = bsDownloaderService.selectedBsVersion$.subscribe(version => {
|
||||
setVersionSelected(version);
|
||||
});
|
||||
|
||||
return () => {
|
||||
versionSelectedSub.unsubscribe();
|
||||
}
|
||||
}, [])
|
||||
|
||||
|
||||
const importVersion = async () => {
|
||||
if(!progressBar.require()){ return; }
|
||||
|
||||
const modalRes = await modal.openModal(ImportVersionModal);
|
||||
|
||||
if(modalRes.exitCode !== ModalExitCode.COMPLETED){ return; }
|
||||
|
||||
const folderRes = await ipc.send<{canceled: boolean, filePaths: string[]}>("choose-folder");
|
||||
|
||||
if(!folderRes.success || folderRes.data.canceled || !folderRes.data.filePaths?.length){ return; }
|
||||
|
||||
notification.notifySuccess({title: "notifications.bs-import-version.success.start-import.title", desc: "notifications.bs-import-version.success.start-import.desc", duration: 6_000});
|
||||
|
||||
progressBar.showFake(.008);
|
||||
|
||||
const toImport = folderRes.data.filePaths.at(0);
|
||||
|
||||
const imported = await installer.importVersion(toImport);
|
||||
|
||||
if(imported){
|
||||
versionManagerService.askInstalledVersions();
|
||||
notification.notifySuccess({title: "notifications.bs-import-version.success.imported.title", duration: 3_000});
|
||||
progressBar.complete();
|
||||
await timer(400).toPromise();
|
||||
}
|
||||
else{
|
||||
notification.notifyError({title: "notifications.types.error", desc: "notifications.bs-import-version.errors.import-error.desc"})
|
||||
}
|
||||
|
||||
progressBar.hide(true);
|
||||
|
||||
}
|
||||
}, [])
|
||||
|
||||
|
||||
return (
|
||||
<div className="relative h-full w-full flex items-center flex-col pt-2">
|
||||
<Slideshow className="absolute w-full h-full top-0"/>
|
||||
<h1 className="text-gray-100 text-2xl mb-4 z-[1]">{t("pages.available-versions.title")}</h1>
|
||||
<AvailableVersionsSlider/>
|
||||
<Slideshow className="absolute w-full h-full top-0"/>
|
||||
<h1 className="text-gray-100 text-2xl mb-4 z-[1]">{t("pages.available-versions.title")}</h1>
|
||||
|
||||
<AnimatePresence>
|
||||
{ versionSelected && !currentVersionDownloading && (
|
||||
<motion.div initial={{y:"150%"}} animate={{y:"0%"}} exit={{y:"150%"}} className="absolute bottom-5" onClick={startDownload}>
|
||||
<BsmButton text={versionManagerService.isVersionInstalled(versionSelected) ? "misc.verify" : "misc.download"} className="relative text-gray-800 dark:text-gray-100 rounded-md text-3xl font-bold italic tracking-wide px-3 pb-2 pt-1 shadow-md shadow-black"/>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
<AvailableVersionsSlider/>
|
||||
|
||||
<AnimatePresence>
|
||||
{ versionSelected && !downloading && (
|
||||
<motion.div initial={{y:"150%"}} animate={{y:"0%"}} exit={{y:"150%"}} className="absolute bottom-5" onClick={startDownload}>
|
||||
<BsmButton text="misc.download" className="relative text-gray-800 dark:text-gray-100 rounded-md text-3xl font-bold italic tracking-wide px-3 pb-2 pt-1 shadow-md shadow-black"/>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
<BsmDropdownButton className="absolute top-5 right-5 h-9 w-9 bg-light-main-color-2 dark:bg-main-color-2 rounded-md" icon="settings" items={[
|
||||
{icon: "sync", text: "pages.available-versions.dropdown.refresh", onClick: () => versionManagerService.askAvailableVersions()},
|
||||
{icon: "download", text: "pages.available-versions.dropdown.import-version", onClick: importVersion}
|
||||
]}/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -28,7 +28,13 @@ export function VersionViewer() {
|
||||
const navigate = useNavigate();
|
||||
const [currentTabIndex, setCurrentTabIndex] = useState(0);
|
||||
|
||||
const navigateToVersion = (version: BSVersion) => navigate(`/bs-version/${version.BSVersion}`, {state: version});
|
||||
const navigateToVersion = (version?: BSVersion) => {
|
||||
console.log(version);
|
||||
if(!version){
|
||||
return navigate("/available-versions");
|
||||
}
|
||||
navigate(`/bs-version/${version.BSVersion}`, {state: version});
|
||||
}
|
||||
const openFolder = () => ipcService.sendLazy("bs-version.open-folder", {args: state});
|
||||
const verifyFiles = () => bsDownloaderService.download(state, true);
|
||||
|
||||
@@ -36,9 +42,9 @@ export function VersionViewer() {
|
||||
const modalCompleted = await modalService.openModal(UninstallModal, state);
|
||||
if(modalCompleted.exitCode === ModalExitCode.COMPLETED){
|
||||
bsUninstallerService.uninstall(state).then(() => {
|
||||
bsVersionManagerService.askInstalledVersions();
|
||||
const newVersionPage = bsVersionManagerService.getInstalledVersions()[0];
|
||||
navigateToVersion(newVersionPage);
|
||||
bsVersionManagerService.askInstalledVersions().then(versions => {
|
||||
navigateToVersion(versions?.at(0));
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DownloadEvent } from 'main/services/bs-installer.service';
|
||||
import { DownloadEvent, DownloadInfo } from 'main/services/bs-installer.service';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { distinctUntilChanged, filter, throttleTime } from 'rxjs/operators';
|
||||
import { IpcResponse } from 'shared/models/ipc';
|
||||
@@ -71,10 +71,15 @@ export class BsDownloaderService{
|
||||
this.ipcService.sendLazy('bs-download.[2FA]', {args: res.data});
|
||||
});
|
||||
|
||||
this.ipcService.watch<BSVersion>("start-download-version").subscribe(res => {
|
||||
this.currentBsVersionDownload$.next(res.data);
|
||||
});
|
||||
|
||||
this.currentBsVersionDownload$.subscribe(version => {
|
||||
if(version){ this.bsVersionManager.setInstalledVersions([...this.bsVersionManager.installedVersions$.value, version]); }
|
||||
else{ this.bsVersionManager.askInstalledVersions(); }
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private resetDownload(): void{
|
||||
@@ -111,7 +116,7 @@ export class BsDownloaderService{
|
||||
}
|
||||
|
||||
this.progressBarService.show(this.downloadProgress$);
|
||||
this._isVerification = !!isVerification;
|
||||
this._isVerification = isVerification;
|
||||
|
||||
let promise;
|
||||
if(!this.authService.sessionExist()){
|
||||
@@ -121,14 +126,12 @@ export class BsDownloaderService{
|
||||
return {success: false};
|
||||
}
|
||||
this.authService.setSteamSession(res.data.username, res.data.stay);
|
||||
promise = this.ipcService.send<DownloadEvent>('bs-download.start', {args: {bsVersion, username: res.data.username, password: res.data.password, stay: res.data.stay}});
|
||||
promise = this.ipcService.send<DownloadEvent, DownloadInfo>('bs-download.start', {args: {bsVersion, username: res.data.username, password: res.data.password, stay: res.data.stay, isVerification}});
|
||||
}
|
||||
else{
|
||||
promise = this.ipcService.send<DownloadEvent>('bs-download.start', {args: {bsVersion, username: this.authService.getSteamUsername()}});
|
||||
promise = this.ipcService.send<DownloadEvent, DownloadInfo>('bs-download.start', {args: {bsVersion, username: this.authService.getSteamUsername(), isVerification}});
|
||||
}
|
||||
|
||||
this.currentBsVersionDownload$.next(bsVersion);
|
||||
|
||||
let res = await promise;
|
||||
|
||||
if(res.data?.type === "[Password]"){
|
||||
@@ -145,15 +148,21 @@ export class BsDownloaderService{
|
||||
}
|
||||
|
||||
public get isDownloading(): boolean{ return !!this.currentBsVersionDownload$.value; }
|
||||
public get isVerification(): boolean{ return this._isVerification; }
|
||||
|
||||
public async getInstallationFolder(): Promise<string>{
|
||||
const res = await this.ipcService.send<string>("bs-download.installation-folder");
|
||||
return res.success ? res.data : "";
|
||||
}
|
||||
|
||||
public get isVerification(): boolean{ return this._isVerification; }
|
||||
|
||||
public setInstallationFolder(path: string): Promise<IpcResponse<string>>{
|
||||
return this.ipcService.send<string>("bs-download.set-installation-folder", {args: path});
|
||||
}
|
||||
|
||||
public async importVersion(pathToImport: string): Promise<boolean>{
|
||||
const res = await this.ipcService.send<void>("bs-download.import-version", {args: pathToImport});
|
||||
return res.success;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import { MapsPage } from "../pages/maps-page.component";
|
||||
import "tailwindcss/tailwind.css";
|
||||
import { BsmIframeView } from "../components/shared/iframe-view.component";
|
||||
import 'tippy.js/dist/tippy.css';
|
||||
import 'tippy.js/animations/shift-away-subtle.css';
|
||||
import { MapsManagerService } from "renderer/services/maps-manager.service";
|
||||
import { PlaylistsManagerService } from "renderer/services/playlists-manager.service";
|
||||
import { ModelsManagerService } from "renderer/services/models-manager.service";
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
export interface BSVersion {
|
||||
BSVersion: string,
|
||||
export interface PartialBSVersion {
|
||||
BSVersion: string,
|
||||
name?: string
|
||||
}
|
||||
|
||||
export interface BSVersion extends PartialBSVersion {
|
||||
BSManifest?: string,
|
||||
ReleaseURL?: string,
|
||||
ReleaseImg?: string,
|
||||
@@ -7,6 +11,5 @@ export interface BSVersion {
|
||||
year?: string,
|
||||
steam?: boolean,
|
||||
oculus?: boolean,
|
||||
name?: string,
|
||||
color?: string
|
||||
}
|
||||
Reference in New Issue
Block a user