getting-wiser / index.html
lamelight07's picture
Add 2 files
701bce4 verified
<!DOCTYPE html>
<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, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;")
.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>