Dark mode, slimmer post profiles, adapting titles
This commit is contained in:
parent
da06d91930
commit
ce8b3c716d
@ -35,7 +35,7 @@ class ArchiveParser:
|
|||||||
cmd = f"ls -1 {self.archive_dir} | tail -{start_index + count + 1} | head -{count}"
|
cmd = f"ls -1 {self.archive_dir} | tail -{start_index + count + 1} | head -{count}"
|
||||||
print("Running cmd: " + cmd)
|
print("Running cmd: " + cmd)
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
cmd, shell=True, capture_output=True, text=True, check=True, timeout=30
|
cmd, shell=True, capture_output=True, text=True, check=True, timeout=300
|
||||||
)
|
)
|
||||||
|
|
||||||
# Parse the output (directory names)
|
# Parse the output (directory names)
|
||||||
@ -118,6 +118,44 @@ class ArchiveParser:
|
|||||||
print(f"Error getting total posts: {e}")
|
print(f"Error getting total posts: {e}")
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
def _extract_real_title_from_url(self, base_url: str) -> str:
|
||||||
|
"""Extract the real title from base_url when title is 'Reddit - Prove your humanity'."""
|
||||||
|
if not base_url or not isinstance(base_url, str):
|
||||||
|
return ""
|
||||||
|
|
||||||
|
# Check if the URL contains reddit pattern
|
||||||
|
if "reddit.com/" in base_url:
|
||||||
|
try:
|
||||||
|
# Parse URL to extract path component
|
||||||
|
parsed_url = urllib.parse.urlparse(base_url)
|
||||||
|
path_parts = parsed_url.path.strip("/").split("/")
|
||||||
|
|
||||||
|
if len(path_parts) >= 2:
|
||||||
|
# Get the last part which should contain the title
|
||||||
|
last_part = path_parts[-1]
|
||||||
|
|
||||||
|
# Handle cases like "why_did_the_dday_beach_landing_soldiers_carry_all/#2025-10-14T23:33:16+00:00"
|
||||||
|
if "#" in last_part:
|
||||||
|
title_part = last_part.split("#")[0]
|
||||||
|
else:
|
||||||
|
title_part = last_part
|
||||||
|
|
||||||
|
# Convert URL-encoded characters back to readable text
|
||||||
|
title_part = urllib.parse.unquote(title_part)
|
||||||
|
|
||||||
|
# Replace underscores with spaces and convert to title case
|
||||||
|
title = title_part.replace("_", " ")
|
||||||
|
|
||||||
|
# Capitalize first letter of each word for proper title formatting
|
||||||
|
title = " ".join(word.capitalize() for word in title.split())
|
||||||
|
|
||||||
|
return title if title else ""
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error extracting real title from URL: {e}")
|
||||||
|
return ""
|
||||||
|
|
||||||
|
return ""
|
||||||
|
|
||||||
def _create_post(self, index) -> Dict[str, Any]:
|
def _create_post(self, index) -> Dict[str, Any]:
|
||||||
"""Convert an index entry to a properly formatted post with ID and default image."""
|
"""Convert an index entry to a properly formatted post with ID and default image."""
|
||||||
try:
|
try:
|
||||||
@ -128,6 +166,13 @@ class ArchiveParser:
|
|||||||
post["title"] = index.get("title", "Untitled Post")
|
post["title"] = index.get("title", "Untitled Post")
|
||||||
post["url"] = "http://archive2.home.ms/" + index.get("timestamp", "#")
|
post["url"] = "http://archive2.home.ms/" + index.get("timestamp", "#")
|
||||||
|
|
||||||
|
# Check if title is the placeholder and extract real title from base_url
|
||||||
|
if post["title"] == "Reddit - Prove your humanity":
|
||||||
|
base_url = index.get("base_url", "")
|
||||||
|
real_title = self._extract_real_title_from_url(base_url)
|
||||||
|
if real_title:
|
||||||
|
post["title"] = real_title
|
||||||
|
|
||||||
# Set default image to reddit_logo.webp if no image is provided
|
# Set default image to reddit_logo.webp if no image is provided
|
||||||
post["image"] = (
|
post["image"] = (
|
||||||
"/Reddit_Logo.webp" # Default to the logo in website directory
|
"/Reddit_Logo.webp" # Default to the logo in website directory
|
||||||
|
|||||||
@ -1,27 +1,57 @@
|
|||||||
// Global variables
|
// Global variables
|
||||||
let isLoading = false;
|
let isLoading = false;
|
||||||
let currentPostCount = 0;
|
let currentPostCount = 0;
|
||||||
|
const POSTS_CACHE_KEY = "cached_posts_100";
|
||||||
|
const CACHE_EXPIRY_TIME = 5 * 60 * 1000; // 5 minutes
|
||||||
|
|
||||||
|
// Check for saved dark mode preference on page load
|
||||||
|
function initializeDarkMode() {
|
||||||
|
const savedMode = localStorage.getItem("darkMode");
|
||||||
|
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
|
||||||
|
|
||||||
|
if (savedMode === "enabled") {
|
||||||
|
enableDarkMode();
|
||||||
|
} else if (savedMode === "disabled") {
|
||||||
|
disableDarkMode();
|
||||||
|
} else if (prefersDark) {
|
||||||
|
// If no preference saved and system prefers dark, enable it
|
||||||
|
enableDarkMode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function enableDarkMode() {
|
||||||
|
document.body.classList.add("dark-mode");
|
||||||
|
document.querySelector(".dark-mode-toggle .dark-mode-icon").textContent =
|
||||||
|
"☀️";
|
||||||
|
localStorage.setItem("darkMode", "enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
function disableDarkMode() {
|
||||||
|
document.body.classList.remove("dark-mode");
|
||||||
|
document.querySelector(".dark-mode-toggle .dark-mode-icon").textContent =
|
||||||
|
"🌙";
|
||||||
|
localStorage.setItem("darkMode", "disabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleDarkMode() {
|
||||||
|
if (document.body.classList.contains("dark-mode")) {
|
||||||
|
disableDarkMode();
|
||||||
|
} else {
|
||||||
|
enableDarkMode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize dark mode on load
|
||||||
|
document.addEventListener("DOMContentLoaded", initializeDarkMode);
|
||||||
|
|
||||||
function createPostElement(post) {
|
function createPostElement(post) {
|
||||||
const postElement = document.createElement("div");
|
const postElement = document.createElement("div");
|
||||||
postElement.className = "post";
|
postElement.className = "post-card";
|
||||||
postElement.onclick = () => {
|
postElement.onclick = () => {
|
||||||
window.location.href = post.url;
|
window.location.href = post.url;
|
||||||
};
|
};
|
||||||
postElement.style.cursor = "pointer";
|
postElement.style.cursor = "pointer";
|
||||||
|
|
||||||
// Create the image container
|
|
||||||
const imageContainer = document.createElement("div");
|
|
||||||
imageContainer.className = "post-image-container";
|
|
||||||
|
|
||||||
const image = document.createElement("img");
|
|
||||||
image.src = post.image || "/Reddit_Logo.webp";
|
|
||||||
image.alt = post.title;
|
|
||||||
image.className = "post-image";
|
|
||||||
image.loading = "lazy";
|
|
||||||
|
|
||||||
imageContainer.appendChild(image);
|
|
||||||
|
|
||||||
// Create the content container
|
// Create the content container
|
||||||
const content = document.createElement("div");
|
const content = document.createElement("div");
|
||||||
content.className = "post-content";
|
content.className = "post-content";
|
||||||
@ -29,11 +59,20 @@ function createPostElement(post) {
|
|||||||
const title = document.createElement("a");
|
const title = document.createElement("a");
|
||||||
title.href = post.url;
|
title.href = post.url;
|
||||||
title.className = "post-title";
|
title.className = "post-title";
|
||||||
|
|
||||||
|
// Check if this is a "Reddit - Prove your humanity" type title and extract real title from base_url
|
||||||
|
if (post.title && post.title.includes("Reddit - Prove your humanity")) {
|
||||||
|
const realTitle = extractRealTitleFromUrl(post.base_url);
|
||||||
|
if (realTitle) {
|
||||||
|
title.textContent = realTitle;
|
||||||
|
} else {
|
||||||
title.textContent = post.title;
|
title.textContent = post.title;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
title.textContent = post.title;
|
||||||
|
}
|
||||||
|
|
||||||
content.appendChild(title);
|
// Create buttons container
|
||||||
|
|
||||||
// Create the buttons container
|
|
||||||
const buttonsContainer = document.createElement("div");
|
const buttonsContainer = document.createElement("div");
|
||||||
buttonsContainer.className = "post-buttons";
|
buttonsContainer.className = "post-buttons";
|
||||||
|
|
||||||
@ -69,9 +108,10 @@ function createPostElement(post) {
|
|||||||
buttonsContainer.appendChild(pdfButton);
|
buttonsContainer.appendChild(pdfButton);
|
||||||
|
|
||||||
// Assemble the complete post
|
// Assemble the complete post
|
||||||
postElement.appendChild(imageContainer);
|
content.appendChild(title);
|
||||||
|
content.appendChild(buttonsContainer);
|
||||||
|
|
||||||
postElement.appendChild(content);
|
postElement.appendChild(content);
|
||||||
postElement.appendChild(buttonsContainer);
|
|
||||||
|
|
||||||
return postElement;
|
return postElement;
|
||||||
}
|
}
|
||||||
@ -85,7 +125,80 @@ function renderPosts(posts) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the initial 10 posts from our API
|
// Cache management functions
|
||||||
|
function getCachedPosts() {
|
||||||
|
try {
|
||||||
|
const cached = localStorage.getItem(POSTS_CACHE_KEY);
|
||||||
|
if (cached) {
|
||||||
|
const { posts, timestamp } = JSON.parse(cached);
|
||||||
|
|
||||||
|
// Check if cache is stale (more than 5 minutes old)
|
||||||
|
if (Date.now() - timestamp < CACHE_EXPIRY_TIME) {
|
||||||
|
return posts;
|
||||||
|
} else {
|
||||||
|
// Clear stale cache
|
||||||
|
clearCachedPosts();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error retrieving cached posts:", error);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cachePosts(posts) {
|
||||||
|
try {
|
||||||
|
const cacheData = {
|
||||||
|
posts: posts,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
};
|
||||||
|
localStorage.setItem(POSTS_CACHE_KEY, JSON.stringify(cacheData));
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error caching posts:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearCachedPosts() {
|
||||||
|
try {
|
||||||
|
localStorage.removeItem(POSTS_CACHE_KEY);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error clearing cached posts:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract real title from base_url
|
||||||
|
function extractRealTitleFromUrl(baseUrl) {
|
||||||
|
if (!baseUrl || typeof baseUrl !== "string") {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the URL contains the pattern we're looking for
|
||||||
|
if (baseUrl.includes("reddit.com/")) {
|
||||||
|
try {
|
||||||
|
// Parse URL to extract path component
|
||||||
|
const urlParts = baseUrl.split("/");
|
||||||
|
const lastPart = urlParts[urlParts.length - 1];
|
||||||
|
|
||||||
|
// Handle cases like "why_did_the_dday_beach_landing_soldiers_carry_all/#2025-10-14T23:33:16+00:00"
|
||||||
|
let title = lastPart.split("#")[0];
|
||||||
|
|
||||||
|
// Convert URL-encoded characters back to readable text
|
||||||
|
title = decodeURIComponent(title);
|
||||||
|
|
||||||
|
// Replace underscores with spaces and capitalize first letter of each word
|
||||||
|
title = title.replace(/_/g, " ").replace(/\b\w/g, (l) => l.toUpperCase());
|
||||||
|
|
||||||
|
return title;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error extracting real title from URL:", error);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the initial 100 posts from our API
|
||||||
async function fetchRedditPosts() {
|
async function fetchRedditPosts() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch("/posts");
|
const response = await fetch("/posts");
|
||||||
@ -118,15 +231,40 @@ async function fetchMorePosts(currentCount) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function loadInitialPosts() {
|
async function loadInitialPosts() {
|
||||||
try {
|
// First try to load from cache
|
||||||
const posts = await fetchRedditPosts();
|
const cachedPosts = getCachedPosts();
|
||||||
renderPosts(posts);
|
|
||||||
currentPostCount = posts.length;
|
if (cachedPosts && cachedPosts.length > 0) {
|
||||||
|
console.log("Loading posts from cache");
|
||||||
|
renderPosts(cachedPosts);
|
||||||
|
currentPostCount = cachedPosts.length;
|
||||||
|
|
||||||
// Check if we have more posts or not
|
// Check if we have more posts or not
|
||||||
const totalResponse = await fetch("/posts/total");
|
const totalResponse = await fetch("/posts/total");
|
||||||
const totalData = await totalResponse.json();
|
const totalData = await totalResponse.json();
|
||||||
if (totalData.total <= 10) {
|
if (totalData.total <= 100) {
|
||||||
|
document.getElementById("no-more").classList.remove("hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no cache, fetch from API
|
||||||
|
try {
|
||||||
|
console.log("Fetching posts from API");
|
||||||
|
const posts = await fetchRedditPosts();
|
||||||
|
renderPosts(posts);
|
||||||
|
currentPostCount = posts.length;
|
||||||
|
|
||||||
|
// Cache the posts
|
||||||
|
if (posts.length > 0) {
|
||||||
|
cachePosts(posts);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have more posts or not
|
||||||
|
const totalResponse = await fetch("/posts/total");
|
||||||
|
const totalData = await totalResponse.json();
|
||||||
|
if (totalData.total <= 100) {
|
||||||
document.getElementById("no-more").classList.remove("hidden");
|
document.getElementById("no-more").classList.remove("hidden");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -161,7 +299,7 @@ async function loadMorePosts() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Infinite scroll implementation
|
// Set up infinite scroll with Intersection Observer
|
||||||
function setupInfiniteScroll() {
|
function setupInfiniteScroll() {
|
||||||
const container = document.getElementById("posts-container");
|
const container = document.getElementById("posts-container");
|
||||||
|
|
||||||
@ -192,6 +330,12 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
loadInitialPosts();
|
loadInitialPosts();
|
||||||
setupInfiniteScroll();
|
setupInfiniteScroll();
|
||||||
|
|
||||||
|
// Add event listener to dark mode toggle button
|
||||||
|
const darkModeToggle = document.getElementById("dark-mode-toggle");
|
||||||
|
if (darkModeToggle) {
|
||||||
|
darkModeToggle.addEventListener("click", toggleDarkMode);
|
||||||
|
}
|
||||||
|
|
||||||
// Also handle scroll manually for browsers that don't support Intersection Observer
|
// Also handle scroll manually for browsers that don't support Intersection Observer
|
||||||
window.addEventListener("scroll", () => {
|
window.addEventListener("scroll", () => {
|
||||||
if (
|
if (
|
||||||
|
|||||||
@ -10,6 +10,13 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<header class="header">
|
<header class="header">
|
||||||
<h1>Red Head</h1>
|
<h1>Red Head</h1>
|
||||||
|
<button
|
||||||
|
id="dark-mode-toggle"
|
||||||
|
class="dark-mode-toggle"
|
||||||
|
aria-label="Toggle dark mode"
|
||||||
|
>
|
||||||
|
<span class="dark-mode-icon">🌙</span>
|
||||||
|
</button>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main class="content">
|
<main class="content">
|
||||||
|
|||||||
@ -12,6 +12,14 @@ body {
|
|||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
color: #333;
|
color: #333;
|
||||||
background-color: #f0f0f0;
|
background-color: #f0f0f0;
|
||||||
|
transition:
|
||||||
|
background-color 0.3s,
|
||||||
|
color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark-mode {
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
color: #e0e0e0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
@ -27,6 +35,14 @@ body {
|
|||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header.dark-mode {
|
||||||
|
background-color: #2d2d2d;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.header h1 {
|
.header h1 {
|
||||||
@ -35,56 +51,126 @@ body {
|
|||||||
color: #ff4500;
|
color: #ff4500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header.dark-mode h1 {
|
||||||
|
color: #ff6b35;
|
||||||
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
padding: 20px 0;
|
padding: 20px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.posts-container {
|
.posts-container {
|
||||||
display: grid;
|
display: flex;
|
||||||
gap: 20px;
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post {
|
/* New rectangular post card design */
|
||||||
|
.post-card {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
border-radius: 8px;
|
border-radius: 4px;
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
transition:
|
transition:
|
||||||
transform 0.2s,
|
transform 0.2s,
|
||||||
box-shadow 0.2s;
|
box-shadow 0.2s;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post:hover {
|
.post-card.dark-mode {
|
||||||
transform: translateY(-2px);
|
background-color: #2d2d2d;
|
||||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-card:hover {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-card.dark-mode:hover {
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-content {
|
.post-content {
|
||||||
padding: 20px;
|
padding: 12px;
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-title {
|
.post-title {
|
||||||
font-size: 1.3rem;
|
font-size: 1.1rem;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 8px;
|
||||||
color: #000;
|
color: #000;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
display: block;
|
display: block;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-title.dark-mode {
|
||||||
|
color: #e0e0e0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-title:hover {
|
.post-title:hover {
|
||||||
color: #ff4500;
|
color: #ff4500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-image-container {
|
.post-title.dark-mode:hover {
|
||||||
width: 100%;
|
color: #ff6b35;
|
||||||
height: 250px;
|
}
|
||||||
overflow: hidden;
|
|
||||||
background-color: #f8f8f8;
|
/* Dark mode toggle button */
|
||||||
border-radius: 4px;
|
.dark-mode-toggle {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
border: none;
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-bottom: 15px;
|
transition: background-color 0.3s;
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-mode-toggle.dark-mode {
|
||||||
|
background-color: #404040;
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-mode-toggle:hover {
|
||||||
|
background-color: #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-mode-toggle.dark-mode:hover {
|
||||||
|
background-color: #505050;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-mode-icon {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
transition: transform 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-mode-toggle.dark-mode .dark-mode-icon {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-image-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 130px;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-image-container.dark-mode {
|
||||||
|
background-color: #3a3a3a;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-image {
|
.post-image {
|
||||||
@ -94,7 +180,7 @@ body {
|
|||||||
transition: transform 0.3s;
|
transition: transform 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post:hover .post-image {
|
.post-card:hover .post-image {
|
||||||
transform: scale(1.05);
|
transform: scale(1.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,6 +189,114 @@ body {
|
|||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.post-content.dark-mode {
|
||||||
|
background-color: #2d2d2d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 0 12px 12px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-self: flex-start;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-button {
|
||||||
|
background-color: #ff4500;
|
||||||
|
color: white;
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 6px 10px;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-button.dark-mode {
|
||||||
|
background-color: #ff6b35;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-button:hover {
|
||||||
|
background-color: #e03d00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-button.dark-mode:hover {
|
||||||
|
background-color: #ff7b45;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Limit body length on wider displays */
|
||||||
|
@media (min-width: 769px) {
|
||||||
|
.post-content {
|
||||||
|
max-height: 15vh; /* About 1/6 screen height for wide displays */
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-card {
|
||||||
|
min-height: 100px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile responsive design */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.container {
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header h1 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-content {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-title {
|
||||||
|
font-size: 1rem;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-image-container {
|
||||||
|
height: 100px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-buttons {
|
||||||
|
padding: 0 10px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-button {
|
||||||
|
padding: 5px 8px;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.post-image-container {
|
||||||
|
height: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header h1 {
|
||||||
|
font-size: 1.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-title {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-card {
|
||||||
|
min-height: 90px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loading and no more messages */
|
||||||
.loading,
|
.loading,
|
||||||
.no-more {
|
.no-more {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@ -111,80 +305,11 @@ body {
|
|||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.loading.dark-mode,
|
||||||
|
.no-more.dark-mode {
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
.hidden {
|
.hidden {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Responsive design */
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.container {
|
|
||||||
padding: 0 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header h1 {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-content {
|
|
||||||
padding: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-title {
|
|
||||||
font-size: 1.1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-image-container {
|
|
||||||
height: 200px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
|
||||||
.post-image-container {
|
|
||||||
height: 150px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header h1 {
|
|
||||||
font-size: 1.3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-title {
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-buttons {
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
padding: 0 20px 20px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-button {
|
|
||||||
background-color: #ff4500;
|
|
||||||
color: white;
|
|
||||||
text-decoration: none;
|
|
||||||
padding: 8px 16px;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
transition: background-color 0.2s;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-button:hover {
|
|
||||||
background-color: #e03d00;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Grid layout for desktop */
|
|
||||||
@media (min-width: 769px) {
|
|
||||||
.posts-container {
|
|
||||||
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tablet view */
|
|
||||||
@media (min-width: 481px) and (max-width: 768px) {
|
|
||||||
.posts-container {
|
|
||||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user