Merge branch 'master' into fix/running-processes

This commit is contained in:
Pierce Thompson
2023-06-28 16:29:27 -04:00
committed by GitHub
14 changed files with 642 additions and 587 deletions
+555 -543
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -179,7 +179,7 @@
"css-minimizer-webpack-plugin": "^4.1.0",
"detect-port": "^1.3.0",
"electron": "^24.3.1",
"electron-builder": "^23.6.0",
"electron-builder": "^24.4.0",
"electron-devtools-installer": "^3.2.0",
"electron-notarize": "^1.2.1",
"electron-rebuild": "^3.2.9",
@@ -238,7 +238,7 @@
"electron-debug": "^3.2.0",
"electron-log": "^4.4.8",
"electron-store": "^8.1.0",
"electron-updater": "^6.0.3",
"electron-updater": "^6.1.1",
"fast-deep-equal": "^3.1.3",
"framer-motion": "^10.12.16",
"fs-extra": "^11.1.1",
+4 -2
View File
@@ -1,4 +1,4 @@
import { ipcMain } from 'electron';
import { ipcMain, shell } from 'electron';
import { UtilsService } from '../services/utils.service';
import { BSVersionLibService } from '../services/bs-version-lib.service'
import { BSVersion } from 'shared/bs-version.interface';
@@ -38,7 +38,9 @@ ipcMain.on('bs-version.installed-versions', async (event, req: IpcRequest<void>)
ipcMain.on("bs-version.open-folder", async (event, req: IpcRequest<BSVersion>) => {
const localVersionService = BSLocalVersionService.getInstance();
const versionFolder = await localVersionService.getVersionPath(req.args);
(await pathExist(versionFolder)) && exec(`start "" "${versionFolder}"`);
if (!(await pathExist(versionFolder)))
return;
shell.openPath(versionFolder);
});
ipcMain.on("bs-version.edit", async (event, req: IpcRequest<{version: BSVersion, name: string, color: string}>) => {
+1 -1
View File
@@ -46,7 +46,7 @@ const installExtensions = async () => {
const forceDownload = !!process.env.UPGRADE_EXTENSIONS;
const extensions = ['REACT_DEVELOPER_TOOLS'];
return installer.default(extensions.map((name) => installer[name]), forceDownload).catch(console.log);
return installer.default(extensions.map((name) => installer[name]), forceDownload).catch(console.error);
};
const createWindow = async (window: AppWindow = "launcher.html") => {
+6 -3
View File
@@ -66,9 +66,12 @@ export class BSInstallerService{
return new Promise(resolve => {
if(this.downloadProcess?.killed && !this.downloadProcess?.pid){ return resolve(false); }
this.downloadProcess.once('exit', () => resolve(true));
ctrlc(this.downloadProcess.pid);
this.downloadProcess?.once('exit', () => resolve(true));
if (process.platform === 'win32') {
ctrlc(this.downloadProcess.pid);
} else {
this.downloadProcess.kill();
}
setTimeout(() => resolve(false), 3000);
});
}
+23 -14
View File
@@ -1,12 +1,13 @@
import { UtilsService } from "./utils.service";
import regedit from 'regedit'
import path from "path";
import { path, join } from "path";
import { parse } from "@node-steam/vdf";
import { readFile } from "fs/promises";
import { spawn } from "child_process";
import { pathExist } from "../helpers/fs.helpers";
import log from "electron-log";
import psList from 'ps-list';
import { app } from "electron";
export class SteamService{
@@ -44,20 +45,28 @@ export class SteamService{
public async getSteamPath(): Promise<string>{
if(!!this.steamPath){ return this.steamPath; }
if(this.steamPath){ return this.steamPath; }
const [win32Res, win64Res] = await Promise.all([
regedit.promisified.list(['HKLM\\SOFTWARE\\Valve\\Steam']),
regedit.promisified.list(['HKLM\\SOFTWARE\\WOW6432Node\\Valve\\Steam'])
]);
switch (process.platform) {
case "linux":
this.steamPath = path.join(app.getPath('home'), '.steam', "steam");
return this.steamPath;
case "win32":
const [win32Res, win64Res] = await Promise.all([
regedit.promisified.list(['HKLM\\SOFTWARE\\Valve\\Steam']),
regedit.promisified.list(['HKLM\\SOFTWARE\\WOW6432Node\\Valve\\Steam'])
]);
const [win32, win64] = [win32Res["HKLM\\SOFTWARE\\Valve\\Steam"], win64Res["HKLM\\SOFTWARE\\WOW6432Node\\Valve\\Steam"]];
let res = '';
if(win64.exists && win64?.values?.InstallPath?.value){ res = win64.values.InstallPath.value as string; }
else if(win32.exists && win32?.values?.InstallPath?.value){ res = win32.values.InstallPath.value as string; }
this.steamPath = res;
return this.steamPath;
const [win32, win64] = [win32Res["HKLM\\SOFTWARE\\Valve\\Steam"], win64Res["HKLM\\SOFTWARE\\WOW6432Node\\Valve\\Steam"]];
let res = '';
if(win64.exists && win64?.values?.InstallPath?.value){ res = win64.values.InstallPath.value as string; }
else if(win32.exists && win32?.values?.InstallPath?.value){ res = win32.values.InstallPath.value as string; }
this.steamPath = res;
return res;
default:
return null;
}
}
public async getGameFolder(gameId: string, gameFolder?: string): Promise<string>{
@@ -88,7 +97,7 @@ export class SteamService{
const process = spawn("start", ["steam://open/games"], {shell: true});
process.on("error", log.error);
return new Promise(async (resolve, reject) => {
// Every 3 seconds check if steam is running
const interval = setInterval(async () => {
@@ -4,7 +4,6 @@ import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useSta
import { BsmLocalMap } from "shared/models/maps/bsm-local-map.interface"
import { Subscription } from "rxjs"
import { MapFilter } from "shared/models/maps/beat-saver.model"
import { useInView } from "framer-motion"
import { MapsDownloaderService } from "renderer/services/maps-downloader.service"
import { VariableSizeList } from "react-window"
import { MapsRow } from "./maps-row.component"
@@ -18,16 +17,18 @@ import BeatConflict from "../../../../assets/images/apngs/beat-conflict.png"
import { BsmImage } from "../shared/bsm-image.component"
import { BsmButton } from "../shared/bsm-button.component"
import TextProgressBar from "../progress-bar/text-progress-bar.component"
import { useChangeOnce } from "renderer/hooks/use-change-once.hook"
type Props = {
version: BSVersion,
className?: string,
filter?: MapFilter
search?: string,
linked?: boolean
linked?: boolean,
isActive?: boolean
}
export const LocalMapsListPanel = forwardRef(({version, className, filter, search, linked} : Props, forwardRef) => {
export const LocalMapsListPanel = forwardRef(({version, className, filter, search, linked, isActive} : Props, forwardRef) => {
const mapsManager = MapsManagerService.getInstance();
const mapsDownloader = MapsDownloaderService.getInstance();
@@ -35,13 +36,13 @@ export const LocalMapsListPanel = forwardRef(({version, className, filter, searc
const os = OsDiagnosticService.getInstance();
const t = useTranslation();
const ref = useRef(null)
const isVisible = useInView(ref, {once: true, amount: .5});
const ref = useRef(null);
const [maps, setMaps] = useState<BsmLocalMap[]>(null);
const [subs] = useState<Subscription[]>([]);
const [selectedMaps$] = useState(new BehaviorSubject<BsmLocalMap[]>([]));
const [itemPerRow, setItemPerRow] = useState(2);
const [listHeight, setListHeight] = useState(0);
const isActiveOnce = useChangeOnce(isActive, true);
const [loadPercent$] = useState(new BehaviorSubject(0));
@@ -64,7 +65,7 @@ export const LocalMapsListPanel = forwardRef(({version, className, filter, searc
useEffect(() => {
if(isVisible){
if(isActiveOnce){
loadMaps();
mapsDownloader.addOnMapDownloadedListener((map, targerVersion) => {
if(targerVersion !== version){ return; }
@@ -78,11 +79,11 @@ export const LocalMapsListPanel = forwardRef(({version, className, filter, searc
subs.forEach(s => s.unsubscribe());
mapsDownloader.removeOnMapDownloadedListener(loadMaps);
}
}, [isVisible, version, linked]);
}, [isActiveOnce, version, linked]);
useEffect(() => {
if(!isVisible){ return () => {}; }
if(!isActiveOnce){ return; }
const updateItemPerRow = (listWidth: number) => {
const newPerRow = Math.min(Math.floor(listWidth / 400), 3);
@@ -106,7 +107,7 @@ export const LocalMapsListPanel = forwardRef(({version, className, filter, searc
sub.unsubscribe();
}
}, [isVisible, itemPerRow])
}, [isActiveOnce, itemPerRow])
const loadMaps = () => {
@@ -136,10 +137,17 @@ export const LocalMapsListPanel = forwardRef(({version, className, filter, searc
const removeMapsFromList = (mapsToRemove: BsmLocalMap[]) => {
const filtredMaps = maps.filter(map => !mapsToRemove.some(toDeleteMaps => map.hash === toDeleteMaps.hash));
setMaps(() => filtredMaps);
const filtredSelectedMaps = selectedMaps$.value.filter(map => !mapsToRemove.some(toDeleteMaps => map.hash === toDeleteMaps.hash));
selectedMaps$.next(filtredSelectedMaps);
};
const handleDelete = useCallback((map: BsmLocalMap) => {
mapsManager.deleteMaps([map], version).then(res => res && removeMapsFromList([map]));
mapsManager.deleteMaps([map], version).then(res => {
if(!res){ return; }
removeMapsFromList([map]);
});
}, [version, maps]);
const onMapSelected = useCallback((map: BsmLocalMap) => {
@@ -21,10 +21,11 @@ import { VersionFolderLinkerService, VersionLinkerActionListener } from "rendere
type Props = {
version?: BSVersion
version?: BSVersion,
isActive?: boolean
}
export function MapsPlaylistsPanel({version}: Props) {
export function MapsPlaylistsPanel({version, isActive}: Props) {
const mapsService = MapsManagerService.getInstance();
const mapsDownloader = MapsDownloaderService.getInstance();
@@ -63,7 +64,7 @@ export function MapsPlaylistsPanel({version}: Props) {
linker.removeVersionFolderUnlinkedListener(onMapsLinked);
}
}, [version]);
}, [version, isActive]);
const loadMapIsLinked = () => {
mapsService.versionHaveMapsLinked(version).then(setMapsLinked);
@@ -160,7 +161,7 @@ export function MapsPlaylistsPanel({version}: Props) {
<div className="w-full h-full flex flex-col bg-light-main-color-3 dark:bg-main-color-2 rounded-md shadow-black shadow-md overflow-hidden">
<TabNavBar className="!rounded-none shadow-sm" tabIndex={tabIndex} tabsText={["misc.maps", "misc.playlists"]} onTabChange={setTabIndex} renderTab={renderTab}/>
<div className="w-full grow min-h-0 flex flex-row items-center transition-transform duration-300" style={{transform: `translate(${-(tabIndex * 100)}%, 0)`}}>
<LocalMapsListPanel ref={mapsRef} className="w-full h-full shrink-0 flex flex-col" version={version} filter={mapFilter} search={mapSearch} linked={mapsLinked}/>
<LocalMapsListPanel isActive={isActive && tabIndex === 0} ref={mapsRef} className="w-full h-full shrink-0 flex flex-col" version={version} filter={mapFilter} search={mapSearch} linked={mapsLinked}/>
<div className="w-full h-full shrink-0 flex flex-col justify-center items-center content-center gap-2 overflow-hidden text-gray-800 dark:text-gray-200">
<BsmImage className="rounded-md" image={wipGif}/>
<span>Coming soon</span>
@@ -41,7 +41,6 @@ export function BsmButton({className, style, imgClassName, iconClassName, icon,
const textColor = (() => {
if(primaryColor){
console.log(getCorrectTextColor(primaryColor), text);
return getCorrectTextColor(primaryColor);
}
return typeColor ? "white" : undefined;
@@ -0,0 +1,17 @@
import equal from "fast-deep-equal";
import { useEffect, useRef, useState } from "react";
export function useChangeOnce<T = unknown>(initialValue: T, onlyFirstIfTruthy?: boolean): T {
const [trackedValue, setTrackedValue] = useState<T>(initialValue);
const didChangeOnceRef = useRef<boolean>(onlyFirstIfTruthy ? !!initialValue : false);
useEffect(() => {
if(didChangeOnceRef.current || equal(initialValue, trackedValue)){ return; }
setTrackedValue(() => initialValue);
didChangeOnceRef.current = true;
}, [initialValue]);
return trackedValue;
}
@@ -3,12 +3,9 @@ import { MapsPlaylistsPanel } from "renderer/components/maps-mangement-component
import { ModelsPanel } from "renderer/components/models-management/models-panel.component";
import { TabNavBar } from "renderer/components/shared/tab-nav-bar.component";
import { Slideshow } from "renderer/components/slideshow/slideshow.component";
import { useTranslation } from "renderer/hooks/use-translation.hook";
export function SharedContentsPage() {
const t = useTranslation();
const [tabIndex, setTabIndex] = useState(0);
return (
@@ -17,7 +14,7 @@ export function SharedContentsPage() {
<TabNavBar className='my-4' tabIndex={tabIndex} tabsText={["misc.maps", "misc.models"]} onTabChange={setTabIndex}/>
<div className='w-full min-h-0 grow flex transition-transform duration-300' style={{transform: `translate(${-(tabIndex * 100)}%, 0)`}}>
<div className="w-full shrink-0 px-3 pb-3 flex flex-col items-center">
<MapsPlaylistsPanel/>
<MapsPlaylistsPanel isActive={tabIndex === 0}/>
</div>
<div className="w-full shrink-0 px-3 pb-3 flex flex-col items-center">
<ModelsPanel isActive={tabIndex === 1}/>
@@ -80,7 +80,7 @@ export function VersionViewer() {
<div className='w-full min-h-0 grow flex transition-transform duration-300' style={{transform: `translate(${-(currentTabIndex * 100)}%, 0)`}}>
<LaunchSlide version={state}/>
<div className="w-full shrink-0 px-3 pb-3 flex flex-col items-center">
<MapsPlaylistsPanel version={state}/>
<MapsPlaylistsPanel version={state} isActive={currentTabIndex === 1}/>
</div>
<div className="w-full shrink-0 px-3 pb-3 flex flex-col items-center">
<ModelsPanel version={state} isActive={currentTabIndex === 2} goToMods={() => setCurrentTabIndex(() => 3)}/>
@@ -86,7 +86,7 @@ export class MapsManagerService {
public async deleteMaps(maps: BsmLocalMap[], version?: BSVersion): Promise<boolean>{
const versionLinked = await this.versionHaveMapsLinked(version);
const versionLinked = !version || await this.versionHaveMapsLinked(version);
const askModal = maps.length > 1 || !this.config.get<boolean>(MapsManagerService.REMEMBER_CHOICE_DELETE_MAP_KEY);
+8 -1
View File
@@ -1,5 +1,12 @@
const colors = require('tailwindcss/colors');
/** @type {import('tailwindcss').Config} */
// Suppress deprecation warnings during build
delete colors.lightBlue;
delete colors.warmGray;
delete colors.trueGray;
delete colors.coolGray;
delete colors.blueGray;
module.exports = {
darkMode: 'class',
content: ['./src/renderer/**/*.{js,jsx,ts,tsx,ejs}'],