Skip to content

Commit 440d8a1

Browse files
committed
Error and Overlay
1 parent f135967 commit 440d8a1

File tree

6 files changed

+348
-14
lines changed

6 files changed

+348
-14
lines changed
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/* -- VIDEO PLAYER OVERLAY */
2+
:host {
3+
position: absolute;
4+
top: 0;
5+
left: 0;
6+
bottom: 0;
7+
right: 0;
8+
z-index: 10000;
9+
--title-size: 16px;
10+
--title-line-height: 20px;
11+
--title-gap: 16px;
12+
--button-gap: 6px;
13+
--logo-size: 32px;
14+
--logo-gap: 8px;
15+
--gutter: 16px;
16+
}
17+
/* iphone 15 */
18+
@media screen and (min-width: 390px) {
19+
:host {
20+
--title-size: 20px;
21+
--title-line-height: 25px;
22+
--button-gap: 16px;
23+
--logo-size: 40px;
24+
--logo-gap: 12px;
25+
--title-gap: 16px;
26+
}
27+
}
28+
/* iphone 15 Pro Max */
29+
@media screen and (min-width: 430px) {
30+
:host {
31+
--title-size: 22px;
32+
--title-gap: 24px;
33+
--button-gap: 20px;
34+
--logo-gap: 16px;
35+
}
36+
}
37+
/* small landscape */
38+
@media screen and (min-width: 568px) {
39+
}
40+
/* large landscape */
41+
@media screen and (min-width: 844px) {
42+
:host {
43+
--title-gap: 30px;
44+
--button-gap: 24px;
45+
--logo-size: 48px;
46+
}
47+
}
48+
49+
50+
:host * {
51+
font-family: system, -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
52+
}
53+
54+
:root *, :root *:after, :root *:before {
55+
box-sizing: border-box;
56+
}
57+
58+
.error {
59+
align-items: center;
60+
background: rgba(0, 0, 0, 0.6);
61+
display: grid;
62+
height: 100%;
63+
justify-items: center;
64+
}
65+
66+
.error.mobile {
67+
border-radius: var(--inner-radius);
68+
height: 100%;
69+
overflow: auto;
70+
71+
/* Prevents automatic text resizing */
72+
text-size-adjust: 100%;
73+
-webkit-text-size-adjust: 100%;
74+
75+
@media screen and (min-width: 600px) and (min-height: 600px) {
76+
aspect-ratio: 16 / 9;
77+
}
78+
}
79+
80+
.container {
81+
column-gap: 24px;
82+
display: flex;
83+
flex-flow: row;
84+
margin: 0;
85+
max-width: 680px;
86+
padding: 0 40px;
87+
row-gap: 4px;
88+
}
89+
90+
.mobile .container {
91+
flex-flow: column;
92+
padding: 0 24px;
93+
94+
@media screen and (min-height: 320px) {
95+
margin: 16px 0;
96+
}
97+
98+
@media screen and (min-width: 375px) and (min-height: 400px) {
99+
margin: 36px 0;
100+
}
101+
}
102+
103+
.content {
104+
display: flex;
105+
flex-direction: column;
106+
gap: 4px;
107+
margin: 16px 0;
108+
109+
@media screen and (min-width: 600px) {
110+
margin: 24px 0;
111+
}
112+
}
113+
114+
115+
.icon {
116+
align-self: center;
117+
display: flex;
118+
justify-content: center;
119+
120+
&::before {
121+
content: ' ';
122+
display: block;
123+
background: url('../img/warning-96.data.svg') no-repeat;
124+
height: 48px;
125+
width: 48px;
126+
}
127+
128+
@media screen and (max-width: 320px) {
129+
display: none;
130+
}
131+
132+
@media screen and (min-width: 600px) and (min-height: 600px) {
133+
justify-content: start;
134+
135+
&::before {
136+
background-image: url('../img/warning-128.data.svg');
137+
height: 96px;
138+
width: 128px;
139+
}
140+
}
141+
}
142+
143+
.heading {
144+
color: #fff;
145+
font-size: 20px;
146+
font-weight: 700;
147+
line-height: calc(24 / 20);
148+
margin: 0;
149+
}
150+
151+
.messages {
152+
color: #ccc;
153+
font-size: 16px;
154+
line-height: calc(24 / 16);
155+
}
156+
157+
div.messages {
158+
display: flex;
159+
flex-direction: column;
160+
gap: 24px;
161+
162+
& p {
163+
margin: 0;
164+
}
165+
}
166+
167+
p.messages {
168+
margin: 0;
169+
}
170+
171+
ul.messages {
172+
li {
173+
list-style: disc;
174+
margin-left: 24px;
175+
}
176+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import mobilecss from './custom-error.css';
2+
import { createPolicy, html } from '../../../dom-utils.js';
3+
import { customElementsDefine, customElementsGet } from '../../../captured-globals.js';
4+
5+
/** @typedef {import('../error-detection').YouTubeError} YouTubeError */
6+
7+
export function registerCustomElements() {
8+
if (!customElementsGet(CustomError.CUSTOM_TAG_NAME)) {
9+
customElementsDefine(CustomError.CUSTOM_TAG_NAME, CustomError);
10+
}
11+
}
12+
13+
/**
14+
* The custom element that we use to present our UI elements
15+
* over the YouTube player
16+
*/
17+
export class CustomError extends HTMLElement {
18+
static CUSTOM_TAG_NAME = 'ddg-video-error';
19+
20+
policy = createPolicy();
21+
/** @type {boolean} */
22+
testMode = false;
23+
/** @type {YouTubeError} */
24+
error;
25+
/** @type {string} */
26+
title = '';
27+
/** @type {string[]} */
28+
messages = [];
29+
30+
connectedCallback() {
31+
this.createMarkupAndStyles();
32+
}
33+
34+
createMarkupAndStyles() {
35+
const shadow = this.attachShadow({ mode: this.testMode ? 'open' : 'closed' });
36+
const style = document.createElement('style');
37+
style.innerText = mobilecss;
38+
const container = document.createElement('div');
39+
const content = this.render();
40+
container.innerHTML = this.policy.createHTML(content);
41+
shadow.append(style, container);
42+
this.container = container;
43+
}
44+
45+
/**
46+
* @returns {string}
47+
*/
48+
render() {
49+
if (!this.title || !this.messages) {
50+
console.warn('missing error text. Please assign before rendering');
51+
return '';
52+
}
53+
54+
const messagesHtml = this.messages.map((message) => html`<p>${message}</p>`);
55+
56+
return html`
57+
<div class="error mobile">
58+
<div class="container">
59+
<span class="icon"></span>
60+
61+
<div class="content">
62+
<h1 class="heading">{heading}</h1>
63+
<div class="messages">${messagesHtml}</div>
64+
</div>
65+
</div>
66+
</div>
67+
`.toString();
68+
}
69+
}
70+
71+
/**
72+
*
73+
* @param {HTMLElement} targetElement
74+
* @param {object} options
75+
* @param {string} options.title
76+
* @param {string[]} options.messages
77+
*/
78+
export function showError(targetElement, { title, messages }) {
79+
registerCustomElements();
80+
console.log('Appending custom error view');
81+
const customError = /** @type {CustomError} */ (document.createElement(CustomError.CUSTOM_TAG_NAME));
82+
customError.testMode = true;
83+
customError.title = title;
84+
customError.messages = messages;
85+
console.log('Custom error view', customError, targetElement);
86+
targetElement.appendChild(customError);
87+
88+
return () => {
89+
document.querySelector(CustomError.CUSTOM_TAG_NAME)?.remove();
90+
};
91+
}

injected/src/features/duckplayer-native/duckplayer-native.js

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { muteAudio } from './mute-audio.js';
44
import { serpNotify } from './serp-notify.js';
55
import { ErrorDetection } from './error-detection.js';
66
import { appendThumbnailOverlay } from './overlays/thumbnail-overlay.js';
7+
import { stopVideoFromPlaying } from './pause-video.js';
8+
import { showError } from './custom-error/custom-error.js';
79

810
/**
911
*
@@ -36,15 +38,18 @@ export async function initDuckPlayerNative(messages) {
3638
const onMediaControlHandler = ({ pause }) => {
3739
console.log('MEDIA CONTROL', pause);
3840

39-
// TODO: move to settings.selectors.videoElementContainer or something similar
40-
const targetElement = document.querySelector('#player .html5-video-player');
41-
if (targetElement) {
42-
const destroy = appendThumbnailOverlay(/** @type {HTMLElement} */(targetElement));
43-
sideEffects.push(destroy);
41+
// TODO: move to settings.selectors.videoElement/videoElementContainer or something similar
42+
const videoElementContainer = document.querySelector('#player .html5-video-player');
43+
const videoElement = document.querySelector('#player video');
44+
if (videoElement && videoElementContainer) {
45+
sideEffects.push(
46+
stopVideoFromPlaying(/** @type {HTMLVideoElement} */ (videoElement)),
47+
appendThumbnailOverlay(/** @type {HTMLElement} */ (videoElementContainer)),
48+
);
4449
}
4550

