add linked unlinked maps indication

This commit is contained in:
MathieuG-P
2022-11-24 01:18:08 +01:00
parent 46f5f9486c
commit 8372d3bed9
9 changed files with 128 additions and 26 deletions
+9
View File
@@ -15,4 +15,13 @@ ipcMain.on('get-version-maps', (event, request: IpcRequest<BSVersion>) => {
utilsService.ipcSend(request.responceChannel, {success: false});
})
});
ipcMain.on("verion-have-maps-linked", async (event, request: IpcRequest<BSVersion>) => {
const utils = UtilsService.getInstance();
const maps = LocalMapsManagerService.getInstance();
console.log(request.args);
utils.ipcSend<boolean>(request.responceChannel, {success: true, data: await maps.versionIsLinked(request.args)});
});
@@ -6,6 +6,7 @@ import { BSLocalVersionService } from "../bs-local-version.service";
import { InstallationLocationService } from "../installation-location.service";
import { UtilsService } from "../utils.service";
import crypto from "crypto";
import { lstat, lstatSync, symlinkSync } from "fs";
export class LocalMapsManagerService {
@@ -29,15 +30,14 @@ export class LocalMapsManagerService {
this.utils = UtilsService.getInstance();
}
private async getMapsPath(version?: BSVersion): Promise<string>{
if(version){ return path.join(await this.localVersion.getVersionPath(version), this.LEVELS_ROOT_FOLDER); }
return this.installLocation.sharedMapsPath;
private async getMapsFolderPath(version?: BSVersion): Promise<string>{
if(version){ return path.join(await this.localVersion.getVersionPath(version), this.LEVELS_ROOT_FOLDER, this.CUSTOM_LEVELS_FOLDER); }
return path.join(this.installLocation.sharedMapsPath, this.CUSTOM_LEVELS_FOLDER);
}
private async computeMapHash(mapPath: string, rawInfoString: string): Promise<string>{
const mapRawInfo = JSON.parse(rawInfoString);
let content = rawInfoString;
console.log("oui");
for(const set of mapRawInfo._difficultyBeatmapSets){
for(const diff of set._difficultyBeatmaps){
const diffFilePath = path.join(mapPath, diff._beatmapFilename);
@@ -69,13 +69,10 @@ export class LocalMapsManagerService {
}
public async getMaps(version?: BSVersion): Promise<BsmLocalMap[]>{
const levelsRoot = await this.getMapsPath(version);
const levelsFolder = path.join(levelsRoot, this.CUSTOM_LEVELS_FOLDER);
const levelsFolder = await this.getMapsFolderPath(version)
const levelsPath = (await this.utils.pathExist(levelsFolder)) ? this.utils.listDirsInDir(levelsFolder, true) : [];
const mapsInfo = await Promise.all(levelsPath.map(levelPath => this.loadMapInfoFromPath(levelPath)))
console.log(mapsInfo);
@@ -87,4 +84,28 @@ export class LocalMapsManagerService {
}
public async versionIsLinked(version: BSVersion): Promise<boolean>{
const levelsPath = await this.getMapsFolderPath(version);
console.log(levelsPath, version);
const isPathExist = await this.utils.pathExist(levelsPath);
if(!isPathExist){ return false; }
return lstatSync(levelsPath).isSymbolicLink()
}
public async linkVersionMaps(verion: BSVersion, includeVersionMaps: boolean): Promise<void>{
//TODO
//Can use fs.symlink to create symlink
}
public async unlinkVersionMaps(version: BSVersion, keepLinkedMaps: boolean): Promise<void>{
//TODO
}
}
@@ -1,10 +1,14 @@
import { useState } from "react"
import { DetailedHTMLProps, useEffect, useState } from "react"
import { BSVersion } from "shared/bs-version.interface"
import { TabNavBar } from "../shared/tab-nav-bar.component"
import { LocalMapsListPanel } from "./local-maps-list-panel.component"
import { BsmDropdownButton } from "../shared/bsm-dropdown-button.component"
import { FilterPanel } from "./filter-panel.component"
import { MapFilter, MapTag } from "shared/models/maps/beat-saver.model"
import { BsmButton } from "../shared/bsm-button.component"
import { useThemeColor } from "renderer/hooks/use-theme-color.hook"
import { MapsManagerService } from "renderer/services/maps-manager.service"
import { motion } from "framer-motion";
type Props = {
oneBlock?: boolean,
@@ -12,6 +16,8 @@ type Props = {
}
export function MapsPlaylistsPanel({version, oneBlock = false}: Props) {
const mapsService = MapsManagerService.getInstance();
const [tabIndex, setTabIndex] = useState(0);
const [mapFilter, setMapFilter] = useState<MapFilter>({
@@ -20,6 +26,14 @@ export function MapsPlaylistsPanel({version, oneBlock = false}: Props) {
});
const [mapSearch, setMapSearch] = useState("");
const [playlistSearch, setPlaylistSearch] = useState("");
const [mapsLinked, setMapsLinked] = useState(false);
const color = useThemeColor("first-color")
useEffect(() => {
mapsService.versionHaveMapsLinked(version).then(setMapsLinked);
}, [version])
const handleSearch = (value: string) => {
if(tabIndex === 0){
@@ -28,6 +42,21 @@ export function MapsPlaylistsPanel({version, oneBlock = false}: Props) {
return setPlaylistSearch(() => value);
}
const renderTab = (props: DetailedHTMLProps<React.HTMLAttributes<any>, any>, text: string): JSX.Element => {
const mainColor = mapsLinked ? color : "red";
return (
<li className="relative text-center text-lg font-bold hover:backdrop-brightness-75 flex justify-center items-center content-center" onClick={props.onClick}>
<span>{text}</span>
<motion.div whileHover={{rotate: [0, 20, 0]}} transition={{duration: .35}} className="absolute block p-1 right-3 h-[calc(100%-5px)] aspect-square blur-0 hover:brightness-75">
<span className="absolute top-0 left-0 h-full w-full rounded-full opacity-20" style={{backgroundColor: mainColor}}/>
<BsmButton className="p-1 absolute top-0 left-0 h-full w-full !bg-transparent -rotate-45" iconClassName="" icon={mapsLinked ? "link" : "unlink"} withBar={false} style={{color: mainColor}}/>
</motion.div>
</li>
)
}
return (
<>
{!oneBlock && <TabNavBar className="mb-8 w-72" tabsText={["misc.maps", "Playlists"]} onTabChange={setTabIndex}/>}
@@ -44,13 +73,12 @@ export function MapsPlaylistsPanel({version, oneBlock = false}: Props) {
</BsmDropdownButton>
</nav>
<div className="w-full h-full flex flex-col bg-main-color-2 rounded-md shadow-black shadow-md overflow-hidden">
{oneBlock && <TabNavBar className="!rounded-none shadow-sm" tabsText={["misc.maps", "Playlists"]} onTabChange={setTabIndex}/>}
{oneBlock && <TabNavBar className="!rounded-none shadow-sm" tabsText={["misc.maps", "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 className="w-full h-full shrink-0 flex flex-col" version={version} filter={mapFilter} search={mapSearch}/>
<div className="w-full h-full grow shrink-0">b</div>
</div>
</div>
<div>b</div>
</div>
</div>
</div>
</>
@@ -45,7 +45,7 @@ export function BsmButton({className, style, imgClassName, iconClassName, icon,
const renderTypeColor = (() => {
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){ return `bg-light-main-color-2 dark:bg-main-color-2 ${!withBar && "hover:brightness-125"}`; }
if(typeColor === "cancel"){ return "bg-gray-500"; }
if(typeColor === "error"){ return "bg-red-500"; }
if(typeColor === "success"){ return "bg-green-500"; }
@@ -1,17 +1,23 @@
import { useState } from "react";
import { DetailedHTMLProps, Fragment, useState } from "react";
import { useThemeColor } from "renderer/hooks/use-theme-color.hook";
import { useTranslation } from "renderer/hooks/use-translation.hook";
export function TabNavBar(props: {tabsText: string[], onTabChange: (index: number) => void, className?: string}) {
type Props = {
tabsText: string[],
onTabChange: (index: number) => void,
className?: string,
renderTab?: (props: DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, text: string) => JSX.Element
}
export function TabNavBar(props: Props) {
const [currentTabIndex, setCurrentTabIndex] = useState(0);
const t = useTranslation();
const secondColor = useThemeColor("second-color");
const selectYear = (tab: string) => {
const newIndex = props.tabsText.indexOf(tab);
setCurrentTabIndex(newIndex);
props.onTabChange(newIndex);
const selectTab = (index: number) => {
setCurrentTabIndex(index);
props.onTabChange(index);
}
return (
@@ -21,9 +27,16 @@ export function TabNavBar(props: {tabsText: string[], onTabChange: (index: numbe
<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 text-gray-800 dark:text-gray-200 text-lg font-bold hover:backdrop-brightness-75" key={index} onClick={() => selectYear(y)}>{t(y)}</li>
)}
{props.tabsText.map((text, index) => (
props.renderTab ? (
<Fragment key={text}>
{props.renderTab({onClick: () => selectTab(index)}, t(text))}
</Fragment>
) : (
<li className="px-4 h-full text-center text-gray-800 dark:text-gray-200 text-lg font-bold hover:backdrop-brightness-75" key={text} onClick={() => selectTab(index)}>{t(text)}</li>
)
))}
</ul>
</nav>
)
@@ -39,11 +39,13 @@ import { NoArrowIcon } from "./icons/no-arrow-icon.component";
import { OneSaberIcon } from "./icons/one-saber-icon.component";
import { NinetyDregreeIcon } from "./icons/ninety-dregree-icon.component";
import { ThreeSixtyDegreeIcon } from "./icons/three-sixty-degree-icon.component";
import { LinkIcon } from "./icons/link-icon.component";
import { UnlinkIcon } from "./icons/unlink-icon.component";
export type BsmIconType = BsvMapCharacteristic | (
"settings"|"trash"|"favorite"|"folder"|"bsNote"|"check"|"three-dots"|"twitch"|"eye"|"play"|"checkCircleIcon"|
"terminal"|"desktop"|"oculus"|"add"|"cross"|"task"|"github"|"close"|"thumbUpFill"|"timerFill"|"pause"|
"copy"|"steam"|"edit"|"export"|"patreon"|"search"|"bsMapDifficulty"|
"copy"|"steam"|"edit"|"export"|"patreon"|"search"|"bsMapDifficulty"|"link"|"unlink"|
"fr-FR-flag"|"es-ES-flag"|"en-US-flag"|"en-EN-flag"
);
@@ -89,6 +91,8 @@ export const BsmIcon = memo(({className, icon, style}: {className?: string, icon
if(icon === "Lightshow"){ return <LightshowIcon className={className} style={style}/> }
if(icon === "90Degree"){ return <NinetyDregreeIcon className={className} style={style}/> }
if(icon === "360Degree"){ return <ThreeSixtyDegreeIcon className={className} style={style}/> }
if(icon === "link"){ return <LinkIcon className={className} style={style}/> }
if(icon === "unlink"){ return <UnlinkIcon className={className} style={style}/> }
return <TrashIcon className={className} style={style}/>
}
@@ -0,0 +1,11 @@
import { CSSProperties } from "react";
export function LinkIcon(props: {className?: string, style?: CSSProperties}) {
return (
<svg className={props.className} style={props.style} stroke="currentColor" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" strokeWidth="48" strokeLinecap="round" strokeLinejoin="round">
<path d="M200.66,352H144a96,96,0,0,1,0-192h55.41"/>
<path d="M312.59,160H368a96,96,0,0,1,0,192H311.34"/>
<line x1="169.07" x2="344.93" y1="256" y2="256"/>
</svg>
)
}
@@ -0,0 +1,10 @@
import { CSSProperties } from "react";
export function UnlinkIcon(props: {className?: string, style?: CSSProperties}) {
return (
<svg className={props.className} style={props.style} viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="48">
<path d="M200.66,352H144a96,96,0,0,1,0-192h55.41"/>
<path d="M312.59,160H368a96,96,0,0,1,0,192H311.34"/>
</svg>
)
}
@@ -22,7 +22,6 @@ export class MapsManagerService {
}
public getMaps(version?: BSVersion): Observable<BsmLocalMap[]>{
console.log("get maps")
return new Observable(obs => {
this.ipcService.send<BsmLocalMap[], BSVersion>("get-version-maps", {args: version}).then(res => {
if(!res.success){ return obs.next(null);}
@@ -38,6 +37,13 @@ export class MapsManagerService {
});
}
public versionHaveMapsLinked(version: BSVersion): Promise<boolean>{
return this.ipcService.send<boolean, BSVersion>("verion-have-maps-linked", {args: version}).then(res => {
if(!res.success){ throw "error"; }
return res.data;
})
}
public downloadMap(map: any, version?: BSVersion){
}