mirror of
https://github.com/Zagrios/bs-manager.git
synced 2026-07-03 14:08:25 +02:00
[chore] rework useObservable hook to properly re-subsrive when the state change
This commit is contained in:
@@ -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;
|
||||
|
||||
+6
-3
@@ -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 (
|
||||
|
||||
+1
-1
@@ -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,
|
||||
|
||||
+3
-3
@@ -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" ? "%" : "";
|
||||
|
||||
|
||||
+1
-1
@@ -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$];
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
|
||||
@@ -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>();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user