Refactor interaction requirement. Add indicator

This commit is contained in:
Thomas Lovén 2022-07-14 12:00:47 +00:00
parent a3cd0c9fd6
commit 1109980d61
9 changed files with 136 additions and 47 deletions

View File

@ -812,9 +812,11 @@ const ScreenSaverMixin = (SuperClass) => {
this._listeners = {}; this._listeners = {};
this._brightness = 255; this._brightness = 255;
const panel = (this._panel = document.createElement("div")); const panel = (this._panel = document.createElement("div"));
panel.setAttribute("browser-mod", ""); document.body.append(panel);
panel.classList.add("browser-mod-blackout");
panel.attachShadow({ mode: "open" }); panel.attachShadow({ mode: "open" });
const styleEl = document.createElement("style"); const styleEl = document.createElement("style");
panel.shadowRoot.append(styleEl);
styleEl.innerHTML = ` styleEl.innerHTML = `
:host { :host {
background: rgba(0,0,0, var(--darkness)); background: rgba(0,0,0, var(--darkness));
@ -833,8 +835,6 @@ const ScreenSaverMixin = (SuperClass) => {
background: rgba(0,0,0,1); background: rgba(0,0,0,1);
} }
`; `;
panel.shadowRoot.appendChild(styleEl);
document.body.appendChild(panel);
this.addEventListener("command-screen_off", () => this._screen_off()); this.addEventListener("command-screen_off", () => this._screen_off());
this.addEventListener("command-screen_on", (ev) => this._screen_on(ev)); this.addEventListener("command-screen_on", (ev) => this._screen_on(ev));
this.connectionPromise.then(() => this._screen_on()); this.connectionPromise.then(() => this._screen_on());
@ -882,11 +882,11 @@ const MediaPlayerMixin = (SuperClass) => {
for (const ev of ["play", "pause", "ended", "volumechange"]) { for (const ev of ["play", "pause", "ended", "volumechange"]) {
this.player.addEventListener(ev, () => this._player_update()); this.player.addEventListener(ev, () => this._player_update());
} }
window.addEventListener("pointerdown", () => { this.firstInteraction.then(() => {
this._player_enabled = true; this._player_enabled = true;
if (!this.player.ended) if (!this.player.ended)
this.player.play(); this.player.play();
}, { once: true }); });
this.addEventListener("command-player-play", (ev) => { this.addEventListener("command-player-play", (ev) => {
var _a; var _a;
if ((_a = ev.detail) === null || _a === void 0 ? void 0 : _a.media_content_id) if ((_a = ev.detail) === null || _a === void 0 ? void 0 : _a.media_content_id)
@ -940,24 +940,33 @@ const CameraMixin = (SuperClass) => {
constructor() { constructor() {
super(); super();
this._framerate = 2; this._framerate = 2;
window.addEventListener("pointerdown", () => {
this._setup_camera(); this._setup_camera();
}, { once: true });
} }
async _setup_camera() { async _setup_camera() {
if (this._video) if (this._video)
return; return;
await this.connectionPromise; await this.connectionPromise;
await this.firstInteraction;
if (!this.cameraEnabled) if (!this.cameraEnabled)
return; return;
const div = document.createElement("div");
document.body.append(div);
div.classList.add("browser-mod-camera");
div.attachShadow({ mode: "open" });
const styleEl = document.createElement("style");
div.shadowRoot.append(styleEl);
styleEl.innerHTML = `
:host {
display: none;
}`;
const video = (this._video = document.createElement("video")); const video = (this._video = document.createElement("video"));
div.shadowRoot.append(video);
video.autoplay = true; video.autoplay = true;
video.playsInline = true; video.playsInline = true;
video.style.display = "none"; video.style.display = "none";
const canvas = (this._canvas = document.createElement("canvas")); const canvas = (this._canvas = document.createElement("canvas"));
div.shadowRoot.append(canvas);
canvas.style.display = "none"; canvas.style.display = "none";
document.body.appendChild(video);
document.body.appendChild(canvas);
if (!navigator.mediaDevices) if (!navigator.mediaDevices)
return; return;
const stream = await navigator.mediaDevices.getUserMedia({ const stream = await navigator.mediaDevices.getUserMedia({
@ -994,6 +1003,44 @@ const CameraMixin = (SuperClass) => {
}; };
}; };
const RequireInteractMixin = (SuperClass) => {
return class RequireInteractMixinClass extends SuperClass {
constructor() {
super();
this.firstInteraction = new Promise((resolve) => {
this._interactionResolve = resolve;
});
window.addEventListener("pointerdown", this._interactionResolve);
this.show_indicator();
}
async show_indicator() {
await this.connectionPromise;
if (!this.registered)
return;
const interactSymbol = document.createElement("div");
document.body.append(interactSymbol);
interactSymbol.classList.add("browser-mod-require-interaction");
interactSymbol.attachShadow({ mode: "open" });
const styleEl = document.createElement("style");
interactSymbol.shadowRoot.append(styleEl);
styleEl.innerHTML = `
:host {
position: fixed;
right: 8px;
bottom: 8px;
color: var(--warning-color, red);
opacity: 0.5;
--mdc-icon-size: 48px;
}`;
const icon = document.createElement("ha-icon");
interactSymbol.shadowRoot.append(icon);
icon.icon = "mdi:gesture-tap";
await this.firstInteraction;
interactSymbol.remove();
}
};
};
var name = "browser_mod"; var name = "browser_mod";
var version = "2.0.0b0"; var version = "2.0.0b0";
var description = ""; var description = "";
@ -1041,7 +1088,7 @@ var pjson = {
// FullyKioskMixin, // FullyKioskMixin,
// BrowserModMediaPlayerMixin, // BrowserModMediaPlayerMixin,
// ]) { // ]) {
class BrowserMod extends CameraMixin(MediaPlayerMixin(ScreenSaverMixin(ConnectionMixin(EventTarget)))) { class BrowserMod extends CameraMixin(MediaPlayerMixin(ScreenSaverMixin(RequireInteractMixin(ConnectionMixin(EventTarget))))) {
constructor() { constructor() {
super(); super();
this.entity_id = deviceID.replace("-", "_"); this.entity_id = deviceID.replace("-", "_");

View File

@ -60,6 +60,7 @@ const i=(i,e)=>"method"===e.kind&&e.descriptor&&!("value"in e.descriptor)?{...e,
*/var n;null!=(null===(n=window.HTMLSlotElement)||void 0===n?void 0:n.prototype.assignedElements)?(o,n)=>o.assignedElements(n):(o,n)=>o.assignedNodes(n).filter((o=>o.nodeType===Node.ELEMENT_NODE)); */var n;null!=(null===(n=window.HTMLSlotElement)||void 0===n?void 0:n.prototype.assignedElements)?(o,n)=>o.assignedElements(n):(o,n)=>o.assignedNodes(n).filter((o=>o.nodeType===Node.ELEMENT_NODE));
// Loads in ha-config-dashboard which is used to copy styling // Loads in ha-config-dashboard which is used to copy styling
// Also provides ha-settings-row
const loadDevTools = async () => { const loadDevTools = async () => {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l; var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
if (customElements.get("ha-config-dashboard")) if (customElements.get("ha-config-dashboard"))
@ -202,19 +203,12 @@ loadDevTools().then(() => {
> >
<ha-icon .icon=${"mdi:delete"}></ha-icon> <ha-icon .icon=${"mdi:delete"}></ha-icon>
</ha-icon-button> </ha-icon-button>
<ha-icon-button>
<ha-icon .icon=${"mdi:wrench"}></ha-icon>
</ha-icon-button>
</ha-settings-row>`)} </ha-settings-row>`)}
</div> </div>
</ha-card> </ha-card>
<ha-card outlined header="Tweaks"> <ha-card outlined header="Tweaks">
<div class="card-content"> <div class="card-content">
<ha-settings-row>
<span slot="heading">Auto enable devices</span>
<ha-switch></ha-switch>
</ha-settings-row>
<ha-settings-row> <ha-settings-row>
<span slot="heading">User sidebar</span> <span slot="heading">User sidebar</span>
<span slot="description" <span slot="description"

View File

@ -1,4 +1,5 @@
// Loads in ha-config-dashboard which is used to copy styling // Loads in ha-config-dashboard which is used to copy styling
// Also provides ha-settings-row
export const loadDevTools = async () => { export const loadDevTools = async () => {
if (customElements.get("ha-config-dashboard")) return; if (customElements.get("ha-config-dashboard")) return;
const ppResolver = document.createElement("partial-panel-resolver"); const ppResolver = document.createElement("partial-panel-resolver");

View File

@ -134,9 +134,6 @@ loadDevTools().then(() => {
> >
<ha-icon .icon=${"mdi:delete"}></ha-icon> <ha-icon .icon=${"mdi:delete"}></ha-icon>
</ha-icon-button> </ha-icon-button>
<ha-icon-button>
<ha-icon .icon=${"mdi:wrench"}></ha-icon>
</ha-icon-button>
</ha-settings-row>` </ha-settings-row>`
)} )}
</div> </div>
@ -144,10 +141,6 @@ loadDevTools().then(() => {
<ha-card outlined header="Tweaks"> <ha-card outlined header="Tweaks">
<div class="card-content"> <div class="card-content">
<ha-settings-row>
<span slot="heading">Auto enable devices</span>
<ha-switch></ha-switch>
</ha-settings-row>
<ha-settings-row> <ha-settings-row>
<span slot="heading">User sidebar</span> <span slot="heading">User sidebar</span>
<span slot="description" <span slot="description"

View File

@ -8,30 +8,37 @@ export const CameraMixin = (SuperClass) => {
super(); super();
this._framerate = 2; this._framerate = 2;
window.addEventListener(
"pointerdown",
() => {
this._setup_camera(); this._setup_camera();
},
{ once: true }
);
} }
async _setup_camera() { async _setup_camera() {
if (this._video) return; if (this._video) return;
await this.connectionPromise; await this.connectionPromise;
await this.firstInteraction;
if (!this.cameraEnabled) return; if (!this.cameraEnabled) return;
const div = document.createElement("div");
document.body.append(div);
div.classList.add("browser-mod-camera");
div.attachShadow({ mode: "open" });
const styleEl = document.createElement("style");
div.shadowRoot.append(styleEl);
styleEl.innerHTML = `
:host {
display: none;
}`;
const video = (this._video = document.createElement("video")); const video = (this._video = document.createElement("video"));
div.shadowRoot.append(video);
video.autoplay = true; video.autoplay = true;
video.playsInline = true; video.playsInline = true;
video.style.display = "none"; video.style.display = "none";
const canvas = (this._canvas = document.createElement("canvas")); const canvas = (this._canvas = document.createElement("canvas"));
div.shadowRoot.append(canvas);
canvas.style.display = "none"; canvas.style.display = "none";
document.body.appendChild(video);
document.body.appendChild(canvas);
if (!navigator.mediaDevices) return; if (!navigator.mediaDevices) return;
const stream = await navigator.mediaDevices.getUserMedia({ const stream = await navigator.mediaDevices.getUserMedia({

View File

@ -10,6 +10,7 @@ import { ConnectionMixin } from "./connection";
import { ScreenSaverMixin } from "./screensaver"; import { ScreenSaverMixin } from "./screensaver";
import { MediaPlayerMixin } from "./mediaPlayer"; import { MediaPlayerMixin } from "./mediaPlayer";
import { CameraMixin } from "./camera"; import { CameraMixin } from "./camera";
import { RequireInteractMixin } from "./require-interact";
import { FullyKioskMixin } from "./fullyKiosk"; import { FullyKioskMixin } from "./fullyKiosk";
import { BrowserModScreensaverMixin } from "./screensaver"; import { BrowserModScreensaverMixin } from "./screensaver";
import { BrowserModPopupsMixin } from "./popups"; import { BrowserModPopupsMixin } from "./popups";
@ -28,7 +29,9 @@ const ext = (baseClass, mixins) =>
// BrowserModMediaPlayerMixin, // BrowserModMediaPlayerMixin,
// ]) { // ]) {
export class BrowserMod extends CameraMixin( export class BrowserMod extends CameraMixin(
MediaPlayerMixin(ScreenSaverMixin(ConnectionMixin(EventTarget))) MediaPlayerMixin(
ScreenSaverMixin(RequireInteractMixin(ConnectionMixin(EventTarget)))
)
) { ) {
constructor() { constructor() {
super(); super();

View File

@ -13,14 +13,10 @@ export const MediaPlayerMixin = (SuperClass) => {
this.player.addEventListener(ev, () => this._player_update()); this.player.addEventListener(ev, () => this._player_update());
} }
window.addEventListener( this.firstInteraction.then(() => {
"pointerdown",
() => {
this._player_enabled = true; this._player_enabled = true;
if (!this.player.ended) this.player.play(); if (!this.player.ended) this.player.play();
}, });
{ once: true }
);
this.addEventListener("command-player-play", (ev) => { this.addEventListener("command-player-play", (ev) => {
if (ev.detail?.media_content_id) if (ev.detail?.media_content_id)

View File

@ -0,0 +1,47 @@
export const RequireInteractMixin = (SuperClass) => {
return class RequireInteractMixinClass extends SuperClass {
private _interactionResolve;
public firstInteraction = new Promise((resolve) => {
this._interactionResolve = resolve;
});
constructor() {
super();
window.addEventListener("pointerdown", this._interactionResolve);
this.show_indicator();
}
async show_indicator() {
await this.connectionPromise;
if (!this.registered) return;
const interactSymbol = document.createElement("div");
document.body.append(interactSymbol);
interactSymbol.classList.add("browser-mod-require-interaction");
interactSymbol.attachShadow({ mode: "open" });
const styleEl = document.createElement("style");
interactSymbol.shadowRoot.append(styleEl);
styleEl.innerHTML = `
:host {
position: fixed;
right: 8px;
bottom: 8px;
color: var(--warning-color, red);
opacity: 0.5;
--mdc-icon-size: 48px;
}`;
const icon = document.createElement("ha-icon");
interactSymbol.shadowRoot.append(icon);
(icon as any).icon = "mdi:gesture-tap";
await this.firstInteraction;
interactSymbol.remove();
}
};
};

View File

@ -8,9 +8,12 @@ export const ScreenSaverMixin = (SuperClass) => {
super(); super();
const panel = (this._panel = document.createElement("div")); const panel = (this._panel = document.createElement("div"));
panel.setAttribute("browser-mod", ""); document.body.append(panel);
panel.classList.add("browser-mod-blackout");
panel.attachShadow({ mode: "open" }); panel.attachShadow({ mode: "open" });
const styleEl = document.createElement("style"); const styleEl = document.createElement("style");
panel.shadowRoot.append(styleEl);
styleEl.innerHTML = ` styleEl.innerHTML = `
:host { :host {
background: rgba(0,0,0, var(--darkness)); background: rgba(0,0,0, var(--darkness));
@ -29,8 +32,6 @@ export const ScreenSaverMixin = (SuperClass) => {
background: rgba(0,0,0,1); background: rgba(0,0,0,1);
} }
`; `;
panel.shadowRoot.appendChild(styleEl);
document.body.appendChild(panel);
this.addEventListener("command-screen_off", () => this._screen_off()); this.addEventListener("command-screen_off", () => this._screen_off());
this.addEventListener("command-screen_on", (ev) => this._screen_on(ev)); this.addEventListener("command-screen_on", (ev) => this._screen_on(ev));