4651
// mediaControl(pause);
47-
}
52+
};
4853

4954
messages.onMediaControl(onMediaControlHandler);
5055

@@ -58,10 +63,27 @@ export async function initDuckPlayerNative(messages) {
5863
serpNotify();
5964
});
6065

66+
/* Set up error handler */
67+
/** @type {(errorId: import('./error-detection.js').YouTubeError) => void} */
68+
const errorHandler = (errorId) => {
69+
console.log('Got error', errorId);
70+
// TODO: move to settings.selectors.errorContainer or something similar
71+
const errorContainer = document.querySelector('body');
72+
if (errorContainer) {
73+
// TODO: Get error messages from translated strings
74+
showError(/** @type {HTMLElement} */ (errorContainer), {
75+
title: 'Test Error',
76+
messages: ['This is an error'],
77+
});
78+
}
79+
};
80+
6181
/* Start error detection */
62-
const errorDetection = new ErrorDetection(messages);
82+
const errorDetection = new ErrorDetection(messages, errorHandler);
6383
const destroy = errorDetection.observe();
64-
if (destroy) sideEffects.push(destroy);
84+
if (destroy) {
85+
sideEffects.push(destroy);
86+
}
6587

6688
/* Start timestamp polling */
6789
const timestampPolling = setInterval(() => {

0 commit comments

Comments
 (0)