update frontend

This commit is contained in:
Your Name 2025-12-10 23:29:13 -05:00
parent 75b43a6b33
commit c4a6c3cddc
3 changed files with 137 additions and 93 deletions

View File

@ -1,30 +1,36 @@
import React from "react"; import React from "react";
import "./../../styles/filters.css"; import "../../styles/filters.css";
/**
* Vertical glossy filter buttons for the main categories.
* active: string in { "All", "News", "Friends", "Events", "Market" }
* onSelect(code): called with the category code.
*/
export default function FilterButtons({ active, onSelect }) {
const buttons = [
{ code: "All", label: "All", icon: "fa-solid fa-globe" },
{ code: "News", label: "News", icon: "fa-solid fa-newspaper" },
{ code: "Friends", label: "Friends", icon: "fa-solid fa-user-group" },
{ code: "Events", label: "Events", icon: "fa-solid fa-calendar-days" },
{ code: "Market", label: "Market", icon: "fa-solid fa-chart-line" },
];
export default function FilterButtons({ onSelect }) {
return ( return (
<div style={{ display: "flex", flexDirection: "column", gap: "0.6rem" }}> <div className="sw-filter-column">
{buttons.map((btn) => (
<button className="sw-filter-btn" onClick={() => onSelect("news")}> <button
<i className="fa-solid fa-newspaper"></i> key={btn.code}
<div className="sw-filter-label">News</div> type="button"
</button> className={
"sw-filter-btn" +
<button className="sw-filter-btn" onClick={() => onSelect("friends")}> (active === btn.code ? " sw-filter-active" : "")
<i className="fa-solid fa-user-group"></i> }
<div className="sw-filter-label">Friends</div> onClick={() => onSelect && onSelect(btn.code)}
</button> >
<i className={btn.icon} />
<button className="sw-filter-btn" onClick={() => onSelect("events")}> <div className="sw-filter-label">{btn.label}</div>
<i className="fa-solid fa-calendar-days"></i> </button>
<div className="sw-filter-label">Events</div> ))}
</button>
<button className="sw-filter-btn" onClick={() => onSelect("market")}>
<i className="fa-solid fa-chart-line"></i>
<div className="sw-filter-label">Market</div>
</button>
</div> </div>
); );
} }

View File

