diff --git a/src/routes/Player/CastDevicesMenu/CastDevicesMenu.tsx b/src/routes/Player/CastDevicesMenu/CastDevicesMenu.tsx new file mode 100644 index 000000000..92cf7dd58 --- /dev/null +++ b/src/routes/Player/CastDevicesMenu/CastDevicesMenu.tsx @@ -0,0 +1,56 @@ +// Copyright (C) 2017-2026 Smart code 203358507 + +import React, { forwardRef, memo, MouseEvent } from 'react'; +import { useTranslation } from 'react-i18next'; +import classNames from 'classnames'; +import Option from '../OptionsMenu/Option'; +import styles from './styles.less'; + +type CastDevice = { + id: string, + name: string, +}; + +type Props = { + className: string, + devices: CastDevice[], + loading: boolean, + onDeviceSelected: (deviceId: string) => void, +}; + +const CastDevicesMenu = memo(forwardRef(({ className, devices, loading, onDeviceSelected }, ref) => { + const { t } = useTranslation(); + + const onMouseDown = (event: MouseEvent) => { + // @ts-expect-error: Property 'castDevicesMenuClosePrevented' does not exist on type 'MouseEvent'. + event.nativeEvent.castDevicesMenuClosePrevented = true; + }; + + return ( +
+ { + devices.length > 0 ? + devices.map(({ id, name }) => ( +
+ ); +})); + +export default CastDevicesMenu; diff --git a/src/routes/Player/CastDevicesMenu/index.ts b/src/routes/Player/CastDevicesMenu/index.ts new file mode 100644 index 000000000..76ea8ae60 --- /dev/null +++ b/src/routes/Player/CastDevicesMenu/index.ts @@ -0,0 +1,5 @@ +// Copyright (C) 2017-2026 Smart code 203358507 + +import CastDevicesMenu from './CastDevicesMenu'; + +export default CastDevicesMenu; diff --git a/src/routes/Player/CastDevicesMenu/styles.less b/src/routes/Player/CastDevicesMenu/styles.less new file mode 100644 index 000000000..4b4d861c6 --- /dev/null +++ b/src/routes/Player/CastDevicesMenu/styles.less @@ -0,0 +1,13 @@ +// Copyright (C) 2017-2026 Smart code 203358507 + +.cast-devices-menu-container { + width: 16rem; + padding: 1rem; + + .message { + padding: 1rem; + color: var(--primary-foreground-color); + opacity: 0.7; + text-align: center; + } +} diff --git a/src/routes/Player/ControlBar/ControlBar.js b/src/routes/Player/ControlBar/ControlBar.js index c9ba5cd7e..5523717b1 100644 --- a/src/routes/Player/ControlBar/ControlBar.js +++ b/src/routes/Player/ControlBar/ControlBar.js @@ -39,6 +39,8 @@ const ControlBar = React.forwardRef(({ onToggleSpeedMenu, onToggleSideDrawer, onToggleOptionsMenu, + shellCastSupported, + onToggleCastDevicesMenu, videoScale, videoScaleLabel, onVideoScaleChanged, @@ -68,6 +70,9 @@ const ControlBar = React.forwardRef(({ const onStatisticsButtonMouseDown = React.useCallback((event) => { event.nativeEvent.statisticsMenuClosePrevented = true; }, []); + const onCastDevicesButtonMouseDown = React.useCallback((event) => { + event.nativeEvent.castDevicesMenuClosePrevented = true; + }, []); const onPlayPauseButtonClick = React.useCallback(() => { if (paused) { if (typeof onPlayRequested === 'function') { @@ -95,9 +100,19 @@ const ControlBar = React.forwardRef(({ } } }, [muted, onMuteRequested, onUnmuteRequested]); + const castButtonDisabled = platform.shell.active ? !shellCastSupported : !chromecastServiceActive; const onChromecastButtonClick = React.useCallback(() => { + if (platform.shell.active) { + if (shellCastSupported && typeof onToggleCastDevicesMenu === 'function') { + onToggleCastDevicesMenu(); + } + return; + } + if (castButtonDisabled) { + return; + } chromecast.transport.requestSession(); - }, []); + }, [castButtonDisabled, platform.shell.active, shellCastSupported, onToggleCastDevicesMenu]); React.useEffect(() => { const onStateChanged = () => { setChromecastServiceActive(chromecast.active); @@ -162,7 +177,7 @@ const ControlBar = React.forwardRef(({ -