← back to Bubbesblock

public/chrome.js

125 lines

/* Shared BubbesBlock chrome: top bar + left/right rails. Used by feed + post pages. */
const ICON = {
  house:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 11l9-8 9 8"/><path d="M5 10v10h14V10"/></svg>',
  sugar:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 7h18l-1.5 12.5a2 2 0 0 1-2 1.5H6.5a2 2 0 0 1-2-1.5L3 7z"/><path d="M8 7V5a4 4 0 0 1 8 0v2"/></svg>',
  bell:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.7 21a2 2 0 0 1-3.4 0"/></svg>',
  inbox:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 4h16v16H4z"/><path d="M4 8h5l2 3h2l2-3h5"/></svg>',
  cal:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 2v4M16 2v4M3 9h18M5 5h14v16H5z"/></svg>',
  history:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3l9 6-9 6-9-6 9-6z"/><path d="M3 13l9 6 9-6"/></svg>',
  candle:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2c1 1.5 1 3-0 4-1-1-1-2.5 0-4z"/><path d="M9 8h6v12H9z"/><path d="M7 20h10"/></svg>',
  book:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 5h14v14l-7-3-7 3z"/></svg>',
  opps:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 7h18v13H3z"/><path d="M8 7V5a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><path d="M3 12h18"/></svg>',
  // BubbesBlock original brand mark: a row of homes (a "block") crowned with a heart. NOT a Nextdoor house.
  brand:'<svg viewBox="0 0 32 24" fill="#fff" aria-label="BubbesBlock"><rect x="2.5" y="12.5" width="7" height="9" rx="1"/><rect x="12.5" y="12.5" width="7" height="9" rx="1"/><rect x="22.5" y="12.5" width="7" height="9" rx="1"/><path d="M1.6 12.8 6 8.4l4.4 4.4z"/><path d="M11.6 12.8 16 8.4l4.4 4.4z"/><path d="M21.6 12.8 26 8.4l4.4 4.4z"/><rect x="4.6" y="15" width="2.4" height="2.4" rx=".4" fill="#9c3c63"/><rect x="14.6" y="15" width="2.4" height="2.4" rx=".4" fill="#9c3c63"/><rect x="24.6" y="15" width="2.4" height="2.4" rx=".4" fill="#9c3c63"/><path d="M16 3c-1-1.7-3.7-1.2-3.7 1 0 1.5 2.2 2.9 3.7 3.9 1.5-1 3.7-2.4 3.7-3.9 0-2.2-2.7-2.7-3.7-1z"/></svg>'
};
// page-aware active state for nav
const PATH = (typeof location !== 'undefined') ? location.pathname : '/';

function topbar(me){
  return `<header class="topbar">
    <a class="brand" href="/"><span class="house">${ICON.brand}</span>BubbesBlock<small>♥ your block</small></a>
    <div class="search">
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.3-4.3"/></svg>
      <input placeholder="Search your block — neighbors, favors, history…" />
    </div>
    <nav class="topnav">
      <button class="navbtn ${PATH==='/'?'active':''}" title="Block Feed" onclick="location.href='/'">${ICON.house}</button>
      <button class="navbtn ${PATH.indexOf('/opportunities')===0?'active':''}" title="Opp Alerts" onclick="location.href='/opportunities'"><span class="badge">12</span>${ICON.opps}</button>
      <button class="navbtn" title="Notifications" data-panel="notifications"><span class="badge" data-badge="notifications" style="display:none"></span>${ICON.bell}</button>
      <button class="navbtn" title="Inbox" data-panel="inbox">${ICON.inbox}</button>
      <button class="avatar-btn" data-avatar><span class="ava ${me.color}" style="width:100%;height:100%;border-radius:0;font-size:15px">${me.initials}</span></button>
    </nav>
  </header>`;
}

function leftRail(me){
  return `<aside class="col-left">
    <div class="me">
      <span class="ava ${me.color}" style="width:44px;height:44px;font-size:16px">${me.initials}</span>
      <div><b>${me.name}</b><span>${me.address}${me.verified?' · verified':''}</span></div>
    </div>
    <div class="menu">
      <a class="${PATH==='/'?'active':''}" href="/">${ICON.house}Block Feed</a>
      <a class="${PATH.indexOf('/opportunities')===0?'active':''}" href="/opportunities">${ICON.opps}Opp Alerts<span class="pill">12</span></a>
      <a class="${PATH==='/notifications'?'active':''}" href="/notifications">${ICON.bell}Notifications</a>
      <a class="${PATH==='/groups'?'active':''}" href="/groups">${ICON.sugar}Groups</a>
      <a class="${PATH==='/events'?'active':''}" href="/events">${ICON.cal}Block Events</a>
      <a href="https://wholivedthere.com">${ICON.history}Who Lived Here</a>
      <a class="${PATH==='/bookmarks'?'active':''}" href="/bookmarks">${ICON.book}Bookmarks</a>
    </div>
    <div class="menu">
      <div class="grouphd">Your Block · Beekman Ave</div>
      <div class="grp">
        <a href="/groups"><span class="dot" style="background:#3a7d5d">🥧</span> Babka &amp; Bagel Swap</a>
        <a href="/groups"><span class="dot" style="background:#b8862f">🔧</span> Tools &amp; Ladders to Borrow</a>
        <a href="/groups"><span class="dot" style="background:#5566b5">🐕</span> Block Dog Walkers</a>
        <a href="/groups"><span class="dot" style="background:#c2557a">🕯️</span> Shabbat Dinner Club</a>
        <a href="/groups"><span class="dot" style="background:#a05236">🌿</span> Holiday &amp; Sukkah Help</a>
      </div>
    </div>
  </aside>`;
}

