diff --git a/src/card.ts b/src/card.ts index 283e3cf7..975089d9 100644 --- a/src/card.ts +++ b/src/card.ts @@ -195,8 +195,9 @@ class FrigateCard extends LitElement { this._config, this._controller.getCameraManager(), view, - this._controller.getExpandManager().isExpanded(), { + inExpandedMode: this._controller.getExpandManager().isExpanded(), + inFullscreenMode: this._controller.getFullscreenManager().isInFullscreen(), currentMediaLoadedInfo: this._controller.getMediaLoadedInfoManager().get(), showCameraUIButton: this._controller.getCameraURLManager().hasCameraURL(), mediaPlayerController: this._controller.getMediaPlayerManager(), diff --git a/src/utils/card-controller/controller.ts b/src/utils/card-controller/controller.ts index 58bab556..264cf91e 100644 --- a/src/utils/card-controller/controller.ts +++ b/src/utils/card-controller/controller.ts @@ -1,5 +1,3 @@ -// TODO: Test HA state connection/disconnect logic in real life. -// TODO: Should not need to import screenfull anywhere except the fullscreen manager. // TODO: executeMediaQueryForView should not need a HTMLElement host parameter see the view-manager.ts call in particular. // TODO: Split out zod schema and add tests for config parsing (e.g. view.scan.show_status argument was missing) diff --git a/src/utils/menu-controller.ts b/src/utils/menu-controller.ts index 0b426e7c..45a61097 100644 --- a/src/utils/menu-controller.ts +++ b/src/utils/menu-controller.ts @@ -1,6 +1,5 @@ import { HomeAssistant } from 'custom-card-helpers'; import { StyleInfo } from 'lit/directives/style-map'; -import screenfull from 'screenfull'; import { CameraManager } from '../camera-manager/manager'; import { FRIGATE_BUTTON_MENU_ICON } from '../const'; import { localize } from '../localize/localize.js'; @@ -19,6 +18,15 @@ import { MediaPlayerManager } from './card-controller/media-player-manager'; import { MicrophoneManager } from './card-controller/microphone-manager'; import { hasSubstream } from './substream'; +export interface MenuButtonControllerOptions { + currentMediaLoadedInfo?: MediaLoadedInfo | null; + showCameraUIButton?: boolean; + inFullscreenMode?: boolean; + inExpandedMode?: boolean; + microphoneManager?: MicrophoneManager | null; + mediaPlayerController?: MediaPlayerManager | null; +} + export class MenuButtonController { // Array of dynamic menu buttons to be added to menu. protected _dynamicMenuButtons: MenuButton[] = []; @@ -44,13 +52,7 @@ export class MenuButtonController { config: FrigateCardConfig, cameraManager: CameraManager, view: View, - expanded: boolean, - options?: { - currentMediaLoadedInfo?: MediaLoadedInfo | null; - showCameraUIButton?: boolean, - microphoneManager?: MicrophoneManager | null; - mediaPlayerController?: MediaPlayerManager | null; - }, + options?: MenuButtonControllerOptions, ): MenuButton[] { const visibleCameras = cameraManager.getStore().getVisibleCameras(); const selectedCameraID = view.camera; @@ -294,26 +296,26 @@ export class MenuButtonController { }); } - if (screenfull.isEnabled && !this._isBeingCasted()) { + if (!this._isBeingCasted()) { buttons.push({ - icon: screenfull.isFullscreen ? 'mdi:fullscreen-exit' : 'mdi:fullscreen', + icon: options?.inFullscreenMode ? 'mdi:fullscreen-exit' : 'mdi:fullscreen', ...config.menu.buttons.fullscreen, type: 'custom:frigate-card-menu-icon', title: localize('config.menu.buttons.fullscreen'), tap_action: createFrigateCardCustomAction( 'fullscreen', ) as FrigateCardCustomAction, - style: screenfull.isFullscreen ? this._getEmphasizedStyle() : {}, + style: options?.inFullscreenMode ? this._getEmphasizedStyle() : {}, }); } buttons.push({ - icon: expanded ? 'mdi:arrow-collapse-all' : 'mdi:arrow-expand-all', + icon: options?.inExpandedMode ? 'mdi:arrow-collapse-all' : 'mdi:arrow-expand-all', ...config.menu.buttons.expand, type: 'custom:frigate-card-menu-icon', title: localize('config.menu.buttons.expand'), tap_action: createFrigateCardCustomAction('expand') as FrigateCardCustomAction, - style: expanded ? this._getEmphasizedStyle() : {}, + style: options?.inExpandedMode ? this._getEmphasizedStyle() : {}, }); if ( @@ -417,7 +419,7 @@ export class MenuButtonController { } const styledDynamicButtons = this._dynamicMenuButtons.map((button) => ({ - style: this._getStyleFromActions(config, view, button), + style: this._getStyleFromActions(config, view, button, options), ...button, })); @@ -450,6 +452,7 @@ export class MenuButtonController { config: FrigateCardConfig, view: View, button: MenuButton, + options?: MenuButtonControllerOptions, ): StyleInfo { for (const actionSet of [ button.tap_action, @@ -479,8 +482,7 @@ export class MenuButtonController { (frigateCardAction.frigate_card_action === 'default' && view.is(config.view.default)) || (frigateCardAction.frigate_card_action === 'fullscreen' && - screenfull.isEnabled && - screenfull.isFullscreen) || + !!options?.inFullscreenMode) || (frigateCardAction.frigate_card_action === 'camera_select' && view.camera === frigateCardAction.camera) ) { diff --git a/tests/utils/menu-controller.test.ts b/tests/utils/menu-controller.test.ts index 8c3dca47..7b9a215f 100644 --- a/tests/utils/menu-controller.test.ts +++ b/tests/utils/menu-controller.test.ts @@ -1,6 +1,5 @@ import { HomeAssistant } from 'custom-card-helpers'; import isEqual from 'lodash-es/isEqual'; -import screenfull from 'screenfull'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { mock } from 'vitest-mock-extended'; import { CameraManager } from '../../src/camera-manager/manager'; @@ -8,14 +7,13 @@ import { CameraManagerCameraMetadata } from '../../src/camera-manager/types'; import { FrigateCardConfig, FrigateCardMediaPlayer, - MediaLoadedInfo, MenuButton, ViewDisplayMode, } from '../../src/types'; import { createFrigateCardCustomAction } from '../../src/utils/action'; import { MediaPlayerManager } from '../../src/utils/card-controller/media-player-manager'; import { MicrophoneManager } from '../../src/utils/card-controller/microphone-manager'; -import { MenuButtonController } from '../../src/utils/menu-controller'; +import { MenuButtonController, MenuButtonControllerOptions } from '../../src/utils/menu-controller'; import { ViewMedia } from '../../src/view/media'; import { MediaQueriesResults } from '../../src/view/media-queries-results'; import { View } from '../../src/view/view'; @@ -35,20 +33,14 @@ import { vi.mock('../../src/camera-manager/manager.js'); vi.mock('../../src/utils/media-player-controller.js'); vi.mock('../../src/utils/card-controller/microphone-manager.js'); -vi.mock('screenfull'); const calculateButtons = ( controller: MenuButtonController, - options?: { + options?: MenuButtonControllerOptions & { hass?: HomeAssistant; config?: FrigateCardConfig; cameraManager?: CameraManager; view?: View; - expanded?: boolean; - currentMediaLoadedInfo?: MediaLoadedInfo | null; - mediaPlayerController?: MediaPlayerManager; - showCameraUIButton?: boolean; - microphoneManager?: MicrophoneManager; }, ): MenuButton[] => { return controller.calculateButtons( @@ -62,13 +54,7 @@ const calculateButtons = ( ]), }), options?.view ?? createView({ camera: 'camera-1' }), - options?.expanded ?? false, - { - currentMediaLoadedInfo: options?.currentMediaLoadedInfo, - mediaPlayerController: options?.mediaPlayerController, - showCameraUIButton: options?.showCameraUIButton, - microphoneManager: options?.microphoneManager, - }, + options, ); }; @@ -863,9 +849,8 @@ describe('MenuButtonController', () => { it('should have fullscreen button', () => { // Need to write a readonly property. - Object.defineProperty(screenfull, 'isEnabled', { value: true }); vi.stubGlobal('navigator', { userAgent: 'foo' }); - const buttons = calculateButtons(controller); + const buttons = calculateButtons(controller, { inFullscreenMode: false }); expect(buttons).toContainEqual({ icon: 'mdi:fullscreen', @@ -879,11 +864,8 @@ describe('MenuButtonController', () => { }); it('should have unfullscreen', () => { - // Need to write a readonly property. - Object.defineProperty(screenfull, 'isEnabled', { value: true }); - Object.defineProperty(screenfull, 'isFullscreen', { value: true }); vi.stubGlobal('navigator', { userAgent: 'foo' }); - const buttons = calculateButtons(controller); + const buttons = calculateButtons(controller, { inFullscreenMode: true }); expect(buttons).toContainEqual({ icon: 'mdi:fullscreen-exit', @@ -897,7 +879,7 @@ describe('MenuButtonController', () => { }); it('should have expand button', () => { - const buttons = calculateButtons(controller); + const buttons = calculateButtons(controller, { inExpandedMode: false }); expect(buttons).toContainEqual({ icon: 'mdi:arrow-expand-all', @@ -911,7 +893,7 @@ describe('MenuButtonController', () => { }); it('should have unexpand button', () => { - const buttons = calculateButtons(controller, { expanded: true }); + const buttons = calculateButtons(controller, { inExpandedMode: true }); expect(buttons).toContainEqual({ icon: 'mdi:arrow-collapse-all', @@ -1224,17 +1206,13 @@ describe('MenuButtonController', () => { }); it('should set style for dynamic button with fullscreen action', () => { - // Need to write a readonly property. - Object.defineProperty(screenfull, 'isEnabled', { value: true }); - Object.defineProperty(screenfull, 'isFullscreen', { value: true }); - const button: MenuButton = { ...dynamicButton, tap_action: { action: 'fire-dom-event', frigate_card_action: 'fullscreen' }, }; controller.addDynamicMenuButton(button); - expect(calculateButtons(controller)).toContainEqual({ + expect(calculateButtons(controller, { inFullscreenMode: true })).toContainEqual({ ...button, style: { color: 'var(--primary-color, white)' }, });