mirror of
https://github.com/Matysh/houseplan-card
synced 2026-07-31 08:28:31 +00:00
fix+test v1.13.2: audit round 3
- 12-test buildDevices suite on a fake hass (curation, markers, groups, dedup, icon rules, lock override, device_class fallback, primary/LQI/temp) - t() substitutes all placeholder occurrences (pure subst() + regression test) - _saveConfigNow resyncs config on rev conflict before rethrowing - pointercancel clears the long-press timer (phantom info card on touch) - repairs.py: plan-file check re-runs after every config save - test build compiles devices.ts/types.ts; fix-test-build.mjs adds .js to ESM imports
This commit is contained in:
+16
-6
@@ -23,7 +23,7 @@ import './editor';
|
||||
import { cardStyles } from './styles';
|
||||
import { langOf, t, type I18nKey } from './i18n';
|
||||
|
||||
const CARD_VERSION = '1.13.1';
|
||||
const CARD_VERSION = '1.13.2';
|
||||
const LS_KEY = 'houseplan_card_layout_v1';
|
||||
const LS_CFG = 'houseplan_card_cfg_v1'; // cache of the server config+layout for instant rendering
|
||||
const LS_ZOOM = 'houseplan_card_zoom_v1';
|
||||
@@ -1554,12 +1554,21 @@ class HouseplanCard extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
/** Immediate config save with a revision bump (no debounce). */
|
||||
/** Immediate config save with a revision bump (no debounce).
|
||||
|
||||
On a rev conflict the local copy is refreshed before rethrowing, so the
|
||||
user's retry starts from the fresh config instead of hitting the same
|
||||
conflict again. */
|
||||
private async _saveConfigNow(): Promise<void> {
|
||||
const r = await this.hass.callWS({
|
||||
type: 'houseplan/config/set', config: this._serverCfg, expected_rev: this._cfgRev,
|
||||
});
|
||||
this._cfgRev = r?.rev ?? this._cfgRev + 1;
|
||||
try {
|
||||
const r = await this.hass.callWS({
|
||||
type: 'houseplan/config/set', config: this._serverCfg, expected_rev: this._cfgRev,
|
||||
});
|
||||
this._cfgRev = r?.rev ?? this._cfgRev + 1;
|
||||
} catch (e: any) {
|
||||
if (e?.code === 'conflict') await this._reloadConfigOnly();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1917,6 +1926,7 @@ class HouseplanCard extends LitElement {
|
||||
@pointerdown=${(e: PointerEvent) => this._pointerDown(e, d)}
|
||||
@pointermove=${(e: PointerEvent) => this._pointerMove(e, d)}
|
||||
@pointerup=${(e: PointerEvent) => this._pointerUp(e, d)}
|
||||
@pointercancel=${(e: PointerEvent) => this._pointerUp(e, d)}
|
||||
>
|
||||
<ha-icon icon="${d.icon}"></ha-icon>
|
||||
${temp != null ? html`<span class="tval">${temp}°</span>` : nothing}
|
||||
|
||||
+2
-3
@@ -5,6 +5,7 @@
|
||||
* HA user profile (hass.locale.language); anything that is not a known
|
||||
* language falls back to English.
|
||||
*/
|
||||
import { subst } from './logic';
|
||||
import en from './i18n/en.json';
|
||||
import ru from './i18n/ru.json';
|
||||
|
||||
@@ -22,9 +23,7 @@ export function langOf(hass: any, configLang?: string | null): Lang {
|
||||
|
||||
/** Translate a key with optional {placeholder} substitution. */
|
||||
export function t(lang: Lang, key: Key, vars?: Record<string, string | number>): string {
|
||||
let s = DICTS[lang][key] ?? en[key] ?? key;
|
||||
if (vars) for (const [k, v] of Object.entries(vars)) s = s.replace('{' + k + '}', String(v));
|
||||
return s;
|
||||
return subst(DICTS[lang][key] ?? en[key] ?? key, vars);
|
||||
}
|
||||
|
||||
export type { Key as I18nKey };
|
||||
|
||||
@@ -172,3 +172,11 @@ export function floorsOf(hass: any): FloorInfo[] {
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
/** Substitute every occurrence of {name} placeholders in a template string. */
|
||||
export function subst(s: string, vars?: Record<string, string | number>): string {
|
||||
if (!vars) return s;
|
||||
let out = s;
|
||||
for (const [k, v] of Object.entries(vars)) out = out.split('{' + k + '}').join(String(v));
|
||||
return out;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user