/** * Discourse API Integration for enviPath Community * Handles fetching topics from the Discourse forum API */ class DiscourseAPI { constructor() { this.baseUrl = 'https://community.envipath.org'; this.categoryId = 10; // Announcements category this.limit = 3; // Number of topics to fetch } /** * Fetch topics from Discourse API * @param {number} limit - Number of topics to fetch * @returns {Promise} Array of topic objects */ async fetchTopics(limit = this.limit) { try { const url = `${this.baseUrl}/c/announcements/${this.categoryId}.json`; const response = await fetch(url); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); return this.processTopics(data.topic_list.topics, limit); } catch (error) { console.error('Error fetching Discourse topics:', error); return this.getFallbackTopics(); } } /** * Process raw Discourse topics into standardized format * @param {Array} topics - Raw topics from Discourse API * @param {number} limit - Number of topics to return * @returns {Array} Processed topics */ processTopics(topics, limit) { return topics .slice(0, limit) .map(topic => ({ id: topic.id, title: topic.title, excerpt: this.extractExcerpt(topic.excerpt), url: `${this.baseUrl}/t/${topic.slug}/${topic.id}`, replies: topic.reply_count, views: topic.views, created_at: topic.created_at, category: 'Announcements', category_id: this.categoryId, author: topic.last_poster_username, author_avatar: this.getAvatarUrl(topic.last_poster_username) })) .sort((a, b) => new Date(b.created_at) - new Date(a.created_at)); // Latest first } /** * Extract excerpt from topic content * @param {string} excerpt - Raw excerpt from Discourse * @returns {string} Cleaned excerpt */ extractExcerpt(excerpt) { if (!excerpt) return 'Click to read more'; // Remove HTML tags and clean up; collapse whitespace; do not add manual ellipsis return excerpt .replace(/<[^>]*>/g, '') // Remove HTML tags .replace(/ /g, ' ') // Replace   with spaces .replace(/&/g, '&') // Replace & with & .replace(/</g, '<') // Replace < with < .replace(/>/g, '>') // Replace > with > .replace(/\s+/g, ' ') // Collapse all whitespace/newlines .trim() } /** * Get avatar URL for user * @param {string} username - Username * @returns {string} Avatar URL */ getAvatarUrl(username) { if (!username) return `${this.baseUrl}/letter_avatar_proxy/v4/letter/u/1.png`; return `${this.baseUrl}/user_avatar/${this.baseUrl.replace('https://', '')}/${username}/40/1_1.png`; } /** * Get fallback topics when API fails * @returns {Array} Fallback topics */ getFallbackTopics() { return [ { id: 110, title: "enviPath Beta Update: Major Improvements to Prediction, Analysis & Collaboration!", excerpt: "We're excited to announce major updates to the enviPath beta platform! This release includes significant improvements to our prediction algorithms, enhanced analysis tools, and new collaboration features that will make environmental biotransformation research more accessible and efficient.", url: "https://community.envipath.org/t/envipath-beta-update-major-improvements-to-prediction-analysis-collaboration/110", replies: 0, views: 16, created_at: "2025-09-23T00:00:00Z", category: "Announcements", category_id: 10, author: "wicker", author_avatar: "https://community.envipath.org/user_avatar/community.envipath.org/wicker/40/1_1.png" } ]; } /** * Format date for display * @param {string} dateString - ISO date string * @returns {string} Formatted date */ formatDate(dateString) { const date = new Date(dateString); return date.toLocaleDateString(); } /** * Load topics and call render function * @param {string} containerId - ID of container element * @param {Function} renderCallback - Function to render topics */ async loadTopics(containerId = 'community-news-container', renderCallback = null) { const container = document.getElementById(containerId); if (!container) { console.error(`Container with ID '${containerId}' not found`); return; } // Hide loading spinner const loading = document.getElementById('loading'); if (loading) { loading.style.display = 'none'; } try { const topics = await this.fetchTopics(); if (renderCallback && typeof renderCallback === 'function') { renderCallback(topics); } else { // Default rendering - just log topics console.log('Topics loaded:', topics); } } catch (error) { console.error('Error loading topics:', error); container.innerHTML = '

No updates found. Head over to the community to see the latest discussions.

'; } } } // Export for use in other scripts window.DiscourseAPI = DiscourseAPI; // Auto-initialize if container exists document.addEventListener('DOMContentLoaded', function() { if (document.getElementById('community-news-container')) { const discourseAPI = new DiscourseAPI(); discourseAPI.loadTopics('community-news-container', function(topics) { // This will be handled by the template's render function if (window.renderDiscourseTopics) { window.renderDiscourseTopics(topics); } }); } });