Add truth rail and remove duplicate modal content
This commit is contained in:
parent
c4b7f6cb90
commit
3d7a33b06e
|
|
@ -63,6 +63,16 @@ function updateStat(modalWrap, statKey, value) {
|
|||
if (el) el.textContent = formatCount(value);
|
||||
}
|
||||
|
||||
function updateTruth(modalWrap, post) {
|
||||
const rail = modalWrap.querySelector(".sw-truth-rail");
|
||||
if (!rail) return;
|
||||
const score = truthScore(post);
|
||||
const marker = rail.querySelector(".sw-truth-rail-marker");
|
||||
const label = rail.querySelector(".sw-truth-rail-score");
|
||||
if (marker) marker.style.top = `${100 - score}%`;
|
||||
if (label) label.textContent = `${score}`;
|
||||
}
|
||||
|
||||
function isAuthed() {
|
||||
try {
|
||||
const auth = loadAuth();
|
||||
|
|
@ -139,6 +149,7 @@ function applyPostDetails(modalWrap, post) {
|
|||
updateStat(modalWrap, "likes", likeCount);
|
||||
updateStat(modalWrap, "shares", shareCount);
|
||||
updateStat(modalWrap, "comments", commentCount);
|
||||
updateTruth(modalWrap, post);
|
||||
}
|
||||
|
||||
function renderComments(container, items) {
|
||||
|
|
@ -162,11 +173,18 @@ function renderComments(container, items) {
|
|||
.join("");
|
||||
}
|
||||
|
||||
function mountCard(container, spec, data, theme) {
|
||||
function mountCard(container, spec, data, theme, post) {
|
||||
const root = createRoot(container);
|
||||
const tokens = cardTokens?.[theme] || cardTokens.blue;
|
||||
const score = truthScore(post || data || {});
|
||||
|
||||
root.render(
|
||||
React.createElement(
|
||||
"div",
|
||||
{
|
||||
className: "sw-card-wrap",
|
||||
style: { width: spec?.size?.w, height: spec?.size?.h },
|
||||
},
|
||||
React.createElement(CardRenderer, {
|
||||
spec,
|
||||
data,
|
||||
|
|
@ -177,7 +195,21 @@ function mountCard(container, spec, data, theme) {
|
|||
}
|
||||
},
|
||||
className: "",
|
||||
}),
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "sw-truth-mini-bar" },
|
||||
React.createElement("div", {
|
||||
className: "sw-truth-mini-marker",
|
||||
style: { top: `${100 - score}%` },
|
||||
})
|
||||
),
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "sw-truth-mini-score", style: { background: truthColor(score) } },
|
||||
`${score}`
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
return () => {
|
||||
|
|
@ -249,6 +281,29 @@ function formatRelativeTime(createdAt) {
|
|||
}
|
||||
}
|
||||
|
||||
function truthScore(post) {
|
||||
const raw = Number(post?.truth_score || post?.truthScore);
|
||||
if (Number.isFinite(raw)) {
|
||||
return clamp(Math.round(raw), 0, 100);
|
||||
}
|
||||
const seed = `${post?.id || ""}|${post?.url || ""}|${post?.title || ""}`;
|
||||
let h = 0;
|
||||
for (let i = 0; i < seed.length; i++) {
|
||||
h = (h * 31 + seed.charCodeAt(i)) >>> 0;
|
||||
}
|
||||
return 35 + (h % 61);
|
||||
}
|
||||
|
||||
function truthColor(score) {
|
||||
const s = clamp(score, 0, 100);
|
||||
const hue = Math.round((s / 100) * 120);
|
||||
return `hsl(${hue} 80% 42%)`;
|
||||
}
|
||||
|
||||
function clamp(v, min, max) {
|
||||
return Math.min(Math.max(v, min), max);
|
||||
}
|
||||
|
||||
function normalizeImageUrl(raw) {
|
||||
const value = (raw || "").toString().trim();
|
||||
if (!value) return "";
|
||||
|
|
@ -300,6 +355,7 @@ export function openCenteredOverlay({ map, post, theme, fullScreen = false }) {
|
|||
const metaLeft = author ? `by ${author}` : "";
|
||||
const metaRight = sub ? sub : "";
|
||||
const img = heroImageForPost(postData);
|
||||
const truth = truthScore(postData);
|
||||
const url = data?.url || "";
|
||||
const postID = postData?.id || postData?.ID || 0;
|
||||
const viewCount = readCount(postData, ["view_count", "views", "viewCount"]);
|
||||
|
|
@ -347,6 +403,15 @@ export function openCenteredOverlay({ map, post, theme, fullScreen = false }) {
|
|||
<button class="sw-modal-x" type="button" aria-label="Close">✕</button>
|
||||
</div>
|
||||
|
||||
<div class="sw-modal-hero-row">
|
||||
<div class="sw-truth-rail" data-score="${truth}">
|
||||
<div class="sw-truth-rail-top">TRUE</div>
|
||||
<div class="sw-truth-rail-bar">
|
||||
<div class="sw-truth-rail-marker" style="top:${100 - truth}%"></div>
|
||||
</div>
|
||||
<div class="sw-truth-rail-score">${truth}</div>
|
||||
<div class="sw-truth-rail-bottom">FALSE</div>
|
||||
</div>
|
||||
<div class="sw-modal-hero">
|
||||
${
|
||||
img
|
||||
|
|
@ -354,6 +419,7 @@ export function openCenteredOverlay({ map, post, theme, fullScreen = false }) {
|
|||
: `<div class="sw-modal-hero-fallback"><div class="sw-modal-hero-fallback-inner"><i class="${categoryIconClass(postData)}"></i><span>${escapeHtml(normCatLabel(postData))}</span></div></div>`
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sw-modal-title">${escapeHtml(data?.headline || postData?.title || "Untitled")}</div>
|
||||
|
||||
|
|
@ -393,8 +459,6 @@ export function openCenteredOverlay({ map, post, theme, fullScreen = false }) {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- (Optional) keep template renderer mounted but hidden for future -->
|
||||
<div class="sw-template-full-wrap" style="display:none;"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
|
@ -595,13 +659,7 @@ export function openCenteredOverlay({ map, post, theme, fullScreen = false }) {
|
|||
};
|
||||
window.addEventListener("sociowire:auth-changed", onAuth);
|
||||
|
||||
// still mount template full (hidden) so you can switch back later instantly
|
||||
let unmountFull = null;
|
||||
const fullWrap = modalWrap.querySelector(".sw-template-full-wrap");
|
||||
if (fullWrap) {
|
||||
const spec = getTemplateSpecForPost(post, "full");
|
||||
unmountFull = mountCard(fullWrap, spec, data, theme);
|
||||
}
|
||||
|
||||
map.__swCenteredOverlay = {
|
||||
postId: postData?.id,
|
||||
|
|
@ -735,7 +793,7 @@ export function createMarkerForPost(post, mapRef, markersRef, expandedElRef, the
|
|||
if (wrap) {
|
||||
const spec = getTemplateSpecForPost(post, "mini");
|
||||
const data = adaptPostToTemplateData(post);
|
||||
root.__swUnmount = mountCard(wrap, spec, data, theme);
|
||||
root.__swUnmount = mountCard(wrap, spec, data, theme, post);
|
||||
}
|
||||
|
||||
clearOcclusion(markersRef);
|
||||
|
|
|
|||
|
|
@ -531,6 +531,107 @@ body[data-theme="light"] .sw-chat-bubble::before{
|
|||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.sw-modal-hero-row{
|
||||
display:flex;
|
||||
gap:10px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.sw-truth-rail{
|
||||
width: 16px;
|
||||
min-width: 16px;
|
||||
height: 100%;
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
align-items:center;
|
||||
justify-content:space-between;
|
||||
font-size: 9px;
|
||||
font-weight: 900;
|
||||
letter-spacing: .08em;
|
||||
text-transform: uppercase;
|
||||
color:#e2e8f0;
|
||||
}
|
||||
|
||||
.sw-truth-rail-bar{
|
||||
flex: 1 1 auto;
|
||||
width: 12px;
|
||||
border-radius: 999px;
|
||||
background: linear-gradient(180deg, #22c55e 0%, #facc15 50%, #ef4444 100%);
|
||||
border: 2px solid rgba(0,0,0,0.45);
|
||||
position: relative;
|
||||
box-shadow: inset 0 0 10px rgba(0,0,0,0.35);
|
||||
}
|
||||
|
||||
.sw-truth-rail-marker{
|
||||
position:absolute;
|
||||
left:50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 20px;
|
||||
height: 6px;
|
||||
border-radius: 999px;
|
||||
background:#0b1220;
|
||||
border:2px solid #fff;
|
||||
box-shadow: 0 4px 10px rgba(0,0,0,0.35);
|
||||
}
|
||||
|
||||
.sw-truth-rail-score{
|
||||
font-size: 11px;
|
||||
font-weight: 900;
|
||||
color:#fff;
|
||||
background:#0b1220;
|
||||
padding: 4px 6px;
|
||||
border-radius: 999px;
|
||||
box-shadow: 0 6px 12px rgba(0,0,0,0.35);
|
||||
}
|
||||
|
||||
.sw-modal-hero-row{
|
||||
display:flex;
|
||||
gap:12px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.sw-card-wrap{
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sw-truth-mini-bar{
|
||||
position:absolute;
|
||||
left:0;
|
||||
top:0;
|
||||
bottom:0;
|
||||
width: 8px;
|
||||
background: linear-gradient(180deg, #22c55e 0%, #facc15 50%, #ef4444 100%);
|
||||
border-top-left-radius: 18px;
|
||||
border-bottom-left-radius: 18px;
|
||||
box-shadow: inset 0 0 6px rgba(0,0,0,0.35);
|
||||
pointer-events:none;
|
||||
}
|
||||
|
||||
.sw-truth-mini-marker{
|
||||
position:absolute;
|
||||
left:50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 14px;
|
||||
height: 4px;
|
||||
border-radius: 999px;
|
||||
background:#0b1220;
|
||||
border:1px solid #fff;
|
||||
box-shadow: 0 3px 6px rgba(0,0,0,0.35);
|
||||
}
|
||||
|
||||
.sw-truth-mini-score{
|
||||
position:absolute;
|
||||
right:6px;
|
||||
bottom:6px;
|
||||
padding: 3px 6px;
|
||||
border-radius: 999px;
|
||||
font-size: 10px;
|
||||
font-weight: 900;
|
||||
color:#fff;
|
||||
letter-spacing: .03em;
|
||||
pointer-events:none;
|
||||
}
|
||||
|
||||
.sw-modal-hero img{
|
||||
width:100%;
|
||||
height:100%;
|
||||
|
|
@ -639,6 +740,13 @@ body[data-theme="light"] .sw-modal-hero-fallback{
|
|||
width: min(96vw, 420px);
|
||||
height: min(92vh, 700px);
|
||||
}
|
||||
.sw-modal-hero-row{
|
||||
align-items: stretch;
|
||||
}
|
||||
.sw-truth-rail{
|
||||
width: 12px;
|
||||
min-width: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
/* SW_CENTERED_MODAL_ANIM */
|
||||
|
|
|
|||
Loading…
Reference in New Issue