feat: Day/night less dark, enabled by default; remove weather widget
- Day/night terminator opacity reduced from 0.3 to 0.15 - Day/night enabled by default for new users - Removed weather button, widget, and modal from map - Kept blue theme as default 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
025a05063f
commit
1ff240802b
|
|
@ -59,12 +59,12 @@ export default function App() {
|
||||||
setTheme(saved);
|
setTheme(saved);
|
||||||
document.body.setAttribute("data-theme", saved);
|
document.body.setAttribute("data-theme", saved);
|
||||||
} else {
|
} else {
|
||||||
setTheme("light");
|
setTheme("blue");
|
||||||
document.body.setAttribute("data-theme", "light");
|
document.body.setAttribute("data-theme", "blue");
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setTheme("light");
|
setTheme("blue");
|
||||||
document.body.setAttribute("data-theme", "light");
|
document.body.setAttribute("data-theme", "blue");
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,8 @@ import "../../styles/mapMarkers.css";
|
||||||
import "../../styles/posts.css";
|
import "../../styles/posts.css";
|
||||||
import "../../styles/filters.css";
|
import "../../styles/filters.css";
|
||||||
|
|
||||||
import { createPost, fetchPostPreview, resolveAssetRef, fetchWeatherPosts } from "../../api/client";
|
import { createPost, fetchPostPreview, resolveAssetRef } from "../../api/client";
|
||||||
import { LiveKitBroadcast, LiveKitViewerModal } from "../Live";
|
import { LiveKitBroadcast, LiveKitViewerModal } from "../Live";
|
||||||
import { WeatherModal } from "../Weather";
|
|
||||||
import {
|
import {
|
||||||
FILTER_MAIN_CATEGORIES,
|
FILTER_MAIN_CATEGORIES,
|
||||||
FILTER_CATEGORY_MAP,
|
FILTER_CATEGORY_MAP,
|
||||||
|
|
@ -147,9 +146,11 @@ export default function MapView({
|
||||||
const WEATHER_VIEW_KEY = "sociowire:weatherView";
|
const WEATHER_VIEW_KEY = "sociowire:weatherView";
|
||||||
const [weatherViewEnabled, setWeatherViewEnabled] = useState(() => {
|
const [weatherViewEnabled, setWeatherViewEnabled] = useState(() => {
|
||||||
try {
|
try {
|
||||||
return localStorage.getItem(WEATHER_VIEW_KEY) === "true";
|
const saved = localStorage.getItem(WEATHER_VIEW_KEY);
|
||||||
|
// Default to true if not set
|
||||||
|
return saved === null ? true : saved === "true";
|
||||||
} catch {
|
} catch {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -196,11 +197,6 @@ export default function MapView({
|
||||||
const [showLiveBroadcast, setShowLiveBroadcast] = useState(false);
|
const [showLiveBroadcast, setShowLiveBroadcast] = useState(false);
|
||||||
const [activeLivePost, setActiveLivePost] = useState(null);
|
const [activeLivePost, setActiveLivePost] = useState(null);
|
||||||
const [liveCoords, setLiveCoords] = useState(null);
|
const [liveCoords, setLiveCoords] = useState(null);
|
||||||
// Weather modal state
|
|
||||||
const [activeWeatherPost, setActiveWeatherPost] = useState(null);
|
|
||||||
// Weather cities widget (top left under search)
|
|
||||||
const [weatherCities, setWeatherCities] = useState([]);
|
|
||||||
const [nearestWeather, setNearestWeather] = useState(null);
|
|
||||||
const openedPostRef = useRef(null);
|
const openedPostRef = useRef(null);
|
||||||
const queryPostRef = useRef(false);
|
const queryPostRef = useRef(false);
|
||||||
const overlayTimerRef = useRef(null);
|
const overlayTimerRef = useRef(null);
|
||||||
|
|
@ -1210,82 +1206,6 @@ export default function MapView({
|
||||||
return () => map.off("movestart", handleMove);
|
return () => map.off("movestart", handleMove);
|
||||||
}, [mapRef, searchQuery]);
|
}, [mapRef, searchQuery]);
|
||||||
|
|
||||||
// Fetch weather cities on mount
|
|
||||||
useEffect(() => {
|
|
||||||
const loadWeatherCities = async () => {
|
|
||||||
try {
|
|
||||||
const posts = await fetchWeatherPosts();
|
|
||||||
if (Array.isArray(posts) && posts.length > 0) {
|
|
||||||
// Parse weather data from posts
|
|
||||||
const cities = posts.map(p => {
|
|
||||||
const title = p.title || '';
|
|
||||||
const tempMatch = title.match(/:\s*(-?\d+)°/);
|
|
||||||
const cityMatch = title.match(/^[^\w]*([A-Za-zÀ-ÿ\s]+):/);
|
|
||||||
const temp = tempMatch ? parseInt(tempMatch[1], 10) : null;
|
|
||||||
const city = cityMatch ? cityMatch[1].trim() : '';
|
|
||||||
|
|
||||||
// Detect weather icon from emoji in title
|
|
||||||
let icon = '🌡️';
|
|
||||||
if (title.includes('☀️')) icon = '☀️';
|
|
||||||
else if (title.includes('🌙')) icon = '🌙';
|
|
||||||
else if (title.includes('⛅')) icon = '⛅';
|
|
||||||
else if (title.includes('☁️')) icon = '☁️';
|
|
||||||
else if (title.includes('🌫️')) icon = '🌫️';
|
|
||||||
else if (title.includes('🌧️')) icon = '🌧️';
|
|
||||||
else if (title.includes('❄️')) icon = '❄️';
|
|
||||||
else if (title.includes('🌦️')) icon = '🌦️';
|
|
||||||
else if (title.includes('🌨️')) icon = '🌨️';
|
|
||||||
else if (title.includes('⛈️')) icon = '⛈️';
|
|
||||||
|
|
||||||
return { ...p, city, temp, icon };
|
|
||||||
}).filter(c => c.city && c.temp !== null);
|
|
||||||
|
|
||||||
setWeatherCities(cities);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.warn('[Weather] Failed to load cities:', err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
loadWeatherCities();
|
|
||||||
// Refresh every 10 minutes
|
|
||||||
const interval = setInterval(loadWeatherCities, 10 * 60 * 1000);
|
|
||||||
return () => clearInterval(interval);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Find nearest weather city when map moves
|
|
||||||
useEffect(() => {
|
|
||||||
const map = mapRef.current;
|
|
||||||
if (!map || weatherCities.length === 0) return;
|
|
||||||
|
|
||||||
const updateNearest = () => {
|
|
||||||
try {
|
|
||||||
const center = map.getCenter();
|
|
||||||
if (!center) return;
|
|
||||||
|
|
||||||
let nearest = null;
|
|
||||||
let minDist = Infinity;
|
|
||||||
|
|
||||||
for (const w of weatherCities) {
|
|
||||||
if (w.lat == null || w.lon == null) continue;
|
|
||||||
const dist = Math.sqrt(
|
|
||||||
Math.pow(center.lat - w.lat, 2) + Math.pow(center.lng - w.lon, 2)
|
|
||||||
);
|
|
||||||
if (dist < minDist) {
|
|
||||||
minDist = dist;
|
|
||||||
nearest = w;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nearest) setNearestWeather(nearest);
|
|
||||||
} catch {}
|
|
||||||
};
|
|
||||||
|
|
||||||
updateNearest();
|
|
||||||
map.on("idle", updateNearest);
|
|
||||||
return () => map.off("idle", updateNearest);
|
|
||||||
}, [mapRef, weatherCities]);
|
|
||||||
|
|
||||||
const handleProfileVisitIsland = (island) => {
|
const handleProfileVisitIsland = (island) => {
|
||||||
console.log('[MapView] Visiting island from profile:', island.username);
|
console.log('[MapView] Visiting island from profile:', island.username);
|
||||||
setSelectedProfile(null); // Close profile modal
|
setSelectedProfile(null); // Close profile modal
|
||||||
|
|
@ -1801,7 +1721,7 @@ export default function MapView({
|
||||||
<div className="map-page">
|
<div className="map-page">
|
||||||
<div className="map-stage" ref={stageRef}>
|
<div className="map-stage" ref={stageRef}>
|
||||||
<div ref={containerRef} className="map-container" />
|
<div ref={containerRef} className="map-container" />
|
||||||
<DayNightTerminator map={mapRef.current} enabled={weatherViewEnabled} opacity={0.3} />
|
<DayNightTerminator map={mapRef.current} enabled={weatherViewEnabled} opacity={0.15} />
|
||||||
{status && <div className="map-status">{status}</div>}
|
{status && <div className="map-status">{status}</div>}
|
||||||
{debugEnabled && (
|
{debugEnabled && (
|
||||||
<>
|
<>
|
||||||
|
|
@ -1885,60 +1805,9 @@ export default function MapView({
|
||||||
>
|
>
|
||||||
<i className="fa-solid fa-umbrella-beach"></i>
|
<i className="fa-solid fa-umbrella-beach"></i>
|
||||||
</button>
|
</button>
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={"sw-search__iconBtn" + (weatherViewEnabled ? " is-active" : "")}
|
|
||||||
title="Météo & Ensoleillement"
|
|
||||||
onClick={() => setWeatherViewEnabled((prev) => !prev)}
|
|
||||||
>
|
|
||||||
<i className="fa-solid fa-cloud-sun"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* Weather widget - single nearest city - BELOW search bar */}
|
|
||||||
{nearestWeather && (() => {
|
|
||||||
const w = nearestWeather;
|
|
||||||
const isSunny = w.icon === '☀️' || w.icon === '⛅';
|
|
||||||
const isNight = w.icon === '🌙';
|
|
||||||
const isRainy = w.icon === '🌧️' || w.icon === '🌦️' || w.icon === '⛈️';
|
|
||||||
const isSnow = w.icon === '❄️' || w.icon === '🌨️';
|
|
||||||
const bg = isNight
|
|
||||||
? 'linear-gradient(135deg, rgba(30, 41, 59, 0.9), rgba(15, 23, 42, 0.9))'
|
|
||||||
: isSunny
|
|
||||||
? 'linear-gradient(135deg, rgba(56, 189, 248, 0.85), rgba(14, 165, 233, 0.85))'
|
|
||||||
: isRainy
|
|
||||||
? 'linear-gradient(135deg, rgba(71, 85, 105, 0.9), rgba(51, 65, 85, 0.9))'
|
|
||||||
: isSnow
|
|
||||||
? 'linear-gradient(135deg, rgba(148, 163, 184, 0.9), rgba(203, 213, 225, 0.9))'
|
|
||||||
: 'linear-gradient(135deg, rgba(100, 116, 139, 0.85), rgba(71, 85, 105, 0.85))';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
onClick={() => setActiveWeatherPost(w)}
|
|
||||||
style={{
|
|
||||||
display: 'inline-flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: '5px',
|
|
||||||
marginTop: '8px',
|
|
||||||
padding: '5px 12px',
|
|
||||||
borderRadius: '16px',
|
|
||||||
background: bg,
|
|
||||||
border: '1px solid rgba(255,255,255,0.25)',
|
|
||||||
color: isSnow ? '#1e293b' : '#fff',
|
|
||||||
fontSize: '13px',
|
|
||||||
fontWeight: '600',
|
|
||||||
cursor: 'pointer',
|
|
||||||
backdropFilter: 'blur(8px)',
|
|
||||||
boxShadow: '0 2px 8px rgba(0,0,0,0.2)',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span style={{ fontSize: '16px' }}>{w.icon}</span>
|
|
||||||
<span>{w.city}:</span>
|
|
||||||
<span style={{ fontWeight: '700' }}>{w.temp}°</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})()}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="map-overlay map-overlay-left">
|
<div className="map-overlay map-overlay-left">
|
||||||
|
|
@ -2235,15 +2104,6 @@ export default function MapView({
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{activeWeatherPost && (
|
|
||||||
<WeatherModal
|
|
||||||
postId={activeWeatherPost.id || activeWeatherPost.ID}
|
|
||||||
city={activeWeatherPost.city}
|
|
||||||
lat={activeWeatherPost.lat}
|
|
||||||
lon={activeWeatherPost.lon}
|
|
||||||
onClose={() => setActiveWeatherPost(null)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ body[data-theme="blue"] .post-pin--compact .post-pin-bubble {
|
||||||
border-color: rgba(96, 165, 250, 0.95);
|
border-color: rgba(96, 165, 250, 0.95);
|
||||||
}
|
}
|
||||||
|
|
||||||
body[data-theme="light"] { background:#f8fafc; color:#111827; }
|
body[data-theme="light"] { background:#e5e7eb; color:#111827; }
|
||||||
|
|
||||||
body[data-theme="light"] .sociowall-panel,
|
body[data-theme="light"] .sociowall-panel,
|
||||||
body[data-theme="light"] .chat-panel,
|
body[data-theme="light"] .chat-panel,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue