From c54185b3480b56561fcc63b68b2ddaf992b9ec43 Mon Sep 17 00:00:00 2001 From: MathieuG-P <40181755+Zagrios@users.noreply.github.com> Date: Sun, 7 Jan 2024 16:22:51 +0100 Subject: [PATCH] [chore] rework useObservable hook to properly re-subsrive when the state change --- .../available-versions-slider.component.tsx | 2 +- .../link-button.component.tsx | 8 ++--- .../map-item.component.tsx | 2 +- .../maps-playlists-panel.component.tsx | 9 ++++-- .../maps-row.component.tsx | 30 ++++++++++++++----- .../login-to-steam-modal.component.tsx | 2 +- .../download-maps-modal.component.tsx | 6 ++-- .../download-models-modal.component.tsx | 6 ++-- .../share-folders-modal.component.tsx | 6 ++-- .../components/modal/modal.component.tsx | 2 +- .../models-panel.component.tsx | 22 ++++++++------ .../nav-bar/bsmanager-icon.component.tsx | 2 +- .../shared-nav-bar-item.component.tsx | 2 +- .../components/nav-bar/nav-bar.component.tsx | 4 +-- .../notification-overlay.component.tsx | 2 +- .../bsm-progress-bar.component.tsx | 6 ++-- .../text-progress-bar.component.tsx | 2 +- .../bs-content-tab-item.component.tsx | 2 +- .../shared/iframe-view.component.tsx | 2 +- .../title-bar/title-bar.component.tsx | 2 +- .../slides/launch/launch-slide.component.tsx | 4 +-- .../slides/mods/mod-item.component.tsx | 2 +- .../slides/mods/mods-slide.component.tsx | 4 +-- .../hooks/use-behavior-subject.hook.ts | 2 +- src/renderer/hooks/use-observable.hook.ts | 8 ++--- src/renderer/hooks/use-theme-color.hook.ts | 4 +-- src/renderer/hooks/use-translation.hook.ts | 2 +- .../available-versions-list.components.tsx | 2 +- .../pages/settings-page.component.tsx | 10 +++---- .../services/version-folder-linker.service.ts | 5 ++-- .../OneClick/OneClickDownloadPlaylist.tsx | 4 +-- src/renderer/windows/ShortcutLaunch.tsx | 2 +- 32 files changed, 93 insertions(+), 75 deletions(-) diff --git a/src/renderer/components/available-versions/available-versions-slider.component.tsx b/src/renderer/components/available-versions/available-versions-slider.component.tsx index 89255959..02f45b9f 100644 --- a/src/renderer/components/available-versions/available-versions-slider.component.tsx +++ b/src/renderer/components/available-versions/available-versions-slider.component.tsx @@ -8,7 +8,7 @@ import { useObservable } from "renderer/hooks/use-observable.hook"; export function AvailableVersionsSlider() { const versionManagerService = useService(BSVersionManagerService); - const availableVersions = useObservable(versionManagerService.availableVersions$); + const availableVersions = useObservable(() => versionManagerService.availableVersions$); const [yearIndex, setYearIndex] = useState(0); const availableYears = (() => { diff --git a/src/renderer/components/maps-mangement-components/link-button.component.tsx b/src/renderer/components/maps-mangement-components/link-button.component.tsx index 5f353b77..b50bb6a3 100644 --- a/src/renderer/components/maps-mangement-components/link-button.component.tsx +++ b/src/renderer/components/maps-mangement-components/link-button.component.tsx @@ -1,23 +1,19 @@ import { motion } from "framer-motion"; -import { useState } from "react"; import { useThemeColor } from "renderer/hooks/use-theme-color.hook"; import { BsmIcon } from "../svgs/bsm-icon.component"; import { useTranslation } from "renderer/hooks/use-translation.hook"; import { FolderLinkState } from "renderer/services/version-folder-linker.service"; -import { Observable } from "rxjs"; -import { useObservable } from "renderer/hooks/use-observable.hook"; export type LinkBtnProps = { className?: string; title?: string; - state$: Observable; + state: FolderLinkState; onClick?: () => void; }; -export const LinkButton = ({className, title, state$, onClick}: LinkBtnProps) => { +export const LinkButton = ({className, title, state, onClick}: LinkBtnProps) => { const t = useTranslation(); - const state = useObservable(state$); const color = useThemeColor("first-color"); const disabled = state === FolderLinkState.Processing || state === FolderLinkState.Pending; diff --git a/src/renderer/components/maps-mangement-components/map-item.component.tsx b/src/renderer/components/maps-mangement-components/map-item.component.tsx index c015725f..49242a6f 100644 --- a/src/renderer/components/maps-mangement-components/map-item.component.tsx +++ b/src/renderer/components/maps-mangement-components/map-item.component.tsx @@ -70,7 +70,7 @@ export const MapItem = memo(({ hash, title, autor, songAutor, coverUrl, songUrl, onDoubleClick: () => onDoubleClick?.(callBackParam), }); - const songPlaying = useObservable(audioPlayer.playing$.pipe(map(playing => playing && audioPlayer.src === songUrl))); + const songPlaying = useObservable(() => audioPlayer.playing$.pipe(map(playing => playing && audioPlayer.src === songUrl))); const previewUrl = mapId ? `https://allpoland.github.io/ArcViewer/?id=${mapId}` : null; const mapUrl = mapId ? `https://beatsaver.com/maps/${mapId}` : null; diff --git a/src/renderer/components/maps-mangement-components/maps-playlists-panel.component.tsx b/src/renderer/components/maps-mangement-components/maps-playlists-panel.component.tsx index f9d0920f..3705f055 100644 --- a/src/renderer/components/maps-mangement-components/maps-playlists-panel.component.tsx +++ b/src/renderer/components/maps-mangement-components/maps-playlists-panel.component.tsx @@ -9,12 +9,14 @@ import { MapsDownloaderService } from "renderer/services/maps-downloader.service import { BsmImage } from "../shared/bsm-image.component"; import wipGif from "../../../../assets/images/gifs/wip.gif"; import { useTranslation } from "renderer/hooks/use-translation.hook"; -import { VersionFolderLinkerService, VersionLinkerActionListener } from "renderer/services/version-folder-linker.service"; +import { FolderLinkState, VersionFolderLinkerService, VersionLinkerActionListener } from "renderer/services/version-folder-linker.service"; import { useService } from "renderer/hooks/use-service.hook"; import { BsContentTabPanel } from "../shared/bs-content-tab-panel/bs-content-tab-panel.component"; import { BsmButton } from "../shared/bsm-button.component"; import { MapIcon } from "../svgs/icons/map-icon.component"; import { PlaylistIcon } from "../svgs/icons/playlist-icon.component"; +import { useObservable } from "renderer/hooks/use-observable.hook"; +import { of } from "rxjs"; type Props = { version?: BSVersion; @@ -32,6 +34,7 @@ export function MapsPlaylistsPanel({ version, isActive }: Props) { const [mapSearch, setMapSearch] = useState(""); const [playlistSearch, setPlaylistSearch] = useState(""); const [mapsLinked, setMapsLinked] = useState(false); + const mapsLinkedState = useObservable(() => version ? mapsService.$mapsFolderLinkState(version) : of(null), FolderLinkState.Unlinked, [version]); const t = useTranslation(); const mapsRef = useRef(); @@ -119,9 +122,9 @@ export function MapsPlaylistsPanel({ version, isActive }: Props) { icon: MapIcon, onClick: () => setTabIndex(0), linkProps: version ? { - state$: mapsService.$mapsFolderLinkState(version), + state: mapsLinkedState, onClick: handleMapsLinkClick, - } : undefined, + } : null, }, { text: "misc.playlists", diff --git a/src/renderer/components/maps-mangement-components/maps-row.component.tsx b/src/renderer/components/maps-mangement-components/maps-row.component.tsx index 4aa185e4..0e99fd27 100644 --- a/src/renderer/components/maps-mangement-components/maps-row.component.tsx +++ b/src/renderer/components/maps-mangement-components/maps-row.component.tsx @@ -16,13 +16,10 @@ type Props = { }; export const MapsRow = memo(({ maps, style, selectedMaps$, onMapSelect, onMapDelete }: Props) => { - const selectedMaps = useObservable( - selectedMaps$.pipe( + const selectedMaps = useObservable(() => selectedMaps$.pipe( map(selectedMaps => selectedMaps.filter(selected => maps.some(map => map.hash === selected.hash))), - distinctUntilChanged(equal) - ), - [] - ); + distinctUntilChanged(equal), + ), []); const extractMapDiffs = (map: BsmLocalMap): Map => { const res = new Map(); @@ -48,7 +45,26 @@ export const MapsRow = memo(({ maps, style, selectedMaps$, onMapSelect, onMapDel }; const renderMapItem = (map: BsmLocalMap) => { - return selected.hash === map.hash)} diffs={extractMapDiffs(map)} mapId={map.bsaverInfo?.id} ranked={map.bsaverInfo?.ranked} autorId={map.bsaverInfo?.uploader?.id} likes={map.bsaverInfo?.stats?.upvotes} createdAt={map.bsaverInfo?.createdAt} onDelete={onMapDelete} onSelected={onMapSelect} callBackParam={map} />; + return selected.hash === map.hash)} + diffs={extractMapDiffs(map)} mapId={map.bsaverInfo?.id} + ranked={map.bsaverInfo?.ranked} + autorId={map.bsaverInfo?.uploader?.id} + likes={map.bsaverInfo?.stats?.upvotes} + createdAt={map.bsaverInfo?.createdAt} + onDelete={onMapDelete} + onSelected={onMapSelect} + callBackParam={map} + />; }; return ( diff --git a/src/renderer/components/modal/modal-types/bs-downgrade/login-to-steam-modal.component.tsx b/src/renderer/components/modal/modal-types/bs-downgrade/login-to-steam-modal.component.tsx index ef511bf6..5d4169d2 100644 --- a/src/renderer/components/modal/modal-types/bs-downgrade/login-to-steam-modal.component.tsx +++ b/src/renderer/components/modal/modal-types/bs-downgrade/login-to-steam-modal.component.tsx @@ -21,7 +21,7 @@ export const LoginToSteamModal: ModalComponent< const [password, setPassword] = useState(""); const [stay, setStay] = useState(false); const [showPassword, setShowPassword] = useState(false); - const qrCodeUrl = useObservable(data.qrCode$); + const qrCodeUrl = useObservable(() => data.qrCode$); const t = useTranslation(); useEffect(() => { diff --git a/src/renderer/components/modal/modal-types/download-maps-modal.component.tsx b/src/renderer/components/modal/modal-types/download-maps-modal.component.tsx index a42e710f..04fa513b 100644 --- a/src/renderer/components/modal/modal-types/download-maps-modal.component.tsx +++ b/src/renderer/components/modal/modal-types/download-maps-modal.component.tsx @@ -27,8 +27,8 @@ export const DownloadMapsModal: ModalComponent mapsDownloader.currentMapDownload$); + const mapsInQueue = useObservable(() => mapsDownloader.mapsInQueue$); const t = useTranslation(); const filterContainerRef = useRef(null); const [filter, setFilter] = useState({}); @@ -37,7 +37,7 @@ export const DownloadMapsModal: ModalComponent(BSV_SORT_ORDER.at(0)); const [ownedMapHashs, setOwnedMapHashs] = useState(ownedMaps?.map(map => map.hash) ?? []); const [loading, setLoading] = useState(false); - const isOnline = useObservable(os.isOnline$); + const isOnline = useObservable(() => os.isOnline$); const [searchParams, setSearchParams] = useState({ sortOrder, filter, diff --git a/src/renderer/components/modal/modal-types/models/download-models-modal.component.tsx b/src/renderer/components/modal/modal-types/models/download-models-modal.component.tsx index 0d0b1a76..d6982bd3 100644 --- a/src/renderer/components/modal/modal-types/models/download-models-modal.component.tsx +++ b/src/renderer/components/modal/modal-types/models/download-models-modal.component.tsx @@ -44,10 +44,10 @@ export const DownloadModelsModal: ModalComponent modelsDownloader.currentDownload$(), null); + const downloadQueue = useObservable(() => modelsDownloader.getQueue$(), []); const [msModels, msModels$] = useBehaviorSubject([]); - const isOnline = useObservable(os.isOnline$, true); + const isOnline = useObservable(() => os.isOnline$, true); const [error, error$] = useBehaviorSubject(false); const [isLoading, isLoading$] = useBehaviorSubject(false); const [ownedModels, setOwnedModels] = useState(owned ?? []); diff --git a/src/renderer/components/modal/modal-types/share-folders-modal.component.tsx b/src/renderer/components/modal/modal-types/share-folders-modal.component.tsx index a4d33886..c04865d7 100644 --- a/src/renderer/components/modal/modal-types/share-folders-modal.component.tsx +++ b/src/renderer/components/modal/modal-types/share-folders-modal.component.tsx @@ -3,7 +3,6 @@ import { useEffect, useState } from "react"; import { LinkButton } from "renderer/components/maps-mangement-components/link-button.component"; import { BsmBasicSpinner } from "renderer/components/shared/bsm-basic-spinner/bsm-basic-spinner.component"; import { BsmButton } from "renderer/components/shared/bsm-button.component"; -import { useConstant } from "renderer/hooks/use-constant.hook"; import { useObservable } from "renderer/hooks/use-observable.hook"; import { useService } from "renderer/hooks/use-service.hook"; import { useThemeColor } from "renderer/hooks/use-theme-color.hook"; @@ -108,8 +107,7 @@ const FolderItem = ({ version, relativeFolder, onDelete }: FolderProps) => { const t = useTranslation(); const color = useThemeColor("first-color"); - const state$ = useConstant(() => linker.$folderLinkedState(version, relativeFolder)); - const state = useObservable(state$); + const state = useObservable(() => linker.$folderLinkedState(version, relativeFolder), FolderLinkState.Unlinked, [version, relativeFolder]); const name = relativeFolder.split(window.electron.path.sep).at(-1); const onClickLink = () => { @@ -141,7 +139,7 @@ const FolderItem = ({ version, relativeFolder, onDelete }: FolderProps) => { diff --git a/src/renderer/components/modal/modal.component.tsx b/src/renderer/components/modal/modal.component.tsx index 17aeb47e..53bde46c 100644 --- a/src/renderer/components/modal/modal.component.tsx +++ b/src/renderer/components/modal/modal.component.tsx @@ -8,7 +8,7 @@ import { BsmIcon } from "../svgs/bsm-icon.component"; export function Modal() { const modalSevice = ModalService.getInstance(); - const ModalComponent = useObservable(modalSevice.getModalToShow()); + const ModalComponent = useObservable(() => modalSevice.getModalToShow()); const modalData = modalSevice.getModalData(); const resolver = modalSevice.getResolver(); diff --git a/src/renderer/components/models-management/models-panel.component.tsx b/src/renderer/components/models-management/models-panel.component.tsx index 2b0b7a0b..1b1f96fe 100644 --- a/src/renderer/components/models-management/models-panel.component.tsx +++ b/src/renderer/components/models-management/models-panel.component.tsx @@ -13,16 +13,16 @@ import { NotificationService } from "renderer/services/notification.service"; import { ConfigurationService } from "renderer/services/configuration.service"; import { useTranslation } from "renderer/hooks/use-translation.hook"; import { lt } from "semver"; -import { lastValueFrom, take } from "rxjs"; +import { lastValueFrom, of, take } from "rxjs"; import { FolderLinkState } from "renderer/services/version-folder-linker.service"; import { BsContentTabItemProps } from "../shared/bs-content-tab-panel/bs-content-tab-item.component"; import { BsContentTabPanel } from "../shared/bs-content-tab-panel/bs-content-tab-panel.component"; -import { LinkBtnProps } from "../maps-mangement-components/link-button.component"; import { ModelTypeAvatarIcon } from "../svgs/icons/model-type-avatar-icon.component"; import { SvgIcon } from "../svgs/svg-icon.type"; import { ModelTypeSaberIcon } from "../svgs/icons/model-type-saber-icon.component"; import { ModelTypePlatformIcon } from "../svgs/icons/model-type-platform-icon.component"; import { ModelTypeBloqIcon } from "../svgs/icons/model-type-bloq-icon.component"; +import { useObservable } from "renderer/hooks/use-observable.hook"; export function ModelsPanel({ version, isActive, goToMods }: { version?: BSVersion; isActive: boolean; goToMods?: () => void }) { const modelsManager = useService(ModelsManagerService); @@ -38,9 +38,15 @@ export function ModelsPanel({ version, isActive, goToMods }: { version?: BSVersi const [modelTypeTab, setModelTypeTab] = useState(MSModelType.Avatar); const [currentTabIndex, setCurrentTabIndex] = useState(0); - const [search, setSearch] = useState(""); + const modelsLinkStats = { + [MSModelType.Avatar]: useObservable(() => version ? modelsManager.$modelsLinkingState(version, MSModelType.Avatar) : of(null), FolderLinkState.Unlinked, [version]), + [MSModelType.Saber]: useObservable(() => version ? modelsManager.$modelsLinkingState(version, MSModelType.Saber) : of(null), FolderLinkState.Unlinked, [version]), + [MSModelType.Platfrom]: useObservable(() => version ? modelsManager.$modelsLinkingState(version, MSModelType.Platfrom) : of(null), FolderLinkState.Unlinked, [version]), + [MSModelType.Bloq]: useObservable(() => version ? modelsManager.$modelsLinkingState(version, MSModelType.Bloq) : of(null), FolderLinkState.Unlinked, [version]), + } + useOnUpdate(() => { if (!isActive || !goToMods) { return; @@ -114,11 +120,6 @@ export function ModelsPanel({ version, isActive, goToMods }: { version?: BSVersi } } - const linkProps: LinkBtnProps = version ? { - state$: modelsManager.$modelsLinkingState(version, model), - onClick, - } : undefined; - const getModelIcon = (model: MSModelType): SvgIcon => { switch (model) { case MSModelType.Avatar: @@ -138,7 +139,10 @@ export function ModelsPanel({ version, isActive, goToMods }: { version?: BSVersi text: `models.types.plural.${model}`, icon: getModelIcon(model), onClick: () => setModelTypeTab(model), - linkProps + linkProps: version ? { + onClick, + state: modelsLinkStats[model], + } : null } }; diff --git a/src/renderer/components/nav-bar/bsmanager-icon.component.tsx b/src/renderer/components/nav-bar/bsmanager-icon.component.tsx index 0a0ddcda..b4e1355d 100644 --- a/src/renderer/components/nav-bar/bsmanager-icon.component.tsx +++ b/src/renderer/components/nav-bar/bsmanager-icon.component.tsx @@ -12,7 +12,7 @@ export const BsManagerIcon = memo(({ className }: { className?: string }) => { const audioPlayer = useService(AudioPlayerService); const { firstColor, secondColor } = useThemeColor(); - const playing = useObservable(audioPlayer.playing$); + const playing = useObservable(() => audioPlayer.playing$); const { bpm } = audioPlayer; diff --git a/src/renderer/components/nav-bar/nav-bar-items/shared-nav-bar-item.component.tsx b/src/renderer/components/nav-bar/nav-bar-items/shared-nav-bar-item.component.tsx index 2b24dfb5..a820cdfc 100644 --- a/src/renderer/components/nav-bar/nav-bar-items/shared-nav-bar-item.component.tsx +++ b/src/renderer/components/nav-bar/nav-bar-items/shared-nav-bar-item.component.tsx @@ -12,7 +12,7 @@ export function SharedNavBarItem() { const pageState = useService(PageStateService); const t = useTranslation(); - const route = useObservable(pageState.route$); + const route = useObservable(() => pageState.route$); const color = useThemeColor("first-color"); return ( diff --git a/src/renderer/components/nav-bar/nav-bar.component.tsx b/src/renderer/components/nav-bar/nav-bar.component.tsx index adb87168..f3fcc58b 100644 --- a/src/renderer/components/nav-bar/nav-bar.component.tsx +++ b/src/renderer/components/nav-bar/nav-bar.component.tsx @@ -19,8 +19,8 @@ export function NavBar() { const versionManager = useService(BSVersionManagerService); const versionDownloader = useService(BsDownloaderService); - const downloadingVersion = useObservable(versionDownloader.downloadingVersion$.pipe(distinctUntilChanged(equal))); - const installedVersions = useObservable(versionManager.installedVersions$); + const downloadingVersion = useObservable(() => versionDownloader.downloadingVersion$.pipe(distinctUntilChanged(equal))); + const installedVersions = useObservable(() => versionManager.installedVersions$); const color = useThemeColor("first-color"); const t = useTranslation(); diff --git a/src/renderer/components/notification/notification-overlay.component.tsx b/src/renderer/components/notification/notification-overlay.component.tsx index 4709235a..eea04f13 100644 --- a/src/renderer/components/notification/notification-overlay.component.tsx +++ b/src/renderer/components/notification/notification-overlay.component.tsx @@ -7,7 +7,7 @@ import { useService } from "renderer/hooks/use-service.hook"; export function NotificationOverlay() { const notificationService = useService(NotificationService); - const notifications = useObservable(notificationService.notifications$); + const notifications = useObservable(() => notificationService.notifications$); return (
    diff --git a/src/renderer/components/progress-bar/bsm-progress-bar.component.tsx b/src/renderer/components/progress-bar/bsm-progress-bar.component.tsx index 13eea1d6..1b005c79 100644 --- a/src/renderer/components/progress-bar/bsm-progress-bar.component.tsx +++ b/src/renderer/components/progress-bar/bsm-progress-bar.component.tsx @@ -9,9 +9,9 @@ import { useService } from "renderer/hooks/use-service.hook"; export function BsmProgressBar() { const progressBarService = useService(ProgressBarService); - const progressData = useObservable(progressBarService.progressData$); - const visible = useObservable(progressBarService.visible$); - const style = useObservable(progressBarService.style$); + const progressData = useObservable(() => progressBarService.progressData$); + const visible = useObservable(() => progressBarService.visible$); + const style = useObservable(() => progressBarService.style$); const progressLabel = (() => { if (!progressData) { diff --git a/src/renderer/components/progress-bar/text-progress-bar.component.tsx b/src/renderer/components/progress-bar/text-progress-bar.component.tsx index 3179f2a2..81964e87 100644 --- a/src/renderer/components/progress-bar/text-progress-bar.component.tsx +++ b/src/renderer/components/progress-bar/text-progress-bar.component.tsx @@ -9,7 +9,7 @@ type Props = { }; export default function TextProgressBar({ value$, className, style }: Props) { - const value = useObservable(value$); + const value = useObservable(() => value$); const prefix = typeof value === "number" ? "%" : ""; diff --git a/src/renderer/components/shared/bs-content-tab-panel/bs-content-tab-item.component.tsx b/src/renderer/components/shared/bs-content-tab-panel/bs-content-tab-item.component.tsx index d8c6511d..336dc3df 100644 --- a/src/renderer/components/shared/bs-content-tab-panel/bs-content-tab-item.component.tsx +++ b/src/renderer/components/shared/bs-content-tab-panel/bs-content-tab-item.component.tsx @@ -32,7 +32,7 @@ export const BsContentTabItem: BsContentTabItemComponent = ({ text, icon: Icon, {linkProps && (
    linkService.iframeLink$); const ref = useRef(null); useClickOutside(ref, () => linkService.closeIframe()); diff --git a/src/renderer/components/title-bar/title-bar.component.tsx b/src/renderer/components/title-bar/title-bar.component.tsx index 9045d91f..13c12717 100644 --- a/src/renderer/components/title-bar/title-bar.component.tsx +++ b/src/renderer/components/title-bar/title-bar.component.tsx @@ -16,7 +16,7 @@ export default function TitleBar({ template = "index.html" }: { template: AppWin const ipcService = useService(IpcService); const audio = useService(AudioPlayerService); - const volume = useObservable(audio.volume$, audio.volume); + const volume = useObservable(() => audio.volume$, audio.volume); const color = useThemeColor("first-color"); const [previewVersion, setPreviewVersion] = useState(null); diff --git a/src/renderer/components/version-viewer/slides/launch/launch-slide.component.tsx b/src/renderer/components/version-viewer/slides/launch/launch-slide.component.tsx index 67d3100d..bbe412af 100644 --- a/src/renderer/components/version-viewer/slides/launch/launch-slide.component.tsx +++ b/src/renderer/components/version-viewer/slides/launch/launch-slide.component.tsx @@ -29,9 +29,9 @@ export function LaunchSlide({ version }: Props) { const [debugMode, setDebugMode] = useState(!!configService.get(LaunchMods.DEBUG_MOD)); const [advancedLaunch, setAdvancedLaunch] = useState(false); const [additionalArgsString, setAdditionalArgsString] = useState(configService.get("additionnal-args") || ""); - const versionDownloading = useObservable(bsDownloader.downloadingVersion$); + const versionDownloading = useObservable(() => bsDownloader.downloadingVersion$); - const versionRunning = useObservable(bsLauncherService.versionRunning$); + const versionRunning = useObservable(() => bsLauncherService.versionRunning$); useEffect(() => { configService.set("additionnal-args", additionalArgsString); diff --git a/src/renderer/components/version-viewer/slides/mods/mod-item.component.tsx b/src/renderer/components/version-viewer/slides/mods/mod-item.component.tsx index 611e968d..56c24cd7 100644 --- a/src/renderer/components/version-viewer/slides/mods/mod-item.component.tsx +++ b/src/renderer/components/version-viewer/slides/mods/mod-item.component.tsx @@ -17,7 +17,7 @@ export function ModItem({ className, mod, installedVersion, isDependency, isSele const pageState = useService(PageStateService); const themeColor = useThemeColor("second-color"); - const uninstalling = useObservable(modsManager.isUninstalling$); + const uninstalling = useObservable(() => modsManager.isUninstalling$); const clickRef = useRef(); useDoubleClick({ diff --git a/src/renderer/components/version-viewer/slides/mods/mods-slide.component.tsx b/src/renderer/components/version-viewer/slides/mods/mods-slide.component.tsx index 01edb60d..0f980b1e 100644 --- a/src/renderer/components/version-viewer/slides/mods/mods-slide.component.tsx +++ b/src/renderer/components/version-viewer/slides/mods/mods-slide.component.tsx @@ -35,8 +35,8 @@ export function ModsSlide({ version, onDisclamerDecline }: { version: BSVersion; const [modsInstalled, setModsInstalled] = useState(null as Map); const [modsSelected, setModsSelected] = useState([] as Mod[]); const [moreInfoMod, setMoreInfoMod] = useState(null as Mod); - const isOnline = useObservable(os.isOnline$); - const installing = useObservable(modsManager.isInstalling$); + const isOnline = useObservable(() => os.isOnline$); + const installing = useObservable(() => modsManager.isInstalling$); const downloadRef = useRef(null); const [downloadWith, setDownloadWidth] = useState(0); diff --git a/src/renderer/hooks/use-behavior-subject.hook.ts b/src/renderer/hooks/use-behavior-subject.hook.ts index ad783517..685c6d81 100644 --- a/src/renderer/hooks/use-behavior-subject.hook.ts +++ b/src/renderer/hooks/use-behavior-subject.hook.ts @@ -4,7 +4,7 @@ import { useConstant } from "./use-constant.hook"; export function useBehaviorSubject(value: T): [T, BehaviorSubject] { const subject$ = useConstant(() => new BehaviorSubject(value)); - const subjectValue = useObservable(subject$, value); + const subjectValue = useObservable(() => subject$, value); return [subjectValue, subject$]; } diff --git a/src/renderer/hooks/use-observable.hook.ts b/src/renderer/hooks/use-observable.hook.ts index 7c928bb2..baf21621 100644 --- a/src/renderer/hooks/use-observable.hook.ts +++ b/src/renderer/hooks/use-observable.hook.ts @@ -1,13 +1,13 @@ import { Observable } from "rxjs"; import { useState, useEffect } from "react"; -export function useObservable(observable: Observable, defaultValue?: T): T { - const [obsValue, setObsValue] = useState(defaultValue ?? (null as T)); +export function useObservable(factory: () => Observable, initValue?: T, deps?: unknown[]): T { + const [obsValue, setObsValue] = useState(initValue); useEffect(() => { - const sub = observable.subscribe(val => setObsValue(() => val)); + const sub = factory().subscribe(val => setObsValue(() => val)); return () => sub.unsubscribe(); - }, []); + }, deps ?? []); return obsValue; } diff --git a/src/renderer/hooks/use-theme-color.hook.ts b/src/renderer/hooks/use-theme-color.hook.ts index 9bf38aa4..8f4b3571 100644 --- a/src/renderer/hooks/use-theme-color.hook.ts +++ b/src/renderer/hooks/use-theme-color.hook.ts @@ -9,8 +9,8 @@ export function useThemeColor(themeColor: ThemeColor): string; export function useThemeColor(themeColor?: ThemeColor): string | { firstColor: string; secondColor: string } { const configService = useService(ConfigurationService); - const firstColor = useObservable(!themeColor ? configService.watch("first-color" as DefaultConfigKey).pipe(throttleTime(16)) : configService.watch(themeColor).pipe(throttleTime(16))); - const secondColor = useObservable(!themeColor ? configService.watch("second-color" as DefaultConfigKey).pipe(throttleTime(16)) : of("")); + const firstColor = useObservable(() => !themeColor ? configService.watch("first-color" as DefaultConfigKey).pipe(throttleTime(16)) : configService.watch(themeColor).pipe(throttleTime(16))); + const secondColor = useObservable(() => !themeColor ? configService.watch("second-color" as DefaultConfigKey).pipe(throttleTime(16)) : of("")); if (themeColor) { return firstColor; diff --git a/src/renderer/hooks/use-translation.hook.ts b/src/renderer/hooks/use-translation.hook.ts index b98f3241..5663835c 100644 --- a/src/renderer/hooks/use-translation.hook.ts +++ b/src/renderer/hooks/use-translation.hook.ts @@ -6,7 +6,7 @@ export function useTranslation(): (translationKey: string, args?: Record i18nService.currentLanguage$); return (key: string, args?: Record) => { if (!key) { diff --git a/src/renderer/pages/available-versions-list.components.tsx b/src/renderer/pages/available-versions-list.components.tsx index 045251e5..eed439a8 100644 --- a/src/renderer/pages/available-versions-list.components.tsx +++ b/src/renderer/pages/available-versions-list.components.tsx @@ -22,7 +22,7 @@ export function AvailableVersionsList() { const [selectedVersion, setSelectedVersion] = useState(null); const contextValue = useMemo(() => ({ selectedVersion, setSelectedVersion }), [selectedVersion]); - const downloading = useObservable(bsDownloader.downloadingVersion$.pipe(map(v => !!v))); + const downloading = useObservable(() => bsDownloader.downloadingVersion$.pipe(map(v => !!v))); const t = useTranslation(); const startDownload = async () => { diff --git a/src/renderer/pages/settings-page.component.tsx b/src/renderer/pages/settings-page.component.tsx index 7bf12f88..9420f7b4 100644 --- a/src/renderer/pages/settings-page.component.tsx +++ b/src/renderer/pages/settings-page.component.tsx @@ -77,9 +77,9 @@ export function SettingsPage() { const nav = useNavigate(); const t = useTranslation(); - const themeSelected = useObservable(themeService.theme$, "os"); - const languageSelected = useObservable(i18nService.currentLanguage$, i18nService.getFallbackLanguage()); - const downloadStore = useObservable(bsDownloader.defaultStore$); + const themeSelected = useObservable(() => themeService.theme$, "os"); + const languageSelected = useObservable(() => i18nService.currentLanguage$, i18nService.getFallbackLanguage()); + const downloadStore = useObservable(() => bsDownloader.defaultStore$); const [installationFolder, setInstallationFolder] = useState(null); const [showSupporters, setShowSupporters] = useState(false); @@ -87,7 +87,7 @@ export function SettingsPage() { const [playlistsDeepLinkEnabled, setPlaylistsDeepLinkEnabled] = useState(false); const [modelsDeepLinkEnabled, setModelsDeepLinkEnabled] = useState(false); const [hasDownloaderSession, setHasDownloaderSession] = useState(false); - const appVersion = useObservable(ipcService.sendV2("current-version")); + const appVersion = useObservable(() => ipcService.sendV2("current-version")); useEffect(() => { loadInstallationFolder(); @@ -252,7 +252,7 @@ export function SettingsPage() {
    - + diff --git a/src/renderer/services/version-folder-linker.service.ts b/src/renderer/services/version-folder-linker.service.ts index d6a58b1e..936de9d6 100644 --- a/src/renderer/services/version-folder-linker.service.ts +++ b/src/renderer/services/version-folder-linker.service.ts @@ -4,6 +4,7 @@ import { BehaviorSubject, Observable, of } from "rxjs"; import { BSVersion } from "shared/bs-version.interface"; import { IpcService } from "./ipc.service"; import { ProgressBarService } from "./progress-bar.service"; +import equal from "fast-deep-equal"; export class VersionFolderLinkerService { private static instance: VersionFolderLinkerService; @@ -132,11 +133,11 @@ export class VersionFolderLinkerService { return this._queue$.pipe( mergeMap(queue => { const currentAction = queue.at(0); - if(currentAction && currentAction.version === version && currentAction.relativeFolder === relativeFolder) { + if(currentAction && equal(currentAction.version, version) && currentAction.relativeFolder === relativeFolder) { return of(FolderLinkState.Processing) } - if(queue.some(action => action.version === version && action.relativeFolder === relativeFolder)) { + if(queue.some(action => equal(action.version, version) && action.relativeFolder === relativeFolder)) { return of(FolderLinkState.Pending); } diff --git a/src/renderer/windows/OneClick/OneClickDownloadPlaylist.tsx b/src/renderer/windows/OneClick/OneClickDownloadPlaylist.tsx index 8dab7d1b..a21bf590 100644 --- a/src/renderer/windows/OneClick/OneClickDownloadPlaylist.tsx +++ b/src/renderer/windows/OneClick/OneClickDownloadPlaylist.tsx @@ -24,8 +24,8 @@ export default function OneClickDownloadPlaylist() { const mapsContainer = useRef(null); const playlistUrl = useConstant(() => new URLSearchParams(window.location.search).get("playlistUrl")); const download$ = useConstant(() => playlistDownloader.oneClickInstallPlaylist(playlistUrl)); - const playlistInfos = useObservable(download$.pipe(filter(progress => !!progress.data?.playlistInfos), map(progress => progress.data.playlistInfos), take(1))); - const downloadedMaps = useObservable(download$.pipe(filter(progress => !!progress.data?.downloadedMaps), map(progress => progress.data.downloadedMaps))); + const playlistInfos = useObservable(() => download$.pipe(filter(progress => !!progress.data?.playlistInfos), map(progress => progress.data.playlistInfos), take(1))); + const downloadedMaps = useObservable(() => download$.pipe(filter(progress => !!progress.data?.downloadedMaps), map(progress => progress.data.downloadedMaps))); useEffect(() => { diff --git a/src/renderer/windows/ShortcutLaunch.tsx b/src/renderer/windows/ShortcutLaunch.tsx index ca2f2a87..1049676a 100644 --- a/src/renderer/windows/ShortcutLaunch.tsx +++ b/src/renderer/windows/ShortcutLaunch.tsx @@ -26,7 +26,7 @@ export default function ShortcutLaunch() { const t = useTranslation(); const color = useThemeColor("second-color"); - const launchOptions = useObservable(ipc.sendV2("shortcut-launch-options").pipe(take(1)), null) + const launchOptions = useObservable(() => ipc.sendV2("shortcut-launch-options").pipe(take(1)), null) const [rotation, setRotation] = useState(0); const [status, setStatus] = useState();