Fixes for Firefox.

This commit is contained in:
Dermot Duffy
2022-03-05 08:58:01 -08:00
parent b947df3e14
commit fed2cba89a
2 changed files with 67 additions and 12 deletions
+6 -5
View File
@@ -32,6 +32,7 @@ export class CachedValueController<T> implements ReactiveController {
*/ */
public updateValue(): void { public updateValue(): void {
this._value = this._callback(); this._value = this._callback();
this._startTimer();
} }
/** /**
@@ -39,12 +40,13 @@ export class CachedValueController<T> implements ReactiveController {
*/ */
public clearValue(): void { public clearValue(): void {
this._value = undefined; this._value = undefined;
this._stopTimer();
} }
/** /**
* Disable the timer. * Disable the timer.
*/ */
public stopTimer(): void { protected _stopTimer(): void {
clearInterval(this._timerID); clearInterval(this._timerID);
this._timerID = undefined; this._timerID = undefined;
} }
@@ -52,8 +54,8 @@ export class CachedValueController<T> implements ReactiveController {
/** /**
* Enable the timer. Repeated calls will have no effect. * Enable the timer. Repeated calls will have no effect.
*/ */
public startTimer(): void { protected _startTimer(): void {
this.stopTimer(); this._stopTimer();
if (this._timerSeconds > 0) { if (this._timerSeconds > 0) {
this._timerID = window.setInterval(() => { this._timerID = window.setInterval(() => {
this.updateValue(); this.updateValue();
@@ -67,7 +69,6 @@ export class CachedValueController<T> implements ReactiveController {
*/ */
hostConnected(): void { hostConnected(): void {
this.updateValue(); this.updateValue();
this.startTimer();
this._host.requestUpdate(); this._host.requestUpdate();
} }
@@ -75,6 +76,6 @@ export class CachedValueController<T> implements ReactiveController {
* Host has disconnected from the cache. * Host has disconnected from the cache.
*/ */
hostDisconnected(): void { hostDisconnected(): void {
this.stopTimer(); this.clearValue();
} }
} }
+61 -7
View File
@@ -38,7 +38,7 @@ export class FrigateCardImage extends LitElement {
protected _image?: HTMLImageElement; protected _image?: HTMLImageElement;
protected _cachedValueController?: CachedValueController<string>; protected _cachedValueController?: CachedValueController<string>;
protected _boundVisibilityHandler = this._visibilityHandler.bind(this);
/** /**
* Set the image configuration. * Set the image configuration.
*/ */
@@ -52,7 +52,6 @@ export class FrigateCardImage extends LitElement {
this._imageConfig.refresh_seconds, this._imageConfig.refresh_seconds,
this._getImageSource.bind(this), this._getImageSource.bind(this),
); );
this._cachedValueController.startTimer();
} }
/** /**
@@ -69,7 +68,7 @@ export class FrigateCardImage extends LitElement {
* @returns `true` if the element should be updated. * @returns `true` if the element should be updated.
*/ */
protected shouldUpdate(changedProps: PropertyValues): boolean { protected shouldUpdate(changedProps: PropertyValues): boolean {
if (!this.hass) { if (!this.hass || document.visibilityState !== 'visible') {
return false; return false;
} }
@@ -95,10 +94,10 @@ export class FrigateCardImage extends LitElement {
this._imageConfig?.mode === 'camera' && this._imageConfig?.mode === 'camera' &&
cameraEntity) { cameraEntity) {
if (shouldUpdateBasedOnHass(this.hass, changedProps.get('hass'), [cameraEntity])) { if (shouldUpdateBasedOnHass(this.hass, changedProps.get('hass'), [cameraEntity])) {
// Image needs to update if the image view is in camera mode and the camera // If the state of the camera entity has changed, remove the cached
// entity changes, as this could be a security token change. // value (will be re-calculated in willUpdate). This is important to
this._cachedValueController?.updateValue(); // ensure a changed access token is immediately used.
this._cachedValueController?.startTimer(); this._cachedValueController?.clearValue();
return true; return true;
} }
return false; return false;
@@ -106,6 +105,61 @@ export class FrigateCardImage extends LitElement {
return true; return true;
} }
/**
* Ensure there is a cached value before an update.
* @param _changedProps The changed properties
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
protected willUpdate(_changedProps: PropertyValues): void {
if (!this._cachedValueController?.value) {
this._cachedValueController?.updateValue();
}
}
/**
* Component connected callback.
*/
connectedCallback(): void {
super.connectedCallback();
document.addEventListener('visibilitychange', this._boundVisibilityHandler);
}
/**
* Component disconnected callback.
*/
disconnectedCallback(): void {
document.removeEventListener('visibilitychange', this._boundVisibilityHandler);
super.disconnectedCallback();
}
/**
* Handle document visibility changes.
*/
protected _visibilityHandler(): void {
if (!this._image) {
return;
}
if (document.visibilityState === 'hidden') {
// Set the image to default when the document is hidden. This is to avoid
// some browsers (e.g. Firefox) eagerly re-loading the old image when the
// document regains visibility -- for some images (e.g. camera mode) the
// image may be using an old-expired token and re-use prior to
// re-generation of a new URL would generate an unauthorized request
// (401), see:
// https://github.com/dermotduffy/frigate-hass-card/issues/398
this._cachedValueController?.clearValue();
this._image.src = defaultImage;
} else {
// If the document is freshly re-visible, immediately re-render it to
// restore the image src. If the HASS object is old (i.e. browser tab was
// inactive for some time) this update request may be (correctly)
// rejected.
this.requestUpdate();
}
}
/** /**
* Build a working absolute image URL that the browser will not cache. * Build a working absolute image URL that the browser will not cache.
* @param url An input URL (may be relative to document origin) * @param url An input URL (may be relative to document origin)