mirror of
https://github.com/Zagrios/bs-manager.git
synced 2026-07-03 14:08:25 +02:00
add notification and system notification
This commit is contained in:
@@ -2,3 +2,4 @@ export const BS_EXECUTABLE = "Beat Saber.exe";
|
||||
export const OCULUS_BS_DIR = "hyperbolic-magnetism-beat-saber"
|
||||
export const BS_APP_ID = "620980";
|
||||
export const BS_DEPOT = "620981";
|
||||
export const APP_NAME = "BSManager";
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { ipcMain, shell, dialog, app } from 'electron';
|
||||
import { UtilsService } from '../services/utils.service';
|
||||
import { IpcRequest } from 'shared/models/ipc';
|
||||
import { SystemNotificationOptions } from 'shared/models/notification/system-notification.model';
|
||||
import { NotificationService } from '../services/notification.service';
|
||||
|
||||
|
||||
// TODO IMPROVE WINDOW CONTROL BY USING WINDOW SERVICE
|
||||
@@ -55,3 +57,7 @@ ipcMain.on("current-version", async (event, request: IpcRequest<void>) => {
|
||||
ipcMain.on("open-logs", async (event, request: IpcRequest<void>) => {
|
||||
shell.openPath(app.getPath("logs"));
|
||||
});
|
||||
|
||||
ipcMain.on("notify-system", async (event, request: IpcRequest<SystemNotificationOptions>) => {
|
||||
NotificationService.getInstance().notify(request.args)
|
||||
});
|
||||
+4
-1
@@ -19,6 +19,7 @@ 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 { LocalModelsManagerService } from './services/additional-content/local-models-manager.service';
|
||||
import { APP_NAME } from './constants';
|
||||
|
||||
const isDebug = process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true';
|
||||
|
||||
@@ -89,7 +90,9 @@ else{
|
||||
|
||||
app.whenReady().then(() => {
|
||||
|
||||
// process.argv.push("modelsaber://platform/1671435302/The grinch mountain.plat"); // to force deep-link (oneClick map)
|
||||
app.setAppUserModelId(APP_NAME);
|
||||
|
||||
process.argv.push("beatsaver://2d5bc"); // to force deep-link (oneClick map)
|
||||
|
||||
initServicesMustBeInitialized();
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Notification } from "electron";
|
||||
import { SystemNotificationOptions } from "shared/models/notification/system-notification.model";
|
||||
import { UtilsService } from "./utils.service";
|
||||
|
||||
export class NotificationService {
|
||||
|
||||
private static instance: NotificationService;
|
||||
|
||||
public static getInstance(): NotificationService{
|
||||
if(!NotificationService.instance){ NotificationService.instance = new NotificationService(); }
|
||||
return NotificationService.instance;
|
||||
}
|
||||
|
||||
private readonly APP_ICON: string;
|
||||
|
||||
private readonly utils: UtilsService;
|
||||
|
||||
private constructor(){
|
||||
this.utils = UtilsService.getInstance();
|
||||
this.APP_ICON = this.utils.getAssetsPath("favicon.ico");
|
||||
}
|
||||
|
||||
public notify(options: SystemNotificationOptions){
|
||||
new Notification({...options, icon: this.APP_ICON}).show();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { resolveHtmlPath } from "../util";
|
||||
import { UtilsService } from "./utils.service";
|
||||
import { AppWindow } from "shared/models/window-manager/app-window.model";
|
||||
import path from "path";
|
||||
import { APP_NAME } from "../constants";
|
||||
|
||||
export class WindowManagerService{
|
||||
|
||||
@@ -21,6 +22,7 @@ export class WindowManagerService{
|
||||
}
|
||||
|
||||
private readonly baseWindowOption: BrowserWindowConstructorOptions = {
|
||||
title: APP_NAME,
|
||||
icon: this.utilsService.getAssetsPath("favicon.ico"),
|
||||
show: false,
|
||||
frame: false,
|
||||
|
||||
@@ -150,16 +150,51 @@ export function SettingsPage() {
|
||||
|
||||
const openLogs = () => ipcService.sendLazy("open-logs");
|
||||
|
||||
// TODO TRANSLATE
|
||||
const showDeepLinkError = (isDesactivation: boolean) => {
|
||||
const desc = isDesactivation ? "Une erreur inconnue c'est produite" : "Impossible d'activer les installations OneClick";
|
||||
notificationService.notifyError({title: "notifications.types.error", desc, duration: 3000});
|
||||
}
|
||||
|
||||
const showDeepLinkSuccess = (isDesactivation: boolean) => {
|
||||
const desc = isDesactivation ? "Les installations OneClick ont été désactivées" : "Les installations OneClick ont été activées";
|
||||
const title = isDesactivation ? "OneClick désactivée !" : "OneClick activée !";
|
||||
notificationService.notifySuccess({title, desc, duration: 3000});
|
||||
}
|
||||
|
||||
const showDeepLinkWarning = (isDesactivation: boolean) => {
|
||||
const desc = isDesactivation ? "Un problème est survenu lors de la désactivation des OneClicks, veuillez réessayez" : "Un problème est survenu lors de l'activation des OneClicks, veuillez réessayez";
|
||||
notificationService.notifyWarning({title: "Attention !", desc, duration: 3000});
|
||||
}
|
||||
|
||||
const toogleMapDeepLinks = () => {
|
||||
mapsManager.toogleDeepLinks().finally(() => mapsManager.isDeepLinksEnabled().then(enabled => setMapDeepLinksEnabled(() => enabled)));
|
||||
const isDesactivation = mapDeepLinksEnabled;
|
||||
mapsManager.toogleDeepLinks().catch(() => {
|
||||
showDeepLinkError(isDesactivation);
|
||||
}).then(res => {
|
||||
if(!res){ return showDeepLinkWarning(isDesactivation); }
|
||||
showDeepLinkSuccess(isDesactivation)
|
||||
}).finally(() => mapsManager.isDeepLinksEnabled().then(enabled => setMapDeepLinksEnabled(() => enabled)));
|
||||
}
|
||||
|
||||
const tooglePlaylistsDeepLinks = () => {
|
||||
playlistsManager.toogleDeepLinks().finally(() => playlistsManager.isDeepLinksEnabled().then(enabled => setPlaylistsDeepLinkEnabled(() => enabled)));
|
||||
const isDesactivation = playlistsDeepLinkEnabled;
|
||||
playlistsManager.toogleDeepLinks().catch(() => {
|
||||
showDeepLinkError(isDesactivation);
|
||||
}).then(res => {
|
||||
if(!res){ return showDeepLinkWarning(isDesactivation); }
|
||||
showDeepLinkSuccess(isDesactivation)
|
||||
}).finally(() => playlistsManager.isDeepLinksEnabled().then(enabled => setPlaylistsDeepLinkEnabled(() => enabled)));
|
||||
}
|
||||
|
||||
const toogleModelsDeepLinks = () => {
|
||||
modelsManager.toogleDeepLinks().finally(() => modelsManager.isDeepLinksEnabled().then(enabled => setModelsDeepLinkEnabled(() => enabled)));
|
||||
const isDesactivation = modelsDeepLinkEnabled;
|
||||
modelsManager.toogleDeepLinks().catch(() => {
|
||||
showDeepLinkError(isDesactivation)
|
||||
}).then(res => {
|
||||
if(!res){ return showDeepLinkWarning(isDesactivation); }
|
||||
showDeepLinkSuccess(isDesactivation)
|
||||
}).finally(() => modelsManager.isDeepLinksEnabled().then(enabled => setModelsDeepLinkEnabled(() => enabled)));
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,17 +1,27 @@
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
import { SystemNotificationOptions } from "shared/models/notification/system-notification.model";
|
||||
import { I18nService } from "./i18n.service";
|
||||
import { IpcService } from "./ipc.service";
|
||||
|
||||
export class NotificationService{
|
||||
|
||||
private static instance: NotificationService;
|
||||
|
||||
public readonly notifications$: BehaviorSubject<ResolvableNotification[]>;
|
||||
|
||||
|
||||
public static getInstance(): NotificationService{
|
||||
if(!NotificationService.instance){ NotificationService.instance = new NotificationService(); }
|
||||
return NotificationService.instance;
|
||||
}
|
||||
|
||||
public readonly notifications$: BehaviorSubject<ResolvableNotification[]>;
|
||||
|
||||
private readonly ipc: IpcService;
|
||||
private readonly i18n: I18nService;
|
||||
|
||||
private constructor(){
|
||||
this.ipc = IpcService.getInstance();
|
||||
this.i18n = I18nService.getInstance();
|
||||
this.notifications$ = new BehaviorSubject<ResolvableNotification[]>([]);
|
||||
}
|
||||
|
||||
@@ -46,6 +56,14 @@ export class NotificationService{
|
||||
return this.notify(notification);
|
||||
}
|
||||
|
||||
public notifySystem(options: SystemNotificationOptions){
|
||||
|
||||
options.body = options.body && this.i18n.translate(options.body);
|
||||
options.title = options.title && this.i18n.translate(options.title);
|
||||
|
||||
this.ipc.sendLazy<SystemNotificationOptions>("notify-system", {args: options});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export interface Notification {
|
||||
|
||||
@@ -4,6 +4,7 @@ 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 { MapsDownloaderService } from "renderer/services/maps-downloader.service";
|
||||
import { NotificationService } from "renderer/services/notification.service";
|
||||
import { ProgressBarService } from "renderer/services/progress-bar.service";
|
||||
import { ThemeService } from "renderer/services/theme.service";
|
||||
import { BeatSaverService } from "renderer/services/thrird-partys/beat-saver.service";
|
||||
@@ -20,6 +21,7 @@ export default function OneClickDownloadMap() {
|
||||
const themeService = ThemeService.getInstance();
|
||||
const progressBar = ProgressBarService.getInstance();
|
||||
const windows = WindowManagerService.getInstance();
|
||||
const notification = NotificationService.getInstance();
|
||||
|
||||
const [mapInfo, setMapInfo] = useState<BsvMapDetail>(null);
|
||||
|
||||
@@ -38,6 +40,7 @@ export default function OneClickDownloadMap() {
|
||||
const promise = new Promise<void>(async (resolve, reject) => {
|
||||
|
||||
try{
|
||||
|
||||
const ipcRes = await ipc.send<{id: string, isHash: boolean}>("one-click-map-info");
|
||||
|
||||
if(!ipcRes.success){ return reject(ipcRes.error); }
|
||||
@@ -60,6 +63,16 @@ export default function OneClickDownloadMap() {
|
||||
|
||||
});
|
||||
|
||||
// TODO TRANSLATE
|
||||
|
||||
promise.catch(() => {
|
||||
notification.notifySystem({title: "Erreur", body: "Une erreur c'est produite lors de l'installation de la map"});
|
||||
})
|
||||
|
||||
promise.then(() => {
|
||||
notification.notifySystem({title: "OneClick", body: "Installation de la map terminé"});
|
||||
});
|
||||
|
||||
promise.finally(() =>{
|
||||
windows.close("oneclick-download-map.html");
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ 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 { NotificationService } from "renderer/services/notification.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";
|
||||
@@ -19,6 +20,7 @@ export default function OneClickDownloadModel() {
|
||||
const progress = ProgressBarService.getInstance();
|
||||
const modelDownloader = ModelDownloaderService.getInstance();
|
||||
const windows = WindowManagerService.getInstance();
|
||||
const notification = NotificationService.getInstance();
|
||||
|
||||
const [model, setModel] = useState<MSModel>(null);
|
||||
|
||||
@@ -48,6 +50,16 @@ export default function OneClickDownloadModel() {
|
||||
|
||||
});
|
||||
|
||||
// TODO TRANSLATE
|
||||
|
||||
promise.catch(() => {
|
||||
notification.notifySystem({title: "Erreur", body: "Une erreur c'est produite lors de l'installation du model"});
|
||||
})
|
||||
|
||||
promise.then(() => {
|
||||
notification.notifySystem({title: "OneClick", body: "Installation du model terminé"});
|
||||
});
|
||||
|
||||
promise.finally(() => windows.close("oneclick-download-model.html"));
|
||||
|
||||
return () => {
|
||||
|
||||
@@ -5,6 +5,7 @@ import { BsmImage } from "renderer/components/shared/bsm-image.component";
|
||||
import TitleBar from "renderer/components/title-bar/title-bar.component";
|
||||
import { useObservable } from "renderer/hooks/use-observable.hook";
|
||||
import { IpcService } from "renderer/services/ipc.service"
|
||||
import { NotificationService } from "renderer/services/notification.service";
|
||||
import { PlaylistDownloaderService } from "renderer/services/playlist-downloader.service";
|
||||
import { ThemeService } from "renderer/services/theme.service";
|
||||
import { BeatSaverService } from "renderer/services/thrird-partys/beat-saver.service";
|
||||
@@ -21,6 +22,7 @@ export default function OneClickDownloadPlaylist() {
|
||||
const playlistDownloader = PlaylistDownloaderService.getInstance();
|
||||
const mapsContainer = useRef<HTMLDivElement>(null);
|
||||
const windows = WindowManagerService.getInstance();
|
||||
const notification = NotificationService.getInstance();
|
||||
|
||||
const [playlist, setPlaylist] = useState<BsvPlaylist>(null);
|
||||
const downloadedMap = useObservable(playlistDownloader.progress$.pipe(filter(download => !!download), map(download => [...(download.downloadedMaps ?? []), download?.current])), []);
|
||||
@@ -49,6 +51,16 @@ export default function OneClickDownloadPlaylist() {
|
||||
|
||||
});
|
||||
|
||||
// TODO TRANSLATE
|
||||
|
||||
promise.catch(() => {
|
||||
notification.notifySystem({title: "Erreur", body: "Une erreur c'est produite lors de l'installation de la playlist"});
|
||||
})
|
||||
|
||||
promise.then(() => {
|
||||
notification.notifySystem({title: "OneClick", body: "Installation de la playlist terminé"});
|
||||
});
|
||||
|
||||
promise.finally(() => {
|
||||
windows.close("oneclick-download-playlist.html");
|
||||
});
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface SystemNotificationOptions {
|
||||
title?: string,
|
||||
body?: string
|
||||
}
|
||||
Reference in New Issue
Block a user