// FlowFlex - Main JavaScript class FlowFlexApp { constructor() { this.currentFeed = 'trending'; this.posts = []; this.seenPosts = new Set(); this.userPreferences = this.getUserPreferences(); this.isLoading = false; this.page = 1; this.init(); } init() { this.bindEvents(); this.loadInitialFeed(); this.setupIntersectionObserver(); this.checkAuthStatus(); } bindEvents() { // Feed filter buttons document.querySelectorAll('.feed-filter-btn').forEach(btn => { btn.addEventListener('click', (e) => { this.switchFeed(e.target.closest('.feed-filter-btn').dataset.filter); }); // Like button functionality document.addEventListener('click', (e) => { if (e.target.closest('.like-btn')) { this.handleLike(e.target.closest('.like-btn')); }); // Save post functionality document.addEventListener('click', (e) => { if (e.target.closest('.save-btn')) { this.handleSave(e.target.closest('.save-btn')); } // Post interaction tracking document.addEventListener('click', (e) => { const postCard = e.target.closest('.post-card'); if (postCard) { this.trackPostInteraction(postCard.dataset.postId); } // Auth modal triggers document.addEventListener('click', (e) => { if (e.target.closest('[data-auth-trigger]')) { this.showAuthModal(); } }); } switchFeed(feedType) { // Update active button document.querySelectorAll('.feed-filter-btn').forEach(btn => { btn.classList.remove('active', 'bg-primary-500', 'text-white'); btn.classList.add('bg-gray-200', 'text-gray-700'); }); const activeBtn = document.querySelector(`[data-filter="${feedType}"]`); activeBtn.classList.remove('bg-gray-200', 'text-gray-700'); activeBtn.classList.add('active', 'bg-primary-500', 'text-white'); this.currentFeed = feedType; this.page = 1; this.posts = []; this.loadFeed(); } async loadInitialFeed() { await this.loadFeed(); } async loadFeed() { if (this.isLoading) return; this.isLoading = true; this.showLoading(); try { // Simulate API call with timeout await new Promise(resolve => setTimeout(resolve, 1000)); const newPosts = await this.fetchPosts(this.currentFeed, this.page); const filteredPosts = this.filterDuplicatePosts(newPosts); if (filteredPosts.length > 0) { this.posts = [...this.posts, ...filteredPosts]; this.renderPosts(filteredPosts); this.page++; } else { this.showEmptyState(); } } catch (error) { console.error('Error loading feed:', error); this.showError('Failed to load posts. Please try again.'); } finally { this.isLoading = false; this.hideLoading(); } } async fetchPosts(feedType, page = 1) { // Using Unsplash API for demo posts (real implementation would use your backend) const accessKey = 'YOUR_UNSPLASH_ACCESS_KEY'; // Replace with actual key const endpoints = { trending: `https://api.unsplash.com/photos?page=${page}&per_page=9&order_by=popular`, following: `https://api.unsplash.com/photos?page=${page}&per_page=9&order_by=latest`, discover: `https://api.unsplash.com/photos/random?count=9`, personalized: `https://api.unsplash.com/photos?page=${page}&per_page=9` }; // For demo purposes, we'll use mock data return this.generateMockPosts(9); } generateMockPosts(count) { const categories = ['nature', 'technology', 'travel', 'food', 'architecture', 'people']; const users = [ { name: 'Alex Johnson', username: 'alexj', followers: 1243 }, { name: 'Sarah Miller', username: 'sarahm', followers: 856 }, { name: 'Mike Chen', username: 'mikec', followers: 2107 }, { name: 'Emma Davis', username: 'emmad', followers: 932 }, { name: 'James Wilson', username: 'jamesw', followers: 1541 } ]; return Array.from({ length: count }, (_, i) => { const user = users[Math.floor(Math.random() * users.length)]; const category = categories[Math.floor(Math.random() * categories.length)]; const postId = `post_${Date.now()}_${i}`; return { id: postId, user: user, image: `http://static.photos/${category}/640x360/${i + 1}`, caption: this.generateMockCaption(category), likes: Math.floor(Math.random() * 1000), comments: Math.floor(Math.random() * 50), shares: Math.floor(Math.random() * 20), timestamp: new Date(Date.now() - Math.random() * 7 * 24 * 60 * 60 * 1000), // Within last 7 days category: category, engagement: Math.random() * 100 }; }); } generateMockCaption(category) { const captions = { nature: ['Beautiful sunset at the beach! πŸŒ…', 'Morning hike in the mountains πŸ”οΈ', 'Peaceful forest walk 🌲'], technology: ['Working on some exciting new projects! πŸ’»', 'Tech conference was amazing! πŸš€', 'Latest gadget unboxing πŸ“±'], travel: ['Exploring new places! ✈️', 'Cultural experience of a lifetime 🌍', 'Travel dreams coming true πŸ—ΊοΈ'], food: ['Delicious homemade meal! 🍽️', 'Food photography session πŸ“Έ', 'Trying out new recipes πŸ‘¨β€πŸ³'], architecture: ['Modern architecture never fails to impress! πŸ›οΈ', 'Historical building tour 🏰', 'Architectural marvels πŸ—οΈ'], people: ['Great time with friends! πŸ‘₯', 'Community event was fantastic! πŸŽ‰', 'Networking and making connections 🀝'] }; const categoryCaptions = captions[category] || ['Great day! 😊']; return categoryCaptions[Math.floor(Math.random() * categoryCaptions.length)]; } filterDuplicatePosts(newPosts) { return newPosts.filter(post => !this.seenPosts.has(post.id)); } renderPosts(posts) { const feedContainer = document.getElementById('feed-container'); const emptyState = document.getElementById('empty-state'); if (posts.length === 0 && this.posts.length === 0) { this.showEmptyState(); return; } emptyState.classList.add('hidden'); posts.forEach(post => { this.seenPosts.add(post.id); const postElement = this.createPostElement(post); feedContainer.appendChild(postElement); }); } createPostElement(post) { const postDiv = document.createElement('div'); postDiv.className = 'post-card bg-white rounded-xl shadow-sm overflow-hidden fade-in'; postDiv.dataset.postId = post.id; const timeAgo = this.getTimeAgo(post.timestamp); postDiv.innerHTML = `
Post image
${post.user.name}

