mirror of
https://github.com/Zagrios/bs-manager.git
synced 2026-07-03 14:08:25 +02:00
Binary file not shown.
|
Before Width: | Height: | Size: 260 KiB |
@@ -8,7 +8,9 @@
|
||||
"playlists": "Playlists",
|
||||
"models": "Models",
|
||||
"cancel": "Cancel",
|
||||
"delete": "Delete"
|
||||
"delete": "Delete",
|
||||
"accept": "Accept",
|
||||
"refuse": "Refuse"
|
||||
},
|
||||
"pages": {
|
||||
"version-viewer": {
|
||||
@@ -134,6 +136,9 @@
|
||||
"supporters": "Supporters"
|
||||
}
|
||||
},
|
||||
"discord": {
|
||||
"description": "Join the BSManager community by joining our Discord!"
|
||||
},
|
||||
"contribution": {
|
||||
"description": "Suggest new features or report a bug to help improve BSManager!",
|
||||
"buttons": {
|
||||
@@ -249,6 +254,14 @@
|
||||
"error":{
|
||||
"description":"An unknown error occurred."
|
||||
}
|
||||
},
|
||||
"check-all-enabled":{
|
||||
"title": "OneClick Disabled",
|
||||
"description": "One or more OneClick installations are disabled. Go to settings to enable them.",
|
||||
"actions":{
|
||||
"settings": "Settings",
|
||||
"not-remind": "Do not remind me"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -481,6 +494,14 @@
|
||||
"download-maps": {
|
||||
"search-btn": "Search",
|
||||
"loading-maps": "Loading maps..."
|
||||
},
|
||||
"mods-disclaimer":{
|
||||
"title": "disclaimer",
|
||||
"p-1": "By choosing to use mods, you understand that:",
|
||||
"li-1": "You may experience problems that don't exist in the vanilla game. 99.9% of bugs, crashes, and lag are due to mods.",
|
||||
"li-2": "Mods are subject to being broken by updates and that's normal - be patient and respectful when this happens, as modders are volunteers with real lives.",
|
||||
"li-3": "Beat Games aren't purposefully trying to break mods. They wish to work on the codebase and sometimes this breaks mods, but they are not out to kill mods.",
|
||||
"p-2": "Do not attack the game developers for issues related to mods, and vice versa - modders and game developers are two separate groups. Just don't be a jerk ok."
|
||||
}
|
||||
},
|
||||
"maps": {
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
"playlists": "Playlists",
|
||||
"models": "Modelos",
|
||||
"cancel": "Cancelar",
|
||||
"delete": "Borrar"
|
||||
"delete": "Borrar",
|
||||
"accept": "Aceptar",
|
||||
"refuse": "Rechazar"
|
||||
},
|
||||
"pages": {
|
||||
"version-viewer": {
|
||||
@@ -134,6 +136,9 @@
|
||||
"supporters": "Apoyos"
|
||||
}
|
||||
},
|
||||
"discord": {
|
||||
"description": "¡Únete a la comunidad de BSManager uniéndote a nuestro Discord!"
|
||||
},
|
||||
"contribution": {
|
||||
"description": "¡Sugiera nuevas características o reporte un error para ayudar a mejorar BSManager!",
|
||||
"buttons": {
|
||||
@@ -249,6 +254,14 @@
|
||||
"error":{
|
||||
"description":"Ocurrió un error desconocido."
|
||||
}
|
||||
},
|
||||
"check-all-enabled":{
|
||||
"title": "OneClick deshabilitado",
|
||||
"description": "Una o más instalaciones de OneClick están deshabilitadas. Vaya a la configuración para habilitarlas.",
|
||||
"actions":{
|
||||
"settings": "Ajustes",
|
||||
"not-remind": "No volver a recordarme"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -481,6 +494,14 @@
|
||||
"download-maps": {
|
||||
"search-btn": "buscar",
|
||||
"loading-maps": "Cargando mapas..."
|
||||
},
|
||||
"mods-disclaimer":{
|
||||
"title": "descargo de responsabilidad",
|
||||
"p-1": "Al elegir usar mods, entiendes y aceptas que:",
|
||||
"li-1": "Es posible que encuentres problemas que no existen en el juego base. El 99,9% de los errores, bloqueos y ralentizaciones son debidos a los mods.",
|
||||
"li-2": "Es probable que los mods dejen de funcionar debido a las actualizaciones y eso es normal. Sé paciente y respetuoso cuando suceda esto, ya que los modders son voluntarios y tienen una vida.",
|
||||
"li-3": "Beat Games no está intencionalmente tratando de romper los mods. Quieren mejorar el juego base y a veces esto rompe los mods, pero no es su objetivo matar a los mods.",
|
||||
"p-2": "No ataques a los desarrolladores por problemas relacionados con los mods y viceversa. Los modders y los desarrolladores son dos grupos distintos. No seas tonto, ¿de acuerdo?"
|
||||
}
|
||||
},
|
||||
"maps": {
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
"playlists": "Playlists",
|
||||
"models": "Modèles",
|
||||
"cancel": "Annuler",
|
||||
"delete": "Supprimer"
|
||||
"delete": "Supprimer",
|
||||
"accept": "Accepter",
|
||||
"refuse": "Refuser"
|
||||
},
|
||||
"pages": {
|
||||
"version-viewer": {
|
||||
@@ -134,6 +136,9 @@
|
||||
"supporters": "Supporteurs"
|
||||
}
|
||||
},
|
||||
"discord":{
|
||||
"description": "Prends part à la communauté de BSManager en rejoignant notre Discord !"
|
||||
},
|
||||
"contribution": {
|
||||
"description": "Propose de nouvelles fonctionnalités ou signale un bug pour contribuer à l'amélioration de BSManager !",
|
||||
"buttons": {
|
||||
@@ -249,6 +254,14 @@
|
||||
"error":{
|
||||
"description":"Une erreur inconnue s'est produite."
|
||||
}
|
||||
},
|
||||
"check-all-enabled":{
|
||||
"title": "OneClick désactivée(s)",
|
||||
"description": "Une ou plusieurs installations OneClick sont désactivées. Rendez-vous dans les paramètres pour les activer.",
|
||||
"actions":{
|
||||
"settings": "Paramètres",
|
||||
"not-remind": "Ne plus me rappeler"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -481,6 +494,14 @@
|
||||
"download-maps": {
|
||||
"search-btn": "Rechercher",
|
||||
"loading-maps": "Chargement des maps..."
|
||||
},
|
||||
"mods-disclaimer":{
|
||||
"title": "avis de non-responsabilité",
|
||||
"p-1": "En choisissant d'utiliser des mods, vous comprenez et acceptez que :",
|
||||
"li-1": "Vous pouvez rencontrer des problèmes qui n'existent pas dans le jeu de base. 99.9% des bugs, plantages et ralentissements sont dus aux mods.",
|
||||
"li-2": "Les mods sont susceptibles de ne plus fonctionner à cause des mises à jour et c'est normal - soyez patient et respectueux lorsque cela se produit, parce que les moddeurs sont bénévoles et ont une vie.",
|
||||
"li-3": "Beat Games ne cherche pas volontairement à casser les mods. Ils souhaitent améliorer la base du jeu et parfois cela casse les mods, mais ce n'est pas dans leur objectif de tuer les mods.",
|
||||
"p-2": "N'attaquez pas les développeurs pour des problèmes relatifs aux mods, et inversement - les moddeurs et les développeurs sont deux groupes distincts. Ne sois pas idiot, d'accord ?"
|
||||
}
|
||||
},
|
||||
"maps": {
|
||||
|
||||
Generated
+5
-10
@@ -10354,7 +10354,8 @@
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
|
||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||
"dev": true
|
||||
},
|
||||
"object-hash": {
|
||||
"version": "3.0.0",
|
||||
@@ -11280,6 +11281,7 @@
|
||||
"version": "15.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"loose-envify": "^1.4.0",
|
||||
"object-assign": "^4.1.1",
|
||||
@@ -11672,7 +11674,8 @@
|
||||
"react-is": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
||||
"dev": true
|
||||
},
|
||||
"react-range": {
|
||||
"version": "1.8.14",
|
||||
@@ -11737,14 +11740,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-visibility-sensor": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/react-visibility-sensor/-/react-visibility-sensor-5.1.1.tgz",
|
||||
"integrity": "sha512-cTUHqIK+zDYpeK19rzW6zF9YfT4486TIgizZW53wEZ+/GPBbK7cNS0EHyJVyHYacwFEvvHLEKfgJndbemWhB/w==",
|
||||
"requires": {
|
||||
"prop-types": "^15.7.2"
|
||||
}
|
||||
},
|
||||
"read-cache": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||
|
||||
+2
-2
@@ -76,7 +76,8 @@
|
||||
"target": [
|
||||
"nsis"
|
||||
],
|
||||
"icon": "assets/favicon.ico"
|
||||
"icon": "assets/favicon.ico",
|
||||
"certificateSha1": "f3363a89b084f8f6b616babea3c91f9d79e15000"
|
||||
},
|
||||
"linux": {
|
||||
"target": [
|
||||
@@ -244,7 +245,6 @@
|
||||
"react-dom": "^18.2.0",
|
||||
"react-range": "^1.8.14",
|
||||
"react-router-dom": "^6.3.0",
|
||||
"react-visibility-sensor": "^5.1.1",
|
||||
"regedit": "^5.1.1",
|
||||
"rxjs": "^7.5.6",
|
||||
"sanitize-filename": "^1.6.3",
|
||||
|
||||
@@ -5,6 +5,7 @@ import { IpcRequest } from 'shared/models/ipc';
|
||||
import { InstallationLocationService } from '../services/installation-location.service';
|
||||
import { UtilsService } from '../services/utils.service';
|
||||
import { BsmException } from 'shared/models/bsm-exception.model';
|
||||
import { LocalMapsManagerService } from '../services/additional-content/local-maps-manager.service';
|
||||
|
||||
|
||||
export interface InitDownloadInfoInterface {
|
||||
@@ -25,7 +26,8 @@ export interface DownloadInfo {
|
||||
}
|
||||
|
||||
ipcMain.on('bs-download.start', async (event, request: IpcRequest<DownloadInfo>) => {
|
||||
BSInstallerService.getInstance().downloadBsVersion(request.args).then(res => {
|
||||
BSInstallerService.getInstance().downloadBsVersion(request.args).then(async res => {
|
||||
await LocalMapsManagerService.getInstance().linkVersionMaps(request.args.bsVersion, true).catch(e => {});
|
||||
UtilsService.getInstance().ipcSend(request.responceChannel, {success: true, data: res});
|
||||
}).catch(e => {
|
||||
UtilsService.getInstance().ipcSend(request.responceChannel, {success: false, data: e});
|
||||
|
||||
@@ -23,7 +23,7 @@ export function AvailableVersionsSlider() {
|
||||
|
||||
return (
|
||||
<div className="w-full h-fit max-h-full flex flex-col items-center grow min-h-0">
|
||||
<TabNavBar className="mb-3" tabsText={availableYears} onTabChange={setSelectedYear}/>
|
||||
<TabNavBar className="mb-3" tabIndex={yearIndex} tabsText={availableYears} onTabChange={setSelectedYear}/>
|
||||
<ol className="w-full min-h-0 flex transition-transform duration-300" style={{transform: `translate(${-(yearIndex * 100)}%, 0)`}}>
|
||||
{ availableYears.map(year =>
|
||||
<AvailableVersionsSlide key={year} year={year}/>
|
||||
|
||||
+2
-2
@@ -82,7 +82,7 @@ export function MapsPlaylistsPanel({version}: Props) {
|
||||
{index === 0 &&(
|
||||
<div className="h-full flex absolute right-0 top-0 gap-1.5 items-center pr-2">
|
||||
{isOnline && (
|
||||
<motion.div whileHover={"hover"} whileTap={"tap"} className="relative h-[calc(100%-5px)] flex flex-row justify-center items-center shrink-0 rounded-full overflow-hidden pr-2" style={{color}} onClick={e => {e.stopPropagation(); onClickAdd(index)}}>
|
||||
<motion.div whileHover="hover" whileTap="tap" className="relative h-[calc(100%-5px)] flex flex-row justify-center items-center shrink-0 rounded-full overflow-hidden pr-2" style={{color}} onClick={e => {e.stopPropagation(); onClickAdd(index)}}>
|
||||
<span className="absolute top-0 left-0 h-full w-full brightness-50 opacity-75 dark:opacity-20 dark:filter-none" style={{backgroundColor: "currentcolor"}}/>
|
||||
<motion.div className="h-full p-0.5" variants={variants}>
|
||||
<BsmIcon className="block h-full aspect-square brightness-150" icon="add"/>
|
||||
@@ -128,7 +128,7 @@ export function MapsPlaylistsPanel({version}: Props) {
|
||||
<BsmDropdownButton className="h-full flex aspect-square relative rounded-full z-[1] bg-light-main-color-1 dark:bg-main-color-3" buttonClassName="rounded-full h-full w-full p-[6px]" icon="three-dots" withBar={false} items={dropDownItems} menuTranslationY="6px" align="center"/>
|
||||
</nav>
|
||||
<div className="w-full h-full flex flex-col bg-light-main-color-3 dark:bg-main-color-2 rounded-md shadow-black shadow-md overflow-hidden">
|
||||
<TabNavBar className="!rounded-none shadow-sm" tabsText={["misc.maps", "misc.playlists"]} onTabChange={setTabIndex} renderTab={renderTab}/>
|
||||
<TabNavBar className="!rounded-none shadow-sm" tabIndex={tabIndex} tabsText={["misc.maps", "misc.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 ref={mapsRef} className="w-full h-full shrink-0 flex flex-col" version={version} filter={mapFilter} search={mapSearch}/>
|
||||
<div className="w-full h-full shrink-0 flex flex-col justify-center items-center content-center gap-2 overflow-hidden">
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { BsmButton } from "renderer/components/shared/bsm-button.component";
|
||||
import { BsmImage } from "renderer/components/shared/bsm-image.component";
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
import { ModalComponent, ModalExitCode } from "renderer/services/modale.service";
|
||||
import BeatConflict from '../../../../../assets/images/apngs/beat-conflict.png'
|
||||
|
||||
export const ModsDisclaimerModal: ModalComponent<void, void> = ({resolver}) => {
|
||||
|
||||
const t = useTranslation();
|
||||
|
||||
return (
|
||||
<form className="max-w-[800px] text-gray-800 dark:text-gray-200">
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center text-gray-800 dark:text-gray-200">{t("modals.mods-disclaimer.title")}</h1>
|
||||
<BsmImage className="mx-auto h-20" image={BeatConflict} />
|
||||
<p className="mb-1">{t("modals.mods-disclaimer.p-1")}</p>
|
||||
<ul className="list-disc w-full pl-6">
|
||||
<li>{t("modals.mods-disclaimer.li-1")}</li>
|
||||
<li>{t("modals.mods-disclaimer.li-2")}</li>
|
||||
<li>{t("modals.mods-disclaimer.li-3")}</li>
|
||||
</ul>
|
||||
<p className="mt-1">{t("modals.mods-disclaimer.p-2")}</p>
|
||||
<div className="grid grid-flow-col grid-cols-2 gap-4 pt-3">
|
||||
<BsmButton typeColor="cancel" className="rounded-md text-center transition-all" onClick={() => {resolver({exitCode: ModalExitCode.CANCELED})}} withBar={false} text="misc.refuse"/>
|
||||
<BsmButton typeColor="primary" className="rounded-md text-center transition-all" onClick={() => {resolver({exitCode: ModalExitCode.COMPLETED})}} withBar={false} text="misc.accept"/>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
@@ -37,7 +37,7 @@ export function NotificationItem({resolver, notification}: {resolver?: (value: N
|
||||
}
|
||||
|
||||
return (
|
||||
<motion.li layout drag="x" dragConstraints={{left: 0, right: 0}} dragElastic={{left: 0, right: .3}} onDragEnd={handleDragEnd} initial={{x: "110%"}} animate={{x: "0%"}} exit={{x:"110%"}} className="relative w-[270px] rounded-md overflow-hidden -left-[285px] mb-4 cursor-grab active:cursor-grabbing shadow-md shadow-black bg-gradient-to-br from-light-main-color-2 to-light-main-color-2 dark:from-main-color-2 dark:to-main-color-2 text-gray-800 dark:text-white">
|
||||
<motion.li layout drag="x" dragConstraints={{left: 0, right: 0}} dragElastic={{left: 0, right: .3}} onDragEnd={handleDragEnd} initial={{x: "110%"}} animate={{x: "0%"}} exit={{x:"110%"}} className="relative w-[290px] rounded-md overflow-hidden -left-[300px] mb-4 cursor-grab active:cursor-grabbing shadow-md shadow-black bg-gradient-to-br from-light-main-color-2 to-light-main-color-2 dark:from-main-color-2 dark:to-main-color-2 text-gray-800 dark:text-white">
|
||||
<div className="w-full flex flex-col">
|
||||
<div className="flex items-center justify-start pl-1">
|
||||
<BsmImage className="h-14 mr-1 pointer-events-none" image={renderImage}/>
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
import { ReactNode } from "react";
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
|
||||
export function SettingContainer({className, title, minorTitle, description, children}: {className?: string, title?: string, minorTitle?: string, description?: string, children?: ReactNode}) {
|
||||
type Props = {
|
||||
id?: string,
|
||||
className?: string,
|
||||
title?: string,
|
||||
minorTitle?: string,
|
||||
description?: string,
|
||||
children?: ReactNode
|
||||
}
|
||||
|
||||
export function SettingContainer({id, className, title, minorTitle, description, children}: Props) {
|
||||
|
||||
const t = useTranslation();
|
||||
|
||||
return (
|
||||
<div className={className || "relative mb-5"}>
|
||||
<div id={id} className={className || "relative mb-5"}>
|
||||
{ title && <h1 className="font-bold text-2xl mb-1 tracking-wide">{t(title)}</h1> }
|
||||
{ minorTitle && <h2 className="font-bold mb-1 tracking-wide text-gray-600 dark:text-gray-300">{t(minorTitle)}</h2> }
|
||||
{ description && <p className="mb-3 text-sm text-gray-600 dark:text-gray-400">{t(description)}</p> }
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
import {motion, useCycle} from "framer-motion"
|
||||
import { useState } from "react"
|
||||
|
||||
type Props = {className?: string, nbClickToShow?: number, children?: JSX.Element}
|
||||
|
||||
export function SpoilerClick({className, children, nbClickToShow = 5}: Props) {
|
||||
|
||||
const [remainingClicks, setRemainingClicks] = useState(nbClickToShow);
|
||||
const [translateY, setTranslateY] = useState("100%");
|
||||
|
||||
const handleClicks = () => {
|
||||
if(remainingClicks - 1 > 0){ setRemainingClicks(nb => nb - 1); }
|
||||
setTranslateY(`${(((remainingClicks - 1) / nbClickToShow) * 100)}%`)
|
||||
}
|
||||
|
||||
return (
|
||||
<motion.div className={className} initial={{y: translateY}} animate={{y: translateY}} transition={{type: "spring", stiffness: 300, mass: 1 }} onClick={handleClicks}>
|
||||
{children}
|
||||
</motion.div>
|
||||
)
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
import { DetailedHTMLProps, Fragment, useState } from "react";
|
||||
import { DetailedHTMLProps, Fragment } from "react";
|
||||
import { useThemeColor } from "renderer/hooks/use-theme-color.hook";
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
|
||||
type Props = {
|
||||
tabIndex: number
|
||||
tabsText: string[],
|
||||
onTabChange: (index: number) => void,
|
||||
className?: string,
|
||||
@@ -11,12 +12,12 @@ type Props = {
|
||||
|
||||
export function TabNavBar(props: Props) {
|
||||
|
||||
const [currentTabIndex, setCurrentTabIndex] = useState(0);
|
||||
const currentIndex = props.tabIndex ?? 0;
|
||||
|
||||
const t = useTranslation();
|
||||
const secondColor = useThemeColor("second-color");
|
||||
|
||||
const selectTab = (index: number) => {
|
||||
setCurrentTabIndex(index);
|
||||
props.onTabChange(index);
|
||||
}
|
||||
|
||||
@@ -24,7 +25,7 @@ export function TabNavBar(props: Props) {
|
||||
<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})`}}/>
|
||||
<span className="absolute h-full block bg-current transition-transform duration-300 shadow-center shadow-current" style={{transform: `translate(${currentIndex * 100}%, 0)`, width: `calc(100% / ${props.tabsText.length})`}}/>
|
||||
</div>
|
||||
<ul className="grid" style={{gridTemplateColumns: `repeat(${props.tabsText.length}, minmax(0, 1fr))`}}>
|
||||
{props.tabsText.map((text, index) => (
|
||||
|
||||
@@ -43,9 +43,10 @@ import { LinkIcon } from "./icons/link-icon.component";
|
||||
import { UnlinkIcon } from "./icons/unlink-icon.component";
|
||||
import { DownloadIcon } from "./icons/download-icon.component";
|
||||
import { FilterIcon } from "./icons/filter-icon.component";
|
||||
import DiscordIcon from "./icons/discord-icon.component";
|
||||
|
||||
export type BsmIconType = BsvMapCharacteristic | (
|
||||
"settings"|"trash"|"favorite"|"folder"|"bsNote"|"check"|"three-dots"|"twitch"|"eye"|"play"|"checkCircleIcon"|
|
||||
"settings"|"trash"|"favorite"|"folder"|"bsNote"|"check"|"three-dots"|"twitch"|"eye"|"play"|"checkCircleIcon"|"discord"|
|
||||
"terminal"|"desktop"|"oculus"|"add"|"cross"|"task"|"github"|"close"|"thumbUpFill"|"timerFill"|"pause"|
|
||||
"copy"|"steam"|"edit"|"export"|"patreon"|"search"|"bsMapDifficulty"|"link"|"unlink"|"download"|"filter"|
|
||||
"fr-FR-flag"|"es-ES-flag"|"en-US-flag"|"en-EN-flag"
|
||||
@@ -97,6 +98,7 @@ export const BsmIcon = memo(({className, icon, style}: {className?: string, icon
|
||||
if(icon === "unlink"){ return <UnlinkIcon className={className} style={style}/> }
|
||||
if(icon === "download"){ return <DownloadIcon className={className} style={style}/> }
|
||||
if(icon === "filter"){ return <FilterIcon className={className} style={style}/> }
|
||||
if(icon === "discord"){ return <DiscordIcon className={className} style={style}/> }
|
||||
return <TrashIcon className={className} style={style}/>
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { CSSProperties } from "react";
|
||||
|
||||
export default function DiscordIcon(props: {className?: string, style?: CSSProperties}) {
|
||||
return (
|
||||
<svg className={props.className} style={props.style} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 127.14 96.36" fill="currentColor">
|
||||
<g id="图层_2" data-name="图层 2">
|
||||
<g id="Discord_Logos" data-name="Discord Logos">
|
||||
<g id="Discord_Logo_-_Large_-_White" data-name="Discord Logo - Large - White">
|
||||
<path d="M107.7,8.07A105.15,105.15,0,0,0,81.47,0a72.06,72.06,0,0,0-3.36,6.83A97.68,97.68,0,0,0,49,6.83,72.37,72.37,0,0,0,45.64,0,105.89,105.89,0,0,0,19.39,8.09C2.79,32.65-1.71,56.6.54,80.21h0A105.73,105.73,0,0,0,32.71,96.36,77.7,77.7,0,0,0,39.6,85.25a68.42,68.42,0,0,1-10.85-5.18c.91-.66,1.8-1.34,2.66-2a75.57,75.57,0,0,0,64.32,0c.87.71,1.76,1.39,2.66,2a68.68,68.68,0,0,1-10.87,5.19,77,77,0,0,0,6.89,11.1A105.25,105.25,0,0,0,126.6,80.22h0C129.24,52.84,122.09,29.11,107.7,8.07ZM42.45,65.69C36.18,65.69,31,60,31,53s5-12.74,11.43-12.74S54,46,53.89,53,48.84,65.69,42.45,65.69Zm42.24,0C78.41,65.69,73.25,60,73.25,53s5-12.74,11.44-12.74S96.23,46,96.12,53,91.08,65.69,84.69,65.69Z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -1,28 +1,32 @@
|
||||
import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
||||
import { BsModsManagerService } from "renderer/services/bs-mods-manager.service";
|
||||
import { BSVersion } from "shared/bs-version.interface";
|
||||
import VisibilitySensor from 'react-visibility-sensor';
|
||||
import { Mod } from "shared/models/mods/mod.interface";
|
||||
import { ModsGrid } from "./mods-grid.component";
|
||||
import { ConfigurationService } from "renderer/services/configuration.service";
|
||||
import { DefaultConfigKey } from "renderer/config/default-configuration.config";
|
||||
import { BsmButton } from "renderer/components/shared/bsm-button.component";
|
||||
import BeatWaitingImg from "../../../../../../assets/images/apngs/beat-waiting.png"
|
||||
import { SpoilerClick } from "renderer/components/shared/UwU/spoiler-click.component";
|
||||
import YuruYuriDance from "../../../../../../assets/images/gifs/yuruyuri-dance.gif"
|
||||
import { useObservable } from "renderer/hooks/use-observable.hook";
|
||||
import { skip, filter } from "rxjs/operators";
|
||||
import { Subscription } from "rxjs";
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
import { LinkOpenerService } from "renderer/services/link-opener.service";
|
||||
import { useInView } from "framer-motion";
|
||||
import { ModalExitCode, ModalService } from "renderer/services/modale.service";
|
||||
import { ModsDisclaimerModal } from "renderer/components/modal/modal-types/mods-disclaimer-modal.component";
|
||||
|
||||
export function ModsSlide({version}: {version: BSVersion}) {
|
||||
export function ModsSlide({version, onDisclamerDecline}: {version: BSVersion, onDisclamerDecline: () => void}) {
|
||||
|
||||
const ACCEPTED_DISCLAIMER_KEY = "accepted-mods-disclaimer";
|
||||
|
||||
const modsManager = BsModsManagerService.getInstance();
|
||||
const configService = ConfigurationService.getInstance();
|
||||
const linkOpener = LinkOpenerService.getInstance();
|
||||
const modals = ModalService.getInsance();
|
||||
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const ref = useRef(null);
|
||||
const isVisible = useInView(ref, {amount: .5});
|
||||
const [modsAvailable, setModsAvailable] = useState(null as Map<string, Mod[]>);
|
||||
const [modsInstalled, setModsInstalled] = useState(null as Map<string, Mod[]>);
|
||||
const [modsSelected, setModsSelected] = useState([] as Mod[]);
|
||||
@@ -81,10 +85,27 @@ export function ModsSlide({version}: {version: BSVersion}) {
|
||||
const subs: Subscription[] = [];
|
||||
|
||||
if(isVisible){
|
||||
loadMods();
|
||||
subs.push(modsManager.isUninstalling$.pipe(skip(1), filter(uninstalling => !uninstalling)).subscribe(() => {
|
||||
|
||||
const promise = new Promise<boolean>(async resolve => {
|
||||
|
||||
if(configService.get<boolean>(ACCEPTED_DISCLAIMER_KEY)){ return resolve(true); }
|
||||
|
||||
const res = await modals.openModal(ModsDisclaimerModal);
|
||||
const haveAccepted = res.exitCode === ModalExitCode.COMPLETED;
|
||||
|
||||
if(haveAccepted){ configService.set(ACCEPTED_DISCLAIMER_KEY, true) }
|
||||
|
||||
resolve(haveAccepted);
|
||||
|
||||
});
|
||||
|
||||
promise.then(canLoad => {
|
||||
if(!canLoad){ return onDisclamerDecline?.(); }
|
||||
loadMods();
|
||||
}))
|
||||
subs.push(modsManager.isUninstalling$.pipe(skip(1), filter(uninstalling => !uninstalling)).subscribe(() => {
|
||||
loadMods();
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
return () => {
|
||||
@@ -105,33 +126,28 @@ export function ModsSlide({version}: {version: BSVersion}) {
|
||||
|
||||
|
||||
return (
|
||||
<VisibilitySensor onChange={setIsVisible}>
|
||||
<div className='shrink-0 w-full h-full px-8 pb-7 flex justify-center'>
|
||||
<div className='relative flex flex-col grow-0 bg-light-main-color-2 dark:bg-main-color-2 h-full w-full rounded-md shadow-black shadow-center overflow-hidden'>
|
||||
{modsAvailable ? (
|
||||
<>
|
||||
<div className="overflow-scroll w-full min-h-0 scrollbar-thin scrollbar-thumb-neutral-900 scrollbar-thumb-rounded-full">
|
||||
<ModsGrid modsMap={modsAvailable} installed={modsInstalled} modsSelected={modsSelected} onModChange={handleModChange} moreInfoMod={moreInfoMod} onWantInfos={handleMoreInfo}/>
|
||||
</div>
|
||||
<div className="h-10 shrink-0 flex items-center justify-between px-3">
|
||||
<BsmButton className="text-center rounded-md px-2 py-[2px]" text="pages.version-viewer.mods.buttons.more-infos" typeColor="cancel" withBar={false} disabled={!moreInfoMod} onClick={handleOpenMoreInfo} style={{width: downloadWith}}/>
|
||||
<div ref={downloadRef}>
|
||||
<BsmButton className="text-center rounded-md px-2 py-[2px]" text="pages.version-viewer.mods.buttons.install-or-update" withBar={false} disabled={installing} typeColor="primary" onClick={installMods}/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="w-full h-full flex flex-col items-center justify-center relative">
|
||||
<img className="w-32 h-32 spin-loading" src={BeatWaitingImg}></img>
|
||||
<span className="text-xl mt-3 h-0 italic">{t("pages.version-viewer.mods.loading-mods")}</span>
|
||||
<SpoilerClick className="absolute right-5 w-20 h-[120px] bottom-5 cursor-pointer">
|
||||
<img className="relative w-full" src={YuruYuriDance}/>
|
||||
</SpoilerClick>
|
||||
<div ref={ref} className='shrink-0 w-full h-full px-8 pb-7 flex justify-center'>
|
||||
<div className='relative flex flex-col grow-0 bg-light-main-color-2 dark:bg-main-color-2 h-full w-full rounded-md shadow-black shadow-center overflow-hidden'>
|
||||
{modsAvailable ? (
|
||||
<>
|
||||
<div className="overflow-scroll w-full min-h-0 scrollbar-thin scrollbar-thumb-neutral-900 scrollbar-thumb-rounded-full">
|
||||
<ModsGrid modsMap={modsAvailable} installed={modsInstalled} modsSelected={modsSelected} onModChange={handleModChange} moreInfoMod={moreInfoMod} onWantInfos={handleMoreInfo}/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="h-10 shrink-0 flex items-center justify-between px-3">
|
||||
<BsmButton className="text-center rounded-md px-2 py-[2px]" text="pages.version-viewer.mods.buttons.more-infos" typeColor="cancel" withBar={false} disabled={!moreInfoMod} onClick={handleOpenMoreInfo} style={{width: downloadWith}}/>
|
||||
<div ref={downloadRef}>
|
||||
<BsmButton className="text-center rounded-md px-2 py-[2px]" text="pages.version-viewer.mods.buttons.install-or-update" withBar={false} disabled={installing} typeColor="primary" onClick={installMods}/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="w-full h-full flex flex-col items-center justify-center">
|
||||
<img className="w-32 h-32 spin-loading" src={BeatWaitingImg} alt=" "/>
|
||||
<span className="text-xl mt-3 h-0 italic">{t("pages.version-viewer.mods.loading-mods")}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</VisibilitySensor>
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
@@ -147,6 +147,7 @@ export function SettingsPage() {
|
||||
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 openLogs = () => ipcService.sendLazy("open-logs");
|
||||
|
||||
@@ -228,7 +229,7 @@ export function SettingsPage() {
|
||||
|
||||
<SettingContainer title="pages.settings.additional-content.title" description="pages.settings.additional-content.description">
|
||||
|
||||
<SettingContainer minorTitle="pages.settings.additional-content.deep-links.sub-title">
|
||||
<SettingContainer id="one-clicks" minorTitle="pages.settings.additional-content.deep-links.sub-title">
|
||||
<ul className="w-full flex flex-col gap-1.5">
|
||||
<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">
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -283,6 +284,9 @@ export function SettingsPage() {
|
||||
<BsmButton className="flex w-fit rounded-md h-8 px-2 font-bold py-1 whitespace-nowrap mr-2 !text-white" iconClassName="mr-1" text="pages.settings.patreon.buttons.support" icon="patreon" color="#EC6350" withBar={false} onClick={openPatreonPage}/>
|
||||
<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">
|
||||
<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}/>
|
||||
</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">
|
||||
|
||||
@@ -57,6 +57,10 @@ export function VersionViewer() {
|
||||
});
|
||||
}
|
||||
|
||||
const handleModsDisclaimerDecline = () => {
|
||||
setCurrentTabIndex(() => 0);
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -64,13 +68,13 @@ export function VersionViewer() {
|
||||
<div className="relative flex items-center flex-col w-full h-full text-gray-200 backdrop-blur-lg">
|
||||
<BsmImage className='relative object-cover h-28' image={BSLogo}/>
|
||||
<h1 className='relative text-4xl font-bold italic -top-3'>{state.name ? `${state.BSVersion} - ${state.name}` : state.BSVersion}</h1>
|
||||
<TabNavBar className='my-3' tabsText={["misc.launch", "misc.maps", "misc.mods"]} onTabChange={(i : number) => setCurrentTabIndex(i)}/>
|
||||
<TabNavBar className='my-3' tabIndex={currentTabIndex} tabsText={["misc.launch", "misc.maps", "misc.mods"]} onTabChange={(i : number) => setCurrentTabIndex(i)}/>
|
||||
<div className='mt-2 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}/>
|
||||
</div>
|
||||
<ModsSlide version={state}/>
|
||||
<ModsSlide version={state} onDisclamerDecline={handleModsDisclaimerDecline}/>
|
||||
</div>
|
||||
</div>
|
||||
<BsmDropdownButton className='absolute top-5 right-5 h-9 w-9 bg-light-main-color-2 dark:bg-main-color-2 rounded-md' items={[
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { NavBar } from "../components/nav-bar/nav-bar.component";
|
||||
import TitleBar from "../components/title-bar/title-bar.component";
|
||||
import { Routes, Route, useLocation } from "react-router-dom";
|
||||
import { Routes, Route, useLocation, useNavigate } from "react-router-dom";
|
||||
import { AvailableVersionsList } from "../pages/available-versions-list.components";
|
||||
import { VersionViewer } from "../pages/version-viewer.component";
|
||||
import { Modal } from "../components/modal/modal.component";
|
||||
@@ -14,24 +14,83 @@ import { MapsPage } from "../pages/maps-page.component";
|
||||
import "tailwindcss/tailwind.css";
|
||||
import { BsmIframeView } from "../components/shared/bsm-map-preview.component";
|
||||
import 'tippy.js/dist/tippy.css';
|
||||
import { MapsManagerService } from "renderer/services/maps-manager.service";
|
||||
import { PlaylistsManagerService } from "renderer/services/playlists-manager.service";
|
||||
import { ModelsManagerService } from "renderer/services/models-manager.service";
|
||||
import { NotificationService } from "renderer/services/notification.service";
|
||||
import { timer } from "rxjs";
|
||||
import { ConfigurationService } from "renderer/services/configuration.service";
|
||||
|
||||
export default function App() {
|
||||
|
||||
const themeService = ThemeService.getInstance();
|
||||
const pageState = PageStateService.getInstance();
|
||||
const maps = MapsManagerService.getInstance();
|
||||
const playlists = PlaylistsManagerService.getInstance();
|
||||
const models = ModelsManagerService.getInstance();
|
||||
const notification = NotificationService.getInstance();
|
||||
const config = ConfigurationService.getInstance();
|
||||
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
themeService.theme$.subscribe(() => {
|
||||
if(themeService.isDark || (themeService.isOS && window.matchMedia('(prefers-color-scheme: dark)').matches)){ return document.documentElement.classList.add('dark'); }
|
||||
document.documentElement.classList.remove('dark');
|
||||
});
|
||||
|
||||
checkOneClicks();
|
||||
|
||||
}, []);
|
||||
|
||||
const checkOneClicks = async () => {
|
||||
|
||||
if(config.get("not-remind-oneclick") === true){ return; }
|
||||
|
||||
await timer(2_000).toPromise();
|
||||
|
||||
const oneClicks = await Promise.all([
|
||||
maps.isDeepLinksEnabled(),
|
||||
playlists.isDeepLinksEnabled(),
|
||||
models.isDeepLinksEnabled()
|
||||
]);
|
||||
|
||||
if(!oneClicks.some(enabled => enabled === false)){ return; }
|
||||
|
||||
const choice = await notification.notifyWarning({
|
||||
title: "notifications.settings.additional-content.deep-link.check-all-enabled.title",
|
||||
desc: "notifications.settings.additional-content.deep-link.check-all-enabled.description",
|
||||
duration: 10_000,
|
||||
actions: [
|
||||
{id: "0", title: "notifications.settings.additional-content.deep-link.check-all-enabled.actions.settings"},
|
||||
{id: "1", title: "notifications.settings.additional-content.deep-link.check-all-enabled.actions.not-remind", cancel: true}
|
||||
]
|
||||
});
|
||||
|
||||
if(choice === "0"){
|
||||
return navigate("/settings#one-clicks");
|
||||
}
|
||||
|
||||
if(choice === "1"){
|
||||
config.set("not-remind-oneclick", true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
pageState.setLocation(location);
|
||||
|
||||
if(!location.hash){ return; }
|
||||
|
||||
const hash = location.hash.replace("#", "");
|
||||
const el = document.getElementById(hash);
|
||||
|
||||
if(!el){ return; }
|
||||
|
||||
el.scrollIntoView({block: "start", behavior: "smooth"});
|
||||
|
||||
}, [location])
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user