refactor feature hooks to be conditionally initialized

This commit is contained in:
jeffvli
2026-01-21 02:23:25 -08:00
parent 9a4a8eb742
commit f4072c183b
12 changed files with 236 additions and 35 deletions
@@ -1,6 +1,6 @@
import { SetActivity, StatusDisplayType } from '@xhayper/discord-rpc';
import isElectron from 'is-electron';
import { useCallback, useEffect, useRef, useState } from 'react';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { api } from '/@/renderer/api';
import { useItemImageUrl } from '/@/renderer/components/item-image/item-image';
@@ -12,6 +12,7 @@ import {
useLastfmApiKey,
usePlayerSong,
usePlayerStore,
useSettingsStore,
useTimestampStoreBase,
} from '/@/renderer/store';
import { sentenceCase } from '/@/renderer/utils';
@@ -410,3 +411,21 @@ export const useDiscordRpc = () => {
setActivity,
]);
};
const DiscordRpcHookInner = () => {
useDiscordRpc();
return null;
};
export const DiscordRpcHook = () => {
const isElectronEnv = isElectron();
const isDiscordRpcEnabled = useSettingsStore((state) => state.discord.enabled);
const isPrivateMode = useAppStore((state) => state.privateMode);
const discordRpc = isElectronEnv ? window.api.discordRpc : null;
if (!isElectronEnv || !discordRpc || !isDiscordRpcEnabled || isPrivateMode) {
return null;
}
return React.createElement(DiscordRpcHookInner);
};
@@ -150,3 +150,19 @@ export const useMainPlayerListener = () => {
toggleShuffle,
]);
};
const MainPlayerListenerHookInner = () => {
useMainPlayerListener();
return null;
};
export const MainPlayerListenerHook = () => {
const isElectronEnv = isElectron();
const mpvPlayerListener = isElectronEnv ? window.api.mpvPlayerListener : null;
if (mpvPlayerListener === null) {
return null;
}
return <MainPlayerListenerHookInner />;
};
@@ -3,24 +3,24 @@ import { useEffect } from 'react';
import { eventEmitter } from '/@/renderer/events/event-emitter';
import { UserFavoriteEventPayload, UserRatingEventPayload } from '/@/renderer/events/events';
import { useDiscordRpc } from '/@/renderer/features/discord-rpc/use-discord-rpc';
import { useMainPlayerListener } from '/@/renderer/features/player/audio-player/hooks/use-main-player-listener';
import { DiscordRpcHook } from '/@/renderer/features/discord-rpc/use-discord-rpc';
import { MainPlayerListenerHook } from '/@/renderer/features/player/audio-player/hooks/use-main-player-listener';
import { MpvPlayer } from '/@/renderer/features/player/audio-player/mpv-player';
import { WebPlayer } from '/@/renderer/features/player/audio-player/web-player';
import { useAutoDJ } from '/@/renderer/features/player/hooks/use-auto-dj';
import { useMediaSession } from '/@/renderer/features/player/hooks/use-media-session';
import { useMPRIS } from '/@/renderer/features/player/hooks/use-mpris';
import { usePlaybackHotkeys } from '/@/renderer/features/player/hooks/use-playback-hotkeys';
import { usePowerSaveBlocker } from '/@/renderer/features/player/hooks/use-power-save-blocker';
import { useQueueRestoreTimestamp } from '/@/renderer/features/player/hooks/use-queue-restore';
import { useScrobble } from '/@/renderer/features/player/hooks/use-scrobble';
import { useUpdateCurrentSong } from '/@/renderer/features/player/hooks/use-update-current-song';
import { AutoDJHook } from '/@/renderer/features/player/hooks/use-auto-dj';
import { MediaSessionHook } from '/@/renderer/features/player/hooks/use-media-session';
import { MPRISHook } from '/@/renderer/features/player/hooks/use-mpris';
import { PlaybackHotkeysHook } from '/@/renderer/features/player/hooks/use-playback-hotkeys';
import { PowerSaveBlockerHook } from '/@/renderer/features/player/hooks/use-power-save-blocker';
import { QueueRestoreTimestampHook } from '/@/renderer/features/player/hooks/use-queue-restore';
import { ScrobbleHook } from '/@/renderer/features/player/hooks/use-scrobble';
import { UpdateCurrentSongHook } from '/@/renderer/features/player/hooks/use-update-current-song';
import { useWebAudio } from '/@/renderer/features/player/hooks/use-webaudio';
import { RadioWebPlayer } from '/@/renderer/features/radio/components/radio-web-player';
import {
RadioAudioInstanceHook,
RadioMetadataHook,
useIsRadioActive,
useRadioAudioInstance,
useRadioMetadata,
} from '/@/renderer/features/radio/hooks/use-radio-player';
import {
updateQueueFavorites,
@@ -46,19 +46,54 @@ export const AudioPlayers = () => {
} = usePlaybackSettings();
const { setWebAudio, webAudio: audioContext } = useWebAudio();
useScrobble();
usePowerSaveBlocker();
useDiscordRpc();
useMPRIS();
useMainPlayerListener();
useMediaSession();
usePlaybackHotkeys();
useAutoDJ();
useQueueRestoreTimestamp();
useUpdateCurrentSong();
return (
<>
<ScrobbleHook />
<PowerSaveBlockerHook />
<DiscordRpcHook />
<MPRISHook />
<MainPlayerListenerHook />
<MediaSessionHook />
<PlaybackHotkeysHook />
<AutoDJHook />
<QueueRestoreTimestampHook />
<UpdateCurrentSongHook />
<RadioAudioInstanceHook />
<RadioMetadataHook />
<AudioPlayersContent
audioContext={audioContext}
audioDeviceId={audioDeviceId}
audioSampleRateHz={audioSampleRateHz}
playbackType={playbackType}
resetSampleRate={resetSampleRate}
serverId={serverId}
setWebAudio={setWebAudio}
webAudio={webAudio}
/>
</>
);
};
useRadioAudioInstance();
useRadioMetadata();
const AudioPlayersContent = ({
audioContext,
audioDeviceId,
audioSampleRateHz,
playbackType,
resetSampleRate,
serverId,
setWebAudio,
webAudio,
}: {
audioContext: ReturnType<typeof useWebAudio>['webAudio'];
audioDeviceId: null | string | undefined;
audioSampleRateHz: number | undefined;
playbackType: PlayerType;
resetSampleRate: ReturnType<typeof useSettingsStoreActions>['resetSampleRate'];
serverId: null | string;
setWebAudio: ReturnType<typeof useWebAudio>['setWebAudio'];
webAudio: boolean;
}) => {
const isRadioActive = useIsRadioActive();
useEffect(() => {
if (webAudio && 'AudioContext' in window) {
@@ -143,8 +178,6 @@ export const AudioPlayers = () => {
};
}, [serverId]);
const isRadioActive = useIsRadioActive();
if (isRadioActive && playbackType === PlayerType.LOCAL) {
return <MpvPlayer />;
}
@@ -1,5 +1,5 @@
import { useQueryClient } from '@tanstack/react-query';
import { useEffect } from 'react';
import React, { useEffect } from 'react';
import { queryKeys } from '/@/renderer/api/query-keys';
import { eventEmitter } from '/@/renderer/events/event-emitter';
@@ -13,6 +13,7 @@ import {
useCurrentServerId,
usePlayerStore,
usePlayerStoreBase,
useSettingsStore,
} from '/@/renderer/store';
import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { logMsg } from '/@/renderer/utils/logger-message';
@@ -232,3 +233,18 @@ export const useAutoDJ = () => {
settings.timing,
]);
};
const AutoDJHookInner = () => {
useAutoDJ();
return null;
};
export const AutoDJHook = () => {
const isAutoDJEnabled = useSettingsStore((state) => state.autoDJ.enabled);
if (!isAutoDJEnabled) {
return null;
}
return React.createElement(AutoDJHookInner);
};
@@ -1,11 +1,12 @@
import isElectron from 'is-electron';
import { useCallback, useEffect, useMemo } from 'react';
import React, { useCallback, useEffect, useMemo } from 'react';
import { getItemImageUrl } from '/@/renderer/components/item-image/item-image';
import { usePlayerEvents } from '/@/renderer/features/player/audio-player/hooks/use-player-events';
import { usePlayer } from '/@/renderer/features/player/context/player-context';
import {
usePlaybackSettings,
usePlaybackType,
usePlayerStore,
useSettingsStore,
useSkipButtons,
@@ -143,3 +144,25 @@ export const useMediaSession = () => {
[isMediaSessionEnabled, mediaSession],
);
};
const MediaSessionHookInner = () => {
useMediaSession();
return null;
};
export const MediaSessionHook = () => {
const isElectronEnv = isElectron();
const playbackType = usePlaybackType();
const isMediaSessionEnabled = useSettingsStore((state) => state.playback.mediaSession);
// We always want to enable media session when on web
// Otherwise, only enable if it is explicitly enabled in the settings AND using the web player
const shouldUseMediaSession =
!isElectronEnv || (isMediaSessionEnabled && playbackType === PlayerType.WEB);
if (!shouldUseMediaSession) {
return null;
}
return React.createElement(MediaSessionHookInner);
};
@@ -1,5 +1,5 @@
import isElectron from 'is-electron';
import { useEffect } from 'react';
import React, { useEffect } from 'react';
import { useItemImageUrl } from '/@/renderer/components/item-image/item-image';
import { usePlayerEvents } from '/@/renderer/features/player/audio-player/hooks/use-player-events';
@@ -119,3 +119,20 @@ export const useMPRIS = () => {
[],
);
};
const MPRISHookInner = () => {
useMPRIS();
return null;
};
export const MPRISHook = () => {
const isElectronEnv = isElectron();
const utils = isElectronEnv ? window.api.utils : null;
const mpris = isElectronEnv && utils?.isLinux() ? window.api.mpris : null;
if (mpris === null) {
return null;
}
return React.createElement(MPRISHookInner);
};
@@ -38,3 +38,8 @@ export const usePlaybackHotkeys = () => {
useHotkeys(playbackHotkeysItems);
};
export const PlaybackHotkeysHook = () => {
usePlaybackHotkeys();
return null;
};
@@ -1,8 +1,7 @@
import isElectron from 'is-electron';
import { useCallback, useEffect } from 'react';
import React, { useCallback, useEffect } from 'react';
import { usePlayerStatus } from '/@/renderer/store';
import { useWindowSettings } from '/@/renderer/store';
import { usePlayerStatus, useSettingsStore, useWindowSettings } from '/@/renderer/store';
import { PlayerStatus } from '/@/shared/types/types';
const ipc = isElectron() ? window.api.ipc : null;
@@ -48,3 +47,19 @@ export const usePowerSaveBlocker = () => {
};
}, [stopPowerSaveBlocker]);
};
const PowerSaveBlockerHookInner = () => {
usePowerSaveBlocker();
return null;
};
export const PowerSaveBlockerHook = () => {
const isElectronEnv = isElectron();
const preventSleepOnPlayback = useSettingsStore((state) => state.window.preventSleepOnPlayback);
if (!isElectronEnv || !preventSleepOnPlayback) {
return null;
}
return React.createElement(PowerSaveBlockerHookInner);
};
@@ -32,6 +32,11 @@ export const useQueueRestoreTimestamp = () => {
);
};
export const QueueRestoreTimestampHook = () => {
useQueueRestoreTimestamp();
return null;
};
export const useSaveQueue = () => {
const serverId = useCurrentServerId();
@@ -1,4 +1,4 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { useItemImageUrl } from '/@/renderer/components/item-image/item-image';
import { usePlayerEvents } from '/@/renderer/features/player/audio-player/hooks/use-player-events';
@@ -8,6 +8,7 @@ import {
usePlaybackSettings,
usePlayerSong,
usePlayerStore,
useSettingsStore,
useTimestampStoreBase,
} from '/@/renderer/store';
import { LogCategory, logFn } from '/@/renderer/utils/logger';
@@ -425,3 +426,19 @@ export const useScrobble = () => {
[handleScrobbleFromSongChange, handleProgressUpdate, handleScrobbleFromSeek],
);
};
const ScrobbleHookInner = () => {
useScrobble();
return null;
};
export const ScrobbleHook = () => {
const isScrobbleEnabled = useSettingsStore((state) => state.playback.scrobble.enabled);
const privateMode = useAppStore((state) => state.privateMode);
if (!isScrobbleEnabled || privateMode) {
return null;
}
return React.createElement(ScrobbleHookInner);
};
@@ -80,3 +80,8 @@ export const useUpdateCurrentSong = () => {
[handleSongChange],
);
};
export const UpdateCurrentSongHook = () => {
useUpdateCurrentSong();
return null;
};
@@ -1,6 +1,6 @@
import IcecastMetadataStats from 'icecast-metadata-stats';
import isElectron from 'is-electron';
import { useEffect } from 'react';
import React, { useEffect } from 'react';
import { createWithEqualityFn } from 'zustand/traditional';
import { usePlayerEvents } from '/@/renderer/features/player/audio-player/hooks/use-player-events';
@@ -287,3 +287,33 @@ export const useRadioMetadata = () => {
};
}, [currentStreamUrl, setMetadata, isUsingMpv]);
};
const RadioAudioInstanceHookInner = () => {
useRadioAudioInstance();
return null;
};
export const RadioAudioInstanceHook = () => {
const isRadioActive = useIsRadioActive();
if (!isRadioActive) {
return null;
}
return React.createElement(RadioAudioInstanceHookInner);
};
const RadioMetadataHookInner = () => {
useRadioMetadata();
return null;
};
export const RadioMetadataHook = () => {
const isRadioActive = useIsRadioActive();
if (!isRadioActive) {
return null;
}
return React.createElement(RadioMetadataHookInner);
};