${post.user.name}

@${post.user.username}

${timeAgo}

${post.caption}

`; return postDiv; } handleLike(likeBtn) { const heartIcon = likeBtn.querySelector('i'); const likesCount = likeBtn.querySelector('span'); likeBtn.classList.add('like-animation'); setTimeout(() => likeBtn.classList.remove('like-animation'), 600); const isLiked = heartIcon.style.fill === 'currentColor'; const currentLikes = parseInt(likesCount.textContent); if (isLiked) { heartIcon.style.fill = 'none'; likesCount.textContent = currentLikes - 1; likesCount.style.color = ''; } else { heartIcon.style.fill = 'currentColor'; likesCount.textContent = currentLikes + 1; likesCount.style.color = '#ef4444'; } } handleSave(saveBtn) { const bookmarkIcon = saveBtn.querySelector('i'); const isSaved = bookmarkIcon.style.fill === 'currentColor'; if (isSaved) { bookmarkIcon.style.fill = 'none'; } else { bookmarkIcon.style.fill = 'currentColor'; } } trackPostInteraction(postId) { // Track user engagement for personalization const post = this.posts.find(p => p.id === postId); if (post) { this.updateUserPreferences(post); } } updateUserPreferences(post) { if (!this.userPreferences.engagedCategories) { this.userPreferences.engagedCategories = {}; } this.userPreferences.engagedCategories[post.category] = (this.userPreferences.engagedCategories[post.category] || 0) + 1; localStorage.setItem('flowflex_preferences', JSON.stringify(this.userPreferences)); } getUserPreferences() { const stored = localStorage.getItem('flowflex_preferences'); return stored ? JSON.parse(stored) : { engagedCategories: {}, preferredFeed: 'trending', blockedUsers: [] }; } getTimeAgo(timestamp) { const now = new Date(); const diff = now - new Date(timestamp); const minutes = Math.floor(diff / 60000); const hours = Math.floor(diff / 3600000); const days = Math.floor(diff / 86400000); if (days > 0) return `${days}d ago`; if (hours > 0) return `${hours}h ago`; return `${minutes}m ago`; } showLoading() { document.getElementById('loading').classList.remove('hidden'); } hideLoading() { document.getElementById('loading').classList.add('hidden'); } showEmptyState() { document.getElementById('feed-container').innerHTML = ''; document.getElementById('empty-state').classList.remove('hidden'); } showError(message) { // Simple error notification const errorDiv = document.createElement('div'); errorDiv.className = 'fixed top-4 right-4 bg-red-500 text-white px-6 py-3 rounded-lg shadow-lg z-50'; errorDiv.textContent = message; document.body.appendChild(errorDiv); setTimeout(() => { errorDiv.remove(); }, 3000); } setupIntersectionObserver() { const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting && !this.isLoading) { this.loadFeed(); } }, { rootMargin: '100px' }); observer.observe(document.getElementById('loading')); } checkAuthStatus() { const isLoggedIn = localStorage.getItem('flowflex_user'); if (!isLoggedIn) { // Show login prompt or redirect console.log('User not logged in'); } } showAuthModal() { const authModal = document.querySelector('auth-modal'); if (authModal) { authModal.show(); } } // Profile page specific methods loadProfilePosts() { // Load user's posts for profile page const posts = this.generateMockPosts(12); this.renderProfilePosts(posts); } renderProfilePosts(posts) { const profileContent = document.getElementById('profile-content'); if (!profileContent) return; posts.forEach(post => { const postElement = this.createPostElement(post); profileContent.appendChild(postElement); }); } } // Initialize the app when DOM is loaded document.addEventListener('DOMContentLoaded', () => { new FlowFlexApp(); }); // Utility function for API calls async function apiCall(url, options = {}) { try { const response = await fetch(url, { headers: { 'Authorization': 'Client-ID YOUR_UNSPLASH_ACCESS_KEY', 'Accept-Version': 'v1' }, ...options }); if (!response.ok) { throw new Error(`API call failed: ${response.status}`); } return await response.json(); } catch (error) { console.error('API call error:', error); throw error; } }