diff --git a/src/data/backup_manager.ts b/src/data/backup_manager.ts index e92f0f7c99..9665bed0d6 100644 --- a/src/data/backup_manager.ts +++ b/src/data/backup_manager.ts @@ -13,6 +13,7 @@ export type CreateBackupStage = | "app_repositories" | "apps" | "await_app_restarts" + | "cleaning_up" | "docker_config" | "finishing_file" | "folders" diff --git a/src/panels/config/backup/components/overview/ha-backup-overview-progress.ts b/src/panels/config/backup/components/overview/ha-backup-overview-progress.ts index d5a249cbeb..e28d4d1286 100644 --- a/src/panels/config/backup/components/overview/ha-backup-overview-progress.ts +++ b/src/panels/config/backup/components/overview/ha-backup-overview-progress.ts @@ -1,7 +1,7 @@ import { mdiCheck, mdiHarddisk, mdiNas } from "@mdi/js"; -import type { CSSResultGroup } from "lit"; +import type { CSSResultGroup, PropertyValues } from "lit"; import { css, html, LitElement, nothing } from "lit"; -import { customElement, property } from "lit/decorators"; +import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import { isComponentLoaded } from "../../../../../common/config/is_component_loaded"; import { computeDomain } from "../../../../../common/entity/compute_domain"; @@ -51,6 +51,7 @@ const STAGE_ORDER: CreateBackupStage[][] = [ MEDIA_STAGES, HA_STAGES, ["upload_to_agents"], + ["cleaning_up"], ]; @customElement("ha-backup-overview-progress") @@ -66,6 +67,22 @@ export class HaBackupOverviewProgress extends LitElement { { uploaded_bytes: number; total_bytes: number } > = {}; + @state() private _collapsingAgents = false; + + @state() private _wasUploadStage = false; + + @state() private _delayingCollapse = false; + + private _collapseTimeout?: ReturnType; + + override disconnectedCallback() { + super.disconnectedCallback(); + if (this._collapseTimeout) { + clearTimeout(this._collapseTimeout); + this._collapseTimeout = undefined; + } + } + private get _heading() { const managerState = this.manager.manager_state; if (managerState === "idle") { @@ -144,61 +161,75 @@ export class HaBackupOverviewProgress extends LitElement { const isHassio = isComponentLoaded(this.hass, "hassio"); if (isHassio) { - // Split creation into 3 sub-segments + Upload + // Split creation into 3 sub-segments + Upload + Cleaning up return [ { label: this.hass.localize( "ui.panel.config.backup.overview.progress.segments.apps" ), state: this._getSegmentState(0, currentGroupIndex), - flex: 1, + flex: 2, }, { label: this.hass.localize( "ui.panel.config.backup.overview.progress.segments.media" ), state: this._getSegmentState(1, currentGroupIndex), - flex: 1, + flex: 2, }, { label: this.hass.localize( "ui.panel.config.backup.overview.progress.segments.home_assistant" ), state: this._getSegmentState(2, currentGroupIndex), - flex: 1, + flex: 2, }, { label: this.hass.localize( "ui.panel.config.backup.overview.progress.segments.upload" ), state: this._getSegmentState(3, currentGroupIndex), - flex: 3, + flex: 5, + }, + { + label: this.hass.localize( + "ui.panel.config.backup.overview.progress.segments.cleaning_up" + ), + state: this._getSegmentState(4, currentGroupIndex), + flex: 1, }, ]; } - // Non-HAOS: No app segment, just Media, HA and Upload + // Non-HAOS: No app segment, just Media, HA, Upload and Cleaning up return [ { label: this.hass.localize( "ui.panel.config.backup.overview.progress.segments.media" ), state: this._getSegmentState(1, currentGroupIndex), - flex: 1, + flex: 2, }, { label: this.hass.localize( "ui.panel.config.backup.overview.progress.segments.home_assistant" ), state: this._getSegmentState(2, currentGroupIndex), - flex: 1, + flex: 2, }, { label: this.hass.localize( "ui.panel.config.backup.overview.progress.segments.upload" ), state: this._getSegmentState(3, currentGroupIndex), - flex: 3, + flex: 5, + }, + { + label: this.hass.localize( + "ui.panel.config.backup.overview.progress.segments.cleaning_up" + ), + state: this._getSegmentState(4, currentGroupIndex), + flex: 1, }, ]; } @@ -229,6 +260,26 @@ export class HaBackupOverviewProgress extends LitElement { `; } + override willUpdate(changedProps: PropertyValues) { + if (changedProps.has("manager")) { + const isUpload = this._isUploadStage; + if (this._wasUploadStage && !isUpload) { + // Delay collapse to let the checkmark animation finish + this._delayingCollapse = true; + this._collapseTimeout = setTimeout(() => { + this._delayingCollapse = false; + this._collapsingAgents = true; + this._collapseTimeout = undefined; + }, 300); + } + this._wasUploadStage = isUpload; + } + } + + private _handleAgentCollapseEnd = () => { + this._collapsingAgents = false; + }; + private _renderSegmentedProgress() { const managerState = this.manager.manager_state; @@ -261,7 +312,10 @@ export class HaBackupOverviewProgress extends LitElement { } private _renderAgentProgress() { - if (!this._isUploadStage || this.agents.length === 0) { + const showAgents = + this._isUploadStage || this._delayingCollapse || this._collapsingAgents; + + if (!showAgents || this.agents.length === 0) { return nothing; } @@ -272,7 +326,12 @@ export class HaBackupOverviewProgress extends LitElement { } return html` -
+
${this.agents.map((agent) => { const name = computeBackupAgentName( @@ -417,6 +476,10 @@ export class HaBackupOverviewProgress extends LitElement { grid-template-rows: 1fr; animation: expand var(--ha-animation-duration-slow, 350ms) ease-out; } + .agent-list-wrapper.collapsing { + animation: collapse var(--ha-animation-duration-slow, 350ms) ease-out + forwards; + } @keyframes expand { from { grid-template-rows: 0fr; @@ -427,6 +490,16 @@ export class HaBackupOverviewProgress extends LitElement { opacity: 1; } } + @keyframes collapse { + from { + grid-template-rows: 1fr; + opacity: 1; + } + to { + grid-template-rows: 0fr; + opacity: 0; + } + } .agent-list { background: none; padding: 0; diff --git a/src/translations/en.json b/src/translations/en.json index b754596b11..ae2de1beaa 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -3275,7 +3275,8 @@ "finishing_file": "Finishing backup file", "folders": "Backing up folders", "home_assistant": "Backing up Home Assistant", - "upload_to_agents": "Uploading to locations" + "upload_to_agents": "Uploading to locations", + "cleaning_up": "Cleaning up" }, "restore_backup": { "addon_repositories": "[%key:ui::panel::config::backup::overview::progress::description::restore_backup::app_repositories%]", @@ -3302,7 +3303,8 @@ "home_assistant": "Home Assistant", "apps": "Apps", "media": "Media", - "upload": "Uploading backup" + "upload": "Uploading backup", + "cleaning_up": "Cleaning up" }, "agent_status": { "uploading": "Uploading...",