diff --git a/src/main/ipcs/bs-maps-ipcs.ts b/src/main/ipcs/bs-maps-ipcs.ts index d378c643..6cb53976 100644 --- a/src/main/ipcs/bs-maps-ipcs.ts +++ b/src/main/ipcs/bs-maps-ipcs.ts @@ -15,4 +15,13 @@ ipcMain.on('get-version-maps', (event, request: IpcRequest) => { utilsService.ipcSend(request.responceChannel, {success: false}); }) +}); + +ipcMain.on("verion-have-maps-linked", async (event, request: IpcRequest) => { + const utils = UtilsService.getInstance(); + const maps = LocalMapsManagerService.getInstance(); + + console.log(request.args); + utils.ipcSend(request.responceChannel, {success: true, data: await maps.versionIsLinked(request.args)}); + }); \ No newline at end of file diff --git a/src/main/services/maps/local-maps-manager.service.ts b/src/main/services/maps/local-maps-manager.service.ts index 5aa5dd6e..35453b20 100644 --- a/src/main/services/maps/local-maps-manager.service.ts +++ b/src/main/services/maps/local-maps-manager.service.ts @@ -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{ - if(version){ return path.join(await this.localVersion.getVersionPath(version), this.LEVELS_ROOT_FOLDER); } - return this.installLocation.sharedMapsPath; + private async getMapsFolderPath(version?: BSVersion): Promise{ + 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{ 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{ - 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{ + + 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{ + //TODO + //Can use fs.symlink to create symlink + } + + public async unlinkVersionMaps(version: BSVersion, keepLinkedMaps: boolean): Promise{ + //TODO + } + + + } \ No newline at end of file diff --git a/src/renderer/components/maps-mangement-components/maps-playlists-panel.component.tsx b/src/renderer/components/maps-mangement-components/maps-playlists-panel.component.tsx index dfc10a3e..19825fd1 100644 --- a/src/renderer/components/maps-mangement-components/maps-playlists-panel.component.tsx +++ b/src/renderer/components/maps-mangement-components/maps-playlists-panel.component.tsx @@ -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({ @@ -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, any>, text: string): JSX.Element => { + + const mainColor = mapsLinked ? color : "red"; + + return ( +
  • + {text} + + + + +
  • + ) + } + return ( <> {!oneBlock && } @@ -44,13 +73,12 @@ export function MapsPlaylistsPanel({version, oneBlock = false}: Props) {
    - {oneBlock && } + {oneBlock && }
    -
    b
    -
    -
    - +
    b
    + + diff --git a/src/renderer/components/shared/bsm-button.component.tsx b/src/renderer/components/shared/bsm-button.component.tsx index 80d78356..cdfe89e8 100644 --- a/src/renderer/components/shared/bsm-button.component.tsx +++ b/src/renderer/components/shared/bsm-button.component.tsx @@ -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"; } diff --git a/src/renderer/components/shared/tab-nav-bar.component.tsx b/src/renderer/components/shared/tab-nav-bar.component.tsx index aeccae18..8edaa13d 100644 --- a/src/renderer/components/shared/tab-nav-bar.component.tsx +++ b/src/renderer/components/shared/tab-nav-bar.component.tsx @@ -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, 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
      - { props.tabsText.map((y, index) => -
    • selectYear(y)}>{t(y)}
    • - )} + {props.tabsText.map((text, index) => ( + props.renderTab ? ( + + {props.renderTab({onClick: () => selectTab(index)}, t(text))} + + ) : ( +
    • selectTab(index)}>{t(text)}
    • + ) + + ))}
    ) diff --git a/src/renderer/components/svgs/bsm-icon.component.tsx b/src/renderer/components/svgs/bsm-icon.component.tsx index 9ea5767d..78f11de1 100644 --- a/src/renderer/components/svgs/bsm-icon.component.tsx +++ b/src/renderer/components/svgs/bsm-icon.component.tsx @@ -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 } if(icon === "90Degree"){ return } if(icon === "360Degree"){ return } + if(icon === "link"){ return } + if(icon === "unlink"){ return } return } diff --git a/src/renderer/components/svgs/icons/link-icon.component.tsx b/src/renderer/components/svgs/icons/link-icon.component.tsx new file mode 100644 index 00000000..db3baac5 --- /dev/null +++ b/src/renderer/components/svgs/icons/link-icon.component.tsx @@ -0,0 +1,11 @@ +import { CSSProperties } from "react"; + +export function LinkIcon(props: {className?: string, style?: CSSProperties}) { + return ( + + + + + + ) +} diff --git a/src/renderer/components/svgs/icons/unlink-icon.component.tsx b/src/renderer/components/svgs/icons/unlink-icon.component.tsx new file mode 100644 index 00000000..e77fe246 --- /dev/null +++ b/src/renderer/components/svgs/icons/unlink-icon.component.tsx @@ -0,0 +1,10 @@ +import { CSSProperties } from "react"; + +export function UnlinkIcon(props: {className?: string, style?: CSSProperties}) { + return ( + + + + + ) +} diff --git a/src/renderer/services/maps-manager.service.ts b/src/renderer/services/maps-manager.service.ts index 145c3866..ebe988b8 100644 --- a/src/renderer/services/maps-manager.service.ts +++ b/src/renderer/services/maps-manager.service.ts @@ -22,7 +22,6 @@ export class MapsManagerService { } public getMaps(version?: BSVersion): Observable{ - console.log("get maps") return new Observable(obs => { this.ipcService.send("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{ + return this.ipcService.send("verion-have-maps-linked", {args: version}).then(res => { + if(!res.success){ throw "error"; } + return res.data; + }) + } + public downloadMap(map: any, version?: BSVersion){ }