[chore] prettify + config prettier

This commit is contained in:
MathieuG-P
2023-07-01 22:12:47 +02:00
parent 0f85e0dcf9
commit f47a1de05e
330 changed files with 42816 additions and 41968 deletions
@@ -2,7 +2,7 @@ import { AvailableVersionsSlider } from "../components/available-versions/availa
import { BsDownloaderService } from "../services/bs-downloader.service";
import { Slideshow } from "renderer/components/slideshow/slideshow.component";
import { useEffect, useState } from "react";
import { BSVersion } from 'shared/bs-version.interface';
import { BSVersion } from "shared/bs-version.interface";
import { BsmButton } from "renderer/components/shared/bsm-button.component";
import { AnimatePresence, motion } from "framer-motion";
import { useTranslation } from "renderer/hooks/use-translation.hook";
@@ -16,25 +16,24 @@ import { NotificationService } from "renderer/services/notification.service";
import { timer } from "rxjs";
export function AvailableVersionsList() {
const bsDownloaderService = BsDownloaderService.getInstance();
const versionManagerService = BSVersionManagerService.getInstance();
const progressBar = ProgressBarService.getInstance();
const modal = ModalService.getInsance();
const ipc = IpcService.getInstance();
const installer = BsDownloaderService.getInstance();
const notification = NotificationService.getInstance();
const bsDownloaderService = BsDownloaderService.getInstance();
const versionManagerService = BSVersionManagerService.getInstance();
const progressBar = ProgressBarService.getInstance();
const modal = ModalService.getInsance();
const ipc = IpcService.getInstance();
const installer = BsDownloaderService.getInstance();
const notification = NotificationService.getInstance();
const [versionSelected, setVersionSelected] = useState(null as BSVersion);
const [downloading, setDownloading] = useState(false);
const t = useTranslation();
const [versionSelected, setVersionSelected] = useState(null as BSVersion);
const [downloading, setDownloading] = useState(false);
const t = useTranslation();
const startDownload = () => {
setDownloading(() => true)
setDownloading(() => true);
bsDownloaderService.download(versionSelected).finally(() => {
setDownloading(() => false);
});
}
};
useEffect(() => {
const versionSelectedSub = bsDownloaderService.selectedBsVersion$.subscribe(version => {
@@ -43,63 +42,69 @@ export function AvailableVersionsList() {
return () => {
versionSelectedSub.unsubscribe();
}
}, [])
};
}, []);
const importVersion = async () => {
if(!progressBar.require()){ return; }
if (!progressBar.require()) {
return;
}
const modalRes = await modal.openModal(ImportVersionModal);
if(modalRes.exitCode !== ModalExitCode.COMPLETED){ return; }
if (modalRes.exitCode !== ModalExitCode.COMPLETED) {
return;
}
const folderRes = await ipc.sendV2<{canceled: boolean, filePaths: string[]}>("choose-folder").toPromise();
const folderRes = await ipc.sendV2<{ canceled: boolean; filePaths: string[] }>("choose-folder").toPromise();
if(!folderRes || folderRes.canceled || !folderRes.filePaths?.length){ return; }
if (!folderRes || folderRes.canceled || !folderRes.filePaths?.length) {
return;
}
notification.notifySuccess({title: "notifications.bs-import-version.success.start-import.title", desc: "notifications.bs-import-version.success.start-import.desc", duration: 6_000});
notification.notifySuccess({ title: "notifications.bs-import-version.success.start-import.title", desc: "notifications.bs-import-version.success.start-import.desc", duration: 6_000 });
progressBar.showFake(.008);
progressBar.showFake(0.008);
const toImport = folderRes.filePaths.at(0);
const imported = await installer.importVersion(toImport);
if(imported){
if (imported) {
versionManagerService.askInstalledVersions();
notification.notifySuccess({title: "notifications.bs-import-version.success.imported.title", duration: 3_000});
notification.notifySuccess({ title: "notifications.bs-import-version.success.imported.title", duration: 3_000 });
progressBar.complete();
await timer(400).toPromise();
}
else{
notification.notifyError({title: "notifications.types.error", desc: "notifications.bs-import-version.errors.import-error.desc"})
} else {
notification.notifyError({ title: "notifications.types.error", desc: "notifications.bs-import-version.errors.import-error.desc" });
}
progressBar.hide(true);
};
}
return (
<div className="relative h-full w-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]">{t("pages.available-versions.title")}</h1>
<AvailableVersionsSlider />
return (
<div className="relative h-full w-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]">{t("pages.available-versions.title")}</h1>
<AnimatePresence>
{versionSelected && !downloading && (
<motion.div initial={{ y: "150%" }} animate={{ y: "0%" }} exit={{ y: "150%" }} className="absolute bottom-5" onClick={startDownload}>
<BsmButton text="misc.download" className="relative text-gray-800 dark:text-gray-100 rounded-md text-3xl font-bold italic tracking-wide px-3 pb-2 pt-1 shadow-md shadow-black" />
</motion.div>
)}
</AnimatePresence>
<AvailableVersionsSlider/>
<AnimatePresence>
{ versionSelected && !downloading && (
<motion.div initial={{y:"150%"}} animate={{y:"0%"}} exit={{y:"150%"}} className="absolute bottom-5" onClick={startDownload}>
<BsmButton text="misc.download" className="relative text-gray-800 dark:text-gray-100 rounded-md text-3xl font-bold italic tracking-wide px-3 pb-2 pt-1 shadow-md shadow-black"/>
</motion.div>
)}
</AnimatePresence>
<BsmDropdownButton className="absolute top-5 right-5 h-9 w-9 bg-light-main-color-2 dark:bg-main-color-2 rounded-md" icon="settings" items={[
{icon: "sync", text: "pages.available-versions.dropdown.refresh", onClick: () => versionManagerService.askAvailableVersions()},
{icon: "download", text: "pages.available-versions.dropdown.import-version", onClick: importVersion}
]}/>
</div>
)
<BsmDropdownButton
className="absolute top-5 right-5 h-9 w-9 bg-light-main-color-2 dark:bg-main-color-2 rounded-md"
icon="settings"
items={[
{ icon: "sync", text: "pages.available-versions.dropdown.refresh", onClick: () => versionManagerService.askAvailableVersions() },
{ icon: "download", text: "pages.available-versions.dropdown.import-version", onClick: importVersion },
]}
/>
</div>
);
}
+257 -185
View File
@@ -7,7 +7,7 @@ import { BsmIconType } from "renderer/components/svgs/bsm-icon.component";
import { DefaultConfigKey, ThemeConfig } from "renderer/config/default-configuration.config";
import { useThemeColor } from "renderer/hooks/use-theme-color.hook";
import { BsDownloaderService } from "renderer/services/bs-downloader.service";
import { ConfigurationService } from "renderer/services/configuration.service"
import { ConfigurationService } from "renderer/services/configuration.service";
import { I18nService } from "renderer/services/i18n.service";
import { IpcService } from "renderer/services/ipc.service";
import { ModalExitCode, ModalService } from "renderer/services/modale.service";
@@ -25,7 +25,7 @@ import beatSaverIcon from "../../../assets/images/third-party-icons/beat-saver.p
import beastSaberIcon from "../../../assets/images/third-party-icons/beast-saber.png";
import scoreSaberIcon from "../../../assets/images/third-party-icons/score-saber.png";
import beatleaderIcon from "../../../assets/images/third-party-icons/beat-leader.png";
import Tippy from '@tippyjs/react';
import Tippy from "@tippyjs/react";
import { MapsManagerService } from "renderer/services/maps-manager.service";
import { PlaylistsManagerService } from "renderer/services/playlists-manager.service";
import { ModelsManagerService } from "renderer/services/models-management/models-manager.service";
@@ -33,89 +33,95 @@ import { useTranslation } from "renderer/hooks/use-translation.hook";
import { VersionFolderLinkerService } from "renderer/services/version-folder-linker.service";
export function SettingsPage() {
const configService: ConfigurationService = ConfigurationService.getInstance();
const themeService: ThemeService = ThemeService.getInstance();
const ipcService: IpcService = IpcService.getInstance();
const modalService: ModalService = ModalService.getInsance();
const downloaderService: BsDownloaderService = BsDownloaderService.getInstance();
const progressBarService: ProgressBarService = ProgressBarService.getInstance();
const notificationService: NotificationService = NotificationService.getInstance();
const i18nService: I18nService = I18nService.getInstance();
const linkOpener: LinkOpenerService = LinkOpenerService.getInstance();
const mapsManager = MapsManagerService.getInstance();
const playlistsManager = PlaylistsManagerService.getInstance();
const modelsManager = ModelsManagerService.getInstance();
const versionLinker = VersionFolderLinkerService.getInstance();
const configService: ConfigurationService = ConfigurationService.getInstance();
const themeService: ThemeService = ThemeService.getInstance();
const ipcService: IpcService = IpcService.getInstance();
const modalService: ModalService = ModalService.getInsance();
const downloaderService: BsDownloaderService = BsDownloaderService.getInstance();
const progressBarService: ProgressBarService = ProgressBarService.getInstance();
const notificationService: NotificationService = NotificationService.getInstance();
const i18nService: I18nService = I18nService.getInstance();
const linkOpener: LinkOpenerService = LinkOpenerService.getInstance();
const mapsManager = MapsManagerService.getInstance();
const playlistsManager = PlaylistsManagerService.getInstance();
const modelsManager = ModelsManagerService.getInstance();
const versionLinker = VersionFolderLinkerService.getInstance();
const { firstColor, secondColor } = useThemeColor();
const {firstColor, secondColor} = useThemeColor();
const themeItem: RadioItem[] = [
{ id: 0, text: "pages.settings.appearance.themes.dark", value: "dark" as ThemeConfig },
{ id: 1, text: "pages.settings.appearance.themes.light", value: "light" as ThemeConfig },
{ id: 3, text: "pages.settings.appearance.themes.os", value: "os" as ThemeConfig },
];
const themeItem: RadioItem[] = [
{id: 0, text: "pages.settings.appearance.themes.dark", value: "dark" as ThemeConfig},
{id: 1, text: "pages.settings.appearance.themes.light", value: "light" as ThemeConfig},
{id: 3, text: "pages.settings.appearance.themes.os", value: "os" as ThemeConfig}
];
const languagesItems: RadioItem[] = i18nService
.getSupportedLanguages()
.map((l, index) => {
return { id: index, text: `pages.settings.language.languages.${l}`, value: l, textIcon: `pages.settings.language.languages.translated.${l}`, icon: `${l}-flag` as BsmIconType };
})
.sort((a, b) => a.text.localeCompare(b.text));
const languagesItems: RadioItem[] = i18nService.getSupportedLanguages().map((l, index) => {
return {id: index, text: `pages.settings.language.languages.${l}`, value: l, textIcon:`pages.settings.language.languages.translated.${l}`, icon: `${l}-flag` as BsmIconType};
}).sort((a, b) => a.text.localeCompare(b.text));
const [themeIdSelected, setThemeIdSelected] = useState(themeItem.find(e => e.value === themeService.getTheme()).id);
const [languageSelected, setLanguageSelected] = useState(languagesItems.find(e => e.value === i18nService.currentLanguage).id);
const [installationFolder, setInstallationFolder] = useState(null);
const [showSupporters, setShowSupporters] = useState(false);
const [mapDeepLinksEnabled, setMapDeepLinksEnabled] = useState(false);
const [playlistsDeepLinkEnabled, setPlaylistsDeepLinkEnabled] = useState(false);
const [modelsDeepLinkEnabled, setModelsDeepLinkEnabled] = useState(false);
const [appVersion, setAppVersion] = useState("");
const nav = useNavigate();
const t = useTranslation();
const[themeIdSelected, setThemeIdSelected]= useState(themeItem.find(e => e.value === themeService.getTheme()).id);
const[languageSelected, setLanguageSelected]= useState(languagesItems.find(e => e.value === i18nService.currentLanguage).id);
const [installationFolder, setInstallationFolder] = useState(null);
const [showSupporters, setShowSupporters] = useState(false);
const [mapDeepLinksEnabled, setMapDeepLinksEnabled] = useState(false);
const [playlistsDeepLinkEnabled, setPlaylistsDeepLinkEnabled] = useState(false);
const [modelsDeepLinkEnabled, setModelsDeepLinkEnabled] = useState(false);
const [appVersion, setAppVersion] = useState("");
const nav = useNavigate();
const t = useTranslation();
useEffect(() => {
loadInstallationFolder();
ipcService.send<string>("current-version").then(res => setAppVersion(res.data));
mapsManager.isDeepLinksEnabled().then(enabled => setMapDeepLinksEnabled(() => enabled));
playlistsManager.isDeepLinksEnabled().then(enabled => setPlaylistsDeepLinkEnabled(() => enabled));
modelsManager.isDeepLinksEnabled().then(enabled => setModelsDeepLinkEnabled(() => enabled));
}, []);
useEffect(() => {
loadInstallationFolder();
ipcService.send<string>("current-version").then(res => setAppVersion(res.data));
mapsManager.isDeepLinksEnabled().then(enabled => setMapDeepLinksEnabled(() => enabled));
playlistsManager.isDeepLinksEnabled().then(enabled => setPlaylistsDeepLinkEnabled(() => enabled));
modelsManager.isDeepLinksEnabled().then(enabled => setModelsDeepLinkEnabled(() => enabled))
}, []);
const allDeepLinkEnabled = mapDeepLinksEnabled && playlistsDeepLinkEnabled && modelsDeepLinkEnabled;
const allDeepLinkEnabled = mapDeepLinksEnabled && playlistsDeepLinkEnabled && modelsDeepLinkEnabled;
const resetColors = () => {
configService.delete("first-color" as DefaultConfigKey);
configService.delete("second-color" as DefaultConfigKey);
};
const resetColors = () => {
configService.delete("first-color" as DefaultConfigKey);
configService.delete("second-color" as DefaultConfigKey);
}
const loadInstallationFolder = () => {
downloaderService.getInstallationFolder().then(res => setInstallationFolder(res));
};
const loadInstallationFolder = () => {
downloaderService.getInstallationFolder().then(res => setInstallationFolder(res));
}
const setFirstColorSetting = (hex: string) => configService.set("first-color", hex);
const setSecondColorSetting = (hex: string) => configService.set("second-color", hex);
const setFirstColorSetting = (hex: string) => configService.set("first-color", hex);
const setSecondColorSetting = (hex: string) => configService.set("second-color", hex);
const handleChangeTheme = (id: number) => {
setThemeIdSelected(id);
themeService.setTheme(themeItem.find(e => e.id === id).value);
};
const handleChangeTheme = (id: number) => {
setThemeIdSelected(id);
themeService.setTheme(themeItem.find(e => e.id === id).value);
}
const handleChangeLanguage = (id: number) => {
const selectedLanguage = languagesItems.find(l => l.id === id).value;
i18nService.setLanguage(selectedLanguage);
setLanguageSelected(languagesItems.find(l => l.value === i18nService.currentLanguage).id);
}
const handleChangeLanguage = (id: number) => {
const selectedLanguage = languagesItems.find(l => l.id === id).value;
i18nService.setLanguage(selectedLanguage);
setLanguageSelected(languagesItems.find(l => l.value === i18nService.currentLanguage).id);
};
const setDefaultInstallationFolder = () => {
if(!progressBarService.require()){ return; }
if (!progressBarService.require()) {
return;
}
modalService.openModal(InstallationFolderModal).then(async res => {
if(res.exitCode !== ModalExitCode.COMPLETED){ return; }
if (res.exitCode !== ModalExitCode.COMPLETED) {
return;
}
const fileChooserRes = await ipcService.sendV2<{canceled: boolean, filePaths: string[]}>("choose-folder").toPromise();
const fileChooserRes = await ipcService.sendV2<{ canceled: boolean; filePaths: string[] }>("choose-folder").toPromise();
if(!fileChooserRes.canceled && fileChooserRes.filePaths?.length){
progressBarService.showFake(.008);
if (!fileChooserRes.canceled && fileChooserRes.filePaths?.length) {
progressBarService.showFake(0.008);
notificationService.notifySuccess({title: "notifications.settings.move-folder.success.titles.transfer-started", desc: "notifications.settings.move-folder.success.descs.transfer-started"});
notificationService.notifySuccess({ title: "notifications.settings.move-folder.success.titles.transfer-started", desc: "notifications.settings.move-folder.success.descs.transfer-started" });
downloaderService.setInstallationFolder(fileChooserRes.filePaths[0]).then(async res => {
setTimeout(() => {
@@ -123,216 +129,282 @@ export function SettingsPage() {
setTimeout(() => progressBarService.hide(true), 1000);
}, 1000);
if(res.success){
if (res.success) {
setInstallationFolder(res.data);
notificationService.notifySuccess({title: "notifications.settings.move-folder.success.titles.transfer-finished", duration: 3000});
notificationService.notifySuccess({ title: "notifications.settings.move-folder.success.titles.transfer-finished", duration: 3000 });
versionLinker.relinkAllVersionsFolders().toPromise().catch(() => {
notificationService.notifyError({title: "notifications.types.error", desc: "notifications.settings.move-folder.errors.descs.restore-linked-folders", duration: 15_000});
});
}
else{
if(res?.error?.code === "COPY_TO_SUBPATH"){
notificationService.notifyError({title: "notifications.settings.move-folder.errors.titles.transfer-failed", desc: "notifications.settings.move-folder.errors.descs.COPY_TO_SUBPATH", duration: 10_000});
versionLinker
.relinkAllVersionsFolders()
.toPromise()
.catch(() => {
notificationService.notifyError({ title: "notifications.types.error", desc: "notifications.settings.move-folder.errors.descs.restore-linked-folders", duration: 15_000 });
});
} else {
if (res?.error?.code === "COPY_TO_SUBPATH") {
notificationService.notifyError({ title: "notifications.settings.move-folder.errors.titles.transfer-failed", desc: "notifications.settings.move-folder.errors.descs.COPY_TO_SUBPATH", duration: 10_000 });
return;
}
notificationService.notifyError({title: "notifications.settings.move-folder.errors.titles.transfer-failed"});
notificationService.notifyError({ title: "notifications.settings.move-folder.errors.titles.transfer-failed" });
}
});
}
});
}
};
const toogleShowSupporters = () => {
setShowSupporters(show => !show);
}
const openSupportPage = () => linkOpener.open("https://www.patreon.com/bsmanager");
const openGithub = () => linkOpener.open("https://github.com/Zagrios/bs-manager");
const openReportBug = () => linkOpener.open("https://github.com/Zagrios/bs-manager/issues/new?assignees=Zagrios&labels=bug&template=-bug--bug-report.md&title=%5BBUG%5D+%3A+");
const openRequestFeatures = () => linkOpener.open("https://github.com/Zagrios/bs-manager/issues/new?assignees=Zagrios&labels=enhancement&template=-feat---feature-request.md&title=%5BFEAT.%5D+%3A+");
const openDiscord = () => linkOpener.open("https://discord.gg/uSqbHVpKdV");
const openTwitter = () => linkOpener.open("https://twitter.com/BSManager_");
const toogleShowSupporters = () => {
setShowSupporters(show => !show);
};
const openLogs = () => ipcService.sendLazy("open-logs");
const openSupportPage = () => linkOpener.open("https://www.patreon.com/bsmanager");
const openGithub = () => linkOpener.open("https://github.com/Zagrios/bs-manager");
const openReportBug = () => linkOpener.open("https://github.com/Zagrios/bs-manager/issues/new?assignees=Zagrios&labels=bug&template=-bug--bug-report.md&title=%5BBUG%5D+%3A+");
const openRequestFeatures = () => linkOpener.open("https://github.com/Zagrios/bs-manager/issues/new?assignees=Zagrios&labels=enhancement&template=-feat---feature-request.md&title=%5BFEAT.%5D+%3A+");
const openDiscord = () => linkOpener.open("https://discord.gg/uSqbHVpKdV");
const openTwitter = () => linkOpener.open("https://twitter.com/BSManager_");
const openLogs = () => ipcService.sendLazy("open-logs");
const showDeepLinkError = (isDeactivation: boolean) => {
const desc = isDeactivation ? "notifications.settings.additional-content.deep-link.deactivation.error.description" : "notifications.settings.additional-content.deep-link.activation.error.description";
notificationService.notifyError({title: "notifications.types.error", desc, duration: 3000});
}
notificationService.notifyError({ title: "notifications.types.error", desc, duration: 3000 });
};
const showDeepLinkSuccess = (isDeactivation: boolean) => {
const desc = isDeactivation ? "notifications.settings.additional-content.deep-link.deactivation.success.description" : "notifications.settings.additional-content.deep-link.activation.success.description";
const title = isDeactivation ? "notifications.settings.additional-content.deep-link.deactivation.success.title" : "notifications.settings.additional-content.deep-link.activation.success.title";
notificationService.notifySuccess({title, desc, duration: 3000});
}
notificationService.notifySuccess({ title, desc, duration: 3000 });
};
const switchDeepLink = async (manager: MapsManagerService|PlaylistsManagerService|ModelsManagerService, enable: boolean, showNotification: boolean, setter: Dispatch<SetStateAction<boolean>>) => {
const switchDeepLink = async (manager: MapsManagerService | PlaylistsManagerService | ModelsManagerService, enable: boolean, showNotification: boolean, setter: Dispatch<SetStateAction<boolean>>) => {
const res = await (enable ? manager.enableDeepLink() : manager.disableDeepLink());
showNotification && (res ? showDeepLinkSuccess(!enable) : showDeepLinkError(!enable));
const isEnable = await manager.isDeepLinksEnabled()
setter(() => isEnable)
const isEnable = await manager.isDeepLinksEnabled();
setter(() => isEnable);
return res;
}
};
const toogleMapDeepLinks = (showNotification = true) => switchDeepLink(mapsManager, !mapDeepLinksEnabled, showNotification, setMapDeepLinksEnabled);
const tooglePlaylistsDeepLinks = (showNotification = true) => switchDeepLink(playlistsManager, !playlistsDeepLinkEnabled, showNotification, setPlaylistsDeepLinkEnabled);
const toogleModelsDeepLinks = (showNotification = true) => switchDeepLink(modelsManager, !modelsDeepLinkEnabled, showNotification, setModelsDeepLinkEnabled);
const toogleAllDeepLinks = async () => {
const res = (await Promise.all([
switchDeepLink(mapsManager, !allDeepLinkEnabled, false, setMapDeepLinksEnabled),
switchDeepLink(playlistsManager, !allDeepLinkEnabled, false, setPlaylistsDeepLinkEnabled),
switchDeepLink(modelsManager, !allDeepLinkEnabled, false, setModelsDeepLinkEnabled)
])).every(activation => activation === true);
const res = (await Promise.all([switchDeepLink(mapsManager, !allDeepLinkEnabled, false, setMapDeepLinksEnabled), switchDeepLink(playlistsManager, !allDeepLinkEnabled, false, setPlaylistsDeepLinkEnabled), switchDeepLink(modelsManager, !allDeepLinkEnabled, false, setModelsDeepLinkEnabled)])).every(activation => activation === true);
res ? showDeepLinkSuccess(allDeepLinkEnabled) : showDeepLinkError(allDeepLinkEnabled);
};
return (
<div className="w-full h-full flex justify-center overflow-y-scroll scrollbar-thin scrollbar-thumb-rounded-full scrollbar-thumb-neutral-900 text-gray-800 dark:text-gray-200">
<div className="max-w-2xl w-full h-fit">
<div className="inline-block sticky top-8 left-[calc(100%)] translate-x-12 grow-0 w-9 h-9">
<BsmButton className="inline-block grow-0 bg-transparent sticky h-full w-full top-20 right-20 !m-0 rounded-full p-1" onClick={() => nav(-1)} icon="close" withBar={false}/>
<BsmButton className="inline-block grow-0 bg-transparent sticky h-full w-full top-20 right-20 !m-0 rounded-full p-1" onClick={() => nav(-1)} icon="close" withBar={false} />
</div>
<SettingContainer title="pages.settings.appearance.title" description="pages.settings.appearance.description">
<div className="relative w-full h-8 bg-light-main-color-1 dark:bg-main-color-1 flex justify-center rounded-md py-1">
<SettingColorChooser color={firstColor} onChange={setFirstColorSetting}/>
<SettingColorChooser color={secondColor} onChange={setSecondColorSetting}/>
<SettingColorChooser color={firstColor} onChange={setFirstColorSetting} />
<SettingColorChooser color={secondColor} onChange={setSecondColorSetting} />
<div className="absolute right-2 top-0 h-full flex items-center">
<BsmButton onClick={resetColors} className="px-2 font-bold italic text-sm rounded-md" text="pages.settings.appearance.reset" withBar={false}/>
<BsmButton onClick={resetColors} className="px-2 font-bold italic text-sm rounded-md" text="pages.settings.appearance.reset" withBar={false} />
</div>
</div>
<SettingContainer minorTitle="pages.settings.appearance.sub-title" className="mt-3">
<SettingRadioArray items={themeItem} selectedItem={themeIdSelected} onItemSelected={handleChangeTheme}/>
<SettingRadioArray items={themeItem} selectedItem={themeIdSelected} onItemSelected={handleChangeTheme} />
</SettingContainer>
</SettingContainer>
<SettingContainer title="pages.settings.installation-folder.title" description="pages.settings.installation-folder.description">
<div className="relative flex items-center justify-between w-full h-8 bg-light-main-color-1 dark:bg-main-color-1 rounded-md pl-2 py-1">
<span className="block text-ellipsis overflow-hidden min-w-0" title={installationFolder}>{installationFolder}</span>
<BsmButton onClick={setDefaultInstallationFolder} className="shrink-0 whitespace-nowrap mr-2 px-2 font-bold italic text-sm rounded-md" text="pages.settings.installation-folder.choose-folder" withBar={false}/>
<span className="block text-ellipsis overflow-hidden min-w-0" title={installationFolder}>
{installationFolder}
</span>
<BsmButton onClick={setDefaultInstallationFolder} className="shrink-0 whitespace-nowrap mr-2 px-2 font-bold italic text-sm rounded-md" text="pages.settings.installation-folder.choose-folder" withBar={false} />
</div>
</SettingContainer>
<SettingContainer title="pages.settings.additional-content.title" description="pages.settings.additional-content.description">
<SettingContainer id="one-clicks" minorTitle="pages.settings.additional-content.deep-links.sub-title">
<div className="bg-light-main-color-1 dark:bg-main-color-1 rounded-md group-one flex justify-between items-center basis-0 py-2 px-3 cursor-pointer mb-1.5" onClick={toogleAllDeepLinks}>
<SettingContainer id="one-clicks" minorTitle="pages.settings.additional-content.deep-links.sub-title">
<div className="bg-light-main-color-1 dark:bg-main-color-1 rounded-md group-one flex justify-between items-center basis-0 py-2 px-3 cursor-pointer mb-1.5" onClick={toogleAllDeepLinks}>
<div className="flex items-center gap-2">
<BsmCheckbox className="relative z-[1] h-5 w-5" onChange={toogleAllDeepLinks} checked={allDeepLinkEnabled}/>
<BsmCheckbox className="relative z-[1] h-5 w-5" onChange={toogleAllDeepLinks} checked={allDeepLinkEnabled} />
<span className="font-extrabold">{t("notifications.settings.additional-content.deep-link.select-all")}</span>
</div>
<div className="flex h-full gap-2">
<Tippy content="BeatSaver" placement="top" className="font-bold bg-main-color-3" arrow={false} duration={[200, 0]}>
<BsmImage className="h-8 cursor-pointer" image={beatSaverIcon} onClick={e => {e.stopPropagation(); linkOpener.open("https://beatsaver.com/")}}/>
<BsmImage
className="h-8 cursor-pointer"
image={beatSaverIcon}
onClick={e => {
e.stopPropagation();
linkOpener.open("https://beatsaver.com/");
}}
/>
</Tippy>
<Tippy content="BeastSaber" placement="top" className="font-bold bg-main-color-3" arrow={false} duration={[200, 0]}>
<BsmImage className="h-8 rounded-md cursor-pointer" image={beastSaberIcon} onClick={e => {e.stopPropagation(); linkOpener.open("https://bsaber.com/")}}/>
<BsmImage
className="h-8 rounded-md cursor-pointer"
image={beastSaberIcon}
onClick={e => {
e.stopPropagation();
linkOpener.open("https://bsaber.com/");
}}
/>
</Tippy>
<Tippy content="ScoreSaber" placement="top" className="font-bold bg-main-color-3" arrow={false} duration={[200, 0]}>
<BsmImage className="h-8 cursor-pointer" image={scoreSaberIcon} onClick={e => {e.stopPropagation(); linkOpener.open("https://scoresaber.com/")}}/>
<BsmImage
className="h-8 cursor-pointer"
image={scoreSaberIcon}
onClick={e => {
e.stopPropagation();
linkOpener.open("https://scoresaber.com/");
}}
/>
</Tippy>
<Tippy content="BeatLeader" placement="top" className="font-bold bg-main-color-3" arrow={false} duration={[200, 0]}>
<BsmImage className="h-8 cursor-pointer" image={beatleaderIcon} onClick={e => {e.stopPropagation(); linkOpener.open("https://www.beatleader.xyz/")}}/>
<BsmImage
className="h-8 cursor-pointer"
image={beatleaderIcon}
onClick={e => {
e.stopPropagation();
linkOpener.open("https://www.beatleader.xyz/");
}}
/>
</Tippy>
<Tippy content="ModelSaber" placement="top" className="font-bold bg-main-color-3" arrow={false} duration={[200, 0]}>
<BsmImage className="h-8 cursor-pointer" image={modelSaberIcon} onClick={e => {e.stopPropagation(); linkOpener.open("https://modelsaber.com/")}}/>
<BsmImage
className="h-8 cursor-pointer"
image={modelSaberIcon}
onClick={e => {
e.stopPropagation();
linkOpener.open("https://modelsaber.com/");
}}
/>
</Tippy>
</div>
</div>
<ul className="w-full flex flex-col gap-1.5 pl-10">
<li className="bg-light-main-color-1 dark:bg-main-color-1 rounded-md group-one flex justify-between items-center basis-0 py-2 px-3 cursor-pointer" onClick={() => toogleMapDeepLinks()}>
<div className="flex items-center gap-2">
<BsmCheckbox className="relative z-[1] h-5 w-5" onChange={() => toogleMapDeepLinks()} checked={mapDeepLinksEnabled}/>
<span className="font-extrabold">{t("misc.maps")}</span>
</div>
<div className="flex h-full gap-2">
<Tippy content="BeatSaver" placement="top" className="font-bold bg-main-color-3" arrow={false} duration={[200, 0]}>
<BsmImage className="h-8 cursor-pointer" image={beatSaverIcon} onClick={e => {e.stopPropagation(); linkOpener.open("https://beatsaver.com/")}}/>
</Tippy>
<Tippy content="BeastSaber" placement="top" className="font-bold bg-main-color-3" arrow={false} duration={[200, 0]}>
<BsmImage className="h-8 rounded-md cursor-pointer" image={beastSaberIcon} onClick={e => {e.stopPropagation(); linkOpener.open("https://bsaber.com/")}}/>
</Tippy>
<Tippy content="ScoreSaber" placement="top" className="font-bold bg-main-color-3" arrow={false} duration={[200, 0]}>
<BsmImage className="h-8 cursor-pointer" image={scoreSaberIcon} onClick={e => {e.stopPropagation(); linkOpener.open("https://scoresaber.com/")}}/>
</Tippy>
<Tippy content="BeatLeader" placement="top" className="font-bold bg-main-color-3" arrow={false} duration={[200, 0]}>
<BsmImage className="h-8 cursor-pointer" image={beatleaderIcon} onClick={e => {e.stopPropagation(); linkOpener.open("https://www.beatleader.xyz/")}}/>
</Tippy>
</div>
</li>
<li className="bg-light-main-color-1 dark:bg-main-color-1 rounded-md group-one flex justify-between items-center basis-0 py-2 px-3 cursor-pointer" onClick={() => tooglePlaylistsDeepLinks()}>
<div className="flex items-center gap-2">
<BsmCheckbox className="relative z-[1] h-5 w-5" onChange={() => tooglePlaylistsDeepLinks()} checked={playlistsDeepLinkEnabled}/>
<span className="font-extrabold">{t("misc.playlists")}</span>
</div>
<div className="flex h-full">
<Tippy content="BeatSaver" placement="top" className="font-bold bg-main-color-3" arrow={false} duration={[200, 0]}>
<BsmImage className="h-8 cursor-pointer" image={beatSaverIcon} onClick={e => {e.stopPropagation(); linkOpener.open("https://beatsaver.com/")}}/>
</Tippy>
</div>
</li>
<li className="bg-light-main-color-1 dark:bg-main-color-1 rounded-md group-one flex justify-between items-center basis-0 py-2 px-3 cursor-pointer" onClick={() => toogleModelsDeepLinks()}>
<div className="flex items-center gap-2">
<BsmCheckbox className="relative z-[1] h-5 w-5" onChange={() => toogleModelsDeepLinks()} checked={modelsDeepLinkEnabled}/>
<span className="font-extrabold">{t("misc.models")}</span>
</div>
<div className="flex h-full">
<Tippy content="ModelSaber" placement="top" className="font-bold bg-main-color-3" arrow={false} duration={[200, 0]}>
<BsmImage className="h-8 cursor-pointer" image={modelSaberIcon} onClick={e => {e.stopPropagation(); linkOpener.open("https://modelsaber.com/")}}/>
</Tippy>
</div>
</li>
</ul>
</SettingContainer>
</div>
<ul className="w-full flex flex-col gap-1.5 pl-10">
<li className="bg-light-main-color-1 dark:bg-main-color-1 rounded-md group-one flex justify-between items-center basis-0 py-2 px-3 cursor-pointer" onClick={() => toogleMapDeepLinks()}>
<div className="flex items-center gap-2">
<BsmCheckbox className="relative z-[1] h-5 w-5" onChange={() => toogleMapDeepLinks()} checked={mapDeepLinksEnabled} />
<span className="font-extrabold">{t("misc.maps")}</span>
</div>
<div className="flex h-full gap-2">
<Tippy content="BeatSaver" placement="top" className="font-bold bg-main-color-3" arrow={false} duration={[200, 0]}>
<BsmImage
className="h-8 cursor-pointer"
image={beatSaverIcon}
onClick={e => {
e.stopPropagation();
linkOpener.open("https://beatsaver.com/");
}}
/>
</Tippy>
<Tippy content="BeastSaber" placement="top" className="font-bold bg-main-color-3" arrow={false} duration={[200, 0]}>
<BsmImage
className="h-8 rounded-md cursor-pointer"
image={beastSaberIcon}
onClick={e => {
e.stopPropagation();
linkOpener.open("https://bsaber.com/");
}}
/>
</Tippy>
<Tippy content="ScoreSaber" placement="top" className="font-bold bg-main-color-3" arrow={false} duration={[200, 0]}>
<BsmImage
className="h-8 cursor-pointer"
image={scoreSaberIcon}
onClick={e => {
e.stopPropagation();
linkOpener.open("https://scoresaber.com/");
}}
/>
</Tippy>
<Tippy content="BeatLeader" placement="top" className="font-bold bg-main-color-3" arrow={false} duration={[200, 0]}>
<BsmImage
className="h-8 cursor-pointer"
image={beatleaderIcon}
onClick={e => {
e.stopPropagation();
linkOpener.open("https://www.beatleader.xyz/");
}}
/>
</Tippy>
</div>
</li>
<li className="bg-light-main-color-1 dark:bg-main-color-1 rounded-md group-one flex justify-between items-center basis-0 py-2 px-3 cursor-pointer" onClick={() => tooglePlaylistsDeepLinks()}>
<div className="flex items-center gap-2">
<BsmCheckbox className="relative z-[1] h-5 w-5" onChange={() => tooglePlaylistsDeepLinks()} checked={playlistsDeepLinkEnabled} />
<span className="font-extrabold">{t("misc.playlists")}</span>
</div>
<div className="flex h-full">
<Tippy content="BeatSaver" placement="top" className="font-bold bg-main-color-3" arrow={false} duration={[200, 0]}>
<BsmImage
className="h-8 cursor-pointer"
image={beatSaverIcon}
onClick={e => {
e.stopPropagation();
linkOpener.open("https://beatsaver.com/");
}}
/>
</Tippy>
</div>
</li>
<li className="bg-light-main-color-1 dark:bg-main-color-1 rounded-md group-one flex justify-between items-center basis-0 py-2 px-3 cursor-pointer" onClick={() => toogleModelsDeepLinks()}>
<div className="flex items-center gap-2">
<BsmCheckbox className="relative z-[1] h-5 w-5" onChange={() => toogleModelsDeepLinks()} checked={modelsDeepLinkEnabled} />
<span className="font-extrabold">{t("misc.models")}</span>
</div>
<div className="flex h-full">
<Tippy content="ModelSaber" placement="top" className="font-bold bg-main-color-3" arrow={false} duration={[200, 0]}>
<BsmImage
className="h-8 cursor-pointer"
image={modelSaberIcon}
onClick={e => {
e.stopPropagation();
linkOpener.open("https://modelsaber.com/");
}}
/>
</Tippy>
</div>
</li>
</ul>
</SettingContainer>
</SettingContainer>
<SettingContainer title="pages.settings.language.title" description="pages.settings.language.description">
<SettingRadioArray items={languagesItems} selectedItem={languageSelected} onItemSelected={handleChangeLanguage}/>
<SettingRadioArray items={languagesItems} selectedItem={languageSelected} onItemSelected={handleChangeLanguage} />
</SettingContainer>
<SettingContainer title="pages.settings.patreon.title" description="pages.settings.patreon.description">
<div className="flex gap-2">
<BsmButton color="#ef4444" className="flex w-fit rounded-md h-8 px-2 font-bold py-1 whitespace-nowrap !text-white" text="pages.settings.patreon.buttons.support" withBar={false} onClick={openSupportPage}/>
<BsmButton className="flex w-fit rounded-md h-8 px-2 font-bold py-1 !text-white" withBar={false} text="pages.settings.patreon.buttons.supporters" color="#6c5ce7" onClick={toogleShowSupporters}/>
<BsmButton color="#ef4444" className="flex w-fit rounded-md h-8 px-2 font-bold py-1 whitespace-nowrap !text-white" text="pages.settings.patreon.buttons.support" withBar={false} onClick={openSupportPage} />
<BsmButton className="flex w-fit rounded-md h-8 px-2 font-bold py-1 !text-white" withBar={false} text="pages.settings.patreon.buttons.supporters" color="#6c5ce7" onClick={toogleShowSupporters} />
</div>
<SettingContainer className="mt-3" description="pages.settings.discord.description">
<div className="flex gap-2">
<BsmButton className="flex w-fit rounded-md h-8 px-2 font-bold py-1 !text-white" withBar={false} text="Discord" icon="discord" iconClassName="p-0.5 mr-1" color="#5865f2" onClick={openDiscord}/>
<BsmButton className="flex w-fit rounded-md h-8 px-2 font-bold py-1 !text-white" withBar={false} text="Twitter" icon="twitter" iconClassName="p-0.5 mr-1" color="#1A8CD8" onClick={openTwitter}/>
<BsmButton className="flex w-fit rounded-md h-8 px-2 font-bold py-1 !text-white" withBar={false} text="Discord" icon="discord" iconClassName="p-0.5 mr-1" color="#5865f2" onClick={openDiscord} />
<BsmButton className="flex w-fit rounded-md h-8 px-2 font-bold py-1 !text-white" withBar={false} text="Twitter" icon="twitter" iconClassName="p-0.5 mr-1" color="#1A8CD8" onClick={openTwitter} />
</div>
</SettingContainer>
<SettingContainer className="pt-3" description="pages.settings.contribution.description">
<div className="flex items-center justify-between w-full h-8 bg-light-main-color-1 dark:bg-main-color-1 rounded-md pl-2 py-1">
<div className="flex">
<BsmButton onClick={openRequestFeatures} className="shrink-0 whitespace-nowrap mr-2 px-2 font-bold italic text-sm rounded-md" text="pages.settings.contribution.buttons.request-features" withBar={false}/>
<BsmButton onClick={openReportBug} className="shrink-0 whitespace-nowrap mr-2 px-2 font-bold italic text-sm rounded-md" text="pages.settings.contribution.buttons.report-bug" withBar={false}/>
<BsmButton onClick={openRequestFeatures} className="shrink-0 whitespace-nowrap mr-2 px-2 font-bold italic text-sm rounded-md" text="pages.settings.contribution.buttons.request-features" withBar={false} />
<BsmButton onClick={openReportBug} className="shrink-0 whitespace-nowrap mr-2 px-2 font-bold italic text-sm rounded-md" text="pages.settings.contribution.buttons.report-bug" withBar={false} />
</div>
<div className="flex px-2 gap-2">
<BsmButton onClick={openLogs} className="shrink-0 whitespace-nowrap px-2 font-bold italic text-sm rounded-md" text="pages.settings.contribution.buttons.open-logs" withBar={false}/>
<BsmButton onClick={openGithub} className="shrink-0 px-2 rounded-md" icon="github" title="GitHub" withBar={false}/>
<BsmButton onClick={openLogs} className="shrink-0 whitespace-nowrap px-2 font-bold italic text-sm rounded-md" text="pages.settings.contribution.buttons.open-logs" withBar={false} />
<BsmButton onClick={openGithub} className="shrink-0 px-2 rounded-md" icon="github" title="GitHub" withBar={false} />
</div>
</div>
</SettingContainer>
</SettingContainer>
<span className="bg-light-main-color-1 dark:bg-main-color-1 rounded-md py-1 px-2 font-bold float-right mb-5">v{appVersion}</span>
</div>
<SupportersView isVisible={showSupporters} setVisible={setShowSupporters}/>
<SupportersView isVisible={showSupporters} setVisible={setShowSupporters} />
</div>
)
);
}
@@ -5,21 +5,20 @@ import { TabNavBar } from "renderer/components/shared/tab-nav-bar.component";
import { Slideshow } from "renderer/components/slideshow/slideshow.component";
export function SharedContentsPage() {
const [tabIndex, setTabIndex] = useState(0);
return (
<div className="relative flex items-center flex-col w-full h-full text-gray-200 backdrop-blur-lg">
<Slideshow className="absolute w-full h-full top-0"/>
<TabNavBar className='my-4' tabIndex={tabIndex} tabsText={["misc.maps", "misc.models"]} onTabChange={setTabIndex}/>
<div className='w-full min-h-0 grow flex transition-transform duration-300' style={{transform: `translate(${-(tabIndex * 100)}%, 0)`}}>
<Slideshow className="absolute w-full h-full top-0" />
<TabNavBar className="my-4" tabIndex={tabIndex} tabsText={["misc.maps", "misc.models"]} onTabChange={setTabIndex} />
<div className="w-full min-h-0 grow flex transition-transform duration-300" style={{ transform: `translate(${-(tabIndex * 100)}%, 0)` }}>
<div className="w-full shrink-0 px-3 pb-3 flex flex-col items-center">
<MapsPlaylistsPanel isActive={tabIndex === 0}/>
<MapsPlaylistsPanel isActive={tabIndex === 0} />
</div>
<div className="w-full shrink-0 px-3 pb-3 flex flex-col items-center">
<ModelsPanel isActive={tabIndex === 1}/>
<ModelsPanel isActive={tabIndex === 1} />
</div>
</div>
</div>
)
);
}
+55 -60
View File
@@ -1,101 +1,96 @@
import { BSVersion } from 'shared/bs-version.interface';
import { useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import { TabNavBar } from 'renderer/components/shared/tab-nav-bar.component';
import { BsmDropdownButton } from 'renderer/components/shared/bsm-dropdown-button.component';
import { BsmImage } from 'renderer/components/shared/bsm-image.component';
import { BSUninstallerService } from '../services/bs-uninstaller.service';
import { BSVersionManagerService } from '../services/bs-version-manager.service';
import { ModalExitCode, ModalService } from '../services/modale.service';
import { BSVersion } from "shared/bs-version.interface";
import { useState } from "react";
import { useLocation, useNavigate } from "react-router-dom";
import { TabNavBar } from "renderer/components/shared/tab-nav-bar.component";
import { BsmDropdownButton } from "renderer/components/shared/bsm-dropdown-button.component";
import { BsmImage } from "renderer/components/shared/bsm-image.component";
import { BSUninstallerService } from "../services/bs-uninstaller.service";
import { BSVersionManagerService } from "../services/bs-version-manager.service";
import { ModalExitCode, ModalService } from "../services/modale.service";
import DefautVersionImage from "../../../assets/images/default-version-img.jpg";
import { BsDownloaderService } from 'renderer/services/bs-downloader.service';
import { IpcService } from 'renderer/services/ipc.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';
import { UninstallModal } from 'renderer/components/modal/modal-types/uninstall-modal.component';
import { MapsPlaylistsPanel } from 'renderer/components/maps-mangement-components/maps-playlists-panel.component';
import { ShareFoldersModal } from 'renderer/components/modal/modal-types/share-folders-modal.component';
import { ModelsPanel } from 'renderer/components/models-management/models-panel.component';
import { BsDownloaderService } from "renderer/services/bs-downloader.service";
import { IpcService } from "renderer/services/ipc.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";
import { UninstallModal } from "renderer/components/modal/modal-types/uninstall-modal.component";
import { MapsPlaylistsPanel } from "renderer/components/maps-mangement-components/maps-playlists-panel.component";
import { ShareFoldersModal } from "renderer/components/modal/modal-types/share-folders-modal.component";
import { ModelsPanel } from "renderer/components/models-management/models-panel.component";
export function VersionViewer() {
const bsUninstallerService = BSUninstallerService.getInstance();
const bsVersionManagerService = BSVersionManagerService.getInstance();
const modalService = ModalService.getInsance();
const bsDownloaderService = BsDownloaderService.getInstance();
const ipcService = IpcService.getInstance();
const {state} = useLocation() as {state: BSVersion};
const { state } = useLocation() as { state: BSVersion };
const navigate = useNavigate();
const [currentTabIndex, setCurrentTabIndex] = useState(0);
const navigateToVersion = (version?: BSVersion) => {
if(!version){
if (!version) {
return navigate("/available-versions");
}
navigate(`/bs-version/${version.BSVersion}`, {state: version});
}
const openFolder = () => ipcService.sendLazy("bs-version.open-folder", {args: state});
navigate(`/bs-version/${version.BSVersion}`, { state: version });
};
const openFolder = () => ipcService.sendLazy("bs-version.open-folder", { args: state });
const verifyFiles = () => bsDownloaderService.download(state, true);
const uninstall = async () => {
const modalCompleted = await modalService.openModal(UninstallModal, state);
if(modalCompleted.exitCode === ModalExitCode.COMPLETED){
if (modalCompleted.exitCode === ModalExitCode.COMPLETED) {
bsUninstallerService.uninstall(state).then(() => {
bsVersionManagerService.askInstalledVersions().then(versions => {
navigateToVersion(versions?.at(0));
});
})
});
}
}
};
const edit = () => {
bsVersionManagerService.editVersion(state).then(newVersion => {
if(!newVersion){ return; }
if (!newVersion) {
return;
}
navigateToVersion(newVersion);
});
}
};
const clone = () => {
bsVersionManagerService.cloneVersion(state).then(newVersion => {
if(!newVersion){ return; }
if (!newVersion) {
return;
}
navigateToVersion(newVersion);
});
}
};
const handleModsDisclaimerDecline = () => {
setCurrentTabIndex(() => 0);
}
};
const openShareFolderModal = () => {
modalService.openModal(ShareFoldersModal, state);
}
};
return (
<>
<BsmImage className="absolute w-full h-full top-0 left-0 object-cover" image={state.ReleaseImg || DefautVersionImage} errorImage={DefautVersionImage}/>
<div className="relative flex items-center flex-col w-full h-full text-gray-200 backdrop-blur-lg">
<TabNavBar className='my-4' tabIndex={currentTabIndex} tabsText={["misc.launch", "misc.maps", "misc.models", "misc.mods"]} onTabChange={(i : number) => setCurrentTabIndex(i)}/>
<div className='w-full min-h-0 grow flex transition-transform duration-300' style={{transform: `translate(${-(currentTabIndex * 100)}%, 0)`}}>
<LaunchSlide version={state}/>
<div className="w-full shrink-0 px-3 pb-3 flex flex-col items-center">
<MapsPlaylistsPanel version={state} isActive={currentTabIndex === 1}/>
</div>
<div className="w-full shrink-0 px-3 pb-3 flex flex-col items-center">
<ModelsPanel version={state} isActive={currentTabIndex === 2} goToMods={() => setCurrentTabIndex(() => 3)}/>
</div>
<ModsSlide version={state} onDisclamerDecline={handleModsDisclaimerDecline}/>
</div>
</div>
<BsmDropdownButton className='absolute top-3 right-4 h-9 w-9 bg-light-main-color-2 dark:bg-main-color-2 rounded-md' items={[
{text: "pages.version-viewer.dropdown.open-folder", icon: "folder", onClick: openFolder},
((!state.steam && !state.oculus) && {text: "pages.version-viewer.dropdown.verify-files", icon: "task", onClick: verifyFiles}),
((!state.steam && !state.oculus) && {text: "pages.version-viewer.dropdown.edit", icon: "edit", onClick: edit}),
(!state.oculus && {text: "pages.version-viewer.dropdown.clone", icon: "copy", onClick: clone}),
{text: "pages.version-viewer.dropdown.shared-folders", icon: "link", onClick: openShareFolderModal},
((!state.steam && !state.oculus) && {text: "pages.version-viewer.dropdown.uninstall", icon:"trash", onClick: uninstall})
]}/>
</>
)
}
return (
<>
<BsmImage className="absolute w-full h-full top-0 left-0 object-cover" image={state.ReleaseImg || DefautVersionImage} errorImage={DefautVersionImage} />
<div className="relative flex items-center flex-col w-full h-full text-gray-200 backdrop-blur-lg">
<TabNavBar className="my-4" tabIndex={currentTabIndex} tabsText={["misc.launch", "misc.maps", "misc.models", "misc.mods"]} onTabChange={(i: number) => setCurrentTabIndex(i)} />
<div className="w-full min-h-0 grow flex transition-transform duration-300" style={{ transform: `translate(${-(currentTabIndex * 100)}%, 0)` }}>
<LaunchSlide version={state} />
<div className="w-full shrink-0 px-3 pb-3 flex flex-col items-center">
<MapsPlaylistsPanel version={state} isActive={currentTabIndex === 1} />
</div>
<div className="w-full shrink-0 px-3 pb-3 flex flex-col items-center">
<ModelsPanel version={state} isActive={currentTabIndex === 2} goToMods={() => setCurrentTabIndex(() => 3)} />
</div>
<ModsSlide version={state} onDisclamerDecline={handleModsDisclaimerDecline} />
</div>
</div>
<BsmDropdownButton className="absolute top-3 right-4 h-9 w-9 bg-light-main-color-2 dark:bg-main-color-2 rounded-md" items={[{ text: "pages.version-viewer.dropdown.open-folder", icon: "folder", onClick: openFolder }, !state.steam && !state.oculus && { text: "pages.version-viewer.dropdown.verify-files", icon: "task", onClick: verifyFiles }, !state.steam && !state.oculus && { text: "pages.version-viewer.dropdown.edit", icon: "edit", onClick: edit }, !state.oculus && { text: "pages.version-viewer.dropdown.clone", icon: "copy", onClick: clone }, { text: "pages.version-viewer.dropdown.shared-folders", icon: "link", onClick: openShareFolderModal }, !state.steam && !state.oculus && { text: "pages.version-viewer.dropdown.uninstall", icon: "trash", onClick: uninstall }]} />
</>
);
}