mirror of
https://github.com/home-assistant/frontend.git
synced 2025-04-24 13:27:22 +00:00
Move notifications to the sidebar (#3317)
* Move notifications to the sidebar * Close when navigating * Lint
This commit is contained in:
parent
58e6be12af
commit
42e75e7cdf
@ -21,6 +21,11 @@ import {
|
||||
getExternalConfig,
|
||||
ExternalConfig,
|
||||
} from "../external_app/external_config";
|
||||
import {
|
||||
PersistentNotification,
|
||||
subscribeNotifications,
|
||||
} from "../data/persistent_notification";
|
||||
import computeDomain from "../common/entity/compute_domain";
|
||||
|
||||
const SHOW_AFTER_SPACER = ["config", "developer-tools"];
|
||||
|
||||
@ -102,12 +107,14 @@ const renderPanel = (hass, panel) => html`
|
||||
* @appliesMixin LocalizeMixin
|
||||
*/
|
||||
class HaSidebar extends LitElement {
|
||||
@property() public hass?: HomeAssistant;
|
||||
@property() public hass!: HomeAssistant;
|
||||
|
||||
@property({ type: Boolean }) public alwaysExpand = false;
|
||||
@property({ type: Boolean, reflect: true }) public expanded = false;
|
||||
@property() public _defaultPage?: string =
|
||||
localStorage.defaultPage || DEFAULT_PANEL;
|
||||
@property() private _externalConfig?: ExternalConfig;
|
||||
@property() private _notifications?: PersistentNotification[];
|
||||
|
||||
protected render() {
|
||||
const hass = this.hass;
|
||||
@ -118,6 +125,15 @@ class HaSidebar extends LitElement {
|
||||
|
||||
const [beforeSpacer, afterSpacer] = computePanels(hass);
|
||||
|
||||
let notificationCount = this._notifications
|
||||
? this._notifications.length
|
||||
: 0;
|
||||
for (const entityId in hass.states) {
|
||||
if (computeDomain(entityId) === "configurator") {
|
||||
notificationCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return html`
|
||||
${this.expanded
|
||||
? html`
|
||||
@ -167,57 +183,60 @@ class HaSidebar extends LitElement {
|
||||
slot="item-icon"
|
||||
icon="hass:cellphone-settings-variant"
|
||||
></ha-icon>
|
||||
<span class="item-text"
|
||||
>${hass.localize(
|
||||
"ui.sidebar.external_app_configuration"
|
||||
)}</span
|
||||
>
|
||||
</paper-icon-item>
|
||||
</a>
|
||||
`
|
||||
: ""}
|
||||
${hass.user
|
||||
? html`
|
||||
<a
|
||||
href="/profile"
|
||||
data-panel="panel"
|
||||
tabindex="-1"
|
||||
aria-role="option"
|
||||
aria-label=${hass.localize("panel.profile")}
|
||||
>
|
||||
<paper-icon-item class="profile">
|
||||
<ha-user-badge
|
||||
slot="item-icon"
|
||||
.user=${hass.user}
|
||||
></ha-user-badge>
|
||||
|
||||
<span class="item-text">
|
||||
${hass.user.name}
|
||||
${hass.localize("ui.sidebar.external_app_configuration")}
|
||||
</span>
|
||||
</paper-icon-item>
|
||||
</a>
|
||||
`
|
||||
: html`
|
||||
<paper-icon-item
|
||||
@click=${this._handleLogOut}
|
||||
class="logout"
|
||||
aria-role="option"
|
||||
>
|
||||
<ha-icon slot="item-icon" icon="hass:exit-to-app"></ha-icon>
|
||||
<span class="item-text"
|
||||
>${hass.localize("ui.sidebar.log_out")}</span
|
||||
>
|
||||
</paper-icon-item>
|
||||
`}
|
||||
: ""}
|
||||
|
||||
<div disabled class="divider sticky-el"></div>
|
||||
|
||||
<paper-icon-item
|
||||
class="notifications sticky-el"
|
||||
aria-role="option"
|
||||
@click=${this._handleShowNotificationDrawer}
|
||||
>
|
||||
<ha-icon slot="item-icon" icon="hass:bell"></ha-icon>
|
||||
${notificationCount > 0
|
||||
? html`
|
||||
<span class="notification-badge" slot="item-icon">
|
||||
${notificationCount}
|
||||
</span>
|
||||
`
|
||||
: ""}
|
||||
<span class="item-text">
|
||||
${hass.localize("ui.notification_drawer.title")}
|
||||
</span>
|
||||
</paper-icon-item>
|
||||
|
||||
<a
|
||||
class="profile sticky-el"
|
||||
href="/profile"
|
||||
data-panel="panel"
|
||||
tabindex="-1"
|
||||
aria-role="option"
|
||||
aria-label=${hass.localize("panel.profile")}
|
||||
>
|
||||
<paper-icon-item>
|
||||
<ha-user-badge slot="item-icon" .user=${hass.user}></ha-user-badge>
|
||||
|
||||
<span class="item-text">
|
||||
${hass.user ? hass.user.name : ""}
|
||||
</span>
|
||||
</paper-icon-item>
|
||||
</a>
|
||||
</paper-listbox>
|
||||
`;
|
||||
}
|
||||
|
||||
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||
if (
|
||||
changedProps.has("_externalConfig") ||
|
||||
changedProps.has("expanded") ||
|
||||
changedProps.has("alwaysExpand")
|
||||
changedProps.has("alwaysExpand") ||
|
||||
changedProps.has("_externalConfig") ||
|
||||
changedProps.has("_notifications")
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
@ -233,7 +252,8 @@ class HaSidebar extends LitElement {
|
||||
hass.panels !== oldHass.panels ||
|
||||
hass.panelUrl !== oldHass.panelUrl ||
|
||||
hass.user !== oldHass.user ||
|
||||
hass.localize !== oldHass.localize
|
||||
hass.localize !== oldHass.localize ||
|
||||
hass.states !== oldHass.states
|
||||
);
|
||||
}
|
||||
|
||||
@ -253,6 +273,17 @@ class HaSidebar extends LitElement {
|
||||
this.addEventListener("mouseleave", () => {
|
||||
this._contract();
|
||||
});
|
||||
subscribeNotifications(this.hass.connection, (notifications) => {
|
||||
this._notifications = notifications;
|
||||
});
|
||||
// Deal with configurator
|
||||
// private _updateNotifications(
|
||||
// states: HassEntities,
|
||||
// persistent: unknown[]
|
||||
// ): unknown[] {
|
||||
// const configurator = computeNotifications(states);
|
||||
// return persistent.concat(configurator);
|
||||
// }
|
||||
}
|
||||
|
||||
protected updated(changedProps) {
|
||||
@ -266,13 +297,13 @@ class HaSidebar extends LitElement {
|
||||
this.expanded = this.alwaysExpand || false;
|
||||
}
|
||||
|
||||
private _handleLogOut() {
|
||||
fireEvent(this, "hass-logout");
|
||||
private _handleShowNotificationDrawer() {
|
||||
fireEvent(this, "hass-show-notifications");
|
||||
}
|
||||
|
||||
private _handleExternalAppConfiguration(ev: Event) {
|
||||
ev.preventDefault();
|
||||
this.hass!.auth.external!.fireMessage({
|
||||
this.hass.auth.external!.fireMessage({
|
||||
type: "config_screen/show",
|
||||
});
|
||||
}
|
||||
@ -386,24 +417,64 @@ class HaSidebar extends LitElement {
|
||||
color: var(--sidebar-selected-text-color);
|
||||
}
|
||||
|
||||
a .item-text {
|
||||
paper-icon-item .item-text {
|
||||
display: none;
|
||||
}
|
||||
:host([expanded]) a .item-text {
|
||||
:host([expanded]) paper-icon-item .item-text {
|
||||
display: block;
|
||||
}
|
||||
|
||||
paper-icon-item.logout {
|
||||
margin-top: 16px;
|
||||
.divider {
|
||||
bottom: 88px;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
paper-icon-item.profile {
|
||||
.divider::before {
|
||||
content: " ";
|
||||
display: block;
|
||||
height: 1px;
|
||||
background-color: var(--divider-color);
|
||||
}
|
||||
|
||||
.notifications {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
bottom: 48px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.profile {
|
||||
bottom: 0;
|
||||
}
|
||||
.profile paper-icon-item {
|
||||
padding-left: 4px;
|
||||
}
|
||||
.profile .item-text {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.sticky-el {
|
||||
position: sticky;
|
||||
background-color: var(
|
||||
--sidebar-background-color,
|
||||
var(--primary-background-color)
|
||||
);
|
||||
}
|
||||
|
||||
.notification-badge {
|
||||
position: absolute;
|
||||
font-weight: 400;
|
||||
bottom: 14px;
|
||||
left: 26px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--primary-color);
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
padding: 0px 6px;
|
||||
font-size: 0.65em;
|
||||
color: var(--text-primary-color);
|
||||
}
|
||||
|
||||
.spacer {
|
||||
flex: 1;
|
||||
pointer-events: none;
|
||||
|
43
src/data/persistent_notification.ts
Normal file
43
src/data/persistent_notification.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import {
|
||||
createCollection,
|
||||
Connection,
|
||||
HassEntity,
|
||||
} from "home-assistant-js-websocket";
|
||||
|
||||
export interface PersitentNotificationEntity extends HassEntity {
|
||||
notification_id?: string;
|
||||
created_at?: string;
|
||||
title?: string;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export interface PersistentNotification {
|
||||
created_at: string;
|
||||
message: string;
|
||||
notification_id: string;
|
||||
title: string;
|
||||
status: "read" | "unread";
|
||||
}
|
||||
|
||||
const fetchNotifications = (conn) =>
|
||||
conn.sendMessagePromise({
|
||||
type: "persistent_notification/get",
|
||||
});
|
||||
|
||||
const subscribeUpdates = (conn, store) =>
|
||||
conn.subscribeEvents(
|
||||
() => fetchNotifications(conn).then((ntf) => store.setState(ntf, true)),
|
||||
"persistent_notifications_updated"
|
||||
);
|
||||
|
||||
export const subscribeNotifications = (
|
||||
conn: Connection,
|
||||
onChange: (notifications: PersistentNotification[]) => void
|
||||
) =>
|
||||
createCollection<PersistentNotification[]>(
|
||||
"_ntf",
|
||||
fetchNotifications,
|
||||
subscribeUpdates,
|
||||
conn,
|
||||
onChange
|
||||
);
|
@ -1,24 +0,0 @@
|
||||
import { createCollection, Connection } from "home-assistant-js-websocket";
|
||||
|
||||
const fetchNotifications = (conn) =>
|
||||
conn.sendMessagePromise({
|
||||
type: "persistent_notification/get",
|
||||
});
|
||||
|
||||
const subscribeUpdates = (conn, store) =>
|
||||
conn.subscribeEvents(
|
||||
() => fetchNotifications(conn).then((ntf) => store.setState(ntf, true)),
|
||||
"persistent_notifications_updated"
|
||||
);
|
||||
|
||||
export const subscribeNotifications = (
|
||||
conn: Connection,
|
||||
onChange: (notifications: Notification[]) => void
|
||||
) =>
|
||||
createCollection<Notification[]>(
|
||||
"_ntf",
|
||||
fetchNotifications,
|
||||
subscribeUpdates,
|
||||
conn,
|
||||
onChange
|
||||
);
|
@ -7,17 +7,17 @@ import {
|
||||
} from "lit-element";
|
||||
import "@material/mwc-button";
|
||||
|
||||
import "./hui-notification-item-template";
|
||||
import "./notification-item-template";
|
||||
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { HassNotification } from "./types";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { PersitentNotificationEntity } from "../../data/persistent_notification";
|
||||
|
||||
@customElement("hui-configurator-notification-item")
|
||||
@customElement("configurator-notification-item")
|
||||
export class HuiConfiguratorNotificationItem extends LitElement {
|
||||
@property() public hass?: HomeAssistant;
|
||||
|
||||
@property() public notification?: HassNotification;
|
||||
@property() public notification?: PersitentNotificationEntity;
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
if (!this.hass || !this.notification) {
|
||||
@ -25,7 +25,7 @@ export class HuiConfiguratorNotificationItem extends LitElement {
|
||||
}
|
||||
|
||||
return html`
|
||||
<hui-notification-item-template>
|
||||
<notification-item-template>
|
||||
<span slot="header">${this.hass.localize("domain.configurator")}</span>
|
||||
|
||||
<div>
|
||||
@ -41,7 +41,7 @@ export class HuiConfiguratorNotificationItem extends LitElement {
|
||||
`state.configurator.${this.notification.state}`
|
||||
)}</mwc-button
|
||||
>
|
||||
</hui-notification-item-template>
|
||||
</notification-item-template>
|
||||
`;
|
||||
}
|
||||
|
||||
@ -54,6 +54,6 @@ export class HuiConfiguratorNotificationItem extends LitElement {
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-configurator-notification-item": HuiConfiguratorNotificationItem;
|
||||
"configurator-notification-item": HuiConfiguratorNotificationItem;
|
||||
}
|
||||
}
|
@ -5,13 +5,14 @@ import "@polymer/app-layout/app-toolbar/app-toolbar";
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
|
||||
import "./hui-notification-item";
|
||||
import "../../../../components/ha-paper-icon-button-next";
|
||||
|
||||
import { EventsMixin } from "../../../../mixins/events-mixin";
|
||||
import LocalizeMixin from "../../../../mixins/localize-mixin";
|
||||
import { computeRTL } from "../../../../common/util/compute_rtl";
|
||||
import "./notification-item";
|
||||
import "../../components/ha-paper-icon-button-next";
|
||||
|
||||
import { EventsMixin } from "../../mixins/events-mixin";
|
||||
import LocalizeMixin from "../../mixins/localize-mixin";
|
||||
import { computeRTL } from "../../common/util/compute_rtl";
|
||||
import { subscribeNotifications } from "../../data/persistent_notification";
|
||||
import computeDomain from "../../common/entity/compute_domain";
|
||||
/*
|
||||
* @appliesMixin EventsMixin
|
||||
* @appliesMixin LocalizeMixin
|
||||
@ -129,7 +130,7 @@ export class HuiNotificationDrawer extends EventsMixin(
|
||||
<dom-repeat items="[[notifications]]">
|
||||
<template>
|
||||
<div class="notification">
|
||||
<hui-notification-item hass="[[hass]]" notification="[[item]]"></hui-notification-item>
|
||||
<notification-item hass="[[hass]]" notification="[[item]]"></notification-item>
|
||||
</div>
|
||||
</template>
|
||||
</dom-repeat>
|
||||
@ -160,6 +161,10 @@ export class HuiNotificationDrawer extends EventsMixin(
|
||||
reflectToAttribute: true,
|
||||
},
|
||||
notifications: {
|
||||
type: Array,
|
||||
computed: "_computeNotifications(open, hass, _notificationsBackend)",
|
||||
},
|
||||
_notificationsBackend: {
|
||||
type: Array,
|
||||
value: [],
|
||||
},
|
||||
@ -171,6 +176,16 @@ export class HuiNotificationDrawer extends EventsMixin(
|
||||
};
|
||||
}
|
||||
|
||||
ready() {
|
||||
super.ready();
|
||||
window.addEventListener("location-changed", () => {
|
||||
// close drawer when we navigate away.
|
||||
if (this.open) {
|
||||
this.open = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_closeDrawer(ev) {
|
||||
ev.stopPropagation();
|
||||
this.open = false;
|
||||
@ -188,17 +203,44 @@ export class HuiNotificationDrawer extends EventsMixin(
|
||||
this._openTimer = setTimeout(() => {
|
||||
this.classList.add("open");
|
||||
}, 50);
|
||||
this._unsubNotifications = subscribeNotifications(
|
||||
this.hass.connection,
|
||||
(notifications) => {
|
||||
this._notificationsBackend = notifications;
|
||||
}
|
||||
);
|
||||
} else {
|
||||
// Animate closed then hide
|
||||
this.classList.remove("open");
|
||||
this._openTimer = setTimeout(() => {
|
||||
this.hidden = true;
|
||||
}, 250);
|
||||
if (this._unsubNotifications) {
|
||||
this._unsubNotifications();
|
||||
this._unsubNotifications = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_computeRTL(hass) {
|
||||
return computeRTL(hass);
|
||||
}
|
||||
|
||||
_computeNotifications(open, hass, notificationsBackend) {
|
||||
if (!open) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const configuratorEntities = Object.keys(hass.states)
|
||||
.filter((entityId) => computeDomain(entityId) === "configurator")
|
||||
.map((entityId) => hass.states[entityId]);
|
||||
|
||||
return notificationsBackend.concat(configuratorEntities);
|
||||
}
|
||||
|
||||
showDialog({ narrow }) {
|
||||
this.open = true;
|
||||
this.narrow = narrow;
|
||||
}
|
||||
}
|
||||
customElements.define("hui-notification-drawer", HuiNotificationDrawer);
|
||||
customElements.define("notification-drawer", HuiNotificationDrawer);
|
@ -7,9 +7,9 @@ import {
|
||||
CSSResult,
|
||||
} from "lit-element";
|
||||
|
||||
import "../../../../components/ha-card";
|
||||
import "../../components/ha-card";
|
||||
|
||||
@customElement("hui-notification-item-template")
|
||||
@customElement("notification-item-template")
|
||||
export class HuiNotificationItemTemplate extends LitElement {
|
||||
protected render(): TemplateResult | void {
|
||||
return html`
|
||||
@ -60,6 +60,6 @@ export class HuiNotificationItemTemplate extends LitElement {
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-notification-item-template": HuiNotificationItemTemplate;
|
||||
"notification-item-template": HuiNotificationItemTemplate;
|
||||
}
|
||||
}
|
@ -6,18 +6,19 @@ import {
|
||||
TemplateResult,
|
||||
html,
|
||||
} from "lit-element";
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
|
||||
import "./hui-configurator-notification-item";
|
||||
import "./hui-persistent-notification-item";
|
||||
import "./configurator-notification-item";
|
||||
import "./persistent-notification-item";
|
||||
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { HassNotification } from "./types";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import { PersistentNotification } from "../../data/persistent_notification";
|
||||
|
||||
@customElement("hui-notification-item")
|
||||
@customElement("notification-item")
|
||||
export class HuiNotificationItem extends LitElement {
|
||||
@property() public hass?: HomeAssistant;
|
||||
|
||||
@property() public notification?: HassNotification;
|
||||
@property() public notification?: HassEntity | PersistentNotification;
|
||||
|
||||
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||
if (!this.hass || !this.notification || changedProps.has("notification")) {
|
||||
@ -32,24 +33,24 @@ export class HuiNotificationItem extends LitElement {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return this.notification.entity_id
|
||||
return "entity_id" in this.notification
|
||||
? html`
|
||||
<hui-configurator-notification-item
|
||||
<configurator-notification-item
|
||||
.hass="${this.hass}"
|
||||
.notification="${this.notification}"
|
||||
></hui-configurator-notification-item>
|
||||
></configurator-notification-item>
|
||||
`
|
||||
: html`
|
||||
<hui-persistent-notification-item
|
||||
<persistent-notification-item
|
||||
.hass="${this.hass}"
|
||||
.notification="${this.notification}"
|
||||
></hui-persistent-notification-item>
|
||||
></persistent-notification-item>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-notification-item": HuiNotificationItem;
|
||||
"notification-item": HuiNotificationItem;
|
||||
}
|
||||
}
|
@ -10,18 +10,18 @@ import {
|
||||
import "@material/mwc-button";
|
||||
import "@polymer/paper-tooltip/paper-tooltip";
|
||||
|
||||
import "../../../../components/ha-relative-time";
|
||||
import "../../../../components/ha-markdown";
|
||||
import "./hui-notification-item-template";
|
||||
import "../../components/ha-relative-time";
|
||||
import "../../components/ha-markdown";
|
||||
import "./notification-item-template";
|
||||
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { HassNotification } from "./types";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import { PersistentNotification } from "../../data/persistent_notification";
|
||||
|
||||
@customElement("hui-persistent-notification-item")
|
||||
@customElement("persistent-notification-item")
|
||||
export class HuiPersistentNotificationItem extends LitElement {
|
||||
@property() public hass?: HomeAssistant;
|
||||
|
||||
@property() public notification?: HassNotification;
|
||||
@property() public notification?: PersistentNotification;
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
if (!this.hass || !this.notification) {
|
||||
@ -29,8 +29,10 @@ export class HuiPersistentNotificationItem extends LitElement {
|
||||
}
|
||||
|
||||
return html`
|
||||
<hui-notification-item-template>
|
||||
<span slot="header">${this._computeTitle(this.notification)}</span>
|
||||
<notification-item-template>
|
||||
<span slot="header">
|
||||
${this.notification.title || this.notification.notification_id}
|
||||
</span>
|
||||
|
||||
<ha-markdown content="${this.notification.message}"></ha-markdown>
|
||||
|
||||
@ -54,7 +56,7 @@ export class HuiPersistentNotificationItem extends LitElement {
|
||||
"ui.card.persistent_notification.dismiss"
|
||||
)}</mwc-button
|
||||
>
|
||||
</hui-notification-item-template>
|
||||
</notification-item-template>
|
||||
`;
|
||||
}
|
||||
|
||||
@ -80,13 +82,9 @@ export class HuiPersistentNotificationItem extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
private _computeTitle(notification: HassNotification): string | undefined {
|
||||
return notification.title || notification.notification_id;
|
||||
}
|
||||
|
||||
private _computeTooltip(
|
||||
hass: HomeAssistant,
|
||||
notification: HassNotification
|
||||
notification: PersistentNotification
|
||||
): string | undefined {
|
||||
if (!hass || !notification) {
|
||||
return undefined;
|
||||
@ -105,6 +103,6 @@ export class HuiPersistentNotificationItem extends LitElement {
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-persistent-notification-item": HuiPersistentNotificationItem;
|
||||
"persistent-notification-item": HuiPersistentNotificationItem;
|
||||
}
|
||||
}
|
16
src/dialogs/notifications/show-notification-drawer.ts
Normal file
16
src/dialogs/notifications/show-notification-drawer.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
|
||||
export interface NotificationDrawerParams {
|
||||
narrow: boolean;
|
||||
}
|
||||
|
||||
export const showNotificationDrawer = (
|
||||
element: HTMLElement,
|
||||
dialogParams: NotificationDrawerParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "notification-drawer" as any, // Not in TS yet
|
||||
dialogImport: () => import("./notification-drawer"),
|
||||
dialogParams,
|
||||
});
|
||||
};
|
@ -20,6 +20,7 @@ import { fireEvent } from "../common/dom/fire_event";
|
||||
import { PolymerChangedEvent } from "../polymer-types";
|
||||
// tslint:disable-next-line: no-duplicate-imports
|
||||
import { AppDrawerLayoutElement } from "@polymer/app-layout/app-drawer-layout/app-drawer-layout";
|
||||
import { showNotificationDrawer } from "../dialogs/notifications/show-notification-drawer";
|
||||
|
||||
const NON_SWIPABLE_PANELS = ["kiosk", "map"];
|
||||
|
||||
@ -27,6 +28,7 @@ declare global {
|
||||
// for fire event
|
||||
interface HASSDomEvents {
|
||||
"hass-toggle-menu": undefined;
|
||||
"hass-show-notifications": undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,6 +68,7 @@ class HomeAssistantMain extends LitElement {
|
||||
>
|
||||
<ha-sidebar
|
||||
.hass=${hass}
|
||||
.narrow=${this.narrow}
|
||||
.alwaysExpand=${this.narrow || hass.dockedSidebar}
|
||||
></ha-sidebar>
|
||||
</app-drawer>
|
||||
@ -96,6 +99,12 @@ class HomeAssistantMain extends LitElement {
|
||||
setTimeout(() => this.appLayout.resetLayout());
|
||||
}
|
||||
});
|
||||
|
||||
this.addEventListener("hass-show-notifications", () => {
|
||||
showNotificationDrawer(this, {
|
||||
narrow: this.narrow!,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues) {
|
||||
|
@ -1,9 +0,0 @@
|
||||
import { HassEntities, HassEntity } from "home-assistant-js-websocket";
|
||||
|
||||
import computeDomain from "../../../common/entity/compute_domain";
|
||||
|
||||
export const computeNotifications = (states: HassEntities): HassEntity[] => {
|
||||
return Object.keys(states)
|
||||
.filter((entityId) => computeDomain(entityId) === "configurator")
|
||||
.map((entityId) => states[entityId]);
|
||||
};
|
@ -1,81 +0,0 @@
|
||||
import {
|
||||
html,
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
css,
|
||||
CSSResult,
|
||||
property,
|
||||
} from "lit-element";
|
||||
import "@polymer/paper-icon-button/paper-icon-button";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
|
||||
declare global {
|
||||
// tslint:disable-next-line
|
||||
interface HASSDomEvents {
|
||||
"opened-changed": { value: boolean };
|
||||
}
|
||||
}
|
||||
|
||||
class HuiNotificationsButton extends LitElement {
|
||||
@property() public notifications?: string[];
|
||||
@property() public opened?: boolean;
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
return html`
|
||||
<paper-icon-button
|
||||
aria-label="Show Notifications"
|
||||
icon="hass:bell"
|
||||
@click="${this._clicked}"
|
||||
></paper-icon-button>
|
||||
${this.notifications && this.notifications.length > 0
|
||||
? html`
|
||||
<span class="indicator">
|
||||
<div>${this.notifications.length}</div>
|
||||
</span>
|
||||
`
|
||||
: ""}
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
css`
|
||||
:host {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.indicator {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: -3px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background: var(--accent-color);
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.indicator > div {
|
||||
right: 7px;
|
||||
top: 3px;
|
||||
position: absolute;
|
||||
font-size: 0.55em;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
private _clicked() {
|
||||
this.opened = true;
|
||||
fireEvent(this, "opened-changed", { value: this.opened });
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-notifications-button": HuiNotificationsButton;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("hui-notifications-button", HuiNotificationsButton);
|
@ -1,8 +0,0 @@
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
|
||||
export declare type HassNotification = HassEntity & {
|
||||
notification_id?: string;
|
||||
created_at?: string;
|
||||
title?: string;
|
||||
message?: string;
|
||||
};
|
@ -20,7 +20,6 @@ import "@polymer/paper-listbox/paper-listbox";
|
||||
import "@polymer/paper-menu-button/paper-menu-button";
|
||||
import "@polymer/paper-tabs/paper-tab";
|
||||
import "@polymer/paper-tabs/paper-tabs";
|
||||
import { HassEntities } from "home-assistant-js-websocket";
|
||||
|
||||
import scrollToTarget from "../../common/dom/scroll-to-target";
|
||||
|
||||
@ -30,17 +29,13 @@ import "../../components/ha-paper-icon-button-arrow-next";
|
||||
import "../../components/ha-paper-icon-button-arrow-prev";
|
||||
import "../../components/ha-icon";
|
||||
import { loadModule, loadCSS, loadJS } from "../../common/dom/load_resource";
|
||||
import { subscribeNotifications } from "../../data/ws-notifications";
|
||||
import { debounce } from "../../common/util/debounce";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import { LovelaceConfig } from "../../data/lovelace";
|
||||
import { navigate } from "../../common/navigate";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { computeNotifications } from "./common/compute-notifications";
|
||||
import { swapView } from "./editor/config-util";
|
||||
|
||||
import "./components/notifications/hui-notification-drawer";
|
||||
import "./components/notifications/hui-notifications-button";
|
||||
import "./hui-view";
|
||||
// Not a duplicate import, this one is for type
|
||||
// tslint:disable-next-line
|
||||
@ -65,12 +60,9 @@ class HUIRoot extends LitElement {
|
||||
@property() public route?: { path: string; prefix: string };
|
||||
@property() private _routeData?: { view: string };
|
||||
@property() private _curView?: number | "hass-unused-entities";
|
||||
@property() private _notificationsOpen = false;
|
||||
@property() private _persistentNotifications?: Notification[];
|
||||
private _viewCache?: { [viewId: string]: HUIView };
|
||||
|
||||
private _debouncedConfigChanged: () => void;
|
||||
private _unsubNotifications?: () => void;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@ -83,35 +75,11 @@ class HUIRoot extends LitElement {
|
||||
);
|
||||
}
|
||||
|
||||
public connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
this._unsubNotifications = subscribeNotifications(
|
||||
this.hass!.connection,
|
||||
(notifications) => {
|
||||
this._persistentNotifications = notifications;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
if (this._unsubNotifications) {
|
||||
this._unsubNotifications();
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
return html`
|
||||
<app-route .route="${this.route}" pattern="/:view" data="${
|
||||
this._routeData
|
||||
}" @data-changed="${this._routeDataChanged}"></app-route>
|
||||
<hui-notification-drawer
|
||||
.hass="${this.hass}"
|
||||
.notifications="${this._notifications}"
|
||||
.open="${this._notificationsOpen}"
|
||||
@open-changed="${this._handleNotificationsOpenChanged}"
|
||||
.narrow="${this.narrow}"
|
||||
></hui-notification-drawer>
|
||||
<ha-app-layout id="layout">
|
||||
<app-header slot="header" effects="waterfall" class="${classMap({
|
||||
"edit-mode": this._editMode,
|
||||
@ -165,12 +133,6 @@ class HUIRoot extends LitElement {
|
||||
<app-toolbar>
|
||||
<ha-menu-button></ha-menu-button>
|
||||
<div main-title>${this.config.title || "Home Assistant"}</div>
|
||||
<hui-notifications-button
|
||||
.hass="${this.hass}"
|
||||
.opened="${this._notificationsOpen}"
|
||||
@opened-changed="${this._handleNotificationsOpenChanged}"
|
||||
.notifications="${this._notifications}"
|
||||
></hui-notifications-button>
|
||||
<ha-start-voice-button
|
||||
.hass="${this.hass}"
|
||||
></ha-start-voice-button>
|
||||
@ -449,13 +411,6 @@ class HUIRoot extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private get _notifications() {
|
||||
return this._updateNotifications(
|
||||
this.hass!.states,
|
||||
this._persistentNotifications! || []
|
||||
);
|
||||
}
|
||||
|
||||
private get config(): LovelaceConfig {
|
||||
return this.lovelace!.config;
|
||||
}
|
||||
@ -480,18 +435,6 @@ class HUIRoot extends LitElement {
|
||||
this._routeData = ev.detail.value;
|
||||
}
|
||||
|
||||
private _handleNotificationsOpenChanged(ev): void {
|
||||
this._notificationsOpen = ev.detail.value;
|
||||
}
|
||||
|
||||
private _updateNotifications(
|
||||
states: HassEntities,
|
||||
persistent: unknown[]
|
||||
): unknown[] {
|
||||
const configurator = computeNotifications(states);
|
||||
return persistent.concat(configurator);
|
||||
}
|
||||
|
||||
private _handleRefresh(): void {
|
||||
fireEvent(this, "config-refresh");
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user