diff --git a/.erb/configs/webpack.config.renderer.dev.ts b/.erb/configs/webpack.config.renderer.dev.ts index 7462cbc0..8bcbcc73 100644 --- a/.erb/configs/webpack.config.renderer.dev.ts +++ b/.erb/configs/webpack.config.renderer.dev.ts @@ -168,6 +168,7 @@ const configuration: webpack.Configuration = { new HtmlWebpackPlugin(getHtmlPageOptions("launcher.html")), new HtmlWebpackPlugin(getHtmlPageOptions("oneclick-download-map.html")), new HtmlWebpackPlugin(getHtmlPageOptions("oneclick-download-playlist.html")), + new HtmlWebpackPlugin(getHtmlPageOptions("oneclick-download-model.html")), ], diff --git a/.erb/configs/webpack.config.renderer.prod.ts b/.erb/configs/webpack.config.renderer.prod.ts index e03f66b9..54b67a5e 100644 --- a/.erb/configs/webpack.config.renderer.prod.ts +++ b/.erb/configs/webpack.config.renderer.prod.ts @@ -150,6 +150,7 @@ const configuration: webpack.Configuration = { new HtmlWebpackPlugin(getHtmlPageOptions("launcher.html")), new HtmlWebpackPlugin(getHtmlPageOptions("oneclick-download-map.html")), new HtmlWebpackPlugin(getHtmlPageOptions("oneclick-download-playlist.html")), + new HtmlWebpackPlugin(getHtmlPageOptions("oneclick-download-model.html")), ], }; diff --git a/package-lock.json b/package-lock.json index 6192367f..77afe151 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2202,6 +2202,17 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, + "@types/plist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.2.tgz", + "integrity": "sha512-ULqvZNGMv0zRFvqn8/4LSPtnmN4MfhlPNtJCTpKuIIxGVGZ2rYWzFXrvEBoh9CVyqSE7D6YFRJ1hydLHI6kbWw==", + "dev": true, + "optional": true, + "requires": { + "@types/node": "*", + "xmlbuilder": ">=11.0.1" + } + }, "@types/prettier": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.1.tgz", @@ -2238,12 +2249,12 @@ } }, "@types/react-dom": { - "version": "17.0.18", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.18.tgz", - "integrity": "sha512-rLVtIfbwyur2iFKykP2w0pl/1unw26b5td16d5xMgp7/yjTHomkyxPYChFoCr/FtEX1lN9wY6lFj1qvKdS5kDw==", + "version": "18.0.10", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.10.tgz", + "integrity": "sha512-E42GW/JA4Qv15wQdqJq8DL4JhNpB3prJgjgapN3qJT9K2zO5IIAQh4VXvCEDupoqAwnz0cY4RlXeC/ajX5SFHg==", "dev": true, "requires": { - "@types/react": "^17" + "@types/react": "*" } }, "@types/react-outside-click-handler": { @@ -2363,6 +2374,13 @@ "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", "dev": true }, + "@types/verror": { + "version": "1.10.6", + "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.6.tgz", + "integrity": "sha512-NNm+gdePAX1VGvPcGZCDKQZKYSiAWigKhKaz5KF94hG6f2s8de9Ow5+7AbXoeKxL8gavZfk4UquSAygOF2duEQ==", + "dev": true, + "optional": true + }, "@types/webpack-bundle-analyzer": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/@types/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.6.0.tgz", @@ -3168,6 +3186,13 @@ } } }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true, + "optional": true + }, "ast-metadata-inferer": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/ast-metadata-inferer/-/ast-metadata-inferer-0.7.0.tgz", @@ -4257,6 +4282,16 @@ "yaml": "^1.10.0" } }, + "crc": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", + "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", + "dev": true, + "optional": true, + "requires": { + "buffer": "^5.1.0" + } + }, "crc-32": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", @@ -4850,8 +4885,36 @@ "dev": true, "optional": true, "requires": { + "@types/plist": "^3.0.1", + "@types/verror": "^1.10.3", + "ajv": "^6.10.0", + "crc": "^3.8.0", + "iconv-corefoundation": "^1.1.7", "plist": "^3.0.4", - "smart-buffer": "^4.0.2" + "smart-buffer": "^4.0.2", + "verror": "^1.10.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "optional": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "optional": true + } } }, "dns-equal": { @@ -6581,6 +6644,13 @@ "yauzl": "^2.10.0" } }, + "extsprintf": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", + "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", + "dev": true, + "optional": true + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -7485,6 +7555,13 @@ "ms": "^2.0.0" } }, + "iconv-corefoundation": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", + "integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==", + "dev": true, + "optional": true + }, "iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -13577,6 +13654,27 @@ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "dev": true }, + "verror": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", + "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", + "dev": true, + "optional": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, + "dependencies": { + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true, + "optional": true + } + } + }, "w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", diff --git a/package.json b/package.json index 0afd4fd8..b96958f7 100644 --- a/package.json +++ b/package.json @@ -153,7 +153,7 @@ "@types/jest": "^27.5.2", "@types/node": "17.0.23", "@types/react": "^17.0.48", - "@types/react-dom": "^17.0.17", + "@types/react-dom": "^18.0.10", "@types/react-outside-click-handler": "^1.3.1", "@types/react-test-renderer": "^17.0.2", "@types/terser-webpack-plugin": "^5.0.4", diff --git a/src/main/ipcs/index.ts b/src/main/ipcs/index.ts index 78cfbd31..9889d131 100644 --- a/src/main/ipcs/index.ts +++ b/src/main/ipcs/index.ts @@ -9,4 +9,5 @@ import './window-manager-ipcs'; import './bs-mods-ipcs'; import './bs-maps-ipcs'; import './beat-saver-ipcs'; -import './bs-playlist-ipcs'; \ No newline at end of file +import './bs-playlist-ipcs'; +import './model-saber.ipcs'; \ No newline at end of file diff --git a/src/main/ipcs/model-saber.ipcs.ts b/src/main/ipcs/model-saber.ipcs.ts new file mode 100644 index 00000000..911d7542 --- /dev/null +++ b/src/main/ipcs/model-saber.ipcs.ts @@ -0,0 +1,15 @@ +import { ipcMain } from "electron"; +import { IpcRequest } from "shared/models/ipc"; +import { ModelSaberService } from "../services/thrid-party/model-saber/model-saber.service"; +import { UtilsService } from "../services/utils.service"; + +ipcMain.on("ms-get-model-by-id", async (event, request: IpcRequest) => { + const utils = UtilsService.getInstance(); + const ms = ModelSaberService.getInstance(); + + ms.getModelById(request.args).then(model => { + utils.ipcSend(request.responceChannel, {success: true, data: model}); + }).catch(e => { + utils.ipcSend(request.responceChannel, {success: false, error: e}); + }) +}); \ No newline at end of file diff --git a/src/main/main.ts b/src/main/main.ts index 52e7b0b9..eff82c52 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -18,6 +18,8 @@ import { DeepLinkService } from './services/deep-link.service'; import { AppWindow } from 'shared/models/window-manager/app-window.model'; import { LocalMapsManagerService } from './services/additional-content/local-maps-manager.service'; import { LocalPlaylistsManagerService } from './services/additional-content/local-playlists-manager.service'; +import { ModelSaberService } from './services/thrid-party/model-saber/model-saber.service'; +import { LocalModelsManagerService } from './services/additional-content/local-models-manager.service'; export const PRELOAD_PATH = app.isPackaged ? path.join(__dirname, 'preload.js') : path.join(__dirname, '../../.erb/dll/preload.js') @@ -59,6 +61,7 @@ const createWindow = async (window: AppWindow = "launcher.html") => { const initServicesMustBeInitialized = () => { LocalMapsManagerService.getInstance(); LocalPlaylistsManagerService.getInstance(); + LocalModelsManagerService.getInstance(); // Model } @@ -89,7 +92,7 @@ else{ app.whenReady().then(() => { - process.argv.push("bsplaylist://playlist/https://api.beatsaver.com/playlists/id/11199/download/beatsaver-11199.bplist"); // to force deep-link (oneClick map) + process.argv.push("modelsaber://saber/1670981586/r_Happy Crimbo!.saber"); // to force deep-link (oneClick map) initServicesMustBeInitialized(); diff --git a/src/main/services/additional-content/local-models-manager.service.ts b/src/main/services/additional-content/local-models-manager.service.ts new file mode 100644 index 00000000..509407d7 --- /dev/null +++ b/src/main/services/additional-content/local-models-manager.service.ts @@ -0,0 +1,54 @@ +import { DeepLinkService } from "../deep-link.service"; +import log from "electron-log" +import { ipcMain } from "electron"; +import { IpcRequest } from "shared/models/ipc"; +import { UtilsService } from "../utils.service"; +import { WindowManagerService } from "../window-manager.service"; + +export class LocalModelsManagerService { + + private static instance: LocalModelsManagerService; + + public static getInstance(): LocalModelsManagerService{ + if(!LocalModelsManagerService.instance){ LocalModelsManagerService.instance = new LocalModelsManagerService(); } + return LocalModelsManagerService.instance; + } + + private readonly DEEP_LINKS = { + ModelSaber: "modelsaber", + }; + + private readonly deepLink: DeepLinkService; + private readonly utils: UtilsService; + private readonly windows: WindowManagerService; + + private constructor(){ + this.deepLink = DeepLinkService.getInstance(); + this.utils = UtilsService.getInstance(); + this.windows = WindowManagerService.getInstance(); + + this.deepLink.addLinkOpenedListener(this.DEEP_LINKS.ModelSaber, (link) => { + log.info("DEEP-LINK RECEIVED FROM", this.DEEP_LINKS.ModelSaber, link); + const url = new URL(link); + + const type = url.host + const id = url.pathname.replace("/", '').split("/").at(0); + + this.openOneClickDownloadModelWindow(id, type); + }); + } + + private openOneClickDownloadModelWindow(id: string, type: string){ + + // TODO make once + ipcMain.on("one-click-model-info", async (event, req: IpcRequest) => { + this.utils.ipcSend(req.responceChannel, {success: true, data: {id, type}}); + }); + + this.windows.openWindow("oneclick-download-model.html"); + + } + + + +} \ No newline at end of file diff --git a/src/main/services/thrid-party/beat-saver/beat-saver.service.ts b/src/main/services/thrid-party/beat-saver/beat-saver.service.ts index ffde38c3..34281670 100644 --- a/src/main/services/thrid-party/beat-saver/beat-saver.service.ts +++ b/src/main/services/thrid-party/beat-saver/beat-saver.service.ts @@ -1,8 +1,7 @@ import { splitIntoChunk } from "../../../helpers/array-tools"; import { BsvMapDetail } from "shared/models/maps"; -import { BsvPlaylist, BsvPlaylistPage, SearchParams } from "shared/models/maps/beat-saver.model"; +import { BsvPlaylist, SearchParams } from "shared/models/maps/beat-saver.model"; import { BeatSaverApiService } from "./beat-saver-api.service"; -import { red } from "tailwindcss/colors"; export class BeatSaverService { diff --git a/src/main/services/thrid-party/model-saber/model-saber-api.service.ts b/src/main/services/thrid-party/model-saber/model-saber-api.service.ts new file mode 100644 index 00000000..f24ed97c --- /dev/null +++ b/src/main/services/thrid-party/model-saber/model-saber-api.service.ts @@ -0,0 +1,70 @@ +import fetch from "node-fetch"; +import { ApiResult } from "renderer/models/api/api.model"; +import { MSGetQuery, MSGetQueryFilter, MSGetResponse } from "shared/models/model-saber/model-saber.model"; + +export class ModelSaberApiService { + + private static instance: ModelSaberApiService; + + public static getInstance(): ModelSaberApiService{ + if(!ModelSaberApiService.instance){ ModelSaberApiService.instance = new ModelSaberApiService(); } + return ModelSaberApiService.instance; + } + + private readonly API_URL = "https://modelsaber.com/api/v2/"; + private readonly ENDPOINTS = {get: "get.php", types: "types.php"}; + + private constructor(){} + + private parseFilters(filters: MSGetQueryFilter[]): string{ + + if(!filters){ return null; } + + const parsed = filters.map(filter => { + const stringFilter = filter.type === "searchName" ? filter.value : `${filter.type}:${filter.value}`; + return filter.isNegative ? `-${stringFilter}` : stringFilter; + }); + + return parsed.join(","); + + } + + private buildUrlQuery(query: MSGetQuery): URLSearchParams{ + + if(!query){ return new URLSearchParams(); } + + const filterQuery = this.parseFilters(query.filter); + + const searchParams = { + ...(query.type && {type: query.type}), + ...(query.platform && {platform: query.platform}), + ...(query.start && {start: `${query.start}`}), + ...(query.end && {end: `${query.end}`}), + ...(query.sort && {sort: query.sort}), + ...(query.sortDirection && {sortDirection: query.sortDirection}), + ...(filterQuery && {filter: filterQuery}), + } + + return new URLSearchParams(searchParams); + + } + + public async searchModel(query: MSGetQuery): Promise>{ + + const url = new URL(this.ENDPOINTS.get, this.API_URL); + + url.search = this.buildUrlQuery(query).toString(); + + const res = await fetch(url.toString()); + + if(!res.ok){ + return {data: null, status: res.status}; + } + + const data = await res.json() as MSGetResponse; + + return {data, status: res.status}; + + } + +} \ No newline at end of file diff --git a/src/main/services/thrid-party/model-saber/model-saber.service.ts b/src/main/services/thrid-party/model-saber/model-saber.service.ts index 44a2e4a1..c694987e 100644 --- a/src/main/services/thrid-party/model-saber/model-saber.service.ts +++ b/src/main/services/thrid-party/model-saber/model-saber.service.ts @@ -1,3 +1,6 @@ +import { MSGetQuery, MSGetQueryFilter, MSModel } from "shared/models/model-saber/model-saber.model"; +import { ModelSaberApiService } from "./model-saber-api.service"; + export class ModelSaberService { private static instance: ModelSaberService; @@ -7,6 +10,36 @@ export class ModelSaberService { return ModelSaberService.instance; } - private constructor(){} + private readonly modelSaberApi: ModelSaberApiService; + + private constructor(){ + this.modelSaberApi = ModelSaberApiService.getInstance(); + } + + public async getModelById(id: number|string): Promise{ + + const query: MSGetQuery = { + start: 0, + end: 1, + platform: "pc", + filter: [{type: "id", value: id}] + } + + try{ + const res = await this.modelSaberApi.searchModel(query); + + if(res.status !== 200){ return null; } + + if(Object.keys(res.data).length === 0){ + return null; + } + + return res.data[`${id}`]; + } + catch(e){ + return null; + } + + } } \ No newline at end of file diff --git a/src/main/services/thrid-party/model-saber/model-saver-api.service.ts b/src/main/services/thrid-party/model-saber/model-saver-api.service.ts deleted file mode 100644 index 213a239e..00000000 --- a/src/main/services/thrid-party/model-saber/model-saver-api.service.ts +++ /dev/null @@ -1,19 +0,0 @@ -export class ModelSaberApiService { - - private static instance: ModelSaberApiService; - - public static getInstance(): ModelSaberApiService{ - if(!ModelSaberApiService.instance){ ModelSaberApiService.instance = new ModelSaberApiService(); } - return ModelSaberApiService.instance; - } - - private readonly API_URL = "https://modelsaber.com/api/v2"; - private readonly ENDPOINTS = {get: "get.php", types: "types.php"}; - - private constructor(){} - - public searchModel(search: any): any{ - - } - -} \ No newline at end of file diff --git a/src/main/services/window-manager.service.ts b/src/main/services/window-manager.service.ts index 2f60d335..909dd516 100644 --- a/src/main/services/window-manager.service.ts +++ b/src/main/services/window-manager.service.ts @@ -15,6 +15,7 @@ export class WindowManagerService{ "index.html": {width: 1080, height: 720, minWidth: 900, minHeight: 500}, "oneclick-download-map.html": {width: 350, height: 400, minWidth: 350, minHeight: 400, resizable: false}, "oneclick-download-playlist.html": {width: 350, height: 400, minWidth: 350, minHeight: 400, resizable: false}, + "oneclick-download-model.html": {width: 350, height: 400, minWidth: 350, minHeight: 400, resizable: false}, } private readonly baseWindowOption: BrowserWindowConstructorOptions = { diff --git a/src/renderer/index.tsx b/src/renderer/index.tsx index 79f22484..66737af6 100644 --- a/src/renderer/index.tsx +++ b/src/renderer/index.tsx @@ -1,31 +1,40 @@ -//@ts-ignore import { createRoot } from 'react-dom/client'; import { HashRouter } from 'react-router-dom'; -import App from './windows/App'; -import Launcher from './windows/Launcher'; import 'tailwindcss/tailwind.css'; import './index.css' -import OneClickDownloadMap from './windows/OneClick/OneClickDownloadMap'; -import OneClickDownloadPlaylist from './windows/OneClick/OneClickDownloadPlaylist'; const launcherContainer = document.getElementById('launcher'); const oneclickDownloadMapContainer = document.getElementById('oneclick-download-map'); const oneclickDownloadPlaylistContainer = document.getElementById('oneclick-download-playlist'); +const oneclickDownloadModelContainer = document.getElementById('oneclick-download-model'); -if(!!launcherContainer){ - createRoot(launcherContainer).render(); +if(launcherContainer){ + import("./windows/Launcher").then(reactWindow => { + createRoot(launcherContainer).render(); + }); } -else if(!!oneclickDownloadMapContainer){ - createRoot(oneclickDownloadMapContainer).render(); +else if(oneclickDownloadMapContainer){ + import("./windows/OneClick/OneClickDownloadMap").then(reactWindow => { + createRoot(oneclickDownloadMapContainer).render(); + }); } -else if(!!oneclickDownloadPlaylistContainer){ - createRoot(oneclickDownloadPlaylistContainer).render(); +else if(oneclickDownloadPlaylistContainer){ + import("./windows/OneClick/OneClickDownloadPlaylist").then(reactWindow => { + createRoot(oneclickDownloadPlaylistContainer).render(); + }); +} +else if(oneclickDownloadModelContainer){ + import("./windows/OneClick/OneClickDownloadModel").then(reactWindow => { + createRoot(oneclickDownloadModelContainer).render(); + }); } else{ const root = document.getElementById('root'); - createRoot(root).render( - - - - ); + import("./windows/App").then(reactWindow => { + createRoot(root).render( + + + + ); + }); } \ No newline at end of file diff --git a/src/renderer/oneclick-download-model.ejs b/src/renderer/oneclick-download-model.ejs new file mode 100644 index 00000000..a66d8316 --- /dev/null +++ b/src/renderer/oneclick-download-model.ejs @@ -0,0 +1,14 @@ + + + + + + + + +
+ + \ No newline at end of file diff --git a/src/renderer/services/thrird-partys/model-saber.service.ts b/src/renderer/services/thrird-partys/model-saber.service.ts new file mode 100644 index 00000000..29e30d90 --- /dev/null +++ b/src/renderer/services/thrird-partys/model-saber.service.ts @@ -0,0 +1,25 @@ +import { MSModel } from "shared/models/model-saber/model-saber.model"; +import { IpcService } from "../ipc.service"; + +export class ModelSaberService { + + private static instance: ModelSaberService; + + public static getInstance(): ModelSaberService{ + if(!ModelSaberService.instance){ ModelSaberService.instance = new ModelSaberService(); } + return ModelSaberService.instance; + } + + private readonly ipc: IpcService; + + private constructor(){ + this.ipc = IpcService.getInstance(); + } + + public async getModelById(id: number|string): Promise{ + const res = await this.ipc.send("ms-get-model-by-id", {args: id}); + if(!res.success){ return null; } + return res.data; + } + +} \ No newline at end of file diff --git a/src/renderer/windows/OneClick/OneClickDownloadModel.tsx b/src/renderer/windows/OneClick/OneClickDownloadModel.tsx new file mode 100644 index 00000000..2ed4ee47 --- /dev/null +++ b/src/renderer/windows/OneClick/OneClickDownloadModel.tsx @@ -0,0 +1,45 @@ +import { useEffect, useState } from "react"; +import { IpcService } from "renderer/services/ipc.service"; +import { ThemeService } from "renderer/services/theme.service"; +import { ModelSaberService } from "renderer/services/thrird-partys/model-saber.service"; +import { MSModel } from "shared/models/model-saber/model-saber.model"; + +export default function OneClickDownloadModel() { + + const ipc = IpcService.getInstance(); + const themeService = ThemeService.getInstance(); + const modelSaber = ModelSaberService.getInstance(); + + const [model, setModel] = useState(null); + + useEffect(() => { + + const sub = themeService.theme$.subscribe(() => { + if(themeService.isDark || (themeService.isOS && window.matchMedia('(prefers-color-scheme: dark)').matches)){ document.documentElement.classList.add('dark'); } + else { document.documentElement.classList.remove('dark'); } + }); + + const promise = new Promise(async (resolve, reject) => { + + const infos = await ipc.send<{id: string, type: string}>("one-click-model-info"); + + if(!infos.success){ reject(); } + + const model = await modelSaber.getModelById(infos.data.id); + + setModel(() => model); + + // Download model + + }); + + return () => { + sub.unsubscribe(); + } + }, []) + + + return ( +
OneClickDownloadModel
+ ) +} diff --git a/src/shared/models/model-saber/model-saber.model.ts b/src/shared/models/model-saber/model-saber.model.ts index e69de29b..e5c40bc4 100644 --- a/src/shared/models/model-saber/model-saber.model.ts +++ b/src/shared/models/model-saber/model-saber.model.ts @@ -0,0 +1,42 @@ +export interface MSModel { + tags: string[], + type: MSModelType, + name: string, + author: string, + thumbnail: string, + id: number, + hash: string, + bsaber: string, + status: string, + discordid: string, + discord?: string, + variationid?: number, + platform: MSModelPlatform, + download: string, + install_link: string, + date: string +} + +export type MSModelType = "saber"|"platform"|"bloq"|"misc"|"avatar"; +export type MSModelPlatform = "pc"|"quest"|"all"; + +export interface MSGetQuery { + type?: MSModelType; + platform?: MSModelPlatform, + start?: number, + end?: number, + sort?: MSGetSort, + sortDirection?: MSGetSortDirection, + filter?: MSGetQueryFilter[] +} + +export interface MSGetQueryFilter { + type:MSGetQueryFilterType, + value: string|number, + isNegative?: boolean +} + +export type MSGetResponse = Record; +export type MSGetSort = "date"|"name"|"author" +export type MSGetSortDirection = "asc"|"desc" +export type MSGetQueryFilterType = "author"|"name"|"tag"|"hash"|"discordid"|"id"|"searchName" \ No newline at end of file diff --git a/src/shared/models/window-manager/app-window.model.ts b/src/shared/models/window-manager/app-window.model.ts index 03ca56ac..33785902 100644 --- a/src/shared/models/window-manager/app-window.model.ts +++ b/src/shared/models/window-manager/app-window.model.ts @@ -2,5 +2,6 @@ export type AppWindow = ( "index.html" | "launcher.html" | "oneclick-download-map.html"| - "oneclick-download-playlist.html" + "oneclick-download-playlist.html"| + "oneclick-download-model.html" ); \ No newline at end of file