mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-14 04:46:34 +00:00
Add last-changed date to more-info (#16509)
This commit is contained in:
parent
557fe33807
commit
75f080ee85
23
src/common/datetime/absolute_time.ts
Normal file
23
src/common/datetime/absolute_time.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { isSameDay, isSameYear } from "date-fns";
|
||||
import { FrontendLocaleData } from "../../data/translation";
|
||||
import {
|
||||
formatShortDateTime,
|
||||
formatShortDateTimeWithYear,
|
||||
} from "./format_date_time";
|
||||
import { formatTime } from "./format_time";
|
||||
|
||||
export const absoluteTime = (
|
||||
from: Date,
|
||||
locale: FrontendLocaleData,
|
||||
to?: Date
|
||||
): string => {
|
||||
const _to = to ?? new Date();
|
||||
|
||||
if (isSameDay(from, _to)) {
|
||||
return formatTime(from, locale);
|
||||
}
|
||||
if (isSameYear(from, _to)) {
|
||||
return formatShortDateTime(from, locale);
|
||||
}
|
||||
return formatShortDateTimeWithYear(from, locale);
|
||||
};
|
@ -24,6 +24,29 @@ const formatDateTimeMem = memoizeOne(
|
||||
)
|
||||
);
|
||||
|
||||
// Aug 9, 2021, 8:23 AM
|
||||
export const formatShortDateTimeWithYear = (
|
||||
dateObj: Date,
|
||||
locale: FrontendLocaleData
|
||||
) => formatShortDateTimeWithYearMem(locale).format(dateObj);
|
||||
|
||||
const formatShortDateTimeWithYearMem = memoizeOne(
|
||||
(locale: FrontendLocaleData) =>
|
||||
new Intl.DateTimeFormat(
|
||||
locale.language === "en" && !useAmPm(locale)
|
||||
? "en-u-hc-h23"
|
||||
: locale.language,
|
||||
{
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
hour: useAmPm(locale) ? "numeric" : "2-digit",
|
||||
minute: "2-digit",
|
||||
hour12: useAmPm(locale),
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Aug 9, 8:23 AM
|
||||
export const formatShortDateTime = (
|
||||
dateObj: Date,
|
||||
|
76
src/components/ha-absolute-time.ts
Normal file
76
src/components/ha-absolute-time.ts
Normal file
@ -0,0 +1,76 @@
|
||||
import { addDays, differenceInMilliseconds, startOfDay } from "date-fns";
|
||||
import { PropertyValues, ReactiveElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { absoluteTime } from "../common/datetime/absolute_time";
|
||||
import type { HomeAssistant } from "../types";
|
||||
|
||||
const SAFE_MARGIN = 5 * 1000;
|
||||
|
||||
@customElement("ha-absolute-time")
|
||||
class HaAbsoluteTime extends ReactiveElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public datetime?: string | Date;
|
||||
|
||||
private _timeout?: number;
|
||||
|
||||
public disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
this._clearTimeout();
|
||||
}
|
||||
|
||||
public connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
if (this.datetime) {
|
||||
this._updateNextDay();
|
||||
}
|
||||
}
|
||||
|
||||
protected createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
|
||||
protected firstUpdated(changedProps: PropertyValues) {
|
||||
super.firstUpdated(changedProps);
|
||||
this._updateAbsolute();
|
||||
}
|
||||
|
||||
protected update(changedProps: PropertyValues) {
|
||||
super.update(changedProps);
|
||||
this._updateAbsolute();
|
||||
}
|
||||
|
||||
private _clearTimeout(): void {
|
||||
if (this._timeout) {
|
||||
window.clearTimeout(this._timeout);
|
||||
this._timeout = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private _updateNextDay(): void {
|
||||
this._clearTimeout();
|
||||
|
||||
const now = new Date();
|
||||
const nextDay = addDays(startOfDay(now), 1);
|
||||
const ms = differenceInMilliseconds(nextDay, now) + SAFE_MARGIN;
|
||||
|
||||
this._timeout = window.setTimeout(() => {
|
||||
this._updateNextDay();
|
||||
this._updateAbsolute();
|
||||
}, ms);
|
||||
}
|
||||
|
||||
private _updateAbsolute(): void {
|
||||
if (!this.datetime) {
|
||||
this.innerHTML = this.hass.localize("ui.components.absolute_time.never");
|
||||
} else {
|
||||
this.innerHTML = absoluteTime(new Date(this.datetime), this.hass.locale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-absolute-time": HaAbsoluteTime;
|
||||
}
|
||||
}
|
@ -1,7 +1,9 @@
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { html, LitElement, TemplateResult, css, CSSResultGroup } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
|
||||
import "../../../components/ha-absolute-time";
|
||||
import "../../../components/ha-relative-time";
|
||||
import { isUnavailableState } from "../../../data/entity";
|
||||
import { LightEntity } from "../../../data/light";
|
||||
import { SENSOR_DEVICE_CLASS_TIMESTAMP } from "../../../data/sensor";
|
||||
@ -16,6 +18,8 @@ export class HaMoreInfoStateHeader extends LitElement {
|
||||
|
||||
@property({ attribute: false }) public stateOverride?: string;
|
||||
|
||||
@state() private _absoluteTime = false;
|
||||
|
||||
private _computeStateDisplay(stateObj: HassEntity): TemplateResult | string {
|
||||
if (
|
||||
stateObj.attributes.device_class === SENSOR_DEVICE_CLASS_TIMESTAMP &&
|
||||
@ -41,15 +45,32 @@ export class HaMoreInfoStateHeader extends LitElement {
|
||||
return stateDisplay;
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const name = this.stateObj.attributes.friendly_name;
|
||||
private _toggleAbsolute() {
|
||||
this._absoluteTime = !this._absoluteTime;
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const stateDisplay =
|
||||
this.stateOverride ?? this._computeStateDisplay(this.stateObj);
|
||||
|
||||
return html`
|
||||
<p class="name">${name}</p>
|
||||
<p class="state">${stateDisplay}</p>
|
||||
<p class="last-changed" @click=${this._toggleAbsolute}>
|
||||
${this._absoluteTime
|
||||
? html`
|
||||
<ha-absolute-time
|
||||
.hass=${this.hass}
|
||||
.datetime=${this.stateObj.last_changed}
|
||||
></ha-absolute-time>
|
||||
`
|
||||
: html`
|
||||
<ha-relative-time
|
||||
.hass=${this.hass}
|
||||
.datetime=${this.stateObj.last_changed}
|
||||
capitalize
|
||||
></ha-relative-time>
|
||||
`}
|
||||
</p>
|
||||
`;
|
||||
}
|
||||
|
||||
@ -59,20 +80,24 @@ export class HaMoreInfoStateHeader extends LitElement {
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
}
|
||||
.name {
|
||||
.state {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 28px;
|
||||
line-height: 36px;
|
||||
margin-bottom: 4px;
|
||||
font-size: 36px;
|
||||
line-height: 44px;
|
||||
}
|
||||
.state {
|
||||
.last-changed {
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
letter-spacing: 0.1px;
|
||||
margin-bottom: 24px;
|
||||
padding: 4px 0;
|
||||
margin-bottom: 20px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
@ -36,7 +36,6 @@ import { HomeAssistant } from "../../types";
|
||||
import {
|
||||
computeShowHistoryComponent,
|
||||
computeShowLogBookComponent,
|
||||
computeShowNewMoreInfo,
|
||||
DOMAINS_WITH_MORE_INFO,
|
||||
EDITABLE_DOMAINS_WITH_ID,
|
||||
EDITABLE_DOMAINS_WITH_UNIQUE_ID,
|
||||
@ -240,7 +239,6 @@ export class MoreInfoDialog extends LitElement {
|
||||
const title = this._childView?.viewTitle ?? name;
|
||||
|
||||
const isInfoView = this._currView === "info" && !this._childView;
|
||||
const isNewMoreInfo = stateObj && computeShowNewMoreInfo(stateObj);
|
||||
|
||||
return html`
|
||||
<ha-dialog open @closed=${this.closeDialog} .heading=${title} hideActions>
|
||||
@ -265,13 +263,9 @@ export class MoreInfoDialog extends LitElement {
|
||||
)}
|
||||
></ha-icon-button-prev>
|
||||
`}
|
||||
${!isInfoView || !isNewMoreInfo
|
||||
? html`
|
||||
<span slot="title" .title=${title} @click=${this._enlarge}>
|
||||
${title}
|
||||
</span>
|
||||
`
|
||||
: nothing}
|
||||
<span slot="title" .title=${title} @click=${this._enlarge}>
|
||||
${title}
|
||||
</span>
|
||||
${isInfoView
|
||||
? html`
|
||||
${this.shouldShowHistory(domain)
|
||||
|
@ -509,6 +509,9 @@
|
||||
"relative_time": {
|
||||
"never": "Never"
|
||||
},
|
||||
"absolute_time": {
|
||||
"never": "[%key:ui::components::relative_time::never%]"
|
||||
},
|
||||
"history_charts": {
|
||||
"history_disabled": "History integration disabled",
|
||||
"loading_history": "Loading state history…",
|
||||
|
Loading…
x
Reference in New Issue
Block a user