Skip to content

Commit

Permalink
Add last-changed date to more-info (#16509)
Browse files Browse the repository at this point in the history
  • Loading branch information
piitaya committed May 12, 2023
1 parent 557fe33 commit 75f080e
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 20 deletions.
23 changes: 23 additions & 0 deletions src/common/datetime/absolute_time.ts
@@ -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);
};
23 changes: 23 additions & 0 deletions src/common/datetime/format_date_time.ts
Expand Up @@ -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,
Expand Down
76 changes: 76 additions & 0 deletions src/components/ha-absolute-time.ts
@@ -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;
}
}
47 changes: 36 additions & 11 deletions src/dialogs/more-info/components/ha-more-info-state-header.ts
@@ -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";
Expand All @@ -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 &&
Expand All @@ -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>
`;
}

Expand All @@ -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);
}
`;
}
Expand Down
12 changes: 3 additions & 9 deletions src/dialogs/more-info/ha-more-info-dialog.ts
Expand Up @@ -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,
Expand Down Expand Up @@ -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>
Expand All @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions src/translations/en.json
Expand Up @@ -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…",
Expand Down

0 comments on commit 75f080e

Please sign in to comment.