modal system lot impoved and simpler to use

This commit is contained in:
MathieuG-P
2022-10-31 23:04:42 +01:00
parent 47fce8a6dd
commit 4a7949fd2a
15 changed files with 65 additions and 84 deletions
@@ -5,18 +5,17 @@ import { BsmIcon } from "renderer/components/svgs/bsm-icon.component";
import { DefaultConfigKey } from "renderer/config/default-configuration.config";
import { useTranslation } from "renderer/hooks/use-translation.hook";
import { ConfigurationService } from "renderer/services/configuration.service";
import { ModalExitCode, ModalResponse, ModalService } from "renderer/services/modale.service";
import { ModalComponent, ModalExitCode } from "renderer/services/modale.service";
import { BSVersion } from "shared/bs-version.interface";
export function EditVersionModal({resolver, clone = false}: {resolver: (x: ModalResponse<{name: string, color: string}>) => void, clone?: boolean}) {
export const EditVersionModal: ModalComponent<{name: string, color: string}, {version: BSVersion, clone?: boolean}> = ({resolver, data}) => {
const {version, clone} = data;
const configService = ConfigurationService.getInstance();
const modalService = ModalService.getInsance();
const modalData: BSVersion = modalService.getModalData();
const [name, setName] = useState(modalData.name || modalData.BSVersion);
const [color, setColor] = useState(modalData.color ?? configService.get<string>("second-color" as DefaultConfigKey));
const [name, setName] = useState(version.name || version.BSVersion);
const [color, setColor] = useState(version.color ?? configService.get<string>("second-color" as DefaultConfigKey));
const t = useTranslation();
const rename = () => {
@@ -1,11 +1,11 @@
import { ModalExitCode, ModalResponse } from "../../../services/modale.service";
import { ModalComponent, ModalExitCode } from "../../../services/modale.service";
import { useState } from "react";
import BeatConflict from "../../../../../assets/images/apngs/beat-conflict.png"
import { BsmImage } from "renderer/components/shared/bsm-image.component";
import { BsmButton } from "renderer/components/shared/bsm-button.component";
import { useTranslation } from "renderer/hooks/use-translation.hook";
export function GuardModal({resolver}: {resolver: (x: ModalResponse<string>) => void}) {
export const GuardModal: ModalComponent<string> = ({resolver}) => {
const [guardCode, setGuardCode] = useState('');
const t = useTranslation();
@@ -1,16 +1,15 @@
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 { ModalExitCode, ModalResponse } from "renderer/services/modale.service";
import { ModalComponent, ModalExitCode } from "renderer/services/modale.service";
import BeatConflict from "../../../../../assets/images/apngs/beat-conflict.png"
export function InstallationFolderModal({resolver}: {resolver: (x: ModalResponse) => void}) {
export const InstallationFolderModal: ModalComponent<void> = ({resolver}) => {
const t = useTranslation();
return (
<>
<span className="absolute bg-gradient-to-r from-blue-500 to-red-500 top-0 w-full left-0 h-1"></span>
<form>
<h1 className="text-3xl uppercase tracking-wide w-full text-center text-gray-800 dark:text-gray-200">{t("modals.install-folder.title")}</h1>
<BsmImage className="mx-auto h-24" image={BeatConflict}/>
<p className="max-w-sm text-gray-800 dark:text-gray-200">{t("modals.install-folder.description")}</p>
@@ -18,6 +17,6 @@ export function InstallationFolderModal({resolver}: {resolver: (x: ModalResponse
<BsmButton typeColor="cancel" className="rounded-md text-center transition-all" onClick={() => resolver({exitCode: ModalExitCode.CANCELED})} withBar={false} text="misc.cancel"/>
<BsmButton typeColor="primary" className="rounded-md text-center transition-all" onClick={() => resolver({exitCode: ModalExitCode.COMPLETED})} withBar={false} text="modals.install-folder.buttons.submit"/>
</div>
</>
</form>
)
}
@@ -2,10 +2,10 @@ import { useState } from "react";
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 { ModalExitCode, ModalResponse } from "renderer/services/modale.service";
import { ModalComponent, ModalExitCode } from "renderer/services/modale.service";
import BeatImpatient from '../../../../../assets/images/apngs/beat-impatient.png'
export function LoginModal({resolver}: {resolver: (x: ModalResponse<any>) => void}) {
export const LoginModal: ModalComponent<{username: string, password: string, stay: boolean}> = ({resolver}) => {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
@@ -35,7 +35,7 @@ export function LoginModal({resolver}: {resolver: (x: ModalResponse<any>) => voi
</div>
<div className="grid grid-flow-col grid-cols-2 gap-4">
<BsmButton typeColor="cancel" className="rounded-md text-center transition-all" onClick={() => {resolver({exitCode: ModalExitCode.CANCELED})}} withBar={false} text="misc.cancel"/>
<BsmButton typeColor="primary" className="rounded-md text-center transition-all" type="submit" withBar={false} text={t("modals.steam-login.buttons.submit")}/>
<BsmButton typeColor="primary" className="rounded-md text-center transition-all" type="submit" withBar={false} text="modals.steam-login.buttons.submit"/>
</div>
</form>
)
@@ -1,4 +1,4 @@
import { ModalExitCode, ModalResponse, ModalService } from "../../../services/modale.service";
import { ModalComponent, ModalExitCode } from "../../../services/modale.service";
import BeatConflict from "../../../../../assets/images/apngs/beat-conflict.png"
import { BsmButton } from "renderer/components/shared/bsm-button.component";
import { BsmImage } from "renderer/components/shared/bsm-image.component";
@@ -6,9 +6,9 @@ import { useTranslation } from "renderer/hooks/use-translation.hook";
import { BSVersion } from "shared/bs-version.interface";
export function UninstallAllModsModal({resolver}: {resolver: (x: ModalResponse) => void}) {
export const UninstallAllModsModal: ModalComponent<void, BSVersion> = ({resolver, data}) => {
const version = ModalService.getInsance().getModalData<BSVersion>();
const version = data;
const t = useTranslation();
const desc = "modals.uninstall-all-mods.description";
@@ -1,4 +1,4 @@
import { ModalExitCode, ModalResponse, ModalService } from "../../../services/modale.service";
import { ModalComponent, ModalExitCode } from "../../../services/modale.service";
import BeatConflict from "../../../../../assets/images/apngs/beat-conflict.png"
import { BsmButton } from "renderer/components/shared/bsm-button.component";
import { BsmImage } from "renderer/components/shared/bsm-image.component";
@@ -6,9 +6,9 @@ import { useTranslation } from "renderer/hooks/use-translation.hook";
import { Mod } from "shared/models/mods/mod.interface";
export function UninstallModModal({resolver}: {resolver: (x: ModalResponse) => void}) {
export const UninstallModModal: ModalComponent<void, Mod> = ({resolver, data}) => {
const mod = ModalService.getInsance().getModalData<Mod>();
const mod = data;
const t = useTranslation();
const desc = mod.name.toLowerCase() === "bsipa" ? "modals.uninstall-mod.description-bsipa" : "modals.uninstall-mod.description";
@@ -1,13 +1,13 @@
import { ModalExitCode, ModalResponse, ModalService } from "../../../services/modale.service";
import { ModalComponent, ModalExitCode } from "../../../services/modale.service";
import BeatConflict from "../../../../../assets/images/apngs/beat-conflict.png"
import { BSVersion } from 'shared/bs-version.interface';
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";
export function UninstallModal({resolver}: {resolver: (x: ModalResponse) => void}) {
export const UninstallModal: ModalComponent<void, BSVersion> = ({resolver, data}) => {
const version = ModalService.getInsance().getModalData<BSVersion>();
const version = data;
const t = useTranslation();
return (
@@ -1,44 +1,28 @@
import { ModalExitCode, ModalService, ModalType } from "renderer/services/modale.service";
import { ModalExitCode, ModalService } from "renderer/services/modale.service";
import { AnimatePresence, motion } from "framer-motion";
import { LoginModal } from "./modal-types/login-modal.component";
import { GuardModal } from "./modal-types/guard-modal.component";
import { UninstallModal } from "./modal-types/uninstall-modal.component";
import { InstallationFolderModal } from "./modal-types/installation-folder-modal.component";
import { EditVersionModal } from "./modal-types/edit-version-modal.component";
import { useObservable } from "renderer/hooks/use-observable.hook";
import { useThemeColor } from "renderer/hooks/use-theme-color.hook";
import { UninstallModModal } from "./modal-types/uninstall-mod-modal.component";
import { UninstallAllModsModal } from "./modal-types/uninstall-all-mods-modal.component";
export function Modal() {
const modalSevice = ModalService.getInsance();
const modalType = useObservable(modalSevice.modalType$);
const {firstColor, secondColor} = useThemeColor();
const ModalComponent = useObservable(modalSevice.getModalToShow());
const renderModal = () => {
const resolver = modalSevice.getResolver();
if(modalType === ModalType.STEAM_LOGIN){ return <LoginModal resolver={resolver}/> }
if(modalType === ModalType.GUARD_CODE){ return <GuardModal resolver={resolver}/> }
if(modalType === ModalType.UNINSTALL){ return <UninstallModal resolver={resolver}/> }
if(modalType === ModalType.INSTALLATION_FOLDER){ return <InstallationFolderModal resolver={resolver}/> }
if(modalType === ModalType.EDIT_VERSION){ return <EditVersionModal resolver={resolver}/> }
if(modalType === ModalType.CLONE_VERSION){ return <EditVersionModal resolver={resolver}/> }
if(modalType === ModalType.UNINSTALL_MOD){ return <UninstallModModal resolver={resolver}/> }
if(modalType === ModalType.UNINSTALL_ALL_MODS){ return <UninstallAllModsModal resolver={resolver}/> }
return null;
}
const modalData = modalSevice.getModalData();
const resolver = modalSevice.getResolver();
const {firstColor, secondColor} = useThemeColor();
return (
<AnimatePresence>
{modalType && (
{ModalComponent && (
<div className="top-0 absolute w-screen h-screen flex content-center items-center justify-center z-50">
<motion.span key="modal-overlay" onClick={() => modalSevice.resolve({exitCode: ModalExitCode.NO_CHOICE})} className="absolute top-0 bottom-0 right-0 left-0 bg-black" initial={{opacity: 0}} animate={{opacity: modalType && .60}} exit={{opacity: 0}} transition={{duration: .2}}/>
<motion.span key="modal-overlay" onClick={() => modalSevice.resolve({exitCode: ModalExitCode.NO_CHOICE})} className="absolute top-0 bottom-0 right-0 left-0 bg-black" initial={{opacity: 0}} animate={{opacity: ModalComponent && .60}} exit={{opacity: 0}} transition={{duration: .2}}/>
<motion.div key="modal" initial={{y: "100vh"}} animate={{y: 0}} exit={{y: "100vh"}}>
<div className="relative p-4 text-gray-800 dark:text-gray-200 overflow-hidden rounded-md shadow-lg shadow-black bg-gradient-to-br from-light-main-color-3 to-light-main-color-2 dark:from-main-color-3 dark:to-main-color-2">
<span className="absolute top-0 w-full left-0 h-1" style={{backgroundImage: `linear-gradient(to right, ${firstColor}, ${secondColor})`}}/>
{renderModal()}
<ModalComponent resolver={resolver} data={modalData}/>
</div>
</motion.div>
</div>
+2 -2
View File
@@ -2,10 +2,10 @@ import { Observable } from "rxjs";
import { useState, useEffect } from "react";
export function useObservable<T>(observable: Observable<T>): T{
const [obsValue, setObsValue] = useState(null);
const [obsValue, setObsValue] = useState(null as T);
useEffect(() => {
const sub = observable.subscribe(val => setObsValue(val));
const sub = observable.subscribe(val => setObsValue(() => val));
return () => sub.unsubscribe();
}, [])
@@ -12,13 +12,14 @@ import { BsDownloaderService } from "renderer/services/bs-downloader.service";
import { ConfigurationService } from "renderer/services/configuration.service"
import { I18nService } from "renderer/services/i18n.service";
import { IpcService } from "renderer/services/ipc.service";
import { ModalExitCode, ModalService, ModalType } from "renderer/services/modale.service";
import { ModalExitCode, ModalService } from "renderer/services/modale.service";
import { NotificationService } from "renderer/services/notification.service";
import { ProgressBarService } from "renderer/services/progress-bar.service";
import { ThemeService } from "renderer/services/theme.service";
import { SupportersView } from "renderer/components/settings/supporters-view/supporters-view.component";
import { LinkOpenerService } from "renderer/services/link-opener.service";
import { useNavigate } from "react-router-dom";
import { InstallationFolderModal } from "renderer/components/modal/modal-types/installation-folder-modal.component";
export function SettingsPage() {
@@ -84,7 +85,7 @@ export function SettingsPage() {
const setDefaultInstallationFolder = () => {
if(!progressBarService.require()){ return; }
modalService.openModal(ModalType.INSTALLATION_FOLDER).then(async res => {
modalService.openModal(InstallationFolderModal).then(async res => {
if(res.exitCode !== ModalExitCode.COMPLETED){ return; }
const fileChooserRes = await ipcService.send<{canceled: boolean, filePaths: string[]}>("choose-folder");
@@ -8,13 +8,14 @@ import { BsmDropdownButton } from 'renderer/components/shared/bsm-dropdown-butto
import { BsmImage } from 'renderer/components/shared/bsm-image.component';
import { BSUninstallerService } from '../services/bs-uninstaller.service';
import { BSVersionManagerService } from '../services/bs-version-manager.service';
import { ModalExitCode, ModalService, ModalType } from '../services/modale.service';
import { ModalExitCode, ModalService } from '../services/modale.service';
import DefautVersionImage from "../../../assets/images/default-version-img.jpg";
import { BsDownloaderService } from 'renderer/services/bs-downloader.service';
import { IpcService } from 'renderer/services/ipc.service';
import { MapService } from 'renderer/services/maps.service';
import { LaunchSlide } from 'renderer/components/version-viewer/slides/launch/launch-slide.component';
import { ModsSlide } from 'renderer/components/version-viewer/slides/mods/mods-slide.component';
import { UninstallModal } from 'renderer/components/modal/modal-types/uninstall-modal.component';
export function VersionViewer() {
@@ -35,7 +36,7 @@ export function VersionViewer() {
const verifyFiles = () => bsDownloaderService.download(state, true);
const uninstall = async () => {
const modalCompleted = await modalService.openModal(ModalType.UNINSTALL, state)
const modalCompleted = await modalService.openModal(UninstallModal, state);
if(modalCompleted.exitCode === ModalExitCode.COMPLETED){
bsUninstallerService.uninstall(state).then(() => {
bsVersionManagerService.askInstalledVersions();
@@ -6,9 +6,11 @@ import { BSVersion } from 'shared/bs-version.interface';
import { AuthUserService } from './auth-user.service';
import { BSVersionManagerService } from './bs-version-manager.service';
import { IpcService } from './ipc.service';
import { ModalExitCode, ModalService, ModalType } from './modale.service';
import { ModalExitCode, ModalService } from './modale.service';
import { NotificationService } from './notification.service';
import { ProgressBarService } from './progress-bar.service';
import { LoginModal } from 'renderer/components/modal/modal-types/login-modal.component';
import { GuardModal } from 'renderer/components/modal/modal-types/guard-modal.component';
export class BsDownloaderService{
@@ -57,7 +59,7 @@ export class BsDownloaderService{
this.ipcService.watch<void>("bs-download.[2FA]").subscribe(async response => {
if(!response.success){ this.ipcService.sendLazy("bs-download.kill"); return; }
const res = await this.modalService.openModal(ModalType.GUARD_CODE);
const res = await this.modalService.openModal(GuardModal);
if(res.exitCode !== ModalExitCode.COMPLETED){
this.progressBarService.hide(true);
this.ipcService.sendLazy("bs-download.kill");
@@ -90,7 +92,7 @@ export class BsDownloaderService{
let promise;
if(!this.authService.sessionExist()){
const res = await this.modalService.openModal<{username: string, stay: boolean, password: string}>(ModalType.STEAM_LOGIN);
const res = await this.modalService.openModal(LoginModal);
if(res.exitCode !== ModalExitCode.COMPLETED){
this.progressBarService.hide(true);
return {success: false};
@@ -1,3 +1,5 @@
import { UninstallAllModsModal } from "renderer/components/modal/modal-types/uninstall-all-mods-modal.component";
import { UninstallModModal } from "renderer/components/modal/modal-types/uninstall-mod-modal.component";
import { Observable } from "rxjs";
import { BehaviorSubject } from "rxjs";
import { map } from "rxjs/operators";
@@ -6,7 +8,7 @@ import { InstallModsResult, UninstallModsResult } from "shared/models/mods";
import { Mod, ModInstallProgression } from "shared/models/mods";
import { ProgressionInterface } from "shared/models/progress-bar";
import { IpcService } from "./ipc.service";
import { ModalExitCode, ModalService, ModalType } from "./modale.service";
import { ModalExitCode, ModalService } from "./modale.service";
import { NotificationService, NotificationType } from "./notification.service";
import { ProgressBarService } from "./progress-bar.service";
@@ -74,7 +76,7 @@ export class BsModsManagerService {
if(!this.progressBar.require()){ return; }
const modalRes = await this.modals.openModal(ModalType.UNINSTALL_MOD, mod);
const modalRes = await this.modals.openModal(UninstallModModal, mod);
if(modalRes.exitCode !== ModalExitCode.COMPLETED){ return; }
@@ -100,7 +102,7 @@ export class BsModsManagerService {
if(!this.progressBar.require()){ return; }
const modalRes = await this.modals.openModal(ModalType.UNINSTALL_ALL_MODS, version);
const modalRes = await this.modals.openModal(UninstallAllModsModal, version);
if(modalRes.exitCode !== ModalExitCode.COMPLETED){ return; }
@@ -1,9 +1,10 @@
import { BSVersion } from 'shared/bs-version.interface';
import { BehaviorSubject } from "rxjs";
import { IpcService } from "./ipc.service";
import { ModalExitCode, ModalService, ModalType } from './modale.service';
import { ModalExitCode, ModalService } from './modale.service';
import { NotificationService } from './notification.service';
import { ProgressBarService } from './progress-bar.service';
import { EditVersionModal } from 'renderer/components/modal/modal-types/edit-version-modal.component';
export class BSVersionManagerService {
@@ -75,7 +76,7 @@ export class BSVersionManagerService {
}
public async editVersion(version: BSVersion): Promise<BSVersion>{
const modalRes = await this.modalService.openModal<{name: string, color: string}>(ModalType.EDIT_VERSION, version);
const modalRes = await this.modalService.openModal(EditVersionModal, {version, clone: false});
if(modalRes.exitCode !== ModalExitCode.COMPLETED){ return null; }
if(modalRes.data.name?.length < 2){ return null; }
return this.ipcService.send<BSVersion>("bs-version.edit", {args: {version, name: modalRes.data.name, color: modalRes.data.color}}).then(res => {
@@ -93,7 +94,7 @@ export class BSVersionManagerService {
public async cloneVersion(version: BSVersion): Promise<BSVersion>{
if(!this.progressBarService.require()){ return null; }
const modalRes = await this.modalService.openModal<{name: string, color: string}>(ModalType.CLONE_VERSION, version);
const modalRes = await this.modalService.openModal(EditVersionModal, {version, clone: true});
if(modalRes.exitCode !== ModalExitCode.COMPLETED){ return null; }
if(modalRes.data.name?.length < 2){ return null; }
this.progressBarService.showFake(0.01);
+10 -18
View File
@@ -1,3 +1,4 @@
import { Observable } from "rxjs";
import { BehaviorSubject } from "rxjs";
import { timeout } from "rxjs/operators";
@@ -5,7 +6,7 @@ export class ModalService{
private static instance: ModalService;
private _modalType$: BehaviorSubject<ModalType> = new BehaviorSubject<ModalType>(null);
private _modalToShow$: BehaviorSubject<ModalComponent> = new BehaviorSubject(null);
private modalData: any = null;
private resolver: any;
@@ -18,11 +19,7 @@ export class ModalService{
private close(){
if(this.resolver){ this.resolve({exitCode: ModalExitCode.NO_CHOICE}); }
this.modalType$.next(null);
}
public get modalType$(): BehaviorSubject<ModalType>{
return this._modalType$;
this._modalToShow$.next(null);
}
public getModalData<Type>(): Type{
@@ -37,29 +34,24 @@ export class ModalService{
this.resolver(data);
}
public async openModal<T>(modalType: ModalType, data?: any): Promise<ModalResponse<T>>{
public async openModal<T, K>(modal: ModalComponent<T, K>, data?: K): Promise<ModalResponse<T>>{
this.close();
await timeout(100); //Must wait resolve
const promise = new Promise<ModalResponse<T>>((resolve) => { this.resolver = resolve; });
this._modalToShow$.next(modal);
promise.then(() => this.close());
this.modalType$.next(modalType);
if(data){ this.modalData = data; }
else{ this.modalData = null; }
return promise;
}
public getModalToShow(): Observable<ModalComponent>{
return this._modalToShow$.asObservable();
}
}
export const enum ModalType {
STEAM_LOGIN = "STEAM_LOGIN",
GUARD_CODE = "GUARD_CODE",
UNINSTALL = "UNINSTALL",
INSTALLATION_FOLDER = "INSTALLATION_FOLDER",
EDIT_VERSION = "EDIT_VERSION",
CLONE_VERSION = "CLONE_VERSION",
UNINSTALL_MOD = "UNINSTALL_MOD",
UNINSTALL_ALL_MODS = "UNINSTALL_ALL_MODS",
}
export type ModalComponent<T = unknown, K = any> = ({resolver, data}: {resolver : (x: ModalResponse<T>) => void, data?: K}) => JSX.Element;
export const enum ModalExitCode {
NO_CHOICE = -1,