-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpalindrom-error-catcher.html
179 lines (156 loc) · 7.23 KB
/
palindrom-error-catcher.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
<!-- palindrom-error-catcher version: 1.1.0 | MIT License -->
<!--
Custom Element that binds with [palindrom-client](https://github.com/Palindrom/palindrom-client) connection events and shows a simple UX that allows the user to interact with them. It is can be used as an example of designing your own error catcher.
-->
<template id="palindrom-error-catcher">
<style>
.box-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 1000;
}
.box-container[hidden] {
display: none;
}
.box {
padding: 5px 5px;
background-color: #fee;
border: 1px solid #f9c1c1;
border-top: none;
color: #444;
cursor: pointer;
}
.hourglass {
animation: rotateHG 2s cubic-bezier(.03, 1, 1, 0) infinite;
transform-origin: 50% 50%;
display: inline-block;
text-decoration: none !important;
}
@keyframes rotateHG {
100% {
transform: rotate(360deg)
}
}
</style>
<div class="box-container connection-error-pane" hidden>
<div class="box error tappable">
<span class="hourglass">⏳</span> Connection error.</strong> See console for details.
<a class="reloading-in-message">
Reloading page in
<span class="reconnection-seconds">5</span>... •
<a href="javascript:;" class="cancel-reloading-btn">Do not reload</a> •
</a>
<a href="javascript:;" class="reload-btn">Reload now</a>
</div>
</div>
<div class="box-container generic-error-pane" hidden>
<div class="box error tappable">
<strong>⚠️ An error has occurred.</strong> See console for details.
<span>Click here to reload</span>
</div>
</div>
</template>
<script>
(function () {
const template = document.currentScript.ownerDocument.querySelector('#palindrom-error-catcher');
function bindCallbacks(to) {
to.handleConnectionError = to.handleConnectionError.bind(to);
to.handleReconnectionCountdownTick = to.handleReconnectionCountdownTick.bind(to);
to.handleReconnectionEnd = to.handleReconnectionEnd.bind(to);
to.handleGenericError = to.handleGenericError.bind(to);
to.cancelReloading = to.cancelReloading.bind(to);
}
function reload() {
location.reload();
}
function bindToElement(instance) {
instance.target = instance.getRootNode().querySelector(instance.targetSelector) || document.querySelector(instance.targetSelector);
}
function subscribeToEvents(instance) {
unsubscribeToEvents(instance);
instance.target.addEventListener('generic-error', instance.handleGenericError);
instance.target.addEventListener('connection-error', instance.handleConnectionError);
instance.target.addEventListener('reconnection-countdown', instance.handleReconnectionCountdownTick);
instance.target.addEventListener('reconnection-end', instance.handleReconnectionEnd);
instance.cancelReloadingBtn.addEventListener('click', instance.cancelReloading);
instance.reloadNowBtn.addEventListener('click', reload);
instance.genericErrorPane.addEventListener('click', reload);
instance.subscribed = true;
}
function unsubscribeToEvents(instance) {
if (instance.subscribed) {
instance.target.removeEventListener('generic-error', instance.handleGenericError);
instance.target.removeEventListener('connection-error', instance.handleConnectionError);
instance.target.removeEventListener('reconnection-countdown', instance.handleReconnectionCountdownTick);
instance.target.removeEventListener('reconnection-end', instance.handleReconnectionEnd);
instance.genericErrorPane.removeEventListener('click', reload);
instance.cancelReloadingBtn.removeEventListener('click', instance.cancelReloading);
instance.reloadNowBtn.removeEventListener('click', reload);
instance.subscribed = false;
}
}
class PalindromErrorCatcher extends HTMLElement {
static get observedAttributes() {
return ['target-selector']
}
constructor() {
super();
// Creates the shadow root
const shadowRoot = this.attachShadow({ mode: 'open' });
const clone = document.importNode(template.content, true);
shadowRoot.appendChild(clone);
this.connectionErrorPane = shadowRoot.querySelector('.connection-error-pane');
this.genericErrorPane = shadowRoot.querySelector('.generic-error-pane');
this.reconnectionSecondsSpan = shadowRoot.querySelector('.reconnection-seconds');
this.cancelReloadingBtn = shadowRoot.querySelector('.cancel-reloading-btn');
this.reloadNowBtn = shadowRoot.querySelector('.reload-btn');
bindCallbacks(this);
}
connectedCallback() {
this.targetSelector = this.getAttribute('target-selector') || 'palindrom-client';
bindToElement(this);
subscribeToEvents(this);
}
disconnectedCallback() {
unsubscribeToEvents(this);
}
handleGenericError(event) {
this.genericErrorPane.removeAttribute('hidden');
}
cancelReloading() {
this.connectionErrorPane.setAttribute('hidden', '');
clearInterval(this.reloadInterval);
delete this.reloadInterval;
}
handleConnectionError(event) {
this.connectionErrorPane.removeAttribute('hidden');
let timeout = 4;
// in case two consequtive errors happen, we don't want to schedule reloading twice
if (!this.reloadInterval) {
this.reloadInterval = setInterval(() => {
this.reconnectionSecondsSpan.textContent = timeout--;
if (timeout === 0) {
reload();
}
}, 1000);
}
}
handleReconnectionCountdownTick(event) {
/* you can use this method to add your logic to handle reconnection timer ticks */
console.info('Reconnection tick', event)
}
handleReconnectionEnd() {
/* you can use this method to add your logic to handle reconnection initiation. */
console.info('Reconnection will happen now...')
}
attributeChangedCallback(name, oldVal, newVal) {
this.targetSelector = newVal;
this.bindToElement(this);
this.subscribeToEvents(this);
}
}
customElements.define('palindrom-error-catcher', PalindromErrorCatcher)
})();
</script>