Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Today I Learned</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| .gradient-bg { | |
| background: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%); | |
| } | |
| .post-card { | |
| transition: transform 0.2s, box-shadow 0.2s; | |
| } | |
| .post-card:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); | |
| } | |
| .like-btn.liked { | |
| color: #ff0000; | |
| } | |
| .fade-in { | |
| animation: fadeIn 0.3s ease-in; | |
| } | |
| @keyframes fadeIn { | |
| from { opacity: 0; transform: translateY(10px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| .heart-animation { | |
| animation: heartBeat 0.5s; | |
| } | |
| @keyframes heartBeat { | |
| 0% { transform: scale(1); } | |
| 25% { transform: scale(1.2); } | |
| 50% { transform: scale(0.8); } | |
| 75% { transform: scale(1.1); } | |
| 100% { transform: scale(1); } | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-100 min-h-screen"> | |
| <header class="gradient-bg text-white shadow-lg"> | |
| <div class="container mx-auto px-4 py-6"> | |
| <div class="flex justify-between items-center"> | |
| <h1 class="text-3xl font-bold">Today I Learned</h1> | |
| <div class="text-sm"> | |
| <span id="post-count">0</span> posts shared | |
| </div> | |
| </div> | |
| <p class="mt-2 opacity-90">Share what you've learned today with the community!</p> | |
| </div> | |
| </header> | |
| <main class="container mx-auto px-4 py-8"> | |
| <div class="max-w-2xl mx-auto"> | |
| <!-- Post Form --> | |
| <div class="bg-white rounded-lg shadow-md p-6 mb-8"> | |
| <h2 class="text-xl font-semibold mb-4 text-gray-800">What did you learn today?</h2> | |
| <form id="post-form" class="space-y-4"> | |
| <div> | |
| <input type="text" id="user-name" class="w-full px-4 py-2 mb-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500" placeholder="Your name (optional)"> | |
| <textarea id="post-content" rows="3" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500" placeholder="Share your knowledge..." required></textarea> | |
| </div> | |
| <div class="flex justify-between items-center"> | |
| <div class="text-sm text-gray-500"> | |
| <i class="fas fa-info-circle mr-1"></i> Only text posts allowed | |
| </div> | |
| <button type="submit" class="px-6 py-2 bg-blue-600 text-white font-medium rounded-lg hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition"> | |
| Post | |
| </button> | |
| </div> | |
| </form> | |
| </div> | |
| <!-- Posts Wall --> | |
| <div id="posts-wall" class="space-y-6"> | |
| <!-- Posts will be inserted here by JavaScript --> | |
| </div> | |
| <!-- No Posts Message --> | |
| <div id="no-posts" class="text-center py-12"> | |
| <div class="text-gray-400 mb-4"> | |
| <i class="fas fa-comment-slash text-5xl"></i> | |
| </div> | |
| <h3 class="text-xl font-medium text-gray-600">No posts yet</h3> | |
| <p class="text-gray-500 mt-2">Be the first to share what you've learned today!</p> | |
| </div> | |
| </div> | |
| </main> | |
| <footer class="bg-gray-800 text-white py-6"> | |
| <div class="container mx-auto px-4 text-center"> | |
| <p>© 2023 Today I Learned. All posts are automatically deleted after 50 newest ones.</p> | |
| </div> | |
| </footer> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Initialize posts array from localStorage or create empty array | |
| let posts = JSON.parse(localStorage.getItem('posts')) || []; | |
| const postsWall = document.getElementById('posts-wall'); | |
| const noPosts = document.getElementById('no-posts'); | |
| const postForm = document.getElementById('post-form'); | |
| const postContent = document.getElementById('post-content'); | |
| const userName = document.getElementById('user-name'); | |
| const postCount = document.getElementById('post-count'); | |
| // Update post count | |
| postCount.textContent = posts.length; | |
| // Display posts | |
| function displayPosts() { | |
| postsWall.innerHTML = ''; | |
| if (posts.length === 0) { | |
| noPosts.style.display = 'block'; | |
| return; | |
| } else { | |
| noPosts.style.display = 'none'; | |
| } | |
| // Show only the 50 most recent posts | |
| const recentPosts = posts.slice(0, 50); | |
| recentPosts.forEach((post, index) => { | |
| const postElement = document.createElement('div'); | |
| postElement.className = 'post-card bg-white rounded-lg shadow-md p-6 fade-in'; | |
| postElement.innerHTML = ` | |
| <div class="flex items-start mb-4"> | |
| <div class="flex-shrink-0 h-10 w-10 rounded-full bg-gray-200 flex items-center justify-center text-gray-500"> | |
| <i class="fas fa-user"></i> | |
| </div> | |
| <div class="ml-3"> | |
| <p class="text-sm font-medium text-gray-900">${post.name ? escapeHtml(post.name) : 'Anonymous Learner'}</p> | |
| <p class="text-xs text-gray-500">${formatDate(post.timestamp)}</p> | |
| </div> | |
| </div> | |
| <div class="mb-4 text-gray-700"> | |
| ${escapeHtml(post.content)} | |
| </div> | |
| <div class="flex items-center"> | |
| <button class="like-btn flex items-center text-gray-500 hover:text-red-500 focus:outline-none ${post.liked ? 'liked' : ''}" data-index="${index}"> | |
| <i class="fas fa-heart ${post.liked ? 'heart-animation' : ''} mr-1"></i> | |
| <span>${post.likes}</span> | |
| </button> | |
| </div> | |
| `; | |
| postsWall.appendChild(postElement); | |
| }); | |
| } | |
| // Format date to relative time (e.g., "2 hours ago") | |
| function formatDate(timestamp) { | |
| const now = new Date(); | |
| const postDate = new Date(timestamp); | |
| const seconds = Math.floor((now - postDate) / 1000); | |
| let interval = Math.floor(seconds / 31536000); | |
| if (interval >= 1) return `${interval} year${interval === 1 ? '' : 's'} ago`; | |
| interval = Math.floor(seconds / 2592000); | |
| if (interval >= 1) return `${interval} month${interval === 1 ? '' : 's'} ago`; | |
| interval = Math.floor(seconds / 86400); | |
| if (interval >= 1) return `${interval} day${interval === 1 ? '' : 's'} ago`; | |
| interval = Math.floor(seconds / 3600); | |
| if (interval >= 1) return `${interval} hour${interval === 1 ? '' : 's'} ago`; | |
| interval = Math.floor(seconds / 60); | |
| if (interval >= 1) return `${interval} minute${interval === 1 ? '' : 's'} ago`; | |
| return `${Math.floor(seconds)} second${seconds === 1 ? '' : 's'} ago`; | |
| } | |
| // Escape HTML to prevent XSS | |
| function escapeHtml(unsafe) { | |
| return unsafe | |
| .replace(/&/g, "&") | |
| .replace(/</g, "<") | |
| .replace(/>/g, ">") | |
| .replace(/"/g, """) | |
| .replace(/'/g, "'") | |
| .replace(/\n/g, "<br>"); | |
| } | |
| // Handle form submission | |
| postForm.addEventListener('submit', function(e) { | |
| e.preventDefault(); | |
| const content = postContent.value.trim(); | |
| if (!content) return; | |
| // Create new post | |
| const newPost = { | |
| name: userName.value.trim(), | |
| content: content, | |
| timestamp: new Date().toISOString(), | |
| likes: 0, | |
| liked: false | |
| }; | |
| // Add to beginning of array (most recent first) | |
| posts.unshift(newPost); | |
| // Keep only the 50 most recent posts | |
| if (posts.length > 50) { | |
| posts = posts.slice(0, 50); | |
| } | |
| // Save to localStorage | |
| localStorage.setItem('posts', JSON.stringify(posts)); | |
| // Update UI | |
| postCount.textContent = posts.length; | |
| displayPosts(); | |
| // Reset form | |
| postContent.value = ''; | |
| userName.value = ''; | |
| }); | |
| // Handle like button clicks | |
| postsWall.addEventListener('click', function(e) { | |
| if (e.target.closest('.like-btn')) { | |
| const likeBtn = e.target.closest('.like-btn'); | |
| const postIndex = parseInt(likeBtn.dataset.index); | |
| const heartIcon = likeBtn.querySelector('i.fa-heart'); | |
| // Toggle like status | |
| if (posts[postIndex].liked) { | |
| posts[postIndex].likes--; | |
| posts[postIndex].liked = false; | |
| heartIcon.classList.remove('heart-animation'); | |
| } else { | |
| posts[postIndex].likes++; | |
| posts[postIndex].liked = true; | |
| heartIcon.classList.add('heart-animation'); | |
| // Remove animation class after animation completes | |
| setTimeout(() => { | |
| heartIcon.classList.remove('heart-animation'); | |
| }, 500); | |
| } | |
| // Save to localStorage | |
| localStorage.setItem('posts', JSON.stringify(posts)); | |
| // Update UI | |
| displayPosts(); | |
| } | |
| }); | |
| // Initial display | |
| displayPosts(); | |
| }); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=lamelight07/getting-wiser" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |