diff --git a/src/App.css b/src/App.css index 46613f4..8f42b67 100644 --- a/src/App.css +++ b/src/App.css @@ -353,6 +353,7 @@ button:disabled { margin-top: 1.15rem; display: grid; gap: 0.9rem; + padding-bottom: 1rem; } .post-loading { @@ -376,7 +377,7 @@ button:disabled { /* Post Card & Headers */ .post-card { - padding: 0; + padding: 0 0 1rem 0; overflow: hidden; } @@ -384,11 +385,16 @@ button:disabled { .post-card > .post-copy, .post-card > .inline-actions, .post-card > .post-form, -.post-card > .error-text { +.post-card > .error-text, +.post-card > .post-image { margin-left: 1rem; margin-right: 1rem; } +.post-card > .post-image { + width: calc(100% - 2rem); +} + .post-card > .post-top { margin-top: 1rem; } diff --git a/src/App.jsx b/src/App.jsx index 6675fa0..f8db6be 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -187,7 +187,6 @@ function App() { deletePost={(id) => apiClient.deletePost(session.token, id)} loadImage={(filename) => apiClient.postImage(session.token, filename)} currentUserId={session.user.id} - currentUsername={userProfile?.username} /> } /> diff --git a/src/api/client.js b/src/api/client.js index 1e8e9b6..8af846d 100644 --- a/src/api/client.js +++ b/src/api/client.js @@ -1,3 +1,5 @@ +import { getAuthSession } from "../utils/cookie"; + const API_BASE_URL = import.meta.env.VITE_API_BASE_URL; async function request(path, { method = "GET", token, body, formData } = {}) { @@ -7,7 +9,7 @@ async function request(path, { method = "GET", token, body, formData } = {}) { const headers = {}; - if (token) { + if (token && token !== "active-session") { headers.Authorization = `Bearer ${token}`; } @@ -87,10 +89,40 @@ export const apiClient = { }), postImage: async (token, filename) => { const headers = {}; - if (token) { - headers.Authorization = `Bearer ${token}`; + const effectiveToken = token && token !== "active-session" ? token : getAuthSession().token || null; + + if (effectiveToken && effectiveToken !== "active-session") { + headers.Authorization = `Bearer ${effectiveToken}`; } - const response = await fetch(`${API_BASE_URL}/posts/image/${encodeURIComponent(filename)}`, { + + let url; + if (typeof filename === "string") { + let target = filename.trim(); + if (API_BASE_URL && target.startsWith("API_BASE")) { + target = target.replace(/^API_BASE/, API_BASE_URL); + } + + if (target.startsWith("http://") || target.startsWith("https://")) { + if (target.includes("/posts/image/")) { + const imagePart = target.substring(target.indexOf("/posts/image/") + "/posts/image/".length); + const cleanName = encodeURIComponent(decodeURIComponent(imagePart)); + url = `${API_BASE_URL}/posts/image/${cleanName}`; + } else { + url = target; + } + } else if (target.includes("/posts/image/")) { + const imagePart = target.substring(target.indexOf("/posts/image/") + "/posts/image/".length); + const cleanName = encodeURIComponent(decodeURIComponent(imagePart)); + url = `${API_BASE_URL}/posts/image/${cleanName}`; + } else { + const cleanName = encodeURIComponent(decodeURIComponent(target.split("/").pop())); + url = `${API_BASE_URL}/posts/image/${cleanName}`; + } + } else { + url = `${API_BASE_URL}/posts/image/${encodeURIComponent(filename)}`; + } + + const response = await fetch(url, { headers, credentials: "include", }); diff --git a/src/components/PostCard.jsx b/src/components/PostCard.jsx index c341da7..d9f9bee 100644 --- a/src/components/PostCard.jsx +++ b/src/components/PostCard.jsx @@ -2,11 +2,55 @@ import { useEffect, useState } from "react"; import { Link } from "react-router-dom"; function getImageFilename(post) { - return post.image || post.image_filename || post.filename || null; + if (!post) return null; + + let img = + post.image_link || + post.imageLink || + post.image || + post.image_filename || + post.imageFilename || + post.image_url || + post.imageUrl || + post.image_path || + post.imagePath || + post.img || + post.photo || + post.picture || + post.filename || + post.file_name || + post.fileName || + post.file || + post.filepath || + post.file_path || + post.media || + post.media_url || + post.media_filename || + post.media_link || + post.attachment || + post.url || + post.link || + null; + + if (typeof img === "object" && img !== null) { + img = img.url || img.filename || img.src || img.path || img.name || img.link || null; + } + + if (!img) { + const text = post.text || post.content || post.body || post.description || ""; + const match = + text.match(/(?:(?:https?:\/\/[^\s]+|API_BASE)?\/posts\/image\/[^\s"'<>)]+)/i) || + text.match(/https?:\/\/[^\s]+\.(?:png|jpe?g|gif|webp|svg|avif)/i); + if (match) { + img = match[0]; + } + } + + return img; } -function getAuthorName(post, authorId) { - return post.author?.name || post.author_name || (post.author_username ? `@${post.author_username}` : `User ${authorId || ""}`); +function getAuthorName(post) { + return post.author?.name || post.author_name || (post.author_username ? `@${post.author_username}` : "User"); } function getAuthorUsername(post) { @@ -50,23 +94,46 @@ function AuthenticatedImage({ filename, alt, loadImage }) { let active = true; let objectUrl = ""; + setError(""); if (!filename) { setSource(""); return () => {}; } - loadImage(filename) - .then((blob) => { - if (active) { - objectUrl = URL.createObjectURL(blob); - setSource(objectUrl); - } - }) - .catch(() => { - if (active) { - setError("Image unavailable."); - } - }); + if (typeof loadImage === "function") { + loadImage(filename) + .then((result) => { + if (!active) return; + if (result instanceof Blob) { + objectUrl = URL.createObjectURL(result); + setSource(objectUrl); + } else if (typeof result === "string") { + setSource(result); + } + setError(""); + }) + .catch(() => { + if (!active) return; + if (typeof filename === "string") { + const apiBase = import.meta.env.VITE_API_BASE_URL || ""; + let directUrl = filename; + if (directUrl.startsWith("API_BASE")) { + directUrl = directUrl.replace(/^API_BASE/, apiBase); + } else if (directUrl.includes("/posts/image/")) { + const imagePart = directUrl.substring(directUrl.indexOf("/posts/image/") + "/posts/image/".length); + directUrl = `${apiBase}/posts/image/${encodeURIComponent(decodeURIComponent(imagePart))}`; + } else if (!directUrl.startsWith("http://") && !directUrl.startsWith("https://")) { + directUrl = `${apiBase}/posts/image/${encodeURIComponent(decodeURIComponent(directUrl.split("/").pop()))}`; + } + setSource(directUrl); + setError(""); + } else { + setError("Image unavailable."); + } + }); + } else if (typeof filename === "string") { + setSource(filename); + } return () => { active = false; @@ -79,7 +146,19 @@ function AuthenticatedImage({ filename, alt, loadImage }) { if (error) { return
{error}
; } - return source ?Your daily archive
{error}
: null} {loading ? (Loading community feed...
) : posts.length === 0 ? (Posts created by you will appear on your profile. Check back later for community updates!
+When others share posts, they will show up here.
@{displayUsername}
: null} - ID: {displayId}