1/** 2 * community_skills_row.js â Skill Board row on community.html. 3 * 4 * Loads up to 6 recent open posts from the current community via the 5 * listSkillPosts callable and renders compact cards. The callable is for 6 * signed-in members, so the row waits for the restored sign-in and stays 7 * hidden for visitors (calling it signed out only produced a 401 in the 8 * console, and calling it before auth restored hid the row for members 9 * too). Clicking a card 10 * jumps to /skill_board.html?cid=â¦&post=â¦; the "Browse all" button 11 * carries the cid through too. 12 * 13 * Community id resolution mirrors what other community.html rows do: 14 * 1. URL param ?community=<cid> 15 * 2. URL param ?cid=<cid> 16 * 3. window.__COMMUNITY_ID__ if set by the page bootstrap 17 * 4. single-town mode via window.COMMUNITY_CONTEXT if exposed 18 */ 19 20(function () { 21 "use strict"; 22 23 function esc(s) { 24 return String(s == null ? "" : s).replace(/[&<>"']/g, function (c) { 25 return { "&": "&", "<": "<", ">": ">", "\"": """, "'": "'" }[c]; 26 }); 27 } 28 function human(k) { return String(k || "").replace(/_/g, " ").trim(); } 29 30 function resolveCid() { 31 try { 32 var u = new URL(window.location.href); 33 var cid = u.searchParams.get("community") || u.searchParams.get("cid"); 34 if (cid) return cid; 35 } catch (e) { /* ignore */ } 36 if (window.__COMMUNITY_ID__) return window.__COMMUNITY_ID__; 37 if (window.COMMUNITY_CONTEXT && window.COMMUNITY_CONTEXT.id) return window.COMMUNITY_CONTEXT.id; 38 return null; 39 } 40 41 function cardHtml(p, cid) { 42 var typeTone = p.type === "request" ? "request" : "offer"; 43 var tags = (p.skill_keys || []).slice(0, 3).map(function (k) { 44 return '<span class="skills-tag">' + esc(human(k)) + '</span>'; 45 }).join(""); 46 var href = "/skill_board.html?cid=" + encodeURIComponent(cid || "") + "&post=" + encodeURIComponent(p.id); 47 return '' + 48 '<a class="skills-community-card skills-community-card--' + typeTone + '" href="' + href + '">' + 49 '<span class="skills-community-card__label">' + (p.type === "request" ? "NEED" : "OFFER") + '</span>' + 50 '<h3 class="skills-community-card__title">' + esc(p.title || "(untitled)") + '</h3>' + 51 '<p class="skills-community-card__desc">' + esc((p.description || "").slice(0, 140)) + '</p>' + 52 '<div class="skills-community-card__tags">' + tags + '</div>' + 53 (p.response_count ? '<div class="skills-community-card__meta">' + p.response_count + ' responses</div>' : '') + 54 '</a>'; 55 } 56 57 /** Resolves the signed-in user once Firebase has restored the session (null if none). */ 58 function signedInUser() { 59 return new Promise(function (resolve) { 60 try { 61 var unsub = firebase.auth().onAuthStateChanged(function (user) { 62 unsub(); 63 resolve(user || null); 64 }); 65 } catch (e) { 66 resolve(null); 67 } 68 }); 69 } 70 71 async function load() { 72 var feed = document.getElementById("community_skill_feed"); 73 var empty = document.getElementById("community_skill_empty"); 74 var btn = document.getElementById("skill_board_all_btn"); 75 if (!feed) return; 76 var cid = resolveCid(); 77 if (btn) btn.href = "/skill_board.html" + (cid ? ("?cid=" + encodeURIComponent(cid)) : ""); 78 if (!cid) { 79 // No community context â hide the whole row so we don't show 80 // a confusing empty state on the homepage. 81 var sec = document.getElementById("row_skill_board"); 82 if (sec) sec.style.display = "none"; 83 return; 84 } 85 if (!window.firebase || typeof window.firebase.functions !== "function") return; 86 var user = await signedInUser(); 87 if (!user) { 88 var hidden = document.getElementById("row_skill_board"); 89 if (hidden) hidden.style.display = "none"; 90 return; 91 } 92 try { 93 var res = await firebase.functions().httpsCallable("listSkillPosts")({ 94 communityId: cid, status: "open", limit: 6, 95 }); 96 var posts = (res && res.data && res.data.posts) || []; 97 if (posts.length === 0) { 98 feed.innerHTML = ""; 99 if (empty) empty.style.display = ""; 100 return; 101 } 102 if (empty) empty.style.display = "none"; 103 feed.innerHTML = posts.map(function (p) { return cardHtml(p, cid); }).join(""); 104 } catch (e) { 105 // Stay quiet on the public homepage â just hide the row. 106 console.warn("[community-skill-row] load failed", e); 107 var sec2 = document.getElementById("row_skill_board"); 108 if (sec2) sec2.style.display = "none"; 109 } 110 } 111 112 if (document.readyState === "loading") { 113 document.addEventListener("DOMContentLoaded", load); 114 } else { 115 load(); 116 } 117})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.