mirror of
https://github.com/Zagrios/bs-manager.git
synced 2026-07-03 14:08:25 +02:00
create page of shared maps and playlists
This commit is contained in:
@@ -10,6 +10,7 @@ import { useEffect } from "react";
|
||||
import { ThemeService } from "./services/theme.service";
|
||||
import { NotificationOverlay } from "./components/notification/notification-overlay.component";
|
||||
import { PageStateService } from "./services/page-state.service";
|
||||
import MapsPage from "./pages/maps-page.component";
|
||||
|
||||
export default function App() {
|
||||
|
||||
@@ -38,6 +39,7 @@ export default function App() {
|
||||
<div className="bg-light-main-color-2 dark:bg-main-color-2 relative rounded-tl-lg grow overflow-hidden max-w-full">
|
||||
<Routes>
|
||||
<Route path={"/bs-version/:versionNumber"} element={<VersionViewer/>}/>
|
||||
<Route path={"/maps"} element={<MapsPage/>}/>
|
||||
<Route path={"/settings"} element={<SettingsPage/>}/>
|
||||
<Route path="*" element={<AvailableVersionsList/>}/>
|
||||
</Routes>
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { BSVersion } from "shared/bs-version.interface"
|
||||
import { TabNavBar } from "../shared/tab-nav-bar.component"
|
||||
|
||||
type Props = {
|
||||
version?: BSVersion
|
||||
}
|
||||
|
||||
export function MapsPlaylistsPanel({version}: Props) {
|
||||
return (
|
||||
<div className="w-full h-full bg-main-color-1 rounded-md shadow-black shadow-md overflow-hidden">
|
||||
<TabNavBar className="!rounded-none shadow-none" tabsText={["Maps", "Playlists"]} onTabChange={() => {}}/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
+13
-18
@@ -5,12 +5,12 @@ import { useEffect, useState } from "react";
|
||||
import { combineLatest, Subscription } from "rxjs";
|
||||
import { BSLauncherService, LaunchMods } from "renderer/services/bs-launcher.service";
|
||||
import { ConfigurationService } from "renderer/services/configuration.service";
|
||||
import { BsmButton } from "../shared/bsm-button.component";
|
||||
import { BSUninstallerService } from "renderer/services/bs-uninstaller.service";
|
||||
import { BSVersionManagerService } from "renderer/services/bs-version-manager.service";
|
||||
import { BsmIcon } from "../svgs/bsm-icon.component";
|
||||
import { BsmIcon } from "../../svgs/bsm-icon.component";
|
||||
import { ReactFitty } from "react-fitty";
|
||||
import { useThemeColor } from 'renderer/hooks/use-theme-color.hook';
|
||||
import { NavBarItem } from './nav-bar-item.component';
|
||||
|
||||
export function BsVersionItem(props: {version: BSVersion}) {
|
||||
|
||||
@@ -24,7 +24,7 @@ export function BsVersionItem(props: {version: BSVersion}) {
|
||||
|
||||
const [downloading, setDownloading] = useState(false);
|
||||
const [downloadPercent, setDownloadPercent] = useState(0);
|
||||
const {firstColor, secondColor} = useThemeColor();
|
||||
const secondColor = useThemeColor("second-color");
|
||||
|
||||
const isActive = (): boolean => {
|
||||
return props.version?.BSVersion === state?.BSVersion && props?.version.steam === state?.steam && props?.version.oculus === state?.oculus && props?.version.name === state?.name;
|
||||
@@ -77,19 +77,14 @@ export function BsVersionItem(props: {version: BSVersion}) {
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<li className={`outline-none relative p-[1px] overflow-hidden rounded-xl flex justify-center items-center mb-1 ${downloading && "nav-item-download"} active:translate-y-[1px]`}>
|
||||
{downloading && <div className="download-progress absolute top-0 w-full h-full" style={{transform: `translate(${-(100 - downloadPercent)}%, 0)`, background: `linear-gradient(90deg, ${firstColor}, ${secondColor}, ${firstColor}, ${secondColor})`}}/>}
|
||||
<div className={`wrapper z-[1] px-1 py-[3px] w-full rounded-xl ${downloading && 'bg-white dark:bg-black'} ${!downloading && "hover:bg-light-main-color-3 dark:hover:bg-main-color-3"} ${(isActive() && !downloading) && "bg-light-main-color-3 dark:bg-main-color-3"}`}>
|
||||
<Link onDoubleClick={handleDoubleClick} to={`/bs-version/${props.version.BSVersion}`} state={props.version} title={props.version.name && `${props.version.BSVersion} - ${props.version.name}`} className="w-full flex items-center justify-start content-center max-w-full">
|
||||
{renderIcon()}
|
||||
<div className="overflow-hidden whitespace-nowrap text-xl dark:text-gray-200 text-gray-800 font-bold tracking-wide">
|
||||
<ReactFitty maxSize={19} minSize={9} className='align-middle pb-[2px] max-w-full overflow-hidden text-ellipsis'>{props.version.name || props.version.BSVersion}</ReactFitty>
|
||||
</div>
|
||||
</Link>
|
||||
{downloading && <BsmButton onClick={cancel} className="my-1 text-xs text-white rounded-md text-center" withBar={false} text="misc.cancel" typeColor="error"/>}
|
||||
</div>
|
||||
</li>
|
||||
|
||||
)
|
||||
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} title={props.version.name && `${props.version.BSVersion} - ${props.version.name}`} className="w-full flex items-center justify-start content-center max-w-full">
|
||||
{renderIcon()}
|
||||
<div className="overflow-hidden whitespace-nowrap text-xl dark:text-gray-200 text-gray-800 font-bold tracking-wide">
|
||||
<ReactFitty maxSize={19} minSize={9} className='align-middle pb-[2px] max-w-full overflow-hidden text-ellipsis'>{props.version.name || props.version.BSVersion}</ReactFitty>
|
||||
</div>
|
||||
</Link>
|
||||
</NavBarItem>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Link } from "react-router-dom";
|
||||
import { BsmIcon } from "renderer/components/svgs/bsm-icon.component";
|
||||
import { useThemeColor } from "renderer/hooks/use-theme-color.hook";
|
||||
import { NavBarItem } from "./nav-bar-item.component";
|
||||
|
||||
export function MapsNavBarItem() {
|
||||
|
||||
const color = useThemeColor("first-color");
|
||||
|
||||
return (
|
||||
<NavBarItem>
|
||||
<Link to={"maps"} className="w-full flex items-center justify-start content-center max-w-full h-[30px]">
|
||||
<BsmIcon className="w-[19px] h-[19px] mr-[5px] shrink-0 brightness-125" icon="bsMapDifficulty" style={{color}}/>
|
||||
<span className="dark:text-gray-200 text-gray-800 font-bold tracking-wide">Maps</span>
|
||||
</Link>
|
||||
</NavBarItem>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { useThemeColor } from "renderer/hooks/use-theme-color.hook";
|
||||
import { BsmButton } from "../../shared/bsm-button.component";
|
||||
|
||||
type Props = {
|
||||
children: JSX.Element,
|
||||
isDownloading?: boolean
|
||||
progress?: number,
|
||||
isActive?: boolean,
|
||||
onCancel?: (e: React.MouseEvent) => void
|
||||
}
|
||||
|
||||
export function NavBarItem({progress, isDownloading, children, isActive, onCancel}: Props) {
|
||||
|
||||
const {firstColor, secondColor} = useThemeColor();
|
||||
|
||||
return (
|
||||
<li className={`outline-none relative p-[1px] overflow-hidden rounded-xl flex justify-center items-center mb-1 ${isDownloading && "nav-item-download"} active:translate-y-[1px]`}>
|
||||
{isDownloading && <div className="download-progress absolute top-0 w-full h-full" style={{transform: `translate(${-(100 - progress)}%, 0)`, background: `linear-gradient(90deg, ${firstColor}, ${secondColor}, ${firstColor}, ${secondColor})`}}/>}
|
||||
<div className={`wrapper z-[1] px-1 py-[3px] w-full rounded-xl ${isDownloading && 'bg-white dark:bg-black'} ${!isDownloading && "hover:bg-light-main-color-3 dark:hover:bg-main-color-3"} ${(isActive && !isDownloading) && "bg-light-main-color-3 dark:bg-main-color-3"}`}>
|
||||
{children}
|
||||
{isDownloading && <BsmButton onClick={onCancel} className="my-1 text-xs text-white rounded-md text-center" withBar={false} text="misc.cancel" typeColor="error"/>}
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export function NavBarSpliter() {
|
||||
return (
|
||||
<span className="block w-[calc(100%-25px)] mx-auto my-2 h-1 rounded-full bg-main-color-3"/>
|
||||
)
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
import './nav-bar.component.css'
|
||||
import { BsVersionItem } from './bs-version-item.component';
|
||||
import { BsVersionItem } from './nav-bar-items/bs-version-item.component';
|
||||
import { BSVersionManagerService } from '../../services/bs-version-manager.service';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { BsmIcon } from '../svgs/bsm-icon.component';
|
||||
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';
|
||||
|
||||
export function NavBar() {
|
||||
|
||||
@@ -16,6 +18,8 @@ export function NavBar() {
|
||||
<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'>
|
||||
<BsManagerIcon className='relative aspect-square w-16 h-16 mb-3'/>
|
||||
<ol id='versions' className='w-fit max-w-[120px] relative left-[2px] grow overflow-y-hidden scrollbar-track-transparent scrollbar-thin scrollbar-thumb-rounded-full scrollbar-thumb-neutral-900 hover:overflow-y-scroll'>
|
||||
<MapsNavBarItem/>
|
||||
<NavBarSpliter/>
|
||||
{installedVersions && installedVersions.map((version) => <BsVersionItem key={JSON.stringify(version)} version={version}/>)}
|
||||
</ol>
|
||||
<div className='w-full p-2 flex flex-col items-center content-center justify-start'>
|
||||
|
||||
@@ -15,14 +15,14 @@ export function TabNavBar(props: {tabsText: string[], onTabChange: Function, cla
|
||||
}
|
||||
|
||||
return (
|
||||
<nav className={`relative h-8 shrink-0 cursor-pointer rounded-md overflow-hidden shadow-md shadow-black ${props.className}`}>
|
||||
<nav className={`relative h-8 shrink-0 cursor-pointer rounded-md overflow-hidden shadow-md shadow-black bg-light-main-color-2 dark:bg-main-color-2 ${props.className}`}>
|
||||
<div className="absolute w-full h-1 bottom-0" style={{color: secondColor}}>
|
||||
<span className="absolute h-full w-full bg-current brightness-50" />
|
||||
<span className="absolute h-full block bg-current transition-transform duration-300 shadow-center shadow-current" style={{transform: `translate(${currentTabIndex * 100}%, 0)`, width: `calc(100% / ${props.tabsText.length})`}}/>
|
||||
</div>
|
||||
<ul className="grid" style={{gridTemplateColumns: `repeat(${props.tabsText.length}, minmax(0, 1fr))`}}>
|
||||
{ props.tabsText.map((y, index) =>
|
||||
<li className="px-4 h-full text-center bg-light-main-color-2 text-gray-800 dark:bg-main-color-2 dark:text-gray-200 text-lg font-bold hover:bg-light-main-color-1 dark:hover:bg-main-color-1" key={index} onClick={() => selectYear(y)}>{t(y)}</li>
|
||||
<li className="px-4 h-full text-center text-gray-800 dark:text-gray-200 text-lg font-bold hover:bg-light-main-color-1 dark:hover:bg-main-color-1" key={index} onClick={() => selectYear(y)}>{t(y)}</li>
|
||||
)}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
@@ -1,11 +1,22 @@
|
||||
import { BsmImage } from '../shared/bsm-image.component';
|
||||
import './slideshow.component.css';
|
||||
|
||||
export function Slideshow(props: {className: string, images: string[]}) {
|
||||
export function Slideshow(props: {className: string}) {
|
||||
|
||||
const slideshowImages = [
|
||||
require('../../../../assets/images/slideshow-images/image-1-blur.jpg'),
|
||||
require('../../../../assets/images/slideshow-images/image-2-blur.jpg'),
|
||||
require('../../../../assets/images/slideshow-images/image-3-blur.jpg'),
|
||||
require('../../../../assets/images/slideshow-images/image-4-blur.jpg'),
|
||||
require('../../../../assets/images/slideshow-images/image-5-blur.png'),
|
||||
require('../../../../assets/images/slideshow-images/image-6-blur.png'),
|
||||
require('../../../../assets/images/slideshow-images/image-7-blur.png'),
|
||||
];
|
||||
|
||||
return (
|
||||
<div className={`slide ${props.className}`}>
|
||||
{
|
||||
props.images.map((i, index) => <BsmImage key={i} className='w-full h-full object-cover' image={i} style={{animationDelay: `${index * 10}s`}}/>)
|
||||
slideshowImages.map((i, index) => <BsmImage key={i} className='w-full h-full object-cover' image={i} style={{animationDelay: `${index * 10}s`}}/>)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -24,11 +24,12 @@ import { CheckIcon } from "./icons/check-icon.component";
|
||||
import { ThreeDotsIcon } from "./icons/three-dots-icon.component";
|
||||
import { GitHubIcon } from "./icons/github-icon.component";
|
||||
import { CloseIcon } from "./icons/close-icon.component";
|
||||
import { BsMapDifficultyIcon } from "./icons/bs-map-difficulty-icon.component";
|
||||
|
||||
export type BsmIconType = (
|
||||
"settings"|"trash"|"favorite"|"folder"|"bsNote"|"check"|"three-dots"|
|
||||
"terminal"|"desktop"|"oculus"|"add"|"cross"|"task"|"github"|"close"|
|
||||
"copy"|"steam"|"edit"|"export"|"patreon"|"search"|
|
||||
"copy"|"steam"|"edit"|"export"|"patreon"|"search"|"bsMapDifficulty"|
|
||||
"fr-FR-flag"|"es-ES-flag"|"en-US-flag"|"en-EN-flag"
|
||||
);
|
||||
|
||||
@@ -60,6 +61,7 @@ export const BsmIcon = memo(({className, icon, style}: {className?: string, icon
|
||||
if(icon === "three-dots"){ return <ThreeDotsIcon className={className} style={style}/> }
|
||||
if(icon === "github"){ return <GitHubIcon className={className} style={style}/> }
|
||||
if(icon === "close"){ return <CloseIcon className={className} style={style}/> }
|
||||
if(icon === "bsMapDifficulty"){ return <BsMapDifficultyIcon className={className} style={style}/> }
|
||||
return <TrashIcon className={className} style={style}/>
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { CSSProperties } from "react";
|
||||
|
||||
export function BsMapDifficultyIcon(props: {className?: string, style?: CSSProperties}) {
|
||||
|
||||
return (
|
||||
<svg className={props.className} style={props.style} xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" viewBox="0 0 1280 1280" height="1280" width="1280">
|
||||
<path fill="currentColor" d="M1014,1160c39,5,63-3,75.87-31.69l0,0c13.53-32.46-1.82-69.75-34.28-83.27L708.07,900.22l71.48-37.92 l321.62,145.05c32.72,14.76,71.2,0.2,85.96-32.52v0c7.38-16.36,8.52-35.88,3.74-53.15c-0.68-2.47-1.5-4.8-2.41-7.02l8.48-529.63 c0.33-20.64-16.13-37.64-36.77-37.97l0,0c-20.64-0.33-37.64,16.13-37.97,36.77l-7.66,478.4C974.56,800.83,832.1,745.34,692.3,683.52 c5.01,0.61,10.11,0.93,15.28,0.93c69.05,0,125.03-55.98,125.03-125.03S776.63,434.4,707.58,434.4s-125.03,55.98-125.03,125.03 c0,37.23,16.28,70.65,42.1,93.56c-142.4-65.53-282.26-137.13-424.6-202.8L409.6,121.57c11.1-17.41,5.99-40.51-11.42-51.61l0,0 c-17.41-11.1-40.51-5.99-51.61,11.42L126.42,426.63c-25.08,3.05-49.73,19.87-60.04,42.73l0,0c-14.76,32.72-0.2,71.2,32.52,85.96 l313.11,141.22l-68.3,175.18l-88.76,86.09c-20.92,20.29-25.68,50.93-14.09,75.96c3.82,8.45,9.46,16.24,16.9,22.74l115.03,100.42 c27.77,24.24,69.93,21.38,94.17-6.38h0c24.24-27.77,21.38-69.93-6.38-94.17l-60.8-53.08l19.39-18.81h130.07l0.02-0.01L1014,1160z"/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -11,15 +11,6 @@ import { useObservable } from "renderer/hooks/use-observable.hook";
|
||||
|
||||
export function AvailableVersionsList() {
|
||||
|
||||
const slideshowImages = [
|
||||
require('../../../assets/images/slideshow-images/image-1-blur.jpg'),
|
||||
require('../../../assets/images/slideshow-images/image-2-blur.jpg'),
|
||||
require('../../../assets/images/slideshow-images/image-3-blur.jpg'),
|
||||
require('../../../assets/images/slideshow-images/image-4-blur.jpg'),
|
||||
require('../../../assets/images/slideshow-images/image-5-blur.png'),
|
||||
require('../../../assets/images/slideshow-images/image-6-blur.png'),
|
||||
require('../../../assets/images/slideshow-images/image-7-blur.png'),
|
||||
];
|
||||
const bsDownloaderService: BsDownloaderService = BsDownloaderService.getInstance();
|
||||
const versionManagerService: BSVersionManagerService = BSVersionManagerService.getInstance();
|
||||
|
||||
@@ -44,7 +35,7 @@ export function AvailableVersionsList() {
|
||||
|
||||
return (
|
||||
<div className="relative h-full w-full flex items-center flex-col pt-2">
|
||||
<Slideshow className="absolute w-full h-full top-0" images={slideshowImages}></Slideshow>
|
||||
<Slideshow className="absolute w-full h-full top-0"></Slideshow>
|
||||
<h1 className="text-gray-100 text-2xl mb-4 z-[1]">{t("pages.available-versions.title")}</h1>
|
||||
<AvailableVersionsSlider/>
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { MapsPlaylistsPanel } from "renderer/components/maps-mangement-components/maps-playlists-panel.component";
|
||||
import { Slideshow } from "renderer/components/slideshow/slideshow.component";
|
||||
|
||||
export default function MapsPage() {
|
||||
return (
|
||||
<div className="w-full h-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]">Maps partagées</h1>
|
||||
<div className="z-[1] w-full grow p-10">
|
||||
<MapsPlaylistsPanel/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user