Improve more info update release note display (#22502)

* Fix ha-settings-row

* Improve update more info and update available card

* Set actions at the bottom on mobile

* Use update instead of install

* Improve markdown loaded
This commit is contained in:
Paul Bottein 2024-10-29 09:16:02 +01:00 committed by GitHub
parent e55f32ae91
commit 901f736d5f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 306 additions and 198 deletions

View File

@ -1,11 +1,10 @@
import "@material/mwc-list/mwc-list-item";
import {
css,
CSSResultGroup,
html,
LitElement,
PropertyValues,
nothing,
PropertyValues,
} from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
@ -16,12 +15,12 @@ import "../../../src/components/ha-button-menu";
import "../../../src/components/ha-card";
import "../../../src/components/ha-checkbox";
import "../../../src/components/ha-faded";
import "../../../src/components/ha-formfield";
import "../../../src/components/ha-icon-button";
import "../../../src/components/ha-markdown";
import "../../../src/components/ha-settings-row";
import "../../../src/components/ha-svg-icon";
import "../../../src/components/ha-switch";
import type { HaSwitch } from "../../../src/components/ha-switch";
import {
fetchHassioAddonChangelog,
fetchHassioAddonInfo,
@ -42,6 +41,7 @@ import { updateCore } from "../../../src/data/supervisor/core";
import { StoreAddon } from "../../../src/data/supervisor/store";
import { Supervisor } from "../../../src/data/supervisor/supervisor";
import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box";
import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant, Route } from "../../../src/types";
import { addonArchIsSupported, extractChangelog } from "../util/addon";
@ -149,7 +149,7 @@ class UpdateAvailableCard extends LitElement {
</ha-markdown>
</ha-faded>
`
: ""}
: nothing}
<div class="versions">
<p>
${this.supervisor.localize(
@ -164,15 +164,17 @@ class UpdateAvailableCard extends LitElement {
</div>
${["core", "addon"].includes(this._updateType)
? html`
<ha-formfield
.label=${this.supervisor.localize(
"update_available.create_backup"
)}
>
<ha-checkbox checked></ha-checkbox>
</ha-formfield>
<hr />
<ha-settings-row>
<span slot="heading">
${this.supervisor.localize(
"update_available.create_backup"
)}
</span>
<ha-switch id="create_backup" checked></ha-switch>
</ha-settings-row>
`
: ""}
: nothing}
`
: html`<ha-circular-progress
aria-label="Updating"
@ -191,22 +193,24 @@ class UpdateAvailableCard extends LitElement {
? html`
<div class="card-actions">
${changelog
? html`<a .href=${changelog} target="_blank" rel="noreferrer">
<mwc-button
.label=${this.supervisor.localize(
"update_available.open_release_notes"
)}
>
</mwc-button>
</a>`
: ""}
? html`
<a href=${changelog} target="_blank" rel="noreferrer">
<ha-button
.label=${this.supervisor.localize(
"update_available.open_release_notes"
)}
>
</ha-button>
</a>
`
: nothing}
<span></span>
<ha-progress-button @click=${this._update} raised>
<ha-progress-button @click=${this._update}>
${this.supervisor.localize("common.update")}
</ha-progress-button>
</div>
`
: ""}
: nothing}
</ha-card>
`;
}
@ -242,9 +246,11 @@ class UpdateAvailableCard extends LitElement {
if (this._updateType && !["core", "addon"].includes(this._updateType)) {
return false;
}
const checkbox = this.shadowRoot?.querySelector("ha-checkbox");
if (checkbox) {
return checkbox.checked;
const createBackupSwitch = this.shadowRoot?.getElementById(
"create-backup"
) as HaSwitch;
if (createBackupSwitch) {
return createBackupSwitch.checked;
}
return true;
}
@ -397,41 +403,50 @@ class UpdateAvailableCard extends LitElement {
}
static get styles(): CSSResultGroup {
return css`
:host {
display: block;
}
ha-card {
margin: auto;
}
a {
text-decoration: none;
color: var(--primary-text-color);
}
ha-settings-row {
padding: 0;
}
.card-actions {
display: flex;
justify-content: space-between;
border-top: none;
padding: 0 8px 8px;
}
return [
haStyle,
css`
:host {
display: block;
}
ha-card {
margin: auto;
}
a {
text-decoration: none;
color: var(--primary-text-color);
}
.card-actions {
display: flex;
justify-content: space-between;
}
ha-circular-progress {
display: block;
margin: 32px;
text-align: center;
}
ha-circular-progress {
display: block;
margin: 32px;
text-align: center;
}
.progress-text {
text-align: center;
}
.progress-text {
text-align: center;
}
ha-markdown {
padding-bottom: 8px;
}
`;
ha-markdown {
padding-bottom: 8px;
}
ha-settings-row {
padding: 0;
margin-bottom: -16px;
}
hr {
border-color: var(--divider-color);
border-bottom: none;
margin: 16px 0 0 0;
}
`,
];
}
}

