mirror of
https://github.com/jeffvli/feishin.git
synced 2026-06-16 16:34:24 +02:00
refactor feature hooks to be conditionally initialized
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import { SetActivity, StatusDisplayType } from '@xhayper/discord-rpc';
|
import { SetActivity, StatusDisplayType } from '@xhayper/discord-rpc';
|
||||||
import isElectron from 'is-electron';
|
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 { api } from '/@/renderer/api';
|
||||||
import { useItemImageUrl } from '/@/renderer/components/item-image/item-image';
|
import { useItemImageUrl } from '/@/renderer/components/item-image/item-image';
|
||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
useLastfmApiKey,
|
useLastfmApiKey,
|
||||||
usePlayerSong,
|
usePlayerSong,
|
||||||
usePlayerStore,
|
usePlayerStore,
|
||||||
|
useSettingsStore,
|
||||||
useTimestampStoreBase,
|
useTimestampStoreBase,
|
||||||
} from '/@/renderer/store';
|
} from '/@/renderer/store';
|
||||||
import { sentenceCase } from '/@/renderer/utils';
|
import { sentenceCase } from '/@/renderer/utils';
|
||||||
@@ -410,3 +411,21 @@ export const useDiscordRpc = () => {
|
|||||||
setActivity,
|
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,
|
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 { eventEmitter } from '/@/renderer/events/event-emitter';
|
||||||
import { UserFavoriteEventPayload, UserRatingEventPayload } from '/@/renderer/events/events';
|
import { UserFavoriteEventPayload, UserRatingEventPayload } from '/@/renderer/events/events';
|
||||||
import { useDiscordRpc } from '/@/renderer/features/discord-rpc/use-discord-rpc';
|
import { DiscordRpcHook } from '/@/renderer/features/discord-rpc/use-discord-rpc';
|
||||||
import { useMainPlayerListener } from '/@/renderer/features/player/audio-player/hooks/use-main-player-listener';
|
import { MainPlayerListenerHook } from '/@/renderer/features/player/audio-player/hooks/use-main-player-listener';
|
||||||
import { MpvPlayer } from '/@/renderer/features/player/audio-player/mpv-player';
|
import { MpvPlayer } from '/@/renderer/features/player/audio-player/mpv-player';
|
||||||
import { WebPlayer } from '/@/renderer/features/player/audio-player/web-player';
|
import { WebPlayer } from '/@/renderer/features/player/audio-player/web-player';
|
||||||
import { useAutoDJ } from '/@/renderer/features/player/hooks/use-auto-dj';
|
import { AutoDJHook } from '/@/renderer/features/player/hooks/use-auto-dj';
|
||||||
import { useMediaSession } from '/@/renderer/features/player/hooks/use-media-session';
|
import { MediaSessionHook } from '/@/renderer/features/player/hooks/use-media-session';
|
||||||
import { useMPRIS } from '/@/renderer/features/player/hooks/use-mpris';
|
import { MPRISHook } from '/@/renderer/features/player/hooks/use-mpris';
|
||||||
import { usePlaybackHotkeys } from '/@/renderer/features/player/hooks/use-playback-hotkeys';
|
import { PlaybackHotkeysHook } from '/@/renderer/features/player/hooks/use-playback-hotkeys';
|
||||||
import { usePowerSaveBlocker } from '/@/renderer/features/player/hooks/use-power-save-blocker';
|
import { PowerSaveBlockerHook } from '/@/renderer/features/player/hooks/use-power-save-blocker';
|
||||||
import { useQueueRestoreTimestamp } from '/@/renderer/features/player/hooks/use-queue-restore';
|
import { QueueRestoreTimestampHook } from '/@/renderer/features/player/hooks/use-queue-restore';
|
||||||
import { useScrobble } from '/@/renderer/features/player/hooks/use-scrobble';
|
import { ScrobbleHook } from '/@/renderer/features/player/hooks/use-scrobble';
|
||||||
import { useUpdateCurrentSong } from '/@/renderer/features/player/hooks/use-update-current-song';
|
import { UpdateCurrentSongHook } from '/@/renderer/features/player/hooks/use-update-current-song';
|
||||||
import { useWebAudio } from '/@/renderer/features/player/hooks/use-webaudio';
|
import { useWebAudio } from '/@/renderer/features/player/hooks/use-webaudio';
|
||||||
import { RadioWebPlayer } from '/@/renderer/features/radio/components/radio-web-player';
|
import { RadioWebPlayer } from '/@/renderer/features/radio/components/radio-web-player';
|
||||||
import {
|
import {
|
||||||
|
RadioAudioInstanceHook,
|
||||||
|
RadioMetadataHook,
|
||||||
useIsRadioActive,
|
useIsRadioActive,
|
||||||
useRadioAudioInstance,
|
|
||||||
useRadioMetadata,
|
|
||||||
} from '/@/renderer/features/radio/hooks/use-radio-player';
|
} from '/@/renderer/features/radio/hooks/use-radio-player';
|
||||||
import {
|
import {
|
||||||
updateQueueFavorites,
|
updateQueueFavorites,
|
||||||
@@ -46,19 +46,54 @@ export const AudioPlayers = () => {
|
|||||||
} = usePlaybackSettings();
|
} = usePlaybackSettings();
|
||||||
const { setWebAudio, webAudio: audioContext } = useWebAudio();
|
const { setWebAudio, webAudio: audioContext } = useWebAudio();
|
||||||
|
|
||||||
useScrobble();
|
return (
|
||||||
usePowerSaveBlocker();
|
<>
|
||||||
useDiscordRpc();
|
<ScrobbleHook />
|
||||||
useMPRIS();
|
<PowerSaveBlockerHook />
|
||||||
useMainPlayerListener();
|
<DiscordRpcHook />
|
||||||
useMediaSession();
|
<MPRISHook />
|
||||||
usePlaybackHotkeys();
|
<MainPlayerListenerHook />
|
||||||
useAutoDJ();
|
<MediaSessionHook />
|
||||||
useQueueRestoreTimestamp();
|
<PlaybackHotkeysHook />
|
||||||
useUpdateCurrentSong();
|
<AutoDJHook />
|
||||||
|
<QueueRestoreTimestampHook />
|
||||||
|
<UpdateCurrentSongHook />
|
||||||
|
<RadioAudioInstanceHook />
|
||||||
|
<RadioMetadataHook />
|
||||||
|
<AudioPlayersContent
|
||||||
|
audioContext={audioContext}
|
||||||
|
audioDeviceId={audioDeviceId}
|
||||||
|
audioSampleRateHz={audioSampleRateHz}
|
||||||
|
playbackType={playbackType}
|
||||||
|
resetSampleRate={resetSampleRate}
|
||||||
|
serverId={serverId}
|
||||||
|
setWebAudio={setWebAudio}
|
||||||
|
webAudio={webAudio}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
useRadioAudioInstance();
|
const AudioPlayersContent = ({
|
||||||
useRadioMetadata();
|
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(() => {
|
useEffect(() => {
|
||||||
if (webAudio && 'AudioContext' in window) {
|
if (webAudio && 'AudioContext' in window) {
|
||||||
@@ -143,8 +178,6 @@ export const AudioPlayers = () => {
|
|||||||
};
|
};
|
||||||
}, [serverId]);
|
}, [serverId]);
|
||||||
|
|
||||||
const isRadioActive = useIsRadioActive();
|
|
||||||
|
|
||||||
if (isRadioActive && playbackType === PlayerType.LOCAL) {
|
if (isRadioActive && playbackType === PlayerType.LOCAL) {
|
||||||
return <MpvPlayer />;
|
return <MpvPlayer />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
import { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
|
|
||||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||||
import { eventEmitter } from '/@/renderer/events/event-emitter';
|
import { eventEmitter } from '/@/renderer/events/event-emitter';
|
||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
useCurrentServerId,
|
useCurrentServerId,
|
||||||
usePlayerStore,
|
usePlayerStore,
|
||||||
usePlayerStoreBase,
|
usePlayerStoreBase,
|
||||||
|
useSettingsStore,
|
||||||
} from '/@/renderer/store';
|
} from '/@/renderer/store';
|
||||||
import { LogCategory, logFn } from '/@/renderer/utils/logger';
|
import { LogCategory, logFn } from '/@/renderer/utils/logger';
|
||||||
import { logMsg } from '/@/renderer/utils/logger-message';
|
import { logMsg } from '/@/renderer/utils/logger-message';
|
||||||
@@ -232,3 +233,18 @@ export const useAutoDJ = () => {
|
|||||||
settings.timing,
|
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 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 { getItemImageUrl } from '/@/renderer/components/item-image/item-image';
|
||||||
import { usePlayerEvents } from '/@/renderer/features/player/audio-player/hooks/use-player-events';
|
import { usePlayerEvents } from '/@/renderer/features/player/audio-player/hooks/use-player-events';
|
||||||
import { usePlayer } from '/@/renderer/features/player/context/player-context';
|
import { usePlayer } from '/@/renderer/features/player/context/player-context';
|
||||||
import {
|
import {
|
||||||
usePlaybackSettings,
|
usePlaybackSettings,
|
||||||
|
usePlaybackType,
|
||||||
usePlayerStore,
|
usePlayerStore,
|
||||||
useSettingsStore,
|
useSettingsStore,
|
||||||
useSkipButtons,
|
useSkipButtons,
|
||||||
@@ -143,3 +144,25 @@ export const useMediaSession = () => {
|
|||||||
[isMediaSessionEnabled, mediaSession],
|
[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 isElectron from 'is-electron';
|
||||||
import { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
|
|
||||||
import { useItemImageUrl } from '/@/renderer/components/item-image/item-image';
|
import { useItemImageUrl } from '/@/renderer/components/item-image/item-image';
|
||||||
import { usePlayerEvents } from '/@/renderer/features/player/audio-player/hooks/use-player-events';
|
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);
|
useHotkeys(playbackHotkeysItems);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const PlaybackHotkeysHook = () => {
|
||||||
|
usePlaybackHotkeys();
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import isElectron from 'is-electron';
|
import isElectron from 'is-electron';
|
||||||
import { useCallback, useEffect } from 'react';
|
import React, { useCallback, useEffect } from 'react';
|
||||||
|
|
||||||
import { usePlayerStatus } from '/@/renderer/store';
|
import { usePlayerStatus, useSettingsStore, useWindowSettings } from '/@/renderer/store';
|
||||||
import { useWindowSettings } from '/@/renderer/store';
|
|
||||||
import { PlayerStatus } from '/@/shared/types/types';
|
import { PlayerStatus } from '/@/shared/types/types';
|
||||||
|
|
||||||
const ipc = isElectron() ? window.api.ipc : null;
|
const ipc = isElectron() ? window.api.ipc : null;
|
||||||
@@ -48,3 +47,19 @@ export const usePowerSaveBlocker = () => {
|
|||||||
};
|
};
|
||||||
}, [stopPowerSaveBlocker]);
|
}, [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 = () => {
|
export const useSaveQueue = () => {
|
||||||
const serverId = useCurrentServerId();
|
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 { useItemImageUrl } from '/@/renderer/components/item-image/item-image';
|
||||||
import { usePlayerEvents } from '/@/renderer/features/player/audio-player/hooks/use-player-events';
|
import { usePlayerEvents } from '/@/renderer/features/player/audio-player/hooks/use-player-events';
|
||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
usePlaybackSettings,
|
usePlaybackSettings,
|
||||||
usePlayerSong,
|
usePlayerSong,
|
||||||
usePlayerStore,
|
usePlayerStore,
|
||||||
|
useSettingsStore,
|
||||||
useTimestampStoreBase,
|
useTimestampStoreBase,
|
||||||
} from '/@/renderer/store';
|
} from '/@/renderer/store';
|
||||||
import { LogCategory, logFn } from '/@/renderer/utils/logger';
|
import { LogCategory, logFn } from '/@/renderer/utils/logger';
|
||||||
@@ -425,3 +426,19 @@ export const useScrobble = () => {
|
|||||||
[handleScrobbleFromSongChange, handleProgressUpdate, handleScrobbleFromSeek],
|
[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],
|
[handleSongChange],
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const UpdateCurrentSongHook = () => {
|
||||||
|
useUpdateCurrentSong();
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import IcecastMetadataStats from 'icecast-metadata-stats';
|
import IcecastMetadataStats from 'icecast-metadata-stats';
|
||||||
import isElectron from 'is-electron';
|
import isElectron from 'is-electron';
|
||||||
import { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { createWithEqualityFn } from 'zustand/traditional';
|
import { createWithEqualityFn } from 'zustand/traditional';
|
||||||
|
|
||||||
import { usePlayerEvents } from '/@/renderer/features/player/audio-player/hooks/use-player-events';
|
import { usePlayerEvents } from '/@/renderer/features/player/audio-player/hooks/use-player-events';
|
||||||
@@ -287,3 +287,33 @@ export const useRadioMetadata = () => {
|
|||||||
};
|
};
|
||||||
}, [currentStreamUrl, setMetadata, isUsingMpv]);
|
}, [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);
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user