Add sidebar with friends list to chat window
- Added toggleable sidebar showing friends list on right side of chat - Small avatars with online status and unread badges - Click friend to switch conversation - Toggle button in chat header - Hidden on mobile (use contacts dropdown instead) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
f84888ea06
commit
9d207f53cf
|
|
@ -41,6 +41,7 @@ export default function ChatContainer() {
|
||||||
const [activeChats, setActiveChats] = useState([]); // [{ username, avatar }]
|
const [activeChats, setActiveChats] = useState([]); // [{ username, avatar }]
|
||||||
const [minimizedChats, setMinimizedChats] = useState({}); // { username: boolean }
|
const [minimizedChats, setMinimizedChats] = useState({}); // { username: boolean }
|
||||||
const [unreadCounts, setUnreadCounts] = useState({}); // { username: number }
|
const [unreadCounts, setUnreadCounts] = useState({}); // { username: number }
|
||||||
|
const [showSidebar, setShowSidebar] = useState(false); // sidebar with friends list
|
||||||
|
|
||||||
const notificationPermissionRef = useRef("default");
|
const notificationPermissionRef = useRef("default");
|
||||||
const minimizedChatsRef = useRef(minimizedChats);
|
const minimizedChatsRef = useRef(minimizedChats);
|
||||||
|
|
@ -573,7 +574,7 @@ export default function ChatContainer() {
|
||||||
<div
|
<div
|
||||||
key={chat.username}
|
key={chat.username}
|
||||||
className="chat-window-wrapper"
|
className="chat-window-wrapper"
|
||||||
style={{ right: 16 + index * 340 }}
|
style={{ right: 16 + index * (showSidebar ? 396 : 340) }}
|
||||||
>
|
>
|
||||||
<ChatWindow
|
<ChatWindow
|
||||||
recipientID={chat.username}
|
recipientID={chat.username}
|
||||||
|
|
@ -587,6 +588,12 @@ export default function ChatContainer() {
|
||||||
onTyping={handleTyping(chat.username)}
|
onTyping={handleTyping(chat.username)}
|
||||||
onClose={() => closeChat(chat.username)}
|
onClose={() => closeChat(chat.username)}
|
||||||
onMinimize={() => toggleMinimize(chat.username)}
|
onMinimize={() => toggleMinimize(chat.username)}
|
||||||
|
showSidebar={showSidebar}
|
||||||
|
friends={friends}
|
||||||
|
presence={presence}
|
||||||
|
unreadCounts={unreadCounts}
|
||||||
|
onSelectFriend={openChat}
|
||||||
|
onToggleSidebar={() => setShowSidebar(!showSidebar)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,14 @@
|
||||||
|
/* Outer wrapper for chat + sidebar */
|
||||||
|
.chat-window-outer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-window-outer.with-sidebar {
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.chat-window {
|
.chat-window {
|
||||||
width: 320px;
|
width: 320px;
|
||||||
height: 420px;
|
height: 420px;
|
||||||
|
|
@ -11,6 +22,134 @@
|
||||||
backdrop-filter: blur(12px);
|
backdrop-filter: blur(12px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chat-window-outer.with-sidebar .chat-window {
|
||||||
|
border-radius: 0 12px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sidebar toggle button */
|
||||||
|
.chat-sidebar-toggle {
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
color: #e2e8f0;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 11px;
|
||||||
|
transition: background 0.2s, color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-sidebar-toggle:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-sidebar-toggle.active {
|
||||||
|
background: rgba(59, 130, 246, 0.4);
|
||||||
|
color: #60a5fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Friends Sidebar */
|
||||||
|
.chat-sidebar {
|
||||||
|
width: 56px;
|
||||||
|
height: 420px;
|
||||||
|
background: rgba(15, 23, 42, 0.95);
|
||||||
|
border: 1px solid rgba(148, 163, 184, 0.3);
|
||||||
|
border-right: none;
|
||||||
|
border-radius: 12px 0 0 0;
|
||||||
|
backdrop-filter: blur(12px);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-sidebar-list {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 8px 6px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-sidebar-item {
|
||||||
|
width: 42px;
|
||||||
|
height: 42px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
transition: background 0.2s;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-sidebar-item:hover {
|
||||||
|
background: rgba(59, 130, 246, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-sidebar-item.active {
|
||||||
|
background: rgba(59, 130, 246, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-sidebar-item.has-unread {
|
||||||
|
background: rgba(239, 68, 68, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-sidebar-avatar {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: linear-gradient(135deg, #3b82f6, #8b5cf6);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: white;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 14px;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-sidebar-avatar img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-sidebar-avatar.online::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
background: #22c55e;
|
||||||
|
border: 2px solid rgba(15, 23, 42, 0.95);
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-sidebar-unread {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
min-width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
padding: 0 4px;
|
||||||
|
background: #ef4444;
|
||||||
|
color: white;
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 700;
|
||||||
|
border-radius: 999px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
.chat-window--minimized {
|
.chat-window--minimized {
|
||||||
height: auto;
|
height: auto;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
@ -306,8 +445,30 @@ body[data-theme="light"] .chat-input {
|
||||||
border-color: rgba(148, 163, 184, 0.4);
|
border-color: rgba(148, 163, 184, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Light theme sidebar */
|
||||||
|
body[data-theme="light"] .chat-sidebar {
|
||||||
|
background: rgba(255, 255, 255, 0.98);
|
||||||
|
border-color: rgba(148, 163, 184, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
body[data-theme="light"] .chat-sidebar-item:hover {
|
||||||
|
background: rgba(59, 130, 246, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
body[data-theme="light"] .chat-sidebar-item.active {
|
||||||
|
background: rgba(59, 130, 246, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
body[data-theme="light"] .chat-sidebar-avatar.online::after {
|
||||||
|
border-color: rgba(255, 255, 255, 0.98);
|
||||||
|
}
|
||||||
|
|
||||||
/* Responsive */
|
/* Responsive */
|
||||||
@media (max-width: 640px) {
|
@media (max-width: 640px) {
|
||||||
|
.chat-window-outer {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.chat-window {
|
.chat-window {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 100vw;
|
max-width: 100vw;
|
||||||
|
|
@ -318,6 +479,19 @@ body[data-theme="light"] .chat-input {
|
||||||
border-radius: 16px 16px 0 0;
|
border-radius: 16px 16px 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chat-window-outer.with-sidebar .chat-window {
|
||||||
|
border-radius: 16px 16px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide sidebar on mobile */
|
||||||
|
.chat-sidebar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-sidebar-toggle {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.chat-input-area {
|
.chat-input-area {
|
||||||
padding-bottom: max(10px, env(safe-area-inset-bottom, 10px));
|
padding-bottom: max(10px, env(safe-area-inset-bottom, 10px));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,13 @@ export default function ChatWindow({
|
||||||
onClose,
|
onClose,
|
||||||
onMinimize,
|
onMinimize,
|
||||||
minimized = false,
|
minimized = false,
|
||||||
|
// Sidebar props
|
||||||
|
showSidebar = false,
|
||||||
|
friends = [],
|
||||||
|
presence = {},
|
||||||
|
unreadCounts = {},
|
||||||
|
onSelectFriend,
|
||||||
|
onToggleSidebar,
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [input, setInput] = useState("");
|
const [input, setInput] = useState("");
|
||||||
|
|
@ -106,6 +113,7 @@ export default function ChatWindow({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div className={"chat-window-outer" + (showSidebar ? " with-sidebar" : "")}>
|
||||||
<div className="chat-window">
|
<div className="chat-window">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="chat-header">
|
<div className="chat-header">
|
||||||
|
|
@ -125,6 +133,16 @@ export default function ChatWindow({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="chat-actions">
|
<div className="chat-actions">
|
||||||
|
{onToggleSidebar && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={"chat-sidebar-toggle" + (showSidebar ? " active" : "")}
|
||||||
|
onClick={onToggleSidebar}
|
||||||
|
title={t("common.contacts")}
|
||||||
|
>
|
||||||
|
<i className="fa-solid fa-users" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="chat-minimize"
|
className="chat-minimize"
|
||||||
|
|
@ -214,5 +232,39 @@ export default function ChatWindow({
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Friends Sidebar */}
|
||||||
|
{showSidebar && (
|
||||||
|
<div className="chat-sidebar">
|
||||||
|
<div className="chat-sidebar-list">
|
||||||
|
{friends.map((friend) => {
|
||||||
|
const friendUsername = friend.username || friend.name;
|
||||||
|
const avatar = friend.avatar_url || friend.avatar;
|
||||||
|
const isOnline = presence[friendUsername] || false;
|
||||||
|
const unread = unreadCounts[friendUsername] || 0;
|
||||||
|
const isActive = friendUsername === recipientName;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={friendUsername}
|
||||||
|
className={"chat-sidebar-item" + (isActive ? " active" : "") + (unread > 0 ? " has-unread" : "")}
|
||||||
|
onClick={() => onSelectFriend?.(friend)}
|
||||||
|
title={friendUsername}
|
||||||
|
>
|
||||||
|
<div className={"chat-sidebar-avatar" + (isOnline ? " online" : "")}>
|
||||||
|
{avatar ? (
|
||||||
|
<img src={avatar} alt={friendUsername} />
|
||||||
|
) : (
|
||||||
|
<span>{(friendUsername || "?")[0].toUpperCase()}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{unread > 0 && <span className="chat-sidebar-unread">{unread}</span>}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,7 @@ export default function RadialMenu({
|
||||||
|
|
||||||
const handleTimeClick = (hours) => {
|
const handleTimeClick = (hours) => {
|
||||||
onTime?.(hours);
|
onTime?.(hours);
|
||||||
|
setRightOpen(false); // Close menu after selection
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleWeatherClick = () => {
|
const handleWeatherClick = () => {
|
||||||
|
|
@ -206,7 +207,7 @@ export default function RadialMenu({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* RIGHT FAB - Time & Weather */}
|
{/* RIGHT FAB - Time & Weather */}
|
||||||
<div className={`fab-right ${rightOpen ? "is-open" : ""}`}>
|
<div className={`fab-right ${rightOpen ? "is-open" : ""}`} style={rightOpen ? { zIndex: 9000 } : undefined}>
|
||||||
<div className="fab-ring">
|
<div className="fab-ring">
|
||||||
{/* Time options */}
|
{/* Time options */}
|
||||||
{TIME_OPTIONS.map((t, i) => {
|
{TIME_OPTIONS.map((t, i) => {
|
||||||
|
|
|
||||||
|
|
@ -567,7 +567,7 @@ function openImageLightbox(src) {
|
||||||
overlay.style.position = "fixed";
|
overlay.style.position = "fixed";
|
||||||
overlay.style.inset = "0";
|
overlay.style.inset = "0";
|
||||||
overlay.style.background = "rgba(8,10,16,0.86)";
|
overlay.style.background = "rgba(8,10,16,0.86)";
|
||||||
overlay.style.zIndex = "10000000";
|
overlay.style.zIndex = "6000"; /* Image lightbox - above post modal (5000), below auth (10000) */
|
||||||
overlay.style.display = "flex";
|
overlay.style.display = "flex";
|
||||||
overlay.style.alignItems = "center";
|
overlay.style.alignItems = "center";
|
||||||
overlay.style.justifyContent = "center";
|
overlay.style.justifyContent = "center";
|
||||||
|
|
@ -997,7 +997,7 @@ function openCameraOverlay({ map, post, theme, fullScreen, interactionState }) {
|
||||||
backdrop.style.cssText = `
|
backdrop.style.cssText = `
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
z-index: 9999999;
|
z-index: 5000;
|
||||||
background: rgba(0,0,0,0.85);
|
background: rgba(0,0,0,0.85);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
@ -1218,7 +1218,7 @@ export function openCenteredOverlay({ map, post, theme, fullScreen = false }) {
|
||||||
if (fullScreen) backdrop.classList.add("sw-modal-fullscreen");
|
if (fullScreen) backdrop.classList.add("sw-modal-fullscreen");
|
||||||
backdrop.style.position = "fixed";
|
backdrop.style.position = "fixed";
|
||||||
backdrop.style.inset = "0";
|
backdrop.style.inset = "0";
|
||||||
backdrop.style.zIndex = "9999999";
|
backdrop.style.zIndex = "5000";
|
||||||
backdrop.style.background = "rgba(0,0,0,0.08)";
|
backdrop.style.background = "rgba(0,0,0,0.08)";
|
||||||
backdrop.style.backdropFilter = "blur(1px)";
|
backdrop.style.backdropFilter = "blur(1px)";
|
||||||
backdrop.style.display = "flex";
|
backdrop.style.display = "flex";
|
||||||
|
|
|
||||||
|
|
@ -194,7 +194,7 @@ export function openImageLightbox(src) {
|
||||||
overlay.style.position = "fixed";
|
overlay.style.position = "fixed";
|
||||||
overlay.style.inset = "0";
|
overlay.style.inset = "0";
|
||||||
overlay.style.background = "rgba(8,10,16,0.86)";
|
overlay.style.background = "rgba(8,10,16,0.86)";
|
||||||
overlay.style.zIndex = "10000000";
|
overlay.style.zIndex = "6000"; /* Image lightbox - above post modal (5000), below auth (10000) */
|
||||||
overlay.style.display = "flex";
|
overlay.style.display = "flex";
|
||||||
overlay.style.alignItems = "center";
|
overlay.style.alignItems = "center";
|
||||||
overlay.style.justifyContent = "center";
|
overlay.style.justifyContent = "center";
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { useCallback, useEffect, useRef, useState } from "react";
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
import { fetchPosts, fetchSmartFeed, fetchUnifiedSearch, resolveAssetRef } from "../../api/client";
|
import { fetchPosts, fetchSmartFeed, fetchUnifiedSearch, resolveAssetRef } from "../../api/client";
|
||||||
import { categoryCode, matchesSubFilter } from "./mapFilter";
|
import { categoryCode, matchesSubFilter, matchesTimeFilter, effectivePostTime } from "./mapFilter";
|
||||||
import { getCoords, getViewFromMap, haversineKm } from "./mapGeo";
|
import { getCoords, getViewFromMap, haversineKm } from "./mapGeo";
|
||||||
import { createMarkerForPost, createSimpleMarkerForPost } from "./markerManager";
|
import { createMarkerForPost, createSimpleMarkerForPost } from "./markerManager";
|
||||||
import { getTemplateKeyForPost } from "./templateSpecs";
|
import { getTemplateKeyForPost } from "./templateSpecs";
|
||||||
|
|
@ -875,6 +875,12 @@ export function usePostsEngine({
|
||||||
// Apply content filter (links, video, image, live, event)
|
// Apply content filter (links, video, image, live, event)
|
||||||
if (!matchesContentFilter(p, contentFilter)) return false;
|
if (!matchesContentFilter(p, contentFilter)) return false;
|
||||||
|
|
||||||
|
// Apply time filter
|
||||||
|
if (tf && tf > 0) {
|
||||||
|
const postTime = effectivePostTime(p);
|
||||||
|
if (!matchesTimeFilter(postTime, tf)) return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (isClusterFocus) {
|
if (isClusterFocus) {
|
||||||
const id = Number(getId(p));
|
const id = Number(getId(p));
|
||||||
const clusterPostId = Number(p?.cluster_post_id);
|
const clusterPostId = Number(p?.cluster_post_id);
|
||||||
|
|
@ -1521,12 +1527,14 @@ export function usePostsEngine({
|
||||||
|
|
||||||
const handlePostDelete = useCallback(
|
const handlePostDelete = useCallback(
|
||||||
(payload) => {
|
(payload) => {
|
||||||
const postId = payload?.post_id || payload?.postId || payload?.id;
|
const rawId = payload?.post_id || payload?.postId || payload?.id;
|
||||||
if (!postId) return;
|
if (!rawId) return;
|
||||||
|
const postId = Number(rawId); // Ensure numeric comparison
|
||||||
|
console.log("[PostsEngine] handlePostDelete postId=", postId);
|
||||||
|
|
||||||
const map = mapRef.current;
|
const map = mapRef.current;
|
||||||
const overlay = map?.__swCenteredOverlay;
|
const overlay = map?.__swCenteredOverlay;
|
||||||
if (overlay && overlay.postId === postId) {
|
if (overlay && Number(overlay.postId) === postId) {
|
||||||
// Remove event listeners
|
// Remove event listeners
|
||||||
try { if (overlay.onKey) window.removeEventListener("keydown", overlay.onKey); } catch {}
|
try { if (overlay.onKey) window.removeEventListener("keydown", overlay.onKey); } catch {}
|
||||||
try { if (overlay.onAuth) window.removeEventListener("sociowire:auth-changed", overlay.onAuth); } catch {}
|
try { if (overlay.onAuth) window.removeEventListener("sociowire:auth-changed", overlay.onAuth); } catch {}
|
||||||
|
|
@ -1544,13 +1552,13 @@ export function usePostsEngine({
|
||||||
map.__swCenteredOverlay = null;
|
map.__swCenteredOverlay = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
allPostsRef.current = (allPostsRef.current || []).filter((p) => (p?.id || p?.ID) !== postId);
|
allPostsRef.current = (allPostsRef.current || []).filter((p) => Number(p?.id || p?.ID) !== postId);
|
||||||
setVisiblePosts((current) => current.filter((p) => (p?.id || p?.ID) !== postId));
|
setVisiblePosts((current) => current.filter((p) => Number(p?.id || p?.ID) !== postId));
|
||||||
|
|
||||||
const markers = markersRef.current || [];
|
const markers = markersRef.current || [];
|
||||||
const nextMarkers = [];
|
const nextMarkers = [];
|
||||||
for (const item of markers) {
|
for (const item of markers) {
|
||||||
if (!item || item.id !== postId) {
|
if (!item || Number(item.id) !== postId) {
|
||||||
nextMarkers.push(item);
|
nextMarkers.push(item);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -157,7 +157,7 @@ export default function PostCard({ post, selected, onSelect, onPostDeleted, onPo
|
||||||
comment: post?.comment_count ?? 0,
|
comment: post?.comment_count ?? 0,
|
||||||
share: post?.share_count ?? 0,
|
share: post?.share_count ?? 0,
|
||||||
}));
|
}));
|
||||||
const { username, token } = useAuth();
|
const { username, token, userId } = useAuth();
|
||||||
|
|
||||||
// Edit/Delete state
|
// Edit/Delete state
|
||||||
const [showEditDelete, setShowEditDelete] = useState(false);
|
const [showEditDelete, setShowEditDelete] = useState(false);
|
||||||
|
|
@ -232,8 +232,11 @@ export default function PostCard({ post, selected, onSelect, onPostDeleted, onPo
|
||||||
const title = post?.title || "Untitled";
|
const title = post?.title || "Untitled";
|
||||||
const snippet = post?.snippet || "";
|
const snippet = post?.snippet || "";
|
||||||
const author = (post?.author || post?.Author || "anon").toString();
|
const author = (post?.author || post?.Author || "anon").toString();
|
||||||
|
const authorId = post?.author_id || post?.authorId || null;
|
||||||
|
// Check if current user is the author (by username OR by user ID)
|
||||||
const isAuthor =
|
const isAuthor =
|
||||||
username && author && author.toLowerCase() === username.toLowerCase();
|
(username && author && author.toLowerCase() === username.toLowerCase()) ||
|
||||||
|
(userId && authorId && Number(userId) === Number(authorId));
|
||||||
const [authorAvatar, setAuthorAvatar] = useState("");
|
const [authorAvatar, setAuthorAvatar] = useState("");
|
||||||
const sub = (post?.sub_category || post?.subCategory || "").toString();
|
const sub = (post?.sub_category || post?.subCategory || "").toString();
|
||||||
const contentType = (post?.content_type || "").toString().toLowerCase();
|
const contentType = (post?.content_type || "").toString().toLowerCase();
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
.auth-modal-backdrop {
|
.auth-modal-backdrop {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
z-index: 10000; /* Auth modal - highest priority */
|
z-index: 10000; /* Auth modal - above post modals (5000) and chat (8000) */
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background: rgba(2, 6, 23, 0.65);
|
background: rgba(2, 6, 23, 0.65);
|
||||||
backdrop-filter: blur(8px);
|
backdrop-filter: blur(8px);
|
||||||
|
isolation: isolate; /* Create new stacking context */
|
||||||
}
|
}
|
||||||
|
|
||||||
.auth-modal {
|
.auth-modal {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue