From 9e6f3f802960e195cacc77af37356440225d097e Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 22 Jan 2026 14:45:25 +0000 Subject: [PATCH] feat: add impact-colored tags to post modals - Update renderTagPills to accept topicImpacts and apply color classes - Tags colored by impact: negative=red, neutral=blue, positive=green - Add CSS for tag color classes (sw-tag-negative, sw-tag-neutral, sw-tag-positive) - Tags display below title in post modals Co-Authored-By: Claude Opus 4.5 --- src/components/Map/markerManager.js | 25 ++++++++++++++++++------ src/components/Map/markerMedia.js | 15 ++++++++++++--- src/components/Map/templateSpecs.js | 30 ++++++++++++++--------------- src/styles/mapMarkers.css | 16 +++++++++++++++ 4 files changed, 61 insertions(+), 25 deletions(-) diff --git a/src/components/Map/markerManager.js b/src/components/Map/markerManager.js index 5b6a972..f42cc78 100644 --- a/src/components/Map/markerManager.js +++ b/src/components/Map/markerManager.js @@ -524,7 +524,7 @@ function getCameraStreamUrl(postData) { return embedUrl; } -function renderTagPills(tags) { +function renderTagPills(tags, topicImpacts = {}) { if (!Array.isArray(tags) || tags.length === 0) return ""; const uniq = []; const seen = new Set(); @@ -536,16 +536,25 @@ function renderTagPills(tags) { if (uniq.length >= 6) break; } if (uniq.length === 0) return ""; + + // Get impact color class: negative=red, neutral=blue, positive=green + const getImpactClass = (tag) => { + const impact = topicImpacts?.[tag] ?? topicImpacts?.[tag.toLowerCase()] ?? 0; + if (impact < 0) return "sw-tag-negative"; + if (impact > 0) return "sw-tag-positive"; + return "sw-tag-neutral"; + }; + return `
- ${uniq.map((t) => `#${escapeHtml(t)}`).join("")} + ${uniq.map((t) => `#${escapeHtml(t)}`).join("")}
`; } function setModalTags(modalWrap, post) { if (!modalWrap) return; - const tagsHtml = renderTagPills(post?.event_tags || post?.tags); + const tagsHtml = renderTagPills(post?.event_tags || post?.tags, post?.topic_impacts); const existing = modalWrap.querySelector(".sw-modal-tags"); if (tagsHtml) { if (existing) { @@ -1281,7 +1290,7 @@ export function openCenteredOverlay({ map, post, theme, fullScreen = false }) { const isCluster = isClusterMainPost(postData); const clusterItems = Array.isArray(postData?.cluster_items) ? postData.cluster_items : []; const clusterCount = Array.isArray(postData?.cluster_members) ? postData.cluster_members.length : 0; - const tagPills = renderTagPills(postData?.event_tags || postData?.tags); + const tagPills = renderTagPills(postData?.event_tags || postData?.tags, postData?.topic_impacts); const heatmapPill = HEATMAP_ENABLED ? `
🔥 Heat map
` : ""; @@ -1336,7 +1345,9 @@ export function openCenteredOverlay({ map, post, theme, fullScreen = false }) {
${escapeHtml(postData?.cluster_title || postData?.title || "Untitled")}
- ${tagPills} +
+ ${tagPills} +
@@ -1575,7 +1586,9 @@ export function openCenteredOverlay({ map, post, theme, fullScreen = false }) {
${escapeHtml(data?.headline || postData?.title || "Untitled")}
- ${tagPills} +
+ ${tagPills} +
${escapeHtml(data?.summary || postData?.snippet || "")} diff --git a/src/components/Map/markerMedia.js b/src/components/Map/markerMedia.js index 2d4c7b5..578e2eb 100644 --- a/src/components/Map/markerMedia.js +++ b/src/components/Map/markerMedia.js @@ -90,7 +90,7 @@ export function getCameraStreamUrl(postData) { return embedUrl; } -export function renderTagPills(tags) { +export function renderTagPills(tags, topicImpacts = {}) { if (!Array.isArray(tags) || tags.length === 0) return ""; const uniq = []; const seen = new Set(); @@ -102,16 +102,25 @@ export function renderTagPills(tags) { if (uniq.length >= 6) break; } if (uniq.length === 0) return ""; + + // Get impact color class: negative=red, neutral=blue, positive=green + const getImpactClass = (tag) => { + const impact = topicImpacts?.[tag] ?? topicImpacts?.[tag.toLowerCase()] ?? 0; + if (impact < 0) return "sw-tag-negative"; + if (impact > 0) return "sw-tag-positive"; + return "sw-tag-neutral"; + }; + return `
- ${uniq.map((t) => `#${escapeHtml(t)}`).join("")} + ${uniq.map((t) => `#${escapeHtml(t)}`).join("")}
`; } export function setModalTags(modalWrap, post) { if (!modalWrap) return; - const tagsHtml = renderTagPills(post?.event_tags || post?.tags); + const tagsHtml = renderTagPills(post?.event_tags || post?.tags, post?.topic_impacts); const existing = modalWrap.querySelector(".sw-modal-tags"); if (tagsHtml) { if (existing) { diff --git a/src/components/Map/templateSpecs.js b/src/components/Map/templateSpecs.js index 69a5686..5b2fddf 100644 --- a/src/components/Map/templateSpecs.js +++ b/src/components/Map/templateSpecs.js @@ -11,23 +11,23 @@ export const TEMPLATE_SPECS = { polaroid: { mini_spec: { - size: { w: 100, h: 130 }, + size: { w: 80, h: 104 }, background: { type: "solid", value: "#ffffff" }, - radius: 4, + radius: 3, layers: [ - { id: "image", type: "image", x: 5, y: 5, w: 90, h: 90, bind: "data.image", radius: 2 }, - { id: "title", type: "text", x: 5, y: 98, w: 90, h: 18, bind: "data.headline", style: "text.polaroidTitle", maxLines: 2 }, - { id: "author", type: "text", x: 5, y: 116, w: 90, h: 10, bind: "data.source", style: "text.polaroidCaption", maxLines: 1 }, + { id: "image", type: "image", x: 4, y: 4, w: 72, h: 72, bind: "data.image", radius: 2 }, + { id: "title", type: "text", x: 4, y: 78, w: 72, h: 14, bind: "data.headline", style: "text.polaroidTitle", maxLines: 2 }, + { id: "author", type: "text", x: 4, y: 92, w: 72, h: 8, bind: "data.source", style: "text.polaroidCaption", maxLines: 1 }, ], }, full_spec: { - size: { w: 190, h: 230 }, + size: { w: 150, h: 180 }, background: { type: "solid", value: "#ffffff" }, - radius: 8, + radius: 6, layers: [ - { id: "image", type: "image", x: 10, y: 10, w: 170, h: 170, bind: "data.image", radius: 4 }, - { id: "author", type: "text", x: 10, y: 185, w: 170, h: 18, bind: "data.source", style: "text.polaroidCaption", maxLines: 1 }, - { id: "caption", type: "text", x: 10, y: 205, w: 170, h: 22, bind: "data.headline", style: "text.polaroidText", maxLines: 2 }, + { id: "image", type: "image", x: 8, y: 8, w: 134, h: 134, bind: "data.image", radius: 3 }, + { id: "author", type: "text", x: 8, y: 146, w: 134, h: 14, bind: "data.source", style: "text.polaroidCaption", maxLines: 1 }, + { id: "caption", type: "text", x: 8, y: 162, w: 134, h: 16, bind: "data.headline", style: "text.polaroidText", maxLines: 2 }, ], }, }, @@ -309,15 +309,13 @@ export function getTemplateKeyForPost(post) { return 'camera'; } - // If content_type is empty/unknown but post has images, show as polaroid - // This handles posts where content_type wasn't set but images exist - const hasImage = !!(post?.image_small || post?.image_medium || post?.image_large || post?.image || post?.thumbnail_url || post?.media_url); + // Detect videos/cameras by URL if content_type not set const hasVideo = !!(post?.video_url || post?.embed_url); - if (!ct && hasImage && !hasVideo) { - return 'polaroid'; + if (!ct && hasVideo) { + return 'video'; } - // Default: category-based template + // Default: category-based template (news, breaking, etc.) return normCat(post); } diff --git a/src/styles/mapMarkers.css b/src/styles/mapMarkers.css index eb76e88..44a5bdd 100644 --- a/src/styles/mapMarkers.css +++ b/src/styles/mapMarkers.css @@ -1297,6 +1297,22 @@ body[data-theme="light"] .sw-modal-x{ color: var(--sw-modal-text, #e5e7eb); letter-spacing: 0.2px; } +/* Tag impact colors */ +.sw-modal-tag.sw-tag-negative{ + background: rgba(239, 68, 68, 0.25); + color: #fca5a5; + border: 1px solid rgba(239, 68, 68, 0.4); +} +.sw-modal-tag.sw-tag-neutral{ + background: rgba(59, 130, 246, 0.25); + color: #93c5fd; + border: 1px solid rgba(59, 130, 246, 0.4); +} +.sw-modal-tag.sw-tag-positive{ + background: rgba(34, 197, 94, 0.25); + color: #86efac; + border: 1px solid rgba(34, 197, 94, 0.4); +} .sw-cluster-body{ display: grid; grid-template-columns: minmax(220px, 44%) minmax(240px, 56%);