Improve map UX, theming, and privacy page
This commit is contained in:
parent
55ecfbd34e
commit
909715c2d7
|
|
@ -17,6 +17,7 @@
|
|||
<meta property="og:image" content="https://sociowire.com/icons/og-image.png" />
|
||||
<meta property="og:url" content="https://sociowire.com" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta name="facebook-domain-verification" content="o4iph92mh9fgqx58sn1b77ia08h8cp" />
|
||||
|
||||
<!-- Twitter Card -->
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,113 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>SocioWire Privacy Policy</title>
|
||||
<style>
|
||||
:root {
|
||||
color-scheme: light;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: "Georgia", "Times New Roman", serif;
|
||||
background: #f8fafc;
|
||||
color: #0f172a;
|
||||
}
|
||||
main {
|
||||
max-width: 820px;
|
||||
margin: 0 auto;
|
||||
padding: 48px 24px 72px;
|
||||
}
|
||||
h1 {
|
||||
font-size: 34px;
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 18px;
|
||||
margin: 28px 0 8px;
|
||||
}
|
||||
p, li {
|
||||
line-height: 1.6;
|
||||
font-size: 16px;
|
||||
}
|
||||
ul {
|
||||
padding-left: 20px;
|
||||
}
|
||||
.meta {
|
||||
font-size: 14px;
|
||||
color: #475569;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.card {
|
||||
background: #ffffff;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 14px;
|
||||
padding: 20px 22px;
|
||||
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.08);
|
||||
}
|
||||
a {
|
||||
color: #1d4ed8;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<div class="card">
|
||||
<h1>Privacy Policy</h1>
|
||||
<div class="meta">Last updated: 2025-12-27</div>
|
||||
|
||||
<p>
|
||||
SocioWire respects your privacy. This policy explains what data we collect, how we use it,
|
||||
and your choices.
|
||||
</p>
|
||||
|
||||
<h2>What we collect</h2>
|
||||
<ul>
|
||||
<li>Account data you provide (username, email, profile image).</li>
|
||||
<li>Content you create (posts, comments, media, location selections).</li>
|
||||
<li>Usage data (views, likes, shares, and basic analytics).</li>
|
||||
<li>Device and browser data for security and performance.</li>
|
||||
</ul>
|
||||
|
||||
<h2>How we use data</h2>
|
||||
<ul>
|
||||
<li>To operate the platform and deliver core features.</li>
|
||||
<li>To personalize your feed and recommendations.</li>
|
||||
<li>To improve safety, moderation, and service reliability.</li>
|
||||
<li>To provide analytics for performance and growth.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Sharing</h2>
|
||||
<p>
|
||||
We do not sell personal data. We share data only with service providers required to run
|
||||
SocioWire (hosting, analytics, storage) or when required by law.
|
||||
</p>
|
||||
|
||||
<h2>Public vs private content</h2>
|
||||
<p>
|
||||
Content marked as public is visible to anyone. Private, group, or custom content is limited
|
||||
to the audience you select.
|
||||
</p>
|
||||
|
||||
<h2>Data retention</h2>
|
||||
<p>
|
||||
We keep data as long as necessary to provide the service. You can request deletion of your
|
||||
account or content.
|
||||
</p>
|
||||
|
||||
<h2>Your choices</h2>
|
||||
<ul>
|
||||
<li>Update your profile or privacy settings at any time.</li>
|
||||
<li>Control who can see your posts and media.</li>
|
||||
<li>Request deletion by contacting us.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Contact</h2>
|
||||
<p>
|
||||
Email: <a href="mailto:support@sociowire.com">support@sociowire.com</a>
|
||||
</p>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -565,11 +565,14 @@ export default function TopBar({ theme = "dark", onChangeTheme, onIslandsClick,
|
|||
<button
|
||||
type="button"
|
||||
className="btn-oauth btn-oauth-facebook"
|
||||
disabled={true}
|
||||
title="Facebook SSO coming soon"
|
||||
disabled={loading}
|
||||
onClick={() => {
|
||||
trackEvent("login_provider_click", { provider: "facebook" });
|
||||
loginWithProvider && loginWithProvider("facebook");
|
||||
}}
|
||||
>
|
||||
<i className="fa-brands fa-facebook-f"></i>
|
||||
<span>Facebook (soon)</span>
|
||||
<span>Continue with Facebook</span>
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -134,6 +134,7 @@ export default function MapView({
|
|||
const [uploadingImage, setUploadingImage] = useState(false);
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const [saveError, setSaveError] = useState("");
|
||||
const [createStep, setCreateStep] = useState(0);
|
||||
|
||||
const bottomCategories = FILTER_CATEGORY_MAP[mainFilter] || ["All"];
|
||||
|
||||
|
|
@ -559,6 +560,7 @@ export default function MapView({
|
|||
}
|
||||
|
||||
setIsCreating(true);
|
||||
setCreateStep(0);
|
||||
setDraftTitle("");
|
||||
setDraftBody("");
|
||||
setDraftImage("");
|
||||
|
|
@ -866,157 +868,189 @@ export default function MapView({
|
|||
</button>
|
||||
</div>
|
||||
|
||||
<span className="crosshair-label">
|
||||
Move the map, aim with the crosshair — your wire will be pinned
|
||||
there.
|
||||
</span>
|
||||
|
||||
<div className="create-row">
|
||||
<select
|
||||
value={draftCategory}
|
||||
onChange={(e) => {
|
||||
const cat = e.target.value;
|
||||
setDraftCategory(cat);
|
||||
setDraftSubCategory(
|
||||
(CREATE_CATEGORY_MAP[cat] || CREATE_CATEGORY_MAP.News)[0]
|
||||
);
|
||||
<div className="create-stepper">
|
||||
{["Pin", "Details", "Share"].map((label, idx) => (
|
||||
<div
|
||||
key={label}
|
||||
className={
|
||||
"create-step" +
|
||||
(idx === createStep ? " create-step-current" : "") +
|
||||
(idx <= createStep ? " create-step-active" : "")
|
||||
}
|
||||
>
|
||||
<div className="create-step-dot">{idx + 1}</div>
|
||||
<div className="create-step-label">{label}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="create-step-progress">
|
||||
<span
|
||||
style={{
|
||||
width: `${(createStep / 2) * 100}%`,
|
||||
}}
|
||||
>
|
||||
{["News", "Friends", "Events", "Market"].map((cat) => (
|
||||
<option key={cat}>{cat}</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
<select
|
||||
value={draftSubCategory}
|
||||
onChange={(e) => setDraftSubCategory(e.target.value)}
|
||||
>
|
||||
{(CREATE_CATEGORY_MAP[draftCategory] ||
|
||||
CREATE_CATEGORY_MAP.News
|
||||
).map((sub) => (
|
||||
<option key={sub}>{sub}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<input
|
||||
className="create-input"
|
||||
placeholder="Short title..."
|
||||
value={draftTitle}
|
||||
onChange={(e) => setDraftTitle(e.target.value)}
|
||||
/>
|
||||
|
||||
<textarea
|
||||
className="create-textarea"
|
||||
rows={2}
|
||||
placeholder="Context (optional)..."
|
||||
value={draftBody}
|
||||
onChange={(e) => setDraftBody(e.target.value)}
|
||||
/>
|
||||
|
||||
<div className="create-privacy-row">
|
||||
<label>Post visibility</label>
|
||||
<select
|
||||
value={postVisibility}
|
||||
onChange={(e) => {
|
||||
const next = e.target.value;
|
||||
setPostVisibility(next);
|
||||
if (next !== "group") setPostGroupId("");
|
||||
if (next !== "custom") setPostUsers("");
|
||||
}}
|
||||
>
|
||||
<option value="public">Public</option>
|
||||
<option value="private">Private</option>
|
||||
<option value="group">Group</option>
|
||||
<option value="custom">Custom</option>
|
||||
</select>
|
||||
</div>
|
||||
{postVisibility === "group" ? (
|
||||
<input
|
||||
className="create-input"
|
||||
placeholder="Group ID"
|
||||
value={postGroupId}
|
||||
onChange={(e) => setPostGroupId(e.target.value)}
|
||||
/>
|
||||
) : null}
|
||||
{postVisibility === "custom" ? (
|
||||
<input
|
||||
className="create-input"
|
||||
placeholder="Usernames (comma separated)"
|
||||
value={postUsers}
|
||||
onChange={(e) => setPostUsers(e.target.value)}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<div className="create-image-option">
|
||||
<label style={{ display: "flex", alignItems: "center", gap: "8px", cursor: "pointer" }}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={useCustomImage}
|
||||
onChange={(e) => setUseCustomImage(e.target.checked)}
|
||||
/>
|
||||
<span>Use custom image instead of category icon</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{useCustomImage && (
|
||||
<div className="create-image-panel">
|
||||
<div style={{ display: "flex", gap: "8px" }}>
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onChange={handleImageFileChange}
|
||||
disabled={uploadingImage}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
{uploadingImage && <span>Uploading...</span>}
|
||||
{createStep === 0 && (
|
||||
<div className="create-step-body">
|
||||
<span className="crosshair-label">
|
||||
Move the map, aim with the crosshair — your wire will be pinned
|
||||
there.
|
||||
</span>
|
||||
|
||||
<div className="create-row">
|
||||
<select
|
||||
value={draftCategory}
|
||||
onChange={(e) => {
|
||||
const cat = e.target.value;
|
||||
setDraftCategory(cat);
|
||||
setDraftSubCategory(
|
||||
(CREATE_CATEGORY_MAP[cat] || CREATE_CATEGORY_MAP.News)[0]
|
||||
);
|
||||
}}
|
||||
>
|
||||
{["News", "Friends", "Events", "Market"].map((cat) => (
|
||||
<option key={cat}>{cat}</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
<select
|
||||
value={draftSubCategory}
|
||||
onChange={(e) => setDraftSubCategory(e.target.value)}
|
||||
>
|
||||
{(CREATE_CATEGORY_MAP[draftCategory] ||
|
||||
CREATE_CATEGORY_MAP.News
|
||||
).map((sub) => (
|
||||
<option key={sub}>{sub}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{draftImagePreview && (
|
||||
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
|
||||
<img
|
||||
src={draftImagePreview}
|
||||
alt="Preview"
|
||||
style={{ width: "40px", height: "40px", objectFit: "cover", borderRadius: "6px" }}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setDraftImageFile(null);
|
||||
setDraftImagePreview("");
|
||||
setDraftImage("");
|
||||
}}
|
||||
style={{ padding: "4px 8px", fontSize: "11px" }}
|
||||
>
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div style={{ fontSize: "12px", color: "#888" }}>
|
||||
Or paste an image URL:
|
||||
</div>
|
||||
<input
|
||||
className="create-input"
|
||||
placeholder="https://example.com/image.jpg"
|
||||
value={draftImageFile ? "" : draftImage}
|
||||
onChange={(e) => {
|
||||
setDraftImage(e.target.value);
|
||||
setDraftImageFile(null);
|
||||
setDraftImagePreview("");
|
||||
}}
|
||||
disabled={!!draftImageFile}
|
||||
placeholder="Short title..."
|
||||
value={draftTitle}
|
||||
onChange={(e) => setDraftTitle(e.target.value)}
|
||||
/>
|
||||
<div className="create-hint">Give it a clear, punchy headline.</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{createStep === 1 && (
|
||||
<div className="create-step-body">
|
||||
<textarea
|
||||
className="create-textarea"
|
||||
rows={3}
|
||||
placeholder="Context (optional)..."
|
||||
value={draftBody}
|
||||
onChange={(e) => setDraftBody(e.target.value)}
|
||||
/>
|
||||
|
||||
<div className="create-image-option">
|
||||
<label className="create-checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={useCustomImage}
|
||||
onChange={(e) => setUseCustomImage(e.target.checked)}
|
||||
/>
|
||||
<span>Use a custom image instead of the category icon</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{useCustomImage && (
|
||||
<div className="create-image-panel">
|
||||
<div className="create-image-row">
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onChange={handleImageFileChange}
|
||||
disabled={uploadingImage}
|
||||
/>
|
||||
{uploadingImage && (
|
||||
<span className="create-uploading">Uploading...</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{draftImagePreview && (
|
||||
<div className="create-image-preview-row">
|
||||
<img src={draftImagePreview} alt="Preview" />
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setDraftImageFile(null);
|
||||
setDraftImagePreview("");
|
||||
setDraftImage("");
|
||||
}}
|
||||
>
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="create-muted">
|
||||
Or paste an image URL:
|
||||
</div>
|
||||
<input
|
||||
className="create-input"
|
||||
placeholder="https://example.com/image.jpg"
|
||||
value={draftImageFile ? "" : draftImage}
|
||||
onChange={(e) => {
|
||||
setDraftImage(e.target.value);
|
||||
setDraftImageFile(null);
|
||||
setDraftImagePreview("");
|
||||
}}
|
||||
disabled={!!draftImageFile}
|
||||
/>
|
||||
|
||||
<div className="create-privacy-row">
|
||||
<label>Image visibility</label>
|
||||
<select
|
||||
value={imageVisibility}
|
||||
onChange={(e) => {
|
||||
const next = e.target.value;
|
||||
setImageVisibility(next);
|
||||
if (next !== "group") setImageGroupId("");
|
||||
if (next !== "custom") setImageUsers("");
|
||||
}}
|
||||
>
|
||||
<option value="public">Public</option>
|
||||
<option value="private">Private</option>
|
||||
<option value="group">Group</option>
|
||||
<option value="custom">Custom</option>
|
||||
</select>
|
||||
</div>
|
||||
{imageVisibility === "group" ? (
|
||||
<input
|
||||
className="create-input"
|
||||
placeholder="Group ID"
|
||||
value={imageGroupId}
|
||||
onChange={(e) => setImageGroupId(e.target.value)}
|
||||
/>
|
||||
) : null}
|
||||
{imageVisibility === "custom" ? (
|
||||
<input
|
||||
className="create-input"
|
||||
placeholder="Usernames (comma separated)"
|
||||
value={imageUsers}
|
||||
onChange={(e) => setImageUsers(e.target.value)}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{createStep === 2 && (
|
||||
<div className="create-step-body">
|
||||
<div className="create-privacy-row">
|
||||
<label>Image visibility</label>
|
||||
<label>Post visibility</label>
|
||||
<select
|
||||
value={imageVisibility}
|
||||
value={postVisibility}
|
||||
onChange={(e) => {
|
||||
const next = e.target.value;
|
||||
setImageVisibility(next);
|
||||
if (next !== "group") setImageGroupId("");
|
||||
if (next !== "custom") setImageUsers("");
|
||||
setPostVisibility(next);
|
||||
if (next !== "group") setPostGroupId("");
|
||||
if (next !== "custom") setPostUsers("");
|
||||
}}
|
||||
>
|
||||
<option value="public">Public</option>
|
||||
|
|
@ -1025,22 +1059,48 @@ export default function MapView({
|
|||
<option value="custom">Custom</option>
|
||||
</select>
|
||||
</div>
|
||||
{imageVisibility === "group" ? (
|
||||
{postVisibility === "group" ? (
|
||||
<input
|
||||
className="create-input"
|
||||
placeholder="Group ID"
|
||||
value={imageGroupId}
|
||||
onChange={(e) => setImageGroupId(e.target.value)}
|
||||
value={postGroupId}
|
||||
onChange={(e) => setPostGroupId(e.target.value)}
|
||||
/>
|
||||
) : null}
|
||||
{imageVisibility === "custom" ? (
|
||||
{postVisibility === "custom" ? (
|
||||
<input
|
||||
className="create-input"
|
||||
placeholder="Usernames (comma separated)"
|
||||
value={imageUsers}
|
||||
onChange={(e) => setImageUsers(e.target.value)}
|
||||
value={postUsers}
|
||||
onChange={(e) => setPostUsers(e.target.value)}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<div className="create-review-card">
|
||||
<div className="create-review-media">
|
||||
{useCustomImage && (draftImagePreview || draftImage) ? (
|
||||
<img src={draftImagePreview || draftImage} alt="Preview" />
|
||||
) : (
|
||||
<div className="create-review-icon">
|
||||
<i className={getSubcatIcon(draftCategory, draftSubCategory)} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="create-review-meta">
|
||||
<div className="create-review-title">
|
||||
{draftTitle || "Untitled wire"}
|
||||
</div>
|
||||
<div className="create-review-sub">
|
||||
{draftCategory} · {draftSubCategory}
|
||||
</div>
|
||||
<div className="create-review-body">
|
||||
{draftBody || "Add a quick line of context if you want."}
|
||||
</div>
|
||||
<div className="create-review-visibility">
|
||||
Visibility: {postVisibility}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
@ -1048,12 +1108,33 @@ export default function MapView({
|
|||
|
||||
<div className="create-actions">
|
||||
<button
|
||||
className="chip-pill"
|
||||
onClick={handleSubmitPost}
|
||||
disabled={isSaving}
|
||||
type="button"
|
||||
className="create-back"
|
||||
onClick={() => setCreateStep((s) => Math.max(0, s - 1))}
|
||||
disabled={createStep === 0}
|
||||
>
|
||||
{isSaving ? "Posting..." : "Post"}
|
||||
Back
|
||||
</button>
|
||||
<div className="create-actions-right">
|
||||
{createStep < 2 ? (
|
||||
<button
|
||||
type="button"
|
||||
className="chip-pill"
|
||||
onClick={() => setCreateStep((s) => Math.min(2, s + 1))}
|
||||
disabled={!draftTitle.trim() || uploadingImage}
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
className="chip-pill"
|
||||
onClick={handleSubmitPost}
|
||||
disabled={isSaving}
|
||||
>
|
||||
{isSaving ? "Posting..." : "Post"}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ const SW_ANIM_MS = 840;
|
|||
|
||||
import CardRenderer from "../Cards/CardRenderer";
|
||||
import { cardTokens } from "../../theme/cardTokens";
|
||||
import { getTemplateSpecForPost, adaptPostToTemplateData } from "./templateSpecs";
|
||||
import { getTemplateSpecForPost, getTemplateKeyForPost, adaptPostToTemplateData } from "./templateSpecs";
|
||||
import { getMarkerColorForCategory } from "./postPriority";
|
||||
import { loadAuth } from "../../auth/authStorage";
|
||||
import {
|
||||
|
|
@ -40,6 +40,44 @@ function applyAvatar(el, username, url) {
|
|||
}
|
||||
}
|
||||
|
||||
function resolvePostImage(post) {
|
||||
return normalizeImageUrl(
|
||||
post?.image_small ||
|
||||
post?.image ||
|
||||
post?.image_large ||
|
||||
post?.media_url ||
|
||||
post?.image_url ||
|
||||
""
|
||||
);
|
||||
}
|
||||
|
||||
function refreshMarkerFromPost(map, postId, fresh) {
|
||||
if (!map || !fresh) return;
|
||||
const ref = map.__swMarkersRef;
|
||||
const list = ref?.current || [];
|
||||
for (const item of list) {
|
||||
if (!item || item.id !== postId) continue;
|
||||
const nextPost = { ...(item.post || {}), ...(fresh || {}) };
|
||||
item.post = nextPost;
|
||||
if (!item.el) continue;
|
||||
item.el.__post = nextPost;
|
||||
const nextKey = getTemplateKeyForPost(nextPost);
|
||||
const nextImg = resolvePostImage(nextPost);
|
||||
const nextTitle = (nextPost?.title || "").toString();
|
||||
if (
|
||||
item.el.__lastTemplateKey !== nextKey ||
|
||||
item.el.__lastImage !== nextImg ||
|
||||
item.el.__lastTitle !== nextTitle
|
||||
) {
|
||||
if (item.type === "simple" && item.el.__renderSimple) {
|
||||
try { item.el.__renderSimple(); } catch {}
|
||||
} else if (item.el.__renderCompact) {
|
||||
try { item.el.__renderCompact(); } catch {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function loadAvatar(username) {
|
||||
const key = (username || "").toLowerCase().trim();
|
||||
if (!key) return "";
|
||||
|
|
@ -514,6 +552,7 @@ export function openCenteredOverlay({ map, post, theme, fullScreen = false }) {
|
|||
let postData = { ...(post || {}) };
|
||||
const data = adaptPostToTemplateData(postData);
|
||||
const cat = normCatLabel(postData);
|
||||
const templateKey = getTemplateKeyForPost(postData);
|
||||
const relTime = formatRelativeTime(getPostTime(postData));
|
||||
const author = (postData?.author || postData?.Author || "").toString().trim();
|
||||
const sub = (postData?.sub_category || postData?.subCategory || "").toString().trim();
|
||||
|
|
@ -561,7 +600,7 @@ export function openCenteredOverlay({ map, post, theme, fullScreen = false }) {
|
|||
|
||||
// Keep size behavior from your CSS (max-width/min(92vw,...), max-height etc.)
|
||||
modalWrap.innerHTML = `
|
||||
<div class="post-card sw-expanded-shell sw-modal">
|
||||
<div class="post-card sw-expanded-shell sw-modal" data-template="${escapeHtml(templateKey)}" data-category="${escapeHtml((postData?.category || "").toString().toUpperCase())}">
|
||||
<div class="sw-modal-toprow">
|
||||
<div class="sw-modal-left">
|
||||
<div class="sw-modal-badge">${escapeHtml(cat)}</div>
|
||||
|
|
@ -830,6 +869,7 @@ export function openCenteredOverlay({ map, post, theme, fullScreen = false }) {
|
|||
if (!fresh) return;
|
||||
postData = { ...postData, ...fresh };
|
||||
applyPostDetails(modalWrap, postData);
|
||||
refreshMarkerFromPost(map, postID, postData);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -1027,6 +1067,7 @@ export function createMarkerForPost(post, mapRef, markersRef, expandedElRef, the
|
|||
requestAnimationFrame(() => root.classList.add("sw-appear-in"));
|
||||
root.className = "post-pin post-pin--compact";
|
||||
root.style.zIndex = "1";
|
||||
root.__post = post;
|
||||
|
||||
function unmountIfAny() {
|
||||
if (root.__swUnmount) {
|
||||
|
|
@ -1053,9 +1094,13 @@ export function createMarkerForPost(post, mapRef, markersRef, expandedElRef, the
|
|||
|
||||
const wrap = root.querySelector(".sw-template-mini-wrap");
|
||||
if (wrap) {
|
||||
const spec = getTemplateSpecForPost(post, "mini");
|
||||
const data = adaptPostToTemplateData(post);
|
||||
root.__swUnmount = mountCard(wrap, spec, data, theme, post);
|
||||
const activePost = root.__post || post;
|
||||
const spec = getTemplateSpecForPost(activePost, "mini");
|
||||
const data = adaptPostToTemplateData(activePost);
|
||||
root.__lastTemplateKey = getTemplateKeyForPost(activePost);
|
||||
root.__lastImage = data?.image || "";
|
||||
root.__lastTitle = data?.headline || "";
|
||||
root.__swUnmount = mountCard(wrap, spec, data, theme, activePost);
|
||||
}
|
||||
|
||||
clearOcclusion(markersRef);
|
||||
|
|
@ -1116,11 +1161,6 @@ export function createSimpleMarkerForPost(post, mapRef, markersRef, expandedElRe
|
|||
const lngLat = [lon, lat];
|
||||
|
||||
// Pas d'offset pour les marqueurs simples (ils sont petits)
|
||||
const color = getMarkerColorForCategory(post?.category);
|
||||
const img = normalizeImageUrl(
|
||||
post?.image_small || post?.image || post?.image_large || post?.media_url || "",
|
||||
);
|
||||
|
||||
const root = document.createElement("div");
|
||||
root.classList.add("sw-appear");
|
||||
requestAnimationFrame(() => root.classList.add("sw-appear-in"));
|
||||
|
|
@ -1131,73 +1171,88 @@ export function createSimpleMarkerForPost(post, mapRef, markersRef, expandedElRe
|
|||
root.style.display = "flex";
|
||||
root.style.flexDirection = "column";
|
||||
root.style.alignItems = "center";
|
||||
root.__post = post;
|
||||
|
||||
// Marqueur pin avec image en haut et pointe en bas
|
||||
root.innerHTML = `
|
||||
<div class="sw-simple-marker" style="
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: ${color};
|
||||
border: 2px solid white;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
z-index: 2;
|
||||
function renderSimple() {
|
||||
const activePost = root.__post || post;
|
||||
const color = getMarkerColorForCategory(activePost?.category);
|
||||
const img = normalizeImageUrl(
|
||||
activePost?.image_small || activePost?.image || activePost?.image_large || activePost?.media_url || "",
|
||||
);
|
||||
root.__lastTemplateKey = getTemplateKeyForPost(activePost);
|
||||
root.__lastImage = img || "";
|
||||
root.__lastTitle = (activePost?.title || "").toString();
|
||||
|
||||
// Marqueur pin avec image en haut et pointe en bas
|
||||
root.innerHTML = `
|
||||
<div class="sw-simple-marker" style="
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: ${color};
|
||||
border: 2px solid white;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
z-index: 2;
|
||||
">
|
||||
${img ? `<img src="${img}" alt="" style="width:100%;height:100%;object-fit:cover;" />` : `
|
||||
<div style="
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
background: white;
|
||||
opacity: 0.9;
|
||||
"></div>
|
||||
`}
|
||||
</div>
|
||||
<div class="sw-simple-pin-point" style="
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 8px solid transparent;
|
||||
border-right: 8px solid transparent;
|
||||
border-top: 16px solid ${color};
|
||||
filter: drop-shadow(0 2px 4px rgba(0,0,0,0.3));
|
||||
margin-top: -2px;
|
||||
z-index: 1;
|
||||
"></div>
|
||||
<div class="sw-simple-hover-card" style="
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%) translateY(-6px) scale(0.9);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.2s ease, transform 0.2s ease;
|
||||
z-index: 1000;
|
||||
min-width: 200px;
|
||||
max-width: 280px;
|
||||
background: rgba(8, 20, 40, 0.97);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid rgba(90, 190, 255, 0.7);
|
||||
box-shadow: 0 4px 20px rgba(0,0,0,0.5);
|
||||
white-space: normal;
|
||||
">
|
||||
${img ? `<img src="${img}" alt="" style="width:100%;height:100%;object-fit:cover;" />` : `
|
||||
<div style="
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
background: white;
|
||||
opacity: 0.9;
|
||||
"></div>
|
||||
`}
|
||||
<div style="font-weight: 900; font-size: 12px; color: #e8f5ff; margin-bottom: 4px;">
|
||||
${escapeHtml(activePost?.title || "Untitled")}
|
||||
</div>
|
||||
<div style="font-size: 10px; color: rgba(232, 245, 255, 0.8);">
|
||||
${escapeHtml((activePost?.snippet || "").substring(0, 80))}${(activePost?.snippet || "").length > 80 ? "..." : ""}
|
||||
</div>
|
||||
<div style="margin-top: 6px; font-size: 9px; color: rgba(232, 245, 255, 0.6);">
|
||||
${normCatLabel(activePost)} • ${formatRelativeTime(activePost?.created_at || activePost?.CreatedAt)}
|
||||
</div>
|
||||
</div>
|
||||
<div class="sw-simple-pin-point" style="
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 8px solid transparent;
|
||||
border-right: 8px solid transparent;
|
||||
border-top: 16px solid ${color};
|
||||
filter: drop-shadow(0 2px 4px rgba(0,0,0,0.3));
|
||||
margin-top: -2px;
|
||||
z-index: 1;
|
||||
"></div>
|
||||
<div class="sw-simple-hover-card" style="
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%) translateY(-6px) scale(0.9);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.2s ease, transform 0.2s ease;
|
||||
z-index: 1000;
|
||||
min-width: 200px;
|
||||
max-width: 280px;
|
||||
background: rgba(8, 20, 40, 0.97);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid rgba(90, 190, 255, 0.7);
|
||||
box-shadow: 0 4px 20px rgba(0,0,0,0.5);
|
||||
white-space: normal;
|
||||
">
|
||||
<div style="font-weight: 900; font-size: 12px; color: #e8f5ff; margin-bottom: 4px;">
|
||||
${escapeHtml(post?.title || "Untitled")}
|
||||
</div>
|
||||
<div style="font-size: 10px; color: rgba(232, 245, 255, 0.8);">
|
||||
${escapeHtml((post?.snippet || "").substring(0, 80))}${(post?.snippet || "").length > 80 ? "..." : ""}
|
||||
</div>
|
||||
<div style="margin-top: 6px; font-size: 9px; color: rgba(232, 245, 255, 0.6);">
|
||||
${normCatLabel(post)} • ${formatRelativeTime(post?.created_at || post?.CreatedAt)}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
`;
|
||||
}
|
||||
|
||||
renderSimple();
|
||||
root.__renderSimple = renderSimple;
|
||||
|
||||
const markerEl = root.querySelector(".sw-simple-marker");
|
||||
const hoverCard = root.querySelector(".sw-simple-hover-card");
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { fetchPosts, fetchSmartFeed, fetchUnifiedSearch } from "../../api/client
|
|||
import { categoryCode, matchesSubFilter, matchesTimeFilter } from "./mapFilter";
|
||||
import { getCoords, getViewFromMap, haversineKm } from "./mapGeo";
|
||||
import { createMarkerForPost, createSimpleMarkerForPost } from "./markerManager";
|
||||
import { getTemplateKeyForPost } from "./templateSpecs";
|
||||
import { prioritizePosts } from "./postPriority";
|
||||
import { trackEvent } from "../../utils/analytics";
|
||||
|
||||
|
|
@ -38,6 +39,21 @@ function asText(v) {
|
|||
return String(v);
|
||||
}
|
||||
|
||||
function getTemplateKey(post) {
|
||||
return getTemplateKeyForPost(post);
|
||||
}
|
||||
|
||||
function resolvePostImage(post) {
|
||||
return (
|
||||
post?.image_small ||
|
||||
post?.image ||
|
||||
post?.image_large ||
|
||||
post?.media_url ||
|
||||
post?.image_url ||
|
||||
""
|
||||
).toString();
|
||||
}
|
||||
|
||||
function postHaystack(p) {
|
||||
const title = asText(p?.title || p?.text || p?.body || "");
|
||||
const snippet = asText(p?.snippet || p?.summary || p?.content || "");
|
||||
|
|
@ -439,6 +455,7 @@ export function usePostsEngine({
|
|||
(visible) => {
|
||||
const map = mapRef.current;
|
||||
if (!map) return;
|
||||
map.__swMarkersRef = markersRef;
|
||||
|
||||
// If user is reading an expanded overlay, don't touch markers
|
||||
if (expandedElRef?.current) return;
|
||||
|
|
@ -491,6 +508,27 @@ export function usePostsEngine({
|
|||
try { item?.marker?.remove?.(); } catch {}
|
||||
}, 840);
|
||||
} else {
|
||||
const entry = wanted.get(id);
|
||||
if (entry?.post) {
|
||||
item.post = entry.post;
|
||||
if (item.el) {
|
||||
item.el.__post = entry.post;
|
||||
const nextKey = getTemplateKey(entry.post);
|
||||
const nextImg = resolvePostImage(entry.post);
|
||||
const nextTitle = (entry.post?.title || "").toString();
|
||||
if (
|
||||
item.el.__lastTemplateKey !== nextKey ||
|
||||
item.el.__lastImage !== nextImg ||
|
||||
item.el.__lastTitle !== nextTitle
|
||||
) {
|
||||
if (item.type === "simple" && item.el.__renderSimple) {
|
||||
try { item.el.__renderSimple(); } catch {}
|
||||
} else if (item.el.__renderCompact) {
|
||||
try { item.el.__renderCompact(); } catch {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
next.push(item);
|
||||
}
|
||||
}
|
||||
|
|
@ -695,7 +733,13 @@ export function usePostsEngine({
|
|||
if (Array.isArray(newPosts)) {
|
||||
for (const p of newPosts) {
|
||||
const key = getPostKey(p);
|
||||
if (key && !byId.has(key)) byId.set(key, p);
|
||||
if (!key) continue;
|
||||
const prior = byId.get(key);
|
||||
if (prior) {
|
||||
byId.set(key, { ...prior, ...p });
|
||||
} else {
|
||||
byId.set(key, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -853,6 +897,12 @@ export function usePostsEngine({
|
|||
|
||||
const counts = payload?.counts || null;
|
||||
const truth = payload?.truth || null;
|
||||
const postPatch =
|
||||
payload?.post ||
|
||||
payload?.post_data ||
|
||||
payload?.postData ||
|
||||
payload?.data ||
|
||||
null;
|
||||
const truthScore = Number(
|
||||
truth?.truth_score ??
|
||||
truth?.TruthScore ??
|
||||
|
|
@ -867,7 +917,7 @@ export function usePostsEngine({
|
|||
updated.push(p);
|
||||
continue;
|
||||
}
|
||||
const next = { ...p };
|
||||
const next = { ...p, ...(postPatch || {}) };
|
||||
if (counts) {
|
||||
if (Number.isFinite(counts.view_count)) next.view_count = counts.view_count;
|
||||
if (Number.isFinite(counts.like_count)) next.like_count = counts.like_count;
|
||||
|
|
|
|||
|
|
@ -64,6 +64,14 @@
|
|||
border: 1px solid rgba(56, 189, 248, 0.2);
|
||||
}
|
||||
|
||||
body[data-theme="blue"] .island-viewer-modal{
|
||||
background: linear-gradient(135deg, rgba(3, 12, 40, 0.95) 0%, rgba(2, 8, 28, 0.98) 100%);
|
||||
}
|
||||
body[data-theme="blue"] .island-viewer-container{
|
||||
background: linear-gradient(135deg, #0b1b3b 0%, #0f224d 60%, #163264 100%);
|
||||
border-color: rgba(96, 165, 250, 0.35);
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
transform: translateY(20px);
|
||||
|
|
|
|||
|
|
@ -145,8 +145,8 @@
|
|||
bottom:8%;
|
||||
transform:translateX(-50%);
|
||||
width:92%;
|
||||
max-width:520px;
|
||||
max-height:45vh;
|
||||
max-width:560px;
|
||||
max-height:62vh;
|
||||
overflow-y:auto;
|
||||
background: rgba(15, 23, 42, 0.96);
|
||||
border-radius:16px;
|
||||
|
|
@ -170,6 +170,60 @@
|
|||
padding:.1rem .3rem;
|
||||
cursor:pointer;
|
||||
}
|
||||
.create-stepper{
|
||||
display:flex;
|
||||
justify-content:space-between;
|
||||
align-items:center;
|
||||
gap:.4rem;
|
||||
margin:.3rem 0 .35rem;
|
||||
}
|
||||
.create-step{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap:.35rem;
|
||||
color:#64748b;
|
||||
font-size:.68rem;
|
||||
text-transform:uppercase;
|
||||
letter-spacing:.08em;
|
||||
}
|
||||
.create-step-active{
|
||||
color:#e2e8f0;
|
||||
}
|
||||
.create-step-dot{
|
||||
width:18px;
|
||||
height:18px;
|
||||
border-radius:50%;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:center;
|
||||
font-size:.65rem;
|
||||
background:#0f172a;
|
||||
border:1px solid #334155;
|
||||
color:#cbd5f5;
|
||||
}
|
||||
.create-step-current .create-step-dot{
|
||||
background:linear-gradient(120deg, #38bdf8, #22c55e);
|
||||
border-color:transparent;
|
||||
color:#0f172a;
|
||||
font-weight:700;
|
||||
}
|
||||
.create-step-label{
|
||||
font-weight:600;
|
||||
}
|
||||
.create-step-progress{
|
||||
height:4px;
|
||||
background:rgba(148, 163, 184, 0.15);
|
||||
border-radius:999px;
|
||||
overflow:hidden;
|
||||
}
|
||||
.create-step-progress span{
|
||||
display:block;
|
||||
height:100%;
|
||||
background:linear-gradient(90deg, #38bdf8, #22c55e);
|
||||
}
|
||||
.create-step-body{
|
||||
margin-top:.5rem;
|
||||
}
|
||||
.create-row{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
|
|
@ -180,6 +234,11 @@
|
|||
font-size:.75rem;
|
||||
color:#cbd5f5;
|
||||
}
|
||||
.create-hint{
|
||||
margin-top:.2rem;
|
||||
font-size:.7rem;
|
||||
color:#94a3b8;
|
||||
}
|
||||
|
||||
.create-row select{
|
||||
flex:1;
|
||||
|
|
@ -210,12 +269,60 @@
|
|||
resize:none;
|
||||
font-size:.78rem;
|
||||
}
|
||||
.create-image-option{
|
||||
margin-top:.4rem;
|
||||
}
|
||||
.create-checkbox{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap:.5rem;
|
||||
font-size:.72rem;
|
||||
color:#e5e7eb;
|
||||
cursor:pointer;
|
||||
}
|
||||
.create-image-panel{
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
gap:.4rem;
|
||||
margin-top:.35rem;
|
||||
}
|
||||
.create-image-row{
|
||||
display:flex;
|
||||
gap:.5rem;
|
||||
align-items:center;
|
||||
}
|
||||
.create-image-row input[type="file"]{
|
||||
flex:1;
|
||||
color:#e2e8f0;
|
||||
}
|
||||
.create-uploading{
|
||||
font-size:.7rem;
|
||||
color:#38bdf8;
|
||||
}
|
||||
.create-image-preview-row{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap:.5rem;
|
||||
}
|
||||
.create-image-preview-row img{
|
||||
width:44px;
|
||||
height:44px;
|
||||
border-radius:8px;
|
||||
object-fit:cover;
|
||||
border:1px solid rgba(148, 163, 184, 0.5);
|
||||
}
|
||||
.create-image-preview-row button{
|
||||
background:transparent;
|
||||
border:1px solid rgba(148, 163, 184, 0.4);
|
||||
color:#e2e8f0;
|
||||
padding:.2rem .45rem;
|
||||
border-radius:999px;
|
||||
font-size:.68rem;
|
||||
}
|
||||
.create-muted{
|
||||
font-size:.7rem;
|
||||
color:#94a3b8;
|
||||
}
|
||||
.create-privacy-row{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
|
|
@ -242,9 +349,169 @@
|
|||
color:#f97373;
|
||||
}
|
||||
.create-actions{
|
||||
margin-top:.4rem;
|
||||
margin-top:.6rem;
|
||||
display:flex;
|
||||
justify-content:flex-end;
|
||||
justify-content:space-between;
|
||||
align-items:center;
|
||||
gap:.5rem;
|
||||
}
|
||||
.create-actions-right{
|
||||
display:flex;
|
||||
gap:.45rem;
|
||||
align-items:center;
|
||||
}
|
||||
.create-back{
|
||||
background:transparent;
|
||||
border:1px solid rgba(148, 163, 184, 0.4);
|
||||
color:#cbd5f5;
|
||||
padding:.35rem .8rem;
|
||||
border-radius:999px;
|
||||
font-size:.72rem;
|
||||
cursor:pointer;
|
||||
}
|
||||
.create-back:disabled{
|
||||
opacity:.45;
|
||||
cursor:not-allowed;
|
||||
}
|
||||
.create-review-card{
|
||||
display:grid;
|
||||
grid-template-columns:72px 1fr;
|
||||
gap:.7rem;
|
||||
padding:.6rem;
|
||||
margin-top:.5rem;
|
||||
border-radius:12px;
|
||||
border:1px solid rgba(148, 163, 184, 0.35);
|
||||
background:rgba(2, 6, 23, 0.7);
|
||||
}
|
||||
.create-review-media{
|
||||
width:72px;
|
||||
height:72px;
|
||||
border-radius:12px;
|
||||
overflow:hidden;
|
||||
background:rgba(30, 41, 59, 0.85);
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:center;
|
||||
}
|
||||
.create-review-media img{
|
||||
width:100%;
|
||||
height:100%;
|
||||
object-fit:cover;
|
||||
}
|
||||
.create-review-icon{
|
||||
width:46px;
|
||||
height:46px;
|
||||
border-radius:14px;
|
||||
background:rgba(56, 189, 248, 0.2);
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:center;
|
||||
color:#38bdf8;
|
||||
font-size:1.1rem;
|
||||
}
|
||||
.create-review-meta{
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
gap:.2rem;
|
||||
}
|
||||
.create-review-title{
|
||||
font-size:.85rem;
|
||||
color:#e2e8f0;
|
||||
font-weight:600;
|
||||
}
|
||||
.create-review-sub{
|
||||
font-size:.7rem;
|
||||
color:#94a3b8;
|
||||
}
|
||||
.create-review-body{
|
||||
font-size:.74rem;
|
||||
color:#cbd5f5;
|
||||
max-height:3.8em;
|
||||
overflow:hidden;
|
||||
}
|
||||
.create-review-visibility{
|
||||
font-size:.68rem;
|
||||
color:#38bdf8;
|
||||
}
|
||||
|
||||
body[data-theme="blue"] .create-post-panel{
|
||||
background: rgba(12, 20, 60, 0.96);
|
||||
border-color: rgba(96, 165, 250, 0.95);
|
||||
box-shadow: 0 16px 40px rgba(2,8,26,.85), 0 0 24px rgba(56,189,248,.5);
|
||||
}
|
||||
body[data-theme="blue"] .create-row select,
|
||||
body[data-theme="blue"] .create-input,
|
||||
body[data-theme="blue"] .create-textarea,
|
||||
body[data-theme="blue"] .create-privacy-row select{
|
||||
background: #0b1b3b;
|
||||
border-color: rgba(96, 165, 250, 0.5);
|
||||
color:#e2e8f0;
|
||||
}
|
||||
body[data-theme="blue"] .create-step{
|
||||
color:#94a3b8;
|
||||
}
|
||||
body[data-theme="blue"] .create-step-active{
|
||||
color:#e2e8f0;
|
||||
}
|
||||
body[data-theme="blue"] .create-step-dot{
|
||||
background:#0b1b3b;
|
||||
border-color: rgba(96, 165, 250, 0.6);
|
||||
color:#e2e8f0;
|
||||
}
|
||||
body[data-theme="blue"] .create-back{
|
||||
border-color: rgba(96, 165, 250, 0.55);
|
||||
color:#e2e8f0;
|
||||
}
|
||||
body[data-theme="blue"] .create-review-card{
|
||||
background: rgba(3, 10, 28, 0.7);
|
||||
border-color: rgba(96, 165, 250, 0.4);
|
||||
}
|
||||
|
||||
body[data-theme="light"] .create-post-panel{
|
||||
background: rgba(248, 250, 252, 0.98);
|
||||
border-color: rgba(148, 163, 184, 0.9);
|
||||
box-shadow: 0 18px 36px rgba(15, 23, 42, 0.15);
|
||||
color:#0f172a;
|
||||
}
|
||||
body[data-theme="light"] .create-row select,
|
||||
body[data-theme="light"] .create-input,
|
||||
body[data-theme="light"] .create-textarea,
|
||||
body[data-theme="light"] .create-privacy-row select{
|
||||
background:#ffffff;
|
||||
border-color: rgba(148, 163, 184, 0.7);
|
||||
color:#0f172a;
|
||||
}
|
||||
body[data-theme="light"] .create-step{
|
||||
color:#64748b;
|
||||
}
|
||||
body[data-theme="light"] .create-step-active{
|
||||
color:#0f172a;
|
||||
}
|
||||
body[data-theme="light"] .create-step-dot{
|
||||
background:#ffffff;
|
||||
border-color: rgba(148, 163, 184, 0.7);
|
||||
color:#334155;
|
||||
}
|
||||
body[data-theme="light"] .create-step-current .create-step-dot{
|
||||
color:#0f172a;
|
||||
}
|
||||
body[data-theme="light"] .create-hint,
|
||||
body[data-theme="light"] .create-muted{
|
||||
color:#64748b;
|
||||
}
|
||||
body[data-theme="light"] .create-back{
|
||||
border-color: rgba(148, 163, 184, 0.7);
|
||||
color:#0f172a;
|
||||
}
|
||||
body[data-theme="light"] .create-review-card{
|
||||
background: rgba(241, 245, 249, 0.95);
|
||||
border-color: rgba(148, 163, 184, 0.6);
|
||||
}
|
||||
body[data-theme="light"] .create-review-title{
|
||||
color:#0f172a;
|
||||
}
|
||||
body[data-theme="light"] .create-review-body{
|
||||
color:#334155;
|
||||
}
|
||||
|
||||
.chip-pill{
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@
|
|||
.sw-centered-modal .post-card.sw-expanded-shell {
|
||||
background: rgba(15, 23, 42, 0.98) !important; /* transparence intacte */
|
||||
border: 3px solid #000 !important; /* contour plus épais */
|
||||
box-shadow: 0 12px 30px rgba(0,0,0,0.7), 0 0 16px rgba(56,189,248,0.5) !important;
|
||||
box-shadow: 0 12px 30px rgba(0,0,0,0.7), 0 0 16px var(--sw-modal-accent-glow, rgba(56,189,248,0.5)) !important;
|
||||
padding: 12px 14px;
|
||||
color: #e5e7eb !important; /* texte blanc clair */
|
||||
width: min(94vw, 460px) !important;
|
||||
|
|
@ -70,36 +70,59 @@
|
|||
gap: 8px;
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
--sw-modal-accent: #38bdf8;
|
||||
--sw-modal-accent-soft: rgba(56,189,248,0.28);
|
||||
--sw-modal-accent-glow: rgba(56,189,248,0.5);
|
||||
--sw-modal-accent-text: #D7F3FF;
|
||||
--sw-modal-text: #e5e7eb;
|
||||
--sw-modal-meta: #9eb7ff;
|
||||
--sw-modal-pill-text: #c7e3ff;
|
||||
}
|
||||
|
||||
|
||||
/* Couleur badge par catégorie dans grosse carte */
|
||||
.sw-centered-modal [data-category="NEWS"] .sw-modal-badge {
|
||||
background: rgba(56,189,248,0.35) !important;
|
||||
border-color: #38bdf8 !important;
|
||||
color: #D7F3FF !important;
|
||||
/* Couleur principale par catégorie/template pour grosse carte */
|
||||
.sw-centered-modal [data-template="news"]{
|
||||
--sw-modal-accent: #38bdf8;
|
||||
--sw-modal-accent-soft: rgba(56,189,248,0.28);
|
||||
--sw-modal-accent-glow: rgba(56,189,248,0.5);
|
||||
--sw-modal-accent-text: #D7F3FF;
|
||||
}
|
||||
.sw-centered-modal [data-category="EVENT"] .sw-modal-badge {
|
||||
background: rgba(168,85,247,0.35) !important;
|
||||
border-color: #a855f7 !important;
|
||||
color: #F3E8FF !important;
|
||||
.sw-centered-modal [data-template="breaking"]{
|
||||
--sw-modal-accent: #f43f5e;
|
||||
--sw-modal-accent-soft: rgba(244,63,94,0.26);
|
||||
--sw-modal-accent-glow: rgba(244,63,94,0.5);
|
||||
--sw-modal-accent-text: #FFE4E6;
|
||||
}
|
||||
.sw-centered-modal [data-category="FRIENDS"] .sw-modal-badge {
|
||||
background: rgba(34,197,94,0.35) !important;
|
||||
border-color: #22c55e !important;
|
||||
color: #D1FAE5 !important;
|
||||
.sw-centered-modal [data-template="finance"]{
|
||||
--sw-modal-accent: #14b8a6;
|
||||
--sw-modal-accent-soft: rgba(20,184,166,0.26);
|
||||
--sw-modal-accent-glow: rgba(20,184,166,0.5);
|
||||
--sw-modal-accent-text: #CCFBF1;
|
||||
}
|
||||
.sw-centered-modal [data-category="MARKET"] .sw-modal-badge {
|
||||
background: rgba(250,204,21,0.35) !important;
|
||||
border-color: #facc15 !important;
|
||||
color: #FEFCE8 !important;
|
||||
.sw-centered-modal [data-template="market"]{
|
||||
--sw-modal-accent: #facc15;
|
||||
--sw-modal-accent-soft: rgba(250,204,21,0.26);
|
||||
--sw-modal-accent-glow: rgba(250,204,21,0.5);
|
||||
--sw-modal-accent-text: #FEF9C3;
|
||||
}
|
||||
.sw-centered-modal [data-template="friends"]{
|
||||
--sw-modal-accent: #22c55e;
|
||||
--sw-modal-accent-soft: rgba(34,197,94,0.26);
|
||||
--sw-modal-accent-glow: rgba(34,197,94,0.5);
|
||||
--sw-modal-accent-text: #D1FAE5;
|
||||
}
|
||||
.sw-centered-modal [data-template="events"]{
|
||||
--sw-modal-accent: #a855f7;
|
||||
--sw-modal-accent-soft: rgba(168,85,247,0.26);
|
||||
--sw-modal-accent-glow: rgba(168,85,247,0.5);
|
||||
--sw-modal-accent-text: #EDE9FE;
|
||||
}
|
||||
|
||||
/* Hero (image en haut) + titre + stats : lisibles */
|
||||
.sw-modal-hero {
|
||||
background: rgba(15, 23, 42, 0.98) !important; /* transparence intacte */
|
||||
background: radial-gradient(circle at 30% 30%, var(--sw-modal-accent-soft, rgba(56,189,248,0.18)), rgba(3,14,32,0.98)) !important;
|
||||
border: 3px solid #000 !important; /* contour plus épais */
|
||||
box-shadow: 0 12px 30px rgba(0,0,0,0.7), 0 0 16px rgba(56,189,248,0.5) !important;
|
||||
box-shadow: 0 12px 30px rgba(0,0,0,0.7), 0 0 16px var(--sw-modal-accent-glow, rgba(56,189,248,0.5)) !important;
|
||||
}
|
||||
|
||||
.sw-centered-modal.sw-modal-fullscreen{
|
||||
|
|
@ -119,19 +142,21 @@
|
|||
}
|
||||
.sw-modal-title,
|
||||
.sw-modal-summary,
|
||||
.sw-stat-pill,
|
||||
.sw-modal-meta,
|
||||
.sw-livechat-title,
|
||||
.sw-chat-line,
|
||||
.sw-livechat-input {
|
||||
color: #e5e7eb !important; /* texte blanc partout */
|
||||
color: var(--sw-modal-text, #e5e7eb) !important; /* texte blanc partout */
|
||||
}
|
||||
.sw-modal-meta{
|
||||
color: var(--sw-modal-meta, #9eb7ff) !important;
|
||||
}
|
||||
.sw-stat-pill {
|
||||
color: var(--sw-modal-pill-text, #c7e3ff) !important;
|
||||
background: rgba(148, 163, 184, 0.25) !important;
|
||||
border: 1px solid rgba(148, 163, 184, 0.5) !important;
|
||||
}
|
||||
.sw-cta-primary {
|
||||
background: rgba(56,189,248,0.35) !important;
|
||||
background: var(--sw-modal-accent-soft, rgba(56,189,248,0.35)) !important;
|
||||
color: #ffffff !important;
|
||||
}
|
||||
.sw-livechat-input {
|
||||
|
|
@ -474,8 +499,8 @@ body[data-theme="light"] .sw-chat-bubble::before{
|
|||
font-weight: 900;
|
||||
font-size: 12px;
|
||||
letter-spacing: .04em;
|
||||
color: #d7f3ff;
|
||||
background: rgba(56,189,248,0.16);
|
||||
color: var(--sw-modal-accent-text, #d7f3ff);
|
||||
background: var(--sw-modal-accent-soft, rgba(56,189,248,0.16));
|
||||
border: 3px solid #000 !important; /* contour plus épais */
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
|
@ -506,8 +531,8 @@ body[data-theme="light"] .sw-chat-bubble::before{
|
|||
}
|
||||
|
||||
.post-card-btn{
|
||||
background: rgba(56,189,248,0.14);
|
||||
border: 1px solid rgba(56,189,248,0.35);
|
||||
background: var(--sw-modal-accent-soft, rgba(56,189,248,0.14));
|
||||
border: 1px solid var(--sw-modal-accent, rgba(56,189,248,0.35));
|
||||
color: #e8f5ff;
|
||||
font-weight: 900;
|
||||
padding: 6px 12px;
|
||||
|
|
@ -539,9 +564,9 @@ body[data-theme="light"] .sw-chat-bubble::before{
|
|||
font-size: 11px;
|
||||
font-weight: 900;
|
||||
letter-spacing: .06em;
|
||||
background: rgba(56,189,248,0.16);
|
||||
background: var(--sw-modal-accent-soft, rgba(56,189,248,0.16));
|
||||
border: 3px solid #000 !important; /* contour plus épais */
|
||||
color:#D7F3FF;
|
||||
color: var(--sw-modal-accent-text, #D7F3FF);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
|
|
@ -591,11 +616,46 @@ body[data-theme="light"] .sw-chat-bubble::before{
|
|||
border-radius: 16px;
|
||||
overflow:hidden;
|
||||
border: 3px solid #000 !important; /* contour plus épais */
|
||||
background: radial-gradient(circle at 30% 30%, rgba(56,189,248,0.18), rgba(3,14,32,0.98));
|
||||
box-shadow: 0 10px 24px rgba(0,0,0,0.35);
|
||||
background: radial-gradient(circle at 30% 30%, var(--sw-modal-accent-soft, rgba(56,189,248,0.18)), rgba(3,14,32,0.98));
|
||||
box-shadow: 0 10px 24px rgba(0,0,0,0.35), 0 0 18px var(--sw-modal-accent-glow, rgba(56,189,248,0.45));
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
body[data-theme="light"] .sw-centered-modal .post-card.sw-expanded-shell{
|
||||
--sw-modal-text: #0f172a;
|
||||
--sw-modal-meta: #64748b;
|
||||
--sw-modal-pill-text: #0f172a;
|
||||
--sw-modal-accent-text: #0f172a;
|
||||
background: rgba(248, 250, 252, 0.98) !important;
|
||||
border-color: rgba(148, 163, 184, 0.8) !important;
|
||||
box-shadow: 0 12px 28px rgba(15, 23, 42, 0.12), 0 0 14px rgba(148, 163, 184, 0.5) !important;
|
||||
}
|
||||
body[data-theme="light"] .sw-modal-title{
|
||||
color:#0f172a;
|
||||
}
|
||||
body[data-theme="light"] .sw-modal-summary{
|
||||
color:#334155;
|
||||
}
|
||||
body[data-theme="light"] .sw-modal-meta{
|
||||
color:#64748b;
|
||||
}
|
||||
body[data-theme="light"] .sw-modal-hero-fallback{
|
||||
color:#0f172a;
|
||||
}
|
||||
body[data-theme="light"] .sw-modal-hero-fallback-inner{
|
||||
border-color: rgba(148, 163, 184, 0.5);
|
||||
color:#0f172a;
|
||||
}
|
||||
|
||||
body[data-theme="light"] .sw-centered-modal .post-card.sw-expanded-shell .sw-stat-pill{
|
||||
background: rgba(148, 163, 184, 0.18) !important;
|
||||
border-color: rgba(148, 163, 184, 0.4) !important;
|
||||
}
|
||||
|
||||
body[data-theme="blue"] .sw-centered-modal .post-card.sw-expanded-shell{
|
||||
--sw-modal-meta: #b3ccff;
|
||||
}
|
||||
|
||||
.sw-modal-hero-row{
|
||||
display:flex;
|
||||
gap:16px;
|
||||
|
|
@ -881,9 +941,9 @@ body[data-theme="light"] .sw-modal-hero-fallback{
|
|||
|
||||
.sw-stat-pill{
|
||||
font-size: 11px;
|
||||
color:#c7e3ff;
|
||||
color: var(--sw-modal-pill-text, #c7e3ff);
|
||||
background: rgba(3, 14, 32, 0.58);
|
||||
border: 1px solid rgba(90, 190, 255, 0.22);
|
||||
border: 1px solid var(--sw-modal-accent, rgba(90, 190, 255, 0.22));
|
||||
padding: 6px 10px;
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
|
@ -904,7 +964,7 @@ body[data-theme="light"] .sw-modal-hero-fallback{
|
|||
font-size: 14px;
|
||||
color:#e8f5ff;
|
||||
cursor:pointer;
|
||||
background: rgba(56,189,248,0.22);
|
||||
background: var(--sw-modal-accent-soft, rgba(56,189,248,0.22));
|
||||
box-shadow: 0 10px 18px rgba(0,0,0,0.35);
|
||||
}
|
||||
|
||||
|
|
@ -972,6 +1032,20 @@ body[data-theme="light"] .sw-modal-hero-fallback{
|
|||
opacity: 1.0;
|
||||
}
|
||||
|
||||
body[data-theme="blue"] .sw-centered-backdrop{
|
||||
background: rgba(6, 16, 46, 0.45);
|
||||
}
|
||||
|
||||
body[data-theme="light"] .sw-centered-backdrop{
|
||||
background: rgba(226, 232, 240, 0.75);
|
||||
}
|
||||
|
||||
body[data-theme="light"] .sw-modal-x{
|
||||
background: rgba(226, 232, 240, 0.9);
|
||||
color:#0f172a;
|
||||
border-color: rgba(148, 163, 184, 0.5);
|
||||
}
|
||||
|
||||
/* SW_MINI_SMOOTH_MOVE */
|
||||
.post-pin{
|
||||
will-change: transform;
|
||||
|
|
|
|||
|
|
@ -23,6 +23,14 @@
|
|||
animation: slideUp 0.3s ease;
|
||||
}
|
||||
|
||||
body[data-theme="blue"] .user-profile-modal{
|
||||
background: rgba(3, 10, 32, 0.86);
|
||||
}
|
||||
body[data-theme="blue"] .user-profile-container{
|
||||
background: linear-gradient(135deg, #0b1b3b 0%, #0f224d 100%);
|
||||
border: 1px solid rgba(96, 165, 250, 0.35);
|
||||
}
|
||||
|
||||
.user-profile-close {
|
||||
position: absolute;
|
||||
top: 0.75rem;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,18 @@
|
|||
border: 1px solid rgba(148, 163, 184, 0.1);
|
||||
}
|
||||
|
||||
body[data-theme="blue"] .island-universe-modal{
|
||||
background: linear-gradient(135deg, rgba(3, 10, 32, 0.96) 0%, rgba(6, 12, 36, 0.98) 100%);
|
||||
}
|
||||
body[data-theme="blue"] .island-universe-container{
|
||||
background: linear-gradient(135deg, #0b1b3b 0%, #0f224d 45%, #162f5c 100%);
|
||||
border-color: rgba(96, 165, 250, 0.35);
|
||||
box-shadow:
|
||||
0 30px 100px rgba(2, 8, 26, 0.85),
|
||||
0 0 0 1px rgba(96, 165, 250, 0.2),
|
||||
inset 0 1px 0 0 rgba(255, 255, 255, 0.04);
|
||||
}
|
||||
|
||||
.island-universe-close {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
|
|
|
|||
Loading…
Reference in New Issue