forked from giscus/giscus
-
Notifications
You must be signed in to change notification settings - Fork 0
/
client.ts
167 lines (147 loc) · 5.54 KB
/
client.ts
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
(function () {
const GISCUS_SESSION_KEY = 'giscus-session';
const script = document.currentScript as HTMLScriptElement;
const giscusOrigin = new URL(script.src).origin;
function formatError(message: string) {
return `[giscus] An error occurred. Error message: "${message}".`;
}
// Set up session and clear the session param on load
const url = new URL(location.href);
let session = url.searchParams.get('giscus');
const savedSession = localStorage.getItem(GISCUS_SESSION_KEY);
url.searchParams.delete('giscus');
const cleanedLocation = url.toString();
if (session) {
localStorage.setItem(GISCUS_SESSION_KEY, JSON.stringify(session));
history.replaceState(undefined, document.title, cleanedLocation);
} else {
try {
session = JSON.parse(savedSession) || '';
} catch (e) {
session = '';
localStorage.removeItem(GISCUS_SESSION_KEY);
console.warn(`${formatError(e?.message)} Session has been cleared.`);
}
}
const attributes = script.dataset;
const params: Record<string, string> = {};
const ogDescriptionMeta = document.querySelector(
`meta[property='og:description'],meta[name='description']`,
) as HTMLMetaElement;
params.origin = cleanedLocation;
params.session = session;
params.theme = attributes.theme;
params.reactionsEnabled = attributes.reactionsEnabled || '1';
params.emitMetadata = attributes.emitMetadata || '0';
params.inputPosition = attributes.inputPosition || 'bottom';
params.repo = attributes.repo;
params.repoId = attributes.repoId;
params.category = attributes.category || '';
params.categoryId = attributes.categoryId;
params.description = ogDescriptionMeta ? ogDescriptionMeta.content : '';
switch (attributes.mapping) {
case 'url':
params.term = cleanedLocation;
break;
case 'title':
params.term = document.title;
break;
case 'og:title':
{
const ogtitleMeta = document.querySelector(
`meta[property='og:title'],meta[name='og:title']`,
) as HTMLMetaElement;
params.term = ogtitleMeta ? ogtitleMeta.content : '';
}
break;
case 'specific':
params.term = attributes.term;
break;
case 'number':
params.number = attributes.term;
break;
case 'pathname':
default:
params.term =
location.pathname.length < 2 ? 'index' : location.pathname.substr(1).replace(/\.\w+$/, '');
break;
}
// Check anchor of the existing container and append it to origin URL
const existingContainer = document.querySelector('.giscus');
const id = existingContainer && existingContainer.id;
if (id) {
params.origin = `${cleanedLocation}#${id}`;
}
// Set up iframe src and loading attribute
const locale = attributes.lang ? `/${attributes.lang}` : '';
const src = `${giscusOrigin}${locale}/widget?${new URLSearchParams(params)}`;
const loading = attributes.loading === 'lazy' ? 'lazy' : undefined;
// Set up iframe element
const iframeElement = document.createElement('iframe');
const iframeAttributes = {
class: 'giscus-frame',
title: 'Comments',
scrolling: 'no',
src,
loading,
};
Object.entries(iframeAttributes).forEach(
([key, value]) => value && iframeElement.setAttribute(key, value),
);
// Create default style and prepend as <head>'s first child to make override possible.
const style = document.getElementById('giscus-css') || document.createElement('style');
style.id = 'giscus-css';
style.textContent = `
.giscus, .giscus-frame {
width: 100%;
min-height: 150px;
}
.giscus-frame {
border: none;
color-scheme: normal;
}
`;
document.head.prepend(style);
// Insert iframe element
if (!existingContainer) {
const iframeContainer = document.createElement('div');
iframeContainer.setAttribute('class', 'giscus');
iframeContainer.appendChild(iframeElement);
script.insertAdjacentElement('afterend', iframeContainer);
} else {
while (existingContainer.firstChild) existingContainer.firstChild.remove();
existingContainer.appendChild(iframeElement);
}
const suggestion = `Please consider reporting this error at https://github.com/giscus/giscus/issues/new.`;
// Listen to messages
window.addEventListener('message', (event) => {
if (event.origin !== giscusOrigin) return;
const { data } = event;
if (!(typeof data === 'object' && data.giscus)) return;
if (data.giscus.resizeHeight) {
iframeElement.style.height = `${data.giscus.resizeHeight}px`;
}
if (!data.giscus.error) return;
const message: string = data.giscus.error;
if (message.includes('Bad credentials') || message.includes('Invalid state value')) {
// Might be because token is expired or other causes
if (localStorage.getItem(GISCUS_SESSION_KEY) !== null) {
localStorage.removeItem(GISCUS_SESSION_KEY);
console.warn(`${formatError(message)} Session has been cleared.`);
delete params.session;
const src = `${giscusOrigin}/widget?${new URLSearchParams(params)}`;
iframeElement.src = src; // Force reload
} else if (!savedSession) {
console.error(`${formatError(message)} No session is stored initially. ${suggestion}`);
}
} else if (message.includes('Discussion not found')) {
console.warn(
`[giscus] ${message}. A new discussion will be created if a comment/reaction is submitted.`,
);
} else if (message.includes('API rate limit exceeded')) {
console.warn(formatError(message));
} else {
console.error(`${formatError(message)} ${suggestion}`);
}
});
})();