From ba930eda20bf78e9a5c81968e945c27f278d0676 Mon Sep 17 00:00:00 2001 From: "Timothy Z." Date: Wed, 20 May 2026 14:11:16 +0300 Subject: [PATCH 1/2] feat: support casting with server --- .../CastDevicesMenu/CastDevicesMenu.tsx | 45 +++++++++++++++++++ src/routes/Player/CastDevicesMenu/index.ts | 5 +++ src/routes/Player/CastDevicesMenu/styles.less | 6 +++ src/routes/Player/ControlBar/ControlBar.js | 23 ++++++++-- src/routes/Player/Player.js | 42 ++++++++++++++++- 5 files changed, 116 insertions(+), 5 deletions(-) create mode 100644 src/routes/Player/CastDevicesMenu/CastDevicesMenu.tsx create mode 100644 src/routes/Player/CastDevicesMenu/index.ts create mode 100644 src/routes/Player/CastDevicesMenu/styles.less diff --git a/src/routes/Player/CastDevicesMenu/CastDevicesMenu.tsx b/src/routes/Player/CastDevicesMenu/CastDevicesMenu.tsx new file mode 100644 index 000000000..567361201 --- /dev/null +++ b/src/routes/Player/CastDevicesMenu/CastDevicesMenu.tsx @@ -0,0 +1,45 @@ +// 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[], + onDeviceSelected: (deviceId: string) => void, +}; + +const CastDevicesMenu = memo(forwardRef(({ className, devices, 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.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..5832af248 --- /dev/null +++ b/src/routes/Player/CastDevicesMenu/styles.less @@ -0,0 +1,6 @@ +// Copyright (C) 2017-2026 Smart code 203358507 + +.cast-devices-menu-container { + width: 16rem; + padding: 1rem; +} diff --git a/src/routes/Player/ControlBar/ControlBar.js b/src/routes/Player/ControlBar/ControlBar.js index c9ba5cd7e..1aefdba71 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, + shellCastAvailable, + 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 ? !shellCastAvailable : !chromecastServiceActive; const onChromecastButtonClick = React.useCallback(() => { - chromecast.transport.requestSession(); - }, []); + if (castButtonDisabled) { + return; + } + if (platform.shell.active) { + if (typeof onToggleCastDevicesMenu === 'function') { + onToggleCastDevicesMenu(); + } + } else { + chromecast.transport.requestSession(); + } + }, [castButtonDisabled, platform.shell.active, onToggleCastDevicesMenu]); React.useEffect(() => { const onStateChanged = () => { setChromecastServiceActive(chromecast.active); @@ -162,7 +177,7 @@ const ControlBar = React.forwardRef(({ -