create page of shared maps and playlists

This commit is contained in:
MathieuG-P
2022-11-01 18:42:15 +01:00
parent 701e91462d
commit 322faa6a92
13 changed files with 125 additions and 34 deletions
+2
View File
@@ -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>
)
}
@@ -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>
)
}