Skip to content
This repository was archived by the owner on Jun 26, 2025. It is now read-only.

Commit bd8692b

Browse files
committed
feat: enhance snippet fetching with timeout and online check
1 parent 26d6508 commit bd8692b

File tree

1 file changed

+59
-14
lines changed

1 file changed

+59
-14
lines changed

main.ts

Lines changed: 59 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,21 @@ export default class CssSnippetStore extends Plugin {
1818

1919
async onload() {
2020
// Start the mutation observer when the plugin is loaded.
21-
this.injectWhenSettingsLoaded();
21+
this.injectBrowseButton();
2222

2323

2424
// fetching list of snippets
25-
const url = "https://raw.githubusercontent.com/xavwe/obsidian-css-snippet-store/refs/heads/main/snippets.json"
25+
const url = "https://raw.githubusercontent.com/xavwe/obsidian-css-snippet-store/main/snippets.json"
2626
try {
27-
if (navigator.onLine) {
28-
const response = await fetch(url);
27+
if (await isOnline()) {
28+
const response = await fetchWithTimeout(url);
29+
if (!response.ok) {
30+
throw new Error(`Network response was not ok: ${response.statusText}`);
31+
}
32+
/*
33+
if (!response.headers.get('content-type')?.includes('application/json')) {
34+
throw new Error("Unexpected content type");
35+
}*/
2936
this.snippets = await response.json();
3037
} else {
3138
new Notice(`No Internet connection...`);
@@ -37,7 +44,7 @@ export default class CssSnippetStore extends Plugin {
3744
}
3845
}
3946

40-
injectWhenSettingsLoaded() {
47+
injectBrowseButton() {
4148
this.observer = new MutationObserver(() => {
4249
const settingItems = Array.from(document.querySelectorAll('.setting-item'));
4350

@@ -61,29 +68,34 @@ export default class CssSnippetStore extends Plugin {
6168

6269
controlElement.appendChild(customButton);
6370

71+
customButton.textContent = 'Browse';
72+
customButton.className = "mod-cta my-custom-button";
73+
6474
// Function to update the button text based on connectivity
6575

6676

6777
// Initial check
78+
/*
6879
updateButtonLabel(customButton);
80+
*/
6981

70-
// Update on connectivity change
82+
/* // Update on connectivity change
7183
window.addEventListener('online', () => updateButtonLabel(customButton));
72-
window.addEventListener('offline', () => updateButtonLabel(customButton));
84+
window.addEventListener('offline', () => updateButtonLabel(customButton));*/
7385
}
7486
}
7587
}
7688
});
7789

78-
function updateButtonLabel(button: HTMLButtonElement) {
79-
if (navigator.onLine) {
90+
/* function updateButtonLabel(button: HTMLButtonElement) {
91+
if (true) {
8092
button.textContent = 'Browse';
8193
button.className = "mod-cta my-custom-button";
8294
} else {
8395
button.textContent = 'No Internet';
8496
button.className = "";
8597
}
86-
}
98+
}*/
8799

88100
this.observer.observe(document.body, {
89101
childList: true,
@@ -173,7 +185,7 @@ class CssSnippetStoreModal extends Modal {
173185

174186
contentEl.createEl('h1', { text: 'CSS Snippet Store' });
175187

176-
// --- Search bar ---
188+
// Search bar
177189
const searchInput = contentEl.createEl('input', {
178190
type: 'text',
179191
placeholder: 'Search snippets...',
@@ -185,7 +197,7 @@ class CssSnippetStoreModal extends Modal {
185197

186198
const grid = contentEl.createEl('div', { cls: 'community-items-container' });
187199

188-
// --- Render Function ---
200+
// Render Function
189201
const renderSnippets = (filter: string = "") => {
190202
grid.empty();
191203
const lowerFilter = filter.toLowerCase();
@@ -224,8 +236,15 @@ class CssSnippetStoreModal extends Modal {
224236
button.addEventListener('click', async () => {
225237
const url = `https://raw.githubusercontent.com/${snippet.repo}/refs/heads/main/${snippet.folder}/snippet.css`;
226238
try {
227-
if (navigator.onLine) {
228-
const response = await fetch(url);
239+
if (await isOnline()) {
240+
const response = await fetchWithTimeout(url);
241+
if (!response.ok) {
242+
throw new Error(`Network response was not ok: ${response.statusText}`);
243+
}
244+
/*
245+
if (!response.headers.get('content-type')?.includes('text/css')) {
246+
throw new Error("Expected CSS content");
247+
}*/
229248
const code = await response.text();
230249
await this.install(snippet.id, code);
231250
this.close();
@@ -257,4 +276,30 @@ class CssSnippetStoreModal extends Modal {
257276
const { contentEl } = this;
258277
contentEl.empty();
259278
}
279+
}
280+
281+
function fetchWithTimeout(resource: RequestInfo, options: RequestInit = {}, timeout = 10000): Promise<Response> {
282+
return Promise.race([
283+
fetch(resource, options),
284+
new Promise<Response>((_, reject) => setTimeout(() => reject(new Error("Request timed out")), timeout))
285+
]);
286+
}
287+
288+
289+
export async function isOnline(timeout = 3000): Promise<boolean> {
290+
try {
291+
const controller = new AbortController();
292+
const id = setTimeout(() => controller.abort(), timeout);
293+
294+
await fetch("https://ping.archlinux.org", {
295+
method: "GET",
296+
mode: "no-cors",
297+
signal: controller.signal,
298+
cache: "no-store"
299+
});
300+
clearTimeout(id);
301+
return true;
302+
} catch (e) {
303+
return false;
304+
}
260305
}

0 commit comments

Comments
 (0)