diff --git a/src/renderer/components/models-management/models-grid.component.tsx b/src/renderer/components/models-management/models-grid.component.tsx index c29f9bd6..b1186f16 100644 --- a/src/renderer/components/models-management/models-grid.component.tsx +++ b/src/renderer/components/models-management/models-grid.component.tsx @@ -1,6 +1,6 @@ import { useInView } from "framer-motion"; import { BSVersion } from "shared/bs-version.interface"; -import { useRef, forwardRef, useImperativeHandle } from "react"; +import { useRef, forwardRef, useImperativeHandle, useState } from "react"; import { MSModelType } from "shared/models/models/model-saber.model"; import { useOnUpdate } from "renderer/hooks/use-on-update.hook"; import { useConstant } from "renderer/hooks/use-constant.hook"; @@ -10,6 +10,15 @@ import { Progression } from "main/helpers/fs.helpers"; import { BsmLocalModel } from "shared/models/models/bsm-local-model.interface"; import { ModelItem } from "./model-item.component"; import { useBehaviorSubject } from "renderer/hooks/use-behavior-subject.hook"; +import { BsmImage } from "../shared/bsm-image.component"; +import BeatWaitingImg from "../../../../assets/images/apngs/beat-waiting.png" +import BeatConflict from "../../../../assets/images/apngs/beat-conflict.png" +import TextProgressBar from "../progress-bar/text-progress-bar.component"; +import { BehaviorSubject, distinctUntilChanged, map, startWith, tap } from "rxjs"; +import { BsmButton } from "../shared/bsm-button.component"; +import equal from "fast-deep-equal"; +import { VersionLinkerAction, VersionLinkerActionListener } from "renderer/services/version-folder-linker.service"; +import { MODEL_TYPE_FOLDERS } from "shared/models/models/constants"; type Props = { className?: string, @@ -26,27 +35,28 @@ export const ModelsGrid = forwardRef(({className, version, type, search, active} const ref = useRef(); const isVisible = useInView(ref, {once: true, amount: .1}); - const [models, setModelsLoadObservable,, setModels] = useSwitchableObservable>(); + const [models, setModelsLoadObservable,, setModels] = useSwitchableObservable(); + const [progress$, setProgress$] = useState(new BehaviorSubject(0).asObservable()); const [modelsSelected, modelsSelected$] = useBehaviorSubject([]); - const isLoading = !models || !models?.extra; - const hasModels = !isLoading && models?.extra.length; + + const isLoading = !models; + const hasModels = !!models?.length; useImperativeHandle(forwardRef, () => ({ getModels: () => { - console.log(type, models?.extra); - return models?.extra ?? []; + return models ?? []; }, getSelectedModels: () => { return modelsSelected; }, reloadModels: () => { - setModelsLoadObservable(() => modelsManager.$getModels(type, version)); + loadModels(); }, deleteSelectedModels: () => { modelsManager.deleteModels(modelsSelected, version).then(deleted => { if(!deleted){ return; } - models.extra = models.extra.filter(m => !modelsSelected.some(d => d.hash === m.hash)); - setModels(() => models); + const newModels = models.filter(m => !modelsSelected.some(d => d.hash === m.hash)); + setModels(() => newModels); modelsSelected$.next([]); }); @@ -55,9 +65,29 @@ export const ModelsGrid = forwardRef(({className, version, type, search, active} useOnUpdate(() => { if(!isVisible){ return; } - setModelsLoadObservable(() => modelsManager.$getModels(type, version)); + loadModels(); + + const onLinkStateChangeCb = (action: VersionLinkerAction) => { + if(!equal(version, action.version) || !action.relativeFolder.includes(MODEL_TYPE_FOLDERS[type])){ return; } + loadModels(); + } + + modelsManager.onModelsFolderLinked(onLinkStateChangeCb); + modelsManager.onModelsFolderUnlinked(onLinkStateChangeCb); + + return () => { + modelsManager.removeModelsFolderLinkedListener(onLinkStateChangeCb); + modelsManager.removeModelsFolderUnlinkedListener(onLinkStateChangeCb); + } + }, [version, isVisible, type]); + const loadModels = () => { + const modelsObs$ = modelsManager.$getModels(type, version); + setModelsLoadObservable(() => modelsObs$.pipe(map(models => models?.extra), distinctUntilChanged())); + setProgress$(modelsObs$.pipe(map(models => Math.floor((models.current / models.total) * 100)), startWith(0), distinctUntilChanged())) + } + const handleModelClick = (model: BsmLocalModel) => { const prunedArray = Array.from(new Set(modelsSelected)); if(prunedArray.some(m => m.hash === model.hash)){ @@ -70,11 +100,11 @@ export const ModelsGrid = forwardRef(({className, version, type, search, active} } const filtredModels = () => { - if(!active){ return models?.extra; } + if(!active){ return models; } const lowerSearch = search?.toLowerCase(); - return models?.extra?.filter(model => { + return models?.filter(model => { const findedInRawValues = Object.values(model).some(value => { if(typeof value !== "string" && typeof value !== "number"){ return false; } return value.toString().toLowerCase().includes(lowerSearch); @@ -96,12 +126,29 @@ export const ModelsGrid = forwardRef(({className, version, type, search, active} }) } - return ( -
- {isLoading && <>loading /** TODO that **/} - {!hasModels && <>no models /** TODO that **/} - {hasModels && !isLoading && ( -
    + const renderContent = () => { + if(isLoading){ + return ( +
    + + Chargement des models... {/** TODO TRANSLATE **/} + +
    + ) + } + + if(!hasModels){ + return ( +
    + + Aucun modelès {/** TODO TRANSLATE **/} + {e.preventDefault();}}/> +
    + ) + } + + return ( +
      {filtredModels().map(localModel => ( ))}
    - )} + ) + } + + return ( +
    + {renderContent()}
    ) }) diff --git a/src/renderer/services/ipc.service.ts b/src/renderer/services/ipc.service.ts index 63b008f0..ad01c43a 100644 --- a/src/renderer/services/ipc.service.ts +++ b/src/renderer/services/ipc.service.ts @@ -1,4 +1,4 @@ -import { defaultIfEmpty, share } from "rxjs/operators"; +import { defaultIfEmpty, share, shareReplay } from "rxjs/operators"; import { Observable } from "rxjs"; import { IpcRequest, IpcResponse } from "shared/models/ipc"; import { identity } from "rxjs"; @@ -62,7 +62,7 @@ export class IpcService { window.electron.ipcRenderer.on(request.responceChannel, (res: T) => observer.next(res)); window.electron.ipcRenderer.on(errorChannel, (err: Error) => observer.error(err)); window.electron.ipcRenderer.on(completeChannel, () => observer.complete()); - }).pipe(defaultValue ? defaultIfEmpty(defaultValue) : identity, share()); + }).pipe(defaultValue ? defaultIfEmpty(defaultValue) : identity, shareReplay(1)); window.electron.ipcRenderer.once(completeChannel, () => { window.electron.ipcRenderer.removeAllListeners(request.responceChannel); diff --git a/src/renderer/services/models-management/models-manager.service.ts b/src/renderer/services/models-management/models-manager.service.ts index 66aa30e9..cc6099e5 100644 --- a/src/renderer/services/models-management/models-manager.service.ts +++ b/src/renderer/services/models-management/models-manager.service.ts @@ -1,6 +1,6 @@ import { MSModelType } from "shared/models/models/model-saber.model"; import { IpcService } from "../ipc.service"; -import { VersionFolderLinkerService, VersionLinkerActionType } from "../version-folder-linker.service"; +import { VersionFolderLinkerService, VersionLinkerActionListener, VersionLinkerActionType } from "../version-folder-linker.service"; import { MODEL_TYPE_FOLDERS } from "shared/models/models/constants"; import { Observable, distinctUntilChanged, lastValueFrom, map, mergeMap, share } from "rxjs"; import { BSVersion } from "shared/bs-version.interface"; @@ -52,6 +52,22 @@ export class ModelsManagerService { return this.versionFolderLinked.$isVersionFolderPending(version, MODEL_TYPE_FOLDERS[type]); } + public onModelsFolderLinked(callback: VersionLinkerActionListener): void{ + return this.versionFolderLinked.onVersionFolderLinked(callback); + } + + public onModelsFolderUnlinked(callback: VersionLinkerActionListener): void{ + return this.versionFolderLinked.onVersionFolderUnlinked(callback); + } + + public removeModelsFolderLinkedListener(callback: VersionLinkerActionListener): void{ + return this.versionFolderLinked.removeVersionFolderLinkedListener(callback); + } + + public removeModelsFolderUnlinkedListener(callback: VersionLinkerActionListener): void{ + return this.versionFolderLinked.removeVersionFolderUnlinkedListener(callback); + } + public async linkModels(type: MSModelType, version?: BSVersion): Promise{ const res = await this.modalService.openModal(LinkModelsModal, type);