feat: Add folder view to view folder media (#2077)
- Closes: #2068 Note: Renames existing `folder` view to `folders` [experimental functionality]
This commit is contained in:
@@ -114,7 +114,7 @@ advanced_camera_card_action: expand
|
||||
|
||||
## `folder`
|
||||
|
||||
Show a given folder in the folder gallery.
|
||||
Show media from a given folder in the media viewer.
|
||||
|
||||
```yaml
|
||||
action: custom:advanced-camera-card-action
|
||||
@@ -126,6 +126,20 @@ advanced_camera_card_action: folder
|
||||
| --------- | ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- |
|
||||
| `folder` | The first configured folder (under [`folders`](../../folders.md)). | An optional id of the folder to show, see the `id` parameter under [`folders` configuration](../../folders.md). |
|
||||
|
||||
## `folders`
|
||||
|
||||
Show a given folder in the folders gallery.
|
||||
|
||||
```yaml
|
||||
action: custom:advanced-camera-card-action
|
||||
advanced_camera_card_action: folders
|
||||
# [...]
|
||||
```
|
||||
|
||||
| Parameter | Default | Description |
|
||||
| --------- | ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- |
|
||||
| `folder` | The first configured folder (under [`folders`](../../folders.md)). | An optional id of the folder to show, see the `id` parameter under [`folders` configuration](../../folders.md). |
|
||||
|
||||
## `fullscreen`
|
||||
|
||||
Toggle fullscreen.
|
||||
@@ -821,4 +835,18 @@ elements:
|
||||
- action: custom:advanced-camera-card-action
|
||||
advanced_camera_card_action: status_bar
|
||||
status_bar_action: reset
|
||||
- type: custom:advanced-camera-card-menu-icon
|
||||
icon: mdi:alpha-m-circle-outline
|
||||
title: View Folder Media
|
||||
tap_action:
|
||||
- action: custom:advanced-camera-card-action
|
||||
advanced_camera_card_action: folder
|
||||
folder: my-folder
|
||||
- type: custom:advanced-camera-card-menu-icon
|
||||
icon: mdi:alpha-n-circle-outline
|
||||
title: View Folders Gallery
|
||||
tap_action:
|
||||
- action: custom:advanced-camera-card-action
|
||||
advanced_camera_card_action: folders
|
||||
folder: my-folder
|
||||
```
|
||||
|
||||
@@ -36,7 +36,7 @@ menu:
|
||||
| `display_mode` | The `display_mode` button allows changing between single and grid views. |
|
||||
| `download` | The `download` menu button: allow direct download of the media being displayed. |
|
||||
| `expand` | The `expand` menu button: expand the card into a popup/dialog. |
|
||||
| `folders` | The `folders` menu button to select a folder of media to view in the [`media_gallery`](./media-gallery.md). Will only appear if [`folders`](./folders.md) are configured. |
|
||||
| `folders` | The `folders` menu button: brings the user to a [gallery](./media-gallery.md) of folders on tap and to the media viewer with media from the folder on hold. Will only appear if [`folders`](./folders.md) are configured. |
|
||||
| `fullscreen` | The `fullscreen` menu button: expand the card to consume the fullscreen. Please note that fullscreen behavior on iPhone is limited, see [troubleshooting](../troubleshooting.md?id=fullscreen-doesn39t-work-on-iphone). |
|
||||
| `image` | The `image` view menu button: brings the user to the static `image` view. |
|
||||
| `iris` | The main Advanced Camera Card `iris` menu button: brings the user to the default configured view (`view.default`), or collapses/expands the menu if the `menu.style` is `hidden` . |
|
||||
|
||||
@@ -142,7 +142,8 @@ This card supports several different views.
|
||||
| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `clip` | Shows a viewer for the most recent clip for this camera. Can also be accessed by holding down the `clips` menu icon. |
|
||||
| `clips` | Shows a gallery of clips for this camera. |
|
||||
| `folder` | Shows a gallery of media from a [`folder`](./folders.md). |
|
||||
| `folder` | Shows a viewer for the media from the default [`folder`](./folders.md). |
|
||||
| `folders` | Shows a gallery of media and subfolders from the default [`folder`](./folders.md). |
|
||||
| `image` | Shows a static image specified by the `image` parameter, can be used as a discrete default view or a screensaver (via `view.interaction_seconds`). |
|
||||
| `live` | Shows the live camera view with the configured [live provider](./cameras/live-provider.md). |
|
||||
| `recording` | Shows a viewer for the most recent recording for this camera. Can also be accessed by holding down the `recordings` menu icon. |
|
||||
|
||||
+4
-3
@@ -1,9 +1,9 @@
|
||||
import { FolderActionConfig } from '../../../config/schema/actions/custom/folder';
|
||||
import { FoldersViewActionConfig } from '../../../config/schema/actions/custom/folders-view';
|
||||
import { FolderViewQuery } from '../../../view/query';
|
||||
import { CardActionsAPI } from '../../types';
|
||||
import { AdvancedCameraCardAction } from './base';
|
||||
|
||||
export class FolderAction extends AdvancedCameraCardAction<FolderActionConfig> {
|
||||
export class FoldersViewAction extends AdvancedCameraCardAction<FoldersViewActionConfig> {
|
||||
public async execute(api: CardActionsAPI): Promise<void> {
|
||||
await super.execute(api);
|
||||
|
||||
@@ -19,7 +19,8 @@ export class FolderAction extends AdvancedCameraCardAction<FolderActionConfig> {
|
||||
|
||||
await api.getViewManager().setViewByParametersWithExistingQuery({
|
||||
params: {
|
||||
view: 'folder',
|
||||
// Supports both 'folder' and 'folders' views.
|
||||
view: this._action.advanced_camera_card_action,
|
||||
query: new FolderViewQuery(query),
|
||||
},
|
||||
});
|
||||
@@ -10,7 +10,7 @@ import { DefaultAction } from './actions/default';
|
||||
import { DisplayModeSelectAction } from './actions/display-mode-select';
|
||||
import { DownloadAction } from './actions/download';
|
||||
import { ExpandAction } from './actions/expand';
|
||||
import { FolderAction } from './actions/folder';
|
||||
import { FoldersViewAction } from './actions/folders-view';
|
||||
import { FullscreenAction } from './actions/fullscreen';
|
||||
import { InternalCallbackAction } from './actions/internal-callback';
|
||||
import { LogAction } from './actions/log';
|
||||
@@ -152,7 +152,8 @@ export class ActionFactory {
|
||||
case INTERNAL_CALLBACK_ACTION:
|
||||
return new InternalCallbackAction(context, action, options?.config);
|
||||
case 'folder':
|
||||
return new FolderAction(context, action, options?.config);
|
||||
case 'folders':
|
||||
return new FoldersViewAction(context, action, options?.config);
|
||||
}
|
||||
|
||||
/* istanbul ignore next: this path cannot be reached -- @preserve */
|
||||
|
||||
@@ -120,6 +120,7 @@ export class ViewQueryExecutor {
|
||||
viewModifiers.push(...(await executeMediaQuery(mediaType)));
|
||||
break;
|
||||
case 'folder':
|
||||
case 'folders':
|
||||
viewModifiers.push(...(await executeFolderQuery()));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import { MediaLoadedInfo } from '../types';
|
||||
import {
|
||||
createCameraAction,
|
||||
createDisplayModeAction,
|
||||
createFolderAction,
|
||||
createFoldersViewAction,
|
||||
createGeneralAction,
|
||||
createMediaPlayerAction,
|
||||
createPTZControlsAction,
|
||||
@@ -658,7 +658,8 @@ export class MenuButtonController {
|
||||
type: 'custom:advanced-camera-card-menu-icon',
|
||||
title: folder.title ?? localize('config.menu.buttons.folders'),
|
||||
style: isSelected ? this._getEmphasizedStyle() : {},
|
||||
tap_action: createFolderAction(),
|
||||
tap_action: createFoldersViewAction('folders'),
|
||||
hold_action: createFoldersViewAction('folder'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -667,14 +668,14 @@ export class MenuButtonController {
|
||||
QueryClassifier.isFolderQuery(view?.query) &&
|
||||
view.query.getQuery()?.folder.id === id;
|
||||
|
||||
const action = createFolderAction({ folderID: id });
|
||||
return {
|
||||
enabled: true,
|
||||
title: folder.title ?? folder.id,
|
||||
icon: folder.icon ?? 'mdi:folder',
|
||||
selected: isSelected,
|
||||
style: isSelected ? this._getEmphasizedStyle() : {},
|
||||
...(action && { tap_action: action }),
|
||||
tap_action: createFoldersViewAction('folders', { folderID: id }),
|
||||
hold_action: createFoldersViewAction('folder', { folderID: id }),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -684,7 +685,7 @@ export class MenuButtonController {
|
||||
type: 'custom:advanced-camera-card-menu-submenu',
|
||||
title: localize('config.menu.buttons.folders'),
|
||||
items: submenuItems,
|
||||
style: view?.is('folder') ? this._getEmphasizedStyle() : {},
|
||||
style: view?.isAnyFolderView() ? this._getEmphasizedStyle() : {},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ export class AdvancedCameraCardViews extends LitElement {
|
||||
if (view?.is('live') || this._shouldLivePreload()) {
|
||||
import('./live/index.js');
|
||||
}
|
||||
if (view?.isMediaGalleryView() && !view.is('folder')) {
|
||||
if (view?.isMediaGalleryView() && !view.is('folders')) {
|
||||
import('./gallery/media-gallery.js');
|
||||
} else if (view?.isViewerView()) {
|
||||
import('./viewer/index.js');
|
||||
@@ -76,7 +76,7 @@ export class AdvancedCameraCardViews extends LitElement {
|
||||
import('./image.js');
|
||||
} else if (view?.is('timeline')) {
|
||||
import('./timeline.js');
|
||||
} else if (view?.is('folder')) {
|
||||
} else if (view?.is('folders')) {
|
||||
import('./gallery/folder-gallery.js');
|
||||
}
|
||||
}
|
||||
@@ -157,7 +157,7 @@ export class AdvancedCameraCardViews extends LitElement {
|
||||
>
|
||||
</advanced-camera-card-image>`
|
||||
: ``}
|
||||
${!this.hide && view?.isMediaGalleryView() && !view.is('folder')
|
||||
${!this.hide && view?.isMediaGalleryView() && !view.is('folders')
|
||||
? html` <advanced-camera-card-media-gallery
|
||||
.hass=${this.hass}
|
||||
.viewManagerEpoch=${this.viewManagerEpoch}
|
||||
@@ -200,7 +200,7 @@ export class AdvancedCameraCardViews extends LitElement {
|
||||
>
|
||||
</advanced-camera-card-diagnostics>`
|
||||
: ``}
|
||||
${!this.hide && view?.is('folder')
|
||||
${!this.hide && view?.is('folders')
|
||||
? html` <advanced-camera-card-folder-gallery
|
||||
.hass=${this.hass}
|
||||
.viewManagerEpoch=${this.viewManagerEpoch}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
import { z } from 'zod';
|
||||
import { advancedCameraCardCustomActionsBaseSchema } from './base';
|
||||
|
||||
export const folderActionConfigSchema = advancedCameraCardCustomActionsBaseSchema.extend(
|
||||
{
|
||||
advanced_camera_card_action: z.literal('folder'),
|
||||
folder: z.string().optional(),
|
||||
},
|
||||
);
|
||||
export type FolderActionConfig = z.infer<typeof folderActionConfigSchema>;
|
||||
@@ -0,0 +1,9 @@
|
||||
import { z } from 'zod';
|
||||
import { advancedCameraCardCustomActionsBaseSchema } from './base';
|
||||
|
||||
export const foldersViewActionConfigSchema =
|
||||
advancedCameraCardCustomActionsBaseSchema.extend({
|
||||
advanced_camera_card_action: z.literal('folders').or(z.literal('folder')),
|
||||
folder: z.string().optional(),
|
||||
});
|
||||
export type FoldersViewActionConfig = z.infer<typeof foldersViewActionConfigSchema>;
|
||||
@@ -7,14 +7,14 @@ import { advancedCameraCardCustomActionsBaseSchema } from './base';
|
||||
|
||||
type AdvancedCameraCardUserSpecifiedViewWithoutFolder = Exclude<
|
||||
AdvancedCameraCardUserSpecifiedView,
|
||||
'folder'
|
||||
'folder' | 'folders'
|
||||
>;
|
||||
|
||||
export const viewActionConfigSchema = advancedCameraCardCustomActionsBaseSchema.extend({
|
||||
advanced_camera_card_action: z.enum(
|
||||
// The folder view is handled by the `folder` action since it accepts an
|
||||
// The folder/folders views are handled separately as they accept an
|
||||
// optional folder ID.
|
||||
VIEWS_USER_SPECIFIED.filter((view) => view !== 'folder') as [
|
||||
VIEWS_USER_SPECIFIED.filter((view) => view !== 'folder' && view !== 'folders') as [
|
||||
AdvancedCameraCardUserSpecifiedViewWithoutFolder,
|
||||
...AdvancedCameraCardUserSpecifiedViewWithoutFolder[],
|
||||
],
|
||||
|
||||
@@ -3,7 +3,7 @@ import { statusBarItemBaseSchema } from '../common/status-bar';
|
||||
import { advancedCameraCardCustomActionsBaseSchema } from './custom/base';
|
||||
import { cameraSelectActionConfigSchema } from './custom/camera-select';
|
||||
import { viewDisplayModeActionConfigSchema } from './custom/display-mode';
|
||||
import { folderActionConfigSchema } from './custom/folder';
|
||||
import { foldersViewActionConfigSchema } from './custom/folders-view';
|
||||
import { generalActionConfigSchema } from './custom/general';
|
||||
import { internalCallbackActionConfigSchema } from './custom/internal';
|
||||
import { logActionConfigSchema } from './custom/log';
|
||||
@@ -42,7 +42,7 @@ export const statusBarActionConfigSchema: z.ZodSchema<
|
||||
|
||||
const advancedCameraCardCustomActionSchema = z.union([
|
||||
cameraSelectActionConfigSchema,
|
||||
folderActionConfigSchema,
|
||||
foldersViewActionConfigSchema,
|
||||
generalActionConfigSchema,
|
||||
internalCallbackActionConfigSchema,
|
||||
logActionConfigSchema,
|
||||
|
||||
@@ -17,6 +17,7 @@ export const VIEWS_USER_SPECIFIED = [
|
||||
'clip',
|
||||
'clips',
|
||||
'folder',
|
||||
'folders',
|
||||
'snapshot',
|
||||
'snapshots',
|
||||
'recording',
|
||||
|
||||
@@ -414,6 +414,7 @@ export class AdvancedCameraCardEditor extends LitElement implements LovelaceCard
|
||||
{ value: 'clip', label: localize('config.view.views.clip') },
|
||||
{ value: 'clips', label: localize('config.view.views.clips') },
|
||||
{ value: 'folder', label: localize('config.view.views.folder') },
|
||||
{ value: 'folders', label: localize('config.view.views.folders') },
|
||||
{ value: 'image', label: localize('config.view.views.image') },
|
||||
{ value: 'live', label: localize('config.view.views.live') },
|
||||
{ value: 'recording', label: localize('config.view.views.recording') },
|
||||
|
||||
@@ -589,6 +589,7 @@
|
||||
"clips": "Galeria de clips",
|
||||
"current": "Vista actual",
|
||||
"folder": "",
|
||||
"folders": "",
|
||||
"image": "Imatge estàtica",
|
||||
"live": "Visualització en directe",
|
||||
"recording": "Enregistrament més recent",
|
||||
|
||||
@@ -588,7 +588,8 @@
|
||||
"clip": "Most recent clip",
|
||||
"clips": "Clips gallery",
|
||||
"current": "Current view",
|
||||
"folder": "Folder view",
|
||||
"folder": "Folder media",
|
||||
"folders": "Folders gallery",
|
||||
"image": "Static image",
|
||||
"live": "Live view",
|
||||
"recording": "Most recent recording",
|
||||
|
||||
@@ -589,6 +589,7 @@
|
||||
"clips": "Galerie de clips",
|
||||
"current": "Vue actuelle",
|
||||
"folder": "",
|
||||
"folders": "",
|
||||
"image": "Image statique",
|
||||
"live": "Vue en direct",
|
||||
"recording": "Enregistrement le plus récent",
|
||||
|
||||
@@ -589,6 +589,7 @@
|
||||
"clips": "Galleria delle clip",
|
||||
"current": "Vista corrente",
|
||||
"folder": "",
|
||||
"folders": "",
|
||||
"image": "Immagine statica",
|
||||
"live": "Dal vivo",
|
||||
"recording": "",
|
||||
|
||||
@@ -589,6 +589,7 @@
|
||||
"clips": "Galeria de clipes",
|
||||
"current": "Visualização atual",
|
||||
"folder": "",
|
||||
"folders": "",
|
||||
"image": "Imagem estática",
|
||||
"live": "Visualização ao vivo",
|
||||
"recording": "Gravação mais recente",
|
||||
|
||||
@@ -589,6 +589,7 @@
|
||||
"clips": "Galeria de clipes",
|
||||
"current": "Visualização atual",
|
||||
"folder": "",
|
||||
"folders": "",
|
||||
"image": "Imagem estática",
|
||||
"live": "Visualização ao vivo",
|
||||
"recording": "",
|
||||
|
||||
+10
-7
@@ -2,7 +2,7 @@ import { CardActionsAPI } from '../card-controller/types.js';
|
||||
import { ZoomSettingsBase } from '../components-lib/zoom/types.js';
|
||||
import { CameraSelectActionConfig } from '../config/schema/actions/custom/camera-select.js';
|
||||
import { DisplayModeActionConfig } from '../config/schema/actions/custom/display-mode.js';
|
||||
import { FolderActionConfig } from '../config/schema/actions/custom/folder.js';
|
||||
import { FoldersViewActionConfig } from '../config/schema/actions/custom/folders-view.js';
|
||||
import {
|
||||
AdvancedCameraCardGeneralAction,
|
||||
GeneralActionConfig,
|
||||
@@ -47,7 +47,7 @@ export function createGeneralAction(
|
||||
}
|
||||
|
||||
export function createViewAction(
|
||||
action: Exclude<AdvancedCameraCardUserSpecifiedView, 'folder'>,
|
||||
action: Exclude<AdvancedCameraCardUserSpecifiedView, 'folder' | 'folders'>,
|
||||
options?: {
|
||||
cardID?: string;
|
||||
},
|
||||
@@ -74,13 +74,16 @@ export function createCameraAction(
|
||||
};
|
||||
}
|
||||
|
||||
export function createFolderAction(options?: {
|
||||
cardID?: string;
|
||||
folderID?: string;
|
||||
}): FolderActionConfig {
|
||||
export function createFoldersViewAction(
|
||||
view: 'folder' | 'folders',
|
||||
options?: {
|
||||
cardID?: string;
|
||||
folderID?: string;
|
||||
},
|
||||
): FoldersViewActionConfig {
|
||||
return {
|
||||
action: 'fire-dom-event',
|
||||
advanced_camera_card_action: 'folder',
|
||||
advanced_camera_card_action: view,
|
||||
...(options?.folderID && { folder: options.folderID }),
|
||||
...(options?.cardID && { card_id: options.cardID }),
|
||||
};
|
||||
|
||||
@@ -19,6 +19,7 @@ export const getCameraIDsForViewName = (
|
||||
case 'diagnostics':
|
||||
case 'image':
|
||||
case 'folder':
|
||||
case 'folders':
|
||||
case 'media':
|
||||
return cameraManager.getStore().getCameraIDs();
|
||||
|
||||
|
||||
+6
-2
@@ -126,7 +126,7 @@ export class View {
|
||||
* Determine if a view is a media gallery.
|
||||
*/
|
||||
public isMediaGalleryView(): boolean {
|
||||
return ['clips', 'folder', 'snapshots', 'recordings'].includes(this.view);
|
||||
return ['clips', 'folders', 'snapshots', 'recordings'].includes(this.view);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,11 +137,15 @@ export class View {
|
||||
return this.isViewerView() || this.is('live') || this.is('image');
|
||||
}
|
||||
|
||||
public isAnyFolderView(): boolean {
|
||||
return ['folder', 'folders'].includes(this.view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a view is for the media viewer.
|
||||
*/
|
||||
public isViewerView(): boolean {
|
||||
return ['media', 'clip', 'snapshot', 'recording'].includes(this.view);
|
||||
return ['folder', 'media', 'clip', 'snapshot', 'recording'].includes(this.view);
|
||||
}
|
||||
|
||||
public supportsMultipleDisplayModes(): boolean {
|
||||
|
||||
@@ -105,7 +105,17 @@ describe('ActionsManager', () => {
|
||||
{
|
||||
tap_action: {
|
||||
action: 'navigate',
|
||||
// Folder also uses the media gallery.
|
||||
// Folders also uses the media viewer.
|
||||
navigation_path: '4',
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
'folders' as const,
|
||||
{
|
||||
tap_action: {
|
||||
action: 'navigate',
|
||||
// Folders also uses the media gallery.
|
||||
navigation_path: '3',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { FolderAction } from '../../../../src/card-controller/actions/actions/folder';
|
||||
import { FoldersViewAction } from '../../../../src/card-controller/actions/actions/folders-view';
|
||||
import { FolderQuery } from '../../../../src/card-controller/folders/types';
|
||||
import { FolderViewQuery } from '../../../../src/view/query';
|
||||
import { createCardAPI, createFolder } from '../../../test-utils';
|
||||
@@ -7,7 +7,7 @@ import { createCardAPI, createFolder } from '../../../test-utils';
|
||||
describe('should handle folder action', async () => {
|
||||
it('should handle folder action successfully', async () => {
|
||||
const api = createCardAPI();
|
||||
const action = new FolderAction(
|
||||
const action = new FoldersViewAction(
|
||||
{},
|
||||
{
|
||||
action: 'fire-dom-event',
|
||||
@@ -20,7 +20,7 @@ describe('should handle folder action', async () => {
|
||||
|
||||
const query: FolderQuery = {
|
||||
folder,
|
||||
path: ['path'],
|
||||
path: [{ ha: { id: 'path' } }],
|
||||
};
|
||||
vi.mocked(api.getFoldersManager().generateDefaultFolderQuery).mockReturnValue(query);
|
||||
|
||||
@@ -44,7 +44,7 @@ describe('should handle folder action', async () => {
|
||||
|
||||
it('should do nothing with non-existent folder', async () => {
|
||||
const api = createCardAPI();
|
||||
const action = new FolderAction(
|
||||
const action = new FoldersViewAction(
|
||||
{},
|
||||
{
|
||||
action: 'fire-dom-event',
|
||||
@@ -64,7 +64,7 @@ describe('should handle folder action', async () => {
|
||||
|
||||
it('should do nothing with non-existent default query', async () => {
|
||||
const api = createCardAPI();
|
||||
const action = new FolderAction(
|
||||
const action = new FoldersViewAction(
|
||||
{},
|
||||
{
|
||||
action: 'fire-dom-event',
|
||||
|
||||
@@ -7,7 +7,7 @@ import { DefaultAction } from '../../../src/card-controller/actions/actions/defa
|
||||
import { DisplayModeSelectAction } from '../../../src/card-controller/actions/actions/display-mode-select';
|
||||
import { DownloadAction } from '../../../src/card-controller/actions/actions/download';
|
||||
import { ExpandAction } from '../../../src/card-controller/actions/actions/expand';
|
||||
import { FolderAction } from '../../../src/card-controller/actions/actions/folder';
|
||||
import { FoldersViewAction } from '../../../src/card-controller/actions/actions/folders-view';
|
||||
import { FullscreenAction } from '../../../src/card-controller/actions/actions/fullscreen';
|
||||
import { InternalCallbackAction } from '../../../src/card-controller/actions/actions/internal-callback';
|
||||
import { LogAction } from '../../../src/card-controller/actions/actions/log';
|
||||
@@ -185,7 +185,8 @@ describe('ActionFactory', () => {
|
||||
},
|
||||
InternalCallbackAction,
|
||||
],
|
||||
[{ advanced_camera_card_action: 'folder' as const }, FolderAction],
|
||||
[{ advanced_camera_card_action: 'folder' as const }, FoldersViewAction],
|
||||
[{ advanced_camera_card_action: 'folders' as const }, FoldersViewAction],
|
||||
])(
|
||||
'advanced_camera_card_action: $advanced_camera_card_action',
|
||||
(action: Partial<ActionConfig>, classObject: object) => {
|
||||
|
||||
@@ -1792,7 +1792,8 @@ describe('MenuButtonController', () => {
|
||||
style: {},
|
||||
type: 'custom:advanced-camera-card-menu-icon',
|
||||
title: 'Folders',
|
||||
tap_action: { action: 'fire-dom-event', advanced_camera_card_action: 'folder' },
|
||||
tap_action: { action: 'fire-dom-event', advanced_camera_card_action: 'folders' },
|
||||
hold_action: { action: 'fire-dom-event', advanced_camera_card_action: 'folder' },
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1807,7 +1808,7 @@ describe('MenuButtonController', () => {
|
||||
foldersManager,
|
||||
view: createView({
|
||||
view: 'folder',
|
||||
query: new FolderViewQuery({ folder, path: [{ id: 'one' }] }),
|
||||
query: new FolderViewQuery({ folder, path: [{ ha: { id: 'one' } }] }),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -1820,7 +1821,8 @@ describe('MenuButtonController', () => {
|
||||
},
|
||||
type: 'custom:advanced-camera-card-menu-icon',
|
||||
title: 'Folders',
|
||||
tap_action: { action: 'fire-dom-event', advanced_camera_card_action: 'folder' },
|
||||
tap_action: { action: 'fire-dom-event', advanced_camera_card_action: 'folders' },
|
||||
hold_action: { action: 'fire-dom-event', advanced_camera_card_action: 'folder' },
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1837,7 +1839,7 @@ describe('MenuButtonController', () => {
|
||||
view: 'folder',
|
||||
query: new FolderViewQuery({
|
||||
folder: selectedFolder,
|
||||
path: [{ id: 'id' }],
|
||||
path: [{ ha: { id: 'id' } }],
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -1858,18 +1860,28 @@ describe('MenuButtonController', () => {
|
||||
items: [
|
||||
{
|
||||
enabled: true,
|
||||
hold_action: {
|
||||
action: 'fire-dom-event',
|
||||
advanced_camera_card_action: 'folder',
|
||||
folder: 'folder-0',
|
||||
},
|
||||
icon: 'mdi:folder',
|
||||
selected: false,
|
||||
style: {},
|
||||
tap_action: {
|
||||
action: 'fire-dom-event',
|
||||
advanced_camera_card_action: 'folder',
|
||||
advanced_camera_card_action: 'folders',
|
||||
folder: 'folder-0',
|
||||
},
|
||||
title: 'folder-0',
|
||||
},
|
||||
{
|
||||
enabled: true,
|
||||
hold_action: {
|
||||
action: 'fire-dom-event',
|
||||
advanced_camera_card_action: 'folder',
|
||||
folder: 'folder-selected',
|
||||
},
|
||||
icon: 'mdi:folder',
|
||||
selected: true,
|
||||
style: {
|
||||
@@ -1877,7 +1889,7 @@ describe('MenuButtonController', () => {
|
||||
},
|
||||
tap_action: {
|
||||
action: 'fire-dom-event',
|
||||
advanced_camera_card_action: 'folder',
|
||||
advanced_camera_card_action: 'folders',
|
||||
folder: 'folder-selected',
|
||||
},
|
||||
title: 'folder-selected',
|
||||
@@ -1909,23 +1921,33 @@ describe('MenuButtonController', () => {
|
||||
{
|
||||
enabled: true,
|
||||
icon: 'mdi:folder',
|
||||
hold_action: {
|
||||
action: 'fire-dom-event',
|
||||
advanced_camera_card_action: 'folder',
|
||||
folder: 'folder-0',
|
||||
},
|
||||
selected: false,
|
||||
style: {},
|
||||
tap_action: {
|
||||
action: 'fire-dom-event',
|
||||
advanced_camera_card_action: 'folder',
|
||||
advanced_camera_card_action: 'folders',
|
||||
folder: 'folder-0',
|
||||
},
|
||||
title: 'folder-0',
|
||||
},
|
||||
{
|
||||
enabled: true,
|
||||
hold_action: {
|
||||
action: 'fire-dom-event',
|
||||
advanced_camera_card_action: 'folder',
|
||||
folder: 'folder-1',
|
||||
},
|
||||
icon: 'mdi:folder',
|
||||
selected: false,
|
||||
style: {},
|
||||
tap_action: {
|
||||
action: 'fire-dom-event',
|
||||
advanced_camera_card_action: 'folder',
|
||||
advanced_camera_card_action: 'folders',
|
||||
folder: 'folder-1',
|
||||
},
|
||||
title: 'folder-1',
|
||||
|
||||
@@ -5,7 +5,7 @@ import { ActionConfig } from '../../src/config/schema/actions/types.js';
|
||||
import {
|
||||
createCameraAction,
|
||||
createDisplayModeAction,
|
||||
createFolderAction,
|
||||
createFoldersViewAction,
|
||||
createGeneralAction,
|
||||
createInternalCallbackAction,
|
||||
createLogAction,
|
||||
@@ -64,14 +64,19 @@ describe('createCameraAction', () => {
|
||||
});
|
||||
|
||||
describe('createFolderAction', () => {
|
||||
it('should create folder action', () => {
|
||||
expect(createFolderAction({ folderID: 'folderID', cardID: 'card_id' })).toEqual({
|
||||
action: 'fire-dom-event',
|
||||
advanced_camera_card_action: 'folder',
|
||||
card_id: 'card_id',
|
||||
folder: 'folderID',
|
||||
});
|
||||
});
|
||||
it.each([['folder' as const], ['folders' as const]])(
|
||||
'%s',
|
||||
(viewName: 'folder' | 'folders') => {
|
||||
expect(
|
||||
createFoldersViewAction(viewName, { folderID: 'folderID', cardID: 'card_id' }),
|
||||
).toEqual({
|
||||
action: 'fire-dom-event',
|
||||
advanced_camera_card_action: viewName,
|
||||
card_id: 'card_id',
|
||||
folder: 'folderID',
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe('createMediaPlayerAction', () => {
|
||||
|
||||
@@ -11,31 +11,34 @@ import {
|
||||
|
||||
describe('getCameraIDsForViewName', () => {
|
||||
describe('views that are always supported', () => {
|
||||
it.each([['diagnostics' as const], ['image' as const], ['media' as const]])(
|
||||
'%s',
|
||||
(viewName: AdvancedCameraCardView) => {
|
||||
const cameraManager = createCameraManager();
|
||||
vi.mocked(cameraManager.getStore).mockReturnValue(
|
||||
createStore([
|
||||
{
|
||||
cameraID: 'camera-1',
|
||||
config: createCameraConfig({ dependencies: { cameras: ['camera-2'] } }),
|
||||
},
|
||||
{ cameraID: 'camera-2' },
|
||||
]),
|
||||
);
|
||||
it.each([
|
||||
['diagnostics' as const],
|
||||
['folder' as const],
|
||||
['folders' as const],
|
||||
['image' as const],
|
||||
['media' as const],
|
||||
])('%s', (viewName: AdvancedCameraCardView) => {
|
||||
const cameraManager = createCameraManager();
|
||||
vi.mocked(cameraManager.getStore).mockReturnValue(
|
||||
createStore([
|
||||
{
|
||||
cameraID: 'camera-1',
|
||||
config: createCameraConfig({ dependencies: { cameras: ['camera-2'] } }),
|
||||
},
|
||||
{ cameraID: 'camera-2' },
|
||||
]),
|
||||
);
|
||||
|
||||
expect(getCameraIDsForViewName(cameraManager, viewName)).toEqual(
|
||||
new Set(['camera-1', 'camera-2']),
|
||||
);
|
||||
expect(getCameraIDsForViewName(cameraManager, viewName, 'camera-1')).toEqual(
|
||||
new Set(['camera-1', 'camera-2']),
|
||||
);
|
||||
expect(getCameraIDsForViewName(cameraManager, viewName, 'camera-2')).toEqual(
|
||||
new Set(['camera-1', 'camera-2']),
|
||||
);
|
||||
},
|
||||
);
|
||||
expect(getCameraIDsForViewName(cameraManager, viewName)).toEqual(
|
||||
new Set(['camera-1', 'camera-2']),
|
||||
);
|
||||
expect(getCameraIDsForViewName(cameraManager, viewName, 'camera-1')).toEqual(
|
||||
new Set(['camera-1', 'camera-2']),
|
||||
);
|
||||
expect(getCameraIDsForViewName(cameraManager, viewName, 'camera-2')).toEqual(
|
||||
new Set(['camera-1', 'camera-2']),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('views that respect dependencies and need a capability', () => {
|
||||
|
||||
+66
-41
@@ -153,56 +153,81 @@ describe('View Basics', () => {
|
||||
expect(view.context).toEqual({});
|
||||
});
|
||||
|
||||
it('should detect gallery views', () => {
|
||||
expect(createView({ view: 'clips' }).isMediaGalleryView()).toBeTruthy();
|
||||
expect(createView({ view: 'folder' }).isMediaGalleryView()).toBeTruthy();
|
||||
expect(createView({ view: 'snapshots' }).isMediaGalleryView()).toBeTruthy();
|
||||
expect(createView({ view: 'recordings' }).isMediaGalleryView()).toBeTruthy();
|
||||
describe('should detect folder views', () => {
|
||||
it('should detect folder views', () => {
|
||||
expect(createView({ view: 'folder' }).isAnyFolderView()).toBeTruthy();
|
||||
expect(createView({ view: 'folders' }).isAnyFolderView()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not detect folder views', () => {
|
||||
expect(createView({ view: 'live' }).isAnyFolderView()).toBeFalsy();
|
||||
expect(createView({ view: 'clip' }).isAnyFolderView()).toBeFalsy();
|
||||
expect(createView({ view: 'clips' }).isAnyFolderView()).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not detect gallery view', () => {
|
||||
expect(createView({ view: 'live' }).isMediaGalleryView()).toBeFalsy();
|
||||
expect(createView({ view: 'timeline' }).isMediaGalleryView()).toBeFalsy();
|
||||
describe('should detect media views', () => {
|
||||
it('should detect any media views', () => {
|
||||
expect(createView({ view: 'clip' }).isAnyMediaView()).toBeTruthy();
|
||||
expect(createView({ view: 'snapshot' }).isAnyMediaView()).toBeTruthy();
|
||||
expect(createView({ view: 'media' }).isAnyMediaView()).toBeTruthy();
|
||||
expect(createView({ view: 'recording' }).isAnyMediaView()).toBeTruthy();
|
||||
expect(createView({ view: 'live' }).isAnyMediaView()).toBeTruthy();
|
||||
expect(createView({ view: 'image' }).isAnyMediaView()).toBeTruthy();
|
||||
expect(createView({ view: 'folder' }).isAnyMediaView()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not detect any media view', () => {
|
||||
expect(createView({ view: 'timeline' }).isAnyMediaView()).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
it('should detect any media views', () => {
|
||||
expect(createView({ view: 'clip' }).isAnyMediaView()).toBeTruthy();
|
||||
expect(createView({ view: 'snapshot' }).isAnyMediaView()).toBeTruthy();
|
||||
expect(createView({ view: 'media' }).isAnyMediaView()).toBeTruthy();
|
||||
expect(createView({ view: 'recording' }).isAnyMediaView()).toBeTruthy();
|
||||
expect(createView({ view: 'live' }).isAnyMediaView()).toBeTruthy();
|
||||
expect(createView({ view: 'image' }).isAnyMediaView()).toBeTruthy();
|
||||
describe('should detect gallery views', () => {
|
||||
it('should detect gallery views', () => {
|
||||
expect(createView({ view: 'clips' }).isMediaGalleryView()).toBeTruthy();
|
||||
expect(createView({ view: 'folders' }).isMediaGalleryView()).toBeTruthy();
|
||||
expect(createView({ view: 'snapshots' }).isMediaGalleryView()).toBeTruthy();
|
||||
expect(createView({ view: 'recordings' }).isMediaGalleryView()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not detect gallery view', () => {
|
||||
expect(createView({ view: 'live' }).isMediaGalleryView()).toBeFalsy();
|
||||
expect(createView({ view: 'timeline' }).isMediaGalleryView()).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not detect any media view', () => {
|
||||
expect(createView({ view: 'timeline' }).isAnyMediaView()).toBeFalsy();
|
||||
describe('should detect viewer views', () => {
|
||||
it('should detect viewer views', () => {
|
||||
expect(createView({ view: 'clip' }).isViewerView()).toBeTruthy();
|
||||
expect(createView({ view: 'snapshot' }).isViewerView()).toBeTruthy();
|
||||
expect(createView({ view: 'media' }).isViewerView()).toBeTruthy();
|
||||
expect(createView({ view: 'recording' }).isViewerView()).toBeTruthy();
|
||||
expect(createView({ view: 'folder' }).isAnyMediaView()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not detect viewer views', () => {
|
||||
expect(createView({ view: 'live' }).isViewerView()).toBeFalsy();
|
||||
expect(createView({ view: 'live' }).isViewerView()).toBeFalsy();
|
||||
expect(createView({ view: 'timeline' }).isViewerView()).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
it('should detect viewer views', () => {
|
||||
expect(createView({ view: 'clip' }).isViewerView()).toBeTruthy();
|
||||
expect(createView({ view: 'snapshot' }).isViewerView()).toBeTruthy();
|
||||
expect(createView({ view: 'media' }).isViewerView()).toBeTruthy();
|
||||
expect(createView({ view: 'recording' }).isViewerView()).toBeTruthy();
|
||||
});
|
||||
describe('should get default media type', () => {
|
||||
it('should get default media type', () => {
|
||||
expect(createView({ view: 'clip' }).getDefaultMediaType()).toBe('clips');
|
||||
expect(createView({ view: 'clips' }).getDefaultMediaType()).toBe('clips');
|
||||
expect(createView({ view: 'snapshot' }).getDefaultMediaType()).toBe('snapshots');
|
||||
expect(createView({ view: 'snapshots' }).getDefaultMediaType()).toBe('snapshots');
|
||||
expect(createView({ view: 'recording' }).getDefaultMediaType()).toBe('recordings');
|
||||
expect(createView({ view: 'recordings' }).getDefaultMediaType()).toBe(
|
||||
'recordings',
|
||||
);
|
||||
});
|
||||
|
||||
it('should not detect viewer views', () => {
|
||||
expect(createView({ view: 'live' }).isViewerView()).toBeFalsy();
|
||||
expect(createView({ view: 'live' }).isViewerView()).toBeFalsy();
|
||||
expect(createView({ view: 'timeline' }).isViewerView()).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should get default media type', () => {
|
||||
expect(createView({ view: 'clip' }).getDefaultMediaType()).toBe('clips');
|
||||
expect(createView({ view: 'clips' }).getDefaultMediaType()).toBe('clips');
|
||||
expect(createView({ view: 'snapshot' }).getDefaultMediaType()).toBe('snapshots');
|
||||
expect(createView({ view: 'snapshots' }).getDefaultMediaType()).toBe('snapshots');
|
||||
expect(createView({ view: 'recording' }).getDefaultMediaType()).toBe('recordings');
|
||||
expect(createView({ view: 'recordings' }).getDefaultMediaType()).toBe('recordings');
|
||||
});
|
||||
|
||||
it('should not get default media type', () => {
|
||||
expect(createView({ view: 'live' }).getDefaultMediaType()).toBeNull();
|
||||
expect(createView({ view: 'timeline' }).getDefaultMediaType()).toBeNull();
|
||||
it('should not get default media type', () => {
|
||||
expect(createView({ view: 'live' }).getDefaultMediaType()).toBeNull();
|
||||
expect(createView({ view: 'timeline' }).getDefaultMediaType()).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
it('should determine if display mode is grid', () => {
|
||||
|
||||
Reference in New Issue
Block a user