View File

@ -86,6 +86,11 @@ export class HaMarkdown extends LitElement {
font-size: 1.5em;
font-weight: bold;
}
hr {
border-color: var(--divider-color);
border-bottom: none;
margin: 16px 0;
}
`;
}
}

View File

@ -42,14 +42,17 @@ export class HaSettingsRow extends LitElement {
padding-bottom: 8px;
padding-left: 0;
padding-inline-start: 0;
padding-right: 16x;
padding-right: 16px;
padding-inline-end: 16px;
overflow: hidden;
display: var(--layout-vertical_-_display);
flex-direction: var(--layout-vertical_-_flex-direction);
justify-content: var(--layout-center-justified_-_justify-content);
flex: var(--layout-flex_-_flex);
flex-basis: var(--layout-flex_-_flex-basis);
display: var(--layout-vertical_-_display, flex);
flex-direction: var(--layout-vertical_-_flex-direction, column);
justify-content: var(
--layout-center-justified_-_justify-content,
center
);
flex: var(--layout-flex_-_flex, 1);
flex-basis: var(--layout-flex_-_flex-basis, 0.000000001px);
}
.body[three-line] {
min-height: var(--paper-item-body-three-line-min-height, 88px);

View File

@ -31,6 +31,9 @@ export const DOMAINS_WITH_NEW_MORE_INFO = [
"valve",
"water_heater",
];
/** Domains with full height more info dialog */
export const DOMAINS_FULL_HEIGHT_MORE_INFO = ["update"];
/** Domains with separate more info dialog. */
export const DOMAINS_WITH_MORE_INFO = [
"alarm_control_panel",

View File

@ -1,15 +1,18 @@
import "@material/mwc-button/mwc-button";
import "@material/mwc-linear-progress/mwc-linear-progress";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { BINARY_STATE_OFF } from "../../../common/const";
import { supportsFeature } from "../../../common/entity/supports-feature";
import "../../../components/ha-alert";
import "../../../components/ha-button";
import "../../../components/ha-checkbox";
import "../../../components/ha-circular-progress";
import "../../../components/ha-faded";
import "../../../components/ha-formfield";
import "../../../components/ha-markdown";
import "../../../components/ha-settings-row";
import "../../../components/ha-switch";
import type { HaSwitch } from "../../../components/ha-switch";
import { isUnavailableState } from "../../../data/entity";
import {
UpdateEntity,
@ -30,6 +33,8 @@ class MoreInfoUpdate extends LitElement {
@state() private _error?: string;
@state() private _markdownLoading = true;
protected render() {
if (
!this.hass ||
@ -45,137 +50,174 @@ class MoreInfoUpdate extends LitElement {
this.stateObj.attributes.latest_version;
return html`
${this.stateObj.attributes.in_progress
? supportsFeature(this.stateObj, UpdateEntityFeature.PROGRESS) &&
this.stateObj.attributes.update_percentage !== null
? html`<mwc-linear-progress
.progress=${this.stateObj.attributes.update_percentage / 100}
buffer=""
></mwc-linear-progress>`
: html`<mwc-linear-progress indeterminate></mwc-linear-progress>`
: ""}
<h3>${this.stateObj.attributes.title}</h3>
${this._error
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
: ""}
<div class="row">
<div class="key">
${this.hass.formatEntityAttributeName(
this.stateObj,
"installed_version"
)}
<div class="content">
${this.stateObj.attributes.in_progress
? supportsFeature(this.stateObj, UpdateEntityFeature.PROGRESS) &&
this.stateObj.attributes.update_percentage !== null
? html`<mwc-linear-progress
.progress=${this.stateObj.attributes.update_percentage / 100}
buffer=""
></mwc-linear-progress>`
: html`<mwc-linear-progress indeterminate></mwc-linear-progress>`
: nothing}
<h3>${this.stateObj.attributes.title}</h3>
${this._error
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
: nothing}
<div class="row">
<div class="key">
${this.hass.formatEntityAttributeName(
this.stateObj,
"installed_version"
)}
</div>
<div class="value">
${this.stateObj.attributes.installed_version ??
this.hass.localize("state.default.unavailable")}
</div>
</div>
<div class="value">
${this.stateObj.attributes.installed_version ??
this.hass.localize("state.default.unavailable")}
<div class="row">
<div class="key">
${this.hass.formatEntityAttributeName(
this.stateObj,
"latest_version"
)}
</div>
<div class="value">
${this.stateObj.attributes.latest_version ??
this.hass.localize("state.default.unavailable")}
</div>
</div>
</div>
<div class="row">
<div class="key">
${this.hass.formatEntityAttributeName(
this.stateObj,
"latest_version"
)}
</div>
<div class="value">
${this.stateObj.attributes.latest_version ??
this.hass.localize("state.default.unavailable")}
</div>
</div>
${this.stateObj.attributes.release_url
? html`<div class="row">
<div class="key">
<a
href=${this.stateObj.attributes.release_url}
target="_blank"
rel="noreferrer"
>
${this.hass.localize(
"ui.dialogs.more_info_control.update.release_announcement"
)}
</a>
</div>
</div>`
: ""}
${supportsFeature(this.stateObj!, UpdateEntityFeature.RELEASE_NOTES) &&
!this._error
? this._releaseNotes === undefined
? html`<div class="flex center">
<ha-circular-progress indeterminate></ha-circular-progress>
${this.stateObj.attributes.release_url
? html`<div class="row">
<div class="key">
<a
href=${this.stateObj.attributes.release_url}
target="_blank"
rel="noreferrer"
>
${this.hass.localize(
"ui.dialogs.more_info_control.update.release_announcement"
)}
</a>
</div>
</div>`
: html`<hr />
<ha-faded>
<ha-markdown .content=${this._releaseNotes}></ha-markdown>
</ha-faded> `
: this.stateObj.attributes.release_summary
? html`<hr />
<ha-markdown
.content=${this.stateObj.attributes.release_summary}
></ha-markdown>`
: ""}
${supportsFeature(this.stateObj, UpdateEntityFeature.BACKUP)
? html`<hr />
<ha-formfield
.label=${this.hass.localize(
"ui.dialogs.more_info_control.update.create_backup"
)}
>
<ha-checkbox
checked
.disabled=${updateIsInstalling(this.stateObj)}
></ha-checkbox>
</ha-formfield> `
: ""}
<div class="actions">
${this.stateObj.state === BINARY_STATE_OFF &&
this.stateObj.attributes.skipped_version
: nothing}
${supportsFeature(this.stateObj!, UpdateEntityFeature.RELEASE_NOTES) &&
!this._error
? this._releaseNotes === undefined
? html`
<hr />
${this._markdownLoading ? this._renderLoader() : nothing}
`
: html`
<hr />
<ha-markdown
@content-resize=${this._markdownLoaded}
.content=${this._releaseNotes}
class=${this._markdownLoading ? "hidden" : ""}
></ha-markdown>
${this._markdownLoading ? this._renderLoader() : nothing}
`
: this.stateObj.attributes.release_summary
? html`
<hr />
<ha-markdown
@content-resize=${this._markdownLoaded}
.content=${this.stateObj.attributes.release_summary}
class=${this._markdownLoading ? "hidden" : ""}
></ha-markdown>
${this._markdownLoading ? this._renderLoader() : nothing}
`
: nothing}
</div>
<div class="footer">
${supportsFeature(this.stateObj, UpdateEntityFeature.BACKUP)
? html`
<mwc-button @click=${this._handleClearSkipped}>
${this.hass.localize(
"ui.dialogs.more_info_control.update.clear_skipped"
)}
</mwc-button>
<ha-settings-row>
<span slot="heading">
${this.hass.localize(
"ui.dialogs.more_info_control.update.create_backup"
)}
</span>
<ha-switch
id="create_backup"
checked
.disabled=${updateIsInstalling(this.stateObj)}
></ha-switch>
</ha-settings-row>
`
: html`
<mwc-button
@click=${this._handleSkip}
.disabled=${skippedVersion ||
this.stateObj.state === BINARY_STATE_OFF ||
updateIsInstalling(this.stateObj)}
>
${this.hass.localize(
"ui.dialogs.more_info_control.update.skip"
)}
</mwc-button>
`}
${supportsFeature(this.stateObj, UpdateEntityFeature.INSTALL)
? html`
<mwc-button
@click=${this._handleInstall}
.disabled=${(this.stateObj.state === BINARY_STATE_OFF &&
!skippedVersion) ||
updateIsInstalling(this.stateObj)}
>
${this.hass.localize(
"ui.dialogs.more_info_control.update.install"
)}
</mwc-button>
`
: ""}
: nothing}
<div class="actions">
${this.stateObj.state === BINARY_STATE_OFF &&
this.stateObj.attributes.skipped_version
? html`
<ha-button @click=${this._handleClearSkipped}>
${this.hass.localize(
"ui.dialogs.more_info_control.update.clear_skipped"
)}
</ha-button>
`
: html`
<ha-button
@click=${this._handleSkip}
.disabled=${skippedVersion ||
this.stateObj.state === BINARY_STATE_OFF ||
updateIsInstalling(this.stateObj)}
>
${this.hass.localize(
"ui.dialogs.more_info_control.update.skip"
)}
</ha-button>
`}
${supportsFeature(this.stateObj, UpdateEntityFeature.INSTALL)
? html`
<ha-button
@click=${this._handleInstall}
.disabled=${(this.stateObj.state === BINARY_STATE_OFF &&
!skippedVersion) ||
updateIsInstalling(this.stateObj)}
>
${this.hass.localize(
"ui.dialogs.more_info_control.update.update"
)}
</ha-button>
`
: nothing}
</div>
</div>
`;
}
private _renderLoader() {
return html`
<div class="flex center loader">
<ha-circular-progress indeterminate></ha-circular-progress>
</div>
`;
}
protected firstUpdated(): void {
if (supportsFeature(this.stateObj!, UpdateEntityFeature.RELEASE_NOTES)) {
updateReleaseNotes(this.hass, this.stateObj!.entity_id)
.then((result) => {
this._releaseNotes = result;
})
.catch((err) => {
this._error = err.message;
});
this._fetchReleaseNotes();
}
}
private async _markdownLoaded() {
if (this._markdownLoading) {
this._markdownLoading = false;
}
}
private async _fetchReleaseNotes() {
try {
this._releaseNotes = await updateReleaseNotes(
this.hass,
this.stateObj!.entity_id
);
} catch (err: any) {
this._error = err.message;
}
}
@ -183,9 +225,11 @@ class MoreInfoUpdate extends LitElement {
if (!supportsFeature(this.stateObj!, UpdateEntityFeature.BACKUP)) {
return null;
}
const checkbox = this.shadowRoot?.querySelector("ha-checkbox");
if (checkbox) {
return checkbox.checked;
const createBackupSwitch = this.shadowRoot?.getElementById(
"create-backup"
) as HaSwitch;
if (createBackupSwitch) {
return createBackupSwitch.checked;
}
return true;
}
@ -234,6 +278,12 @@ class MoreInfoUpdate extends LitElement {
static get styles(): CSSResultGroup {
return css`
:host {
display: flex;
flex-direction: column;
flex: 1;
justify-content: space-between;
}
hr {
border-color: var(--divider-color);
border-bottom: none;
@ -248,26 +298,44 @@ class MoreInfoUpdate extends LitElement {
flex-direction: row;
justify-content: space-between;
}
.actions {
.footer {
border-top: 1px solid var(--divider-color);
background: var(
--ha-dialog-surface-background,
var(--mdc-theme-surface, #fff)
);
margin: 8px 0 0;
display: flex;
flex-wrap: wrap;
justify-content: center;
position: sticky;
bottom: 0;
padding: 12px 0;
margin-bottom: -24px;
z-index: 1;
margin: 0 -24px -24px -24px;
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
overflow: hidden;
z-index: 10;
}
.actions mwc-button {
margin: 0 4px 4px;
ha-settings-row {
width: 100%;
padding: 0 24px;
box-sizing: border-box;
margin-bottom: -16px;
margin-top: -4px;
}
.actions {
width: 100%;
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-end;
box-sizing: border-box;
padding: 12px;
z-index: 1;
gap: 8px;
}
a {
color: var(--primary-color);
}
@ -282,6 +350,16 @@ class MoreInfoUpdate extends LitElement {
}
ha-markdown {
direction: ltr;
padding-bottom: 16px;
box-sizing: border-box;
}
ha-markdown.hidden {
display: none;
}
.loader {
height: 80px;
box-sizing: border-box;
padding-bottom: 16px;
}
`;
}

View File

@ -9,6 +9,7 @@ import {
computeShowHistoryComponent,
computeShowLogBookComponent,
computeShowNewMoreInfo,
DOMAINS_FULL_HEIGHT_MORE_INFO,
DOMAINS_NO_INFO,
DOMAINS_WITH_MORE_INFO,
} from "./const";
@ -40,6 +41,8 @@ export class MoreInfoInfo extends LitElement {
const entityRegObj = this.hass.entities[entityId];
const domain = computeDomain(entityId);
const isNewMoreInfo = stateObj && computeShowNewMoreInfo(stateObj);
const isFullHeight =
isNewMoreInfo || DOMAINS_FULL_HEIGHT_MORE_INFO.includes(domain);
return html`
<div class="container" data-domain=${domain}>
@ -89,7 +92,7 @@ export class MoreInfoInfo extends LitElement {
.entityId=${this.entityId}
></ha-more-info-logbook>`}
<more-info-content
?full-height=${isNewMoreInfo}
?full-height=${isFullHeight}
.stateObj=${stateObj}
.hass=${this.hass}
.entry=${this.entry}

View File

@ -1191,6 +1191,7 @@
"skip": "Skip",
"clear_skipped": "Clear skipped",
"install": "Install",
"update": "Update",
"create_backup": "Create backup before updating",
"auto_update_enabled_title": "Can not skip version",
"auto_update_enabled_text": "Automatic updates for this item have been enabled; skipping it is, therefore, unavailable. You can either install this update now or wait for Home Assistant to do it automatically."