fix: Highlight folder icon for correct folder / media (#2560)
- Closes: #2128 - Closes: #2129
This commit is contained in:
@@ -871,7 +871,7 @@ export class MenuButtonController {
|
|||||||
|
|
||||||
if (folders.length === 1) {
|
if (folders.length === 1) {
|
||||||
const folderID = folders[0][0];
|
const folderID = folders[0][0];
|
||||||
const isSelected = !!view?.query?.getFolderQueries(folderID).length;
|
const isSelected = !!view?.query?.hasFolderQueries(folderID);
|
||||||
const folder = folders[0][1];
|
const folder = folders[0][1];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -886,7 +886,7 @@ export class MenuButtonController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const submenuItems = folders.map(([id, folder]) => {
|
const submenuItems = folders.map(([id, folder]) => {
|
||||||
const isSelected = !!view?.query?.getFolderQueries(id).length;
|
const isSelected = !!view?.query?.hasFolderQueries(id);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@@ -951,6 +951,23 @@ export class MenuButtonController {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unlike other views, a folder action targets a specific folder, so
|
||||||
|
// emphasize it only when that folder is the one being viewed. Matching
|
||||||
|
// on the view name alone would emphasize every folder button at once.
|
||||||
|
// An action without a folder ID keeps the plain view-name match.
|
||||||
|
if (
|
||||||
|
action.advanced_camera_card_action === 'folder' ||
|
||||||
|
action.advanced_camera_card_action === 'folders'
|
||||||
|
) {
|
||||||
|
const emphasized = action.folder
|
||||||
|
? !!options?.view?.query?.hasFolderQueries(action.folder)
|
||||||
|
: !!options?.view?.is(action.advanced_camera_card_action);
|
||||||
|
if (emphasized) {
|
||||||
|
return this._getEmphasizedStyle();
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
VIEWS_USER_SPECIFIED.some(
|
VIEWS_USER_SPECIFIED.some(
|
||||||
(viewName) =>
|
(viewName) =>
|
||||||
|
|||||||
@@ -58,6 +58,10 @@ export class UnifiedQuery {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public hasFolderQueries(folderID?: string): boolean {
|
||||||
|
return this.getFolderQueries(folderID).length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
public getNonMediaQueries(): QueryNode[] {
|
public getNonMediaQueries(): QueryNode[] {
|
||||||
return this._nodes.filter((node) => !this._isMediaQuery(node));
|
return this._nodes.filter((node) => !this._isMediaQuery(node));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2699,6 +2699,118 @@ describe('MenuButtonController', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// `folder` (single folder) and `folders` (gallery) actions both carry a
|
||||||
|
// folder ID and are emphasized identically, so exercise both.
|
||||||
|
it.each([['folder' as const], ['folders' as const]])(
|
||||||
|
'with a %s action, emphasizes only the button for the folder being viewed',
|
||||||
|
(action: 'folder' | 'folders') => {
|
||||||
|
const folder = createFolder({ id: 'folder-a' });
|
||||||
|
const folderNode: FolderQuery = {
|
||||||
|
source: QuerySource.Folder,
|
||||||
|
folder: folder,
|
||||||
|
path: [{ ha: { id: 'one' } }],
|
||||||
|
};
|
||||||
|
const view = createView({
|
||||||
|
view: action,
|
||||||
|
query: new UnifiedQuery().addNode(folderNode),
|
||||||
|
});
|
||||||
|
|
||||||
|
const viewedButton: MenuItem = {
|
||||||
|
...dynamicButton,
|
||||||
|
icon: 'mdi:folder-a',
|
||||||
|
tap_action: {
|
||||||
|
action: 'fire-dom-event',
|
||||||
|
advanced_camera_card_action: action,
|
||||||
|
folder: 'folder-a',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const otherButton: MenuItem = {
|
||||||
|
...dynamicButton,
|
||||||
|
icon: 'mdi:folder-b',
|
||||||
|
tap_action: {
|
||||||
|
action: 'fire-dom-event',
|
||||||
|
advanced_camera_card_action: action,
|
||||||
|
folder: 'folder-b',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
controller.addDynamicMenuButton(viewedButton);
|
||||||
|
controller.addDynamicMenuButton(otherButton);
|
||||||
|
|
||||||
|
const buttons = calculateButtons(controller, { view: view });
|
||||||
|
|
||||||
|
expect(buttons).toContainEqual({
|
||||||
|
...viewedButton,
|
||||||
|
style: { color: 'var(--advanced-camera-card-menu-button-active-color)' },
|
||||||
|
});
|
||||||
|
expect(buttons).toContainEqual({
|
||||||
|
...otherButton,
|
||||||
|
style: {},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
it('with a folder action and no folder ID, emphasizes on the folder view', () => {
|
||||||
|
const button: MenuItem = {
|
||||||
|
...dynamicButton,
|
||||||
|
tap_action: { action: 'fire-dom-event', advanced_camera_card_action: 'folder' },
|
||||||
|
};
|
||||||
|
const view = createView({ view: 'folder' });
|
||||||
|
controller.addDynamicMenuButton(button);
|
||||||
|
|
||||||
|
expect(calculateButtons(controller, { view: view })).toContainEqual({
|
||||||
|
...button,
|
||||||
|
style: { color: 'var(--advanced-camera-card-menu-button-active-color)' },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('with a folder action, stays emphasized in the media viewer while its folder query remains', () => {
|
||||||
|
const folder = createFolder({ id: 'folder-a' });
|
||||||
|
const folderNode: FolderQuery = {
|
||||||
|
source: QuerySource.Folder,
|
||||||
|
folder: folder,
|
||||||
|
path: [{ ha: { id: 'one' } }],
|
||||||
|
};
|
||||||
|
// Opening a media item from a folder keeps the folder query but changes
|
||||||
|
// the view to the media viewer, so emphasis must follow the folder query
|
||||||
|
// rather than the view name.
|
||||||
|
const view = createView({
|
||||||
|
view: 'media',
|
||||||
|
query: new UnifiedQuery().addNode(folderNode),
|
||||||
|
});
|
||||||
|
|
||||||
|
const viewedButton: MenuItem = {
|
||||||
|
...dynamicButton,
|
||||||
|
icon: 'mdi:folder-a',
|
||||||
|
tap_action: {
|
||||||
|
action: 'fire-dom-event',
|
||||||
|
advanced_camera_card_action: 'folder',
|
||||||
|
folder: 'folder-a',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const otherButton: MenuItem = {
|
||||||
|
...dynamicButton,
|
||||||
|
icon: 'mdi:folder-b',
|
||||||
|
tap_action: {
|
||||||
|
action: 'fire-dom-event',
|
||||||
|
advanced_camera_card_action: 'folder',
|
||||||
|
folder: 'folder-b',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
controller.addDynamicMenuButton(viewedButton);
|
||||||
|
controller.addDynamicMenuButton(otherButton);
|
||||||
|
|
||||||
|
const buttons = calculateButtons(controller, { view: view });
|
||||||
|
|
||||||
|
expect(buttons).toContainEqual({
|
||||||
|
...viewedButton,
|
||||||
|
style: { color: 'var(--advanced-camera-card-menu-button-active-color)' },
|
||||||
|
});
|
||||||
|
expect(buttons).toContainEqual({
|
||||||
|
...otherButton,
|
||||||
|
style: {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('with array of actions', () => {
|
it('with array of actions', () => {
|
||||||
const button: MenuItem = {
|
const button: MenuItem = {
|
||||||
...dynamicButton,
|
...dynamicButton,
|
||||||
|
|||||||
@@ -51,6 +51,27 @@ describe('UnifiedQuery', () => {
|
|||||||
expect(clipsQueries).toHaveLength(1);
|
expect(clipsQueries).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return true when a folder query matches the folder ID', () => {
|
||||||
|
const query = new UnifiedQuery();
|
||||||
|
query.addNode(createFolderQuery('clips'));
|
||||||
|
|
||||||
|
expect(query.hasFolderQueries('clips')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false when no folder query matches the folder ID', () => {
|
||||||
|
const query = new UnifiedQuery();
|
||||||
|
query.addNode(createFolderQuery('clips'));
|
||||||
|
|
||||||
|
expect(query.hasFolderQueries('recordings')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true when any folder query exists and no folder ID is given', () => {
|
||||||
|
const query = new UnifiedQuery();
|
||||||
|
query.addNode(createFolderQuery('clips'));
|
||||||
|
|
||||||
|
expect(query.hasFolderQueries()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
it('should get all media types', () => {
|
it('should get all media types', () => {
|
||||||
const query = new UnifiedQuery();
|
const query = new UnifiedQuery();
|
||||||
query.addNode(createEventQuery('front', { hasClip: true }));
|
query.addNode(createEventQuery('front', { hasClip: true }));
|
||||||
|
|||||||
Reference in New Issue
Block a user