mirror of
https://github.com/Zagrios/bs-manager.git
synced 2026-07-03 14:08:25 +02:00
partial ui for mods
This commit is contained in:
Generated
+8
@@ -10771,6 +10771,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-visibility-sensor": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/react-visibility-sensor/-/react-visibility-sensor-5.1.1.tgz",
|
||||
"integrity": "sha512-cTUHqIK+zDYpeK19rzW6zF9YfT4486TIgizZW53wEZ+/GPBbK7cNS0EHyJVyHYacwFEvvHLEKfgJndbemWhB/w==",
|
||||
"requires": {
|
||||
"prop-types": "^15.7.2"
|
||||
}
|
||||
},
|
||||
"read-cache": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||
|
||||
@@ -238,6 +238,7 @@
|
||||
"react-fitty": "^1.0.1",
|
||||
"react-outside-click-handler": "^1.3.0",
|
||||
"react-router-dom": "^6.3.0",
|
||||
"react-visibility-sensor": "^5.1.1",
|
||||
"regedit": "^5.1.1",
|
||||
"rxjs": "^7.5.6",
|
||||
"tailwind-scrollbar-hide": "^1.1.7",
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { ipcMain } from "electron";
|
||||
import { BsModsManagerService } from "../services/mods/bs-mods-manager.service";
|
||||
import { UtilsService } from "../services/utils.service";
|
||||
import { BSVersion } from "shared/bs-version.interface";
|
||||
import { IpcRequest } from "shared/models/ipc";
|
||||
|
||||
ipcMain.on("get-available-mods", (event, request: IpcRequest<BSVersion>) => {
|
||||
const utils = UtilsService.getInstance();
|
||||
const modsManager = BsModsManagerService.getInstance();
|
||||
|
||||
modsManager.getAvailableMods(request.args).then(mods => {
|
||||
utils.ipcSend(request.responceChannel, {success: true, data: mods});
|
||||
}).catch(() => utils.ipcSend(request.responceChannel, {success: false}));
|
||||
});
|
||||
@@ -7,3 +7,4 @@ import './map-ipcs';
|
||||
import './supporters-ipcs';
|
||||
import './launcher-ipcs';
|
||||
import './window-manager-ipcs';
|
||||
import './bs-mods-ipcs';
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { BSVersion } from "shared/bs-version.interface";
|
||||
import { Mod } from "shared/models/mods/mod.interface";
|
||||
import { RequestService } from "./request.service";
|
||||
import { RequestService } from "../request.service";
|
||||
|
||||
export class BeatModsApiService {
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { BSVersion } from "shared/bs-version.interface";
|
||||
import { Mod } from "shared/models/mods/mod.interface";import { BeatModsApiService } from "./beat-mods-api.service";
|
||||
;
|
||||
|
||||
export class BsModsManagerService {
|
||||
|
||||
private static instance: BsModsManagerService;
|
||||
|
||||
private beatModsApi: BeatModsApiService;
|
||||
|
||||
public static getInstance(): BsModsManagerService{
|
||||
if(!BsModsManagerService.instance){ BsModsManagerService.instance = new BsModsManagerService(); }
|
||||
return BsModsManagerService.instance;
|
||||
}
|
||||
|
||||
private constructor(){
|
||||
this.beatModsApi = BeatModsApiService.getInstance();
|
||||
}
|
||||
|
||||
public getAvailableMods(version: BSVersion): Promise<Mod[]>{
|
||||
return this.beatModsApi.getVersionMods(version);
|
||||
}
|
||||
|
||||
public getInstalledMods(version: BSVersion): Promise<Mod[]>{
|
||||
throw "NOT IMPLEMENTED YET";
|
||||
}
|
||||
|
||||
public installMods(mods: Mod[], version: BSVersion){
|
||||
throw "NOT IMPLEMENTED YET";
|
||||
}
|
||||
|
||||
public uninstallMods(mods: Mod[], version: BSVersion){
|
||||
throw "NOT IMPLEMENTED YET";
|
||||
}
|
||||
|
||||
public isModsAvailable(version: BSVersion): Promise<boolean>{
|
||||
throw "NOT IMPLEMENTED YET";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -44,7 +44,7 @@ export function BsmButton({className, style, imgClassName, iconClassName, icon,
|
||||
})();
|
||||
|
||||
const renderTypeColor = (() => {
|
||||
if(typeColor === "primary"){ return null; }
|
||||
if(typeColor === "primary"){ return ""; }
|
||||
if(!typeColor){ return `bg-light-main-color-2 dark:bg-main-color-2 ${!withBar && "hover:bg-light-main-color-3 dark:hover:bg-main-color-3"}`; }
|
||||
if(typeColor === "cancel"){ return "bg-gray-500"; }
|
||||
if(typeColor === "error"){ return "bg-red-500"; }
|
||||
@@ -54,7 +54,7 @@ export function BsmButton({className, style, imgClassName, iconClassName, icon,
|
||||
|
||||
return (
|
||||
<OutsideClickHandler onOutsideClick={e => onClickOutside && onClickOutside(e)}>
|
||||
<div onClick={e => onClick && onClick(e)} className={`${className} overflow-hidden cursor-pointer group ${!withBar && (!!typeColor || !!color) && "hover:brightness-[1.15]"} ${disabled && "brightness-75 cursor-not-allowed"} ${renderTypeColor}`} style={{...style, ...((!!primaryColor || !!color) && {backgroundColor: primaryColor ?? color})}}>
|
||||
<div onClick={e => onClick && onClick(e)} className={`${className} overflow-hidden cursor-pointer group ${(!withBar && (!!typeColor || !!color)) && "hover:brightness-[1.15]"} ${disabled && "brightness-75 cursor-not-allowed"} ${renderTypeColor}`} style={{...style, ...((!!primaryColor || !!color) && {backgroundColor: primaryColor ?? color})}}>
|
||||
{ image && <BsmImage image={image} className={imgClassName}/> }
|
||||
{ icon && <BsmIcon icon={icon} className={iconClassName ?? "h-full w-full text-gray-800 dark:text-white"}/> }
|
||||
{text && (type === "submit" ? <button className="w-full h-full" style={{...(!!textColor && {color: textColor})}}>{t(text)}</button> : <span style={{...(!!textColor && {color: textColor})}} >{t(text)}</span>)}
|
||||
|
||||
@@ -19,11 +19,12 @@ import { CSSProperties, memo } from "react";
|
||||
import EditIcon from "./icons/edit-icon.component";
|
||||
import { ExportIcon } from "./icons/export-icon.component";
|
||||
import PatreonIcon from "./icons/patreon-icon.component";
|
||||
import { SearchIcon } from "./icons/search-icon.component";
|
||||
|
||||
export type BsmIconType = (
|
||||
"settings"|"trash"|"favorite"|"folder"|"bsNote"|
|
||||
"terminal"|"desktop"|"oculus"|"add"|"cross"|"task"|
|
||||
"copy"|"steam"|"edit"|"export"|"patreon"|
|
||||
"copy"|"steam"|"edit"|"export"|"patreon"|"search"|
|
||||
"fr-FR-flag"|"es-ES-flag"|"en-US-flag"|"en-EN-flag"
|
||||
);
|
||||
|
||||
@@ -50,6 +51,7 @@ export const BsmIcon = memo(({className, icon, style}: {className?: string, icon
|
||||
if(icon === "edit"){ return <EditIcon className={className} style={style}/> }
|
||||
if(icon === "export"){ return <ExportIcon className={className} style={style}/> }
|
||||
if(icon === "patreon"){ return <PatreonIcon className={className} style={style}/> }
|
||||
if(icon === "search"){ return <SearchIcon className={className} style={style}/> }
|
||||
return <TrashIcon className={className} style={style}/>
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { CSSProperties } from "react";
|
||||
|
||||
export function SearchIcon(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">
|
||||
<path fill="currentColor" d="m31.833 34.167-9.708-9.75q-1.25 1.041-2.896 1.625-1.646.583-3.521.583-4.583 0-7.77-3.208-3.188-3.209-3.188-7.709 0-4.541 3.188-7.729 3.187-3.187 7.729-3.187 4.541 0 7.729 3.187 3.187 3.188 3.187 7.729 0 1.792-.583 3.438-.583 1.646-1.625 3.021l9.792 9.75q.458.458.458 1.104 0 .646-.5 1.146-.458.458-1.146.458-.687 0-1.146-.458ZM15.667 23.458q3.25 0 5.5-2.27 2.25-2.271 2.25-5.48 0-3.25-2.25-5.5t-5.5-2.25q-3.209 0-5.479 2.25-2.271 2.25-2.271 5.5 0 3.209 2.25 5.48 2.25 2.27 5.5 2.27Z"/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
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}
|
||||
|
||||
export function LaunchModToogle({onClick, active, text, icon}: 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 pl-6 pr-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>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import { useState } from "react";
|
||||
import { BsmButton } from "renderer/components/shared/bsm-button.component";
|
||||
import { useObservable } from "renderer/hooks/use-observable.hook";
|
||||
import { BSLauncherService, LaunchMods } from "renderer/services/bs-launcher.service";
|
||||
import { ConfigurationService } from "renderer/services/configuration.service";
|
||||
import { BSVersion } from "shared/bs-version.interface"
|
||||
import { LaunchModToogle } from "./launch-mod-toogle.component";
|
||||
|
||||
type props = {version: BSVersion};
|
||||
|
||||
export function LaunchSlide({version}: props) {
|
||||
|
||||
const configService = ConfigurationService.getInstance();
|
||||
const bsLauncherService = BSLauncherService.getInstance();
|
||||
|
||||
const [oculusMode, setOculusMode] = useState(!!configService.get<boolean>(LaunchMods.OCULUS_MOD));
|
||||
const [desktopMode, setDesktopMode] = useState(!!configService.get<boolean>(LaunchMods.DESKTOP_MOD));
|
||||
const [debugMode, setDebugMode] = useState(!!configService.get<boolean>(LaunchMods.DEBUG_MOD));
|
||||
|
||||
const launchState = useObservable(bsLauncherService.launchState$);
|
||||
|
||||
const setMode = (mode: LaunchMods, value: boolean) => {
|
||||
if(mode === LaunchMods.DEBUG_MOD){ setDebugMode(value); }
|
||||
else if(mode === LaunchMods.OCULUS_MOD){
|
||||
setOculusMode(value);
|
||||
setDesktopMode(false);
|
||||
configService.set(LaunchMods.DESKTOP_MOD, false);
|
||||
}
|
||||
else if(mode === LaunchMods.DESKTOP_MOD){
|
||||
setDesktopMode(value);
|
||||
setOculusMode(false);
|
||||
configService.set(LaunchMods.OCULUS_MOD, false);
|
||||
}
|
||||
configService.set(mode, value);
|
||||
}
|
||||
|
||||
const launch = () => bsLauncherService.launch(version, oculusMode, desktopMode, debugMode)
|
||||
|
||||
return (
|
||||
<div className="w-full shrink-0 items-center relative flex flex-col justify-start">
|
||||
<div className='grid grid-flow-col grid-cols-3 gap-6'>
|
||||
<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"/>
|
||||
</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"/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Mod } from "shared/models/mods/mod.interface"
|
||||
|
||||
type Props = {mod: Mod, installed: Mod}
|
||||
|
||||
export function ModItem({mod, installed}: Props) {
|
||||
return (
|
||||
<>
|
||||
<div className="h-full aspect-square flex items-center justify-center p-2 rounded-l-md bg-main-color-1 ml-3">
|
||||
<span className="w-full h-full border-white border-2 rounded-md"></span>
|
||||
</div>
|
||||
<span className="bg-main-color-1 py-2 pl-3 font-bold text-sm whitespace-nowrap">{mod.name}</span>
|
||||
<span className="min-w-0 text-center bg-main-color-1 py-2 px-1 text-sm">{installed?.version || "-"}</span>
|
||||
<span className="min-w-0 text-center bg-main-color-1 py-2 px-1 text-sm">{mod.version}</span>
|
||||
<span title={mod.description} className="px-3 bg-main-color-1 whitespace-nowrap text-ellipsis overflow-hidden py-2 text-sm rounded-r-md mr-3">{mod.description}</span>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import { motion } from "framer-motion";
|
||||
import { useRef, useState } from "react";
|
||||
import { BsmButton } from "renderer/components/shared/bsm-button.component";
|
||||
import { Mod } from "shared/models/mods/mod.interface"
|
||||
import { ModItem } from "./mod-item.component"
|
||||
|
||||
type Props = {modsMap: Map<string, Mod[]>, installed: Map<string, Mod[]>}
|
||||
|
||||
export function ModsGrid({modsMap, installed}: Props) {
|
||||
|
||||
const [filter, setFilter] = useState("");
|
||||
const [filterEnabled, setFilterEnabled] = useState(false);
|
||||
|
||||
const inputRef = useRef();
|
||||
|
||||
const installedMod = (key: string, mod: Mod): Mod => {
|
||||
if(!installed || !installed.get(key)){ return; }
|
||||
return installed.get(key).find(m => m.name === mod.name);
|
||||
}
|
||||
|
||||
const handleInput = (val: string) => setFilter(val.toLowerCase());
|
||||
const handleToogleFilter = () => {
|
||||
setFilter("");
|
||||
setFilterEnabled(b => !b);
|
||||
}
|
||||
|
||||
return modsMap && (
|
||||
<>
|
||||
<div className="grid gap-y-1 grid-cols-[40px_min-content_min-content_min-content_1fr]">
|
||||
<span className="sticky flex items-center justify-center top-0 bg-main-color-2 border-b-2 border-main-color-1">
|
||||
<div className="pl-4">
|
||||
<BsmButton className="rounded-full h-6 p-[2px]" withBar={false} icon="search" onClick={handleToogleFilter}/>
|
||||
</div>
|
||||
</span>
|
||||
<div className="sticky top-0 flex items-center bg-main-color-2 border-main-color-1 border-b-2 h-8 w-52 px-1">
|
||||
{(filterEnabled ? (
|
||||
<motion.input autoFocus ref={inputRef} className="bg-main-color-1 rounded-md h-6 px-2" initial={{width: 0}} animate={{width: "100%"}} onChange={e => handleInput(e.target.value)}/>
|
||||
):(
|
||||
<span className="w-full text-center">Nom</span>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<span className="sticky flex items-center justify-center text-sm top-0 bg-main-color-2 border-b-2 border-main-color-1 h-8 px-2">Installé</span>
|
||||
<span className="sticky flex items-center justify-center text-sm top-0 bg-main-color-2 border-b-2 border-main-color-1 h-8 px-2">Dernière</span>
|
||||
<span className="sticky flex items-center justify-center text-sm top-0 bg-main-color-2 border-b-2 border-main-color-1 h-8">Description</span>
|
||||
|
||||
{
|
||||
Array.from(modsMap.keys()).map(key => (
|
||||
<>
|
||||
<span className="col-span-full py-1 font-bold pl-3" key={key}>{key}</span>
|
||||
{modsMap.get(key).map(mod => mod.name.toLowerCase().includes(filter) && <ModItem key={mod.name} mod={mod} installed={installedMod(key, mod)}/>)}
|
||||
</>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { BsModsManagerService } from "renderer/services/bs-mods-manager.service";
|
||||
import { BSVersion } from "shared/bs-version.interface";
|
||||
import VisibilitySensor from 'react-visibility-sensor';
|
||||
import { Mod } from "shared/models/mods/mod.interface";
|
||||
import { ModItem } from "./mod-item.component";
|
||||
import { ModsGrid } from "./mods-grid.component";
|
||||
|
||||
export function ModsSlide({version}: {version: BSVersion}) {
|
||||
|
||||
const modsManager = BsModsManagerService.getInstance();
|
||||
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const [modsAvailable, setModsAvailable] = useState(null as Map<string, Mod[]>);
|
||||
const [modsInstalled, setModsInstalled] = useState(null as Map<string, Mod[]>);
|
||||
|
||||
const modsToCategoryMap = (mods: Mod[]): Map<string, Mod[]> => {
|
||||
if(!mods){ return new Map<string, Mod[]>(); }
|
||||
const map = new Map<string, Mod[]>();
|
||||
mods.forEach(mod => map.set(mod.category, [...map.get(mod.category) ?? [], mod]));
|
||||
return map;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if(!isVisible){ return; }
|
||||
|
||||
if(!modsAvailable){
|
||||
console.log("OUI");
|
||||
modsManager.getAvailableMods(version).then(mods => setModsAvailable(modsToCategoryMap(mods)));
|
||||
modsManager.getInstalledMods(version).then(mods => setModsInstalled(modsToCategoryMap(mods)));
|
||||
}
|
||||
}, [isVisible]);
|
||||
|
||||
|
||||
return (
|
||||
<VisibilitySensor onChange={setIsVisible}>
|
||||
<div className='shrink-0 w-full h-full px-8 pb-8 flex justify-center'>
|
||||
<div className='flex flex-col grow-0 bg-light-main-color-2 dark:bg-main-color-2 h-full w-full rounded-md shadow-black shadow-center overflow-hidden'>
|
||||
<div className="overflow-scroll w-full shrink min-h-0 scrollbar-thin scrollbar-thumb-neutral-900 scrollbar-thumb-rounded-full">
|
||||
<ModsGrid modsMap={modsAvailable} installed={modsInstalled}/>
|
||||
</div>
|
||||
<div className=" h-10 shrink-0">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</VisibilitySensor>
|
||||
|
||||
)
|
||||
}
|
||||
@@ -1,100 +1,56 @@
|
||||
import { BSVersion } from 'shared/bs-version.interface';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import BSLogo from '../../../assets/images/apngs/bs-logo.png';
|
||||
import { BSLauncherService, LaunchMods } from '../services/bs-launcher.service';
|
||||
import { TabNavBar } from 'renderer/components/shared/tab-nav-bar.component';
|
||||
import wipGif from "../../../assets/images/gifs/wip.gif"
|
||||
import { ConfigurationService } from 'renderer/services/configuration.service';
|
||||
import { BsmDropdownButton } from 'renderer/components/shared/bsm-dropdown-button.component';
|
||||
import { BsmIcon, BsmIconType } from 'renderer/components/svgs/bsm-icon.component';
|
||||
import { BsmImage } from 'renderer/components/shared/bsm-image.component';
|
||||
import { BsmButton } from 'renderer/components/shared/bsm-button.component';
|
||||
import { BSUninstallerService } from '../services/bs-uninstaller.service';
|
||||
import { BSVersionManagerService } from '../services/bs-version-manager.service';
|
||||
import { ModalExitCode, ModalService, ModalType } from '../services/modale.service';
|
||||
import DefautVersionImage from "../../../assets/images/default-version-img.jpg";
|
||||
import { BsDownloaderService } from 'renderer/services/bs-downloader.service';
|
||||
import { useTranslation } from 'renderer/hooks/use-translation.hook';
|
||||
import { IpcService } from 'renderer/services/ipc.service';
|
||||
import { useObservable } from 'renderer/hooks/use-observable.hook';
|
||||
import { MapService } from 'renderer/services/maps.service';
|
||||
import { LaunchSlide } from 'renderer/components/version-viewer/slides/launch/launch-slide.component';
|
||||
import { ModsSlide } from 'renderer/components/version-viewer/slides/mods/mods-slide.component';
|
||||
|
||||
export function VersionViewer() {
|
||||
|
||||
const configService = ConfigurationService.getInstance();
|
||||
const bsUninstallerService = BSUninstallerService.getInstance();
|
||||
const bsVersionManagerService = BSVersionManagerService.getInstance();
|
||||
const modalService = ModalService.getInsance();
|
||||
const bsDownloaderService = BsDownloaderService.getInstance();
|
||||
const ipcService = IpcService.getInstance();
|
||||
const mapService = MapService.getInstance();
|
||||
const bsUninstallerService = BSUninstallerService.getInstance();
|
||||
const bsVersionManagerService = BSVersionManagerService.getInstance();
|
||||
const modalService = ModalService.getInsance();
|
||||
const bsDownloaderService = BsDownloaderService.getInstance();
|
||||
const ipcService = IpcService.getInstance();
|
||||
const mapService = MapService.getInstance();
|
||||
|
||||
const {state} = useLocation() as {state: BSVersion};
|
||||
const navigate = useNavigate();
|
||||
const bsLauncherService = BSLauncherService.getInstance();
|
||||
const {state} = useLocation() as {state: BSVersion};
|
||||
const navigate = useNavigate();
|
||||
const [currentTabIndex, setCurrentTabIndex] = useState(0);
|
||||
|
||||
const [oculusMode, setOculusMode] = useState(false);
|
||||
const [desktopMode, setDesktopMode] = useState(false);
|
||||
const [debugMode, setDebugMode] = useState(false);
|
||||
const [currentTabIndex, setCurrentTabIndex] = useState(0);
|
||||
const launchState = useObservable(bsLauncherService.launchState$);
|
||||
const navigateToVersion = (version: BSVersion) => navigate(`/bs-version/${version.BSVersion}`, {state: version});
|
||||
const openFolder = () => ipcService.sendLazy("bs-version.open-folder", {args: state});
|
||||
const exportMaps = () => mapService.exportVersionMaps(state);
|
||||
const verifyFiles = () => bsDownloaderService.download(state, true);
|
||||
|
||||
useEffect(() => {
|
||||
setOculusMode(!!configService.get<boolean>(LaunchMods.OCULUS_MOD));
|
||||
setDesktopMode(!!configService.get<boolean>(LaunchMods.DESKTOP_MOD));
|
||||
setDebugMode(!!configService.get<boolean>(LaunchMods.DEBUG_MOD));
|
||||
}, []);
|
||||
|
||||
const navigateToVersion = (version: BSVersion) => {
|
||||
navigate(`/bs-version/${version.BSVersion}`, {state: version});
|
||||
}
|
||||
|
||||
const setMode = (mode: LaunchMods, value: boolean) => {
|
||||
if(mode === LaunchMods.DEBUG_MOD){ setDebugMode(value); }
|
||||
else if(mode === LaunchMods.OCULUS_MOD){
|
||||
setOculusMode(value);
|
||||
setDesktopMode(false);
|
||||
configService.set(LaunchMods.DESKTOP_MOD, false);
|
||||
const uninstall = async () => {
|
||||
const modalCompleted = await modalService.openModal(ModalType.UNINSTALL, state)
|
||||
if(modalCompleted.exitCode === ModalExitCode.COMPLETED){
|
||||
bsUninstallerService.uninstall(state).then(() => {
|
||||
bsVersionManagerService.askInstalledVersions();
|
||||
const newVersionPage = bsVersionManagerService.getInstalledVersions()[0];
|
||||
navigateToVersion(newVersionPage);
|
||||
})
|
||||
}
|
||||
}
|
||||
else if(mode === LaunchMods.DESKTOP_MOD){
|
||||
setDesktopMode(value);
|
||||
setOculusMode(false);
|
||||
configService.set(LaunchMods.OCULUS_MOD, false);
|
||||
|
||||
const edit = () => {
|
||||
bsVersionManagerService.editVersion(state).then(newVersion => {
|
||||
if(!newVersion){ return; }
|
||||
navigateToVersion(newVersion);
|
||||
});
|
||||
}
|
||||
configService.set(mode, value);
|
||||
}
|
||||
|
||||
const openFolder = () => {
|
||||
ipcService.sendLazy("bs-version.open-folder", {args: state});
|
||||
}
|
||||
|
||||
const uninstall = async () => {
|
||||
const modalCompleted = await modalService.openModal(ModalType.UNINSTALL, state)
|
||||
if(modalCompleted.exitCode === ModalExitCode.COMPLETED){
|
||||
bsUninstallerService.uninstall(state)
|
||||
.then(() => {
|
||||
bsVersionManagerService.askInstalledVersions();
|
||||
const newVersionPage = bsVersionManagerService.getInstalledVersions()[0];
|
||||
navigateToVersion(newVersionPage);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const verifyFiles = () => {
|
||||
bsDownloaderService.download(state, true);
|
||||
}
|
||||
|
||||
const launchBs = () => {
|
||||
bsLauncherService.launch(state, oculusMode, desktopMode, debugMode);
|
||||
}
|
||||
|
||||
const edit = () => {
|
||||
bsVersionManagerService.editVersion(state).then(newVersion => {
|
||||
if(!newVersion){ return; }
|
||||
navigateToVersion(newVersion);
|
||||
});
|
||||
}
|
||||
|
||||
const clone = () => {
|
||||
bsVersionManagerService.cloneVersion(state).then(newVersion => {
|
||||
@@ -103,9 +59,6 @@ export function VersionViewer() {
|
||||
});
|
||||
}
|
||||
|
||||
const exportMaps = () => {
|
||||
mapService.exportVersionMaps(state);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -114,29 +67,15 @@ export function VersionViewer() {
|
||||
<BsmImage className='relative object-cover h-28' image={BSLogo}/>
|
||||
<h1 className='relative text-4xl font-bold italic -top-3'>{state.name ? `${state.BSVersion} - ${state.name}` : state.BSVersion}</h1>
|
||||
<TabNavBar className='mt-3' tabsText={["misc.launch", "misc.maps", "misc.mods"]} onTabChange={(i : number) => setCurrentTabIndex(i)}/>
|
||||
<div className='mt-2 w-full grow flex transition-transform duration-300 pt-5' style={{transform: `translate(${-(currentTabIndex * 100)}%, 0)`}}>
|
||||
<div className='w-full shrink-0 items-center relative flex flex-col justify-start -top-2'>
|
||||
<div className='grid grid-flow-col grid-cols-3 gap-6'>
|
||||
<ToogleLunchMod icon='oculus' onClick={() => setMode(LaunchMods.OCULUS_MOD, !oculusMode)} active={oculusMode} text="pages.version-viewer.launch-mods.oculus"/>
|
||||
<ToogleLunchMod icon='desktop' onClick={() => setMode(LaunchMods.DESKTOP_MOD, !desktopMode)} active={desktopMode} text="pages.version-viewer.launch-mods.desktop"/>
|
||||
<ToogleLunchMod 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={launchBs} active={JSON.stringify(state) === 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"/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='shrink-0 w-full h-full flex justify-center'>
|
||||
<div className='p-4 bg-light-main-color-2 dark:bg-main-color-2 h-fit rounded-md'>
|
||||
<img src={wipGif} alt="" />
|
||||
<span className='block w-full text-center font-bold mt-2 text-gray-800 dark:text-white'>Work In Progress</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className='mt-2 w-full min-h-0 grow flex transition-transform duration-300 pt-3' style={{transform: `translate(${-(currentTabIndex * 100)}%, 0)`}}>
|
||||
<LaunchSlide version={state}/>
|
||||
<div className='shrink-0 w-full h-full flex justify-center'>
|
||||
<div className='p-4 bg-light-main-color-2 dark:bg-main-color-2 h-fit rounded-md'>
|
||||
<img src={wipGif} alt="" />
|
||||
<span className='block w-full text-center font-bold mt-2 text-gray-800 dark:text-white'>Work In Progress</span>
|
||||
</div>
|
||||
</div>
|
||||
<ModsSlide version={state}/>
|
||||
</div>
|
||||
</div>
|
||||
<BsmDropdownButton className='absolute top-5 right-5 h-9 w-9' items={[
|
||||
@@ -149,19 +88,4 @@ export function VersionViewer() {
|
||||
]}/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function ToogleLunchMod(props: {onClick: () => void, active: boolean, text: string, icon: BsmIconType}) {
|
||||
|
||||
const t = useTranslation();
|
||||
|
||||
return (
|
||||
<div className={`relative rounded-full cursor-pointer group active:scale-95 transition-transform ${!props.active && "shadow-md shadow-black"}`} onClick={() => props.onClick()}>
|
||||
<div className={`absolute glow-on-hover rounded-full ${props.active && "opacity-100 blur-[2px]"}`}/>
|
||||
<div className='w-full h-full pl-6 pr-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={props.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(props.text)}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import { BSVersion } from "shared/bs-version.interface";
|
||||
import { Mod } from "shared/models/mods/mod.interface";
|
||||
import { IpcService } from "./ipc.service";
|
||||
|
||||
export class BsModsManagerService {
|
||||
|
||||
private static instance: BsModsManagerService;
|
||||
|
||||
private readonly ipcService: IpcService;
|
||||
|
||||
public static getInstance(): BsModsManagerService{
|
||||
if(!BsModsManagerService.instance){ BsModsManagerService.instance = new BsModsManagerService(); }
|
||||
return BsModsManagerService.instance;
|
||||
}
|
||||
|
||||
private constructor(){
|
||||
this.ipcService = IpcService.getInstance();
|
||||
}
|
||||
|
||||
public getAvailableMods(version: BSVersion): Promise<Mod[]>{
|
||||
return this.ipcService.send<Mod[], BSVersion>("get-available-mods", {args: version}).then(res => res.data);
|
||||
}
|
||||
|
||||
public async getInstalledMods(version: BSVersion): Promise<Mod[]>{
|
||||
return [];
|
||||
}
|
||||
|
||||
public installMods(mods: Mod[], version: BSVersion){
|
||||
throw "NOT IMPLEMENTED YET";
|
||||
}
|
||||
|
||||
public uninstallMods(mods: Mod[], version: BSVersion){
|
||||
throw "NOT IMPLEMENTED YET";
|
||||
}
|
||||
|
||||
public isModsAvailable(version: BSVersion): Promise<boolean>{
|
||||
throw "NOT IMPLEMENTED YET";
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user