deep-links models fully work AND add translations

This commit is contained in:
MathieuG-P
2022-12-25 22:18:28 +01:00
parent c674fce0c0
commit 9d7cd708c7
14 changed files with 210 additions and 26 deletions
+2 -1
View File
@@ -34,7 +34,8 @@ module.exports = {
"no-param-reassign": "off",
"no-useless-escape": "off",
"jsx-a11y/no-noninteractive-element-interactions": "off",
"no-async-promise-executor": "off"
"no-async-promise-executor": "off",
"new-cap": "off"
},
parserOptions: {
ecmaVersion: 2020,
+8
View File
@@ -6,6 +6,7 @@
"mods": "Mods",
"maps": "Maps",
"playlists":"Playlists",
"models": "Models",
"cancel": "Cancel",
"delete": "Delete"
},
@@ -94,6 +95,13 @@
"description":"Change the default folder for Beat Saber versions and other upcoming features.",
"choose-folder":"Choose folder"
},
"additional-content":{
"title":"Additional content",
"description":"Additional content allows you to customize BeatSaber!",
"deep-links":{
"sub-title":"OneClick installations"
}
},
"language":{
"title":"Language",
"description":"Select a language.",
+8
View File
@@ -6,6 +6,7 @@
"mods": "Mods",
"maps": "Mapas",
"playlists":"Playlists",
"models": "Modelos",
"cancel": "Cancelar",
"delete": "Borrar"
},
@@ -94,6 +95,13 @@
"description":"Cambia la carpeta por defecto para las versiones de Beat Saber y otras características próximas.",
"choose-folder":"Elija la carpeta"
},
"additional-content":{
"title":"Contenido adicional",
"description":"¡El contenido adicional te permite personalizar BeatSaber!",
"deep-links":{
"sub-title":"Instalaciones OneClick"
}
},
"language":{
"title":"Idioma",
"description":"Selecciona un idioma.",
+8
View File
@@ -6,6 +6,7 @@
"mods": "Mods",
"maps": "Maps",
"playlists":"Playlists",
"models": "Modèles",
"cancel": "Annuler",
"delete": "Supprimer"
},
@@ -94,6 +95,13 @@
"description":"Change le dossier par défaut pour les versions de Beat Saber et d'autres fonctionnalités à venir.",
"choose-folder":"Choisir un dossier"
},
"additional-content":{
"title":"Contenus additionnels",
"description":"Les contenus additionnels te permet de personaliser BeatSaber !",
"deep-links":{
"sub-title":"Installations OneClick"
}
},
"language":{
"title":"Langage",
"description":"Sélectionne un langage.",
+45
View File
@@ -13,4 +13,49 @@ ipcMain.on("one-click-install-model", async (event, request: IpcRequest<MSModel>
}).catch(e => {
utils.ipcSend(request.responceChannel, {success: false, error: e});
})
});
ipcMain.on("register-models-deep-link", async (event, request: IpcRequest<void>) => {
const maps = LocalModelsManagerService.getInstance();
const utils = UtilsService.getInstance();
try{
const res = maps.enableDeepLinks();
utils.ipcSend(request.responceChannel, {success: true, data: res});
}
catch(e){
utils.ipcSend(request.responceChannel, {success: false});
}
});
ipcMain.on("unregister-models-deep-link", async (event, request: IpcRequest<void>) => {
const maps = LocalModelsManagerService.getInstance();
const utils = UtilsService.getInstance();
try{
const res = maps.disableDeepLinks();
utils.ipcSend(request.responceChannel, {success: true, data: res});
}
catch(e){
utils.ipcSend(request.responceChannel, {success: false});
}
});
ipcMain.on("is-models-deep-links-enabled", async (event, request: IpcRequest<void>) => {
const maps = LocalModelsManagerService.getInstance();
const utils = UtilsService.getInstance();
try{
const res = maps.isDeepLinksEnabled();
utils.ipcSend(request.responceChannel, {success: true, data: res});
}
catch(e){
utils.ipcSend(request.responceChannel, {success: false});
}
});
+2 -1
View File
@@ -10,4 +10,5 @@ import './bs-mods-ipcs';
import './bs-maps-ipcs';
import './beat-saver-ipcs';
import './bs-playlist-ipcs';
import './model-saber.ipcs';
import './model-saber.ipcs';
import './bs-model-ipcs';
+1 -1
View File
@@ -89,7 +89,7 @@ else{
app.whenReady().then(() => {
process.argv.push("modelsaber://saber/1670981586/r_Happy Crimbo!.saber"); // to force deep-link (oneClick map)
// process.argv.push("modelsaber://platform/1671435302/The grinch mountain.plat"); // to force deep-link (oneClick map)
initServicesMustBeInitialized();
@@ -6,6 +6,10 @@ import { UtilsService } from "../utils.service";
import { WindowManagerService } from "../window-manager.service";
import { MSModel, MSModelType } from "shared/models/model-saber/model-saber.model";
import { BSVersion } from "shared/bs-version.interface";
import { BSLocalVersionService } from "../bs-local-version.service";
import path from "path";
import { RequestService } from "../request.service";
import { copyFileSync, constants } from "fs-extra";
export class LocalModelsManagerService {
@@ -20,14 +24,25 @@ export class LocalModelsManagerService {
ModelSaber: "modelsaber",
};
private readonly MODEL_TYPE_FOLDER: Record<Exclude<MSModelType, "misc">, string> = {
avatar: "CustomAvatars",
bloq: "CustomNotes",
platform: "CustomPlatforms",
saber: "CustomSabers"
}
private readonly deepLink: DeepLinkService;
private readonly utils: UtilsService;
private readonly windows: WindowManagerService;
private readonly localVersion: BSLocalVersionService;
private readonly request: RequestService;
private constructor(){
this.deepLink = DeepLinkService.getInstance();
this.utils = UtilsService.getInstance();
this.windows = WindowManagerService.getInstance();
this.localVersion = BSLocalVersionService.getInstance();
this.request = RequestService.getInstance();
this.deepLink.addLinkOpenedListener(this.DEEP_LINKS.ModelSaber, (link) => {
log.info("DEEP-LINK RECEIVED FROM", this.DEEP_LINKS.ModelSaber, link);
@@ -42,8 +57,7 @@ export class LocalModelsManagerService {
private openOneClickDownloadModelWindow(id: string, type: string){
// TODO make once
ipcMain.on("one-click-model-info", async (event, req: IpcRequest<void>) => {
ipcMain.once("one-click-model-info", async (event, req: IpcRequest<void>) => {
this.utils.ipcSend(req.responceChannel, {success: true, data: {id, type}});
});
@@ -51,16 +65,61 @@ export class LocalModelsManagerService {
}
private getModelFolderPath(type: MSModelType, version?: BSVersion): Promise<string>{
// TODO : return path of model type of version
private async getModelFolderPath(type: MSModelType, version?: BSVersion): Promise<string>{
if(!version){ throw "will be implemented whith models management" }
if(type === "misc"){ throw "model type not supported"; }
const versionPath = await this.localVersion.getVersionPath(version);
const modelFolderPath = path.join(versionPath, this.MODEL_TYPE_FOLDER[type]);
this.utils.createFolderIfNotExist(modelFolderPath);
return modelFolderPath;
}
public downloadModel(model: MSModel, version: BSVersion): Promise<string>{
// TODO : Download model to the version
public async downloadModel(model: MSModel, version: BSVersion): Promise<string>{
const modelFolder = await this.getModelFolderPath(model.type, version);
const modelDest = path.join(modelFolder, path.basename(model.download));
return this.request.downloadFile(model.download, modelDest);
}
public async oneClickDownloadModel(model: MSModel): Promise<void>{
if(!model){ return; }
const versions = await this.localVersion.getInstalledVersions();
if(versions?.length === 0){ return; }
const fisrtVersion = versions.shift();
const downloaded = await this.downloadModel(model, fisrtVersion);
for(const version of versions){
const modelDest = path.join(await this.getModelFolderPath(model.type, version), path.basename(downloaded));
copyFileSync(downloaded, modelDest);
}
}
public enableDeepLinks(): boolean{
return Array.from(Object.values(this.DEEP_LINKS)).every(link => this.deepLink.registerDeepLink(link));
}
public disableDeepLinks(): boolean{
return Array.from(Object.values(this.DEEP_LINKS)).every(link => this.deepLink.unRegisterDeepLink(link));
}
public isDeepLinksEnabled(): boolean{
return Array.from(Object.values(this.DEEP_LINKS)).every(link => this.deepLink.isDeepLinkRegistred(link));
}
}
@@ -60,11 +60,11 @@ export default function TitleBar({template = "index.html"} : {template: AppWindo
if(template === "launcher.html"){
return (
<header id="titlebar" className="min-h-[22px] bg-transparent w-screen h-[22px] z-10">
<div id="drag-region" className='grow basis-0 h-full'></div>
<div id="drag-region" className='grow basis-0 h-full'/>
</header>
)
}
if(template === "oneclick-download-map.html" || template === "oneclick-download-playlist.html"){
if(template === "oneclick-download-map.html" || template === "oneclick-download-playlist.html" || template === "oneclick-download-model.html"){
return (
<header id="titlebar" className="min-h-[22px] bg-transparent w-screen h-[22px] flex content-center items-center justify-start z-10">
<div id="drag-region" className='grow h-full'>
+21 -10
View File
@@ -3,7 +3,7 @@ import SettingColorChooser from "renderer/components/settings/setting-color-choo
import { SettingContainer } from "renderer/components/settings/setting-container.component";
import { RadioItem, SettingRadioArray } from "renderer/components/settings/setting-radio-array.component";
import { BsmButton } from "renderer/components/shared/bsm-button.component";
import { BsmIcon, BsmIconType } from "renderer/components/svgs/bsm-icon.component";
import { BsmIconType } from "renderer/components/svgs/bsm-icon.component";
import { DefaultConfigKey, ThemeConfig } from "renderer/config/default-configuration.config";
import { useObservable } from "renderer/hooks/use-observable.hook";
import { useThemeColor } from "renderer/hooks/use-theme-color.hook";
@@ -29,6 +29,8 @@ import scoreSaberIcon from "../../../assets/images/third-party-icons/score-saber
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-manager.service";
import { useTranslation } from "renderer/hooks/use-translation.hook";
export function SettingsPage() {
@@ -44,6 +46,7 @@ export function SettingsPage() {
const linkOpener: LinkOpenerService = LinkOpenerService.getInstance();
const mapsManager = MapsManagerService.getInstance();
const playlistsManager = PlaylistsManagerService.getInstance();
const modelsManager = ModelsManagerService.getInstance();
const {firstColor, secondColor} = useThemeColor();
const sessionExist = useObservable(authService.sessionExist$);
@@ -63,15 +66,18 @@ export function SettingsPage() {
const [installationFolder, setInstallationFolder] = useState(null);
const [showSupporters, setShowSupporters] = useState(false);
const [mapDeepLinksEnabled, setMapDeepLinksEnabled] = useState(false);
const [playlistsDeepLinkEnabled, setPlaylistsDeeppLinkEnabled] = 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 => setPlaylistsDeeppLinkEnabled(() => enabled));
playlistsManager.isDeepLinksEnabled().then(enabled => setPlaylistsDeepLinkEnabled(() => enabled));
modelsManager.isDeepLinksEnabled().then(enabled => setModelsDeepLinkEnabled(() => enabled))
}, []);
const resetColors = () => {
@@ -149,7 +155,11 @@ export function SettingsPage() {
}
const tooglePlaylistsDeepLinks = () => {
playlistsManager.toogleDeepLinks().finally(() => playlistsManager.isDeepLinksEnabled().then(enabled => setPlaylistsDeeppLinkEnabled(() => enabled)));
playlistsManager.toogleDeepLinks().finally(() => playlistsManager.isDeepLinksEnabled().then(enabled => setPlaylistsDeepLinkEnabled(() => enabled)));
}
const toogleModelsDeepLinks = () => {
modelsManager.toogleDeepLinks().finally(() => modelsManager.isDeepLinksEnabled().then(enabled => setModelsDeepLinkEnabled(() => enabled)));
}
return (
@@ -187,14 +197,15 @@ export function SettingsPage() {
</div>
</SettingContainer>
<SettingContainer title="Contenus additionnels" description="Les contenus additionnels te permet de personaliser BeatSaber !">
{/* TODO TRANSLATE */}
<SettingContainer title="pages.settings.additional-content.title" description="pages.settings.additional-content.description">
<SettingContainer minorTitle="Installations OneClick">
<SettingContainer minorTitle="pages.settings.additional-content.deep-links.sub-title">
<ul className="w-full flex flex-col gap-1.5">
<li className="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">
<BsmCheckbox className="relative z-[1] h-5 w-5" onChange={toogleMapDeepLinks} checked={mapDeepLinksEnabled}/>
<span className="font-extrabold">Maps</span>
<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]}>
@@ -211,7 +222,7 @@ export function SettingsPage() {
<li className="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">
<BsmCheckbox className="relative z-[1] h-5 w-5" onChange={tooglePlaylistsDeepLinks} checked={playlistsDeepLinkEnabled}/>
<span className="font-extrabold">Playlists</span>
<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]}>
@@ -221,8 +232,8 @@ export function SettingsPage() {
</li>
<li className="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">
<BsmCheckbox className="relative z-[1] h-5 w-5"/>
<span className="font-extrabold">Modèles</span>
<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]}>
@@ -23,7 +23,7 @@ export class ModelDownloaderService {
public async oneClickInstallModel(model: MSModel): Promise<boolean>{
this.progress.showFake(0.07);
this.progress.showFake(0.04);
const res = await this.ipc.send("one-click-install-model", {args: model});
@@ -0,0 +1,38 @@
import { IpcService } from "./ipc.service";
export class ModelsManagerService {
private static instance: ModelsManagerService;
public static getInstance(): ModelsManagerService{
if(!ModelsManagerService.instance){ ModelsManagerService.instance = new ModelsManagerService(); }
return ModelsManagerService.instance;
}
private readonly ipc: IpcService;
private constructor(){
this.ipc = IpcService.getInstance();
}
public isDeepLinksEnabled(): Promise<boolean>{
return this.ipc.send<boolean>("is-models-deep-links-enabled").then(res => (
res.success ? res.data : false
));
}
public async toogleDeepLinks(): Promise<boolean>{
const isEnabled = await this.isDeepLinksEnabled();
const channel = isEnabled ? "unregister-models-deep-link" : "register-models-deep-link";
const res = await this.ipc.send<boolean>(channel);
return res.success ? res.data : false;
}
}
@@ -24,6 +24,7 @@ export default function OneClickDownloadMap() {
const [mapInfo, setMapInfo] = useState<BsvMapDetail>(null);
const cover = mapInfo ? mapInfo.versions.at(0).coverURL : null;
const title = mapInfo ? mapInfo.name : null;
useEffect(() => {
@@ -75,7 +76,7 @@ export default function OneClickDownloadMap() {
<div className="w-full h-full backdrop-brightness-50 backdrop-blur-md flex flex-col justify-start items-center gap-10">
<TitleBar template="oneclick-download-map.html"/>
<BsmImage className="aspect-square w-1/2 object-cover rounded-md shadow-black shadow-lg" placeholder={defaultImage} image={cover} errorImage={defaultImage}/>
<h1 className="overflow-hidden font-bold italic text-xl text-gray-200 tracking-wide w-full text-center whitespace-nowrap text-ellipsis px-2">{mapInfo?.name ?? "Chargement de la map..."}</h1>
<h1 className="overflow-hidden font-bold italic text-xl text-gray-200 tracking-wide w-full text-center whitespace-nowrap text-ellipsis px-2">{title}</h1>
</div>
<BsmProgressBar/>
</div>
@@ -3,9 +3,11 @@ import { BsmProgressBar } from "renderer/components/progress-bar/bsm-progress-ba
import { BsmImage } from "renderer/components/shared/bsm-image.component";
import TitleBar from "renderer/components/title-bar/title-bar.component";
import { IpcService } from "renderer/services/ipc.service";
import { ModelDownloaderService } from "renderer/services/model-downloader.service";
import { ProgressBarService } from "renderer/services/progress-bar.service";
import { ThemeService } from "renderer/services/theme.service";
import { ModelSaberService } from "renderer/services/thrird-partys/model-saber.service";
import { WindowManagerService } from "renderer/services/window-manager.service";
import { MSModel } from "shared/models/model-saber/model-saber.model";
import defaultImage from '../../../../assets/images/default-version-img.jpg'
@@ -15,6 +17,8 @@ export default function OneClickDownloadModel() {
const themeService = ThemeService.getInstance();
const modelSaber = ModelSaberService.getInstance();
const progress = ProgressBarService.getInstance();
const modelDownloader = ModelDownloaderService.getInstance();
const windows = WindowManagerService.getInstance();
const [model, setModel] = useState<MSModel>(null);
@@ -40,11 +44,11 @@ export default function OneClickDownloadModel() {
progress.open();
// Download model
resolve(modelDownloader.oneClickInstallModel(model));
});
promise.finally(() => console.log("close"));
promise.finally(() => windows.close("oneclick-download-model.html"));
return () => {
sub.unsubscribe();
@@ -56,7 +60,7 @@ export default function OneClickDownloadModel() {
<div className="relative w-screen h-screen overflow-hidden">
{cover && <BsmImage className="absolute top-0 left-0 w-full h-full object-cover" image={cover}/>}
<div className="w-full h-full backdrop-brightness-50 backdrop-blur-md flex flex-col justify-start items-center gap-10">
<TitleBar template="oneclick-download-map.html"/>
<TitleBar template="oneclick-download-model.html"/>
<BsmImage className="aspect-[1/1] w-1/2 object-cover rounded-md shadow-black shadow-lg" placeholder={defaultImage} image={cover} errorImage={defaultImage}/>
<h1 className="overflow-hidden font-bold italic text-xl text-gray-200 tracking-wide w-full text-center whitespace-nowrap text-ellipsis px-2">{title}</h1>
</div>