function rightRail(me){
  return `<aside class="col-right">
    <div class="panel claim">
      <h3>Claim ${me.address}</h3>
      <p class="sub">Verify you live here to post, reply &amp; unlock your home's full history.</p>
      <a class="cta" href="https://claimmyaddress.com">Claim your address →</a>
      <p class="fine">Free · takes 2 minutes · powered by claimmyaddress.com</p>
    </div>
    <div class="panel">
      <div class="spon">Neighborhood Favorites</div>
      <div class="biz"><span class="lg" style="background:#c2557a">🥯</span><div><b>Beekman Bagel &amp; Babka</b><span>Bakery · 2 blocks</span><div class="rate">★★★★★ <span style="color:#65676b">· 214</span></div></div></div>
      <div class="biz"><span class="lg" style="background:#3a7d5d">🔨</span><div><b>Sunset Park Hardware</b><span>Hardware · 4 blocks</span><div class="rate">★★★★☆ <span style="color:#65676b">· 96</span></div></div></div>
      <div class="biz"><span class="lg" style="background:#5566b5">🕯️</span><div><b>Adas Yeshurun Shul</b><span>Community · on the block</span><div class="rate">★★★★★ <span style="color:#65676b">· 58</span></div></div></div>
    </div>
    <div class="panel trend">
      <h3 style="margin-bottom:10px">Trending on your block</h3>
      <a href="/p/sukkah-help"><b>🌿 Hands needed for the sukkah</b><br><small>4 neighbors replied · 8h</small></a>
      <a href="/p/lost-cat"><b>🐱 Found: orange tabby near 116</b><br><small>14 neighbors replied · 10h</small></a>
      <a href="/p/block-party"><b>🎉 Sunday block party — 1pm</b><br><small>32 going · 1d</small></a>
      <a href="/p/who-lived-here"><b>🏚️ Who lived at 104 in the '50s?</b><br><small>from wholivedthere.com · 1d</small></a>
    </div>
    <div class="footnote">
      <a href="#">About</a> · <a href="#">Block Guidelines</a> · <a href="#">Privacy</a> · <a href="#">Help</a><br>
      <a href="https://wholivedthere.com">wholivedthere.com</a> · <a href="https://claimmyaddress.com">claimmyaddress.com</a>
      <div class="mark">BubbesBlock © 2026 — your block, the warm way ♥</div>
    </div>
  </aside>`;
}

/* shared post-card builders */
function reactStack(reactions){
  const cls=['#f3603c','#f7b500','#00874b','#5566b5'];
  return `<div class="react">${reactions.map((r,i)=>`<span style="background:${cls[i%cls.length]}">${r}</span>`).join('')}</div>`;
}
function actionBar(id, youThanked, thanks){
  return `<div class="actions">
    <button class="${youThanked?'on':''}" data-react="post:${id}"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20.8 4.6a5.5 5.5 0 0 0-7.8 0L12 5.7l-1-1a5.5 5.5 0 0 0-7.8 7.8l1 1L12 21l7.8-7.6 1-1a5.5 5.5 0 0 0 0-7.8z"/></svg>Thank <span class="cnt">${thanks||''}</span></button>
    <button data-reply="${id}"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 11.5a8.4 8.4 0 0 1-9 8.4 9 9 0 0 1-4-1L3 20l1.2-4A8.4 8.4 0 1 1 21 11.5z"/></svg>Reply</button>
    <button data-share="${id}"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12v8h16v-8"/><path d="M12 16V4"/><path d="M8 8l4-4 4 4"/></svg>Share</button>
  </div>`;
}
function postHead(p){
  return `<div class="post-head">
    <span class="ava ${p.color}" style="width:46px;height:46px;font-size:17px">${p.initials}</span>
    <div class="who"><b>${p.author}</b>
      <div class="meta"><span class="gd">${p.address}</span> · ${p.time}${p.edited?' · <svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg> Edited':''}</div>
    </div>
    <button class="more" data-more="post:${p.id}"><svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><circle cx="5" cy="12" r="2"/><circle cx="12" cy="12" r="2"/><circle cx="19" cy="12" r="2"/></svg></button>
  </div>`;
}
function postBody(p){
  return `<span class="cat ${p.catColor}">${p.category}</span>
    <div class="body">${p.body.map(x=>`<p>${x}</p>`).join('')}</div>`;
}
function photoBlock(ph){
  if(!ph) return '';
  return `<div class="photo"><div class="ph ${ph.bg}"><span class="emoji">${ph.emoji}</span><span class="tag">${ph.tag}</span></div></div>`;
}
function statRow(p,count){
  return `<div class="stats">${reactStack(p.reactions)}<span style="margin-left:8px">${p.reactedBy}</span>
    <div class="right"><span>${count} comments</span><span>${p.shares} shares</span></div></div>`;
}