Merge branch 'v1.5.0' into feature/playlists/107

This commit is contained in:
MathieuG-P
2024-02-26 15:33:13 +01:00
23 changed files with 398 additions and 23 deletions
+1
View File
@@ -0,0 +1 @@
{}
+6
View File
@@ -73,5 +73,11 @@
},
{
"username": "Minescence"
},
{
"username": "Finlay"
},
{
"username": "mereknom"
}
]
+4
View File
@@ -191,6 +191,10 @@
"report-bug": "Fehler melden",
"open-logs": "Protokolle öffnen"
}
},
"changelogs": {
"open" : "🚀 Neues im Changelog entdecken!",
"not_founds": "😕 Hoppla, kein Changelog zu sehen!"
}
}
},
+4
View File
@@ -191,6 +191,10 @@
"report-bug": "Report a bug",
"open-logs": "Open logs"
}
},
"changelogs": {
"open" : "🚀 Check Out the Changelog!",
"not-founds": "😕 Oops, No Changelog Here!"
}
}
},
+4
View File
@@ -191,6 +191,10 @@
"report-bug": "Informar de un error",
"open-logs": "Abrir los registros"
}
},
"changelogs": {
"open" : "🚀 ¡Descubre lo nuevo en el Changelog!",
"not_founds": "😕 Uy, ¡Changelog no encontrado!"
}
}
},
+4
View File
@@ -191,6 +191,10 @@
"report-bug": "Signaler un bug",
"open-logs": "Ouvrir les logs"
}
},
"changelogs": {
"open" : "🚀 Jetez un œil au Changelog !",
"not_founds": "😕 Oups, Changelog introuvable !"
}
}
},
+4
View File
@@ -191,6 +191,10 @@
"report-bug": "バグを報告",
"open-logs": "ログを表示"
}
},
"changelogs": {
"open" : "🚀 チェンジログを覗いてみて!",
"not_founds": "😕 あれ、チェンジログが見当たらない!"
}
}
},
+4
View File
@@ -191,6 +191,10 @@
"report-bug": "Сообщить о баге",
"open-logs": "Открыть логи"
}
},
"changelogs": {
"open" : "🚀 Изучаем Changelog!",
"not_founds": "😕 Ой, Changelog нигде не видно!"
}
}
},
+4
View File
@@ -191,6 +191,10 @@
"report-bug": "報告 bug",
"open-logs": "打開日誌"
}
},
"changelogs": {
"open" : "🚀 探索更新日誌!",
"not_founds": "😕 噢,找不到更新日誌!"
}
}
},
+4
View File
@@ -191,6 +191,10 @@
"report-bug": "报告 bug",
"open-logs": "打开日志"
}
},
"changelogs": {
"open" : "🚀 探索更新日志!",
"not_founds": "😕 哎呀,找不到更新日志!"
}
}
},
+177
View File
@@ -14,6 +14,7 @@
"color": "^4.2.3",
"crypto-js": "^4.2.0",
"dateformat": "^5.0.3",
"dompurify": "^3.0.8",
"dot-prop": "^8.0.2",
"electron-debug": "^3.2.0",
"electron-log": "^4.4.8",
@@ -33,6 +34,7 @@
"pako": "^2.1.0",
"protobufjs": "^7.2.6",
"qrcode.react": "^3.1.0",
"query-process": "^0.0.3",
"react": "^18.2.0",
"react-colorful": "^5.6.1",
"react-dom": "^18.2.0",
@@ -66,6 +68,7 @@
"@types/color": "^3.0.3",
"@types/crypto-js": "^4.2.1",
"@types/dateformat": "^5.0.0",
"@types/dompurify": "^3.0.5",
"@types/got": "^9.6.12",
"@types/jest": "^29.5.11",
"@types/node": "20.11.15",
@@ -2794,6 +2797,15 @@
"@types/ms": "*"
}
},
"node_modules/@types/dompurify": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz",
"integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==",
"dev": true,
"dependencies": {
"@types/trusted-types": "*"
}
},
"node_modules/@types/eslint": {
"version": "8.4.10",
"dev": true,
@@ -3181,6 +3193,12 @@
"integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==",
"dev": true
},
"node_modules/@types/trusted-types": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
"dev": true
},
"node_modules/@types/use-double-click": {
"version": "1.0.4",
"dev": true,
@@ -6884,6 +6902,11 @@
"url": "https://github.com/fb55/domhandler?sponsor=1"
}
},
"node_modules/dompurify": {
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.9.tgz",
"integrity": "sha512-uyb4NDIvQ3hRn6NiC+SIFaP4mJ/MdXlvtunaqK9Bn6dD3RuB/1S/gasEjDHD8eiaqdSael2vBv+hOs7Y+jhYOQ=="
},
"node_modules/domutils": {
"version": "2.8.0",
"dev": true,
@@ -14976,6 +14999,160 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/query-process": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/query-process/-/query-process-0.0.3.tgz",
"integrity": "sha512-6WRue0P+c84abP0o8MkDvigoS/gkkJzsSBt10PsOoBkP7Yodo4DducFsn9XyS4S+LuVYPAE5Hr4+kVNR5Onykg==",
"engines": {
"node": ">= 10"
},
"optionalDependencies": {
"query-process-android-arm-eabi": "0.0.3",
"query-process-android-arm64": "0.0.3",
"query-process-linux-arm-gnueabihf": "0.0.3",
"query-process-linux-arm64-gnu": "0.0.3",
"query-process-linux-arm64-musl": "0.0.3",
"query-process-linux-x64-gnu": "0.0.3",
"query-process-linux-x64-musl": "0.0.3",
"query-process-win32-arm64-msvc": "0.0.3",
"query-process-win32-x64-msvc": "0.0.3"
}
},
"node_modules/query-process-android-arm-eabi": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/query-process-android-arm-eabi/-/query-process-android-arm-eabi-0.0.3.tgz",
"integrity": "sha512-NB+9T+/poBcygDiG+7d2lJSeioP5ZyLT0HwUBdnLgNWvNCpzbJxydup1SLpnt5G2NF6nlTgbUZlH4hvxY7t/bw==",
"cpu": [
"arm"
],
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/query-process-android-arm64": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/query-process-android-arm64/-/query-process-android-arm64-0.0.3.tgz",
"integrity": "sha512-TiAw2yO62vQzM0s02471LToHyUs/PRkLGGlA715z10BPIM5NckzIe245AI45CDyH6tE/3nVHUd0LX/dfjQtTcw==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/query-process-linux-arm-gnueabihf": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/query-process-linux-arm-gnueabihf/-/query-process-linux-arm-gnueabihf-0.0.3.tgz",
"integrity": "sha512-w7NLHBKt7qselCDoh/PVWyeuEb2NiiDDf1A9UB4K3Ns/H24hK3NbfMMBm8mCFxUnFUqoVX8O+VMs7dD/FNlTxA==",
"cpu": [
"arm"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/query-process-linux-arm64-gnu": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/query-process-linux-arm64-gnu/-/query-process-linux-arm64-gnu-0.0.3.tgz",
"integrity": "sha512-nrPHjnSqCBuuN7IZkZWOnBpiJjyKOjfpT0Xt2EjSGp+I7C0CHbnhCL/vG1hBzaWZn0mxJ5nf0c5N0Fja7krnpQ==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/query-process-linux-arm64-musl": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/query-process-linux-arm64-musl/-/query-process-linux-arm64-musl-0.0.3.tgz",
"integrity": "sha512-vk8xMYTnS1PRKhwJgqLKyBAGIFP2CYZW2BsaKuNgQKUrDv7NnBaG7yukZT8niHNO1ycSrFpR/xqeJ8R23hO07A==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/query-process-linux-x64-gnu": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/query-process-linux-x64-gnu/-/query-process-linux-x64-gnu-0.0.3.tgz",
"integrity": "sha512-j1tGcNnGyVCABJ1PHZD1gKuyuomNzsPow3NDuZzUGjPqvKu67RsmKJJJOhMyT1SvUkcqdLFdtS1KTD20I4GS5Q==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/query-process-linux-x64-musl": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/query-process-linux-x64-musl/-/query-process-linux-x64-musl-0.0.3.tgz",
"integrity": "sha512-AOGrrI/Qcb30iKFstiW60zNG/HQevCY0Q38eMHGd7LrBZ5sEN7NzZOjzKp43KeNHejuZBDMxf0gdGAqDznOAng==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/query-process-win32-arm64-msvc": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/query-process-win32-arm64-msvc/-/query-process-win32-arm64-msvc-0.0.3.tgz",
"integrity": "sha512-LHvxbzMFwPPUXHe4bDCAqs1bYbp8j0bMW8XyJGPZnIYrnwHz6g8VE5WQEtXQxSznsbfVxAKJXUtn088EjDVazw==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/query-process-win32-x64-msvc": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/query-process-win32-x64-msvc/-/query-process-win32-x64-msvc-0.0.3.tgz",
"integrity": "sha512-LfHAKmfqyKCgl9K+sTz0KyNwMwB79VcXZUy/Et9iQKLuZt7PDYpHWcFdhrfHw2qqIdKWTED8KmnmsOLhUn/2OA==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/queue-microtask": {
"version": "1.2.3",
"funding": [
+3
View File
@@ -170,6 +170,7 @@
"@types/crypto-js": "^4.2.1",
"@types/dateformat": "^5.0.0",
"@types/got": "^9.6.12",
"@types/dompurify": "^3.0.5",
"@types/jest": "^29.5.11",
"@types/node": "20.11.15",
"@types/node-fetch": "^2.6.3",
@@ -252,6 +253,7 @@
"color": "^4.2.3",
"crypto-js": "^4.2.0",
"dateformat": "^5.0.3",
"dompurify": "^3.0.8",
"dot-prop": "^8.0.2",
"electron-debug": "^3.2.0",
"electron-log": "^4.4.8",
@@ -271,6 +273,7 @@
"pako": "^2.1.0",
"protobufjs": "^7.2.6",
"qrcode.react": "^3.1.0",
"query-process": "^0.0.3",
"react": "^18.2.0",
"react-colorful": "^5.6.1",
"react-dom": "^18.2.0",
+1 -2
View File
@@ -32,7 +32,6 @@ ipc.on<LaunchOption>("create-launch-shortcut", (req, reply) => {
reply(from(bsLauncher.createLaunchShortcut(req.args)));
});
ipc.on<void>("bs-launch.restore-steamvr", (_, reply) => {
const steamLauncher = SteamLauncherService.getInstance();
reply(from(steamLauncher.restoreSteamVR()));
@@ -43,4 +42,4 @@ ipc.on<void>("restore-original-oculus-folder", (_, reply) => {
reply(from(
oculusLauncher.deleteBsSymlinks().then(() => oculusLauncher.restoreOriginalBeatSaber())
));
})
});
@@ -11,6 +11,7 @@ import { AbstractLauncherService } from "./abstract-launcher.service";
import { taskRunning } from "../../helpers/os.helpers";
import { CustomError } from "../../../shared/models/exceptions/custom-error.class";
import { InstallationLocationService } from "../installation-location.service";
import { ensurePathNotAlreadyExist } from "../../helpers/fs.helpers";
export class OculusLauncherService extends AbstractLauncherService implements StoreLauncherInterface {
@@ -86,7 +87,7 @@ export class OculusLauncherService extends AbstractLauncherService implements St
private async backupOriginalBeatSaber(): Promise<void>{
const bsFolder = await this.oculus.getGameFolder(OCULUS_BS_DIR);
if(!bsFolder){ return; }
const backupPath = path.join(bsFolder, "..", OCULUS_BS_BACKUP_DIR);
const backupPath = await ensurePathNotAlreadyExist(path.join(bsFolder, "..", OCULUS_BS_BACKUP_DIR));
log.info("Backing up original Beat Saber", bsFolder, backupPath);
return rename(bsFolder, backupPath);
}
@@ -0,0 +1,3 @@
.content * {
all: revert;
}
@@ -0,0 +1,46 @@
import { BsmButton } from "renderer/components/shared/bsm-button.component";
import { ChangelogVersion } from "renderer/services/auto-updater.service";
import { LinkOpenerService } from "renderer/services/link-opener.service";
import { useService } from "renderer/hooks/use-service.hook";
import { ModalComponent } from "renderer/services/modale.service";
import DOMPurify from 'dompurify';
import './changelog-modal.component.css';
import Tippy from "@tippyjs/react";
export const ChangelogModal: ModalComponent<void, ChangelogVersion> = ({ resolver, data: changelog }) => {
const linkOpener = useService(LinkOpenerService);
const openGithub = () => linkOpener.open("https://github.com/Zagrios/bs-manager");
const openTwitter = () => linkOpener.open("https://twitter.com/BSManager_");
const openSupportPage = () => linkOpener.open("https://www.patreon.com/bsmanager");
const openDiscord = () => linkOpener.open("https://discord.gg/uSqbHVpKdV");
const date = changelog?.timestamp ? new Date(changelog.timestamp * 1000).toLocaleDateString() : '';
return (
<form className="w-[350px] text-gray-800 dark:text-gray-200 h-[70vh] flex flex-col justify-between">
<h1 className=" p-4 pt-1 text-3xl uppercase tracking-wide w-full text-center text-gray-800 dark:text-gray-200 font-bold">{changelog?.title}</h1>
<div className=" overflow-y-scroll h-full content grow" dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(changelog?.htmlBody) }}/>
<span className="block w-[100%] mx-auto mt-0 mb-4 h-1 rounded-full bg-main-color-1" />
<div className="flex flex-row justify-between">
<div className="my-auto flex flex-col text-sm">
<span>{date}</span>
<span>v{changelog?.version}</span>
</div>
<div className="flex flex-row gap-2 my-auto">
<Tippy content="Twitter" placement="top" className="font-bold !bg-neutral-900" duration={[200, 0]} arrow={false}>
<div><BsmButton onClick={openTwitter} className="rounded-md p-1 w-7 h-7" icon="twitter" withBar={false} iconColor="#fff" color="#000"/></div>
</Tippy>
<Tippy content="Github" placement="top" className="font-bold !bg-neutral-900" duration={[200, 0]} arrow={false}>
<div><BsmButton onClick={openGithub} className="rounded-md p-1 w-7 h-7" icon="github" withBar={false} iconColor="#fff" color="#000"/></div>
</Tippy>
<Tippy content="Discord" placement="top" className="font-bold !bg-neutral-900" duration={[200, 0]} arrow={false}>
<div><BsmButton onClick={openDiscord} className="rounded-md p-1 w-7 h-7" icon="discord" withBar={false} iconColor="#fff" color="#000"/></div>
</Tippy>
<Tippy content="Patreon" placement="top" className="font-bold !bg-neutral-900" duration={[200, 0]} arrow={false}>
<div><BsmButton onClick={openSupportPage} className="rounded-md p-1 w-7 h-7 " icon="patreon" withBar={false} iconColor="#fff" color="#000"/></div>
</Tippy>
</div>
</div>
</form>
)
}
@@ -6,7 +6,7 @@ import { useClickOutside } from "renderer/hooks/use-click-outside.hook";
import { useThemeColor } from "renderer/hooks/use-theme-color.hook";
import { getCorrectTextColor } from "renderer/helpers/correct-text-color";
type BsmButtonType = "primary" | "secondary" | "success" | "cancel" | "error";
type BsmButtonType = "primary" | "secondary" | "success" | "cancel" | "error" | "none";
type Props = {
className?: string;
@@ -50,7 +50,7 @@ export function BsmButton({ className, style, imgClassName, iconClassName, icon,
if (primaryColor) {
return getCorrectTextColor(primaryColor);
}
return typeColor ? "white" : undefined;
return typeColor && typeColor !== "none" ? "white" : undefined;
})();
const renderTypeColor = (() => {
@@ -2,9 +2,8 @@ import { CSSProperties } from "react";
export default function PatreonIcon(props: { className?: string; style?: CSSProperties }) {
return (
<svg className={props.className} style={props.style} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 180 180">
<path fill="white" d="M108.8135992 26.06720125c-26.468266 0-48.00213212 21.53066613-48.00213212 47.99733213 0 26.38653268 21.53386613 47.85426547 48.00213213 47.85426547 26.38639937 0 47.8530655-21.4677328 47.8530655-47.85426547 0-26.466666-21.46666613-47.99733213-47.85306547-47.99733213" />
<path fill="#052a49" d="M23.333335 153.93333178V26.0666679h23.46666576v127.8666639z" />
<svg className={props.className} style={props.style} xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0px" y="0px" viewBox="0 0 1080 1080" xmlSpace="preserve" fill="currentColor">
<path d="M1033.05,324.45c-0.19-137.9-107.59-250.92-233.6-291.7c-156.48-50.64-362.86-43.3-512.28,27.2 C106.07,145.41,49.18,332.61,47.06,519.31c-1.74,153.5,13.58,557.79,241.62,560.67c169.44,2.15,194.67-216.18,273.07-321.33 c55.78-74.81,127.6-95.94,216.01-117.82C929.71,603.22,1033.27,483.3,1033.05,324.45z"/>
</svg>
);
}
@@ -2,10 +2,8 @@ import { CSSProperties } from "react";
export function TwitterIcon(props: { className?: string; style?: CSSProperties }) {
return (
<svg className={props.className} style={props.style} viewBox="0 0 24 24" fill="currentColor">
<g>
<path d="M23.643 4.937c-.835.37-1.732.62-2.675.733.962-.576 1.7-1.49 2.048-2.578-.9.534-1.897.922-2.958 1.13-.85-.904-2.06-1.47-3.4-1.47-2.572 0-4.658 2.086-4.658 4.66 0 .364.042.718.12 1.06-3.873-.195-7.304-2.05-9.602-4.868-.4.69-.63 1.49-.63 2.342 0 1.616.823 3.043 2.072 3.878-.764-.025-1.482-.234-2.11-.583v.06c0 2.257 1.605 4.14 3.737 4.568-.392.106-.803.162-1.227.162-.3 0-.593-.028-.877-.082.593 1.85 2.313 3.198 4.352 3.234-1.595 1.25-3.604 1.995-5.786 1.995-.376 0-.747-.022-1.112-.065 2.062 1.323 4.51 2.093 7.14 2.093 8.57 0 13.255-7.098 13.255-13.254 0-.2-.005-.402-.014-.602.91-.658 1.7-1.477 2.323-2.41z" />
</g>
<svg className={props.className} style={props.style} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 1227" fill="currentColor">
<path d="M714.163 519.284L1160.89 0H1055.03L667.137 450.887L357.328 0H0L468.492 681.821L0 1226.37H105.866L515.491 750.218L842.672 1226.37H1200L714.137 519.284H714.163ZM569.165 687.828L521.697 619.934L144.011 79.6944H306.615L611.412 515.685L658.88 583.579L1055.08 1150.3H892.476L569.165 687.854V687.828Z"/>
</svg>
);
}
+34 -7
View File
@@ -40,9 +40,12 @@ import { BsStore } from "shared/models/bs-store.enum";
import { SteamIcon } from "renderer/components/svgs/icons/steam-icon.component";
import { OculusIcon } from "renderer/components/svgs/icons/oculus-icon.component";
import { BsDownloaderService } from "renderer/services/bs-version-download/bs-downloader.service";
import { AutoUpdaterService } from "renderer/services/auto-updater.service";
import BeatWaitingImg from "../../../assets/images/apngs/beat-waiting.png";
export function SettingsPage() {
const configService = useService(ConfigurationService);
const themeService = useService(ThemeService);
const ipcService = useService(IpcService);
@@ -58,6 +61,7 @@ export function SettingsPage() {
const playlistsManager = useService(PlaylistsManagerService);
const modelsManager = useService(ModelsManagerService);
const versionLinker = useService(VersionFolderLinkerService);
const autoUpdater = useService(AutoUpdaterService);
const { firstColor, secondColor } = useThemeColor();
@@ -89,6 +93,9 @@ export function SettingsPage() {
const [hasDownloaderSession, setHasDownloaderSession] = useState(false);
const appVersion = useObservable(() => ipcService.sendV2<string>("current-version"));
const [isChangelogAvailable, setIsChangelogAvailable] = useState(true);
const [changlogsLoading, setChanglogsLoading] = useState(false);
useEffect(() => {
loadInstallationFolder();
loadDownloadersSession();
@@ -137,6 +144,23 @@ export function SettingsPage() {
i18nService.setLanguage(item.value);
};
const handleVersionClick = async () => {
let isChangelogResolved = false;
const timeoutId = setTimeout(() => setChanglogsLoading(() => !isChangelogResolved), 100);
await autoUpdater.showChangelog(await lastValueFrom(autoUpdater.getAppVersion()))
.then(() => {
setIsChangelogAvailable(() => true);
})
.catch(() => {
setIsChangelogAvailable(() => false);
})
.finally(() => { isChangelogResolved = true; });
setChanglogsLoading(() => false);
clearTimeout(timeoutId);
};
const setDefaultInstallationFolder = () => {
if (!progressBarService.require()) {
return;
@@ -232,7 +256,7 @@ export function SettingsPage() {
<SettingContainer title="pages.settings.steam-and-oculus.title" description="pages.settings.steam-and-oculus.description">
<BsmButton onClick={clearDownloadersSession} className="w-fit px-3 py-[2px] text-white rounded-md" withBar={false} text="pages.settings.steam-and-oculus.logout" typeColor="error" disabled={!hasDownloaderSession}/>
<SettingContainer id="choose-default-store" minorTitle="pages.settings.steam-and-oculus.download-platform.title" description="pages.settings.steam-and-oculus.download-platform.desc" className="mt-3">
<SettingRadioArray items={[
{ id: 1, text: "Steam", value: BsStore.STEAM, icon: <SteamIcon className="h-6 w-6 float-left"/> },
@@ -240,7 +264,7 @@ export function SettingsPage() {
{ id: 0, text: t("pages.settings.steam-and-oculus.download-platform.always-ask"), value: null, },
]} selectedItemValue={downloadStore} onItemSelected={handleChangeBsStore}/>
</SettingContainer>
</SettingContainer>
<SettingContainer title="pages.settings.appearance.title" description="pages.settings.appearance.description">
@@ -426,7 +450,7 @@ export function SettingsPage() {
<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="Twitter" icon="twitter" iconClassName="p-0.5 mr-1" color="#000" onClick={openTwitter} />
</div>
</SettingContainer>
<SettingContainer className="pt-3" description="pages.settings.contribution.description">
@@ -442,10 +466,13 @@ export function SettingsPage() {
</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>
<Tippy content={isChangelogAvailable ? t("pages.settings.changelogs.open") : t("pages.settings.changelogs.not-founds")} placement="left" className="font-bold bg-main-color-3">
<div className="!bg-light-main-color-1 dark:!bg-main-color-1 rounded-md py-1 px-2 font-bold float-right mb-5 hover:brightness-125 h-auto w-auto">
<BsmButton onClick={handleVersionClick} text={`v${appVersion}`} withBar={false} typeColor="none"/>
</div>
</Tippy>
<BsmImage className={`h-7 my-auto spin-loading float-right ${changlogsLoading ? "" : "hidden"}`} image={BeatWaitingImg} loading="eager" />
</div>
<SupportersView isVisible={showSupporters} setVisible={setShowSupporters} />
</div>
);
+71 -2
View File
@@ -1,8 +1,21 @@
import { Observable, lastValueFrom } from "rxjs";
import { map } from "rxjs/operators";
import { IpcService } from "./ipc.service";
import { ProgressBarService } from "./progress-bar.service";
import { ModalService } from "renderer/services/modale.service";
import { ChangelogModal } from "renderer/components/modal/modal-types/chabgelog-modal/changelog-modal.component";
import { ConfigurationService } from "./configuration.service";
import { Observable, lastValueFrom } from "rxjs";
export interface Changelog {
[version: string]: ChangelogVersion;
}
export interface ChangelogVersion {
htmlBody: string;
title: string;
timestamp : number;
version: string;
}
export class AutoUpdaterService {
private static instance: AutoUpdaterService;
@@ -11,6 +24,12 @@ export class AutoUpdaterService {
private downloadProgress$: Observable<number>;
private modal: ModalService;
private configurationService: ConfigurationService;
private cacheChangelog: Changelog;
public static getInstance(): AutoUpdaterService {
if (!AutoUpdaterService.instance) {
AutoUpdaterService.instance = new AutoUpdaterService();
@@ -21,6 +40,8 @@ export class AutoUpdaterService {
private constructor() {
this.progressService = ProgressBarService.getInstance();
this.ipcService = IpcService.getInstance();
this.modal = ModalService.getInstance();
this.configurationService = ConfigurationService.getInstance();
this.downloadProgress$ = this.ipcService.watch<number>("update-download-progress").pipe(map(res => (res.success ? res.data : 0)));
}
@@ -35,11 +56,59 @@ export class AutoUpdaterService {
});
this.progressService.show(this.downloadProgress$, true);
return promise;
}
public quitAndInstall() {
this.ipcService.sendLazy("install-update");
}
public getLastAppVersion(): string {
return this.configurationService.get("last-app-version");
}
public setLastAppVersion(value : string){
this.configurationService.set("last-app-version", value);
}
private async getChangelog(): Promise<Changelog> {
if (this.cacheChangelog) {
return this.cacheChangelog;
}
const path = `https://raw.githubusercontent.com/Zagrios/bs-manager/master/assets/jsons/changelogs.json`
const response = await fetch(path);
if (!response.ok) {
throw new Error(`Failed to fetch changelogs (${response.status})`);
}
const data = await response.json();
if (!data) {
throw new Error(`Failed to parse changelogs`);
}
this.cacheChangelog = data;
return data;
}
private async getChangelogVersion(version:string): Promise<ChangelogVersion> {
const changelogs = await this.getChangelog();
const changelogVersion = changelogs[version];
if (!changelogVersion) {
throw new Error(`No changelog found for this version (${version})`);
}
return changelogVersion;
}
public getAppVersion() : Observable<string> {
return this.ipcService.sendV2<string>("current-version");
}
public async showChangelog(version:string): Promise<void>{
const changelog = await this.getChangelogVersion(version);
this.modal.openModal(ChangelogModal, changelog);
}
}
+16 -1
View File
@@ -18,10 +18,12 @@ 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";
import { NotificationService } from "renderer/services/notification.service";
import { timer } from "rxjs";
import { lastValueFrom, timer } from "rxjs";
import { ConfigurationService } from "renderer/services/configuration.service";
import { OsDiagnosticService } from "renderer/services/os-diagnostic.service";
import { useService } from "renderer/hooks/use-service.hook";
import { AutoUpdaterService } from "renderer/services/auto-updater.service";
import { gt } from "semver"
export default function App() {
useService(OsDiagnosticService);
@@ -31,14 +33,27 @@ export default function App() {
const models = useService(ModelsManagerService);
const notification = useService(NotificationService);
const config = useService(ConfigurationService);
const autoUpdater = useService(AutoUpdaterService);
const location = useLocation();
const navigate = useNavigate();
useEffect(() => {
checkIsUpdated();
checkOneClicks();
}, []);
const checkIsUpdated = async () => {
const appVersion = await lastValueFrom(autoUpdater.getAppVersion());
const lastAppVersion = autoUpdater.getLastAppVersion();
autoUpdater.setLastAppVersion(appVersion);
if (lastAppVersion && gt(appVersion, lastAppVersion)) {
autoUpdater.showChangelog(appVersion);
}
};
const checkOneClicks = async () => {
if (config.get("not-remind-oneclick") === true) {
return;
-1
View File
@@ -9,7 +9,6 @@ import { WindowManagerService } from "../services/window-manager.service";
import { useService } from "renderer/hooks/use-service.hook";
export default function Launcher() {
const updaterService = useService(AutoUpdaterService);
const windowService = useService(WindowManagerService);