From 1109980d61543f69ff6c9409ba935c8ba2278412 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Lov=C3=A9n?= Date: Thu, 14 Jul 2022 12:00:47 +0000 Subject: [PATCH] Refactor interaction requirement. Add indicator --- custom_components/browser_mod/browser_mod.js | 69 ++++++++++++++++--- .../browser_mod/browser_mod_panel.js | 8 +-- js/config_panel/helpers.ts | 1 + js/config_panel/main.ts | 7 -- js/plugin/camera.ts | 27 +++++--- js/plugin/main.ts | 5 +- js/plugin/mediaPlayer.ts | 12 ++-- js/plugin/require-interact.ts | 47 +++++++++++++ js/plugin/screensaver.ts | 7 +- 9 files changed, 136 insertions(+), 47 deletions(-) create mode 100644 js/plugin/require-interact.ts diff --git a/custom_components/browser_mod/browser_mod.js b/custom_components/browser_mod/browser_mod.js index 29bd435..eb2c69c 100644 --- a/custom_components/browser_mod/browser_mod.js +++ b/custom_components/browser_mod/browser_mod.js @@ -812,9 +812,11 @@ const ScreenSaverMixin = (SuperClass) => { this._listeners = {}; this._brightness = 255; 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" }); const styleEl = document.createElement("style"); + panel.shadowRoot.append(styleEl); styleEl.innerHTML = ` :host { background: rgba(0,0,0, var(--darkness)); @@ -833,8 +835,6 @@ const ScreenSaverMixin = (SuperClass) => { 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_on", (ev) => this._screen_on(ev)); this.connectionPromise.then(() => this._screen_on()); @@ -882,11 +882,11 @@ const MediaPlayerMixin = (SuperClass) => { for (const ev of ["play", "pause", "ended", "volumechange"]) { this.player.addEventListener(ev, () => this._player_update()); } - window.addEventListener("pointerdown", () => { + this.firstInteraction.then(() => { this._player_enabled = true; if (!this.player.ended) this.player.play(); - }, { once: true }); + }); this.addEventListener("command-player-play", (ev) => { var _a; if ((_a = ev.detail) === null || _a === void 0 ? void 0 : _a.media_content_id) @@ -940,24 +940,33 @@ const CameraMixin = (SuperClass) => { constructor() { super(); this._framerate = 2; - window.addEventListener("pointerdown", () => { - this._setup_camera(); - }, { once: true }); + this._setup_camera(); } async _setup_camera() { if (this._video) return; await this.connectionPromise; + await this.firstInteraction; 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")); + div.shadowRoot.append(video); video.autoplay = true; video.playsInline = true; video.style.display = "none"; const canvas = (this._canvas = document.createElement("canvas")); + div.shadowRoot.append(canvas); canvas.style.display = "none"; - document.body.appendChild(video); - document.body.appendChild(canvas); if (!navigator.mediaDevices) return; 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 version = "2.0.0b0"; var description = ""; @@ -1041,7 +1088,7 @@ var pjson = { // FullyKioskMixin, // BrowserModMediaPlayerMixin, // ]) { -class BrowserMod extends CameraMixin(MediaPlayerMixin(ScreenSaverMixin(ConnectionMixin(EventTarget)))) { +class BrowserMod extends CameraMixin(MediaPlayerMixin(ScreenSaverMixin(RequireInteractMixin(ConnectionMixin(EventTarget))))) { constructor() { super(); this.entity_id = deviceID.replace("-", "_"); diff --git a/custom_components/browser_mod/browser_mod_panel.js b/custom_components/browser_mod/browser_mod_panel.js index 1a9e7df..3779410 100644 --- a/custom_components/browser_mod/browser_mod_panel.js +++ b/custom_components/browser_mod/browser_mod_panel.js @@ -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)); // Loads in ha-config-dashboard which is used to copy styling +// Also provides ha-settings-row const loadDevTools = async () => { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l; if (customElements.get("ha-config-dashboard")) @@ -202,19 +203,12 @@ loadDevTools().then(() => { > - - - `)}
- - Auto enable devices - - User sidebar { if (customElements.get("ha-config-dashboard")) return; const ppResolver = document.createElement("partial-panel-resolver"); diff --git a/js/config_panel/main.ts b/js/config_panel/main.ts index 6887d48..35a8671 100644 --- a/js/config_panel/main.ts +++ b/js/config_panel/main.ts @@ -134,9 +134,6 @@ loadDevTools().then(() => { > - - - ` )}
@@ -144,10 +141,6 @@ loadDevTools().then(() => {
- - Auto enable devices - - User sidebar { super(); this._framerate = 2; - window.addEventListener( - "pointerdown", - () => { - this._setup_camera(); - }, - { once: true } - ); + this._setup_camera(); } async _setup_camera() { if (this._video) return; await this.connectionPromise; + await this.firstInteraction; 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")); + div.shadowRoot.append(video); video.autoplay = true; video.playsInline = true; video.style.display = "none"; const canvas = (this._canvas = document.createElement("canvas")); + div.shadowRoot.append(canvas); canvas.style.display = "none"; - document.body.appendChild(video); - document.body.appendChild(canvas); - if (!navigator.mediaDevices) return; const stream = await navigator.mediaDevices.getUserMedia({ diff --git a/js/plugin/main.ts b/js/plugin/main.ts index 9a07175..30ac223 100644 --- a/js/plugin/main.ts +++ b/js/plugin/main.ts @@ -10,6 +10,7 @@ import { ConnectionMixin } from "./connection"; import { ScreenSaverMixin } from "./screensaver"; import { MediaPlayerMixin } from "./mediaPlayer"; import { CameraMixin } from "./camera"; +import { RequireInteractMixin } from "./require-interact"; import { FullyKioskMixin } from "./fullyKiosk"; import { BrowserModScreensaverMixin } from "./screensaver"; import { BrowserModPopupsMixin } from "./popups"; @@ -28,7 +29,9 @@ const ext = (baseClass, mixins) => // BrowserModMediaPlayerMixin, // ]) { export class BrowserMod extends CameraMixin( - MediaPlayerMixin(ScreenSaverMixin(ConnectionMixin(EventTarget))) + MediaPlayerMixin( + ScreenSaverMixin(RequireInteractMixin(ConnectionMixin(EventTarget))) + ) ) { constructor() { super(); diff --git a/js/plugin/mediaPlayer.ts b/js/plugin/mediaPlayer.ts index 6d1e190..b40419f 100644 --- a/js/plugin/mediaPlayer.ts +++ b/js/plugin/mediaPlayer.ts @@ -13,14 +13,10 @@ export const MediaPlayerMixin = (SuperClass) => { this.player.addEventListener(ev, () => this._player_update()); } - window.addEventListener( - "pointerdown", - () => { - this._player_enabled = true; - if (!this.player.ended) this.player.play(); - }, - { once: true } - ); + this.firstInteraction.then(() => { + this._player_enabled = true; + if (!this.player.ended) this.player.play(); + }); this.addEventListener("command-player-play", (ev) => { if (ev.detail?.media_content_id) diff --git a/js/plugin/require-interact.ts b/js/plugin/require-interact.ts new file mode 100644 index 0000000..f81fab0 --- /dev/null +++ b/js/plugin/require-interact.ts @@ -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(); + } + }; +}; diff --git a/js/plugin/screensaver.ts b/js/plugin/screensaver.ts index be5e5d7..7685b91 100644 --- a/js/plugin/screensaver.ts +++ b/js/plugin/screensaver.ts @@ -8,9 +8,12 @@ export const ScreenSaverMixin = (SuperClass) => { super(); 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" }); + const styleEl = document.createElement("style"); + panel.shadowRoot.append(styleEl); styleEl.innerHTML = ` :host { background: rgba(0,0,0, var(--darkness)); @@ -29,8 +32,6 @@ export const ScreenSaverMixin = (SuperClass) => { 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_on", (ev) => this._screen_on(ev));