@ -3,6 +3,7 @@ import "maplibre-gl/dist/maplibre-gl.css";
import "../../styles/mapMarkers.css"; import "../../styles/mapMarkers.css";
import "../../styles/overlays.css"; import "../../styles/overlays.css";
import "../../styles/posts.css"; import "../../styles/posts.css";
import "../../styles/filters.css";
import { createPost } from "../../api/client"; import { createPost } from "../../api/client";
import { import {
@ -15,6 +16,7 @@ import { useUserPosition } from "./useUserPosition";
import { usePostsEngine } from "./usePostsEngine"; import { usePostsEngine } from "./usePostsEngine";
import PostList from "../Posts/PostList"; import PostList from "../Posts/PostList";
import { useAuth } from "../../auth/AuthContext"; import { useAuth } from "../../auth/AuthContext";
import FilterButtons from "../Filters/FilterButtons";
export default function MapView({ theme = "dark" }) { export default function MapView({ theme = "dark" }) {
const { authenticated, username, token } = useAuth(); const { authenticated, username, token } = useAuth();
@ -93,7 +95,7 @@ export default function MapView({ theme = "dark" }) {
handleIncomingPost(msg.post); handleIncomingPost(msg.post);
} }
} catch { } catch {
// ignore // ignore bad messages
} }
}; };
return () => ws && ws.close(); return () => ws && ws.close();
@ -137,7 +139,7 @@ export default function MapView({ theme = "dark" }) {
const authorName = (username || "").trim() || "anon"; const authorName = (username || "").trim() || "anon";
// Crosshair offset fix // Crosshair offset: adjust so big bubble sits correctly above pointer
let baseLng; let baseLng;
let baseLat; let baseLat;
@ -150,7 +152,8 @@ export default function MapView({ theme = "dark" }) {
baseLat = center.lat; baseLat = center.lat;
} }
const pixelOffsetY = -20; const pixelOffsetY = -20; // tweak if needed
const screenPoint = map.project([baseLng, baseLat]); const screenPoint = map.project([baseLng, baseLat]);
const correctedPoint = { const correctedPoint = {
x: screenPoint.x, x: screenPoint.x,
@ -181,6 +184,7 @@ export default function MapView({ theme = "dark" }) {
token token
); );
// Inject directly into the posts engine in addition to WebSocket broadcast
if (newPost && newPost.id) { if (newPost && newPost.id) {
handleIncomingPost(newPost); handleIncomingPost(newPost);
} }
@ -205,12 +209,18 @@ export default function MapView({ theme = "dark" }) {
} }
}; };
// Map right-side category selection from FilterButtons
const handleMainFilterSelect = (code) => {
if (!FILTER_MAIN_CATEGORIES.includes(code)) return;
setMainFilter(code);
};
return ( return (
<div className="map-view"> <div className="map-view">
<div ref={containerRef} className="map-container" /> <div ref={containerRef} className="map-container" />
{status && <div className="map-status">{status}</div>} {status && <div className="map-status">{status}</div>}
{/* TOP: Look / Place */} {/* TOP */}
<div className="map-overlay map-overlay-top"> <div className="map-overlay map-overlay-top">
<button className="chip-pill" onClick={handleSearchClick}> <button className="chip-pill" onClick={handleSearchClick}>
Look at Look at
@ -220,65 +230,50 @@ export default function MapView({ theme = "dark" }) {
</button> </button>
</div> </div>
{/* LEFT: time filters NOW / TODAY / RECENT / PAST */} {/* LEFT (time filters) */}
<div className="map-overlay game-filter-column game-filter-column-left"> <div className="map-overlay map-overlay-left">
{[ {[
["NOW", "Now"], ["NOW", "Now"],
["TODAY", "Today"], ["TODAY", "Today"],
["RECENT", "Recent"], ["RECENT", "Recently"],
["PAST", "Past"], ["PAST", "Past"],
].map(([code, label]) => ( ].map(([code, label]) => (
<div className="game-filter-item" key={code}>
<button
className={
"game-filter-circle" +
(timeFilter === code ? " game-filter-circle-active" : "")
}
onClick={() => setTimeFilter(code)}
>
<span>{label[0]}</span>
</button>
<span className="game-filter-label">{label}</span>
</div>
))}
</div>
{/* RIGHT: main categories ALL / NEWS / FRIENDS / EVENTS / MARKET */}
<div className="map-overlay game-filter-column game-filter-column-right">
{FILTER_MAIN_CATEGORIES.map((cat) => {
const label = cat;
return (
<div className="game-filter-item" key={cat}>
<button
className={
"game-filter-circle" +
(mainFilter === cat ? " game-filter-circle-active" : "")
}
onClick={() => setMainFilter(cat)}
>
<span>{label[0]}</span>
</button>
<span className="game-filter-label">{label}</span>
</div>
);
})}
</div>
{/* BOTTOM: subcategories */}
<div className="map-overlay map-overlay-bottom">
{bottomCategories.map((c) => (
<button <button
key={c} key={code}
className={ className={
subFilter === c ? "chip-egg chip-egg-active" : "chip-egg" timeFilter === code ? "chip-round chip-selected" : "chip-round"
} }
onClick={() => setSubFilter(c)} onClick={() => setTimeFilter(code)}
> >
{c} {label}
</button> </button>
))} ))}
</div> </div>
{/* RIGHT (main categories with glossy icons) */}
<div className="map-overlay map-overlay-right">
<FilterButtons active={mainFilter} onSelect={handleMainFilterSelect} />
</div>
{/* BOTTOM (subcategories glossy pills) */}
<div className="map-overlay map-overlay-bottom">
<div className="sw-bottom-bar">
{bottomCategories.map((c) => (
<button
key={c}
type="button"
className={
"sw-bottom-btn" +
(subFilter === c ? " sw-bottom-btn-active" : "")
}
onClick={() => setSubFilter(c)}
>
{c}
</button>
))}
</div>
</div>
{/* MY SPOT */} {/* MY SPOT */}
<div className="map-overlay map-overlay-myloc"> <div className="map-overlay map-overlay-myloc">
<button <button
@ -290,7 +285,7 @@ export default function MapView({ theme = "dark" }) {
</button> </button>
</div> </div>
{/* CROSSHAIR */} {/* CROSSHAIR when creating a post */}
{isCreating && ( {isCreating && (
<div className="map-overlay map-crosshair"> <div className="map-overlay map-crosshair">
<div className="crosshair-aim" /> <div className="crosshair-aim" />

View File

@ -1,37 +1,80 @@
/* ===== SocioWire glossy filter buttons ===== */
/* Vertical circular category buttons (right side) */
.sw-filter-column {
display: flex;
flex-direction: column;
gap: 0.6rem;
}
.sw-filter-btn { .sw-filter-btn {
width: 72px; width: 70px;
height: 72px; height: 70px;
border-radius: 50%; border-radius: 50%;
background: radial-gradient(circle at 30% 30%, #38bdf8, #1d4ed8, #0b1020); background: radial-gradient(circle at 30% 30%, #38bdf8, #1d4ed8, #020617);
border: 2px solid rgba(255,255,255,0.25); border: 2px solid rgba(255,255,255,0.2);
box-shadow: 0 4px 12px rgba(0,0,0,0.6); box-shadow: 0 4px 14px rgba(0,0,0,0.7);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
color: #fff; color: #e5f0ff;
cursor: pointer; cursor: pointer;
transition: transform .15s, box-shadow .15s; transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease;
}
.sw-filter-btn:hover {
transform: translateY(-3px);
box-shadow: 0 6px 20px rgba(0,0,0,0.75);
} }
.sw-filter-btn i { .sw-filter-btn i {
font-size: 26px; font-size: 24px;
margin-bottom: 4px; margin-bottom: 2px;
} }
.sw-filter-label { .sw-filter-label {
font-size: 0.7rem; font-size: 0.7rem;
margin-top: 4px; line-height: 1;
text-align: center; text-shadow: 0 1px 2px rgba(0,0,0,0.9);
color: #dbeafe; }
.sw-filter-btn:hover {
transform: translateY(-3px);
box-shadow: 0 7px 20px rgba(0,0,0,0.8);
} }
.sw-filter-active { .sw-filter-active {
border-color: #38bdf8; border-color: #38bdf8;
box-shadow: 0 0 16px #38bdf8; box-shadow: 0 0 18px rgba(56,189,248,0.9);
}
/* ===== Bottom sub-category glossy pills ===== */
.sw-bottom-bar {
display: flex;
gap: 0.4rem;
padding: 0.2rem 0.6rem;
background: radial-gradient(circle at 0% 0%, rgba(15,23,42,0.96), rgba(15,23,42,0.85));
border-radius: 999px;
box-shadow: 0 4px 16px rgba(0,0,0,0.75);
}
.sw-bottom-btn {
border-radius: 999px;
padding: 0.25rem 0.75rem;
font-size: 0.75rem;
border: 1px solid rgba(148,163,184,0.6);
background: rgba(15,23,42,0.9);
color: #e5e7eb;
cursor: pointer;
white-space: nowrap;
transition: background .12s ease, transform .12s ease, box-shadow .12s ease;
}
.sw-bottom-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 10px rgba(0,0,0,0.7);
}
.sw-bottom-btn-active {
background: linear-gradient(135deg, #38bdf8, #1d4ed8);
border-color: #93c5fd;
color: #0b1120;
font-weight: 600;
} }