fix: improved js error handling
This commit is contained in:
@@ -1,72 +1,70 @@
|
||||
/**
|
||||
* Helper function to handle fetch requests, parse the response, and manage common errors.
|
||||
* @param {string} url - The URL to fetch.
|
||||
* @param {object} options - The options for the fetch request.
|
||||
* @returns {Promise<object>} - A promise that resolves to the JSON response data.
|
||||
*/
|
||||
const fetchViewData = async (url, options = {}) => {
|
||||
const response = await fetch(url, options);
|
||||
|
||||
if (!response.ok) {
|
||||
let errorMessage;
|
||||
// Specifically handle the "Too Many Requests" error
|
||||
if (response.status === 429) {
|
||||
// Try to get the plain text message from the response body
|
||||
const errorText = await response.text();
|
||||
errorMessage = errorText || 'Too many requests. Please try again later.';
|
||||
} else {
|
||||
errorMessage = `Server responded with status ${response.status}`;
|
||||
}
|
||||
// Throw an error with our improved message
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
// If the response is OK, try to parse it as JSON.
|
||||
// A SyntaxError here would indicate an API problem (e.g., returning non-JSON on a 200 OK).
|
||||
return response.json();
|
||||
};
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Select all elements that are designated to display view counts.
|
||||
const viewCountElements = document.querySelectorAll('[data-view-count]');
|
||||
|
||||
// If no such elements are found, do nothing.
|
||||
if (viewCountElements.length === 0) {
|
||||
return;
|
||||
}
|
||||
if (viewCountElements.length === 0) return;
|
||||
|
||||
// Process each view count element found on the page.
|
||||
viewCountElements.forEach(element => {
|
||||
viewCountElements.forEach(async (element) => {
|
||||
const slug = element.dataset.slug;
|
||||
if (!slug) {
|
||||
// Skip if the element is missing the data-slug attribute.
|
||||
return;
|
||||
}
|
||||
if (!slug) return;
|
||||
|
||||
// Check if the element is on a single post page by looking for a `.post-meta` parent.
|
||||
// This distinguishes a single post view (which should be counted) from a list view.
|
||||
const isSinglePost = element.closest('.post-meta');
|
||||
const shouldIncrement = isSinglePost && !localStorage.getItem(`viewed-${slug}`);
|
||||
const apiUrl = `/api/views/${slug}`;
|
||||
|
||||
if (isSinglePost) {
|
||||
// On a single post page, increment the view count.
|
||||
const viewed = localStorage.getItem(`viewed-${slug}`);
|
||||
try {
|
||||
const options = shouldIncrement ? { method: 'POST' } : {};
|
||||
const data = await fetchViewData(apiUrl, options);
|
||||
|
||||
// Only increment if the user hasn't viewed this post in the current session.
|
||||
if (!viewed) {
|
||||
fetch(`/api/views/${slug}`, { method: 'POST' })
|
||||
.then(response => {
|
||||
if (!response.ok) throw new Error('Network response was not ok');
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
const count = data.count ?? 'Error';
|
||||
element.textContent = `${count} ${count === 1 ? 'view' : 'views'}`;
|
||||
// Mark as viewed to prevent re-counting on refresh.
|
||||
localStorage.setItem(`viewed-${slug}`, 'true');
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(`Error incrementing view count for slug ${slug}:`, error);
|
||||
element.textContent = 'N/A';
|
||||
});
|
||||
} else {
|
||||
// If already viewed in this session, just fetch the count without incrementing.
|
||||
fetch(`/api/views/${slug}`)
|
||||
.then(response => {
|
||||
if (!response.ok) throw new Error('Network response was not ok');
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
const count = data.count ?? 0;
|
||||
element.textContent = `${count} ${count === 1 ? 'view' : 'views'}`;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(`Error fetching view count for slug ${slug}:`, error);
|
||||
element.textContent = 'N/A';
|
||||
});
|
||||
if (shouldIncrement) {
|
||||
// Mark as viewed to prevent re-counting on refresh.
|
||||
localStorage.setItem(`viewed-${slug}`, 'true');
|
||||
}
|
||||
} else {
|
||||
// On a list page (like the main blog page), just fetch and display the count.
|
||||
fetch(`/api/views/${slug}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
element.textContent = data.count ?? 0;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(`Error fetching view count for slug ${slug}:`, error);
|
||||
element.textContent = 'N/A';
|
||||
});
|
||||
|
||||
// Update UI based on context (single post vs. list)
|
||||
if (isSinglePost) {
|
||||
// On a post, show "N views". Use 'Error' as a fallback if incrementing returns bad data.
|
||||
const count = data.count ?? (shouldIncrement ? 'Error' : 0);
|
||||
element.textContent = `${count} ${count === 1 ? 'view' : 'views'}`;
|
||||
} else {
|
||||
// On a list, just show the number.
|
||||
element.textContent = data.count ?? 0;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error processing view count for slug ${slug}:`, error.message);
|
||||
element.textContent = 'N/A';
|
||||
}
|
||||
});
|
||||
});
|
Reference in New Issue
Block a user