[chore] rework useObservable hook to properly re-subsrive when the state change

This commit is contained in:
MathieuG-P
2024-01-07 16:22:51 +01:00
parent cc42f0a978
commit c54185b348
32 changed files with 93 additions and 75 deletions
@@ -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 = (() => {
@@ -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<FolderLinkState>;
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;
@@ -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;
@@ -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<any>();
@@ -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",
@@ -16,13 +16,10 @@ type Props = {
};
export const MapsRow = memo(({ maps, style, selectedMaps$, onMapSelect, onMapDelete }: Props) => {
const selectedMaps = useObservable<BsmLocalMap[]>(
selectedMaps$.pipe(
const selectedMaps = useObservable<BsmLocalMap[]>(() => selectedMaps$.pipe(
map(selectedMaps => selectedMaps.filter(selected => maps.some(map => map.hash === selected.hash))),
distinctUntilChanged(equal)
),
[]
);
distinctUntilChanged(equal),
), []);
const extractMapDiffs = (map: BsmLocalMap): Map<BsvMapCharacteristic, ParsedMapDiff[]> => {
const res = new Map<BsvMapCharacteristic, ParsedMapDiff[]>();
@@ -48,7 +45,26 @@ export const MapsRow = memo(({ maps, style, selectedMaps$, onMapSelect, onMapDel
};
const renderMapItem = (map: BsmLocalMap) => {
return <MapItem key={map.hash} hash={map.hash} title={map.rawInfo._songName} coverUrl={map.coverUrl} songUrl={map.songUrl} autor={map.rawInfo._levelAuthorName} songAutor={map.rawInfo._songAuthorName} bpm={map.rawInfo._beatsPerMinute} duration={map.bsaverInfo?.metadata?.duration} selected={selectedMaps.some(selected => 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 <MapItem
key={map.hash}
hash={map.hash}
title={map.rawInfo._songName}
coverUrl={map.coverUrl}
songUrl={map.songUrl}
autor={map.rawInfo._levelAuthorName}
songAutor={map.rawInfo._songAuthorName}
bpm={map.rawInfo._beatsPerMinute}
duration={map.bsaverInfo?.metadata?.duration}
selected={selectedMaps.some(selected => 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 (
@@ -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(() => {
@@ -27,8 +27,8 @@ export const DownloadMapsModal: ModalComponent<void, { version: BSVersion; owned
const progressBar = useService(ProgressBarService);
const os = useService(OsDiagnosticService);
const currentDownload = useObservable(mapsDownloader.currentMapDownload$);
const mapsInQueue = useObservable(mapsDownloader.mapsInQueue$);
const currentDownload = useObservable(() => mapsDownloader.currentMapDownload$);
const mapsInQueue = useObservable(() => mapsDownloader.mapsInQueue$);
const t = useTranslation();
const filterContainerRef = useRef(null);
const [filter, setFilter] = useState<MapFilter>({});
@@ -37,7 +37,7 @@ export const DownloadMapsModal: ModalComponent<void, { version: BSVersion; owned
const [sortOrder, setSortOrder] = useState<SearchOrder>(BSV_SORT_ORDER.at(0));
const [ownedMapHashs, setOwnedMapHashs] = useState<string[]>(ownedMaps?.map(map => map.hash) ?? []);
const [loading, setLoading] = useState(false);
const isOnline = useObservable(os.isOnline$);
const isOnline = useObservable(() => os.isOnline$);
const [searchParams, setSearchParams] = useState<SearchParams>({
sortOrder,
filter,
@@ -44,10 +44,10 @@ export const DownloadModelsModal: ModalComponent<void, { version: BSVersion; typ
}))
);
const currentDownload = useObservable(modelsDownloader.currentDownload$(), null);
const downloadQueue = useObservable(modelsDownloader.getQueue$(), []);
const currentDownload = useObservable(() => modelsDownloader.currentDownload$(), null);
const downloadQueue = useObservable(() => modelsDownloader.getQueue$(), []);
const [msModels, msModels$] = useBehaviorSubject<MSModel[]>([]);
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<BsmLocalModel[]>(owned ?? []);
@@ -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) => {
<Tippy placement="left" content={t(`modals.shared-folders.buttons.${state === FolderLinkState.Linked ? "unlink-folder" : "link-folder"}`)} arrow={false}>
<LinkButton
className="p-0.5 h-7 shrink-0 aspect-square blur-0 cursor-pointer hover:brightness-75"
state$={state$}
state={state}
onClick={onClickLink}
/>
</Tippy>
@@ -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();
@@ -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>(MSModelType.Avatar);
const [currentTabIndex, setCurrentTabIndex] = useState<number>(0);
const [search, setSearch] = useState<string>("");
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
}
};
@@ -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;
@@ -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 (
@@ -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();
@@ -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 (
<ul className="absolute h-full w-0 top-0 right-0 z-[100] pt-10">
@@ -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) {
@@ -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" ? "%" : "";
@@ -32,7 +32,7 @@ export const BsContentTabItem: BsContentTabItemComponent = ({ text, icon: Icon,
{linkProps && (
<div className="flex items-center absolute top-1.5 left-1.5">
<LinkButton
state$={linkProps.state$}
state={linkProps.state}
className={linkProps.className ?? "block w-6 h-6 aspect-square blur-0 cursor-pointer hover:brightness-75"}
title={linkProps.title}
onClick={linkProps.onClick}
@@ -8,7 +8,7 @@ import { LinkOpenerService } from "renderer/services/link-opener.service";
export function BsmIframeView() {
const linkService = useService(LinkOpenerService);
const iframeUrl = useObservable(linkService.iframeLink$);
const iframeUrl = useObservable(() => linkService.iframeLink$);
const ref = useRef(null);
useClickOutside(ref, () => linkService.closeIframe());
@@ -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);
@@ -29,9 +29,9 @@ export function LaunchSlide({ version }: Props) {
const [debugMode, setDebugMode] = useState(!!configService.get<boolean>(LaunchMods.DEBUG_MOD));
const [advancedLaunch, setAdvancedLaunch] = useState(false);
const [additionalArgsString, setAdditionalArgsString] = useState<string>(configService.get<string>("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);
@@ -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({
@@ -35,8 +35,8 @@ export function ModsSlide({ version, onDisclamerDecline }: { version: BSVersion;
const [modsInstalled, setModsInstalled] = useState(null as Map<string, Mod[]>);
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);
@@ -4,7 +4,7 @@ import { useConstant } from "./use-constant.hook";
export function useBehaviorSubject<T>(value: T): [T, BehaviorSubject<T>] {
const subject$ = useConstant(() => new BehaviorSubject(value));
const subjectValue = useObservable(subject$, value);
const subjectValue = useObservable(() => subject$, value);
return [subjectValue, subject$];
}
+4 -4
View File
@@ -1,13 +1,13 @@
import { Observable } from "rxjs";
import { useState, useEffect } from "react";
export function useObservable<T>(observable: Observable<T>, defaultValue?: T): T {
const [obsValue, setObsValue] = useState(defaultValue ?? (null as T));
export function useObservable<T>(factory: () => Observable<T>, initValue?: T, deps?: unknown[]): T {
const [obsValue, setObsValue] = useState<T>(initValue);
useEffect(() => {
const sub = observable.subscribe(val => setObsValue(() => val));
const sub = factory().subscribe(val => setObsValue(() => val));
return () => sub.unsubscribe();
}, []);
}, deps ?? []);
return obsValue;
}
+2 -2
View File
@@ -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<string>("first-color" as DefaultConfigKey).pipe(throttleTime(16)) : configService.watch<string>(themeColor).pipe(throttleTime(16)));
const secondColor = useObservable(!themeColor ? configService.watch<string>("second-color" as DefaultConfigKey).pipe(throttleTime(16)) : of(""));
const firstColor = useObservable(() => !themeColor ? configService.watch<string>("first-color" as DefaultConfigKey).pipe(throttleTime(16)) : configService.watch<string>(themeColor).pipe(throttleTime(16)));
const secondColor = useObservable(() => !themeColor ? configService.watch<string>("second-color" as DefaultConfigKey).pipe(throttleTime(16)) : of(""));
if (themeColor) {
return firstColor;
+1 -1
View File
@@ -6,7 +6,7 @@ export function useTranslation(): (translationKey: string, args?: Record<string,
const i18nService = useService(I18nService);
useObservable(i18nService.currentLanguage$);
useObservable(() => i18nService.currentLanguage$);
return (key: string, args?: Record<string, string>) => {
if (!key) {
@@ -22,7 +22,7 @@ export function AvailableVersionsList() {
const [selectedVersion, setSelectedVersion] = useState<BSVersion>(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 () => {
@@ -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<string>("current-version"));
const appVersion = useObservable(() => ipcService.sendV2<string>("current-version"));
useEffect(() => {
loadInstallationFolder();
@@ -252,7 +252,7 @@ export function SettingsPage() {
</div>
</div>
<SettingContainer minorTitle="pages.settings.appearance.sub-title" className="mt-3">
<SettingRadioArray items={themeItem} selectedItemValue={themeSelected} onItemSelected={handleChangeTheme} />
<SettingRadioArray items={themeItem} selectedItemValue={themeSelected as ThemeConfig} onItemSelected={handleChangeTheme} />
</SettingContainer>
</SettingContainer>
@@ -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);
}
@@ -24,8 +24,8 @@ export default function OneClickDownloadPlaylist() {
const mapsContainer = useRef<HTMLDivElement>(null);
const playlistUrl = useConstant(() => new URLSearchParams(window.location.search).get("playlistUrl"));
const download$ = useConstant(() => playlistDownloader.oneClickInstallPlaylist(playlistUrl));
const playlistInfos = useObservable<BPList>(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<BPList>(() => 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(() => {
+1 -1
View File
@@ -26,7 +26,7 @@ export default function ShortcutLaunch() {
const t = useTranslation();
const color = useThemeColor("second-color");
const launchOptions = useObservable(ipc.sendV2<LaunchOption>("shortcut-launch-options").pipe(take(1)), null)
const launchOptions = useObservable(() => ipc.sendV2<LaunchOption>("shortcut-launch-options").pipe(take(1)), null)
const [rotation, setRotation] = useState(0);
const [status, setStatus] = useState<BSLaunchEventType>();